/* * 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.common.intern; import java.awt.Component; import java.awt.Dimension; import java.util.HashMap; import java.util.Map; import javax.swing.SwingUtilities; import bibliothek.gui.DockStation; import bibliothek.gui.Dockable; import bibliothek.gui.dock.common.CControl; import bibliothek.gui.dock.common.CFocusHistory; import bibliothek.gui.dock.common.CGrid; import bibliothek.gui.dock.common.CLocation; import bibliothek.gui.dock.common.CStation; import bibliothek.gui.dock.common.ColorMap; import bibliothek.gui.dock.common.EnableableItem; import bibliothek.gui.dock.common.FontMap; import bibliothek.gui.dock.common.action.CAction; import bibliothek.gui.dock.common.event.CDockableLocationListener; import bibliothek.gui.dock.common.event.CDockablePropertyListener; import bibliothek.gui.dock.common.event.CDockableStateListener; import bibliothek.gui.dock.common.event.CDoubleClickListener; import bibliothek.gui.dock.common.event.CFocusListener; import bibliothek.gui.dock.common.event.CKeyboardListener; import bibliothek.gui.dock.common.event.CVetoClosingEvent; import bibliothek.gui.dock.common.event.CVetoClosingListener; import bibliothek.gui.dock.common.grouping.DockableGrouping; import bibliothek.gui.dock.common.intern.action.CloseActionSource; import bibliothek.gui.dock.common.intern.station.CommonDockStation; import bibliothek.gui.dock.common.layout.RequestDimension; import bibliothek.gui.dock.common.location.CExtendedModeLocation; import bibliothek.gui.dock.common.mode.CLocationMode; import bibliothek.gui.dock.common.mode.CLocationModeManager; import bibliothek.gui.dock.common.mode.ExtendedMode; import bibliothek.gui.dock.control.focus.DefaultFocusRequest; import bibliothek.gui.dock.control.focus.FocusRequest; import bibliothek.gui.dock.disable.DisablingStrategy; import bibliothek.gui.dock.event.VetoableDockFrontendEvent; import bibliothek.gui.dock.title.DockTitle; import bibliothek.util.Filter; import bibliothek.util.FrameworkOnly; import bibliothek.util.Todo; import bibliothek.util.Todo.Compatibility; import bibliothek.util.Todo.Priority; import bibliothek.util.Todo.Version; /** * An abstract implementation of {@link CDockable}. Contains methods to * work with listeners and with {@link CAction}s. * @author Benjamin Sigg */ public abstract class AbstractCDockable implements CDockable { /** the location of this dockable */ private CLocation location = null; /** the graphical representation of this dockable */ private CommonDockable dockable; /** the preferred parent of this dockable */ private CStation<?> workingArea; /** the control managing this dockable */ private CControlAccess control; /** unique id of this {@link CDockable} */ private String uniqueId; /** whether this element likes to have the same height all the time */ private boolean resizeLockedVertically = false; /** whether this element likes to have the same width all the time */ private boolean resizeLockedHorizontally = false; /** whether to remain visible when minimized and unfocused or not */ private boolean sticky = false; /** whether {@link #sticky} can be switched by the user */ private boolean stickySwitchable = true; /** the preferred size when minimized */ private Dimension minimizeSize = new Dimension( -1, -1 ); /** the preferred size of this {@link CDockable} */ private RequestDimension resizeRequest; /** the colors associated with this dockable */ private ColorMap colors = new ColorMap( this ); /** the fonts associated with this dockable */ private FontMap fonts = new FontMap( this ); /** the actions that are shown by other modules */ private Map<String, CAction> actions = new HashMap<String, CAction>(); /** whether the {@link DockTitle} should not be created */ private boolean titleShown = true; /** whether a single tab is shown */ private boolean singleTabShown = false; /** the listeners that were added to this dockable */ protected CListenerCollection listenerCollection = new CListenerCollection(); /** handles the {@link CDockableLocationListener}s */ private CDockableLocationListenerManager locationListenerManager; /** support class to fire {@link CVetoClosingEvent}s */ private ControlVetoClosingListener vetoClosingListenerConverter; /** Source that contains the action that closes this dockable */ private CloseActionSource close = new CloseActionSource( this ); /** The default locations for the available {@link ExtendedMode}s. */ private Map<ExtendedMode, CLocation> defaultLocations = new HashMap<ExtendedMode, CLocation>( 4 ); /** The component which should be focused */ private Component focusComponent; /** All the items that are enabled */ private int enabled = EnableableItem.ALL.getFlag(); /** Tells how this {@link CDockable} tries to automatically group itself with other dockables */ private DockableGrouping grouping; /** * Creates a new dockable */ protected AbstractCDockable(){ // nothing } @Override public String toString(){ return getClass().getSimpleName() + "[unique id=" + uniqueId + "]"; } /** * Creates the {@link CommonDockable} that is associated with this dockable, called the first * time the {@link CommonDockable} is required for an operation. * @return the new dockable */ protected abstract CommonDockable createCommonDockable(); /** * Initializes this CDockable. * @param dockable the representation of this <code>CDockable</code>, not <code>null</code> */ protected void init( CommonDockable dockable ){ if( this.dockable != null ) throw new IllegalStateException( "dockable already set" ); if( dockable == null ) throw new NullPointerException( "dockable is null" ); this.dockable = dockable; } /** * Gets the action source which might show a single action that closes * this dockable. * @return the close source */ protected CloseActionSource getClose(){ return close; } /** * Gets access to the controller. * @return access or <code>null</code> */ protected CControlAccess control(){ return control; } public void addCDockableStateListener( CDockableStateListener listener ){ listenerCollection.addCDockableStateListener( listener ); } public void addCDockablePropertyListener( CDockablePropertyListener listener ) { listenerCollection.addCDockablePropertyListener( listener ); } public void addCDockableLocationListener( CDockableLocationListener listener ){ if( locationListenerManager == null ){ locationListenerManager = new CDockableLocationListenerManager( this ); } boolean has = listenerCollection.hasCDockableLocationListeners(); listenerCollection.addCDockableLocationListener( listener ); if( !has ){ locationListenerManager.setListener( listenerCollection.getCDockableLocationListener() ); } } public void removeCDockableStateListener( CDockableStateListener listener ){ listenerCollection.removeCDockableStateListener( listener ); if( locationListenerManager != null && !listenerCollection.hasCDockableLocationListeners() ){ locationListenerManager.setListener( null ); } } public void removeCDockablePropertyListener( CDockablePropertyListener listener ) { listenerCollection.removeCDockablePropertyListener( listener ); } public void removeCDockableLocationListener( CDockableLocationListener listener ){ listenerCollection.removeCDockableLocationListener( listener ); } public void addFocusListener( CFocusListener listener ){ listenerCollection.addFocusListener( listener ); } public void removeFocusListener( CFocusListener listener ){ listenerCollection.removeFocusListener( listener ); } public void addKeyboardListener( CKeyboardListener listener ){ listenerCollection.addKeyboardListener( listener ); } public void removeKeyboardListener( CKeyboardListener listener ){ listenerCollection.removeKeyboardListener( listener ); } public void addDoubleClickListener( CDoubleClickListener listener ){ listenerCollection.addDoubleClickListener( listener ); } public void removeDoubleClickListener( CDoubleClickListener listener ){ listenerCollection.removeDoubleClickListener( listener ); } public void addVetoClosingListener( CVetoClosingListener listener ){ boolean empty = !listenerCollection.hasVetoClosingListeners(); listenerCollection.addVetoClosingListener( listener ); if( empty && control != null ){ control.getOwner().intern().addVetoableListener( getVetoClosingListenerConverter() ); } } public void removeVetoClosingListener( CVetoClosingListener listener ){ listenerCollection.removeVetoClosingListener( listener ); if( !listenerCollection.hasVetoClosingListeners() ){ if( control != null && vetoClosingListenerConverter != null ){ control.getOwner().intern().removeVetoableListener( vetoClosingListenerConverter ); vetoClosingListenerConverter = null; } } } private ControlVetoClosingListener getVetoClosingListenerConverter(){ if( vetoClosingListenerConverter == null ){ vetoClosingListenerConverter = new ControlVetoClosingListener( control.getOwner(), listenerCollection.getVetoClosingListener() ){ @Override protected CDockable[] getCDockables( VetoableDockFrontendEvent event ){ for( Dockable dockable : event ){ if( dockable == intern() ){ return new CDockable[]{ AbstractCDockable.this }; } } return null; } }; } return vetoClosingListenerConverter; } /** * Gets the list of state listeners. * @return the stateListeners * @deprecated subclasses should use {@link CListenerCollection#getCDockableStateListener()} * of {@link #listenerCollection} if they want to fire an event */ @Deprecated protected CDockableStateListener[] stateListeners(){ return listenerCollection.getCDockableStateListeners(); } /** * Gets the list of property listeners. * @return the stateListeners * @deprecated subclasses should use {@link CListenerCollection#getCDockablePropertyListener()} * of {@link #listenerCollection} if they want to fire an event */ @Deprecated protected CDockablePropertyListener[] propertyListeners(){ return listenerCollection.getCDockablePropertyListeners(); } public void setVisible( boolean visible ){ if( control == null ) throw new IllegalStateException( "This CDockable does not know its CControl. Call CControl.addDockable(...) to connect this CDockable befor calling setVisible(...)." ); if( visible ){ control.show( this ); } else{ control.hide( this ); } } public boolean isVisible(){ if( control == null ) return false; else return control.isVisible( this ); } public boolean hasParent(){ if( control == null ){ return false; } else{ return control.hasParent( this ); } } @Deprecated @Todo( compatibility=Compatibility.BREAK_MAJOR, priority=Priority.ENHANCEMENT, target=Version.VERSION_1_1_3, description="remove this method" ) public boolean isDockableVisible(){ return intern().isDockableShowing(); } public boolean isShowing(){ return isDockableVisible(); } /** * Tries to focus this dockable. In order to gain focus this dockable must at least be visible, additional * restrictions exist, like gaining focus takes some time during which no other dockable must ask for the * focus. This method is best used to highlight existing dockables, but not while building * a new layout (look at methods like {@link CGrid#select(double, double, double, double, CDockable) CGrid.select} * to select a dockable in a stack while building a layout).<br> * There is no guarantee of success, this methods fails silently if the focus cannot be gained. */ public void toFront(){ if( isVisible() ){ FocusRequest request = new DefaultFocusRequest( intern(), null, false, true, false, true ); control.getOwner().intern().getController().setFocusedDockable( request ); } } /** * Tries to focus this dockable, and then ensures that the {@link Component} <code>focus</code> actually gets the focus.<br> * The behavior of this method is not defined for the case where <code>focus</code> is not a child of <code>this</code>.<br> * There is no guarantee of success, this methods fails silently if the focus cannot be gained. * @param focus a child of this dockable, not <code>null</code> * @see #toFront() */ public void toFront( Component focus ){ if( isVisible() ){ FocusRequest request = new DefaultFocusRequest( intern(), focus, true, true, true, true ); control.getOwner().intern().getController().setFocusedDockable( request ); } } public void setLocation( CLocation location ){ this.location = location; if( location != null ){ if( control != null && control.hasParent( this ) ){ control.getLocationManager().setLocation( intern(), location ); this.location = null; } } } public void setLocationsAside( CDockable dockable ){ if( dockable == null ){ throw new IllegalArgumentException( "dockable must not be null" ); } if( dockable == this ){ throw new IllegalArgumentException( "dockable must not be the same object as this" ); } if( dockable.getControl() == null ){ throw new IllegalArgumentException( "dockable is not registered at a CControl" ); } if( dockable.getControl() != getControl() ){ throw new IllegalStateException( "dockable is registered at another CControl" ); } if( dockable.getWorkingArea() != getWorkingArea() ){ throw new IllegalArgumentException( "dockable has another working-area as this" ); } CLocationModeManager locationManager = getControl().getLocationManager(); locationManager.setLocationAside( intern(), dockable.intern() ); CLocationMode mode = locationManager.getCurrentMode( dockable.intern() ); if( mode != null ){ setLocation( new CExtendedModeLocation( mode.getExtendedMode() ) ); } } public boolean setLocationsAside( Filter<CDockable> filter ){ if( getControl() == null ){ throw new IllegalStateException( "this dockable must be registered at a CControl" ); } CFocusHistory history = getControl().getFocusHistory(); CDockable dockable = history.getFirst( filter ); if( dockable == null ){ return false; } setLocationsAside( dockable ); return true; } public boolean setLocationsAsideFocused(){ boolean result = setLocationsAside( new Filter<CDockable>(){ public boolean includes( CDockable item ){ return item != AbstractCDockable.this && item.getWorkingArea() == workingArea && item.isVisible(); } }); if( !result ){ result = setLocationsAside( new Filter<CDockable>(){ public boolean includes( CDockable item ){ return item != AbstractCDockable.this && item.getWorkingArea() == workingArea; } }); } return result; } public CLocation getBaseLocation(){ if( control != null && isVisible() ){ return control.getLocationManager().getLocation( intern() ); } return location; } public CLocation getAutoBaseLocation( boolean noBackwardsTransformation ){ if( control == null || control.hasParent( this ) ){ return null; } return control.getAutoBaseLocation( this, noBackwardsTransformation ); } public void setExtendedMode( ExtendedMode extendedMode ){ if( extendedMode == null ) throw new NullPointerException( "extendedMode must not be null" ); if( extendedMode == ExtendedMode.EXTERNALIZED ){ if( !isExternalizable() ) return; } if( extendedMode == ExtendedMode.MINIMIZED ){ if( !isMinimizable() ) return; } if( extendedMode == ExtendedMode.MAXIMIZED ){ if( !isMaximizable() ) return; } if( extendedMode == ExtendedMode.NORMALIZED ){ if( !isNormalizeable() ){ return; } } CControlAccess control = control(); if( control != null ) control.getLocationManager().setMode( intern(), extendedMode ); } public ExtendedMode getExtendedMode(){ CControlAccess control = control(); if( control == null ) return null; return control.getLocationManager().getMode( intern() ); } /** * Sets an algorithm that defines how this dockable attempts to automatically group itself with * other dockables. * @param grouping the grouping behavior, can be <code>null</code> in which case this dockable * does not attempt to group itself. The default value of this property is <code>null</code>. */ public void setGrouping( DockableGrouping grouping ) { this.grouping = grouping; } public DockableGrouping getGrouping() { return grouping; } public void setWorkingArea( CStation<?> area ) { this.workingArea = area; } public CStation<?> getWorkingArea() { return workingArea; } /** * Tells whether width and height are locked. * @return <code>true</code> if width and height are locked */ public boolean isResizeLocked() { return resizeLockedVertically && resizeLockedHorizontally; } public boolean isResizeLockedVertically() { return resizeLockedVertically; } public boolean isResizeLockedHorizontally() { return resizeLockedHorizontally; } /** * Tells this {@link CDockable} which size it should have. The size will * be stored until it is read by {@link #getAndClearResizeRequest()}.<br> * If <code>process</code> is <code>true</code>, then this method will call * {@link CControl#handleResizeRequests()} in order to try to apply the requested size. * However, there are no guarantees that the requested size can be matched, or that * the request gets handled at all.<br> If this <code>CDockable</code> is not registered at a * {@link CControl}, then the request will remain unprocessed until this <code>CDockable</code> * is registered, and someone calls {@link CControl#handleResizeRequests()} on the new owner. * @param size the new preferred size, can be <code>null</code> to cancel an * earlier request * @param process whether to process all pending requests of all {@link CDockable} * registered at the {@link CControl} which is the owner of <code>this</code>. * Clients can set this parameter to <code>false</code> and call * {@link CControl#handleResizeRequests()} manually to process all pending * requests. * @see #setResizeRequest(RequestDimension, boolean) */ public void setResizeRequest( Dimension size, boolean process ){ resizeRequest = size == null ? null : new RequestDimension( size ); if( process && control != null ){ control.getOwner().handleResizeRequests(); } } /** * Tells this {@link CDockable} which size it should have. The size will * be stored until it is read by {@link #getAndClearResizeRequest()}.<br> * If <code>process</code> is <code>true</code>, then this method will call * {@link CControl#handleResizeRequests()} in order to try to apply the requested size. * However, there are no guarantees that the requested size can be matched, or that the * request gets handled at all.<br> If this <code>CDockable</code> is not registered at * a {@link CControl}, then the request will remain unprocessed until this <code>CDockable</code> * is registered, and someone calls {@link CControl#handleResizeRequests()} on the new owner. * @param size the new preferred size, can be <code>null</code> to cancel an * earlier request * @param process whether to process all pending requests of all {@link CDockable} * registered at the {@link CControl} which is the owner of <code>this</code>. * Clients can set this parameter to <code>false</code> and call * {@link CControl#handleResizeRequests()} manually to process all pending * requests. */ public void setResizeRequest( RequestDimension size, boolean process ){ resizeRequest = size == null ? null : new RequestDimension( size ); if( process && control != null ){ control.getOwner().handleResizeRequests(); } } public RequestDimension getAndClearResizeRequest() { RequestDimension result = resizeRequest; resizeRequest = null; return result; } /** * Sets whether this dockable likes to remain with the same size all the time. * @param resizeLocked <code>true</code> if the size of this dockable should * be kept as long as possible * @see #setResizeLockedHorizontally(boolean) * @see #setResizeLockedVertically(boolean) */ public void setResizeLocked( boolean resizeLocked ) { if( isResizeLocked() != resizeLocked ){ this.resizeLockedHorizontally = resizeLocked; this.resizeLockedVertically = resizeLocked; listenerCollection.getCDockablePropertyListener().resizeLockedChanged( this ); } } /** * Sets whether this dockable likes to remain with the same width all * the time. * @param resizeLockedHorizontally <code>true</code> if the width of * this dockable should be kept as long as possible */ public void setResizeLockedHorizontally( boolean resizeLockedHorizontally ) { if( this.resizeLockedHorizontally != resizeLockedHorizontally ){ this.resizeLockedHorizontally = resizeLockedHorizontally; listenerCollection.getCDockablePropertyListener().resizeLockedChanged( this ); } } /** * Sets whether this dockable likes to remain with the same height * all the time. * @param resizeLockedVertically <code>true</code> if the height * of this dockable should be kept as long as possible */ public void setResizeLockedVertically( boolean resizeLockedVertically ) { if( this.resizeLockedVertically != resizeLockedVertically ){ this.resizeLockedVertically = resizeLockedVertically; listenerCollection.getCDockablePropertyListener().resizeLockedChanged( this ); } } /** * Enables or disables a part of this dockable. Some effects are visible immediately, others * will need some time to show up. Usually disabling a part means that said part is shown in * some gray colors and won't react to any user input (e.g. to the mouse).<br> * Developers which need more accuracy in disabling items, should have a look at the * {@link DisablingStrategy}. * @param item what part of this {@link CDockable} should be enabled or disabled * @param enabled whether the part should be enabled */ public void setEnabled( EnableableItem item, boolean enabled ){ int flag = this.enabled; if( enabled ){ this.enabled = EnableableItem.add( this.enabled, item ); } else{ this.enabled = EnableableItem.remove( this.enabled, item ); } if( flag != this.enabled ){ listenerCollection.getCDockablePropertyListener().enabledChanged( this ); } } public boolean isEnabled( EnableableItem item ){ return EnableableItem.isEnabled( enabled, item ); } public void setSticky( boolean sticky ){ if( this.sticky != sticky ){ this.sticky = sticky; listenerCollection.getCDockablePropertyListener().stickyChanged( this ); } } public boolean isSticky(){ return sticky; } public void setStickySwitchable( boolean switchable ){ if( this.stickySwitchable != switchable ){ this.stickySwitchable = switchable; listenerCollection.getCDockablePropertyListener().stickySwitchableChanged( this ); } } public boolean isStickySwitchable(){ return stickySwitchable; } public void setMinimizedSize( Dimension size ) { minimizeSize = new Dimension( size.width, size.height ); listenerCollection.getCDockablePropertyListener().minimizeSizeChanged( this ); } /** * Always <code>true</code>, clients should not override this method unless they know exactly what they are doing. */ public boolean isNormalizeable(){ return true; } public Dimension getMinimizedSize() { return new Dimension( minimizeSize.width, minimizeSize.height ); } /** * Tells this {@link CDockable} whether to show or to hide its titles. * @param shown <code>true</code> if titles should be shown, <code>false</code> * if they should be hidden. */ public void setTitleShown( boolean shown ){ if( this.titleShown != shown ){ this.titleShown = shown; listenerCollection.getCDockablePropertyListener().titleShownChanged( this ); } } public boolean isTitleShown() { return titleShown; } /** * Tells this {@link CDockable} whether to show a single tab or not. * @param singleTabShown <code>true</code> if a single tab should be shown, * <code>false</code> otherwise * @see #isSingleTabShown() */ public void setSingleTabShown( boolean singleTabShown ){ if( this.singleTabShown != singleTabShown ){ this.singleTabShown = singleTabShown; listenerCollection.getCDockablePropertyListener().singleTabShownChanged( this ); } } public boolean isSingleTabShown(){ return singleTabShown; } /** * Gets the intern representation of this dockable. * @return the intern representation. */ public CommonDockable intern(){ if( dockable == null ){ init( createCommonDockable() ); } return dockable; } /** * Sets the default location for mode <code>mode</code> for this dockable. Note * that this location does not override any existing setting. This method can * be called either before or after making this dockable visible. It is * the client's responsibility to ensure that <code>location</code> is valid * together with <code>mode</code>. * @param mode the mode for which to store the default location * @param location the default location or <code>null</code> */ public void setDefaultLocation( ExtendedMode mode, CLocation location ){ if( mode == null ){ throw new IllegalArgumentException( "mode must not be null" ); } if( location == null ){ defaultLocations.remove( mode ); } else{ ExtendedMode locationMode = location.findMode(); if( locationMode == null ){ throw new IllegalArgumentException( "location does not carry enough information to find its mode" ); } if( !mode.getModeIdentifier().equals( locationMode.getModeIdentifier() )){ throw new IllegalArgumentException( "mode of location and \'mode\' do not have the same identifier" ); } defaultLocations.put( mode, location ); if( control != null ){ CLocationModeManager state = control.getLocationManager(); if( state.getLocation( intern(), mode ) == null ) state.setLocation( intern(), mode, location ); } } } /** * Gets an earlier set value of {@link #setDefaultLocation(ExtendedMode, CLocation)}. * @param mode the mode for which to search the default location * @return the location or <code>null</code> */ public CLocation getDefaultLocation( ExtendedMode mode ){ return defaultLocations.get( mode ); } /** * Gets the unique identifier that has been assigned to this {@link CDockable} by the {@link CControl}. Every * dockable has a unique identifier, but it may not be the same identifier as set by this client. * @return the unique identifier, it is set once this dockable is added to a {@link CControl} */ @FrameworkOnly protected String getDockableUniqueId(){ return uniqueId; } public CControlAccess getControlAccess(){ return control; } public void setControlAccess( CControlAccess control ){ if( this.control == control ) return; if( this.control != null ){ if( this.control.shouldStore( this ) == null ){ this.control.getLocationManager().remove( intern() ); } else{ this.control.getLocationManager().reduceToEmpty( intern() ); } this.control.link( this, null ); if( vetoClosingListenerConverter != null ){ this.control.getOwner().intern().removeVetoableListener( vetoClosingListenerConverter ); vetoClosingListenerConverter = null; } } this.control = control; if( control != null ){ if( uniqueId != null ){ control.getLocationManager().add( uniqueId, intern() ); } if( listenerCollection.hasVetoClosingListeners() ){ control.getOwner().intern().addVetoableListener( getVetoClosingListenerConverter() ); } control.link( this, new CDockableAccess(){ private ExtendedMode currentMode; public void informVisibility( boolean visible ) { listenerCollection.getCDockableStateListener().visibilityChanged( AbstractCDockable.this ); } public void informMode( ExtendedMode mode ) { if( currentMode != mode ){ currentMode = mode; CDockableStateListener forward = listenerCollection.getCDockableStateListener(); forward.extendedModeChanged( AbstractCDockable.this, mode ); } } public CFocusListener getFocusListener() { return listenerCollection.getFocusListener(); } public CKeyboardListener getKeyboardListener() { return listenerCollection.getKeyboardListener(); } public CDoubleClickListener getDoubleClickListener() { return listenerCollection.getDoubleClickListener(); } public void setUniqueId( String id ) { if( (id != null && !id.equals( uniqueId )) || (id == null && uniqueId != null) ){ if( AbstractCDockable.this.control != null && uniqueId != null ){ CLocationModeManager manager = AbstractCDockable.this.control.getLocationManager(); manager.remove( intern() ); } uniqueId = id; if( AbstractCDockable.this.control != null && id != null ){ CLocationModeManager manager = AbstractCDockable.this.control.getLocationManager(); manager.put( uniqueId, intern() ); for( Map.Entry<ExtendedMode, CLocation> location : defaultLocations.entrySet() ){ if( manager.getLocation( intern(), location.getKey() ) == null ) manager.setLocation( intern(), location.getKey(), location.getValue() ); } } } } public String getUniqueId() { return uniqueId; } public CLocation internalLocation( boolean reset ){ if( reset ){ CLocation loc = location; location = null; return loc; } else{ return location; } } }); } close.setControl( control ); } /** * Exchanges an action of this {@link CDockable}. The actions that are associated * with this <code>CDockable</code> through this method are not necessarily shown on the * title. They are used by other modules to create effects that are known * only to them. * @param key the key of the action, one of the <code>ACTION_KEY_xzy</code>-constants * defined in {@link CDockable} * @param action the new action, can be <code>null</code> which might force * back a default action (that depends on the module that uses <code>key</code>) */ public void putAction( String key, CAction action ){ CAction old = actions.put( key, action ); if( old != action ){ listenerCollection.getCDockablePropertyListener().actionChanged( this, key, old, action ); } } public CAction getAction( String key ) { return actions.get( key ); } public ColorMap getColors() { return colors; } public FontMap getFonts() { return fonts; } public Component getFocusComponent(){ return focusComponent; } /** * Sets the {@link Component} which should receive the focus when this <code>CDockable</code> is focused. Please note * that the focus will be transferred to this component every time the dockable lost the focus and gained the focus again. The * default behavior of re-focusing the last focus owner should be sufficient for most applications. * @param component the component to focus, can be <code>null</code>, should be a child of this <code>CDockable</code> */ public void setFocusComponent( Component component ){ this.focusComponent = component; } public CStation<?> getParentStation(){ DockStation parent = intern().getDockParent(); while( parent != null ){ if( parent instanceof CommonDockStation<?,?> ){ return ((CommonDockStation<?, ?>)parent).getStation(); } Dockable item = parent.asDockable(); if( item == null ){ return null; } parent = item.getDockParent(); } return null; } public CControl getControl(){ if( control == null ){ return null; } return control.getOwner(); } }