package tutorial.core.basics;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import javax.swing.SwingUtilities;
import tutorial.support.ColorDockable;
import tutorial.support.JTutorialFrame;
import tutorial.support.Tutorial;
import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.StackDockStation;
import bibliothek.gui.dock.displayer.DisplayerCombinerTarget;
import bibliothek.gui.dock.layout.location.AsideRequest;
import bibliothek.gui.dock.station.Combiner;
import bibliothek.gui.dock.station.StationPaint;
import bibliothek.gui.dock.station.split.SplitDockGrid;
import bibliothek.gui.dock.station.support.CombinerSource;
import bibliothek.gui.dock.station.support.CombinerTarget;
import bibliothek.gui.dock.station.support.Enforcement;
import bibliothek.gui.dock.station.support.PlaceholderMap;
import bibliothek.gui.dock.themes.CombinerValue;
import bibliothek.gui.dock.themes.ThemeManager;
import bibliothek.gui.dock.util.UIBridge;
import bibliothek.gui.dock.util.UIValue;
@Tutorial( id="Combiner", title="Combining Dockables" )
public class CombinerExample {
public static void main( String[] args ){
/* Whenever the user or the client tries to merge (combine) two Dockables, an interface is
* asked to do the job. This interface is called "Combiner" and can be set by clients.
*
* Usually the Combiner is used by DockStations in this way:
*
* 1. The caller creates a "CombinerSource". This object delivers basic information about the
* elements that are going to be combined.
* 2. The caller invokes "Combiner.prepare", which returns a "CombinerTarget" or "null" as answer.
* 3. The caller caches the "CombinerTarget" and may use it for painting.
* 4. The caller invokes "Combiner.combine" which lets the Combiner do its main work: combine two
* Dockables and create a new one.
*
* The chain of events can be canceled in any step. */
/* Setting up basic frame and controller */
JTutorialFrame frame = new JTutorialFrame( CombinerExample.class );
DockController controller = new DockController();
controller.setRootWindow( frame );
frame.destroyOnClose( controller );
/* With the help of the ThemeManager and an UIBridge we can easily replace all occurances of
* a Combiner with our custom implementation */
ThemeManager themeManager = controller.getThemeManager();
themeManager.setCombinerBridge( CombinerValue.KIND_COMBINER, new UIBridge<Combiner, UIValue<Combiner>>(){
public void add( String id, UIValue<Combiner> uiValue ){
// ignore
}
public void remove( String id, UIValue<Combiner> uiValue ){
// ignore
}
public void set( String id, Combiner value, UIValue<Combiner> uiValue ){
uiValue.set( new CustomCombiner() );
}
});
/* And now we set up different DockStations and Dockables */
SplitDockStation splitDockStation = new SplitDockStation();
controller.add( splitDockStation );
frame.add( splitDockStation );
SplitDockGrid grid = new SplitDockGrid();
grid.addDockable( 0, 0, 100, 20, new ColorDockable( "Gray", Color.GRAY ));
grid.addDockable( 0, 20, 30, 50, new ColorDockable( "Dark", Color.DARK_GRAY ));
grid.addDockable( 0, 70, 30, 30, new ColorDockable( "Light", Color.LIGHT_GRAY ));
grid.addDockable( 30, 20, 80, 80, new ColorDockable( "White", Color.WHITE ));
grid.addDockable( 30, 20, 80, 80, new ColorDockable( "Black", Color.BLACK ));
splitDockStation.dropTree( grid.toTree() );
frame.setVisible( true );
}
/* This is our custom Combiner. It has customized painting code, but the final result of combining two
* Dockables will be the same as if using the standard Combiner. */
private static class CustomCombiner implements Combiner{
public CombinerTarget prepare( final CombinerSource source, Enforcement force ){
/* We do not combine anything unless we are forced */
if( force.getForce() < 0.5f ){
return null;
}
/* The CombinerTarget can be used for painting and for keeping information that is later
* needed. In this case we just use it for painting. */
return new CombinerTarget(){
public void paint( Graphics g, Component component, StationPaint paint, Rectangle stationBounds, Rectangle dockableBounds ){
/* Our custom painting code paints some arrows... */
Graphics2D g2 = (Graphics2D)g.create();
g2.setColor( Color.GREEN );
int[] x = new int[]{ 0, 20, 8, 8, -8, -8, -20 };
int[] y = new int[]{ 0, 20, 20, 40, 40, 20, 20 };
Polygon arrow = new Polygon( x, y, x.length );
g2.translate( stationBounds.x + stationBounds.width/2, stationBounds.y + stationBounds.height/2 );
AffineTransform transform = new AffineTransform();
transform.rotate( Math.PI/2 );
g2.translate( 0, 10 );
g2.fillPolygon( arrow );
g2.translate( 0, -10 );
g2.transform( transform );
g2.translate( 0, 10 );
g2.fillPolygon( arrow );
g2.translate( 0, -10 );
g2.transform( transform );
g2.translate( 0, 10 );
g2.fillPolygon( arrow );
g2.translate( 0, -10 );
g2.transform( transform );
g2.translate( 0, 10 );
g2.fillPolygon( arrow );
g2.translate( 0, -10 );
g2.setStroke( new BasicStroke( 10 ) );
g2.drawOval( -55, -55, 110, 110 );
g2.dispose();
// ... and a point at the location of the mouse
Point mouse = source.getMousePosition();
if( mouse != null ){
mouse = SwingUtilities.convertPoint( source.getOld().getComponent(), mouse, component );
g.setColor( new Color( 0, 150, 0 ) );
g.fillOval( mouse.x-5, mouse.y-5, 10, 10 );
}
}
public DisplayerCombinerTarget getDisplayerCombination(){
/* Some meta data that we can ignore */
return null;
}
};
}
/* This method takes two Dockables and combines them. In this case we just create
* a new StackDockStation */
public Dockable combine( CombinerSource source, CombinerTarget target ){
DockStation parent = source.getParent();
PlaceholderMap placeholders = source.getPlaceholders();
StackDockStation stack = new StackDockStation( parent.getTheme() );
stack.setController( parent.getController() );
/* By setting the placeholders we allow the framework to more efficiently keep track
* of the location of invisible Dockables */
if( placeholders != null ){
stack.setPlaceholders( placeholders );
}
/* Finally we add the two Dockables that get combined */
stack.drop( source.getOld() );
stack.drop( source.getNew() );
return stack;
}
/* This method would be used to put a Dockable "aside" another dockable, but we do ignore
* it in this example. */
public void aside( AsideRequest request ){
// ignore
}
}
}