package bibliothek.gui.dock.action; import java.util.Iterator; import bibliothek.gui.DockController; import bibliothek.gui.Dockable; import bibliothek.gui.dock.event.DockActionSourceListener; import bibliothek.gui.dock.event.DockHierarchyEvent; import bibliothek.gui.dock.event.DockHierarchyListener; /** * A {@link DockActionSource} which observes the hierarchy of a {@link bibliothek.gui.Dockable} * and changes its content using {@link bibliothek.gui.DockController#listOffers(bibliothek.gui.Dockable)}.<br> * Clients using this source must call {@link #bind()} to connect the source with its {@link Dockable}, * and {@link #unbind()} to free resources. * @author Benjamin Sigg */ public class HierarchyDockActionSource extends AbstractDockActionSource { /** The observed Dockable */ private Dockable dockable; /** The number of times this source was bound */ private int bound = 0; /** A listener to the hierarchy of {@link #dockable} and the source fetched from the controller */ private Listener listener = new Listener(); /** the source from which currently actions are fetched, can be <code>null</code> */ private DockActionSource source; /** * Creates a new source. * @param dockable the Dockable to observe */ public HierarchyDockActionSource( Dockable dockable ){ this.dockable = dockable; update(); } /** * Ensures that this source observes its Dockable. */ public void bind(){ if( bound == 0 ){ dockable.addDockHierarchyListener( listener ); update(); } bound++; } /** * Ensures that this source frees resources. */ public void unbind(){ bound--; if( bound == 0 ){ dockable.removeDockHierarchyListener( listener ); } } /** * Updates the list of actions known to this source. */ public void update(){ int oldSize = getDockActionCount(); if( source != null ){ source.removeDockActionSourceListener( listener ); source = null; } if( oldSize > 0 ) fireRemoved( 0, oldSize-1 ); DockController controller = dockable.getController(); if( controller != null ) source = controller.listOffers( dockable ); if( source != null && !listeners.isEmpty() ){ source.addDockActionSourceListener( listener ); } int newSize = getDockActionCount(); if( newSize > 0 ){ fireAdded( 0, newSize-1 ); } } @Override public void addDockActionSourceListener( DockActionSourceListener listener ){ if( listeners.isEmpty() && source != null ) source.addDockActionSourceListener( this.listener ); super.addDockActionSourceListener( listener ); } @Override public void removeDockActionSourceListener( DockActionSourceListener listener ){ super.removeDockActionSourceListener( listener ); if( listeners.isEmpty() && source != null ) source.removeDockActionSourceListener( this.listener ); } public DockAction getDockAction( int index ){ if( source == null ) throw new IllegalArgumentException( "index out of bounds" ); else return source.getDockAction( index ); } public int getDockActionCount(){ if( source == null ) return 0; else return source.getDockActionCount(); } public LocationHint getLocationHint(){ if( source == null ) return LocationHint.UNKNOWN; else return source.getLocationHint(); } public Iterator<DockAction> iterator(){ if( source == null ){ return new Iterator<DockAction>(){ public boolean hasNext(){ return false; } public DockAction next(){ return null; } public void remove(){ // ignore } }; } else{ return source.iterator(); } } /** * A listener used to observe the Dockable and the source of the enclosing * class. * @author Benjamin Sigg */ private class Listener implements DockHierarchyListener, DockActionSourceListener{ public void controllerChanged( DockHierarchyEvent event ){ update(); } public void hierarchyChanged( DockHierarchyEvent event ){ update(); } public void actionsAdded( DockActionSource source, int firstIndex, int lastIndex ){ fireAdded( firstIndex, lastIndex ); } public void actionsRemoved( DockActionSource source, int firstIndex, int lastIndex ){ fireRemoved( firstIndex, lastIndex ); } } }