All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bibliothek.gui.dock.ToolbarContainerDockStation Maven / Gradle / Ivy

There is a newer version: 1.1.2p6a
Show newest version
/*
 * 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;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.DockUI;
import bibliothek.gui.Dockable;
import bibliothek.gui.Orientation;
import bibliothek.gui.Position;
import bibliothek.gui.dock.event.DockStationAdapter;
import bibliothek.gui.dock.layout.DockableProperty;
import bibliothek.gui.dock.station.AbstractDockableStation;
import bibliothek.gui.dock.station.DisplayerCollection;
import bibliothek.gui.dock.station.DisplayerFactory;
import bibliothek.gui.dock.station.DockableDisplayer;
import bibliothek.gui.dock.station.DockableDisplayerListener;
import bibliothek.gui.dock.station.OrientationObserver;
import bibliothek.gui.dock.station.OrientedDockStation;
import bibliothek.gui.dock.station.OrientingDockStation;
import bibliothek.gui.dock.station.OrientingDockStationEvent;
import bibliothek.gui.dock.station.OrientingDockStationListener;
import bibliothek.gui.dock.station.OverpaintablePanel;
import bibliothek.gui.dock.station.StationChildHandle;
import bibliothek.gui.dock.station.StationDragOperation;
import bibliothek.gui.dock.station.StationDropItem;
import bibliothek.gui.dock.station.StationDropOperation;
import bibliothek.gui.dock.station.StationPaint;
import bibliothek.gui.dock.station.layer.DefaultDropLayer;
import bibliothek.gui.dock.station.layer.DockStationDropLayer;
import bibliothek.gui.dock.station.span.Span;
import bibliothek.gui.dock.station.support.DockablePlaceholderList;
import bibliothek.gui.dock.station.support.DockableShowingManager;
import bibliothek.gui.dock.station.support.PlaceholderMap;
import bibliothek.gui.dock.station.support.PlaceholderStrategy;
import bibliothek.gui.dock.station.toolbar.DefaultToolbarContainerConverter;
import bibliothek.gui.dock.station.toolbar.ToolbarContainerConverter;
import bibliothek.gui.dock.station.toolbar.ToolbarContainerConverterCallback;
import bibliothek.gui.dock.station.toolbar.ToolbarContainerDockStationFactory;
import bibliothek.gui.dock.station.toolbar.ToolbarContainerDropInfo;
import bibliothek.gui.dock.station.toolbar.ToolbarContainerLayoutManager;
import bibliothek.gui.dock.station.toolbar.ToolbarContainerProperty;
import bibliothek.gui.dock.station.toolbar.ToolbarStrategy;
import bibliothek.gui.dock.station.toolbar.layer.ToolbarContainerDropLayer;
import bibliothek.gui.dock.themes.DefaultDisplayerFactoryValue;
import bibliothek.gui.dock.themes.DefaultStationPaintValue;
import bibliothek.gui.dock.themes.ThemeManager;
import bibliothek.gui.dock.themes.basic.BasicDockTitleFactory;
import bibliothek.gui.dock.title.DockTitle;
import bibliothek.gui.dock.title.DockTitleFactory;
import bibliothek.gui.dock.title.DockTitleVersion;
import bibliothek.gui.dock.util.DockUtilities;
import bibliothek.gui.dock.util.PropertyValue;
import bibliothek.gui.dock.util.SilentPropertyValue;
import bibliothek.gui.dock.util.extension.Extension;
import bibliothek.util.Path;

/**
 * A {@link Dockable} and a {@link DockStation} which stands for a group of
 * {@link ToolbarGroupDockStation}. As dockable it can be put in every
 * {@link DockStation}. As DockStation it accepts only
 * {@link ToolbarElementInterface}s. When ToolbarElement are added, all the
 * ComponentDockable extracted from the element are merged together and wrapped
 * in a {@link ToolbarGroupDockStation} before to be added.
 * 
 * @author Herve Guillaume
 */
public class ToolbarContainerDockStation extends AbstractDockableStation implements OrientingDockStation, OrientedDockStation {

	/** the id of the {@link DockTitleFactory} used with this station */
	public static final String TITLE_ID = "toolbar.container";
	/**
	 * This id is forwarded to {@link Extension}s which load additional
	 * {@link DisplayerFactory}s
	 */
	public static final String DISPLAYER_ID = "toolbar.container";

	public static final Orientation DEFAULT_ORIENTATION = Orientation.VERTICAL;

	/** the orientation of the station */
	private Orientation orientation = DEFAULT_ORIENTATION;

	/** the number of dockables this station will accept */
	private int dockablesMaxNumber = 1;

	/** The containerPane */
	private JPanel containerPanel;
	/**
	 * The graphical representation of this station: the pane which contains
	 * toolbars
	 */
	protected OverpaintablePanelBase mainPanel;

	/** dockables associate with the container pane */
	private DockablePlaceholderList dockables = new DockablePlaceholderList();
	/** the {@link DockableDisplayer} shown */
	private final DisplayerCollection displayer;
	/** factory for {@link DockTitle}s used for the main panel */
	private DockTitleVersion title;
	/** factory for creating new {@link DockableDisplayer}s */
	private final DefaultDisplayerFactoryValue displayerFactory;

	/** A paint to draw lines */
	private final DefaultStationPaintValue paint;
	/** the index of the closest dockable above the mouse */
	private int indexBeneathMouse = -1;
	/** closest side of the the closest dockable above the mouse */
	private Position sideAboveMouse = null;
	/**
	 * Tells if this station is in prepareDrop state and should draw something
	 * accordingly
	 */
	boolean prepareDropDraw = false;

	/** The dockable that is about to be dragged away from this station */
	private Dockable removal = null;
	
	/** all registered {@link OrientingDockStationListener}s. */
	private final List orientingListeners = new ArrayList();

	/** current {@link PlaceholderStrategy} */
	private final PropertyValue placeholderStrategy = new PropertyValue( PlaceholderStrategy.PLACEHOLDER_STRATEGY ){
		@Override
		protected void valueChanged( PlaceholderStrategy oldValue, PlaceholderStrategy newValue ){
			dockables.setStrategy( newValue );
		}
	};
	
	/** a manager to inform listeners about changes in the visibility state */
	private DockableShowingManager visibility;
	
	/** added to the current parent of this dockable */
	private VisibleListener visibleListener;
	
	/** This {@link LayoutManager} is responsible for updating the boundaries of all {@link Dockable}s and keeping track of {@link Span}s */
	private ToolbarContainerLayoutManager layoutManager;

	/** the number of pixels outside this station where a drag and drop operation is still possible */
	private int sideSnapSize = 10;
	
	/**
	 * Constructs a new ContainerLineStation
	 */
	public ToolbarContainerDockStation( Orientation orientation ){
		this( orientation, 1 );
	}
	
	/**
	 * Creates a new station
	 * @param orientation the orientation of the content
	 * @param maxNumberOfDockables the maximum number of children or -1
	 */
	public ToolbarContainerDockStation( Orientation orientation, int maxNumberOfDockables ){
		this.orientation = orientation;
		setDockablesMaxNumber( maxNumberOfDockables );
		
		mainPanel = new OverpaintablePanelBase();
		paint = new DefaultStationPaintValue( ThemeManager.STATION_PAINT + ".toolbar", this );

		displayerFactory = new DefaultDisplayerFactoryValue( ThemeManager.DISPLAYER_FACTORY + ".toolbar.container", this );

		displayer = new DisplayerCollection( this, displayerFactory, DISPLAYER_ID );

		final DockableDisplayerListener listener = new DockableDisplayerListener(){
			@Override
			public void discard( DockableDisplayer displayer ){
				ToolbarContainerDockStation.this.discard( displayer );
			}
			@Override
			public void moveableElementChanged( DockableDisplayer displayer ){
				// ignore
			}
		};
		displayer.addDockableDisplayerListener( listener );
		setTitleIcon( null );

		new OrientationObserver( this ){
			@Override
			protected void orientationChanged( Orientation current ){
				if( current != null ) {
					setOrientation( current );
				}
			}
		};
		
		visibility = new DockableShowingManager( listeners );
		visibleListener = new VisibleListener();
		
		getComponent().addHierarchyListener( new HierarchyListener(){
			public void hierarchyChanged( HierarchyEvent e ){
				if( (e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 ){
					if( getDockParent() == null ){
						getDockableStateListeners().checkShowing();
					}
					
					visibility.fire();
				}
			}
		});
	}

	/**
	 * Create a pane for this dock station
	 */
	private JPanel createPanel(){
		final JPanel panel = new JPanel();

		panel.setOpaque( false );
		
		layoutManager = new ToolbarContainerLayoutManager( panel, ToolbarContainerDockStation.this );
		panel.setLayout( layoutManager );
		panel.setBorder( new EmptyBorder( new Insets( 3, 3, 3, 3 ) ) );
		return panel;
	}

	@Override
	public int getDockableCount(){
		return dockables.dockables().size();
	}

	@Override
	public Dockable getDockable( int index ){
		return dockables.dockables().get( index ).getDockable();
	}

	@Override
	public Dockable getFrontDockable(){
		// there's no child which is more important than another
		return null;
	}

	@Override
	public void setFrontDockable( Dockable dockable ){
		// there's no child which is more important than another
	}

	@Override
	public PlaceholderMap getPlaceholders(){
		return createConverter().getPlaceholders( this );
	}

	/**
	 * Gets the layout of this station encoded as {@link PlaceholderMap}.
	 * 
	 * @param children
	 *            identifiers for the children
	 * @return the encoded layout, not null
	 */
	public PlaceholderMap getPlaceholders( Map children ){
		return createConverter().getPlaceholders( this, children );
	}

	@Override
	public void setPlaceholders( PlaceholderMap placeholders ){
		createConverter().setPlaceholders( this, placeholders );
	}

	/**
	 * Sets the layout of this station using the encoded layout from
	 * placeholders
	 * 
	 * @param placeholders
	 *            the placeholders to read
	 * @param children
	 *            the children to add
	 */
	public void setPlaceholders( PlaceholderMap placeholders, Map children ){
		createConverter().setPlaceholders( this, new ToolbarContainerConverterCallback(){
			int index = 0;

			@Override
			public StationChildHandle wrap( Dockable dockable ){
				return new StationChildHandle( ToolbarContainerDockStation.this, displayer, dockable, title );
			}

			@Override
			public void adding( StationChildHandle handle ){
				listeners.fireDockableAdding( handle.getDockable() );
			}

			@Override
			public void added( StationChildHandle handle ){
				handle.updateDisplayer();

				insertAt( handle, index++ );

				handle.getDockable().setDockParent( ToolbarContainerDockStation.this );
				listeners.fireDockableAdded( handle.getDockable() );
			}

			@Override
			public void setDockables( DockablePlaceholderList list ){
				ToolbarContainerDockStation.this.setDockables( list, false );
			}

			@Override
			public void finished( DockablePlaceholderList list ){
				if( getController() != null ) {
					list.bind();
					list.setStrategy( getPlaceholderStrategy() );
				}
			}

		}, placeholders, children );
	}

	/**
	 * Creates a {@link ToolbarContainerConverter} which will be used for one
	 * call.
	 * 
	 * @return the new converter, not null
	 */
	protected ToolbarContainerConverter createConverter(){
		return new DefaultToolbarContainerConverter();
	}

	/**
	 * Gets the {@link PlaceholderStrategy} that is currently in use.
	 * 
	 * @return the current strategy, may be null
	 */
	public PlaceholderStrategy getPlaceholderStrategy(){
		return placeholderStrategy.getValue();
	}

	/**
	 * Sets the {@link PlaceholderStrategy} to use, null will set
	 * the default strategy.
	 * 
	 * @param strategy
	 *            the new strategy, can be null
	 */
	public void setPlaceholderStrategy( PlaceholderStrategy strategy ){
		placeholderStrategy.setValue( strategy );
	}

	@Override
	public DockableProperty getDockableProperty( Dockable child, Dockable target ){
		final int index = indexOf( child );
		Path placeholder = null;
		PlaceholderStrategy strategy = getPlaceholderStrategy();
		if( strategy != null ){
			if( target != null ){
				placeholder = strategy.getPlaceholderFor( target );
			}
			else{
				placeholder = strategy.getPlaceholderFor( child );
			}
			if( placeholder != null ){
				dockables.dockables().addPlaceholder( index, placeholder );
			}
		}
		return new ToolbarContainerProperty( index, placeholder );
	}
	
	@Override
	public StationDragOperation prepareDrag( Dockable dockable ){
		removal = dockable;
		getComponent().repaint();
		return new StationDragOperation(){
			@Override
			public void succeeded(){
				removal = null;
				getComponent().repaint();
			}
			
			@Override
			public void canceled(){
				removal = null;
				getComponent().repaint();
			}
		};
	}
	
	/**
	 * Sets the number of pixels outside the station where a drag and drop
	 * operation can still start.
	 * @param sideSnapSize the size in pixels
	 */
	public void setSideSnapSize( int sideSnapSize ){
		this.sideSnapSize = sideSnapSize;
	}
	
	/**
	 * Gets the number of pixels outside the station where a drag and drop 
	 * operation can still start
	 * @return the size in pixels
	 */
	public int getSideSnapSize(){
		return sideSnapSize;
	}
	
	@Override
	public DockStationDropLayer[] getLayers(){
		return new DockStationDropLayer[]{
				new DefaultDropLayer( this ){
					@Override
					public Component getComponent(){
						return ToolbarContainerDockStation.this.getComponent();
					}
				},
				new ToolbarContainerDropLayer( this )
		};
	}
	
	@Override
	public StationDropOperation prepareDrop( StationDropItem item ){
		// System.out.println(this.toString() + "## prepareDrop(...) ##");
		final DockController controller = getController();

		Dockable dockable = item.getDockable();
		
		// check if the dockable and the station accept each other
		if( this.accept( dockable ) && dockable.accept( this ) ) {
			// check if controller exist and if the controller accept that
			// the dockable become a child of this station
			if( controller != null ) {
				if( !controller.getAcceptance().accept( this, dockable ) ) {
					return null;
				}
			}

			if( !getToolbarStrategy().isToolbarPart( dockable ) ) {
				// only ToolbarElementInterface can be drop or move into this
				return null;
			}

			final ToolbarContainerDropInfo result = new ToolbarContainerDropInfo( dockable, this, dockables, item.getMouseX(), item.getMouseY() ){
				@Override
				public void execute(){
					drop( this );
				}

				// Note: draw() is called first by the Controller. It seems
				// destroy() is called after, after a new StationDropOperation
				// is created

				@Override
				public void destroy( StationDropOperation next ){
					if( next == null || next.getTarget() != getTarget() ){
						layoutManager.setDrawing( null );
					}
					
					// without this line, nothing is displayed except if you
					// drag another component
					indexBeneathMouse = -1;
					sideAboveMouse = null;
					prepareDropDraw = false;
					mainPanel.repaint();

				}

				@Override
				public int getIndex(){
					return indexOf( getDockableBeneathMouse() );
				}
				
				@Override
				public void draw(){
					boolean effect = true;
					indexBeneathMouse = getIndex();
					if( isMove() ){
						int target = moveIndex( this, indexBeneathMouse );
						int current = indexOf( getItem() );
						effect = target != current && target != current-1;
					}
					
					if( effect ){
						layoutManager.setDrawing( this );
						prepareDropDraw = true;
					}
					else{
						layoutManager.setDrawing( null );
						prepareDropDraw = false;
					}
					
					sideAboveMouse = getSideDockableBeneathMouse();
					mainPanel.repaint();
				}
			};
			// System.out.println(result.toSummaryString());
			return result;

		}
		else {
			return null;
		}
	}

	@Override
	public void addOrientingDockStationListener( OrientingDockStationListener listener ){
		orientingListeners.add( listener );
	}

	@Override
	public void removeOrientingDockStationListener( OrientingDockStationListener listener ){
		orientingListeners.remove( listener );
	}

	@Override
	public Orientation getOrientationOf( Dockable child ){
		return orientation;
	}

	/**
	 * Sets the number of dockables that this station will accept (max = -1
	 * indicates that there's no limit).
	 * 
	 * @param max
	 *            the number of dockables accepted
	 */
	public void setDockablesMaxNumber( int max ){
		dockablesMaxNumber = max;
	}

	/**
	 * Gets the number of dockables that this station will accept.
	 * 
	 * @return the number of dockables accepted (-1 if there is no maximum
	 *         number)
	 */
	public int getDockablesMaxNumber(){
		return dockablesMaxNumber;
	}

	/**
	 * Fires an {@link OrientingDockStationEvent}.
	 * 
	 * @param dockables
	 *            the items whose orientation changed
	 */
	protected void fireOrientingEvent(){
		final OrientingDockStationEvent event = new OrientingDockStationEvent( this );
		for( final OrientingDockStationListener listener : orientingListeners.toArray( new OrientingDockStationListener[orientingListeners.size()] ) ) {
			listener.changed( event );
		}
	}

	private void drop( ToolbarContainerDropInfo dropInfo ){
		// Note: Computation of index to insert drag dockable is not the
		// same between a move() and a drop(), because with a move() it is
		// as if the drag dockable were remove first then added again in the
		// list (Note: It's wird because in fact drag() is called after
		// move()...)

		// we check if there's dockable in this station
		if( getDockables().dockables().size() == 0 ) {
			// in this case, it's inevitably a drop() action
			drop( dropInfo.getItem(), 0 );
		}
		// if the dockable has to be drop at the same place (centered with
		// regards to itself): nothing to be done
		if( dropInfo.getItemPositionVSBeneathDockable() != Position.CENTER ) {
			final int indexBeneathMouse = indexOf( dropInfo.getDockableBeneathMouse() );
			int dropIndex;

			if( dropInfo.isMove() ) {
				move( dropInfo.getItem(), moveIndex( dropInfo, indexBeneathMouse ));
			}
			else {
				int increment = 0;
				if( (dropInfo.getSideDockableBeneathMouse() == Position.SOUTH) || (dropInfo.getSideDockableBeneathMouse() == Position.EAST) ) {
					increment++;
				}
				dropIndex = indexBeneathMouse + increment;
				drop( dropInfo.getItem(), dropIndex );
			}
		}
	}
	
	private int moveIndex( ToolbarContainerDropInfo dropInfo, int indexBeneathMouse ){
		switch( getOrientation() ){
			case VERTICAL:
				if( dropInfo.getItemPositionVSBeneathDockable() == Position.SOUTH ) {
					if( dropInfo.getSideDockableBeneathMouse() == Position.SOUTH ) {
						return indexBeneathMouse + 1;
					}
					else {
						return indexBeneathMouse;
					}
				}
				else {
					if( dropInfo.getSideDockableBeneathMouse() == Position.SOUTH ) {
						return indexBeneathMouse;
					}
					else {
						return indexBeneathMouse - 1;
					}
				}
			case HORIZONTAL:
				if( dropInfo.getItemPositionVSBeneathDockable() == Position.EAST ) {
					if( dropInfo.getSideDockableBeneathMouse() == Position.EAST ) {
						return indexBeneathMouse + 1;
					}
					else {
						return indexBeneathMouse;
					}
				}
				else {
					if( dropInfo.getSideDockableBeneathMouse() == Position.EAST ) {
						return indexBeneathMouse;
					}
					else {
						return indexBeneathMouse - 1;
					}
				}
			default:
				throw new IllegalStateException( "unknown orientation: " + getOrientation() );
		}
	}

	@Override
	public boolean drop( Dockable dockable, DockableProperty property ){
		if( property instanceof ToolbarContainerProperty ) {
			final ToolbarContainerProperty toolbar = (ToolbarContainerProperty) property;
			
			Path placeholder = toolbar.getPlaceholder();
			
			boolean hasPlaceholder = false;
			int index = -1;
			StationChildHandle presetHandle = null;
			
			if( placeholder != null ){
				hasPlaceholder = dockables.hasPlaceholder( placeholder );
				if( hasPlaceholder ){
					index = dockables.getDockableIndex( placeholder );
					presetHandle = dockables.getDockableAt( placeholder );
				}
			}
			
			if( index == -1 ){
				index = toolbar.getIndex();
			}

			if( toolbar.getSuccessor() != null ) {
				final DockablePlaceholderList list = getDockables();
				Dockable preset = null;
				
				if( presetHandle != null ){
					preset = presetHandle.asDockable();
				}
				else if( !hasPlaceholder && index >= 0 && index < list.dockables().size() ) {
					preset = list.dockables().get( index ).getDockable();
				}

				if( (preset != null) && (preset.asDockStation() != null) ) {
					return preset.asDockStation().drop( dockable, property.getSuccessor() );
				}
			}

			final int max = getDockables().dockables().size();
			if( hasPlaceholder && presetHandle == null && toolbar.getSuccessor() != null ){
				Dockable replacement = getToolbarStrategy().ensureToolbarLayer( this, dockable );
				
				DockController controller = getController();
				if( controller != null ){
					controller.freezeLayout();
				}
				try{
					add( replacement, -1, placeholder );
					if( replacement != dockable ){
						if( !replacement.asDockStation().drop( dockable, toolbar.getSuccessor() ) ){
							replacement.asDockStation().drop( dockable );
						}
					}
					return true;
				}
				finally{
					if( controller != null ){
						controller.meltLayout();
					}
				}
			}
			else{
				return drop( dockable, Math.max( 0, Math.min( max, index )) );
			}

		}
		return false;
	}

	/**
	 * Adds dockable to this station. The dockable must be a
	 * {@link ToolbarElementInterface} : if not, do nothing. The dockable is
	 * added at the last position.
	 * 
	 * @param dockable
	 *            a new child
	 */
	@Override
	public void drop( Dockable dockable ){
		// System.out.println(this.toString() +
		// "## drop(Dockable dockable )##");
		this.drop( dockable, getDockables().dockables().size() );
	}

	/**
	 * Inserts dockable to this station at the given index. The
	 * dockable must be a {@link ToolbarElementInterface}: if not, do nothing.
	 * 
	 * @param dockable
	 *            a new child
	 * @param index the group of the child  
	 * @return true if dropping was successfull
	 */
	private boolean drop( Dockable dockable, int index ){
		// System.out.println(this.toString()
		// + "## drop(Dockable dockable, int index )##");
		return add( dockable, index );
	}

	private void move( Dockable dockable, int indexWhereInsert ){
		// System.out.println(this.toString() + "## move() ## ==> ");
		if( getToolbarStrategy().isToolbarPart( dockable ) ) {
			final DockController controller = getController();
			try {
				if( controller != null ) {
					controller.freezeLayout();
				}
				add( dockable, indexWhereInsert );
			}
			finally {
				if( controller != null ) {
					controller.meltLayout();
				}
			}
		}

	}

	@Override
	public void move( Dockable dockable, DockableProperty property ){
	}

	@Override
	public boolean canDrag( Dockable dockable ){
		// System.out.println(this.toString()
		// + "## canDrag(Dockable dockable) ## " + this.toString());
		return true;
	}

	@Override
	public void drag( Dockable dockable ){
		// System.out.println(this.toString() +
		// "## drag(Dockable dockable) ##");
		if( dockable.getDockParent() != this ) {
			throw new IllegalArgumentException( "The dockable cannot be dragged, it is not child of this station." );
		}
		remove( dockable );
	}

	@Override
	public boolean canReplace( Dockable old, Dockable next ){
		if( old.getClass() == next.getClass() ) {
			return true;
		}
		else {
			return false;
		}
	}

	@Override
	public void replace( Dockable old, Dockable next ){
		// System.out.println(this.toString()
		// + "## replace(Dockable old, Dockable next) ## "
		// + this.toString());
		DockUtilities.checkLayoutLocked();
		final DockController controller = getController();
		if( controller != null ) {
			controller.freezeLayout();
		}
		final int index = indexOf( old );
		remove( old );
		// the child is a TollbarGroupDockStation because canReplace()
		// ensure it
		add( next, index );
		controller.meltLayout();
	}

	@Override
	public void replace( DockStation old, Dockable next ){
		// System.out.println(this.toString()
		// + "## replace(DockStation old, Dockable next) ## "
		// + this.toString());
		replace( old.asDockable(), next );
	}

	@Override
	public String getFactoryID(){
		return ToolbarContainerDockStationFactory.ID;
	}

	@Override
	public Component getComponent(){
		return mainPanel;
	}

	@Override
	protected void callDockUiUpdateTheme() throws IOException{
		DockUI.updateTheme( this, new ToolbarContainerDockStationFactory() );
	}

	/**
	 * Gets the panel which contains dockables
	 * 
	 * @return the panel which contains dockables
	 */
	public JPanel getContainerPanel(){
		return containerPanel;
	}

	/**
	 * Gets the {@link ToolbarStrategy} that is currently used by this station.
	 * 
	 * @return the strategy, never null
	 */
	public ToolbarStrategy getToolbarStrategy(){
		final SilentPropertyValue value = new SilentPropertyValue( ToolbarStrategy.STRATEGY, getController() );
		final ToolbarStrategy result = value.getValue();
		value.setProperties( (DockController) null );
		return result;
	}

	@Override
	public boolean accept( Dockable child ){
		if( dockablesMaxNumber == -1 ) {
			return true;
		}
		else if( dockables.dockables().size() >= dockablesMaxNumber ) {
			return false;
		}
		else {
			return true;
		}
	}

	@Override
	public boolean accept( DockStation station ){
		return true;
	}

	@Override
	public String toString(){
		return this.getClass().getSimpleName() + '@' + Integer.toHexString( hashCode() );
	}

	/**
	 * Updates one of the lists containing dockables.
	 * 
	 * @param list
	 *            the new list
	 * @param bind
	 *            whether the new list should be
	 *            {@link DockablePlaceholderList#bind() bound}
	 */
	private void setDockables( DockablePlaceholderList list, boolean bind ){
		if( getController() != null ) {
			final DockablePlaceholderList oldList = getDockables();
			oldList.setStrategy( null );
			oldList.unbind();
		}

		dockables = list;

		if( (getController() != null) && bind ) {
			list.bind();
			list.setStrategy( getPlaceholderStrategy() );
		}
	}

	/**
	 * Gets the dockables in the station
	 * 
	 * @return the dockables associated with the station
	 */
	public DockablePlaceholderList getDockables(){
		return dockables;
	}

	/**
	 * Gets the orientation of dockables in the station
	 * 
	 * @return the orientation
	 */
	@Override
	public Orientation getOrientation(){
		return orientation;
	}

	/**
	 * Sets the orientation of dockables in the station
	 * 
	 * @param orientation
	 *            the orientation
	 */
	@Override
	public void setOrientation( Orientation orientation ){
		this.orientation = orientation;
		fireOrientingEvent();
	}

	/**
	 * Gets the index of a child.
	 * 
	 * @param dockable
	 *            the child which is searched
	 * @return the index of dockable or -1 if it was not found
	 */
	private int indexOf( Dockable dockable ){
		for( int i = 0; i < dockables.dockables().size(); i++ ) {
			if( dockables.dockables().get( i ).getDockable() == dockable ) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * Removes the child with the given index from this station.
* Note: clients may need to invoke {@link DockController#freezeLayout()} * and {@link DockController#meltLayout()} to ensure noone else adds or * removes Dockables. * * @param index * the index of the child that will be removed */ private void remove( Dockable dockable ){ DockUtilities.checkLayoutLocked(); final DockHierarchyLock.Token token = DockHierarchyLock.acquireUnlinking( this, dockable ); try { final int index = indexOf( dockable ); final DockablePlaceholderList.Filter dockables = getDockables().dockables(); listeners.fireDockableRemoving( dockable ); dockable.setDockParent( null ); final StationChildHandle childHandle = dockables.get( index ); getDockables().remove( index ); getContainerPanel().remove( childHandle.getDisplayer().getComponent() ); childHandle.destroy(); mainPanel.getContentPane().revalidate(); mainPanel.getContentPane().repaint(); listeners.fireDockableRemoved( dockable ); fireDockablesRepositioned( index ); } finally { token.release(); } } /** * Add one dockable at the index position. The dockable can be a * {@link ToolbarItemDockable}, {@link ToolbarDockStation} or a * {@link ToolbarGroupDockStation} (see method accept()). All the * ComponentDockable extracted from the element are merged together and * wrapped in a {@link ToolbarDockStation} before to be added at index * position * * @param dockable * Dockable to add * @param index * Index where add dockable * @return true if dropping was successfull */ protected boolean add( Dockable dockable, int index ){ return add( dockable, index, null ); } protected boolean add( Dockable dockable, int index, Path placeholder ){ // System.out.println(this.toString() // + "## add( Dockable dockable, int index ) ##"); DockUtilities.ensureTreeValidity( this, dockable ); DockUtilities.checkLayoutLocked(); final ToolbarStrategy strategy = getToolbarStrategy(); if( strategy.isToolbarPart( dockable ) ) { Dockable replacement = strategy.ensureToolbarLayer( this, dockable ); if( replacement != dockable ){ replacement.asDockStation().drop( dockable ); dockable = replacement; } final DockHierarchyLock.Token token = DockHierarchyLock.acquireLinking( this, dockable ); try { listeners.fireDockableAdding( dockable ); final DockablePlaceholderList.Filter dockables = getDockables().dockables(); final StationChildHandle handle = new StationChildHandle( this, displayer, dockable, title ); if( placeholder != null ){ index = getDockables().put( placeholder, handle ); } else{ dockables.add( index, handle ); } handle.updateDisplayer(); insertAt( handle, index ); dockable.setDockParent( this ); listeners.fireDockableAdded( dockable ); fireDockablesRepositioned( index + 1 ); } finally { token.release(); } mainPanel.revalidate(); mainPanel.repaint(); return true; } return false; } private void insertAt( StationChildHandle handle, int index ){ final Dockable dockable = handle.getDockable(); dockable.setDockParent( this ); getContainerPanel().add( handle.getDisplayer().getComponent(), index ); mainPanel.getContentPane().revalidate(); mainPanel.getContentPane().repaint(); } /** * Replaces displayer with a new {@link DockableDisplayer}. * * @param displayer * the displayer to replace, must actually be shown on this * station */ protected void discard( DockableDisplayer displayer ){ final int index = indexOf( displayer.getDockable() ); final StationChildHandle handle = getDockables().dockables().get( index ); getContainerPanel().remove( displayer.getComponent() ); handle.updateDisplayer(); insertAt( handle, index ); } /** * This panel is used as base of the station. All children of the station * have this panel as parent too. It allows to draw arbitrary figures over * the base panel * * @author Herve Guillaume */ protected class OverpaintablePanelBase extends OverpaintablePanel { /** * Creates a new panel */ public OverpaintablePanelBase(){ setBackground(new Color(100, 100, 100)); containerPanel = createPanel(); // content.setBounds( 0, 0, content.getPreferredSize().width, // content.getPreferredSize().height ); // this.setPreferredSize( new Dimension( // content.getPreferredSize().width, // content.getPreferredSize().height ) ); setBasePane( containerPanel ); setContentPane( containerPanel ); setSolid( true ); getContentPane().revalidate(); getContentPane().repaint(); } @Override protected void paintOverlay( Graphics g ){ paintRemoval( g ); final DefaultStationPaintValue paint = getPaint(); Rectangle rectangleAreaBeneathMouse; if( prepareDropDraw ) { if( indexBeneathMouse != -1 ) { // WARNING: This rectangle stands for the component beneath // mouse. His coordinates are in the frame of reference his // direct parent: getPanel(areaBeneathMouse). // So we need to translate this rectangle in the frame of // reference of the overlay panel, which is the same that // the base pane final Rectangle rectComponentBeneathMouse = getDockables().dockables().get( indexBeneathMouse ).getDisplayer().getComponent().getBounds(); // this rectangle stands for the panel which holds the // mouse. // The return rectangle is in the frame of reference of his // direct parent which is the content of the overlay pane rectangleAreaBeneathMouse = getContainerPanel().getBounds(); // Translation rectComponentBeneathMouse.translate( rectangleAreaBeneathMouse.x, rectangleAreaBeneathMouse.y ); switch( getOrientation() ){ case VERTICAL: int y; if( sideAboveMouse == Position.NORTH ) { y = rectComponentBeneathMouse.y; } else { y = rectComponentBeneathMouse.y + rectComponentBeneathMouse.height; } paint.drawInsertionLine( g, rectComponentBeneathMouse.x, y, rectComponentBeneathMouse.x + rectComponentBeneathMouse.width, y ); break; case HORIZONTAL: int x; if( sideAboveMouse == Position.WEST ) { x = rectComponentBeneathMouse.x; } else { x = rectComponentBeneathMouse.x + rectComponentBeneathMouse.width; } paint.drawInsertionLine( g, x, rectComponentBeneathMouse.y, x, rectComponentBeneathMouse.y + rectComponentBeneathMouse.height ); } } else { // the container pane is empty paint.drawDivider( g, getContainerPanel().getBounds() ); } } } private void paintRemoval( Graphics g ){ if( removal != null ){ for( StationChildHandle handle : dockables.dockables() ){ if( handle.getDockable() == removal ){ Rectangle bounds = handle.getDisplayer().getComponent().getBounds(); getPaint().drawRemoval( g, bounds, bounds ); break; } } } } @Override public String toString(){ return this.getClass().getSimpleName() + '@' + Integer.toHexString( hashCode() ); } } /** * Gets a {@link StationPaint} which is used to paint some lines onto this * station. Use a {@link DefaultStationPaintValue#setDelegate(StationPaint) * delegate} to exchange the paint. * * @return the paint */ public DefaultStationPaintValue getPaint(){ return paint; } @Override public void setDockParent( DockStation station ){ DockStation old = getDockParent(); if( old != null ) old.removeDockStationListener( visibleListener ); super.setDockParent(station); if( station != null ) station.addDockStationListener( visibleListener ); visibility.fire(); } @Override public void setController( DockController controller ){ if( getController() != controller ) { if( getController() != null ) { unbind( dockables ); } super.setController( controller ); paint.setController( controller ); // we catch the DockTitleManager (one by controller) // effect of getVersion(...): we catch the DockTitleVersion // associated // with TITLE_ID. If none exist: a new one is created and registered // in DockTitleManager. If no default factory is registered in the // version TITLE_ID, so an new one is registered (in our case a // BasicDockTitle.FACTORY). displayerFactory.setController( controller ); layoutManager.setController( controller ); if( controller == null ) { title = null; } else { title = controller.getDockTitleManager().getVersion( TITLE_ID, BasicDockTitleFactory.FACTORY ); } displayer.setController( controller ); placeholderStrategy.setProperties( controller ); if( controller != null ) { bind( dockables, title ); } visibility.fire(); } } private void unbind( DockablePlaceholderList list ){ list.unbind(); for( final StationChildHandle handle : list.dockables() ) { handle.setTitleRequest( null ); } } private void bind( DockablePlaceholderList list, DockTitleVersion title ){ list.bind(); for( final StationChildHandle handle : list.dockables() ) { handle.setTitleRequest( title, true ); } } /** * This listener is added to the parent of this station and will forward an event to * {@link ToolbarContainerDockStation#visibility} if the visibility of the station changes. * @author Benjamin Sigg */ private class VisibleListener extends DockStationAdapter{ @Override public void dockableShowingChanged( DockStation station, Dockable dockable, boolean visible ) { if( dockable == ToolbarContainerDockStation.this ){ visibility.fire(); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy