/* * 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) 2013 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 * * Benjamin Sigg * benjamin_sigg@gmx.ch * CH - Switzerland */ package bibliothek.gui.dock.common.behavior; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Rectangle; import bibliothek.gui.DockController; import bibliothek.gui.DockStation; import bibliothek.gui.Dockable; import bibliothek.gui.dock.ScreenDockStation; import bibliothek.gui.dock.SplitDockStation; import bibliothek.gui.dock.common.CControl; import bibliothek.gui.dock.common.CStation; import bibliothek.gui.dock.common.intern.CDockable; import bibliothek.gui.dock.event.DockStationAdapter; import bibliothek.gui.dock.station.LayoutLocked; import bibliothek.gui.dock.station.screen.ScreenDockWindow; /** * The {@link ExternalizingCGridAreaConfiguration} is a piece of code responsible for changing the behavior * of the framework such that externalized {@link CDockable} are not stacked, but put in a {@link ExternalizingCGridArea}.<br> * Clients can use {@link #install()}, {@link #installOn(CControl)} and {@link #uninstall()} to activate or * deactivate this configuration. * * @author Benjamin Sigg */ public class ExternalizingCGridAreaConfiguration { /** the control whose configuration this is */ private CControl control; /** algorithm for inserting {@link ExternalizingCGridArea}s */ private SplitInserter splitInserter; private ExternalizingCGridAreaFactory factory; /** whether this configuration is installed */ private boolean installed = false; /** * Creates a new {@link ExternalizingCGridAreaConfiguration} and {@link #install() installs} it. * @param control the control in whose realm this configuration is used * @return the new configuration */ public static ExternalizingCGridAreaConfiguration installOn( CControl control ){ ExternalizingCGridAreaConfiguration config = new ExternalizingCGridAreaConfiguration( control ); config.install(); return config; } /** * Creates a new configuration. * @param control the control for which this configuration will be used, not <code>null</code> */ public ExternalizingCGridAreaConfiguration( CControl control ){ if( control == null ){ throw new IllegalArgumentException( "control must not be null" ); } this.control = control; factory = new ExternalizingCGridAreaFactory( control ); } /** * Gets the listener that is responsible for inserting new {@link ExternalizingCGridArea}s. * @return the listener, never <code>null</code> */ protected SplitInserter getSplitInserter(){ if( splitInserter == null ){ splitInserter = createSplitInserter(); } return splitInserter; } /** * Creates a new instance of {@link SplitInserter}. * @return a new object, not <code>null</code> */ protected SplitInserter createSplitInserter(){ return new SplitInserter(); } /** * Gets the {@link CControl} for which this configuration is used. * @return the {@link CControl} */ public CControl getControl(){ return control; } /** * Creates a new {@link ExternalizingCGridArea}, the identifier of the new station is unique compared to all * the {@link CDockable}s that are currently registered at {@link #getControl() control}. * @return the new area, not <code>null</code> */ protected ExternalizingCGridArea createGridArea(){ return new ExternalizingCGridArea( control ); } /** * Activates this configuration. */ public void install(){ if( installed ){ throw new IllegalStateException( "already installed" ); } installed = true; getScreenDockStation().addDockStationListener( getSplitInserter() ); control.addSingleDockableFactory( ExternalizingCGridAreaFactory.PATTERN, factory ); } /** * Deactivates this configuration, existing {@link ExternalizingCGridArea}s will <b>not</b> be cleaned up. */ public void uninstall(){ if( !installed ){ throw new IllegalStateException( "not installed" ); } getScreenDockStation().removeDockStationListener( getSplitInserter() ); control.removeSingleDockableFactory( factory ); } /** * Gets the {@link DockStation} which was registered with the name {@link CControl#EXTERNALIZED_STATION_ID}. * @return the station which will be the parent of all the new {@link ExternalizingCGridArea}s */ protected DockStation getScreenDockStation(){ CStation<?> screen = control.getStation( CControl.EXTERNALIZED_STATION_ID ); return screen.getStation(); } /** * A listener that is added to a {@link ScreenDockStation}, every time some {@link Dockable} is added to said * station, a new {@link ExternalizingCGridArea} is created and inserted. * @author Benjamin Sigg */ @LayoutLocked(locked=false) protected class SplitInserter extends DockStationAdapter { public void dockableAdded( DockStation station, final Dockable dockable ){ if( !(dockable instanceof SplitDockStation) ) { EventQueue.invokeLater( new Runnable() { public void run() { checkAndReplace( dockable ); } }); } } private void checkAndReplace( Dockable dockable ){ DockStation station = dockable.getDockParent(); if( !(station instanceof ScreenDockStation) ) { return; } DockController controller = control.getController(); try { controller.freezeLayout(); ScreenDockStation screenDockStation = (ScreenDockStation)station; Dimension oldSize = dockable.getComponent().getSize(); ExternalizingCGridArea split = createGridArea(); control.addDockable( split ); station.replace( dockable, split.getStation() ); split.getStation().drop( dockable ); resizeDelayed( screenDockStation, split.getStation(), dockable, oldSize ); } finally { controller.meltLayout(); } } private void resizeDelayed( final ScreenDockStation station, final Dockable parent, final Dockable dockable, final Dimension oldSize ){ EventQueue.invokeLater( new Runnable() { public void run() { ScreenDockWindow window = station.getWindow( parent ); if( window != null ){ window.validate(); Dimension newSize = dockable.getComponent().getSize(); int dw = oldSize.width - newSize.width; int dh = oldSize.height - newSize.height; Rectangle bounds = window.getWindowBounds(); bounds.width += dw; bounds.height += dh; window.setWindowBounds( bounds ); } } } ); } }; }