/* * 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.action.actions; import java.awt.event.KeyEvent; import java.util.HashMap; import java.util.Map; import javax.swing.Icon; import javax.swing.KeyStroke; import bibliothek.extension.gui.dock.preference.editor.KeyStrokeEditor; import bibliothek.gui.DockController; import bibliothek.gui.Dockable; import bibliothek.gui.dock.DockElement; import bibliothek.gui.dock.action.ActionContentModifier; import bibliothek.gui.dock.action.DockAction; import bibliothek.gui.dock.disable.DisablingStrategy; import bibliothek.gui.dock.event.DockHierarchyEvent; import bibliothek.gui.dock.event.DockHierarchyListener; import bibliothek.gui.dock.event.KeyboardListener; import bibliothek.gui.dock.station.LayoutLocked; /** * A simple implementation of {@link DockAction}. This action uses for * all associated {@link Dockable Dockables} the same settings. * @author Benjamin Sigg */ public abstract class SimpleDockAction extends AbstractStandardDockAction implements SharingStandardDockAction { /** the icons that are used by this action */ private Map<ActionContentModifier, Icon> icons = new HashMap<ActionContentModifier, Icon>(); /** Text of the action */ private String text; /** Tooltip for buttons showing this action */ private String tooltip; /** Whether this action can be triggered or not */ private boolean enabled = true; /** allows to invoke this event by the keyboard, might be <code>null</code> */ private KeyStroke accelerator; /** whether to globally listen for all key events */ private boolean globalAccelerator = false; /** the {@link Dockable} which is represented by this action and for which drag and drop support may be enabled */ private Dockable representative; private Map<Dockable, DockableKeyForwarder> forwarders = new HashMap<Dockable, DockableKeyForwarder>(); /** * Creates a new action * @param monitorDisabling whether the current {@link DisablingStrategy} should be monitored */ public SimpleDockAction( boolean monitorDisabling ){ super( monitorDisabling ); } @Override protected void bound( Dockable dockable ){ super.bound( dockable ); DockableKeyForwarder forwarder = new DockableKeyForwarder( dockable ); forwarders.put( dockable, forwarder ); } @Override protected void unbound( Dockable dockable ){ super.unbound( dockable ); DockableKeyForwarder forwarder = forwarders.remove( dockable ); forwarder.destroy(); } public Icon getIcon( Dockable dockable, ActionContentModifier modifier ){ return icons.get( modifier ); } public String getText( Dockable dockable ) { return text; } public String getText() { return text; } public void setText( String text ) { this.text = text; fireActionTextChanged( getBoundDockables() ); } public String getTooltipText( Dockable dockable ) { return getTooltipText(); } public String getTooltipText(){ if( accelerator == null ) return tooltip; String acceleratorText = KeyStrokeEditor.toString( accelerator, true ); if( tooltip == null ) return acceleratorText; else return tooltip + " (" + acceleratorText + ")"; } public String getTooltip() { return tooltip; } public void setTooltip( String tooltip ) { this.tooltip = tooltip; fireActionTooltipTextChanged( getBoundDockables() ); } @Override public boolean isEnabled( Dockable dockable ) { return enabled && super.isEnabled( dockable ); } public boolean isEnabled() { return enabled; } public void setEnabled( boolean enabled ) { if( this.enabled != enabled ){ this.enabled = enabled; fireActionEnabledChanged( getBoundDockables() ); } } public Icon getIcon(){ return icons.get( ActionContentModifier.NONE ); } public void setIcon( Icon icon ) { setIcon( ActionContentModifier.NONE, icon ); } public ActionContentModifier[] getIconContexts( Dockable dockable ){ return icons.keySet().toArray( new ActionContentModifier[ icons.size() ] ); } public Icon getDisabledIcon() { return icons.get( ActionContentModifier.DISABLED ); } public void setDisabledIcon( Icon icon ) { setIcon( ActionContentModifier.DISABLED, icon ); } /** * Gets the icon which is shown if the conditions of <code>modifier</code> are met. * @param modifier the conditions to met * @return the icon to show or <code>null</code> if not set */ public Icon getIcon( ActionContentModifier modifier ){ return icons.get( modifier ); } /** * Sets the icon that is to be used when the conditions of <code>modifier</code> are met. * @param modifier the conditions to met * @param icon the icon to use or <code>null</code> */ public void setIcon( ActionContentModifier modifier, Icon icon ){ if( icon == null ){ icons.remove( modifier ); } else{ icons.put( modifier, icon ); } fireActionIconChanged( modifier, getBoundDockables() ); } public void setDockableRepresentation( Dockable dockable ){ if( this.representative != dockable ){ this.representative = dockable; fireActionRepresentativeChanged( getBoundDockables() ); } } public Dockable getDockableRepresentation( Dockable dockable ){ return representative; } public Dockable getDockableRepresentation(){ return representative; } public KeyStroke getAccelerator(){ return accelerator; } public void setAccelerator( KeyStroke accelerator ){ this.accelerator = accelerator; fireActionTooltipTextChanged( getBoundDockables() ); } public void setAcceleratorIsGlobal( boolean global ) { this.globalAccelerator = global; } public boolean isAcceleratorGlobal() { return globalAccelerator; } /** * Called when the user hit the {@link #setAccelerator(KeyStroke) accelerator}. * This method directly calls <code>trigger( dockable )</code>, subclasses * might override this method to further analyze <code>event</code>. * @param event the triggering event * @param dockable the source of the event * @return <code>true</code> if this action could do anything, <code>false</code> * if this action was not able to react in any way to the event. */ protected boolean trigger( KeyEvent event, Dockable dockable ){ return trigger( dockable ); } /** * Listens to all {@link KeyEvent}s concerning one {@link Dockable}. * @author Benjamin Sigg */ @LayoutLocked( locked=false ) private class DockableKeyForwarder implements KeyboardListener, DockHierarchyListener{ /** the element which is observed by this listener */ private Dockable dockable; /** the controller which is currently observed by this forwarder, can be <code>null</code> */ private DockController controller; /** whether this forwarder has been destroyed */ private boolean destroyed = false; /** * Creates a new forwarder. * @param dockable the element for which the calls will be forwarded */ public DockableKeyForwarder( Dockable dockable ){ this.dockable = dockable; dockable.addDockHierarchyListener( this ); setController( dockable.getController() ); } public void hierarchyChanged( DockHierarchyEvent event ){ // do nothing } public void controllerChanged( DockHierarchyEvent event ){ setController( dockable.getController() ); } private void setController( DockController controller ){ if( this.controller != null ) this.controller.getKeyboardController().removeListener( this ); if( destroyed ){ this.controller = null; } else{ this.controller = controller; } if( this.controller != null ){ this.controller.getKeyboardController().addListener( this ); } } /** * Removes all listeners added by this forwarder. */ public void destroy(){ destroyed = true; setController( null ); dockable.removeDockHierarchyListener( this ); } /** * Calls {@link DockAction#trigger(Dockable)} * if the event matches the accelerator-{@link KeyStroke}. * @param element the element on which the event occurred * @param event the event * @return <code>true</code> if the event has been consumed */ private boolean forward( DockElement element, KeyEvent event ){ if( accelerator != null ){ if( accelerator.equals( KeyStroke.getKeyStrokeForEvent( event ) )){ return trigger( event, dockable ); } } return false; } public boolean keyPressed( DockElement element, KeyEvent event ){ return forward( element, event ); } public boolean keyReleased( DockElement element, KeyEvent event ){ return forward( element, event ); } public boolean keyTyped( DockElement element, KeyEvent event ){ return forward( element, event ); } public DockElement getTreeLocation(){ if( isAcceleratorGlobal() ){ return null; } else{ return dockable; } } @Override public String toString(){ return getClass().getSimpleName() + " -> " + dockable.getTitleText() + " -> " + getText(); } } }