/* * 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 * rvguillaume@hotmail.com * FR - France * * Benjamin Sigg * benjamin_sigg@gmx.ch * CH - Switzerland */ package bibliothek.gui.dock.station.toolbar.group; import java.awt.Component; import java.awt.Rectangle; import java.awt.event.ComponentListener; import javax.swing.SwingUtilities; import bibliothek.gui.DockController; import bibliothek.gui.Dockable; import bibliothek.gui.dock.ExpandableToolbarItemStrategy; import bibliothek.gui.dock.ToolbarGroupDockStation; import bibliothek.gui.dock.station.StationChildHandle; import bibliothek.gui.dock.station.toolbar.title.ColumnDockActionSource; import bibliothek.gui.dock.title.DockTitle; import bibliothek.gui.dock.toolbar.expand.ExpandableToolbarItemStrategyListener; import bibliothek.gui.dock.toolbar.expand.ExpandedState; import bibliothek.gui.dock.util.PropertyValue; /** * This is a helper class enabling support for {@link ExpandedState} of the children of a {@link ToolbarGroupDockStation}. * This class provides all the tasks that may be useful: * <ul> * <li>It makes sure that during drag and drop operations the dragged dockable changes its state if necessary</li> * <li>It provides a {@link ColumnDockActionSource} to allow implementation of a {@link DockTitle} showing * actions for the different columns.</li> * </ul> * @author Benjamin Sigg */ public class ToolbarGroupExpander { private Actions actions; private ToolbarGroupDockStation station; private DockController controller; private ColumnHandler columnHandler = new ColumnHandler(); private StrategyHandler strategyHandler = new StrategyHandler(); private PropertyValue<ExpandableToolbarItemStrategy> strategy = new PropertyValue<ExpandableToolbarItemStrategy>( ExpandableToolbarItemStrategy.STRATEGY ){ @Override protected void valueChanged( ExpandableToolbarItemStrategy oldValue, ExpandableToolbarItemStrategy newValue ){ if( oldValue != null ) { oldValue.removeExpandedListener( strategyHandler ); } if( newValue != null ) { newValue.addExpandedListener( strategyHandler ); columnHandler.validateAll(); } } }; /** * Creates a new expander * @param station the station for which this expander will work */ public ToolbarGroupExpander( ToolbarGroupDockStation station ){ this.station = station; setController( station.getController() ); station.getColumnModel().addListener( new ToolbarColumnModelListener<Dockable,StationChildHandle>(){ @Override public void removed( ToolbarColumnModel<Dockable,StationChildHandle> model, ToolbarColumn<Dockable,StationChildHandle> column, int index ){ column.removeListener( columnHandler ); } @Override public void inserted( ToolbarColumnModel<Dockable,StationChildHandle> model, ToolbarColumn<Dockable,StationChildHandle> column, int index ){ column.addListener( columnHandler ); } } ); } /** * Sets the {@link DockController} in whose realm this expander works * @param controller the controller */ public void setController( DockController controller ){ if( this.controller != controller ) { if( actions != null ) { actions.setModel( null ); actions.destroy(); actions = null; } this.controller = controller; strategy.setProperties( controller ); if( controller != null ) { actions = new Actions( controller ); actions.setModel( station.getColumnModel() ); } } } /** * Gets a {@link ColumnDockActionSource} which allows users to change the {@link ExpandedState} of an entire * column of {@link Dockable}s. * @return the source */ public ColumnDockActionSource getActions(){ return actions; } private ExpandableToolbarItemStrategy getStrategy(){ return strategy.getValue(); } private class StrategyHandler implements ExpandableToolbarItemStrategyListener { @Override public void enablementChanged( Dockable item, ExpandedState state, boolean enabled ){ ToolbarColumn<Dockable,StationChildHandle> column = station.getColumnModel().getColumn( item ); if( column != null ) { columnHandler.validate( column ); } } @Override public void expanded( Dockable item ){ // ignore } @Override public void shrunk( Dockable item ){ // ignore } @Override public void stretched( Dockable item ){ // ignore } } /** * Ensures that all the items of a column have the same {@link ExpandedState}. */ private class ColumnHandler implements ToolbarColumnListener<Dockable,StationChildHandle> { @Override public void inserted( ToolbarColumn<Dockable,StationChildHandle> column, StationChildHandle item, final Dockable dockable, int index ){ final ExpandableToolbarItemStrategy strategy = getStrategy(); if( strategy != null ) { int count = 0; int length = column.getDockableCount(); ExpandedState state = null; while( count < length && state == null ) { if( count != index ) { state = strategy.getState( column.getDockable( count++ ) ); } else { count++; } } if( state != null ) { if( controller != null ) { final ExpandedState newState = state; controller.getHierarchyLock().onRelease( new Runnable(){ @Override public void run(){ set( strategy, dockable, newState ); actions.update( dockable ); } } ); } else { set( strategy, dockable, state ); } } } } @Override public void removed( ToolbarColumn<Dockable,StationChildHandle> column, StationChildHandle item, Dockable dockable, int index ){ // ignore } public void validateAll(){ ToolbarColumnModel<Dockable,StationChildHandle> model = station.getColumnModel(); for( int i = 0, n = model.getColumnCount(); i < n; i++ ) { validate( model.getColumn( i ) ); } } /** * Tries to ensure that all items in <code>column</code> have the same {@link ExpandedState}, namely the * {@link ExpandedState} of the first item. * @param column the column to validate */ public void validate( ToolbarColumn<Dockable,StationChildHandle> column ){ ExpandableToolbarItemStrategy strategy = getStrategy(); if( strategy != null ) { int index = 0; int length = column.getDockableCount(); ExpandedState state = null; while( index < length && state == null ) { state = strategy.getState( column.getDockable( index++ ) ); } while( index < length ) { Dockable item = column.getDockable( index++ ); set( strategy, item, state ); } } } private void set( ExpandableToolbarItemStrategy strategy, Dockable item, ExpandedState state ){ if( strategy.isEnabled( item, state ) ) { strategy.setState( item, state ); return; } ExpandedState smaller = state; while( smaller != smaller.smaller() ) { smaller = smaller.smaller(); if( strategy.isEnabled( item, smaller ) ) { strategy.setState( item, smaller ); return; } } ExpandedState larger = state; while( larger != larger.larger() ) { larger = larger.larger(); if( strategy.isEnabled( item, larger ) ) { strategy.setState( item, larger ); return; } } } } /** * The actions that are shown over the columns. */ private class Actions extends ExpandToolbarGroupActions<StationChildHandle> { public Actions( DockController controller ){ super( controller, station ); } @Override protected void uninstallListener( StationChildHandle item, ComponentListener listener ){ item.getDockable().getComponent().removeComponentListener( listener ); } @Override protected void installListener( StationChildHandle item, ComponentListener listener ){ item.getDockable().getComponent().addComponentListener( listener ); } @Override protected Rectangle getBoundaries( StationChildHandle item ){ Rectangle result = getBoundaries( item.getDockable().getComponent() ); Rectangle title = null; if( item.getTitle() != null ) { title = getBoundaries( item.getTitle().getComponent() ); if( result == null ){ result = title; } else{ result = result.union( title ); } } return result; } private Rectangle getBoundaries( Component component ){ Rectangle bounds = component.getBounds(); if( SwingUtilities.isDescendingFrom( component, station.getComponent() ) ) { return SwingUtilities.convertRectangle( component.getParent(), bounds, station.getComponent() ); } else { return null; } } } }