Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Bibliothek - DockingFrames
* Library built on Java/Swing, allows the user to "drag and drop"
* panels containing any Swing-Component the developer likes to add.
*
* Copyright (C) 2012 Herve Guillaume, Benjamin Sigg
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Herve Guillaume
* [email protected]
* FR - France
*
* Benjamin Sigg
* [email protected]
* CH - Switzerland
*/
package bibliothek.gui.dock.wizard;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.event.DockStationListener;
import bibliothek.gui.dock.station.StationPaint;
import bibliothek.gui.dock.station.span.Span;
import bibliothek.gui.dock.station.split.DefaultSplitDividerStrategy;
import bibliothek.gui.dock.station.split.DefaultSplitLayoutManager;
import bibliothek.gui.dock.station.split.Divideable;
import bibliothek.gui.dock.station.split.Leaf;
import bibliothek.gui.dock.station.split.Node;
import bibliothek.gui.dock.station.split.Placeholder;
import bibliothek.gui.dock.station.split.PutInfo;
import bibliothek.gui.dock.station.split.PutInfo.Put;
import bibliothek.gui.dock.station.split.Root;
import bibliothek.gui.dock.station.split.SplitDockPlaceholderProperty;
import bibliothek.gui.dock.station.split.SplitLayoutManager;
import bibliothek.gui.dock.station.split.SplitNode;
import bibliothek.gui.dock.station.split.SplitNodeVisitor;
import bibliothek.gui.dock.station.support.CombinerTarget;
import bibliothek.gui.dock.themes.DefaultStationPaintValue;
import bibliothek.gui.dock.themes.basic.NoSpanFactory;
import bibliothek.gui.dock.wizard.WizardNodeMap.Column;
/**
* A {@link WizardSplitDockStation} has some additional restrictions and other behavior than an ordinary {@link SplitDockStation}:
*
*
The {@link Dockable}s are ordered in columns.
*
The station does not use up empty space if not needed.
*
Moving a divider changes the preferred size of the station.
*
This station should be wrapped into a {@link JScrollPane}, it even implements {@link Scrollable} to fully support the {@link JScrollPane}.
*
* @author Benjamin Sigg
*/
public class WizardSplitDockStation extends SplitDockStation implements Scrollable{
/** the side where dockables are pushed to */
public static enum Side{
TOP, LEFT, RIGHT, BOTTOM;
public Orientation getHeaderOrientation(){
switch( this ){
case LEFT:
case RIGHT:
return Orientation.HORIZONTAL;
case TOP:
case BOTTOM:
return Orientation.VERTICAL;
default:
throw new IllegalStateException( "unknown: " + this );
}
}
public Orientation getColumnOrientation(){
switch( this ){
case LEFT:
case RIGHT:
return Orientation.VERTICAL;
case TOP:
case BOTTOM:
return Orientation.HORIZONTAL;
default:
throw new IllegalStateException( "unknown: " + this );
}
}
}
private WizardLayoutManager layoutManager;
private WizardSpanStrategy wizardSpanStrategy;
private Side side;
private boolean onRevalidating = false;
private int sideGap = 3;
/**
* Creates a new station.
* @param side the side at which this station is presented, e.g. if this station is at the left side
* of a {@link JFrame}, the parameter should be {@link Side#LEFT}
*/
public WizardSplitDockStation( Side side ){
this.side = side;
layoutManager = new WizardLayoutManager();
wizardSpanStrategy = new WizardSpanStrategy( this );
setSplitLayoutManager( layoutManager );
setDividerStrategy( new WizardDividerStrategy() );
// getContentPane().setBorder( BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) );
setAllowSideSnap( true );
// disable the standard mechanism for showing spans
getSpanStrategy().getFactory().setDelegate( new NoSpanFactory() );
addDockStationListener( new DockStationListener(){
@Override
public void dockablesRepositioned( DockStation station, Dockable[] dockables ){
revalidateOutside();
}
@Override
public void dockableShowingChanged( DockStation station, Dockable dockable, boolean showing ){
revalidateOutside();
}
@Override
public void dockableSelected( DockStation station, Dockable oldSelection, Dockable newSelection ){
// ignore
}
@Override
public void dockableRemoving( DockStation station, Dockable dockable ){
// ignore
}
@Override
public void dockableRemoved( DockStation station, Dockable dockable ){
revalidateOutside();
}
@Override
public void dockableAdding( DockStation station, Dockable dockable ){
// ignore
}
@Override
public void dockableAdded( DockStation station, Dockable dockable ){
revalidateOutside();
}
} );
}
/**
* Calls {@link #revalidate()} on the first {@link JComponent} that is outside of the current {@link JScrollPane}.
*/
public void revalidateOutside(){
if( !onRevalidating ){
revalidate();
if( EventQueue.isDispatchThread() ){
EventQueue.invokeLater( new Runnable(){
@Override
public void run(){
if( getParent() instanceof JViewport ){
Container parent = getParent();
while( parent != null && !(parent instanceof JScrollPane)){
parent = parent.getParent();
}
if( parent != null ){
parent = parent.getParent();
if( parent != null && parent instanceof JComponent ){
((JComponent)parent).revalidate();
}
}
}
try{
onRevalidating = true;
updateBounds();
}
finally{
onRevalidating = false;
}
}
} );
}
}
}
@Override
public String getFactoryID(){
return WizardSplitDockStationFactory.ID;
}
public WizardLayoutManager getWizardSplitLayoutManager(){
return layoutManager;
}
/**
* Gets the strategy which is responsible for managing the {@link Span}s.
* @return the span strategy
*/
public WizardSpanStrategy getWizardSpanStrategy(){
return wizardSpanStrategy;
}
@Override
public void setDividerSize( int dividerSize ){
super.setDividerSize( dividerSize );
if( wizardSpanStrategy != null ){
wizardSpanStrategy.reset();
}
}
@Override
public void setController( DockController controller ){
wizardSpanStrategy.setController( controller );
super.setController( controller );
}
@Override
protected void setPut( PutInfo info ){
wizardSpanStrategy.setPut( info );
}
@Override
protected void unsetPut(){
wizardSpanStrategy.unsetPut();
}
/**
* Gets the size of the empty space at the moveable side of this station.
* @return the empty space at the side
*/
public int getSideGap(){
return sideGap;
}
/**
* Sets an empty space at the moveable side of this station.
* @param sideGap the size of the gap, should be at least 0
*/
public void setSideGap( int sideGap ){
if( sideGap < 0 ){
throw new IllegalArgumentException( "sideGap must be at least 0: " + sideGap );
}
this.sideGap = sideGap;
revalidate();
}
/**
* Gets the side to which this station leans.
* @return the side which does not change its position ever
*/
public Side getSide(){
return side;
}
/**
* Sets the side to which this station leans. If the current side and the new side do not have the same orientation,
* then the "columns" of this station are rotated by 90 degrees.
* @param side the new side, not null
*/
public void setSide( Side side ){
if( side == null ){
throw new IllegalArgumentException( "side must not be null" );
}
if( this.side != side ){
boolean rotate = this.side.getHeaderOrientation() != side.getHeaderOrientation();
this.side = side;
if( rotate ){
root().visit( new SplitNodeVisitor(){
@Override
public void handleRoot( Root root ){
// ignore
}
@Override
public void handlePlaceholder( Placeholder placeholder ){
// ignore
}
@Override
public void handleNode( Node node ){
switch( node.getOrientation() ){
case HORIZONTAL:
node.setOrientation( Orientation.VERTICAL );
break;
case VERTICAL:
node.setOrientation( Orientation.HORIZONTAL );
break;
}
}
@Override
public void handleLeaf( Leaf leaf ){
// ignore
}
} );
}
resetToPreferredSizes();
}
}
@Override
public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction ){
return 10;
}
@Override
public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction ){
return 20;
}
@Override
public Dimension getPreferredScrollableViewportSize(){
return getPreferredSize();
}
@Override
public boolean getScrollableTracksViewportWidth(){
if( side == Side.LEFT || side == Side.RIGHT ){
return true;
}
if( getParent() instanceof JViewport ){
if( getParent().getWidth() > getPreferredSize().width ){
return true;
}
}
return false;
}
@Override
public boolean getScrollableTracksViewportHeight(){
if( side == Side.TOP || side == Side.BOTTOM ){
return true;
}
if( getParent() instanceof JViewport ){
if( getParent().getHeight() > getPreferredSize().height ){
return true;
}
}
return false;
}
@Override
public Dimension getMinimumSize(){
return getPreferredSize();
}
@Override
public Dimension getPreferredSize(){
if( layoutManager == null ){
return super.getPreferredSize();
}
else{
return layoutManager.getPreferredSize();
}
}
@Override
protected void paintOverlay( Graphics g ){
PutInfo putInfo = getDropInfo();
if( putInfo != null ) {
DefaultStationPaintValue paint = getPaint();
if( putInfo.getNode() == null ) {
Insets insets = getInsets();
Rectangle bounds = new Rectangle( insets.left, insets.top, getWidth()-insets.left-insets.right, getHeight()-insets.top-insets.bottom );
paint.drawInsertion(g, bounds, bounds);
}
else {
CombinerTarget target = putInfo.getCombinerTarget();
if( target == null ){
Rectangle bounds = putInfo.getNode().getBounds();
int gap = getWizardSpanStrategy().getGap();
if( putInfo.getPut() == PutInfo.Put.LEFT ) {
bounds.x -= gap;
bounds.width = gap;
bounds.x = Math.max( 0, bounds.x );
}
else if( putInfo.getPut() == PutInfo.Put.RIGHT ) {
bounds.x += bounds.width;
bounds.width = gap;
bounds.x = Math.min( bounds.x, getWidth()-gap-1 );
}
else if( putInfo.getPut() == PutInfo.Put.TOP ) {
bounds.y -= gap;
bounds.height = gap;
bounds.y = Math.max( 0, bounds.y );
}
else if( putInfo.getPut() == PutInfo.Put.BOTTOM ) {
bounds.y += bounds.height;
bounds.height = gap;
bounds.y = Math.min( bounds.y, getHeight()-gap-1 );
}
paint.drawInsertion(g, putInfo.getNode().getBounds(), bounds);
}
else{
Rectangle bounds = putInfo.getNode().getBounds();
StationPaint stationPaint = paint.get();
if( stationPaint != null ){
target.paint( g, getComponent(), stationPaint, bounds, bounds );
}
}
}
}
getDividerStrategy().paint( this, g );
}
private Leaf resizeableLeafAt( int x, int y ){
Leaf[] leafs = layoutManager.getLastLeafOfColumns();
int gap = getDividerSize();
if( side == Side.RIGHT || side == Side.LEFT ){
for( Leaf leaf : leafs ){
Rectangle bounds = leaf.getBounds();
if( bounds.x <= x && bounds.x + bounds.width >= x ){
if( bounds.y + bounds.height <= y && bounds.y + bounds.height + gap >= y ){
return leaf;
}
}
}
}
else if( side == Side.BOTTOM || side == Side.TOP ){
for( Leaf leaf : leafs ){
Rectangle bounds = leaf.getBounds();
if( bounds.y <= y && bounds.y + bounds.height >= y ){
if( bounds.x + bounds.width <= x && bounds.x + bounds.width + gap >= x ){
return leaf;
}
}
}
}
return null;
}
public PersistentColumn[] getPersistentColumns(){
return layoutManager.model.getPersistentColumns();
}
public void setPersistentColumns( Dockable[][] columnsAndCells, int[][] cellSizes, int[] columnSizes ){
layoutManager.model.setPersistentColumns( columnsAndCells, cellSizes, columnSizes );
}
/**
* Ensures that the dropped {@link Dockable} does not come to rest at a location that would destroy the columns.
*/
@Override
public boolean drop( Dockable dockable, SplitDockPlaceholderProperty property ){
SplitNode node = getRoot().getPlaceholderNode( property.getPlaceholder() );
if( node != null && !(node instanceof Leaf) && !(node instanceof Root)){
if( node instanceof Placeholder || ((Node)node).getOrientation() == side.getHeaderOrientation() ){
pushIntoHeader( node );
}
}
return super.drop( dockable, property );
}
private void pushIntoHeader( SplitNode node ){
while( true ){
SplitNode parent = node.getParent();
if( parent == null || layoutManager.model.isHeaderLevel( parent, false )){
return;
}
if( parent instanceof Node ){
Node n = (Node)parent;
SplitNode superParent = parent.getParent();
if( superParent instanceof Root ){
n.setOrientation( side.getHeaderOrientation() );
return;
}
else if( superParent instanceof Node ){
Node s = (Node)superParent;
if( n.getLeft() == node ){
if( s.getLeft() == n ){
s.setLeft( node );
SplitNode old = s.getRight();
s.setRight( n );
n.setLeft( old );
}
else{
n.setLeft( s.getLeft() );
s.setLeft( node );
}
}
else{
if( s.getRight() == n ){
s.setRight( node );
SplitNode old = s.getLeft();
s.setLeft( n );
n.setRight( old );
}
else{
n.setRight( s.getRight() );
s.setRight( node );
}
}
n.setOrientation( side.getColumnOrientation() );
s.setOrientation( side.getHeaderOrientation() );
}
node = node.getParent();
}
}
}
/**
* Changes the size of all columns and cells such that they met their preferred size.
*/
public void resetToPreferredSizes(){
layoutManager.model.resetToPreferredSizes();
}
/**
* This {@link SplitLayoutManager} adds restrictions on how a drag and drop operation
* can be performed, and what the boundaries of the children are:
*
*
DnD operations must ensure that the {@link Dockable}s remain in columns, see {@link #ensureDropLocation(PutInfo)}
*
* @author Benjamin Sigg
*/
public class WizardLayoutManager extends DefaultSplitLayoutManager {
private WizardColumnModel model;
public WizardLayoutManager(){
model = new WizardColumnModel( WizardSplitDockStation.this );
}
@Override
public PutInfo validatePutInfo( SplitDockStation station, PutInfo putInfo ){
putInfo = ensureDropLocation( putInfo );
return super.validatePutInfo( station, putInfo );
}
public Leaf[] getLastLeafOfColumns(){
return model.getLastLeafOfColumns();
}
@Override
protected PutInfo calculateSideSnap( SplitDockStation station, int x, int y, Leaf leaf, Dockable drop ){
WizardSpanStrategy spanStrategy = getWizardSpanStrategy();
WizardLayoutManager layout = getWizardSplitLayoutManager();
WizardNodeMap map = layout.getMap();
Column[] columns = map.getSortedColumns();
int columnCount = map.getColumnCount();
int first = 0;
int total = 0;
if( columnCount > 0 ){
first = spanStrategy.getGap( 0 );
total = first;
for( int i = 1; i < columnCount; i++ ){
total += spanStrategy.getGap( i );
}
}
if( side.getHeaderOrientation() == Orientation.HORIZONTAL ){
for( int i = 0; i < columnCount; i++ ){
Rectangle bounds = columns[i].getBounds();
if( x >= bounds.x && x <= bounds.x + bounds.width ){
if( y < bounds.y ){
return new PutInfo( columns[i].getRoot(), Put.TOP, drop, false );
}
else if( y > bounds.y + bounds.height ){
return new PutInfo( columns[i].getRoot(), Put.BOTTOM, drop, false );
}
else if( x < bounds.x + bounds.width/2 ){
return new PutInfo( columns[i].getRoot(), Put.LEFT, drop, false );
}
else{
return new PutInfo( columns[i].getRoot(), Put.RIGHT, drop, false );
}
}
}
int width = getWidth() - total;
x -= first;
if( x < width / 2 ){
return new PutInfo( leftMost( getRoot() ), Put.LEFT, drop, false );
}
else{
return new PutInfo( rightMost( getRoot() ), Put.RIGHT, drop, false );
}
}
else{
for( int i = 0; i < columnCount; i++ ){
Rectangle bounds = columns[i].getBounds();
if( y >= bounds.y && y <= bounds.y + bounds.height ){
if( x < bounds.x ){
return new PutInfo( columns[i].getRoot(), Put.LEFT, drop, false );
}
else if( x > bounds.x + bounds.width ){
return new PutInfo( columns[i].getRoot(), Put.RIGHT, drop, false );
}
else if( y < bounds.y + bounds.height/2 ){
return new PutInfo( columns[i].getRoot(), Put.TOP, drop, false );
}
else{
return new PutInfo( columns[i].getRoot(), Put.BOTTOM, drop, false );
}
}
}
int height = getHeight() - total;
y -= first;
if( y < height / 2 ){
return new PutInfo( leftMost( getRoot() ), Put.TOP, drop, false );
}
else{
return new PutInfo( rightMost( getRoot() ), Put.BOTTOM, drop, false );
}
}
}
private SplitNode leftMost( SplitNode node ){
while( true ){
if( node.getMaxChildrenCount() > 0 ){
node = node.getChild( 0 );
}
else{
return node;
}
}
}
private SplitNode rightMost( SplitNode node ){
while( true ){
int max = node.getMaxChildrenCount();
if( max > 0 ){
node = node.getChild( max-1 );
}
else{
return node;
}
}
}
/**
* Ensures that dropping a {@link Dockable} is only possible such that the
* tabular like form of the layout remains intact.
* @param putInfo information about the item that is dropped, can be null
* @return the validated drop information
*/
private PutInfo ensureDropLocation( PutInfo putInfo ){
if( putInfo != null ) {
boolean header;
if( side.getHeaderOrientation() == Orientation.HORIZONTAL ) {
header = putInfo.getPut() == Put.LEFT || putInfo.getPut() == Put.RIGHT;
}
else {
header = putInfo.getPut() == Put.TOP || putInfo.getPut() == Put.BOTTOM;
}
if( header ) {
SplitNode node = putInfo.getNode();
while( node != null && !model.isHeaderLevel( node ) ) {
node = node.getParent();
}
putInfo.setNode( node );
}
}
return putInfo;
}
@Override
public void updateBounds( Root root, double x, double y, double factorW, double factorH ){
model.setFactors( factorW, factorH );
model.updateBounds( x, y );
}
@Override
public double validateDivider( SplitDockStation station, double divider, Node node ){
return model.validateDivider( divider, node );
}
public double validateDivider( double divider, Leaf leaf ){
return model.validateDivider( divider, leaf );
}
public double validateColumnDivider( double divider ){
return model.validateColumnDivider( divider );
}
public Dimension getPreferredSize(){
return model.getPreferredSize();
}
public void setDivider( Divideable node, double divider ){
model.setDivider( node, divider );
revalidate();
}
public WizardNodeMap getMap(){
return model.getMap();
}
}
private class WizardDividerStrategy extends DefaultSplitDividerStrategy{
@Override
protected Handler createHandlerFor( SplitDockStation station ){
return new CustomHandler( station );
}
private class CustomHandler extends Handler{
public CustomHandler( SplitDockStation station ){
super( station );
}
@Override
protected void setDivider( Divideable node, double dividier ){
layoutManager.setDivider( node, dividier );
}
@Override
protected Divideable getDividerNode( int x, int y ){
Divideable node = super.getDividerNode( x, y );
if( node == null ){
int gap = getDividerSize();
if( side == Side.RIGHT && x <= gap ){
return new ColumnDividier( WizardSplitDockStation.this );
}
else if( side == Side.LEFT && x >= getWidth() - gap - 1 ){
return new ColumnDividier( WizardSplitDockStation.this );
}
else if( side == Side.TOP && y >= getHeight() - gap - 1 ){
return new ColumnDividier( WizardSplitDockStation.this );
}
else if( side == Side.BOTTOM && y <= gap ){
return new ColumnDividier( WizardSplitDockStation.this );
}
Leaf leaf = resizeableLeafAt( x, y );
if( leaf != null ){
return new CellDivider( WizardSplitDockStation.this, leaf );
}
}
return node;
}
}
}
}