Package bibliothek.gui.dock.common

Source Code of bibliothek.gui.dock.common.CControl

/*
* Bibliothek - DockingFrames
* Library built on Java/Swing, allows the user to "drag and drop"
* panels containing any Swing-Component the developer likes to add.
*
* Copyright (C) 2007 Benjamin Sigg
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*
* Benjamin Sigg
* benjamin_sigg@gmx.ch
* CH - Switzerland
*/
package bibliothek.gui.dock.common;

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.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.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.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.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.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.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.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.<br>
* Clients which do no longer need a {@link CControl} can call {@link #destroy()}
* to free resources.<br>
* A <code>CControl</code> is an interface between the application and the framework. The task of
* <code>CControl</code> is to provide access for actions that affect the entire realm. Such
* actions may include:
* <ul>
<li>add/remove a global listener.</li>
<li>add/remove/access {@link CDockable}s, {@link CStation}s and {@link CStationContainer}s.</li>
<li>add/remove/access factories for {@link CDockable}s.</li>
<li>read/write/apply the layout, this includes reading/writing from files.</li>
<li>store properties, like for example the {@link #setTheme(String) theme}.</li>
* </ul>
* @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> CCONTROL = new PropertyKey<CControl>( "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 <code>this</code> */
  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<KeyStroke> KEY_MAXIMIZE_CHANGE =
        new PropertyKey<KeyStroke>( "ccontrol.maximize_change" );

    /**
     * {@link KeyStroke} used to change a {@link CDockable} into
     * maximized-state.
     */
    public static final PropertyKey<KeyStroke> KEY_GOTO_MAXIMIZED =
        new PropertyKey<KeyStroke>( "ccontrol.goto_maximized" );

    /**
     * {@link KeyStroke} used to change a {@link CDockable} into
     * normalized-state.
     */
    public static final PropertyKey<KeyStroke> KEY_GOTO_NORMALIZED =
        new PropertyKey<KeyStroke>( "ccontrol.goto_normalized" );

    /**
     * {@link KeyStroke} used to change a {@link CDockable} into
     * minimized-state.
     */
    public static final PropertyKey<KeyStroke> KEY_GOTO_MINIMIZED =
        new PropertyKey<KeyStroke>( "ccontrol.goto_minimized" );

    /**
     * {@link KeyStroke} used to change a {@link CDockable} into
     * externalized-state.
     */
    public static final PropertyKey<KeyStroke> KEY_GOTO_EXTERNALIZED =
        new PropertyKey<KeyStroke>( "ccontrol.goto_externalized" );

    /**
     * {@link KeyStroke} used to close a {@link CDockable}.
     */
    public static final PropertyKey<KeyStroke> KEY_CLOSE =
        new PropertyKey<KeyStroke>( "ccontrol.close" );

    /**
     * {@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<ConflictResolver<RequestDimension>> RESIZE_LOCK_CONFLICT_RESOLVER =
        new PropertyKey<ConflictResolver<RequestDimension>>(
                "ccontrol.resize_lock_conflict_resolver",
                new ConstantPropertyFactory<ConflictResolver<RequestDimension>>( new DefaultConflictResolver<RequestDimension>()), true );

    /**
     * This factory creates the actions that close dockables.
     */
    public static final PropertyKey<CloseActionFactory> CLOSE_ACTION_FACTORY =
        new PropertyKey<CloseActionFactory>( "ccontrol.closeActionFactory",
            new ConstantPropertyFactory<CloseActionFactory>( CloseActionFactory.DEFAULT ), 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<CDockable, CDockableAccess> accesses = new HashMap<CDockable, CDockableAccess>();

    /** 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<DestroyHook> hooks = new ArrayList<DestroyHook>();

    /** 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<CControlListener> listeners = new ArrayList<CControlListener>();

    /** the list of resize-listeners */
    private List<ResizeRequestListener> resizeListeners = new ArrayList<ResizeRequestListener>();

    /** 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 <code>this</code> */
    private PreferenceModel preferenceModel;
   
    /** if <code>true</code>, 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 <code>restrictedEnvironment</code> 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 <code>null</code>, but
     * its search method may return <code>null</code>
     */
    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 <code>restrictedEnvironment</code> 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 <code>null</code>, but
     * its search method may return <code>null</code>
     * @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 <code>restrictedEnvironment</code> 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 <code>null</code>, but
     * its search method may return <code>null</code>
     * @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 <code>null</code>, but
     * its search method may return <code>null</code>
     * @param factory a factory which is used to create new elements for this
     * control.
     * @param init if <code>true</code> 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 <code>null</code>, but
     * its search method may return <code>null</code>
     * @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 <L> boolean shouldCreate( DockFactory<?,?,L> 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<Object>( CCONTROL_EXTENSION, Object.class, EXTENSION_PARAM, this ) );
    }

    /**
     * Initializes additional {@link Extension}s and registers them at the
     * {@link ExtensionManager} of <code>controller</code>.
     * @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 != null && 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 != null && 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 );
                  }
                }
              }
            }
          }
        }
      });
    }

    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;
            }
        });
    }

    /**
     * Sets up the {@link #locationManager}.
     */
    private void initExtendedModes(){
      locationManager = new CLocationModeManager( access );
      locationManager.setHistoryRewriter( new CStationContainerHistoryRewriter( this ) );
      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}.<br>
     * 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<Integer, PerspectiveDockable> 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( 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 ) );
    }
   
    /**
     * Sets up all the default icons used in the ralm 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 <code>null</code>
     */
    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<ResourceBundle> 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 <code>null</code> if some
     * {@link Dockable} that is not a {@link CommonDockable} has the focus.
     * @return the currently focused {@link CDockable}, can be <code>null</code>
     * @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}.<br>
     * Note: listeners directly added to a {@link CDockable} will always
     * be informed first.<br>
     * 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 <code>listener</code>.
     * @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}.<br>
     * Note: listeners directly added to a {@link CDockable} will always
     * be informed first.<br>
     * 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 <code>listener</code> to this control, the listener will be informed whenever a set of
     * {@link CDockable}s is about to be closed.<br>
     * {@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 <code>null</code>
     */
    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.<br>
     * This method installs a {@link DockSituationIgnore} on the intern {@link DockFrontend}, the filter is only
     * used for "normal entries", "final entries" (does stored when the application shuts down) are not affected.<br>
     * The default value for this property is <code>true</code>.
     * @param ignore if <code>true</code> 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.<br>
     * This method only remains for backwards compatibility, it does not do anything but
     * reading some obsolete data from <code>in</code>
     * @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(); i<n; i++ ){
            in.readUTF(); // key
            in.readUTF(); // value
        }
    }

    /**
     * Frees as much resources as possible. This {@link CControl} will no longer
     * work correctly after this method was called.
     */
    public void destroy(){
        frontend.kill();
        for( DestroyHook hook : hooks )
            hook.destroy();
    }
   
    /**
     * Creates and adds a new {@link CWorkingArea} to this control. The area
     * is not made visible by this method.
     * @param uniqueId the unique id of the area
     * @return the new area
     */
    public CWorkingArea createWorkingArea( String uniqueId ){
        CWorkingArea area = new CWorkingArea( this, uniqueId );
        addDockable( area );
        addStation( area, true );
        return area;
    }

    /**
     * Creates a new area where minimized {@link CDockable}s can be stored. This
     * method adds the new area directly as a root station to this control.
     * @param uniqueId a unique identifier
     * @return the new area
     */
    public CMinimizeArea createMinimizeArea( String uniqueId ){
        CMinimizeArea area = new CMinimizeArea( this, uniqueId );
        addStation( area, true );
        return area;
    }

    /**
     * Creates a new area where normalized {@link CDockable}s can be stored.
     * This method adds the new area directly as a root station to this control
     * @param uniqueId a unique identifier
     * @return the new area
     */
    public CGridArea createGridArea( String uniqueId ){
        CGridArea area = new CGridArea( this, uniqueId );
        addStation( area, true );
        if( frontend.getDefaultStation() == null )
            frontend.setDefaultStation( area.getStation() );
        return area;
    }

    /**
     * Creates and adds a new {@link CContentArea}.
     * @param uniqueId the unique id of the new contentarea, the id must be unique
     * in respect to all other contentareas which are registered at this control.
     * @return the new contentarea
     * @throws IllegalArgumentException if the id is not unique
     * @throws NullPointerException if the id is <code>null</code>
     */
    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 <code>container</code> to this control. All children {@link CStation}s of <code>container</code> will
     * be added as root station to this control.
     * @param container the additional set of stations
     * @throws IllegalArgumentException if <code>container</code> is already registered or if the unique identifier
     * of <code>container</code> is already known
     * @throws NullPointerException if <code>container</code> is <code>null</code>
     */
    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 <code>content</code> from the list of known contentareas. This also removes
     * the stations of <code>content</code> 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 <code>content</code>
     * @deprecated use {@link #removeStationContainer(CStationContainer)} instead
     */
    @Deprecated
    public void removeContentArea( CContentArea content ){
      removeStationContainer( content );
    }
   
    /**
     * Removes <code>container</code> from the list of known {@link CStationContainer}s. This also
     * ensures that all child {@link CStation}s of <code>container</code> 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 elemnts that are used
     * by this control.
     * @return the set of elements, never <code>null</code>
     */
    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<CStationContainer> 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.<br>
     * 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.<br>
     * 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 by themself.
     * @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).
     * <table>
     *   <tr><tr>{@link BubbleTheme#ACTION_DISTRIBUTOR}</td><td>Default instance of a {@link DockActionDistributor}.</td></tr>
     <tr><tr>{@link FlatTheme#ACTION_DISTRIBUTOR}</td><td>Default instance of a {@link DockActionDistributor}.</td></tr>
     <tr><td>{@link DockTheme#BACKGROUND_PAINT} </td><td>The default value of the {@link BackgroundPaint}.</td></tr>
     *   <tr><td>{@link BasicTheme#BASIC_COLOR_SCHEME}</td><td>The {@link ColorScheme} to use if the {@link BasicTheme} is installed.</td></tr>
     <tr><td>{@link DockTheme#BORDER_MODIFIER} </td><td>The default value of the {@link BorderModifier}.</td></tr>
     <tr><td>{@link ScreenDockStation#BOUNDARY_RESTRICTION}</td><td>How far the user can push a window with a {@link Dockable} out of the screen(s).</td></tr>
     <tr><td>{@link BubbleTheme#BUBBLE_COLOR_SCHEME} </td><td>The {@link ColorScheme} to use if the {@link BubbleTheme} is installed.</td></tr>
     <tr><td>{@link FlapDockStation#BUTTON_CONTENT} </td><td>Tells what content should be on the buttons that represent minimized {@link Dockable}s.</td></tr>
     *   <tr><td>{@link FlapDockStation#BUTTON_CONTENT_FILTER} </td><td>Tells which {@link DockAction}s should be shown on a button representing a minimized {@link Dockable}.</td></tr>
     <tr><td>*&nbsp;{@link CControl#CCONTROL} </td><td>The {@link CControl} in whose realm the property is read, is a read-only property.</td></tr>
     <tr><td>{@link DockTheme#COMBINER} </td><td>Default value of the {@link Combiner}.</td></tr>
     <tr><td>{@link StackDockStation#COMPONENT_FACTORY} </td><td>The factory creating the "tabbed panes" of the {@link StackDockStation}.</td></tr>
     <tr><td>{@link DockTheme#DISPLAYER_FACTORY} </td><td>Default value of the {@link DisplayerFactory}.</td></tr>
     <tr><td>{@link PropertyKey#DOCK_STATION_ICON} </td><td>The default icon of {@link DockStation}s.</td></tr>
     <tr><td>{@link PropertyKey#DOCK_STATION_TITLE} </td><td>The default title of {@link DockStation}s.</td></tr>
     <tr><td>{@link PropertyKey#DOCK_STATION_TOOLTIP} </td><td>The default tooltip of {@link DockStation}s.</td></tr>
     <tr><td>{@link PropertyKey#DOCKABLE_ICON} </td><td>The default icon of {@link Dockable}s.</td></tr>
     <tr><td>{@link DockTheme#DOCKABLE_MOVING_IMAGE_FACTORY} </td><td>Default value of the {@link DockableMovingImageFactory}.</td></tr>
     <tr><td>{@link DockTheme#DOCKABLE_SELECTION} </td><td>Default value of the {@link DockableSelection}.</td></tr>
     <tr><td>{@link PropertyKey#DOCKABLE_TITLE} </td><td>The default title of {@link Dockable}s.</td></tr>
     <tr><td>{@link PropertyKey#DOCKABLE_TOOLTIP} </td><td>The default tooltip of {@link Dockable}s.</td></tr>
     <tr><td>{@link LocationModeManager#DOUBLE_CLICK_STRATEGY} </td><td>Tells what happens if the user double clicks on a {@link DockTitle} or a {@link Dockable}.</td></tr>
     <tr><td>{@link EclipseTheme#ECLIPSE_COLOR_SCHEME} </td><td>The {@link ColorScheme} to use if the {@link EclipseTheme} is installed.</td></tr>
     <tr><td>*&nbsp;{@link ScreenDockStation#EXPAND_ON_DOUBLE_CLICK} </td><td>Whether a double click on a child of a {@link ScreenDockStation} should maximize the child.</td></tr>
     <tr><td>{@link FlatTheme#FLAT_COLOR_SCHEME} </td><td>The {@link ColorScheme} to use if the {@link FlatTheme} is installed.</td></tr>
     <tr><td>{@link ScreenDockStation#FULL_SCREEN_STRATEGY} </td><td>Defines when a floating {@link Dockable} is considered to be in fullscreen mode.</td></tr>
     <tr><td>*&nbsp;{@link DockFrontend#HIDE_ACCELERATOR} </td><td>The {@link KeyStroke} that will call {@link DockFrontend#hide(Dockable)}</td></tr>
     <tr><td>{@link DockableSelector#INIT_SELECTION} </td><td>The {@link KeyStroke} that opens a window where the user can select a new {@link Dockable}.</td></tr>
     <tr><td>{@link StackDockStation#IMMUTABLE_SELECTION_INDEX} </td><td>Prevents the {@link StackDockStation} from switching the selected index on a drop operation (but does not prevent the {@link FocusManager} from switching the focus!).</td></tr>
     <tr><td>{@link CControl#KEY_CLOSE} </td><td>The {@link KeyStroke} that closes a {@link CDockable}.</td></tr>
     <tr><td>{@link CControl#KEY_GOTO_EXTERNALIZED} </td><td>The {@link KeyStroke} that externalizes a {@link CDockable}.</td></tr>
     <tr><td>{@link CControl#KEY_GOTO_MAXIMIZED} </td><td>The {@link KeyStroke} that maximizes a {@link CDockable}.</td></tr>
     <tr><td>{@link CControl#KEY_GOTO_MINIMIZED} </td><td>The {@link KeyStroke} that minimizes a {@link CDockable}.</td></tr>
     <tr><td>{@link CControl#KEY_GOTO_NORMALIZED} </td><td>The {@link KeyStroke} that normalizes a {@link CDockable}.</td></tr>
     <tr><td>{@link CControl#KEY_MAXIMIZE_CHANGE} </td><td>The {@link KeyStroke} that either maximizes or normalizes a {@link CDockable}.</td></tr>
     <tr><td>{@link FlapDockStation#LAYOUT_MANAGER} </td><td>Tells the {@link FlapDockStation} the size and the hold property of its children.</td></tr>
     <tr><td>{@link SplitDockStation#LAYOUT_MANAGER} </td><td>Logic of all {@link SplitDockStation}s, used when dropping a {@link Dockable} or resizing the station.</td></tr>
     <tr><td>{@link TabPane#LAYOUT_MANAGER} </td><td>Defines the size and location of tabs of a stack.</td></tr>
     <tr><td>*&nbsp;{@link SplitDockStation#MAXIMIZE_ACCELERATOR} </td><td>The {@link KeyStroke} that maximizes a child of a {@link SplitDockStation}.</td></tr>
     <tr><td>{@link CombinedMenuContent#MENU_CONTENT} </td><td>The menu that shows overflowing {@link Dockable}s on a stack.</td></tr>
     <tr><td>{@link IconManager#MINIMUM_ICON_SIZE}</td><td>The expected minimal size of all icons. </td></tr>
     <tr><td>{@link FlapDockStation#MINIMUM_SIZE} </td><td>The mimimum size of the {@link Component} that represents the {@link FlapDockStation}.</td></tr>
     <tr><td>*&nbsp;{@link LocationModeManager#MODE_ENABLEMENT} </td><td>Tells which {@link CDockable} is allowed to have which {@link ExtendedMode}.</td></tr>
     <tr><td>{@link DockRelocatorMode#NO_COMBINATION_MASK} </td><td>What keys the user has to press during a drag and drop operation to prevent the framework from combining {@link Dockable}s.</td></tr>
     <tr><td>{@link DockTitle#ORIENTATION_STRATEGY} </td><td>Tells how to rotate text on a {@link DockTitle}.</td></tr>
     <tr><td>{@link EclipseTheme#PAINT_ICONS_WHEN_DESELECTED} </td><td>Whether to paint icons on unselected tabs if using the {@link EclipseTheme}.</td></tr>
     <tr><td>{@link PlaceholderStrategy#PLACEHOLDER_STRATEGY} </td><td>A strategy that creates placeholders for {@link Dockable}s, see {@link CPlaceholderStrategy}.</td></tr>
     <tr><td>{@link CControl#RESIZE_LOCK_CONFLICT_RESOLVER} </td><td>Tells what happens if two {@link CDockable}s have a locked size and the user is resizing the parent of these two elements.</td></tr>
     <tr><td>{@link DockController#RESTRICTED_ENVIRONMENT} </td><td>Tells whether the application runs as applet/with webstart or as free or authenticated application.</td></tr>
     <tr><td>{@link DockRelocatorMode#SCREEN_MASK} </td><td>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}.</td></tr>
     <tr><td>{@link SingleTabDecider#SINGLE_TAB_DECIDER} </td><td>Tells which {@link Dockable}s should be presented with a single tab - even if there is no reason to show a tab.</td></tr>
     <tr><td>{@link DockTheme#STATION_PAINT} </td><td>The default value of {@link StationPaint}.</td></tr>
     <tr><td>{@link AWTComponentCaptureStrategy#STRATEGY} </td><td>How to make an image of an AWT component.</td></tr>
     <tr><td>{@link DisablingStrategy#STRATEGY}</td><td>Which element to disable.</td></tr>
     <tr><td>{@link StackDockStation#TAB_CONTENT_FILTER} </td><td>A filter deciding what content to show on a tab of a {@link StackDockStation}.</td></tr>
     <tr><td>{@link EclipseTheme#TAB_PAINTER} </td><td>The look of tabs if using the {@link EclipseTheme}.</td></tr>
     <tr><td>{@link StackDockStation#TAB_PLACEMENT} </td><td>The location of the tabs on a {@link StackDockStation}.</td></tr>
     <tr><td>{@link EclipseTheme#THEME_CONNECTOR} </td><td>Detailed instructions how to present a {@link Dockable} if using the {@link EclipseTheme}.</td></tr>
     <tr><td>{@link FlapDockStation#WINDOW_FACTORY} </td><td>A factory creating {@link FlapWindow}s for the {@link FlapDockStation}.</td></tr>
     <tr><td>{@link ScreenDockStation#WINDOW_FACTORY} </td><td>A factory creating {@link ScreenDockWindow}s for the {@link ScreenDockStation}.</td></tr>
     * </table>
     *
     *
     * @param <A> the type of the value
     * @param key the name of the property
     * @param value the new value, can be <code>null</code>
     */
    public <A> void putProperty( PropertyKey<A> key, A value ){
      putProperty( key, value, Priority.CLIENT );
    }
   
    /**
     * Changes the value of a property.
     * @param <A> 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 <code>null</code>
     * @see #putProperty(PropertyKey, Object)
     */
    protected <A> void putProperty( PropertyKey<A> key, A value, Priority priority ){
        frontend.getController().getProperties().set( key, value, priority );
    }

    /**
     * Gets the value of a property.
     * @param <A> the type of the property
     * @param key the name of the property
     * @return the value or <code>null</code>
     */
    public <A> A getProperty( PropertyKey<A> 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 <code>true</code> 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 <code>true</code>
     */
    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 <code>station</code> was {@link #addStation(CStation, boolean) added} to this {@link CControl}
     * with the <code>root</code> flag set to <code>true</code>.
     * @param station the station whose root flag is asked
     * @return the value of the root flag or <code>false</code> if <code>station</code> 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 <code>station</code>
     * @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<CStation<?>> getStations(){
        return register.getStations();
    }

    /**
     * Searches the {@link CStation} whose {@link CStation#getStation() internal representation}
     * is <code>intern</code>.
     * @param intern the internal representation
     * @return the station or <code>null</code>
     */
    public CStation<?> getStation( DockStation intern ){
      if( intern instanceof CommonDockStation<?,?>){
        return ((CommonDockStation<?, ?>)intern).getStation();
      }
        return null;
    }
   
    /**
     * Searches the {@link CStation} with unique identifier <code>id</code>.
     * @param id the identifier
     * @return the station or <code>null</code>
     */
    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 <code>dockable</code> was already registered at this {@link CControl}.
     * @param <S> the type of the new element
     * @param dockable the new element to show
     * @return <code>dockable</code>
     * @throws IllegalArgumentException if <code>dockable</code> already is registered at another {@link CControl}
     * or if the unique id of <code>dockable</code> already is used for another object
     */
    public <S extends SingleCDockable> 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 <code>id</code> is a valid identifier. This means that <code>id</code>
     * is not <code>null</code> and contains at least one sign that is not a whitespace.
     * @param id the unique identifier to check
     * @throws IllegalArgumentException if <code>id</code> 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
     * <code>id</code>.
     * @param id the identifier to look out for
     * @return the element with that identifier or <code>null</code>
     */
    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 <code>id</code>.
     * This has the same effect as calling {@link #remove(SingleCDockable)}.
     * @param id the id of the element to remove
     * @return <code>true</code> if the element was removed, <code>false</code>
     * otherwise
     */
    public boolean removeSingleDockable( String id ){
        for( SingleCDockable dockable : register.getSingleDockables() ){
            if( dockable.getUniqueId().equals( id )){
                return removeDockable( dockable );
            }
        }
        return false;
    }
   
    /**
     * Removes <code>dockable</code> from this control. The location information
     * for <code>dockable</code> 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, <code>false</code> 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.<br>
     * If there is already information for <code>id</code> available and
     * <code>id</code> should be visible, then the factory will be used
     * instantaneously.<br>
     * 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.<br>
     * If there is already information for identifiers that are included by <code>ids</code> available and
     * if they should be visible, then the <code>factory</code> will be used instantaneously to create these elements.
     * During this action <code>factory</code> has a higher priority than any other factory.<br>
     * 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 <code>factory</code>
     * @param factory the new factory
     */
    public void addSingleDockableFactory( Filter<String> 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 <code>id</code>. 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 <code>null</code>
     */
    public SingleCDockableFactory getSingleDockableFactory( String id ){
      return register.getBackupFactory().getFactory( id );
    }
   
    /**
     * Removes all occurrences of <code>factory</code>. Any location information that was held
     * because of the existence of <code>factory</code> 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
     * <code>id</code> 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 <code>dockable</code>, clients can also use {@link #addDockable(String, MultipleCDockable)} if
     * they want to specify the identifier themselves.
     * @param <M> the type of the new element
     * @param dockable the new element to show
     * @return <code>dockable</code>
     */
    public <M extends MultipleCDockable> M addDockable( M dockable) {
        Set<String> ids = new HashSet<String>();

        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 <code>dockable</code>.
     * @param <M> the type of the new element
     * @param uniqueId id the unique id of the new element
     * @param dockable the new element to show
     * @return <code>dockable</code>
     * @throws IllegalArgumentException if the unique identifier is already in
     * use, if <code>dockable</code> is already used elsewhere, if there is
     * no factory for <code>dockable</code>
     * @throws NullPointerException if any argument is <code>null</code>
     */
    public <M extends MultipleCDockable> 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 <code>oldDockable</code> with <code>newDockable</code>. The new dockable
     * inherits settings and location of the old one.
     * @param oldDockable the old dockable, not <code>null</code>
     * @param newDockable the new dockable, not <code>null</code>
     */
    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 <code>id</code>.
     * @param id the identifier to look out for
     * @return the element using <code>id</code> or <code>null</code> 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 <code>dockable</code>
     * @param dockable the item to search
     * @return the internal unique identifier of <code>dockable</code>, may be <code>null</code>
     */
    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{
            return missingStrategy.shouldStoreMulti( register.multiToNormalId( id ) );
        }
    }   
   
    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<?, MultipleCDockableLayout> normalizedFactory = (MultipleCDockableFactory<?, MultipleCDockableLayout>)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
     * <code>id</code>.
     * @param id the identifier to search for
     * @return the factory or <code>null</code>
     */
    public MultipleCDockableFactory<?, ?> getMultipleDockableFactory( String id ){
        return register.getFactory( id );
    }

    /**
     * Gets the unique identifier of <code>factory</code>.
     * @param factory the factory to search
     * @return the unique identifier or <code>null</code>
     */
    public String getFactoryId( MultipleCDockableFactory<?, ?> factory ){
      return access.getFactoryId( factory );
    }

    /**
     * Removes the {@link MultipleCDockableFactory} with identifier <code>id</code>
     * 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 <code>id</code>.
     * @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<MultipleCDockable> toRemove = new ArrayList<MultipleCDockable>();
            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 <code>CDockable</code>s.
     * @param defaultLocation the location, can be <code>null</code>
     */
    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 <code>null</code>
     * @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}.<br>
     * 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 <code>null</code>
     */
    public void setGroupBehavior( CGroupBehavior behavior ){
      locationManager.setGroupBehavior( behavior );
    }
   
    /**
     * Gets the currently used {@link CGroupBehavior}.
     * @return the current behavior, not <code>null</code>
     * @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
     * <code>theme</code> 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 <code>null</code>
     */
    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 <code>null</code>
     */
    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 <code>null</code>
     */
    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 <code>null</code>
     */
    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
     * <code>this</code> and the {@link #getPreferences() preferences}. This
     * model can be set to <code>null</code>.<br>
     * The default value of this property is <code>null</code>.
     * @param preferenceModel the new model, it will used to translate
     * the contents of {@link #getPreferences()} immediately, can be <code>null</code>
     */
    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 <code>this</code>.
     * @return the model, can be <code>null</code>
     * @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, <code>null</code> 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 <code>null</code>
     */
    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 <code>null</code>
     */
    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
     * <code>CDockable</code>s should be processed. There are no
     * guarantees that a resize requests can be granted or even gets processed.<br>
     * All requests, independent from whether they were processed, will be deleted
     * by this method.<br>
     * 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 <code>true</code>.<br>
     * The reasons behind forcing basic modes are:
     * <ul>
     *   <li>If the user changes the layout, he/she most likely would like to see the effects. A maximized {@link Dockable} would
     *  hide the effects.</li>
     <li>For the user re-maximizing an element requires no more than one click with the mouse. It's a cheap operation.</li>
     <li>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.</li>
     * </ul>
     * @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 transfered to another {@link CDockable}. This
     * feature is implemented by the method {@link #initTransferFocusOnMinimize(DockController)}, which may be
     * overriden by subclasses.
     * @param transferFocusOnMinimize whether to enable the feature or not (default is <code>true</code>)
     */
    public void setTransferFocusOnMinimize( boolean transferFocusOnMinimize ){
    this.transferFocusOnMinimize = transferFocusOnMinimize;
  }
   
    /**
     * If a {@link CDockable} is minimized, the focus can be automatically transfered to another {@link Dockable}.
     * @return whether the focus will be transfered
     * @see #setTransferFocusOnMinimize(boolean)
     */
    public boolean isTransferFocusOnMinimize(){
    return transferFocusOnMinimize;
  }

    /**
     * Writes the current and all known layouts into <code>file</code>.<br>
     * This is the same as calling <code>getResources().writeFile( file )</code>.
     * @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 <code>out</code>.<br>
     * This is the same as calling <code>getResources().writeStream( out )</code>.
     * @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 <code>element</code>.<br>
     * This is the same as calling <code>getResources().writeXML( element )</code>.
     * @param element the element to write into
     */
    public void writeXML( XElement element ){
        getResources().writeXML( element );
    }

    /**
     * Writes the current and all known layouts into <code>file</code> 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 <code>file</code>.<br>
     * This is the same as calling <code>getResources().readFile( file )</code>.
     * @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 <code>in</code>.<br>
     * This is the same as calling <code>getResources().readStream( in )</code>.
     * @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 <code>element</code>.<br>
     * This is the same as calling <code>getResources().readXML( element )</code>.
     * @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 <code>file</code>.
     * @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 );
    }

    /**
     * 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 );
    }

    /**
     * Loads an earlier stored layout.
     * @param name the name of the layout.
     */
    public void load( String name ){
        frontend.load( name );
    }

    /**
     * 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<String> settings = frontend.getSettings();
        return settings.toArray( new String[ settings.size() ] );
    }

    /**
     * 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<Dockable, ExtendedMode> nonBasic = new HashMap<Dockable, ExtendedMode>();
             
              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 CWorkingArea can't be made visible unless the CWorkingArea is visible." );
                    }
                }
               
                if( location == null ){
                    frontend.show( dockable.intern(), false );
                }
                else{
                    locationManager.setLocation( dockable.intern(), location );
                    if( !frontend.isShown( dockable.intern() )){
                      frontend.show( dockable.intern(), false );
                    }
                }
                locationManager.ensureValidLocation( dockable );
            }
            finally{
                register.setStalled( false );
            }
        }
       
        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<String, MultipleCDockableFactory<?, ?>> 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 );
        }
    }
}
TOP

Related Classes of bibliothek.gui.dock.common.CControl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.