/* * 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) 2007 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.control; import bibliothek.gui.DockController; import bibliothek.gui.DockStation; import bibliothek.gui.Dockable; import bibliothek.gui.dock.accept.DockAcceptance; import bibliothek.gui.dock.control.relocator.DockRelocatorEvent; import bibliothek.gui.dock.control.relocator.VetoableDockRelocatorAdapter; import bibliothek.gui.dock.event.DockRegisterAdapter; /** * An observer of a {@link DockController}. The remover * ensures that there is no dockable {@link DockStation} with only one * or none child. * @author Benjamin Sigg */ public class SingleParentRemover{ /** the controller using the remover */ private DockController controller; /** observers a {@link DockRelocator} and searches for changes */ private DockRelocatorObserver dockRelocatorObserver = new DockRelocatorObserver(); /** observers a {@link DockRegister} and searches for changes */ private DockRegisterObserver dockRegisterObserver = new DockRegisterObserver(); /** state, ensures that no station is tested more than once in a run */ private boolean onTest = false; /** * Commands this remover to observe <code>controller</code>. * @param controller a controller to observe */ public void install( DockController controller ){ if( this.controller != null ){ throw new IllegalStateException( "a controller is already installed" ); } this.controller = controller; controller.getRelocator().addVetoableDockRelocatorListener( dockRelocatorObserver ); controller.getRegister().addDockRegisterListener( dockRegisterObserver ); testAll( controller ); } /** * Commands this remover that it should no longer observe * <code>controller</code>. * @param controller a controller */ public void uninstall( DockController controller ){ if( this.controller != controller ){ throw new IllegalArgumentException( "controller is not installed" ); } controller.getRelocator().removeVetoableDockRelocatorListener( dockRelocatorObserver ); controller.getRegister().removeDockRegisterListener( dockRegisterObserver ); this.controller = null; } /** * Tests all stations of <code>controller</code> and removes * as many of them as possible * @param controller the controller to test */ public void testAll( DockController controller ){ if( onTest ) return; try{ onTest = true; controller.getRegister().setStalled( true ); int index = 0; while( index < controller.getRegister().getStationCount() ){ if( test( controller.getRegister().getStation( index ))){ index = 0; } else index++; } } finally{ controller.getRegister().setStalled( false ); onTest = false; } } /** * Tells whether <code>station</code> should be automatically * removed or just be ignored. * @param station a station to test * @return <code>true</code> if the station may be removed * by this remover, <code>false</code> otherwise. */ protected boolean shouldTest( DockStation station ){ return true; } /** * Tries to replace <code>station</code> with its only child or * remove <code>station</code> if it has no children at all. If the * parent of <code>station</code> refuses to accept the replacement * or <code>station</code> refuses to let its child go, nothing will * happen. * @param station the station to test * @return whether the station was replaced or removed */ protected boolean test( DockStation station ){ DockController controller = station.getController(); if( controller != null ){ controller.getRegister().setStalled( true ); controller.getHierarchyLock().setConcurrent( true ); } try{ if( !shouldTest( station )) return false; if( station.getDockableCount() > 1 ) return false; Dockable transform = station.asDockable(); if( transform == null ) return false; DockStation parent = transform.getDockParent(); if( parent == null ) return false; if( parent.getController() == null ) return false; if( station.getDockableCount() == 0 ){ if( !parent.canDrag( transform )) return false; parent.drag( transform ); return true; } else{ Dockable dockable = station.getDockable( 0 ); if( !station.canDrag( dockable )) return false; if( !parent.accept( dockable ) || !dockable.accept( parent ) ) return false; if( !parent.canReplace( transform, dockable )) return false; DockAcceptance acceptance = station.getController().getAcceptance(); if( acceptance != null ){ if( !acceptance.accept( parent, dockable )) return false; } station.drag( dockable ); parent.replace( transform.asDockStation(), dockable ); return true; } } finally{ if( controller != null ){ controller.getHierarchyLock().setConcurrent( false ); controller.getRegister().setStalled( false ); } } } /** * Calls {@link SingleParentRemover#testAll(DockController)} * if the structure of the dock-tree changes. * @author Benjamin Sigg */ private class DockRegisterObserver extends DockRegisterAdapter{ @Override public void dockableCycledRegister( DockController controller, Dockable dockable ) { testAll( controller ); } @Override public void dockableRegistered( DockController controller, Dockable dockable ){ if( !controller.getRelocator().isOnPut() ){ testAll( controller ); } } @Override public void dockableUnregistered( DockController controller, Dockable dockable ) { if( !controller.getRelocator().isOnPut() ){ testAll( controller ); } } @Override public void dockStationRegistered( final DockController controller, DockStation station ){ controller.getHierarchyLock().onRelease( new Runnable(){ public void run(){ testAll( controller ); } }); } } /** * Calls {@link SingleParentRemover#testAll(DockController)} * if the structure of the dock-tree changes. * @author Benjamin Sigg */ private class DockRelocatorObserver extends VetoableDockRelocatorAdapter{ @Override public void dropped( DockRelocatorEvent event ){ testAll( controller ); } } }