/* * 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 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.disable; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import bibliothek.gui.DockController; import bibliothek.gui.Dockable; import bibliothek.gui.dock.DockElement; import bibliothek.gui.dock.action.DockAction; import bibliothek.gui.dock.event.DockHierarchyEvent; import bibliothek.gui.dock.event.DockHierarchyListener; import bibliothek.gui.dock.util.PropertyValue; /** * This class offers a convenient way to connect a {@link DockAction} with the current * {@link DisablingStrategy}. It only requires the {@link DockAction} to call {@link #bind(Dockable)} * and {@link #unbind(Dockable)} in order to observe a {@link Dockable}. * @author Benjamin Sigg */ public abstract class ActionDisablingStrategyObserver { private DockAction action; /** all the observers that are currently monitoring {@link Dockable}s to find {@link DockController}s */ private Map<Dockable, DockableObserver> dockables = new HashMap<Dockable, DockableObserver>(); /** all the observers that are currently monitoring {@link DockController}s. */ /* This might be a little overkill, but it's the way to ensure correctness in even the strangest settings */ private Map<DockController, ControllerObserver> controllers = new HashMap<DockController, ControllerObserver>( 1 ); /** * Creates a new observer. * @param action the action whose disabled state will be monitored. */ public ActionDisablingStrategyObserver( DockAction action ){ if( action == null ){ throw new IllegalArgumentException( "action must not be null" ); } this.action = action; } /** * Must be called to inform this observer that <code>dockable</code> is to be observed. Can be * called multiple times. * @param dockable the item to observe */ public void bind( Dockable dockable ){ DockableObserver observer = dockables.get( dockable ); if( observer == null ){ observer = new DockableObserver( dockable ); dockables.put( dockable, observer ); } observer.inc(); } /** * Must be called to inform this observer that <code>dockable</code> is no longer to be observed. * Can be called multiple times. * @param dockable the item to observe */ public void unbind( Dockable dockable ){ DockableObserver observer = dockables.get( dockable ); if( observer != null ){ observer.dec(); if( observer.destroy() ){ dockables.remove( dockable ); } } } private void set( DockController oldController, DockController newController, Dockable dockable ){ if( oldController != newController ){ if( oldController != null ){ ControllerObserver observer = controllers.get( oldController ); observer.remove( dockable ); if( observer.destroy() ){ controllers.remove( oldController ); } } if( newController != null ){ ControllerObserver observer = controllers.get( newController ); if( observer == null ){ observer = new ControllerObserver( newController ); controllers.put( newController, observer ); } observer.add( dockable ); } } } /** * Called if the disabled state of the action changed. * @param dockable the dockable for which the state changed * @param disabled the new state */ protected abstract void setDisabled( Dockable dockable, boolean disabled ); /** * Called if an entire set of {@link Dockable}s changed their state. * @param dockable the items that changed their state * @param disabled the new state */ protected abstract void setDisabled( Set<Dockable> dockable, boolean disabled ); /** * Tells whether the action of <code>dockable</code> is disabled. * @param dockable the element whose action state is searched * @return <code>true</code> if the action is disabled, <code>false</code> otherwise */ public boolean isDisabled( Dockable dockable ){ DockController controller = dockable.getController(); if( controller != null ){ ControllerObserver observer = controllers.get( controller ); if( observer != null ){ return observer.isDisabled( dockable ); } } return false; } /** * Observes a single {@link Dockable} to find its current {@link DockController}. * @author Benjamin Sigg */ private class DockableObserver implements DockHierarchyListener{ private Dockable dockable; private DockController controller; private int count = 0; public DockableObserver( Dockable dockable ){ this.dockable = dockable; dockable.addDockHierarchyListener( this ); DockController controller = dockable.getController(); if( controller != null ){ bind( controller ); } } private void bind( DockController controller ){ set( this.controller, controller, dockable ); this.controller = controller; } public void inc(){ count++; } public void dec(){ count--; } public boolean destroy(){ if( count <= 0 ){ dockable.removeDockHierarchyListener( this ); bind( null ); return true; } return false; } public void controllerChanged( DockHierarchyEvent event ){ bind( dockable.getController() ); } public void hierarchyChanged( DockHierarchyEvent event ){ // ignore } } /** * Observers a {@link DockController} to find its current {@link DisablingStrategy} and uses * the strategy to get the state of several {@link Dockable}s. * @author Benjamin Sigg */ private class ControllerObserver extends PropertyValue<DisablingStrategy> implements DisablingStrategyListener{ private Set<Dockable> dockables = new HashSet<Dockable>(); public ControllerObserver( DockController controller ){ super( DisablingStrategy.STRATEGY ); setProperties( controller ); } @Override protected void valueChanged( DisablingStrategy oldValue, DisablingStrategy newValue ){ if( oldValue != null ){ oldValue.removeDisablingStrategyListener( this ); } if( newValue != null ){ newValue.addDisablingStrategyListener( this ); Set<Dockable> enabled = new HashSet<Dockable>(); Set<Dockable> disabled = new HashSet<Dockable>(); for( Dockable item : dockables ){ if( newValue.isDisabled( item, action )){ disabled.add( item ); } else{ enabled.add( item ); } } if( !enabled.isEmpty() ){ setDisabled( enabled, false ); } if( !disabled.isEmpty() ){ setDisabled( disabled, false ); } } else{ setDisabled( dockables, false ); } } public void changed( DockElement item ){ Dockable dockable = item.asDockable(); if( dockable != null && dockables.contains( dockable )){ setDisabled( dockable, getValue().isDisabled( dockable, action ) ); } } public boolean isDisabled( Dockable dockable ){ DisablingStrategy strategy = getValue(); if( strategy != null ){ return strategy.isDisabled( dockable, action ); } return false; } public void add( Dockable dockable ){ DisablingStrategy strategy = getValue(); if( strategy != null ){ setDisabled( dockable, strategy.isDisabled( dockable, action ) ); } dockables.add( dockable ); } public void remove( Dockable dockable ){ setDisabled( dockable, false ); dockables.remove( dockable ); } public boolean destroy(){ if( dockables.isEmpty() ){ setProperties( (DockController)null ); return true; } return false; } } }