All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bibliothek.gui.dock.common.CControl Maven / Gradle / Ivy

Go to download

DockingFrames is an open source Java Swing docking framework, licenced under LGPL 2.1. This is the same distribution as the original distribution (http://www.docking-frames.org/), only reinstalled in maven

There is a newer version: 1.1.2p20b.fix-1
Show newest version
/*
 * 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
 * [email protected]
 * CH - Switzerland
 */
package bibliothek.gui.dock.common;

import java.awt.Component;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;

import javax.swing.FocusManager;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.KeyStroke;

import bibliothek.extension.gui.dock.preference.PreferenceModel;
import bibliothek.extension.gui.dock.preference.PreferenceStorage;
import bibliothek.extension.gui.dock.theme.BubbleTheme;
import bibliothek.extension.gui.dock.theme.EclipseTheme;
import bibliothek.extension.gui.dock.theme.FlatTheme;
import bibliothek.gui.DockController;
import bibliothek.gui.DockFrontend;
import bibliothek.gui.DockStation;
import bibliothek.gui.DockTheme;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DockElement;
import bibliothek.gui.dock.DockFactory;
import bibliothek.gui.dock.FlapDockStation;
import bibliothek.gui.dock.ScreenDockStation;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.StackDockStation;
import bibliothek.gui.dock.action.DockAction;
import bibliothek.gui.dock.common.action.CloseActionFactory;
import bibliothek.gui.dock.common.action.util.CDefaultDockActionDistributor;
import bibliothek.gui.dock.common.event.CControlListener;
import bibliothek.gui.dock.common.event.CDockableAdapter;
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.CVetoClosingListener;
import bibliothek.gui.dock.common.event.CVetoFocusListener;
import bibliothek.gui.dock.common.event.ResizeRequestListener;
import bibliothek.gui.dock.common.group.CGroupBehavior;
import bibliothek.gui.dock.common.grouping.CGroupingBehavior;
import bibliothek.gui.dock.common.grouping.DefaultCGroupingBehavior;
import bibliothek.gui.dock.common.grouping.DockableGrouping;
import bibliothek.gui.dock.common.grouping.GroupingDockLocationListener;
import bibliothek.gui.dock.common.grouping.GroupingHistoryRewriter;
import bibliothek.gui.dock.common.intern.CControlAccess;
import bibliothek.gui.dock.common.intern.CControlFactory;
import bibliothek.gui.dock.common.intern.CDockFrontend;
import bibliothek.gui.dock.common.intern.CDockable;
import bibliothek.gui.dock.common.intern.CDockableAccess;
import bibliothek.gui.dock.common.intern.CListenerCollection;
import bibliothek.gui.dock.common.intern.CPlaceholderStrategy;
import bibliothek.gui.dock.common.intern.CancelDragAndDropOperation;
import bibliothek.gui.dock.common.intern.CommonDockable;
import bibliothek.gui.dock.common.intern.CommonMultipleDockableFactory;
import bibliothek.gui.dock.common.intern.CommonMultipleDockableLayout;
import bibliothek.gui.dock.common.intern.CommonSingleDockableFactory;
import bibliothek.gui.dock.common.intern.ControlVetoClosingListener;
import bibliothek.gui.dock.common.intern.ControlVetoFocusListener;
import bibliothek.gui.dock.common.intern.EfficientControlFactory;
import bibliothek.gui.dock.common.intern.MutableCControlRegister;
import bibliothek.gui.dock.common.intern.action.CActionImportanceOrder;
import bibliothek.gui.dock.common.intern.action.CActionOffer;
import bibliothek.gui.dock.common.intern.action.CButtonContentFilter;
import bibliothek.gui.dock.common.intern.station.CFlapLayoutManager;
import bibliothek.gui.dock.common.intern.station.CLockedResizeLayoutManager;
import bibliothek.gui.dock.common.intern.station.CScreenDockStationWindowClosingStrategy;
import bibliothek.gui.dock.common.intern.station.CommonDockStation;
import bibliothek.gui.dock.common.intern.station.CommonDockStationFactory;
import bibliothek.gui.dock.common.intern.ui.CDisablingStrategy;
import bibliothek.gui.dock.common.intern.ui.CSingleParentRemover;
import bibliothek.gui.dock.common.intern.ui.CommonSingleTabDecider;
import bibliothek.gui.dock.common.intern.ui.ExtendedModeAcceptance;
import bibliothek.gui.dock.common.intern.ui.StackableAcceptance;
import bibliothek.gui.dock.common.intern.ui.WorkingAreaAcceptance;
import bibliothek.gui.dock.common.layout.FullLockConflictResolver;
import bibliothek.gui.dock.common.layout.RequestDimension;
import bibliothek.gui.dock.common.mode.CLocationMode;
import bibliothek.gui.dock.common.mode.CLocationModeManager;
import bibliothek.gui.dock.common.mode.CMaximizedMode;
import bibliothek.gui.dock.common.mode.CMaximizedModeArea;
import bibliothek.gui.dock.common.mode.CStationContainerHistoryRewriter;
import bibliothek.gui.dock.common.mode.ExtendedMode;
import bibliothek.gui.dock.common.perspective.CControlPerspective;
import bibliothek.gui.dock.common.perspective.CDockablePerspective;
import bibliothek.gui.dock.common.perspective.CStackPerspective;
import bibliothek.gui.dock.common.perspective.CStationPerspective;
import bibliothek.gui.dock.common.perspective.CommonElementPerspective;
import bibliothek.gui.dock.common.perspective.DefaultMissingPerspectiveFactory;
import bibliothek.gui.dock.common.perspective.MissingPerspectiveStrategy;
import bibliothek.gui.dock.common.theme.ThemeMap;
import bibliothek.gui.dock.common.theme.eclipse.CommonEclipseThemeConnector;
import bibliothek.gui.dock.control.DockRegister;
import bibliothek.gui.dock.control.DockRelocatorMode;
import bibliothek.gui.dock.control.DockableSelector;
import bibliothek.gui.dock.control.focus.DefaultFocusStrategy;
import bibliothek.gui.dock.control.focus.FocusStrategyRequest;
import bibliothek.gui.dock.control.relocator.DefaultDockRelocator;
import bibliothek.gui.dock.disable.DisablingStrategy;
import bibliothek.gui.dock.displayer.SingleTabDecider;
import bibliothek.gui.dock.dockable.DockableMovingImageFactory;
import bibliothek.gui.dock.event.DockRegisterAdapter;
import bibliothek.gui.dock.event.DockableFocusEvent;
import bibliothek.gui.dock.event.DockableFocusListener;
import bibliothek.gui.dock.event.DoubleClickListener;
import bibliothek.gui.dock.event.KeyboardListener;
import bibliothek.gui.dock.facile.mode.Location;
import bibliothek.gui.dock.facile.mode.LocationModeManager;
import bibliothek.gui.dock.facile.station.split.ConflictResolver;
import bibliothek.gui.dock.facile.station.split.DefaultConflictResolver;
import bibliothek.gui.dock.focus.DockableSelection;
import bibliothek.gui.dock.frontend.FrontendEntry;
import bibliothek.gui.dock.frontend.MissingDockableStrategy;
import bibliothek.gui.dock.layout.DockSituationIgnore;
import bibliothek.gui.dock.layout.DockableProperty;
import bibliothek.gui.dock.perspective.PerspectiveDockable;
import bibliothek.gui.dock.perspective.PerspectiveElement;
import bibliothek.gui.dock.perspective.PerspectiveStation;
import bibliothek.gui.dock.station.Combiner;
import bibliothek.gui.dock.station.DisplayerFactory;
import bibliothek.gui.dock.station.StationPaint;
import bibliothek.gui.dock.station.flap.FlapWindow;
import bibliothek.gui.dock.station.layer.DockStationDropLayerFactory;
import bibliothek.gui.dock.station.screen.ScreenDockWindow;
import bibliothek.gui.dock.station.stack.StackDockPerspective;
import bibliothek.gui.dock.station.stack.StackDockStationFactory;
import bibliothek.gui.dock.station.stack.StackDockStationLayout;
import bibliothek.gui.dock.station.stack.action.DockActionDistributor;
import bibliothek.gui.dock.station.stack.menu.CombinedMenuContent;
import bibliothek.gui.dock.station.stack.tab.TabPane;
import bibliothek.gui.dock.station.support.PlaceholderStrategy;
import bibliothek.gui.dock.support.mode.HistoryRewriter;
import bibliothek.gui.dock.support.util.ApplicationResource;
import bibliothek.gui.dock.support.util.ApplicationResourceManager;
import bibliothek.gui.dock.themes.BasicTheme;
import bibliothek.gui.dock.themes.ColorScheme;
import bibliothek.gui.dock.themes.ThemeFactory;
import bibliothek.gui.dock.themes.basic.action.DockActionImportanceOrder;
import bibliothek.gui.dock.themes.border.BorderModifier;
import bibliothek.gui.dock.title.DockTitle;
import bibliothek.gui.dock.util.AWTComponentCaptureStrategy;
import bibliothek.gui.dock.util.BackgroundPaint;
import bibliothek.gui.dock.util.DirectWindowProvider;
import bibliothek.gui.dock.util.IconManager;
import bibliothek.gui.dock.util.NullWindowProvider;
import bibliothek.gui.dock.util.Priority;
import bibliothek.gui.dock.util.PropertyKey;
import bibliothek.gui.dock.util.WindowProvider;
import bibliothek.gui.dock.util.extension.Extension;
import bibliothek.gui.dock.util.extension.ExtensionManager;
import bibliothek.gui.dock.util.extension.ExtensionName;
import bibliothek.gui.dock.util.icon.DefaultIconScheme;
import bibliothek.gui.dock.util.property.ConstantPropertyFactory;
import bibliothek.gui.dock.util.text.DefaultTextScheme;
import bibliothek.util.Filter;
import bibliothek.util.Path;
import bibliothek.util.Version;
import bibliothek.util.xml.XElement;
import bibliothek.util.xml.XException;
import bibliothek.util.xml.XIO;

/**
 * Manages the interaction between {@link SingleCDockable}, {@link MultipleCDockable}
 * and {@link CStation}s.
* Clients which do no longer need a {@link CControl} can call {@link #destroy()} * to free resources.
* A CControl is an interface between the application and the framework. The task of * CControl is to provide access for actions that affect the entire realm. Such * actions may include: *
    *
  • add/remove a global listener.
  • *
  • add/remove/access {@link CDockable}s, {@link CStation}s and {@link CStationContainer}s.
  • *
  • add/remove/access factories for {@link CDockable}s.
  • *
  • read/write/apply the layout, this includes reading/writing from files.
  • *
  • store properties, like for example the {@link #setTheme(String) theme}.
  • *
* @author Benjamin Sigg * */ public class CControl { /** * A key for this {@link CControl}. Will be set with the highest priority. To be used * wherever a {@link DockController} but not a {@link CControl} is accessible. */ public static final PropertyKey CCONTROL = new PropertyKey( "ccontrol" ); /** * Name of an {@link ExtensionName} that adds extensions to this control. The extensions * are of type {@link Object} and are not actually used. Rather this extension informs * {@link Extension}s that a {@link CControl} has been created. */ public static final Path CCONTROL_EXTENSION = new Path( "dock.ccontrol" ); /** name of a parameter of an {@link ExtensionName} that points to this */ public static final String EXTENSION_PARAM = "control"; /** * {@link KeyStroke} used to change a {@link CDockable} into maximized-state, * or to go out of maximized-state when needed. */ public static final PropertyKey KEY_MAXIMIZE_CHANGE = new PropertyKey( "ccontrol.maximize_change" ); /** * {@link KeyStroke} used to change a {@link CDockable} into * maximized-state. */ public static final PropertyKey KEY_GOTO_MAXIMIZED = new PropertyKey( "ccontrol.goto_maximized" ); /** * {@link KeyStroke} used to change a {@link CDockable} into * normalized-state. */ public static final PropertyKey KEY_GOTO_NORMALIZED = new PropertyKey( "ccontrol.goto_normalized" ); /** * {@link KeyStroke} used to change a {@link CDockable} into * minimized-state. */ public static final PropertyKey KEY_GOTO_MINIMIZED = new PropertyKey( "ccontrol.goto_minimized" ); /** * {@link KeyStroke} used to change a {@link CDockable} into * externalized-state. */ public static final PropertyKey KEY_GOTO_EXTERNALIZED = new PropertyKey( "ccontrol.goto_externalized" ); /** * {@link KeyStroke} used to close a {@link CDockable}. */ public static final PropertyKey KEY_CLOSE = new PropertyKey( "ccontrol.close" ); /** * {@link KeyStroke} that can be hit during a drag and drop operation, that will cancel the operation (default is ESCAPE). */ public static final PropertyKey KEY_CANCEL_OPERATION = new PropertyKey( "ccontrol.cancel_dnd" ); /** * {@link ConflictResolver} used to determine what happens when there is * a conflict between two resize requests on a {@link SplitDockStation} like * {@link CGridArea}, {@link CWorkingArea} or {@link CContentArea}. * @see DefaultConflictResolver * @see FullLockConflictResolver */ public static final PropertyKey> RESIZE_LOCK_CONFLICT_RESOLVER = new PropertyKey>( "ccontrol.resize_lock_conflict_resolver", new ConstantPropertyFactory>( new DefaultConflictResolver()), true ); /** * This factory creates the actions that close dockables. */ public static final PropertyKey CLOSE_ACTION_FACTORY = new PropertyKey( "ccontrol.closeActionFactory", new ConstantPropertyFactory( CloseActionFactory.DEFAULT ), true ); /** * The grouping behavior defines how {@link Dockable}s tend to automatically group together. */ public static final PropertyKey GROUPING_BEHAVIOR = new PropertyKey( "ccontrol.groupingBehavior", new ConstantPropertyFactory( new DefaultCGroupingBehavior() ), true ); /** the unique id of the station that handles the externalized dockables */ public static final String EXTERNALIZED_STATION_ID = "external"; /** the unique id of the default-{@link CContentArea} created by this control */ public static final String CONTENT_AREA_STATIONS_ID = "ccontrol"; /** connection to the real DockingFrames */ private CDockFrontend frontend; /** strategy what to do when reading layout information of a missing dockable */ private MissingCDockableStrategy missingStrategy = MissingCDockableStrategy.PURGE; /** access to internal methods of some {@link CDockable}s */ private Map accesses = new HashMap(); /** a manager allowing the user to change the extended-state of some {@link CDockable}s */ private CLocationModeManager locationManager; /** the default location of newly opened {@link CDockable}s */ private CLocation defaultLocation; /** a list of available {@link DockTheme}s */ private ThemeMap themes; /** Access to the internal methods of this control */ private CControlAccess access = new Access(); /** A strategy that can create missing {@link CStationPerspective} */ private MissingPerspectiveStrategy missingPerspectiveStrategy = new DefaultMissingPerspectiveFactory(); /** manager used to store and read configurations */ private ApplicationResourceManager resources = new ApplicationResourceManager(); /** a list of listeners which are to be informed when this control is no longer in use */ private List hooks = new ArrayList(); /** factory used to create new elements for this control */ private CControlFactory factory; /** the {@link CDockable}s and other elements used by this control */ private MutableCControlRegister register; /** the list of listeners to this {@link CControl} */ private List listeners = new ArrayList(); /** the list of resize-listeners */ private List resizeListeners = new ArrayList(); /** the collection of global listeners */ private CListenerCollection listenerCollection = new CListenerCollection(); /** the preferences used by this instance of {@link CControl} */ private PreferenceStorage preferences = new PreferenceStorage(); /** the model which is used to translate between {@link #preferences} and this */ private PreferenceModel preferenceModel; /** if true, then minimizing a Dockable will automatically transfer focus to a not minimized Dockable */ private boolean transferFocusOnMinimize = true; /** * Creates a new control. Note that a control should know the main * window of the application, thus {@link CControl#CControl(WindowProvider)} * would be the better choice than this constructor. */ public CControl(){ this( new NullWindowProvider() ); } /** * Creates a new control * @param frame the main frame of the application, needed to create * dialogs for externalized {@link CDockable}s */ public CControl( JFrame frame ){ this( frame == null ? new NullWindowProvider() : new DirectWindowProvider( frame ) ); } /** * Creates a new control * @param restrictedEnvironment whether this application runs in a * restricted environment and is not allowed to listen for global events. * @deprecated it is not necessary to set the restrictedEnvironment parameter anymore, the framework * will choose a fitting value itself */ @Deprecated public CControl( boolean restrictedEnvironment ){ this( new NullWindowProvider() ); getController().setRestrictedEnvironment( restrictedEnvironment ); } /** * Creates a new control * @param window a provider for the main window of this application. Needed * to create dialogs for externalized {@link CDockable}s. Must not be null, but * its search method may return null */ public CControl( WindowProvider window ){ this( window, new EfficientControlFactory() ); } /** * Creates a new control * @param frame the main frame of the application, needed to create * dialogs for externalized {@link CDockable}s * @param restrictedEnvironment whether this application runs in a * restricted environment and is not allowed to listen for global events. * @deprecated it is not necessary to set the restrictedEnvironment parameter anymore, the framework * will choose a fitting value itself */ @Deprecated public CControl( JFrame frame, boolean restrictedEnvironment ){ this( frame == null ? new NullWindowProvider() : new DirectWindowProvider( frame ) ); getController().setRestrictedEnvironment( restrictedEnvironment ); } /** * Creates a new control * @param window a provider for the main window of this application. Needed * to create dialogs for externalized {@link CDockable}s. Must not be null, but * its search method may return null * @param restrictedEnvironment whether this application runs in a * restricted environment and is not allowed to listen for global events. * @deprecated it is not necessary to set the restrictedEnvironment parameter anymore, the framework * will choose a fitting value itself */ @Deprecated public CControl( WindowProvider window, boolean restrictedEnvironment ){ this( window ); getController().setRestrictedEnvironment( restrictedEnvironment ); } /** * Creates a new control * @param frame the main frame of the application, needed to create * dialogs for externalized {@link CDockable}s * @param factory a factory which is used to create new elements for this * control. */ public CControl( JFrame frame, CControlFactory factory ){ this( frame == null ? new NullWindowProvider() : new DirectWindowProvider( frame ), factory ); } /** * Creates a new control * @param window a provider for the main window of this application. Needed * to create dialogs for externalized {@link CDockable}s. Must not be null, but * its search method may return null * @param factory a factory which is used to create new elements for this * control. */ public CControl( WindowProvider window, CControlFactory factory ){ this( window, factory, true ); } /** * Creates a new control * @param window a provider for the main window of this application. Needed * to create dialogs for externalized {@link CDockable}s. Must not be null, but * its search method may return null * @param factory a factory which is used to create new elements for this * control. * @param init if true then this constructor calls {@link #init(WindowProvider, CControlFactory)}, * otherwise this constructor does nothing and returns immediately. Subclasses should call * {@link #init(WindowProvider, CControlFactory)} in that case. */ protected CControl( WindowProvider window, CControlFactory factory, boolean init ){ if( init ){ init( window, factory ); } } /** * Initializes the fields of this {@link CControl}. This method is called during construction * of this {@link CControl}. Subclasses may use {@link #CControl(WindowProvider, CControlFactory, boolean)} * to create an uninitialized {@link CControl} and then call this method by themselves. * @param window a provider for the main window of this application. Needed * to create dialogs for externalized {@link CDockable}s. Must not be null, but * its search method may return null * @param factory a factory which is used to create new elements for this * control. */ protected void init( WindowProvider window, CControlFactory factory ){ if( window == null ){ throw new IllegalArgumentException( "window must not be null, however its search method may return null" ); } this.factory = factory; register = factory.createRegister( this ); DockController controller = factory.createController( this ); controller.getProperties().set( CCONTROL, this, Priority.CLIENT ); controller.getProperties().finalize( CCONTROL ); controller.setSingleParentRemover( new CSingleParentRemover( this ) ); initExtensions( controller ); initFocusListeners( controller ); initInputListener( controller ); initTransferFocusOnMinimize( controller ); frontend = factory.createFrontend( access, controller ); frontend.setOwner( window ); frontend.setMissingDockableStrategy( new MissingDockableStrategy(){ public boolean shouldStoreHidden( String key ) { return shouldStore( key ); } public boolean shouldStoreShown( String key ) { return shouldStore( key ); } public boolean shouldCreate( DockFactory factory, L data ) { if( factory instanceof CommonMultipleDockableFactory && data instanceof CommonMultipleDockableLayout ){ return CControl.this.shouldCreate( ((CommonMultipleDockableFactory)factory).getFactory(), (CommonMultipleDockableLayout)data ); } return false; } }); setIgnoreWorkingForEntry( true ); frontend.setShowHideAction( false ); frontend.getController().addActionOffer( new CActionOffer( this ) ); frontend.getController().getRegister().addDockRegisterListener( new DockRegisterAdapter(){ @Override public void dockableRegistered( DockController controller, Dockable dockable ) { if( dockable instanceof CommonDockable ){ CDockable cdock = ((CommonDockable)dockable).getDockable(); CDockableAccess access = accesses.get( cdock ); if( access != null ){ access.informVisibility( true ); } for( CControlListener listener : listeners() ) listener.opened( CControl.this, cdock ); } } @Override public void dockableUnregistered( DockController controller, Dockable dockable ) { if( dockable instanceof CommonDockable ){ CDockable cdock = ((CommonDockable)dockable).getDockable(); CDockableAccess access = accesses.get( cdock ); if( access != null ){ access.informVisibility( false ); } for( CControlListener listener : listeners() ) listener.closed( CControl.this, cdock ); if( cdock instanceof MultipleCDockable ){ MultipleCDockable multiple = (MultipleCDockable)cdock; if( multiple.isRemoveOnClose() ){ removeDockable( multiple ); } } } } }); frontend.getController().getFocusController().addVetoListener( new ControlVetoFocusListener( this, listenerCollection.getVetoFocusListener() ) ); frontend.getController().getFocusController().setStrategy( new DefaultFocusStrategy( frontend.getController() ){ public Component getFocusComponent( FocusStrategyRequest request ){ Component mouseClicked = request.getMouseClicked(); Dockable dockable = request.getDockable(); if( mouseClicked != null ){ if( (mouseClicked.isFocusable() && !excluded( mouseClicked, request )) || focusable( mouseClicked, request )){ return mouseClicked; } } if( dockable instanceof CommonDockable ){ Component result = ((CommonDockable)dockable).getDockable().getFocusComponent(); if( result != null ){ return result; } } return super.getFocusComponent( request ); } }); frontend.addVetoableListener( new ControlVetoClosingListener( this, listenerCollection.getVetoClosingListener() ) ); frontend.getController().addAcceptance( new StackableAcceptance() ); frontend.getController().addAcceptance( new WorkingAreaAcceptance( access ) ); frontend.getController().addAcceptance( new ExtendedModeAcceptance( access ) ); initFactories(); themes = new ThemeMap( this ); initPersistentStorage(); initExtendedModes(); initProperties(); initIcons(); initTexts(); setTheme( ThemeMap.KEY_SMOOTH_THEME ); controller.getExtensions().load( new ExtensionName( CCONTROL_EXTENSION, Object.class, EXTENSION_PARAM, this ) ); } /** * Initializes additional {@link Extension}s and registers them at the * {@link ExtensionManager} of controller. * @param controller the controller for which additional extensions should be * loaded */ protected void initExtensions( DockController controller ){ ExtensionManager manager = controller.getExtensions(); String[] list = { "glass.eclipse.CGlassExtension", "bibliothek.gui.dock.toolbar.CToolbarExtension" }; for( String className : list ){ try { Class clazz = Class.forName( className ); Object extension = clazz.newInstance(); if( extension instanceof Extension ){ manager.add( (Extension)extension ); } } catch( ClassNotFoundException e ) { // ignore } catch( InstantiationException e ) { e.printStackTrace(); } catch( IllegalAccessException e ) { // ignore } } } /** * Creates and adds the listeners needed to track the focus. * @param controller the controller which will be observed */ private void initFocusListeners( DockController controller ){ controller.addDockableFocusListener( new DockableFocusListener(){ public void dockableFocused( DockableFocusEvent event ) { Dockable oldFocused = event.getOldFocusOwner(); Dockable newFocused = event.getNewFocusOwner(); if( oldFocused instanceof CommonDockable ){ CDockable oldC = ((CommonDockable)oldFocused).getDockable(); CDockableAccess access = accesses.get( oldC ); if( access != null ){ access.getFocusListener().focusLost( oldC ); } listenerCollection.getFocusListener().focusLost( oldC ); } if( newFocused instanceof CommonDockable ){ CDockable newC = ((CommonDockable)newFocused).getDockable(); CDockableAccess access = accesses.get( newC ); if( access != null ){ access.getFocusListener().focusGained( newC ); } listenerCollection.getFocusListener().focusGained( newC ); } } }); } /** * Adds a {@link CDockableStateListener} to this {@link CControl}, if a {@link CDockable} is * {@link ExtendedMode#MINIMIZED minimized}, another {@link Dockable} receives the focus. Subclasses * may override this method to disable or modify the feature. * @param controller the controller used by this {@link CControl} * @see #setTransferFocusOnMinimize(boolean) */ protected void initTransferFocusOnMinimize( DockController controller ){ addStateListener( new CDockableAdapter(){ @Override public void extendedModeChanged( CDockable dockable, ExtendedMode mode ){ if( transferFocusOnMinimize ){ if( mode == ExtendedMode.MINIMIZED ){ Dockable[] history = getController().getFocusHistory().getHistory(); for( int i = history.length-1; i >= 0; i-- ){ Dockable next = history[i]; if( next instanceof CommonDockable ){ CDockable cdockable = ((CommonDockable)next).getDockable(); if( cdockable.getExtendedMode() != ExtendedMode.MINIMIZED ){ getController().setFocusedDockable( cdockable.intern(), true ); break; } } } } } } }); } private void initInputListener( DockController controller ){ controller.getKeyboardController().addListener( new KeyboardListener(){ public boolean keyPressed( DockElement element, KeyEvent event ) { if( element instanceof CommonDockable ){ CDockable source = ((CommonDockable)element).getDockable(); CDockableAccess access = accesses.get( source ); if( access != null ){ if( access.getKeyboardListener().keyPressed( source, event )) return true; } return listenerCollection.getKeyboardListener().keyPressed( source, event ); } return false; } public boolean keyReleased( DockElement element, KeyEvent event ) { if( element instanceof CommonDockable ){ CDockable source = ((CommonDockable)element).getDockable(); CDockableAccess access = accesses.get( source ); if( access != null ){ if( access.getKeyboardListener().keyReleased( source, event )) return true; } return listenerCollection.getKeyboardListener().keyReleased( source, event ); } return false; } public boolean keyTyped( DockElement element, KeyEvent event ) { if( element instanceof CommonDockable ){ CDockable source = ((CommonDockable)element).getDockable(); CDockableAccess access = accesses.get( source ); if( access != null ){ if( access.getKeyboardListener().keyTyped( source, event )) return true; } return listenerCollection.getKeyboardListener().keyTyped( source, event ); } return false; } public DockElement getTreeLocation() { return null; } }); controller.getDoubleClickController().addListener( new DoubleClickListener(){ public boolean process( Dockable dockable, MouseEvent event ) { if( dockable instanceof CommonDockable ){ CDockable source = ((CommonDockable)dockable).getDockable(); CDockableAccess access = accesses.get( source ); if( access != null ){ if( access.getDoubleClickListener().clicked( source, event )) return true; } return listenerCollection.getDoubleClickListener().clicked( source, event ); } return false; } public DockElement getTreeLocation() { return null; } }); addKeyboardListener( new CancelDragAndDropOperation( this ) ); } /** * Sets up the {@link #locationManager}. */ private void initExtendedModes(){ locationManager = new CLocationModeManager( access ); HistoryRewriter validation = new CStationContainerHistoryRewriter( this ); locationManager.setHistoryRewriter( new GroupingHistoryRewriter( this, validation )); GroupingDockLocationListener groupingListener = new GroupingDockLocationListener( this ); getController().getRegister().addDockRegisterListener( groupingListener ); getController().addDockableFocusListener( groupingListener ); initExternalizeArea(); } /** * Called during construction of this {@link CControl}, this method creates a new * {@link CExternalizeArea} and registers it as root-station using the unique identifier * {@value #EXTERNALIZED_STATION_ID}.
* Subclasses may override this method and not create a {@link CExternalizeArea} or create * a customized {@link CExternalizeArea}. */ protected void initExternalizeArea(){ addStation( new CExternalizeArea( this, EXTERNALIZED_STATION_ID ), true ); } /** * Called during construction of this {@link CControl}, this method adds {@link DockFactory}s * to the {@link #intern() intern representation} of this {@link CControl}. */ @SuppressWarnings({ "unchecked", "rawtypes" }) protected void initFactories(){ CommonSingleDockableFactory backupFactory = register.getBackupFactory(); frontend.registerFactory( backupFactory ); frontend.registerBackupFactory( backupFactory ); frontend.registerFactory( new StackDockStationFactory(){ @Override public StackDockPerspective layoutPerspective( StackDockStationLayout layout, Map children ){ CStackPerspective stack = new CStackPerspective(); layoutPerspective( stack, layout, children ); return stack; } }); CommonDockStationFactory stationFactory = new CommonDockStationFactory( this, null, backupFactory ); frontend.registerFactory( stationFactory ); // when creating new DockStations, the factory only creates DockStations that implement Dockable. Altough // the factory can layout DockStations of any kind. frontend.registerBackupFactory( (DockFactory)stationFactory ); } /** * Sets up the default properties. While subclasses can override this method, they should call * this method first. Some parts of this {@link CControl} will not work correctly if the wrong * properties are set or if no properties are set at all. */ protected void initProperties(){ putProperty( KEY_MAXIMIZE_CHANGE, KeyStroke.getKeyStroke( KeyEvent.VK_M, InputEvent.CTRL_MASK ) ); putProperty( KEY_GOTO_EXTERNALIZED, KeyStroke.getKeyStroke( KeyEvent.VK_E, InputEvent.CTRL_MASK ) ); putProperty( KEY_GOTO_NORMALIZED, KeyStroke.getKeyStroke( KeyEvent.VK_N, InputEvent.CTRL_MASK ) ); putProperty( KEY_CLOSE, KeyStroke.getKeyStroke( KeyEvent.VK_F4, InputEvent.CTRL_MASK ) ); putProperty( KEY_CANCEL_OPERATION, KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ) ); putProperty( SplitDockStation.LAYOUT_MANAGER, new CLockedResizeLayoutManager( this ) ); putProperty( FlapDockStation.LAYOUT_MANAGER, new CFlapLayoutManager() ); putProperty( EclipseTheme.THEME_CONNECTOR, new CommonEclipseThemeConnector( this ) ); putProperty( SingleTabDecider.SINGLE_TAB_DECIDER, new CommonSingleTabDecider( this ) ); putProperty( PlaceholderStrategy.PLACEHOLDER_STRATEGY, new CPlaceholderStrategy( this ) ); putProperty( BubbleTheme.ACTION_DISTRIBUTOR, new CDefaultDockActionDistributor() ); putProperty( FlatTheme.ACTION_DISTRIBUTOR, new CDefaultDockActionDistributor() ); putProperty( DockActionImportanceOrder.ORDER, new CActionImportanceOrder() ); putProperty( DockAction.BUTTON_CONTENT_FILTER, new CButtonContentFilter() ); putProperty( DisablingStrategy.STRATEGY, new CDisablingStrategy( this ) ); putProperty( ScreenDockStation.WINDOW_CLOSING_STRATEGY, new CScreenDockStationWindowClosingStrategy() ); } /** * Sets up all the default icons used in the realm of this {@link CControl}. */ protected void initIcons(){ DefaultIconScheme scheme = new DefaultIconScheme( getController(), new DefaultIconScheme.IconResource( "data/bibliothek/gui/dock/core/icons.ini", null, DockController.class.getClassLoader() ), new DefaultIconScheme.IconResource( "data/bibliothek/gui/dock/common/icons/icons.ini", null, CControl.class.getClassLoader() )); scheme.link( PropertyKey.DOCKABLE_ICON, "dockable.default" ); scheme.link( PropertyKey.DOCK_STATION_ICON, "dockStation.default" ); getController().getIcons().setScheme( Priority.DEFAULT, scheme ); } /** * Sets up all the default text that is used in the realm of this {@link CControl} */ protected void initTexts(){ initTexts( Locale.getDefault() ); } /** * Re-initializes the default text that is used in the realm of this {@link CControl}. * @param locale the new language, must not be null */ public void setLanguage( Locale locale ){ initTexts( locale ); } /** * Sets up all the default text that is used in the realm of this {@link CControl} * @param locale what language to use */ protected void initTexts( Locale locale ){ ResourceBundle bundleCore = ResourceBundle.getBundle( "data.bibliothek.gui.dock.core.locale.text", locale, DockController.class.getClassLoader() ); ResourceBundle bundleCommon = ResourceBundle.getBundle( "data.bibliothek.gui.dock.common.locale.common", locale, CControl.class.getClassLoader() ); List list = getController().getTexts().loadExtensionBundles( locale ); ResourceBundle[] bundles = list.toArray( new ResourceBundle[ list.size() + 2 ] ); bundles[ bundles.length-2 ] = bundleCore; bundles[ bundles.length-1 ] = bundleCommon; getController().getTexts().setScheme( Priority.DEFAULT, new DefaultTextScheme( bundles ) ); } /** * Creates new {@link ApplicationResource}s and registers them at the * {@link #getResources() ApplicationResourceManager} of this {@link CControl}. While subclasses * can override this method, they should be aware that missing {@link ApplicationResource}s will * break persistent storage for the location and size of {@link Dockable}s. */ protected void initPersistentStorage(){ try{ addMultipleDockableFactory( "", NullMultipleCDockableFactory.NULL, false ); resources.put( "ccontrol.frontend", new ApplicationResource(){ public void write( DataOutputStream out ) throws IOException { Version.write( out, Version.VERSION_1_1_1 ); frontend.write( out ); } public void read( DataInputStream in ) throws IOException { Version version = Version.read( in ); version.checkCurrent(); if( Version.VERSION_1_1_1.compareTo( version ) > 0 && Version.VERSION_1_0_4.compareTo( version ) <= 0 ){ readWorkingAreas( in ); } frontend.read( in ); } public void writeXML( XElement element ) { frontend.writeXML( element.addElement( "frontend" ) ); } public void readXML( XElement element ) { frontend.readXML( element.getElement( "frontend" ) ); } }); resources.put( "ccontrol.preferences", new ApplicationResource(){ public void read( DataInputStream in ) throws IOException { Version version = Version.read( in ); version.checkCurrent(); preferences.read( in ); if( preferenceModel != null ){ preferences.load( preferenceModel, false ); preferenceModel.write(); } } public void readXML( XElement element ) { preferences.readXML( element ); if( preferenceModel != null ){ preferences.load( preferenceModel, false ); preferenceModel.write(); } } public void write( DataOutputStream out ) throws IOException { if( preferenceModel != null ){ preferenceModel.read(); preferences.store( preferenceModel ); } Version.write( out, Version.VERSION_1_0_6 ); preferences.write( out ); } public void writeXML( XElement element ) { if( preferenceModel != null ){ preferenceModel.read(); preferences.store( preferenceModel ); } preferences.writeXML( element ); } }); } catch( IOException ex ){ System.err.println( "Non lethal IO-error:" ); ex.printStackTrace(); } } /** * Adds a listener to this control. * @param listener the new listener */ public void addControlListener( CControlListener listener ){ if( listener == null ) throw new IllegalArgumentException( "Listener must not be null" ); listeners.add( listener ); } /** * Removes a listener from this control. * @param listener the listener to remove */ public void removeControlListener( CControlListener listener ){ listeners.remove( listener ); } /** * Adds a new focus listener to this control. The listener gets informed * about changes in the focus. * @param listener the new listener */ public void addFocusListener( CFocusListener listener ){ listenerCollection.addFocusListener( listener ); } /** * Removes a listener from this control. * @param listener the listener to remove */ public void removeFocusListener( CFocusListener listener ){ listenerCollection.removeFocusListener( listener ); } /** * Gets the currently focused {@link CDockable}. This might be null if some * {@link Dockable} that is not a {@link CommonDockable} has the focus. * @return the currently focused {@link CDockable}, can be null * @see #addFocusListener(CFocusListener) * @see DockController#getFocusedDockable() */ public CDockable getFocusedCDockable(){ Dockable focused = getController().getFocusedDockable(); if( focused instanceof CommonDockable ){ return ((CommonDockable)focused).getDockable(); } return null; } /** * Gets an object describing which {@link CDockable}s did have the focus in * which order. * @return the focus history */ public CFocusHistory getFocusHistory(){ return new DefaultCFocusHistory( this ); } /** * Adds a new veto focus listener to this control. The listener gets * informed about pending changes in the focus. * @param listener the new listener */ public void addVetoFocusListener( CVetoFocusListener listener ){ listenerCollection.addVetoFocusListener( listener ); } /** * Removes a listener from this control. * @param listener the listener to remove */ public void removeVetoFocusListener( CVetoFocusListener listener ){ listenerCollection.removeVetoFocusListener( listener ); } /** * Adds a global state listener. This has the same effect as adding * a state listener to each {@link CDockable} that is known to this * control. * @param listener the new listener */ public void addStateListener( CDockableStateListener listener ){ listenerCollection.addCDockableStateListener( listener ); } /** * Removes a global state listener. * @param listener the listener to remove */ public void removeStateListener( CDockableStateListener listener ){ listenerCollection.removeCDockableStateListener( listener ); } /** * Adds a global property listener. This has the same effect as adding * a property listener to each {@link CDockable} that is known to this * control. * @param listener the new listener */ public void addPropertyListener( CDockablePropertyListener listener ){ listenerCollection.addCDockablePropertyListener( listener ); } /** * Removes a global listener from this control. * @param listener the listener to remove */ public void removePropertyListener( CDockablePropertyListener listener ){ listenerCollection.removeCDockablePropertyListener( listener ); } /** * Adds a global keyboard listener to this control. The listener gets * informed whenever a key is touched on a {@link Component} which is a child * of a {@link CDockable}.
* Note: listeners directly added to a {@link CDockable} will always * be informed first.
* Note: if a listener processes the event, then the other listeners will * not be informed. * @param listener the new listener */ public void addKeyboardListener( CKeyboardListener listener ){ listenerCollection.addKeyboardListener( listener ); } /** * Removes a listener from this control. * @param listener the listener to remove */ public void removeKeyboardListener( CKeyboardListener listener ){ listenerCollection.removeKeyboardListener( listener ); } /** * Adds a key listener to this control that will be informed about any * {@link KeyEvent} that gets processed or analyzed by this control. Especially * any event that gets forwarded to a {@link CKeyboardListener} gets also * forwarded to listener. * @param listener the new listener */ public void addGlobalKeyListener( KeyListener listener ){ intern().getController().getKeyboardController().addGlobalListener( listener ); } /** * Removes a global {@link KeyListener} from this control. * @param listener the listener to remove */ public void removeGlobalKeyListener( KeyListener listener ){ intern().getController().getKeyboardController().removeGlobalListener( listener ); } /** * Adds a global mouse double click listener to this control. The listener gets * informed whenever the mouse is clicked twice on a {@link Component} which * is a child of a {@link CDockable}.
* Note: listeners directly added to a {@link CDockable} will always * be informed first.
* Note: if a listener processes the event, then the other listeners will * not be informed. * @param listener the new listener */ public void addDoubleClickListener( CDoubleClickListener listener ){ listenerCollection.addDoubleClickListener( listener ); } /** * Removes a listener from this control. * @param listener the listener to remove */ public void removeDoubleClickListener( CDoubleClickListener listener ){ listenerCollection.removeDoubleClickListener( listener ); } /** * Adds listener to this control, the listener will be informed whenever a set of * {@link CDockable}s is about to be closed.
* {@link CVetoClosingListener}s added to the {@link CControl} are invoked before listeners that * are added to a {@link CDockable}. * @param listener the new listener, not null */ public void addVetoClosingListener( CVetoClosingListener listener ){ listenerCollection.addVetoClosingListener( listener ); } /** * Removes a listener from this control. * @param listener the listener to remove */ public void removeVetoClosingListener( CVetoClosingListener listener ){ listenerCollection.removeVetoClosingListener( listener ); } /** * Gets a list of currently registered listeners. * @return the listeners */ private CControlListener[] listeners(){ return listeners.toArray( new CControlListener[ listeners.size() ] ); } /** * Informs this {@link CControl} whether location of {@link CDockable}s that are associated with a * {@link CStation#isWorkingArea() working area} should be stored when storing a layout.
* This method installs a {@link DockSituationIgnore} on the intern {@link DockFrontend}, the filter is only * used for "normal entries", "final entries" (stored when the application shuts down) are not affected.
* The default value for this property is true. * @param ignore if true then some {@link CDockable}s are filtered out, otherwise their location * is stored. */ public void setIgnoreWorkingForEntry( boolean ignore ){ if( ignore ){ frontend.setIgnoreForEntry( new DockSituationIgnore(){ public boolean ignoreChildren( DockStation station ) { CStation cstation = getStation( station ); if( cstation != null ) return cstation.isWorkingArea(); return false; } public boolean ignoreChildren( PerspectiveStation station ){ if( station instanceof CommonElementPerspective ){ CStationPerspective perspective = ((CommonElementPerspective)station).getElement().asStation(); if( perspective != null ){ return perspective.isWorkingArea(); } } return false; } public boolean ignoreElement( DockElement element ) { if( element instanceof CommonDockable ){ CDockable cdockable = ((CommonDockable)element).getDockable(); if( cdockable.getWorkingArea() != null ) return true; } return false; } public boolean ignoreElement( PerspectiveElement element ){ if( element instanceof CommonElementPerspective ){ CDockablePerspective perspective = ((CommonElementPerspective)element).getElement().asDockable(); if( perspective != null ){ return perspective.getWorkingArea() != null; } } return false; } }); } else{ frontend.setIgnoreForEntry( null ); } } /** * Reads a map telling for each {@link SingleCDockable} to which {@link CWorkingArea} * it belongs.
* This method only remains for backwards compatibility, it does not do anything but * reading some obsolete data from in * @param in the stream to read from * @throws IOException if an I/O error occurs */ private void readWorkingAreas( DataInputStream in ) throws IOException{ for( int i = 0, n = in.readInt(); inull */ public CContentArea createContentArea( String uniqueId ){ return createContentArea( uniqueId, false ); } private CContentArea createContentArea( String uniqueId, boolean isDefaultContentArea ){ if( uniqueId == null ) throw new NullPointerException( "uniqueId must not be null" ); if( !isDefaultContentArea && uniqueId.equals( CONTENT_AREA_STATIONS_ID )){ throw new IllegalArgumentException( "the unique identifier '" + uniqueId + "' is reserved for the default CContentArea and may not be used by the client" ); } CContentArea center = new CContentArea( this, uniqueId ); if( isDefaultContentArea ){ register.setDefaultContentArea( center ); } addStationContainer( center ); return center; } /** * Adds container to this control. All children {@link CStation}s of container will * be added as root station to this control. * @param container the additional set of stations * @throws IllegalArgumentException if container is already registered or if the unique identifier * of container is already known * @throws NullPointerException if container is null */ public void addStationContainer( CStationContainer container ){ if( container == null ){ throw new NullPointerException( "container is null" ); } checkValidUniqueId( container.getUniqueId() ); // check control? DockStation defaultStation = frontend.getDefaultStation(); boolean noDefaultStation = defaultStation == null || defaultStation instanceof ScreenDockStation; register.addStationContainer( container ); if( noDefaultStation ){ CStation newDefaultStation = container.getDefaultStation(); if( newDefaultStation != null ){ frontend.setDefaultStation( newDefaultStation.getStation() ); } } } /** * Removes content from the list of known contentareas. This also removes * the stations of content from this control. Elements aboard the * stations are made invisible, but not removed from this control. * @param content the contentarea to remove * @throws IllegalArgumentException if the default-contentarea equals content * @deprecated use {@link #removeStationContainer(CStationContainer)} instead */ @Deprecated public void removeContentArea( CContentArea content ){ removeStationContainer( content ); } /** * Removes container from the list of known {@link CStationContainer}s. This also * ensures that all child {@link CStation}s of container are removed. Elements aboard the * stations are made invisible, but not removed from this {@link CControl}. * @param container the set of stations to remove * @throws IllegalArgumentException if container is the default {@link CContentArea} */ public void removeStationContainer( CStationContainer container ){ if( container == null ) throw new NullPointerException( "container must not be null" ); if( register.getDefaultContentArea() == container ) throw new IllegalArgumentException( "The default-contentarea can't be removed" ); register.removeStationContainer( container ); } /** * Gets the set of dockables, stations and other elements that are used * by this control. * @return the set of elements, never null */ public CControlRegister getRegister(){ return register; } /** * Gets an unmodifiable list of all {@link CStationContainer}s that are registered at this {@link CControl}. * @return the list of containers */ public List getStationContainers(){ return register.getStationContainers(); } /** * Gets the factory which is mainly used to create new elements for this * control. * @return the factory */ public CControlFactory getFactory() { return factory; } /** * Gets the manager that is responsible to handle all changes of the * modes (maximized, normalized, ... ) of {@link Dockable}s.
* Note: clients should be careful when working with the location manager. * Changing the properties of the location manager might introduce failures that * are not visible directly. * @return the manager */ public CLocationModeManager getLocationManager() { return locationManager; } /** * Adds a destroy-hook. The hook is called when this {@link CControl} is * destroyed through {@link #destroy()}. * @param hook the new hook */ public void addDestroyHook( DestroyHook hook ){ if( hook == null ) throw new NullPointerException( "hook must not be null" ); hooks.add( hook ); } /** * Removes a destroy-hook from this {@link CControl}. * @param hook the hook to remove */ public void removeDestroyHook( DestroyHook hook ){ hooks.remove( hook ); } /** * Grants access to the manager that reads and stores configurations * of the common-project.
* Clients can add their own {@link ApplicationResource}s to this manager, * however clients are strongly discouraged from removing {@link ApplicationResource} * which they did not add themselves. * @return the persistent storage */ public ApplicationResourceManager getResources() { return resources; } /** * Changes the value of a property. The incomplete list of properties, in alphabetical order, includes: * (properties marked with '*' should not be changed by clients if using the Common project). * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Properties
{@link BubbleTheme#ACTION_DISTRIBUTOR}Default instance of a {@link DockActionDistributor}.
{@link FlatTheme#ACTION_DISTRIBUTOR}Default instance of a {@link DockActionDistributor}.
{@link DefaultDockRelocator#AUTO_DROP_ON_ANY_MOUSE_RELEASED_EVENT}Stop drag-and-drop operations on any mouse-released event (a workaround necessary for some Linux and Mac systems).
{@link DockTheme#BACKGROUND_PAINT} The default value of the {@link BackgroundPaint}.
{@link BasicTheme#BASIC_COLOR_SCHEME}The {@link ColorScheme} to use if the {@link BasicTheme} is installed.
{@link DockTheme#BORDER_MODIFIER} The default value of the {@link BorderModifier}.
{@link ScreenDockStation#BOUNDARY_RESTRICTION}How far the user can push a window with a {@link Dockable} out of the screen(s).
{@link BubbleTheme#BUBBLE_COLOR_SCHEME} The {@link ColorScheme} to use if the {@link BubbleTheme} is installed.
{@link FlapDockStation#BUTTON_CONTENT} Tells what content should be on the buttons that represent minimized {@link Dockable}s.
{@link FlapDockStation#BUTTON_CONTENT_FILTER} Tells which {@link DockAction}s should be shown on a button representing a minimized {@link Dockable}.
* {@link CControl#CCONTROL} The {@link CControl} in whose realm the property is read, is a read-only property.
{@link DockTheme#COMBINER} Default value of the {@link Combiner}.
{@link StackDockStation#COMPONENT_FACTORY} The factory creating the "tabbed panes" of the {@link StackDockStation}.
{@link DockTheme#DISPLAYER_FACTORY} Default value of the {@link DisplayerFactory}.
{@link DockStationDropLayerFactory#DROP_LAYER_FACTORY}Factory that defines which parts of the screen are targets for a drag and drop operation/
{@link PropertyKey#DOCK_STATION_ICON} The default icon of {@link DockStation}s.
{@link PropertyKey#DOCK_STATION_TITLE} The default title of {@link DockStation}s.
{@link PropertyKey#DOCK_STATION_TOOLTIP} The default tooltip of {@link DockStation}s.
{@link PropertyKey#DOCKABLE_ICON} The default icon of {@link Dockable}s.
{@link DockTheme#DOCKABLE_MOVING_IMAGE_FACTORY} Default value of the {@link DockableMovingImageFactory}.
{@link DockTheme#DOCKABLE_SELECTION} Default value of the {@link DockableSelection}.
{@link PropertyKey#DOCKABLE_TITLE} The default title of {@link Dockable}s.
{@link PropertyKey#DOCKABLE_TOOLTIP} The default tooltip of {@link Dockable}s.
{@link LocationModeManager#DOUBLE_CLICK_STRATEGY} Tells what happens if the user double clicks on a {@link DockTitle} or a {@link Dockable}.
{@link EclipseTheme#ECLIPSE_COLOR_SCHEME} The {@link ColorScheme} to use if the {@link EclipseTheme} is installed.
* {@link ScreenDockStation#EXPAND_ON_DOUBLE_CLICK} Whether a double click on a child of a {@link ScreenDockStation} should maximize the child.
{@link FlatTheme#FLAT_COLOR_SCHEME} The {@link ColorScheme} to use if the {@link FlatTheme} is installed.
{@link ScreenDockStation#FULL_SCREEN_STRATEGY} Defines when a floating {@link Dockable} is considered to be in fullscreen mode.
* {@link DockFrontend#HIDE_ACCELERATOR} The {@link KeyStroke} that will call {@link DockFrontend#hide(Dockable)}
{@link DockableSelector#INIT_SELECTION} The {@link KeyStroke} that opens a window where the user can select a new {@link Dockable}.
{@link StackDockStation#IMMUTABLE_SELECTION_INDEX} Prevents the {@link StackDockStation} from switching the selected index on a drop operation (but does not prevent the {@link FocusManager} from switching the focus!).
{@link CControl#KEY_CLOSE} The {@link KeyStroke} that closes a {@link CDockable}.
{@link CControl#KEY_GOTO_EXTERNALIZED} The {@link KeyStroke} that externalizes a {@link CDockable}.
{@link CControl#KEY_GOTO_MAXIMIZED} The {@link KeyStroke} that maximizes a {@link CDockable}.
{@link CControl#KEY_GOTO_MINIMIZED} The {@link KeyStroke} that minimizes a {@link CDockable}.
{@link CControl#KEY_GOTO_NORMALIZED} The {@link KeyStroke} that normalizes a {@link CDockable}.
{@link CControl#KEY_MAXIMIZE_CHANGE} The {@link KeyStroke} that either maximizes or normalizes a {@link CDockable}.
{@link CControl#KEY_CANCEL_OPERATION} The {@link KeyStroke} that will cancel the current drag and drop operation.
{@link FlapDockStation#LAYOUT_MANAGER} Tells the {@link FlapDockStation} the size and the hold property of its children.
{@link SplitDockStation#LAYOUT_MANAGER} Logic of all {@link SplitDockStation}s, used when dropping a {@link Dockable} or resizing the station.
{@link TabPane#LAYOUT_MANAGER} Defines the size and location of tabs of a stack.
{@link TabPane#USE_SMALL_MINIMUM_SIZE} Use really small minimum sizes for calculating the minimum size of a tab-pane, instead of trying to make the content look good
* {@link SplitDockStation#MAXIMIZE_ACCELERATOR} The {@link KeyStroke} that maximizes a child of a {@link SplitDockStation}.
{@link CombinedMenuContent#MENU_CONTENT} The menu that shows overflowing {@link Dockable}s on a stack.
{@link IconManager#MINIMUM_ICON_SIZE}The expected minimal size of all icons.
{@link FlapDockStation#MINIMUM_SIZE} The minimum size of the {@link Component} that represents the {@link FlapDockStation}.
* {@link LocationModeManager#MODE_ENABLEMENT} Tells which {@link CDockable} is allowed to have which {@link ExtendedMode}.
{@link DockRelocatorMode#NO_COMBINATION_MASK} What keys the user has to press during a drag and drop operation to prevent the framework from combining {@link Dockable}s.
{@link DockTitle#ORIENTATION_STRATEGY} Tells how to rotate text on a {@link DockTitle}.
{@link EclipseTheme#PAINT_ICONS_WHEN_DESELECTED} Whether to paint icons on unselected tabs if using the {@link EclipseTheme}.
{@link PlaceholderStrategy#PLACEHOLDER_STRATEGY} A strategy that creates placeholders for {@link Dockable}s, see {@link CPlaceholderStrategy}.
{@link CControl#RESIZE_LOCK_CONFLICT_RESOLVER} Tells what happens if two {@link CDockable}s have a locked size and the user is resizing the parent of these two elements.
{@link DockController#RESTRICTED_ENVIRONMENT} Tells whether the application runs as applet/with webstart or as free or authenticated application.
{@link DockRelocatorMode#SCREEN_MASK} The keys the user has to press during a drag and drop operation to ensure that the {@link Dockable} is added to a {@link ScreenDockStation}.
{@link SingleTabDecider#SINGLE_TAB_DECIDER} Tells which {@link Dockable}s should be presented with a single tab - even if there is no reason to show a tab.
{@link DockTheme#STATION_PAINT} The default value of {@link StationPaint}.
{@link AWTComponentCaptureStrategy#STRATEGY} How to make an image of an AWT component.
{@link DisablingStrategy#STRATEGY}Which element to disable.
{@link StackDockStation#TAB_CONTENT_FILTER} A filter deciding what content to show on a tab of a {@link StackDockStation}.
{@link EclipseTheme#TAB_PAINTER} The look of tabs if using the {@link EclipseTheme}.
{@link StackDockStation#TAB_PLACEMENT} The location of the tabs on a {@link StackDockStation}.
{@link EclipseTheme#THEME_CONNECTOR} Detailed instructions how to present a {@link Dockable} if using the {@link EclipseTheme}.
{@link FlapDockStation#WINDOW_FACTORY} A factory creating {@link FlapWindow}s for the {@link FlapDockStation}.
{@link ScreenDockStation#WINDOW_FACTORY} A factory creating {@link ScreenDockWindow}s for the {@link ScreenDockStation}.
* * * @param the type of the value * @param key the name of the property * @param value the new value, can be null */ public void putProperty( PropertyKey key, A value ){ putProperty( key, value, Priority.CLIENT ); } /** * Changes the value of a property. * @param the type of the value * @param key the name of the property * @param priority the priority of the new value * @param value the new value, can be null * @see #putProperty(PropertyKey, Object) */ protected void putProperty( PropertyKey key, A value, Priority priority ){ frontend.getController().getProperties().set( key, value, priority ); } /** * Gets the value of a property. * @param the type of the property * @param key the name of the property * @return the value or null */ public A getProperty( PropertyKey key ){ return frontend.getController().getProperties().get( key ); } /** * Gets the element that should be in the center of the mainframe. The {@link CContentArea} * is created the first time this method is called. * @return the center of the mainframe of the application */ public CContentArea getContentArea() { CContentArea content = register.getDefaultContentArea(); if( content == null ){ content = createContentArea( CONTENT_AREA_STATIONS_ID, true ); } return content; } /** * Adds an additional station to this control. * @param station the new station */ public void addStation( CStation station ){ addStation( station, true ); } /** * Adds an additional station to this control. Most {@link CStation}s should * be root-stations, even if they are nested. * @param station the new station * @param root true if the station should be a root station. A root station may * or may not have any parent station. The location of a {@link CDockable} is always relative * to the first root station that can be found when travelling the tree upwards. For most stations * this attribute should be true */ public void addStation( CStation station, boolean root ){ String id = station.getUniqueId(); checkValidUniqueId( id ); register.addStation( station ); if( root ){ frontend.addRoot( id, station.getStation() ); } station.setControlAccess( access ); } /** * Tells whether station was {@link #addStation(CStation, boolean) added} to this {@link CControl} * with the root flag set to true. * @param station the station whose root flag is asked * @return the value of the root flag or false if station is not registered at all */ public boolean isRootStation( CStation station ){ DockStation root = frontend.getRoot( station.getUniqueId() ); return root == station.getStation(); } /** * Removes a {@link CStation} from this control. It is unspecified what * happens with the children on station * @param station the station to remove */ public void removeStation( CStation station ){ if( register.removeStation( station ) ){ frontend.removeRoot( station.getStation() ); station.setControlAccess( null ); } } /** * Gets an unmodifiable list of all stations that are currently * registered at this control. * @return the list of stations */ public List> getStations(){ return register.getStations(); } /** * Searches the {@link CStation} whose {@link CStation#getStation() internal representation} * is intern. * @param intern the internal representation * @return the station or null */ public CStation getStation( DockStation intern ){ if( intern instanceof CommonDockStation){ return ((CommonDockStation)intern).getStation(); } return null; } /** * Searches along the path to the root {@link DockStation} the first {@link CStation} that matches * the {@link DockStation}. If intern is a {@link CStation}, then this method behaves * as if {@link #getStation(DockStation)} was called. If the parent of intern is a {@link CStation}, * then this method behaves as if getStation( intern.getDockParent() ) was called. * @param intern the starting point for the search of a {@link CStation} * @return the next {@link CStation} on the path from intern (incl.) to the root station (incl.) */ public CStation findStation( DockStation intern ){ CStation result = null; while( result == null && intern != null ){ result = getStation( intern ); Dockable dockable = intern.asDockable(); if( dockable == null ){ intern = null; }else{ intern = dockable.getDockParent(); } } return result; } /** * Searches the {@link CStation} with unique identifier id. * @param id the identifier * @return the station or null */ public CStation getStation( String id ){ for( CStation station : register.getStations() ){ if( station.getUniqueId().equals( id )){ return station; } } return null; } /** * Adds a dockable to this control. The dockable can be made visible afterwards. This method will do nothing * if dockable was already registered at this {@link CControl}. * @param the type of the new element * @param dockable the new element to show * @return dockable * @throws IllegalArgumentException if dockable already is registered at another {@link CControl} * or if the unique id of dockable already is used for another object */ public S addDockable( S dockable ){ if( dockable == null ) throw new NullPointerException( "dockable must not be null" ); checkValidUniqueId( dockable.getUniqueId() ); boolean alreadyKnown = dockable.getControl() == this; if( dockable.getControl() != null && !alreadyKnown ){ throw new IllegalArgumentException( "dockable is already part of a control" ); } SingleCDockable preset = register.getSingleDockable( dockable.getUniqueId() ); if( preset != null ){ if( preset == dockable ){ return dockable; } else{ throw new IllegalArgumentException( "unique id \'" + dockable.getUniqueId() + "\' already in use for another SingleCDockable" ); } } if( !alreadyKnown ){ dockable.setControlAccess( access ); } String id = register.toSingleId( dockable.getUniqueId() ); accesses.get( dockable ).setUniqueId( id ); frontend.addDockable( id, dockable.intern() ); frontend.setHideable( dockable.intern(), true ); register.addSingleDockable( dockable ); for( CControlListener listener : listeners() ) listener.added( CControl.this, dockable ); return dockable; } /** * Checks whether the unique identifier id is a valid identifier. This means that id * is not null and contains at least one sign that is not a whitespace. * @param id the unique identifier to check * @throws IllegalArgumentException if id is not valid */ private void checkValidUniqueId( String id ){ if( id == null ){ throw new IllegalArgumentException( "unique id is 'null'"); } if( id.length() == 0 ){ throw new IllegalArgumentException( "unique id has length of 0" ); } if( id.trim().length() == 0 ){ throw new IllegalArgumentException( "unique id consists of whitespaces only" ); } } /** * Searches for the {@link SingleCDockable} which has the unique identifier * id. * @param id the identifier to look out for * @return the element with that identifier or null */ public SingleCDockable getSingleDockable( String id ){ for( SingleCDockable dockable : register.getSingleDockables() ){ if( dockable.getUniqueId().equals( id )){ return dockable; } } return null; } /** * Removes the {@link SingleCDockable} with the identifier id. * @param id the id of the element to remove * @return true if the element was removed, false * otherwise */ public boolean removeSingleDockable( String id ){ for( SingleCDockable dockable : register.getSingleDockables() ){ if( dockable.getUniqueId().equals( id )){ return removeDockable( dockable ); } } return false; } /** * Removes dockable from this control. The location information * for dockable remains stored if either there is a * {@link #addSingleDockableFactory(String, SingleCDockableFactory) SingleCDockableFactory} * registered or the {@link #setMissingStrategy(MissingCDockableStrategy) MissingCDockableStrategy} * tells to store the values. * @param dockable the element to remove * @return true if the element was removed, false otherwise */ public boolean removeDockable( SingleCDockable dockable ){ if( dockable == null ) throw new NullPointerException( "dockable must not be null" ); if( dockable.getControl() == this ){ dockable.setVisible( false ); frontend.remove( dockable.intern() ); register.removeSingleDockable( dockable ); dockable.setControlAccess( null ); for( CControlListener listener : listeners() ) listener.removed( CControl.this, dockable ); return true; } return false; } /** * Adds a factory to this control. The factory will be used * to create and add a {@link SingleCDockable} when one is requested that * is not yet in the cache.
* If there is already information for id available and * id should be visible, then the factory will be used * instantaneously.
* Factories added with a specific identifier always have higher priority than factories * added with a filter, see {@link #addSingleDockableFactory(Filter, SingleCDockableFactory)}. * @param id the id of the dockable that might be requested * @param backupFactory the new factory */ public void addSingleDockableFactory( String id, SingleCDockableFactory backupFactory ){ register.getBackupFactory().add( id, backupFactory ); String singleId = register.toSingleId( id ); // This would happen automatically when loading a layout. However code reading // the entries of DockFrontend is now informed about the possible existence of // such an identifier locationManager.addEmpty( singleId ); frontend.addEmpty( singleId ); // if there is already layout information for id, then load this information now FrontendEntry entry = frontend.getFrontendEntry( singleId ); if( entry != null && entry.getDockable() == null && entry.isShown() ){ SingleCDockable dockable = backupFactory.createBackup( id ); if( dockable != null ){ addDockable( dockable ); if( entry.isShown() || !dockable.isCloseable() ){ dockable.setVisible( true ); } } } } /** * Adds a factory to this control. The factory will be used * to create and add a {@link SingleCDockable} when one is requested that * is not yet in the cache.
* If there is already information for identifiers that are included by ids available and * if they should be visible, then the factory will be used instantaneously to create these elements. * During this action factory has a higher priority than any other factory.
* Factories added with a general filter always have lower priority than factories that were added * with a specific identifier. The factories are stored in a list and a search starts at the front of that * list, so a factory added early has higher priority than a factory that was added lately. * @param ids a filter telling which dockables can be handled by factory * @param factory the new factory */ public void addSingleDockableFactory( Filter ids, SingleCDockableFactory factory ){ register.getBackupFactory().add( ids, factory ); for( FrontendEntry entry : frontend.listFrontendEntries() ){ if( entry.getDockable() == null && entry.isShown() ){ if( register.isSingleId( entry.getKey() )){ String id = register.singleToNormalId( entry.getKey() ); if( ids.includes( id )){ SingleCDockable dockable = factory.createBackup( id ); if( dockable != null ){ addDockable( dockable ); if( entry.isShown() || !dockable.isCloseable() ){ dockable.setVisible( true ); } } } } } } } /** * Searches the {@link SingleCDockableFactory} which is responsible for creating the * {@link SingleCDockable} with identifier id. This method first searches * for a factory which was added with a specific identifier ({@link #addSingleDockableFactory(String, SingleCDockableFactory)}), * if nothing is found then the factories with a filter are searched ({@link #addSingleDockableFactory(Filter, SingleCDockableFactory)}). * @param id the identifier of some factory * @return the factory or null */ public SingleCDockableFactory getSingleDockableFactory( String id ){ return register.getBackupFactory().getFactory( id ); } /** * Removes all occurrences of factory. Any location information that was held * because of the existence of factory will be removed as well. * @param factory the factory to remove */ public void removeSingleDockableFactory( SingleCDockableFactory factory ){ register.getBackupFactory().remove( factory ); for( FrontendEntry entry : frontend.listFrontendEntries() ){ if( entry.getDockable() == null && entry.isShown() ){ if( register.isSingleId( entry.getKey() )){ String id = register.singleToNormalId( entry.getKey() ); if( !missingStrategy.shouldStoreSingle( id )){ locationManager.removeEmpty( entry.getKey()); frontend.removeEmpty( entry.getKey() ); } } } } } /** * Removes a factory from this control. Location information for * id will be deleted if neither a {@link #addDockable(SingleCDockable) SingleCDockable} * is added nor the {@link #setMissingStrategy(MissingCDockableStrategy) MissingCDockableStrategy} * tells to store the information. * @param id the name of the factory * @see #addSingleDockableFactory(String, SingleCDockableFactory) */ public void removeSingleDockableFactory( String id ){ register.getBackupFactory().remove( id ); if( !missingStrategy.shouldStoreSingle( id )){ id = register.toSingleId( id ); locationManager.removeEmpty( id ); frontend.removeEmpty( id ); } } /** * Adds a dockable to this control. The dockable can be made visible afterwards. A random identifier * is assigned to dockable, clients can also use {@link #addDockable(String, MultipleCDockable)} if * they want to specify the identifier themselves. * @param the type of the new element * @param dockable the new element to show * @return dockable * @throws IllegalArgumentException if either the {@link MultipleCDockable#getFactory() factory} of dockable is null, * or is not registered (see {@link #addMultipleDockableFactory(String, MultipleCDockableFactory)}). */ public M addDockable( M dockable) { Set ids = new HashSet(); String factoryId; MultipleCDockableFactory factory = dockable.getFactory(); if( factory == null ){ throw new IllegalArgumentException( "factory of dockable must not be null" ); } factoryId = access.getFactoryId( dockable.getFactory() ); if( factoryId == null ){ throw new IllegalStateException( "the factory for a MultipleCDockable is not registered: " + dockable.getFactory() ); } for( MultipleCDockable multi : register.getMultipleDockables() ){ if( factoryId.equals( access.getFactoryId( multi.getFactory() ))){ ids.add( accesses.get( multi ).getUniqueId() ); } } int count = 0; String id = count + " " + factoryId; while( ids.contains( register.toMultiId( id ) ) ){ count++; id = count + " " + factoryId; } return addDockable( id, dockable ); } /** * Adds a dockable to this control. The dockable can be made visible afterwards. * This method will throw an exception when the unique identifier is already * in use. Clients can also use {@link #addDockable(MultipleCDockable)} if they want to assign a * random identifier to dockable. * @param the type of the new element * @param uniqueId id the unique id of the new element * @param dockable the new element to show * @return dockable * @throws IllegalArgumentException if the unique identifier is already in * use, if dockable is already used elsewhere, if there is * no factory for dockable * @throws NullPointerException if any argument is null */ public M addDockable( String uniqueId, M dockable ){ if( dockable == null ) throw new NullPointerException( "dockable must not be null" ); checkValidUniqueId( uniqueId ); String factory = access.getFactoryId( dockable.getFactory() ); if( factory == null ){ throw new IllegalStateException( "the factory for a MultipleCDockable is not registered: " + dockable.getFactory() ); } if( dockable.getControl() != null ) throw new IllegalStateException( "dockable is already part of a control" ); uniqueId = register.toMultiId( uniqueId ); for( MultipleCDockable multi : register.getMultipleDockables() ){ String id = accesses.get( multi ).getUniqueId(); if( uniqueId.equals( id )){ throw new IllegalArgumentException( "The unique identifier is already in use: " + uniqueId ); } } dockable.setControlAccess( access ); accesses.get( dockable ).setUniqueId( uniqueId ); frontend.addDockable( uniqueId, dockable.intern() ); frontend.setHideable( dockable.intern(), true ); register.addMultipleDockable( dockable ); for( CControlListener listener : listeners() ) listener.added( CControl.this, dockable ); return dockable; } /** * Replaces oldDockable with newDockable. The new dockable * inherits settings and location of the old one. * @param oldDockable the old dockable, not null * @param newDockable the new dockable, not null */ public void replace( MultipleCDockable oldDockable, MultipleCDockable newDockable ){ if( oldDockable == null ) throw new IllegalArgumentException( "old dockable must not be null" ); if( newDockable == null ) throw new IllegalArgumentException( "new dockable must not be null" ); if( oldDockable.getControl() != this ) throw new IllegalArgumentException( "old dockable not registered at this CControl" ); if( newDockable.getControl() != null ) throw new IllegalArgumentException( "new dockable alread registered at some CControl" ); String id = accesses.get( oldDockable ).getUniqueId(); boolean frontendEmpty = frontend.isEmpty( id ); if( !frontendEmpty ){ frontend.addEmpty( id ); } boolean locationEmpty = locationManager.isEmpty( id ); if( !locationEmpty ){ locationManager.addEmpty( id ); } id = register.multiToNormalId( id ); removeDockable( oldDockable ); addDockable( id, newDockable ); if( !frontendEmpty ){ frontend.removeEmpty( id ); } if( !locationEmpty ){ locationManager.removeEmpty( id ); } } /** * Searches and returns the one {@link MultipleCDockable} which uses * the unique identifier id. * @param id the identifier to look out for * @return the element using id or null if nothing * was found */ public MultipleCDockable getMultipleDockable( String id ){ id = register.toMultiId( id ); for( MultipleCDockable dockable : register.getMultipleDockables() ){ if( accesses.get( dockable ).getUniqueId().equals( id )){ return dockable; } } return null; } /** * Gets the unique identifier which is used internally for dockable * @param dockable the item to search * @return the internal unique identifier of dockable, may be null */ public String getUniqueId( MultipleCDockable dockable ){ CDockableAccess access = accesses.get( dockable ); if( access == null ){ return null; } return register.multiToNormalId( access.getUniqueId() ); } private boolean shouldStore( String id ){ if( register.isSingleId( id )){ if( register.getBackupFactory().getFactory( register.singleToNormalId( id ) ) != null ){ return true; } return missingStrategy.shouldStoreSingle( register.singleToNormalId( id ) ); } else if( register.isMultiId( id )){ return missingStrategy.shouldStoreMulti( register.multiToNormalId( id ) ); } else{ return false; } } private String shouldStore( CDockable dockable ){ String key = null; if( dockable instanceof SingleCDockable ){ key = ((SingleCDockable)dockable).getUniqueId(); key = register.toSingleId( key ); } else if( dockable instanceof MultipleCDockable ){ CDockableAccess access = accesses.get( dockable ); if( access == null ){ return null; } key = access.getUniqueId(); } if( shouldStore( key )){ return key; } else{ return null; } } @SuppressWarnings("unchecked") private boolean shouldCreate( MultipleCDockableFactory factory, CommonMultipleDockableLayout layout ){ String uniqueId = layout.getId(); String multiId = register.toMultiId( uniqueId ); for( MultipleCDockable multi : register.getMultipleDockables() ){ if( accesses.get( multi ).getUniqueId().equals( multiId )){ return false; } } String factoryId = access.getFactoryId( factory ); MultipleCDockableFactory normalizedFactory = (MultipleCDockableFactory)factory; return missingStrategy.shouldCreate( factoryId, normalizedFactory, uniqueId, layout.getLayout() ); } /** * Removes a dockable from this control. The dockable is made invisible. * @param dockable the element to remove */ public void removeDockable( MultipleCDockable dockable ){ if( dockable == null ) throw new NullPointerException( "dockable must not be null" ); if( dockable.getControl() == this ){ dockable.setVisible( false ); frontend.remove( dockable.intern() ); register.removeMultipleDockable( dockable ); dockable.setControlAccess( null ); for( CControlListener listener : listeners() ) listener.removed( CControl.this, dockable ); } } /** * Gets the number of {@link CDockable}s that are registered in this * {@link CControl}. * @return the number of dockables */ public int getCDockableCount(){ return register.getDockableCount(); } /** * Gets the index'th dockable that is registered in this control * @param index the index of the element * @return the selected dockable */ public CDockable getCDockable( int index ){ return register.getDockable( index ); } /** * Adds a factory to this control. The factory will create {@link MultipleCDockable}s * when a layout is loaded. The {@link NullMultipleCDockableFactory} will always be preinstalled using * the empty identifier. * @param id the unique id of the factory, must consist of at least one character * @param factory the new factory */ public void addMultipleDockableFactory( final String id, final MultipleCDockableFactory factory ){ addMultipleDockableFactory( id, factory, true ); } private void addMultipleDockableFactory( final String id, final MultipleCDockableFactory factory, boolean check ){ if( check ){ checkValidUniqueId( id ); } if( factory == null ){ throw new NullPointerException( "factory must not be null" ); } if( register.getCommonMultipleDockableFactory( id ) != null ){ throw new IllegalArgumentException( "there is already a factory named " + id ); } if( access.getFactoryId( factory ) != null ){ throw new IllegalArgumentException( "this factory-object is already in use and cannot be added a second time" ); } CommonMultipleDockableFactory cfactory = new CommonMultipleDockableFactory( id, factory, access ); register.putCommonMultipleDockableFactory( id, cfactory ); frontend.registerFactory( cfactory ); } /** * Searches for the {@link MultipleCDockableFactory} with the identifier * id. * @param id the identifier to search for * @return the factory or null */ public MultipleCDockableFactory getMultipleDockableFactory( String id ){ return register.getFactory( id ); } /** * Gets the unique identifier of factory. * @param factory the factory to search * @return the unique identifier or null */ public String getFactoryId( MultipleCDockableFactory factory ){ return access.getFactoryId( factory ); } /** * Removes the {@link MultipleCDockableFactory} with identifier id * from this control. As a side effect all {@link MultipleCDockable}s which * use that factory are removed as well. Nothing happens if there is no * factory registered with id. * @param id the identifier of the factory to remove */ public void removeMultipleDockableFactory( String id ){ CommonMultipleDockableFactory factory = register.removeCommonMultipleDockableFactory( id ); if( factory != null ){ frontend.unregisterFactory( factory ); List toRemove = new ArrayList(); for( MultipleCDockable dockable : register.getMultipleDockables() ){ if( dockable.getFactory() == factory.getFactory() ){ toRemove.add( dockable ); } } for( MultipleCDockable dockable : toRemove ){ removeDockable( dockable ); } } } /** * Sets the location where {@link CDockable}s are opened when there is * nothing else specified for these CDockables. * @param defaultLocation the location, can be null */ public void setDefaultLocation( CLocation defaultLocation ){ this.defaultLocation = defaultLocation; } /** * Gets the location where {@link CDockable}s are opened when nothing else * is specified. * @return the location, might be null * @see #setDefaultLocation(CLocation) */ public CLocation getDefaultLocation(){ return defaultLocation; } /** * Makes sure that all {@link CDockable}s are maximized onto the area * which is registered under the given unique id. * @param id the unique id of the area * @see CGridArea#getUniqueId() * @see CContentArea#getCenterIdentifier() */ public void setMaximizeArea( String id ){ CMaximizedMode mode = locationManager.getMaximizedMode(); CMaximizedModeArea area = mode.get( id ); if( area == null ) throw new IllegalArgumentException( "No area registered with key '" + id + "'" ); mode.setDefaultArea( area ); } /** * Sets the {@link CGroupBehavior}. The behavior decides what happens when the user wants to change * the {@link ExtendedMode} of a {@link CDockable}.
* To be exact: the group behavior is applied for a call to {@link CDockable#setExtendedMode(ExtendedMode)} * respective a call to {@link LocationModeManager#setMode(Dockable, ExtendedMode)}. The buttons that are * visible to the user all link to these methods. * @param behavior the new behavior, not null */ public void setGroupBehavior( CGroupBehavior behavior ){ locationManager.setGroupBehavior( behavior ); } /** * Gets the currently used {@link CGroupBehavior}. * @return the current behavior, not null * @see #setGroupBehavior(CGroupBehavior) */ public CGroupBehavior getGroupBehavior(){ return locationManager.getGroupBehavior(); } /** * Sets the theme of the elements in the realm of this control. * @param theme the new theme * @deprecated replaced by {@link #setTheme(String)}. While this method still * works, the theme will not get stored persistent and any module using * the {@link ThemeMap} ({@link #getThemes()}) will not be informed about * the change. */ @Deprecated public void setTheme( DockTheme theme ){ frontend.getController().setTheme( theme ); } /** * Sets the theme of the elements in the realm of this control. The String * theme is used as key for {@link ThemeMap#select(String)}. * @param theme the name of the theme, this might be one of * {@link ThemeMap#KEY_BASIC_THEME}, {@link ThemeMap#KEY_BUBBLE_THEME}, * {@link ThemeMap#KEY_ECLIPSE_THEME}, {@link ThemeMap#KEY_FLAT_THEME} * or {@link ThemeMap#KEY_SMOOTH_THEME}. This can also be a any other * string which was used for {@link ThemeMap#put(String, ThemeFactory)}, * {@link ThemeMap#add(String, ThemeFactory)} or {@link ThemeMap#insert(int, String, ThemeFactory)}. */ public void setTheme( String theme ){ themes.select( theme ); } /** * Gets the list of installed themes. * @return the list of themes */ public ThemeMap getThemes(){ return themes; } /** * Sets a strategy that creates missing {@link CStationPerspective}s. * @param missingPerspectiveStrategy the strategy, not null */ public void setMissingPerspectiveStrategy( MissingPerspectiveStrategy missingPerspectiveStrategy ){ if( missingPerspectiveStrategy == null ){ throw new IllegalArgumentException( "strategy must not be null" ); } this.missingPerspectiveStrategy = missingPerspectiveStrategy; } /** * Gets the strategy that is used to create missing {@link CStationPerspective}. * @return the strategy, not null */ public MissingPerspectiveStrategy getMissingPerspectiveStrategy(){ return missingPerspectiveStrategy; } /** * Grants access to the perspective API which allows clients to build complex layouts without * the need to create any {@link CDockable dockables} or {@link CStation stations}. * @return access a wrapper around this {@link CControl} allowing to inspect and modify the layouts * that are available * @see #load(String) * @see #save(String) * @see #setMissingPerspectiveStrategy(MissingPerspectiveStrategy) */ public CControlPerspective getPerspectives(){ return new CControlPerspective( access ); } /** * Sets the root window of the application. The root window is used * as owner of any dialog that is created. Already existing dialogs * may be closed and reopened in order to change the owner. Short living * dialogs will not change their owner. * @param window the new owner, can be null */ public void setRootWindow( WindowProvider window ){ frontend.setOwner( window ); } /** * Gets the root window of the application. Note that this method might * not return the same object as given to {@link #setRootWindow(WindowProvider)}, * however the provide returned by this method will return the same window * as specified by {@link #setRootWindow(WindowProvider)}. * @return the provider, never null */ public WindowProvider getRootWindow(){ return frontend.getOwner(); } /** * Gets the storage container for {@link PreferenceModel}s for this control. * The contents of this container are stored in the * {@link #getResources() resource manager}. * @return the storage for preferences * @see #getResources() */ public PreferenceStorage getPreferences(){ return preferences; } /** * Sets the {@link PreferenceModel} which will be used to translate between * this and the {@link #getPreferences() preferences}. This * model can be set to null.
* The default value of this property is null. * @param preferenceModel the new model, it will used to translate * the contents of {@link #getPreferences()} immediately, can be null */ public void setPreferenceModel( PreferenceModel preferenceModel ) { if( this.preferenceModel != null ){ this.preferenceModel.read(); preferences.store( this.preferenceModel ); } this.preferenceModel = preferenceModel; if( preferenceModel != null ){ preferences.load( preferenceModel, false ); preferenceModel.write(); } } /** * Gets the preference model which is used to translate between the * {@link #getPreferences() preferences} and this. * @return the model, can be null * @see #setPreferenceModel(PreferenceModel) */ public PreferenceModel getPreferenceModel() { return preferenceModel; } /** * Sets the strategy that tells what to do if layout information of a missing * {@link CDockable} is found. * @param missingStrategy the strategy, null will set * the default strategy */ public void setMissingStrategy( MissingCDockableStrategy missingStrategy ) { if( missingStrategy == null ){ this.missingStrategy = MissingCDockableStrategy.PURGE; } else{ this.missingStrategy = missingStrategy; } } /** * Gets the strategy that tells what to do if layout information of a missing * {@link CDockable} is found. * @return the strategy, never null */ public MissingCDockableStrategy getMissingStrategy() { return missingStrategy; } /** * Adds a {@link ResizeRequestListener} to this {@link CControl}. The listener * will be informed when the resize requests of a {@link CDockable} should * be processed. * @param listener the new listener, not null */ public void addResizeRequestListener( ResizeRequestListener listener ){ if( listener == null ) throw new NullPointerException( "listener must not be null" ); resizeListeners.add( listener ); } /** * Removes a {@link ResizeRequestListener} from this {@link CControl}. * @param listener the listener to remove */ public void removeResizeRequestListener( ResizeRequestListener listener ){ resizeListeners.remove( listener ); } /** * Informs all {@link ResizeRequestListener}s, that the * {@link CDockable#getAndClearResizeRequest() resize request} of all * CDockables should be processed. There are no * guarantees that a resize requests can be granted or even gets processed.
* All requests, independent from whether they were processed, will be deleted * by this method.
* Note that a request might conflict with a "resize lock" * {@link CDockable#isResizeLockedHorizontally()} and * {@link CDockable#isResizeLockedVertically()}. The behavior of that case is not * specified, but clients can assume that the locked components introduce * additional resize requests. */ public void handleResizeRequests(){ ResizeRequestListener[] listeners = resizeListeners.toArray( new ResizeRequestListener[ resizeListeners.size() ] ); for( ResizeRequestListener listener : listeners ) listener.handleResizeRequest( this ); for( CDockable dockable : register.getDockables() ) dockable.getAndClearResizeRequest(); } /** * Gets the representation of the layer beneath the common-layer. * @return the entry point to DockingFrames */ public CDockFrontend intern(){ return frontend; } /** * Gets the {@link DockController} which is used by this {@link CControl}. * @return the core system of the framework */ public DockController getController(){ return intern().getController(); } /** * Grants access to all the {@link Icon}s that are used within the realm of this * {@link CControl}. Clients are free to modify the set of icons. * @return the set of icons that are used */ public IconManager getIcons(){ return getController().getIcons(); } /** * Tells this control whether basic modes like "normalized", "minimized" or "externalized" are forced upon * {@link Dockable}s after loading a persistent layout. Basically if this property is set, then all {@link Dockable}s * are un-maximized after a layout change. The default value of this property is true.
* The reasons behind forcing basic modes are: *
    *
  • If the user changes the layout, he/she most likely would like to see the effects. A maximized {@link Dockable} would * hide the effects.
  • *
  • For the user re-maximizing an element requires no more than one click with the mouse. It's a cheap operation.
  • *
  • It is an additional layer of security preventing {@link Dockable}s from being in the wrong position if the client * was stared with new settings.
  • *
* @param revert whether non-basic modes should be forbidden when loading a persistent layout */ public void setRevertToBasicModes( boolean revert ){ intern().setRevertToBasicModes( revert ); } /** * Tells whether basic modes are forcibly applied when loading a persistent layout. * @return whether the non-basic modes are forbidden * @see #setRevertToBasicModes(boolean) */ public boolean isRevertToBasicModes(){ return intern().isRevertToBasicModes(); } /** * If a {@link CDockable} is minimized, the focus can be automatically transferred to another {@link CDockable}. This * feature is implemented by the method {@link #initTransferFocusOnMinimize(DockController)}, which may be * overridden by subclasses. * @param transferFocusOnMinimize whether to enable the feature or not (default is true) */ public void setTransferFocusOnMinimize( boolean transferFocusOnMinimize ){ this.transferFocusOnMinimize = transferFocusOnMinimize; } /** * If a {@link CDockable} is minimized, the focus can be automatically transferred to another {@link Dockable}. * @return whether the focus will be transferred * @see #setTransferFocusOnMinimize(boolean) */ public boolean isTransferFocusOnMinimize(){ return transferFocusOnMinimize; } /** * Writes the current and all known layouts into file.
* This is the same as calling getResources().writeFile( file ). * @param file the file to override * @throws IOException if the file can't be written */ public void write( File file ) throws IOException{ getResources().writeFile( file ); } /** * Writes the current and all known layouts into out.
* This is the same as calling getResources().writeStream( out ). * @param out the stream to write into * @throws IOException if the stream is not writable */ public void write( DataOutputStream out ) throws IOException{ getResources().writeStream( out ); } /** * Writes the current and all known layouts into element.
* This is the same as calling getResources().writeXML( element ). * @param element the element to write into */ public void writeXML( XElement element ){ getResources().writeXML( element ); } /** * Writes the current and all known layouts into file in xml format. * @param file the file to write into * @throws IOException if the file is not writable */ public void writeXML( File file ) throws IOException{ XElement root = new XElement( "root" ); getResources().writeXML( root ); BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream( file )); XIO.writeUTF( root, out ); out.close(); } /** * Reads the current and other known layouts from file.
* This is the same as calling getResources().readFile( file ). * @param file the file to read from * @throws IOException if the file can't be read */ public void read( File file ) throws IOException{ getResources().readFile( file ); } /** * Reads the current and other known layouts from in.
* This is the same as calling getResources().readStream( in ). * @param in the stream to read from * @throws IOException if the stream can't be read */ public void read( DataInputStream in ) throws IOException{ getResources().readStream( in ); } /** * Reads the current and other known layouts from element.
* This is the same as calling getResources().readXML( element ). * @param element the element to read * @throws XException if the xml file has the wrong structure */ public void readXML( XElement element ){ getResources().readXML( element ); } /** * Reads the current and other known layouts from file. * @param file the file to open and to read * @throws IOException if the file cannot be read * @throws XException if the xml file has the wrong structure */ public void readXML( File file ) throws IOException{ BufferedInputStream in = new BufferedInputStream( new FileInputStream( file )); XElement element = XIO.readUTF( in ); in.close(); readXML( element ); } /** * Saves the current layout with the current name. Does nothing if there is no name for the current layout. * @return the name that was used to save the layout * @see #save(String) */ public String save(){ return save( false ); } /** * Saves the current layout with the current name. Does nothing if there is no name for the current layout. * @param includeWorkingAreas whether the content of the {@link CStation}s that are marked as * {@link CStation#isWorkingArea() working area} should be stored as well. * @return the name that was used to save the layout * @see #save(String) */ public String save( boolean includeWorkingAreas ){ String current = frontend.getCurrentSetting(); if( current == null ){ return null; } else{ save( current, includeWorkingAreas ); return current; } } /** * Stores the current layout with the given name. This creates "entry" (partial) layout information. * @param name the name of the current layout. */ public void save( String name ){ frontend.save( name ); } /** * Stores the current layout with the given name. This creates "entry" (partial) layout information. * @param name the name of the current layout. * @param includeWorkingAreas whether the content of the {@link CStation}s that are marked as * {@link CStation#isWorkingArea() working area} should be stored as well. */ public void save( String name, boolean includeWorkingAreas ){ frontend.save( name, !includeWorkingAreas ); } /** * Loads an earlier stored layout. * @param name the name of the layout. */ public void load( String name ){ frontend.load( name ); } /** * Loads an earlier stored layout. * @param name the name of the layout. * @param includeWorkingAreas whether the content of the {@link CStation}s that are marked as * {@link CStation#isWorkingArea() working area} should be updated as well. This value should be the same * as was used to call {@link #save(String, boolean)}. */ public void load( String name, boolean includeWorkingAreas ){ frontend.load( name, !includeWorkingAreas ); } /** * Deletes a layout that has been stored earlier. * @param name the name of the layout to delete */ public void delete( String name ){ frontend.delete( name ); } /** * Gets a list of all layouts that are currently known. * @return the list of layouts */ public String[] layouts(){ Set settings = frontend.getSettings(); return settings.toArray( new String[ settings.size() ] ); } /** * Gets the name of the current layout (the one with which {@link #save(String)} was called). The current * layout may not have a name if it was never saved. The result of this method will be a {@link String} * that is part of {@link #layouts()}. * @return the name of the current layout, or null */ public String getLayout(){ return frontend.getCurrentSetting(); } /** * A class giving access to the internal methods of the enclosing * {@link CControl}. * @author Benjamin Sigg */ private class Access implements CControlAccess{ /** action used to close {@link CDockable}s */ private DockAction closeAction; public CControl getOwner(){ return CControl.this; } public void link( CDockable dockable, CDockableAccess access ) { if( access == null ){ CDockableAccess oldAccess = accesses.remove( dockable ); if( oldAccess != null ){ oldAccess.setUniqueId( null ); } dockable.removeCDockablePropertyListener( listenerCollection.getCDockablePropertyListener() ); dockable.removeCDockableStateListener( listenerCollection.getCDockableStateListener() ); } else{ if( accesses.put( dockable, access ) == null ){ dockable.addCDockablePropertyListener( listenerCollection.getCDockablePropertyListener() ); dockable.addCDockableStateListener( listenerCollection.getCDockableStateListener() ); } } } public CDockableAccess access( CDockable dockable ) { return accesses.get( dockable ); } public void hide( CDockable dockable ){ if( !dockable.isVisible() ) return; DockRegister register = frontend.getController().getRegister(); try{ register.setStalled( true ); Map nonBasic = new HashMap(); for( Dockable check : locationManager.listDockables() ){ if( check != dockable.intern() ){ CLocationMode mode = locationManager.getCurrentMode( check ); if( mode != null && !mode.isBasicMode() ){ nonBasic.put( check, mode.getExtendedMode() ); } } } Dockable[] focusHistory = getController().getFocusHistory().getHistory(); boolean changes = locationManager.ensureBasicModes(); frontend.hide( dockable.intern() ); if( changes ){ for( Dockable focused : focusHistory ){ ExtendedMode mode = nonBasic.get( focused ); if( mode != null ){ if( frontend.isShown( focused ) && locationManager.isModeAvailable( focused, mode )){ locationManager.setMode( focused, mode ); } } } } } finally{ register.setStalled( false ); } } public void show( CDockable dockable ){ if( dockable.hasParent() ) return; DockRegister register = frontend.getController().getRegister(); register.setStalled( true ); try{ CLocation location = dockable.getAutoBaseLocation( true ); CDockableAccess access = access( dockable ); if( access != null ){ access.internalLocation( true ); } CStation area = dockable.getWorkingArea(); if( area != null && area.asDockable() != null ){ if( !area.asDockable().isVisible() ){ throw new IllegalStateException( "A dockable that wants to be on a working-area can't be made visible unless the working-area is visible." ); } } if( location == null ){ dockable.setExtendedMode( findInitialMode( dockable ) ); } else{ locationManager.setLocation( dockable.intern(), location ); } if( !frontend.isShown( dockable.intern() )){ frontend.show( dockable.intern(), false ); } locationManager.ensureValidLocation( dockable ); } finally{ register.setStalled( false ); } } private ExtendedMode findInitialMode( CDockable dockable ){ CGroupingBehavior groupingBehavior = getProperty( GROUPING_BEHAVIOR ); DockableGrouping grouping = groupingBehavior.getGrouping( dockable.intern() ); ExtendedMode mode = null; if( grouping != null ){ mode = grouping.getInitialMode( dockable.intern() ); } if( mode == null ){ mode = ExtendedMode.NORMALIZED; } return mode; } public CLocation getAutoBaseLocation( CDockable dockable, boolean noBackwardsTransformation ){ CDockableAccess access = access( dockable ); CLocation location = null; if( access != null ){ location = access.internalLocation( false ); } if( location == null ){ if( frontend.hasLocation( dockable.intern() )){ FrontendEntry entry = frontend.getFrontendEntry( dockable.intern() ); String root = entry.getRoot(); DockableProperty property = entry.getLocation(); CStation station = getStation( root ); if( station != null ){ if( noBackwardsTransformation ){ return null; } location = station.getStationLocation().expandProperty( getController(), property ); } } if( location == null ){ CStation area = dockable.getWorkingArea(); if( area != null ){ location = area.getStationLocation(); } if( location == null ){ location = defaultLocation; } if( location == null && !noBackwardsTransformation ){ location = locationManager.getLocation( dockable.intern(), ExtendedMode.NORMALIZED ); } } } return location; } public boolean isVisible( CDockable dockable ){ return frontend.isShown( dockable.intern() ); } public boolean hasParent( CDockable dockable ){ if( frontend.isHiddenRootStation( dockable.intern() )){ return false; } return isVisible( dockable ); } public String getFactoryId( MultipleCDockableFactory factory ){ for( Map.Entry> entry : register.getFactories().entrySet() ){ if( entry.getValue() == factory ){ return entry.getKey(); } } return null; } public CLocationModeManager getLocationManager() { return locationManager; } public DockAction createCloseAction( final CDockable dockable ) { if( closeAction == null ){ CloseActionFactory factory = getController().getProperties().get( CLOSE_ACTION_FACTORY ); closeAction = factory.create( CControl.this, dockable ).intern(); } return closeAction; } public MutableCControlRegister getRegister() { return register; } public boolean shouldStore( String key ) { return CControl.this.shouldStore( key ); } public String shouldStore( CDockable dockable ) { return CControl.this.shouldStore( dockable ); } } }