/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.netbeans.core.windows;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.*;
import java.net.URL;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import org.netbeans.core.windows.actions.ActionUtils;
import org.netbeans.core.windows.options.WinSysPrefs;
import org.netbeans.core.windows.persistence.PersistenceManager;
import org.netbeans.core.windows.view.dnd.TopComponentDraggable;
import org.netbeans.core.windows.view.ui.MainWindow;
import org.netbeans.swing.tabcontrol.plaf.BusyTabsSupport;
import org.openide.nodes.Node;
import org.openide.util.*;
import org.openide.windows.*;

/**
 * This class extends WindowManager to provide all window system functionality.
 *
 * This class is final only for performance reasons. Can be freely
 * unfinaled if desired.
 *
 * @author Peter Zavadsky
 */
@org.openide.util.lookup.ServiceProvider(service=org.openide.windows.WindowManager.class)
public final class WindowManagerImpl extends WindowManager implements Workspace {
// XXX Implements Workspace for backward compatibility of old API only,
// there are no workspaces any more.
    
    // XXX PENDING additional, not-yet officialy supported properties.
    /** Name of property change fired when active mode changed. */
    public static final String PROP_ACTIVE_MODE = "activeMode"; // NOI18N
    /** Name of property change fired when maximized mode changed. */
    public static final String PROP_MAXIMIZED_MODE = "maximizedMode"; // NOI18N
    /** Name of property change fired when editor area state changed. */
    public static final String PROP_EDITOR_AREA_STATE = "editorAreaState"; // NOI18N    
    
    /** Init lock. */
    private static final Object LOCK_INIT = new Object();
    
    /** The only instance of the window manager implementation in the system */
    private static WindowManagerImpl defaultInstance;

    /** Flag to tell is assertions are on. Note it's package private and non-final for tests */
    static boolean assertsEnabled;

    /** Central unit of window system. */
    private final Central central = new Central();
    
    /** properties support */
    private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
    
    // PENDING
    /** Manages list of recently activated <code>TopCompoennt</code>s. */
    private final RecentViewList recentViewList = new RecentViewList(this);

    /** Only for hack 40237, to not call componentShowing twice */ 
    private TopComponent persistenceShowingTC;
    
    /** exclusive invocation of runnables */
    private Exclusive exclusive;

    /** flag that prevents calling Exclusive.run on each main window repaint */
    private boolean exclusivesCompleted = false;
    /** possibly stacktrace of creator */
    private Throwable createdBy;
    
    private final Collection<WindowSystemListener> listeners = new ArrayList<WindowSystemListener>(10);
    
    /** Default constructor. Don't use directly, use getDefault()
     * instead.
     */
    public WindowManagerImpl() {
        synchronized(LOCK_INIT) {
            // a static object to synchronize on
            if(defaultInstance != null) {
                IllegalStateException ex = new IllegalStateException("Instance already exists"); // NOI18N
                if (defaultInstance.createdBy != null) {
                    ex.initCause(defaultInstance.createdBy);
                }
                throw ex;
            }
            boolean on = false;
            assert on = true;
            if (on) {
                createdBy = new Exception("createdBy");
            }
            defaultInstance = this;
        }
        busyIconWarmUp();
    }
    
    /** Singleton accessor, returns instance of window manager implementation */
    public static WindowManagerImpl getInstance() {
        if (defaultInstance != null) {
            // Save a bunch of time accessing global lookup, acc. to profiler.
            return defaultInstance;
        }
        return (WindowManagerImpl)Lookup.getDefault().lookup(WindowManager.class);
    }
    
    @Override
    public void topComponentRequestAttention(TopComponent tc) {
        if( tc.isOpened() ) {
            ModeImpl mode = (ModeImpl) findMode(tc);

            central.topComponentRequestAttention(mode, tc);
        }
    }

    @Override
    public void topComponentCancelRequestAttention(TopComponent tc) {
        if( tc.isOpened() ) {
            ModeImpl mode = (ModeImpl) findMode(tc);

            central.topComponentCancelRequestAttention(mode, tc);
        }
    }

    @Override
    public void topComponentAttentionHighlight(TopComponent tc, boolean highlight) {
        if( tc.isOpened() ) {
            ModeImpl mode = (ModeImpl) findMode(tc);

            central.topComponentAttentionHighlight(mode, tc, highlight);
        }
    }

    /////////////////////////
    // API impelementation >>
    /////////////////////////
    
    // PENDING revise this method, it is dangerous to expose the GUI.
    /** Provides access to the MainWindow of the IDE.
     * Implements <code>WindowManager</code> abstract method.
     * @return the MainWindow */
    @Override
    public Frame getMainWindow() {
        warnIfNotInEDT();
        
        return central.getMainWindow();
    }
    
    /** Called after a current LookAndFeel change to update the IDE's UI
     * Implements <code>WindowManager</code> abstract method. */
    @Override
    public void updateUI() {
        warnIfNotInEDT();
        
        central.updateUI();
    }
    
    /** Creates a component manager for given top component.
     * Implements <code>WindowManager</code> abstract method.
     * @param c the component
     * @return the manager that handles opening, closing and selecting a component
     * @deprecated Don't use this. */
    @Override
    protected synchronized WindowManager.Component createTopComponentManager(TopComponent c) {
        warnIfNotInEDT();
        
        return null;
    }
    
    /** Creates new workspace with given name and display name.
     * Implements <code>WindowManager</code> abstract method.
     * @return fake implementation of only workspace
     * @deprecated Doesn't have a sense now. Workspaces aren't supported anymore. */
    @Override
    public Workspace createWorkspace(String name, String displayName) {
        warnIfNotInEDT();
        
        // get back fake workspace.
        return this;
    }

    /** Finds workspace given its name.
     * @return fake implementation of only workspace
     * @deprecated Doesn't have a sense now. Workspaces aren't supported anymore. */
    @Override
    public Workspace findWorkspace(String name) {
        warnIfNotInEDT();
        
        // PENDING what to return?
        return this;
    }
    
    /** List of all currenty available workspaces.
     * Implements <code>WindowManager</code> abstract method. 
     * @return array with only one (fake) workspace impl
     * @deprecated Doesn't have a sense now. Workspaces aren't supported anymore. */
    @Override
    public Workspace[] getWorkspaces() {
        warnIfNotInEDT();
        
        return new Workspace[] {this};
    }

    /** Sets new workspaces.
     * Implements <code>WindowManager</code> abstract method.
     * @param workspaces array of new workspaces
     * @deprecated Doesn't have a sense now. Workspaces aren't supported anymore. */
    @Override
    public void setWorkspaces(Workspace[] workspaces) {
        warnIfNotInEDT();
    }

    /** Gets current workspace. Can be changed by calling Workspace.activate ()
     * Implements <code>WindowManager</code> abstract method.
     * @return fake implementation of only workspace
     * @deprecated Doesn't have a sense now. Workspaces aren't supported anymore. */
    @Override
    public Workspace getCurrentWorkspace() {
        warnIfNotInEDT();
        
        // Gets back this as a fake workspace.
        return this;
    }

    /** Finds TopComponentGroup of given name. */
    @Override
    public TopComponentGroup findTopComponentGroup(String name) {
        assertEventDispatchThread();
        
        for(Iterator it = getTopComponentGroups().iterator(); it.hasNext(); ) {
            TopComponentGroupImpl group = (TopComponentGroupImpl)it.next();
            if(group.getName().equals(name)) {
                return group;
            }
        }
        
        return null;
    }
    
    /** Returns <code>TopComponent</code> for given unique ID.
     * @param tcID unique <code>TopComponent</code> ID
     * @return <code>TopComponent</code> instance corresponding to unique ID
     */
    @Override
    public TopComponent findTopComponent(String tcID) {
        warnIfNotInEDT();
        
        return getTopComponentForID(tcID);
    }
    
    /** Adds listener.
    * Implements <code>WindowManager</code> abstract method. */
    @Override
    public void addPropertyChangeListener(PropertyChangeListener l) {
        changeSupport.addPropertyChangeListener(l);
    }
    
    /** Removes listener.
     * Implements <code>WindowManager</code> abstract method. */
    @Override
    public void removePropertyChangeListener(PropertyChangeListener l) {
        changeSupport.removePropertyChangeListener(l);
    }

    ////////////////////////
    // API implementation <<
    ////////////////////////

//    /** Activates <code>TopComponent</code>, if it is opened. */
//    private boolean activateTopComponent(TopComponent tc) {
//        if(tc != null) {
//            // Find whether the component is in mode.
//            ModeImpl mode = (ModeImpl)findMode(tc);
//            if(mode != null) {
//                // Actually activates the TopComponent.
//                central.activateModeTopComponent(mode, tc);
//            } else {
//                // TopComponent not in mode yet.
//                return false;
//            }
//        }
//        
//        return true;
//    }
    
//    /** Selects <code>TopComponent</code>, if it is opened. */
//    protected void selectTopComponentImpl(TopComponent tc) {
//        if(tc != null) {
//            // Find whether the component is in mode.
//            ModeImpl mode = (ModeImpl)findMode(tc);
//            if(mode != null) {
//                // Actually select the TopComponent.
//                central.setModeSelectedTopComponent(mode, tc);
//            }
//        }
//    }
    

    // XXX For backward compatibility (Workspace class), this is the only (fake) workspace.
    // There are not supported workspaces any more.
    ///////////////////////////////////////
    // Start of  Workspace implementation>>
    ///////////////////////////////////////
    
    /** Gets the programmatic unique name of this workspace.
     * Implements <code>Workspace</code> interface method.
     * @return the programmatic name of only workspace impl
     * @deprecated Doesn't have a sense now. Workspaces aren't supported anymore. */
    @Override
    public String getName () {
        return "FakeWorkspace"; // NOI18N
    }
    
    /** Gets human-presentable name of the workspace.
     * Implements <code>Workspace</code> interface method.
     * @return the diplay name of the workspace
     * @deprecated Doesn't have a sense now. Workspaces aren't supported anymore. */
    @Override
    public String getDisplayName () {
        return NbBundle.getMessage(WindowManagerImpl.class, "LBL_FakeWorkspace");
    }

    /** Gets <code>Set</code> of all <code>Mode</code>'s.
     * Implements <code>Workspace</code> interface method. */
    @Override
    public Set<? extends ModeImpl> getModes () {
        return central.getModes();
    }
    
    /** Get bounds.
     * Implements <code>Workspace</code> interface method. */
    @Override
    public Rectangle getBounds () {
        if(getEditorAreaState() == Constants.EDITOR_AREA_JOINED) {
            return getMainWindowBoundsJoined();
        } else {
            return getMainWindowBoundsSeparated();
        }
    }

    /** Activates this workspace to be current one.
     * @deprecated Doesn't have a sense now. Workspaces aren't supported anymore. */
    @Override
    public void activate () {
    }
    
    /** Creates new <code>Mode</code>.
     * Implements <code>Workspace</code> interface method.
     * @param name a unique programmatic name of the mode 
     * @param displayName <em>ignored</em> doesn't have a sense now
     * @param icon <em>ignored</em> doesn't have a sense now
     * @return the new mode */
    @Override
    public Mode createMode(String name, String displayName, URL icon) {
        if(getEditorAreaState() == Constants.EDITOR_AREA_JOINED) {
            return new WrapMode (createMode(name, Constants.MODE_KIND_EDITOR, Constants.MODE_STATE_JOINED, false, null));
        } else {
            // #36945 In 'separate' ui mode create new mode.
            return createMode(name, Constants.MODE_KIND_VIEW, Constants.MODE_STATE_SEPARATED, false,
                new SplitConstraint[] { new SplitConstraint(Constants.HORIZONTAL, 1, 0.2)});
        }
    }

    /**
     * User made the given mode floating.
     * @param mode 
     * @since 2.30
     */
    public void userUndockedMode( ModeImpl mode ) {
        if( mode.getState() != Constants.MODE_STATE_JOINED ) {
            throw new IllegalStateException("Mode is already in floating state: " + mode);
        }

        central.userUndockedMode(mode);
    }

    /**
     * User docked the given mode back to the main window.
     * @param mode 
     * @since 2.30
     */
    public void userDockedMode( ModeImpl mode ) {
        if( mode.getState() != Constants.MODE_STATE_SEPARATED ) {
            throw new IllegalStateException("Mode is not in floating state: " + mode);
        }

        central.userDockedMode(mode);
    }

    /**
     * User minimized the whole mode.
     * @param mode 
     * @since 2.30
     */
    public void userMinimizedMode( ModeImpl mode ) {
        assertEventDispatchThread();
        
        getCentral().userMinimizedMode( mode );
    }

    /**
     * User restored the whole mode from minimized state.
     * @param slidingMode Sliding mode to be searched for minimized TopComponents
     * @param modeToRestore Mode to restored from minimized state.
     * @since 2.35
     */
    public void userRestoredMode( ModeImpl slidingMode, ModeImpl modeToRestore ) {
        assertEventDispatchThread();
        
        getCentral().userRestoredMode( slidingMode, modeToRestore );
    }

    /**
     * User closed the whole mode.
     * @param mode 
     * @since 2.30
     */
    public void userClosedMode( ModeImpl mode ) {
        assertEventDispatchThread();

        getCentral().userClosedMode( mode );
    }

    /**
     * @return the exclusive
     */
    private Exclusive getExclusive() {
        if (exclusive == null) {
            exclusive = new Exclusive();
        }
        return exclusive;
    }

    private void toggleUseNativeFileChooser() {
        if( null == System.getProperty("nb.native.filechooser") ) { //NOI18N
            boolean useNativeFileChooser = WinSysPrefs.HANDLER.getBoolean(WinSysPrefs.MAXIMIZE_NATIVE_LAF, false);
            System.setProperty("nb.native.filechooser", useNativeFileChooser ? "true" : "false"); //NOI18N
        }
    }
    
    private static class WrapMode implements Mode {
        private Mode wrap;
        
        public WrapMode (Mode wrap) {
            this.wrap = wrap;
        }
        
        @Override
        public void addPropertyChangeListener (PropertyChangeListener list) {
            wrap.addPropertyChangeListener (list);
        }
        
        @Override
        public boolean canDock (TopComponent tc) {
            return wrap.canDock (tc);
        }
        
        @Override
        public boolean dockInto (TopComponent c) {
            if (c.getClientProperty (Constants.TOPCOMPONENT_ALLOW_DOCK_ANYWHERE) == null) {
                c.putClientProperty (Constants.TOPCOMPONENT_ALLOW_DOCK_ANYWHERE, Boolean.TRUE);
            }
            return wrap.dockInto (c);
        }
        
        @Override
        public Rectangle getBounds () {
            return wrap.getBounds ();
        }
        
        @Override
        public String getDisplayName () {
            return wrap.getDisplayName ();
        }
        
        @Override
        public Image getIcon () {
            return wrap.getIcon ();
        }
        
        @Override
        public String getName () {
            return wrap.getName ();
        }
        
        @Override
        public TopComponent getSelectedTopComponent () {
            return wrap.getSelectedTopComponent ();
        }
        
        @Override
        public TopComponent[] getTopComponents () {
            return wrap.getTopComponents ();
        }
        
        @Override
        public Workspace getWorkspace () {
            return wrap.getWorkspace ();
        }
        
        @Override
        public void removePropertyChangeListener (PropertyChangeListener list) {
            wrap.removePropertyChangeListener (list);
        }
        
        @Override
        public void setBounds (Rectangle s) {
            wrap.setBounds (s);
        }
    } // end of WrapMode

    /** Finds mode by specified name.
     * Implements <code>Workspace</code> interface method.
     * @param name the name of the mode to search for
     * @return the mode with that name, or <code>null</code> */
    @Override
    public Mode findMode(String name) {
        return findModeImpl(name);
    }
    
    /** Finds mode the component is in.
     * Implements <code>Workspace</code> interface method.
     * @param c component to find mode for
     * @return the mode or <code>null</code> if the component is not in any mode */
    @Override
    public Mode findMode(TopComponent tc) {
        if(tc == null) {
            // Log something?
            return null;
        }

        //#235242
        TopComponent multiviewParent = ( TopComponent ) SwingUtilities.getAncestorOfClass( TopComponent.class, tc);
        if( null != multiviewParent )
            tc = multiviewParent;
        
        for(Iterator it = getModes().iterator(); it.hasNext(); ) {
            ModeImpl mode = (ModeImpl)it.next();
            
            if(mode.containsTopComponent(tc)) {
                return mode;
            }
        }

        return null;
    }
    
    /** Clears this workspace and removes this workspace from window manager.
     * Implements <code>Workspace</code> interface method.
     * @deprecated Doesn't have a sense now. Workspaces aren't supported anymore. */
    @Override
    public void remove () {
    }
    ////////////////////////////////////
    // End of Workspace implementation<<
    ////////////////////////////////////

    //////////////////////////////
    // TopComponentGroup>>
    public void addTopComponentGroup(TopComponentGroupImpl tcGroup) {
        central.addTopComponentGroup(tcGroup);
    }
    
    public void removeTopComponentGroup(TopComponentGroupImpl tcGroup) {
        central.removeTopComponentGroup(tcGroup);
    }
    
    public Set<TopComponentGroupImpl> getTopComponentGroups() {
        return central.getTopComponentGroups();
    }
    // TopComponentGroup<<
    //////////////////////////////

    
    /// Copy from older WorkspaceImpl>>

    ////////////////////////////////////////////////////////
    // PENDING some of the next methods could make inner API
    /** Creates new mode.
     * @param name a unique programmatic name of the mode 
     * @param permanent true if mode has to remain in model even it is emptied */
    public ModeImpl createMode(String name, int kind, int state, boolean permanent, SplitConstraint[] constraints) {
        // It gets existing mode with the same name.
        ModeImpl mode = (ModeImpl)findMode(name);
        if(mode != null) {
            return mode;
        }
        
        // XXX PENDING When no constraints are specified, default (editor or view) mode is returned.
        if(constraints == null && kind != Constants.MODE_KIND_SLIDING) {
            if(kind == Constants.MODE_KIND_EDITOR) {
                return getDefaultEditorMode();
            } else {
                return getDefaultViewMode();
            }
        }

        mode = createModeImpl(name, kind, state, permanent);
        addMode(mode, constraints);
        return mode;
    }
    
    public ModeImpl createSlidingMode(String name, boolean permanent, String side, Map<String,Integer> slideInSizes) {
        // It gets existing mode with the same name.
        ModeImpl mode = (ModeImpl)findMode(name);
        if(mode != null) {
            return mode;
        }
        
        mode = createModeImpl(name, Constants.MODE_KIND_SLIDING, permanent);
        central.addSlidingMode(mode, null, side, slideInSizes);
        return mode;
    }
    
    
    /*private*/ ModeImpl createModeImpl(String name, int kind, boolean permanent) {
        int state = getEditorAreaState() == Constants.EDITOR_AREA_JOINED
                                                ? Constants.MODE_STATE_JOINED
                                                : Constants.MODE_STATE_SEPARATED;
        return createModeImpl(name, kind, state, permanent);
    }
    
    /** */
    /*private*/ ModeImpl createModeImpl(String name, int kind, int state, boolean permanent) {
        if(name == null) {
            name = ModeImpl.getUnusedModeName();
        }
        ModeImpl toReturn =  ModeImpl.createModeImpl(name, state, kind, permanent);
        return toReturn;
    }

    // XXX
    /** Gets default mode. */
    /*private*/ ModeImpl getDefaultEditorMode() {
        ModeImpl mode = findModeImpl("editor"); // NOI18N
        if(mode == null) {
            Logger.getLogger(WindowManagerImpl.class.getName()).log(Level.FINE, null,
                              new java.lang.IllegalStateException("Creating default editor mode. It shouldn\'t happen this way")); // NOI18N
            // PENDING should be defined in winsys layer?
            ModeImpl newMode = createModeImpl("editor", Constants.MODE_KIND_EDITOR, true); // NOI18N
            addMode(newMode, new SplitConstraint[0]);
            return newMode;
        } else {
            return mode;
        }
    }
    
    /** Gets default mode for opening new component. */
    /*private*/ ModeImpl getDefaultEditorModeForOpen() {
        ModeImpl mode = central.getLastActiveEditorMode();
        if (mode == null) {
            return getDefaultEditorMode();
        } else {
            return mode;
        }
    }
    
    // XXX
    /** Gets default view mode. */
    ModeImpl getDefaultViewMode() {
        ModeImpl mode = findModeImpl("explorer"); // NOI18N
        if(mode == null) {
            Logger.getLogger(WindowManagerImpl.class.getName()).log(Level.INFO, null,
                              new java.lang.IllegalStateException("Creating default view mode. It shouldn\'t happen this way")); // NOI18N
            // PENDING should be defined in winsys layer?
            ModeImpl newMode = createModeImpl("explorer", Constants.MODE_KIND_VIEW, true); // NOI18N
            addMode(newMode, new SplitConstraint[] {
                new SplitConstraint(Constants.VERTICAL, 0, 0.7D),
                new SplitConstraint(Constants.HORIZONTAL, 0, 0.25D)
            });
            return newMode;
        } else {
            return mode;
        }
    }
    
    /** Gets default sliding view mode. */
    ModeImpl getDefaultSlidingMode() {
        ModeImpl mode = findModeImpl("sliding"); // NOI18N
        if(mode == null) {
            Logger.getLogger(WindowManagerImpl.class.getName()).log(Level.INFO, null,
                              new java.lang.IllegalStateException("Creating default sliding mode. It shouldn\'t happen this way")); // NOI18N
            // PENDING should be defined in winsys layer?
            ModeImpl newMode = createModeImpl("sliding", Constants.MODE_KIND_SLIDING, true); // NOI18N
            addMode(newMode, new SplitConstraint[] {
                new SplitConstraint(Constants.VERTICAL, 0, 0.7D),
                new SplitConstraint(Constants.HORIZONTAL, 0, 0.25D)
            });
            return newMode;
        } else {
            return mode;
        }
    }
    
    private ModeImpl findModeImpl(String name) {
        if(name == null) {
            // PENDING log something?
            return null;
        }
        
        for(Iterator it = getModes().iterator(); it.hasNext(); ) {
            ModeImpl mode = (ModeImpl)it.next();
            if (name.equals(mode.getName()) || mode.getOtherNames().contains( name ) ) {
                return mode;
            }
        }
        
        return null;
    }
    
    // XXX PENDING see WindowManager
    public TopComponent getSelectedTopComponent(Mode mode) {
        return central.getModeSelectedTopComponent((ModeImpl)mode);
    }
    
    public Rectangle getMainWindowBoundsJoined() {
        return central.getMainWindowBoundsJoined();
    }
    
    public void setMainWindowBoundsJoined(Rectangle bounds) {
        central.setMainWindowBoundsJoined(bounds);
    }
    
    public Rectangle getMainWindowBoundsSeparated() {
        return central.getMainWindowBoundsSeparated();
    }
    
    public void setMainWindowBoundsSeparated(Rectangle bounds) {
        central.setMainWindowBoundsSeparated(bounds);
    }
    
    public int getMainWindowFrameStateJoined() {
        return central.getMainWindowFrameStateJoined();
    }
    
    public void setMainWindowFrameStateJoined(int frameState) {
        central.setMainWindowFrameStateJoined(frameState);
    }
    
    public int getMainWindowFrameStateSeparated() {
        return central.getMainWindowFrameStateSeparated();
    }
    
    public void setMainWindowFrameStateSeparated(int frameState) {
        central.setMainWindowFrameStateSeparated(frameState);
    }
    
    
    /** Gets active mode.
     * @return active mode */
    public ModeImpl getActiveMode () {
        return central.getActiveMode();
    }
    
    /** Sets active mode.
     * @param current active mode */
    public void setActiveMode(ModeImpl activeMode) {
        central.setActiveMode(activeMode);
    }
    
    public void setEditorAreaBounds(Rectangle editorAreaBounds) {
        central.setEditorAreaBounds(editorAreaBounds);
    }
    
    public Rectangle getEditorAreaBounds() {
        return central.getEditorAreaBounds();
    }

    /** Sets editor area constraints. */
    public void setEditorAreaConstraints(SplitConstraint[] editorAreaConstraints) {
        central.setEditorAreaConstraints(editorAreaConstraints);
    }
    
    public java.awt.Component getEditorAreaComponent() {
        return central.getEditorAreaComponent();
    }
    
    /** Gets editor area constraints. */
    public SplitConstraint[] getEditorAreaConstraints() {
        return central.getEditorAreaConstraints();
    }
    
    /** Sets editor area state. */
    public void setEditorAreaState(int editorAreaState) {
        setEditorAreaStateImpl(editorAreaState);
    }
    
    // XXX
    void setEditorAreaStateImpl(int editorAreaState) {
        central.setEditorAreaState(editorAreaState);
    }
    
    public int getEditorAreaState() {
        return central.getEditorAreaState();
    }
    
    public void setEditorAreaFrameState(int editorAreaFrameState) {
        central.setEditorAreaFrameState(editorAreaFrameState);
    }
    
    public int getEditorAreaFrameState() {
        return central.getEditorAreaFrameState();
    }
    
    /** 
     * Sets new maximized mode or cancels the current one. 
     * @param newMaximizedMode Mode to set as the maximized one or null to cancel the current one.
     */
    public void switchMaximizedMode(ModeImpl newMaximizedMode) {
        central.switchMaximizedMode(newMaximizedMode);
    }
    
    /** Sets editor mode that is currenlty maximized (used when the window system loads) */
    public void setEditorMaximizedMode(ModeImpl editorMaximizedMode) {
        central.setEditorMaximizedMode(editorMaximizedMode);
    }
    
    /** Sets view mode that is currenlty maximized (used when the window system loads) */
    public void setViewMaximizedMode(ModeImpl viewMaximizedMode) {
        central.setViewMaximizedMode(viewMaximizedMode);
    }
    
    /** Gets mode that is currently maximized. */
    public ModeImpl getCurrentMaximizedMode() {
        return central.getCurrentMaximizedMode();
    }
    
    /** Gets editor maximized mode. */
    public ModeImpl getEditorMaximizedMode() {
        return central.getEditorMaximizedMode();
    }
    
    /** Gets view maximized mode. */
    public ModeImpl getViewMaximizedMode() {
        return central.getViewMaximizedMode();
    }
    
    /** Sets constraints, delegates from ModeImpl. */
    public void setModeConstraints(ModeImpl mode, SplitConstraint[] modeConstraints) {
        central.setModeConstraints(mode, modeConstraints);
    }
    
    /** Gets constraints, delegates from ModeImpl. */
    public SplitConstraint[] getModeConstraints(ModeImpl mode) {
        return central.getModeConstraints(mode);
    }

    /** Adds mode. */
    private void addMode(ModeImpl mode, SplitConstraint[] modeConstraints) {
        if (mode.getKind() == Constants.MODE_KIND_SLIDING) {
            // TODO.. where to get the side..
            central.addSlidingMode(mode, null, Constants.LEFT, null);
        } else {
            central.addMode(mode, modeConstraints);
        }
    }
    
    /** Removes mode. */
    public void removeMode(ModeImpl mode) {
        if (mode.getKind() == Constants.MODE_KIND_SLIDING) {
            
        } else {
            central.removeMode(mode);
        }
    }

    /** Sets toolbar configuration name. */
    public void setToolbarConfigName(String toolbarConfigName) {
        central.setToolbarConfigName(toolbarConfigName);
    }

    /** Gets toolbar configuration name.
     * @return toolbar configuration name */
    public String getToolbarConfigName () {
        return central.getToolbarConfigName();
    }

    // Copy from older WorkspaceImpl<< 
    
   

    /** Sets visible or invisible window system GUI. */
    public void setVisible(boolean visible) {
        if( !visible ) {
            FloatingWindowTransparencyManager.getDefault().stop();
        }
        central.setVisible(visible);

        // handle timer that assures runnign of exclusives when somehow
        // mainWindow.paint is not called during startup
        if (visible) {
            if (!exclusivesCompleted) {
                getExclusive().restart();
            } else {
                FloatingWindowTransparencyManager.getDefault().start();
            }
            toggleUseNativeFileChooser();
        } else {
            getExclusive().stop();
            exclusivesCompleted = false;
        }
    }
    
    /** Creates a new mode on the side of the given mode 
     * @since 2.32
     */
    public ModeImpl attachModeToSide(ModeImpl referenceMode, String side, String modeName, int modeKind, boolean permanent) {
        return central.attachModeToSide(referenceMode, side, modeName, modeKind, permanent);
    }
    
    /** Indicates whether windows system shows GUI. */
    public boolean isVisible() {
        return central.isVisible();
    }
    
    // XXX
    public TopComponent getTopComponentForID(String tcID) {
        return PersistenceHandler.getDefault().getTopComponentForID(tcID,true);
    }
    
    public boolean isTopComponentAllowedToMoveAnywhere(TopComponent tc) {
        if(Boolean.TRUE.equals(tc.getClientProperty(Constants.TOPCOMPONENT_ALLOW_DOCK_ANYWHERE))) {
            return true;
        }
        
        return false;
    }
    
    // XXX
    public ModeImpl findModeForOpenedID(String tcID) {
        if(tcID == null) {
            return null;
        }
        
        for(ModeImpl mode: getModes()) {
            if(mode.getOpenedTopComponentsIDs().contains(tcID)) {
                return mode;
            }
        }
        
        return null;
    }
    
    // XXX
    public ModeImpl findModeForClosedID(String tcID) {
        if(tcID == null) {
            return null;
        }
        
        for(ModeImpl mode: getModes()) {
            if(mode.getClosedTopComponentsIDs().contains(tcID)) {
                return mode;
            }
        }
        
        return null;
    }
    
    /** Helper method to retrieve some form of display name of TopComponent.
     * First tries TopComponent's getHtmlDisplayName, if is it null then continues
     * with getDisplayName and getName in this order.
     *
     * @param tc TopComponent to retrieve display name from. May be null.
     * @return TopComponent's display name or null if no display name available
     * or null TopComponent is given
     */
    public String getTopComponentDisplayName(TopComponent tc) {
        if(tc == null) {
            return null;
        }
        String displayName = tc.getHtmlDisplayName();
        if (displayName == null) {
            displayName = tc.getDisplayName();
        }
        if (displayName == null) {
            displayName = tc.getName();
        }
        return displayName;
    }
    
    // PENDING for ModeImpl only.
    Central getCentral() {
        return central;
    }

    // XXX
    public boolean isDragInProgress() {
        return central.isDragInProgress();
    }
    
    /** Analyzes bounds of given top component and finds appropriate side
     * of desktop for sliding for given top component.
     *
     * @param tc top component to find side for sliding for
     * @return side where top component should live in sliding state
     * @see Constants.LEFT 
     */ 
    public String guessSlideSide(TopComponent tc) {
        return central.guessSlideSide(tc);
    }

    /**
     * {@inheritDoc}
     */
    public boolean isDocked (TopComponent comp) {
        return central.isDocked(comp);
    }

    /** Takes given top component out of the main window and puts it in
     * new separate floating window.
     *
     * @param tc TopComponent to make floating
     * @param mode mode where TopComponent currently lives (before undock)
     *
     * @throws IllegalStateException when given top component is already floating
     */
    public void userUndockedTopComponent(TopComponent tc, ModeImpl mode) {
        if (!isDocked(tc)) {
            throw new IllegalStateException("TopComponent is already in floating state: " + tc);
        }

        central.userUndockedTopComponent(tc, mode);
    }

    /** Puts given top component back into main window.
     *
     * @param tc TopComponent to put back into main window
     * @param mode mode where TopComponent currently lives (before dock)
     *
     * @throws IllegalStateException when given top component is already inside main window
     */
    public void userDockedTopComponent(TopComponent tc, ModeImpl mode) {
        if (isDocked(tc)) {
            throw new IllegalStateException("TopComponent is already inside main window: " + tc);
        }

        central.userDockedTopComponent(tc, mode);
    }
    /**
     * 
     * @param tc
     * @param minimized 
     * @since 2.32
     */
    @Override
    public void setTopComponentMinimized( TopComponent tc, boolean minimized ) {
        assertEventDispatchThread();
        central.setTopComponentMinimized( tc, minimized );
    }
    
    /**
     * 
     * @param tc
     * @return 
     * @since 2.32
     */
    @Override
    public boolean isTopComponentMinimized( TopComponent tc ) {
        assertEventDispatchThread();
        return central.isTopComponentMinimized( tc );
    }

    // PENDING>>
    public void setRecentViewList(String[] tcIDs) {
        recentViewList.setTopComponents(tcIDs);
    }
    
    public TopComponent[] getRecentViewList() {
        return recentViewList.getTopComponents();
    }

    public String[] getRecentViewIDList() {
        return recentViewList.getTopComponentIDs();
    }
    // PENDING<<
    
    void doFirePropertyChange(final String propName,
    final Object oldValue, final Object newValue) {
        // PENDING When #37529 finished, then uncomment the next row and move the
        // checks of AWT thread away.
        //  WindowManagerImpl.assertEventDispatchThread();
        if(SwingUtilities.isEventDispatchThread()) {
            changeSupport.firePropertyChange(propName, oldValue, newValue);
        } else {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    changeSupport.firePropertyChange(propName, oldValue, newValue);
                }
            });
        }
    }

    // PENDING used in persistence only, revise how to restrict its usage only there.
    /** Gets persistence observer. */
    public org.netbeans.core.windows.persistence.PersistenceObserver getPersistenceObserver() {
        return PersistenceHandler.getDefault();
    }

    
    /////////////////////////
    // Notifications>>
    public void notifyTopComponentOpened(TopComponent tc) {
        // Inform component instance.
        componentOpenNotify(tc);
        // then let others know that top component was opened...
        notifyRegistryTopComponentOpened(tc);
    }
    
    public void notifyTopComponentClosed(TopComponent tc) {
        // Inform component instance.
        componentCloseNotify(tc);
        // let others know that top component was closed...
        notifyRegistryTopComponentClosed(tc);
    }
    // Notifications<<
    /////////////////////////

    /////////////////////////////
    // Registry notifications
    static void notifyRegistryTopComponentActivated(final TopComponent tc) {
        ((RegistryImpl)getDefault().getRegistry()).topComponentActivated(tc);

        
        // #37457 It is needed to ensure the activation calls are in AWT thread.
        if(SwingUtilities.isEventDispatchThread()) {
            WindowManagerImpl.getInstance().activateComponent(tc);
        } else {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    WindowManagerImpl.getInstance().activateComponent(tc);
                }
            });
        }
    }
    
    private static void notifyRegistryTopComponentOpened(TopComponent tc) {
        ((RegistryImpl)getDefault().getRegistry()).topComponentOpened(tc);
    }
    
    private static void notifyRegistryTopComponentClosed(TopComponent tc) {
        ((RegistryImpl)getDefault().getRegistry()).topComponentClosed(tc);
    }
    
    private static void notifyRegistrySelectedNodesChanged(TopComponent tc, Node[] nodes) {
        ((RegistryImpl)getDefault().getRegistry()).selectedNodesChanged(tc, nodes);
    }
    // Registry notifications
    /////////////////////////////

    /** Overrides superclass method, to enhance access modifier. */
    @Override
    public void componentShowing(TopComponent tc) {
        if((tc != null) && (tc != persistenceShowingTC)) {
            super.componentShowing(tc);
        }
    }
    
    /** XXX - Hack for 40237, should be changed to fix real reason of 37188
     * timing of activate events */
    void specialPersistenceCompShow(TopComponent tc) {
        componentShowing(tc);
        persistenceShowingTC = tc;
    }
    
    /** Overrides superclass method, to enhance access modifier. */
    @Override
    public void componentHidden(TopComponent tc) {
        if(tc != null) {
            super.componentHidden(tc);
            if (tc == persistenceShowingTC) {
                persistenceShowingTC = null;
            }
        }
    }

    
    // Manipulating methods (overriding the superclass dummy ones) >>
    @Override
    protected void topComponentOpen (TopComponent tc) {
        topComponentOpenAtTabPosition(tc, -1);
    }

    @Override
    protected void topComponentOpenAtTabPosition (TopComponent tc, int position) {
        warnIfNotInEDT();
        
        if (tc == null) {
            throw new IllegalArgumentException ("Cannot open a null " +
                "TopComponent"); //NOI18N
        }
        
        ModeImpl mode = getMode(tc);
        
        if (mode == null) {
            mode = getDefaultEditorModeForOpen();
            Collection<? extends ModeSelector> selectors = Lookup.getDefault().lookupAll(ModeSelector.class);
            for (ModeSelector s : selectors) {
                Mode hintMode = s.selectModeForOpen(tc, mode);
                if (hintMode instanceof ModeImpl) {
                    mode = (ModeImpl)hintMode;
                    break;
                }
            }
            assert getModes().contains(mode) : "Mode " + mode.getName() + " is not in model."; //NOI18N
            if (tc.getClientProperty (Constants.TOPCOMPONENT_ALLOW_DOCK_ANYWHERE) == null) {
                tc.putClientProperty (Constants.TOPCOMPONENT_ALLOW_DOCK_ANYWHERE, Boolean.TRUE);
            }
        }
        boolean alreadyOpened = mode.getOpenedTopComponents().contains( tc );
        
        TopComponentTracker.getDefault().add( tc, mode );

        // XXX PENDING If necessary, unmaximize the state, but exclude sliding modes
        // Consider to put it in addOpenedTopComponent, to do it in one step.
        ModeImpl maximizedMode = getCurrentMaximizedMode();
        if(maximizedMode != null && mode != maximizedMode
           && mode.getKind() != Constants.MODE_KIND_SLIDING
           && (central.isViewMaximized() || mode.getKind() == Constants.MODE_KIND_EDITOR)) {
            switchMaximizedMode(null);
        }
        
        if (position == -1) {
            mode.addOpenedTopComponent(tc);
        } else {
            mode.addOpenedTopComponent(tc, position);
        }
        
        if( central.isEditorMaximized() 
                && !alreadyOpened 
                && mode.getState() != Constants.MODE_STATE_SEPARATED ) {
            //the editor is maximized so the newly opened TopComponent should slide out
            String tcID = findTopComponentID( tc );
            if( !isTopComponentDockedInMaximizedMode( tcID ) && mode.getKind() == Constants.MODE_KIND_VIEW ) {
                //slide the TopComponent to edgebar and slide it out
                central.slide( tc, mode, central.getSlideSideForMode( mode ) );

                topComponentRequestActive( tc );
                return;
            }
        }
        if( mode.isMinimized() && Switches.isTopComponentAutoSlideInMinimizedModeEnabled() ) {
            central.slide( tc, mode, central.getSlideSideForMode( mode ) );

            topComponentRequestActive( tc );
        }
    }
    
    @Override
    protected int topComponentGetTabPosition (TopComponent tc) {
        warnIfNotInEDT();
        
        ModeImpl mode = getModeForOpenedTopComponent(tc);
        if(mode != null) {
            return mode.getTopComponentTabPosition(tc);
        } else {
            return -1;
        }
    }
    
    @Override
    protected void topComponentClose(TopComponent tc) {
        warnIfNotInEDT();
        
        boolean opened = topComponentIsOpened(tc);
        boolean inCloseAll = tc.getClientProperty("inCloseAll") != null;
        tc.putClientProperty("inCloseAll", null);

        if(!opened) {
            return;
        }

        ModeImpl mode = getModeForOpenedTopComponent(tc);
        if(mode != null) {
            if( mode == central.getCurrentMaximizedMode() && central.isViewMaximized() ) {
                central.switchMaximizedMode( null );
                topComponentClose( tc );
            } else {
                TopComponent recentTc = null;
                if( mode.getKind() == Constants.MODE_KIND_EDITOR && !inCloseAll ) {
                    //an editor document is being closed so let's find the most recent editor to select
                    recentTc = central.getRecentTopComponent( mode, tc );
                }
                mode.close(tc);
                if( !tc.isOpened() && null != recentTc )
                    mode.setSelectedTopComponent(recentTc);
            }
        }
    }
    
    @Override
    protected void topComponentRequestActive(TopComponent tc) {
        warnIfNotInEDT();
        
        ModeImpl mode = getModeForOpenedTopComponent(tc);
        if(mode != null) {
            central.activateModeTopComponent(mode, tc);
        }
    }
    
    @Override
    protected void topComponentRequestVisible(TopComponent tc) {
        warnIfNotInEDT();
        
        ModeImpl mode = getModeForOpenedTopComponent(tc);
        if(mode != null) {
            central.setModeSelectedTopComponent(mode, tc);
            if( mode.getState() == Constants.MODE_STATE_SEPARATED ) {
                tc.toFront();
            }
        }
    }

    @Override
    protected void topComponentDisplayNameChanged(TopComponent tc, String displayName) {
        warnIfNotInEDT();
        
        ModeImpl mode = getModeForOpenedTopComponent(tc);
        if(mode != null) {
            central.topComponentDisplayNameChanged(mode, tc);
        }
    }
    
    @Override
    protected void topComponentHtmlDisplayNameChanged(TopComponent tc, String htmlDisplayName) {
        // do the same thing as for display name, we can because string param is ignored
        topComponentDisplayNameChanged(tc, null);
    }
    
    @Override
    protected void topComponentToolTipChanged(TopComponent tc, String toolTip) {
        warnIfNotInEDT();
        
        ModeImpl mode = getModeForOpenedTopComponent(tc);
        if(mode != null) {
            central.topComponentToolTipChanged(mode, tc);
        }
    }
    
    @Override
    protected void topComponentIconChanged(TopComponent tc, Image icon) {
        warnIfNotInEDT();
        
        ModeImpl mode = getModeForOpenedTopComponent(tc);
        if(mode != null) {
            central.topComponentIconChanged(mode, tc);
        }
    }

    @Override
    protected void topComponentActivatedNodesChanged(TopComponent tc, Node[] activatedNodes) {
        warnIfNotInEDT();
        
        notifyRegistrySelectedNodesChanged(tc, activatedNodes);
    }
    
    @Override
    protected boolean topComponentIsOpened(TopComponent tc) {
        warnIfNotInEDT();
        
        return getModeForOpenedTopComponent(tc) != null;
    }
    
    @Override
    protected Action[] topComponentDefaultActions(TopComponent tc) {
        warnIfNotInEDT();
        
        return ActionUtils.createDefaultPopupActions(tc);
    }
    
    @Override
    protected String topComponentID (TopComponent tc, String preferredID) {
        warnIfNotInEDT();
        
        if (preferredID == null) {
            Logger.getLogger(WindowManagerImpl.class.getName()).log(Level.WARNING, null,
                              new java.lang.IllegalStateException("Assertion failed. " +
                                                                  tc.getClass().getName() +
                                                                  ".preferredID method shouldn\'t be overriden to return null. " +
                                                                  "Please change your impl to return non-null string.")); // NOI18N
        }
        
        return PersistenceManager.getDefault().getGlobalTopComponentID(tc, preferredID);
    }

    @Override
    public void invokeWhenUIReady(Runnable run) {
        getExclusive().register(run);
    }
    
    @Override
    public boolean isEditorTopComponent( TopComponent tc ) {
        if( null == tc )
            return false;
        //check opened TopComponents first to avoid AWT assertion if possible
        for(ModeImpl mode: getModes()) {
            if( mode.getKind() != Constants.MODE_KIND_EDITOR )
                continue;
            if( mode.containsTopComponent( tc ) ) {
                return true;
            }
        }

        //unknown TopComponent
        return false;
    }
    
    @Override
    public boolean isOpenedEditorTopComponent( TopComponent tc ) {
        if( null == tc )
            return false;
        for(ModeImpl mode: getModes()) {
            if( mode.getKind() != Constants.MODE_KIND_EDITOR )
                continue;
            if( mode.getOpenedTopComponents().contains( tc ) ) {
                return true;
            }
        }

        //unknown TopComponent
        return false;
    }
    
    @Override
    public boolean isEditorMode( Mode mode ) {
        if( null == mode )
            return false;
        ModeImpl modeImpl = findModeImpl( mode.getName() );
        return null != modeImpl && modeImpl.getKind() == Constants.MODE_KIND_EDITOR;
    }
    
    public void newTabGroup( TopComponent tc ) {
        assertEventDispatchThread();
        central.newTabGroup( tc );
    }
    
    public void collapseTabGroup( ModeImpl mode ) {
        assertEventDispatchThread();
        central.collapseTabGroup( mode );
    }

    public final void mainWindowPainted () {
        if (!exclusivesCompleted && WindowManagerImpl.getInstance().isVisible()) {
            exclusivesCompleted = true;
            getExclusive().stop();
            getExclusive().register(new Runnable() {
                @Override
                public void run() {
                    FloatingWindowTransparencyManager.getDefault().start();
                }
            });

            SwingUtilities.invokeLater(getExclusive());
        }
    }

    /**
     * #220599
     */
    private void busyIconWarmUp() {
        invokeWhenUIReady( new Runnable() {

            @Override
            public void run() {
                RequestProcessor.getDefault().post( new Runnable() {

                    @Override
                    public void run() {
                        BusyTabsSupport.getDefault().getBusyIcon( false );
                    }
                });
            }
        });
    }

    /** Handles exclusive invocation of Runnables.
     */
    private static final class Exclusive implements Runnable, ActionListener {
        /** lists of runnables to run */
        private ArrayList<Runnable> arr;
        /**
         * timer to ensure exclusive runnables are really invoked in all
         * circumstances
         */
        private javax.swing.Timer paintedTimer;

        public Exclusive() {
            arr = new ArrayList<Runnable>();
            paintedTimer = new javax.swing.Timer(5000, this);
            paintedTimer.setRepeats(false);
        }
        
        final void stop() {
            paintedTimer.stop();
        }
        final void restart() {
            paintedTimer.restart();
        }

        /** Registers given runnable and ensures that it is run when UI 
         * of the system is ready.
         */
        public synchronized void register(Runnable r) {
            arr.add(r);
            SwingUtilities.invokeLater(this);
        }

        @Override
        public void run() {
            if (!WindowManagerImpl.getInstance().isVisible()) {
                return;
            }
            
            synchronized (this) {
                if (arr.isEmpty()) {
                    return;
                }
                
                final Runnable toRun = arr.remove(0);
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        Logger perf = Logger.getLogger("org.netbeans.log.startup"); // NOI18N
                        try {
                            perf.log(Level.FINE, "start", "invokeWhenUIReady: " + toRun.getClass().getName()); // NOI18N
                            toRun.run();
                            perf.log(Level.FINE, "end", "invokeWhenUIReady: " + toRun.getClass().getName()); // NOI18N
                        } catch (RuntimeException ex) {
                            Logger.getLogger(WindowManagerImpl.class.getName()).log(
                                    Level.WARNING, null, ex);
                        }
                        SwingUtilities.invokeLater(Exclusive.this);
                    }
                });
            }
        }

        /** ActionListener implementation - reacts to Timer which ensures
         * invocation of registered exclusive runnables
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            Logger.getLogger(WindowManagerImpl.class.getName()).log(Level.FINE, 
                    "Painted timer action invoked, which probably means that MainWindow.paint was not called!"); //NOI18N
            WindowManagerImpl.getInstance().mainWindowPainted();
        }

    } // end of Exclusive class

    public void resetModel() {
        central.resetModel();
        RegistryImpl rimpl = (RegistryImpl)componentRegistry();
        rimpl.clear();
    }
    
    // Manipulating methods (overriding the superclass dummy ones) <<

    /** Helper only. */
    private ModeImpl getMode(TopComponent tc) {
        return (ModeImpl)findMode(tc);
    }

    // #37561
    /** Helper only */
    private ModeImpl getModeForOpenedTopComponent(TopComponent tc) {
        if(tc == null) {
            // Log something?
            return null;
        }
        
        for(ModeImpl mode: getModes()) {
            if(mode.getOpenedTopComponents().contains(tc)) {
                return mode;
            }
        }
        
        return null;
    }
    
    /**
     * @return The mode where the given TopComponent had been before it was moved to sliding or separate mode.
     */
    public ModeImpl getPreviousModeForTopComponent(String tcID, ModeImpl slidingMode) {
        return getCentral().getModeTopComponentPreviousMode(tcID, slidingMode);
    }
    
    /**
     * @return The position (tab index) of the given TopComponent before it was moved to sliding or separate mode.
     */
    public int getPreviousIndexForTopComponent(String tcID, ModeImpl slidingMode) {
        return getCentral().getModeTopComponentPreviousIndex(tcID, slidingMode);
    
    }
    
    /**
     * Remember the mode and position where the given TopComponent was before moving into sliding or separate mode.
     * 
     * @param tcID TopComponent's id
     * @param currentSlidingMode The mode where the TopComponent is at the moment.
     * @param prevMode The mode where the TopComponent had been before it was moved to the sliding mode.
     * @param prevIndex Tab index of the TopComponent before it was moved to the new mode.
     */
    public void setPreviousModeForTopComponent(String tcID, ModeImpl slidingMode, ModeImpl prevMode, int prevIndex) {
        getCentral().setModeTopComponentPreviousMode(tcID, slidingMode, prevMode, prevIndex);
    }
    
    /**
     * Set the state of the TopComponent when the editor is maximized.
     * 
     * @param tcID TopComponent id
     * @param docked True if the TopComponent should stay docked in maximized editor mode,
     * false if it should slide out when the editor is maximized.
     */
    public void setTopComponentDockedInMaximizedMode( String tcID, boolean docked ) {
        getCentral().setTopComponentDockedInMaximizedMode( tcID, docked );
    }
    
    /**
     * Get the state of the TopComponent when the editor is maximized.
     * 
     * @param tcID TopComponent id.
     * @return True if the TopComponent should stay docked in maximized editor mode,
     * false if it should slide out when the editor is maximized.
     */
    public boolean isTopComponentDockedInMaximizedMode( String tcID ) {
        return getCentral().isTopComponentDockedInMaximizedMode( tcID );
    }
    
    /**
     * Set the state of the TopComponent when no mode is maximized.
     * 
     * @param tcID TopComponent id
     * @param slided True if the TopComponent is slided in the default mode,
     * false if it is docked.
     */
    public void setTopComponentSlidedInDefaultMode( String tcID, boolean slided ) {
        getCentral().setTopComponentSlidedInDefaultMode( tcID, slided );
    }
    
    /**
     * Get the state of the TopComponent when no mode is maximized.
     * 
     * @param tcID TopComponent id.
     * @return True if the TopComponent is slided in the default mode,
     * false if it is docked.
     */
    public boolean isTopComponentSlidedInDefaultMode( String tcID ) {
        return getCentral().isTopComponentSlidedInDefaultMode( tcID );
    }
    
    /**
     * Get the state of the TopComponent when it is slided-in.
     * 
     * @param tcID TopComponent id. 
     * @return true if the TopComponent is maximized when slided-in.
     */
    public boolean isTopComponentMaximizedWhenSlidedIn( String tcID ) {
        return getCentral().isTopComponentMaximizedWhenSlidedIn( tcID );
    }
    
    /**
     * Set the state of the TopComponent when it is slided-in.
     * 
     * @param tcID TopComponent id. 
     * @param maximized true if the TopComponent is maximized when slided-in.
     */
    public void setTopComponentMaximizedWhenSlidedIn( String tcID, boolean maximized ) {
        getCentral().setTopComponentMaximizedWhenSlidedIn( tcID, maximized );
    }
    
    public void userToggledTopComponentSlideInMaximize( String tcID ) {
        getCentral().userToggledTopComponentSlideInMaximize( tcID );
    }

    /** Finds out if given Window is used as separate floating window or not.
     *
     * @return true if Window is separate floating window, false if window
     * is used for other purposes such as independent dialog/window, main window etc. 
     */
    public static boolean isSeparateWindow (Window w) {
        // work only in Swing environment
        if (!(w instanceof RootPaneContainer)) {
            return false;
        }
        // #85089 - getRootPane may return null in some edge situations
        JRootPane rp = ((RootPaneContainer) w).getRootPane();
        if (rp == null) {
            return false;
        }
        return rp.getClientProperty(Constants.SEPARATE_WINDOW_PROPERTY) != null;
    }

    private static final String ASSERTION_ERROR_MESSAGE = "Window System API is required to be called from AWT thread only, see " // NOI18N
        + "http://core.netbeans.org/proposals/threading/"; // NOI18N
    
    static void assertEventDispatchThread() {
        assert SwingUtilities.isEventDispatchThread() : ASSERTION_ERROR_MESSAGE;
    }

    static {
        assertsEnabled = false;
        assert assertsEnabled = true;  // Intentional side-effect!!!
    }

    /** Weaker form of assertion to control if client's code is calling
     * winsys from ED thread. Level of logged exception is lower if
     * assertions are disabled (for releases etc).
     */
    static void warnIfNotInEDT () {
        Level level = assertsEnabled ? Level.WARNING : Level.FINE;
        if(!SwingUtilities.isEventDispatchThread()) {
            // tries to catch known JDK problem, SwingUtilities.isEventDispatchThread()
            // returns false even if it *is* in ED thread.
            // if we find "java.awt.EventDispatchThread" stack line, it's probable
            // that we hit this JDK problem (see links below)
            boolean isJDKProblem = false;
            StackTraceElement[] elems = Thread.currentThread().getStackTrace();
            for (StackTraceElement elem : elems) {
                if ("java.awt.EventDispatchThread".equals(elem.getClassName())) {
                    isJDKProblem = true;
                    break;
                }
            }

            if (!isJDKProblem) {
                // problem somewhere in NetBeans modules' code
                Logger.getLogger(WindowManagerImpl.class.getName()).log(level, null,
                        new java.lang.IllegalStateException(
                        "Problem in some module which uses Window System: "
                        + ASSERTION_ERROR_MESSAGE));
            } else {
                // probably known problem in JDK
                Logger.getLogger(WindowManagerImpl.class.getName()).log(level, null,
                        new java.lang.IllegalStateException(
                        "Known problem in JDK occurred. If you are interested, vote and report at:\n" +
                        "http://bugs.sun.com/view_bug.do?bug_id=6424157, http://bugs.sun.com/view_bug.do?bug_id=6553239 \n" +
                        "Also see related discussion at http://www.netbeans.org/issues/show_bug.cgi?id=90590"));
            }
        }
    }

    /**
     * @return An array of TopComponents that are opened in editor modes (i.e. editor windows).
     */
    public TopComponent[] getEditorTopComponents() {
        TopComponentTracker tcTracker = TopComponentTracker.getDefault();
        ArrayList<TopComponent> editors = new ArrayList<TopComponent>();
        for( TopComponent tc : getRegistry().getOpened() ) {
            if( tcTracker.isViewTopComponent( tc ) )
                continue;
            editors.add( tc );
        }
        return editors.toArray( new TopComponent[editors.size()] );
    }

    /**
     * @return TopComponent that is selected in an arbitrary editor-type mode
     * or null if editor mode(s) is empty.
     */
    public TopComponent getArbitrarySelectedEditorTopComponent() {
        TopComponentTracker tcTracker = TopComponentTracker.getDefault();
        Set modes = getModes();
        for( Iterator i=modes.iterator(); i.hasNext(); ) {
            Mode mode = (Mode)i.next();
            ModeImpl modeImpl = findModeImpl( mode.getName() );
            if( modeImpl.getKind() == Constants.MODE_KIND_EDITOR ) {
                TopComponent tc = mode.getSelectedTopComponent();
                if( null != tc && tcTracker.isEditorTopComponent( tc ) )
                    return tc;
            }
        }
        return null;
    }

    /**
     * Send componentHidden() event to all selected TopComponents in editor modes.
     */
    public void deselectEditorTopComponents() {
        for(ModeImpl modeImpl: getModes()) {
            if( modeImpl.getKind() == Constants.MODE_KIND_EDITOR ) {
                //not a pretty hack - add an empty TopComponent into the mode
                //and make it the selected one so that componentHidden() gets called
                //on the previously selected TopComponent
                TopComponent dummy = new DummyTopComponent();
                modeImpl.addOpenedTopComponent( dummy );
                modeImpl.setSelectedTopComponent( dummy );
            }
        }
    }

    /**
     * Calls close() on all TopComponents that are opened in noneditor-type modes.
     *
     */
    public void closeNonEditorViews() {
        TopComponentTracker tcTracker = TopComponentTracker.getDefault();
        for(ModeImpl modeImpl: getModes()) {
            if( null == modeImpl )
                continue;
            java.util.List tcs = modeImpl.getOpenedTopComponents();
            for( Iterator j=tcs.iterator(); j.hasNext(); ) {
                TopComponent tc = (TopComponent)j.next();
                if( tcTracker.isViewTopComponent( tc ) )
                    tc.close();
            }
        }
    }

    @Override
    public TopComponent[] getOpenedTopComponents(Mode mode) {
        if( mode instanceof ModeImpl ) {
            java.util.List<TopComponent> openedTcs = ((ModeImpl)mode).getOpenedTopComponents();
            return openedTcs.toArray(new TopComponent[openedTcs.size()]);
        }
        return super.getOpenedTopComponents(mode);
    }

    @Override
    public void addWindowSystemListener( WindowSystemListener listener ) {
        synchronized( listeners ) {
            listeners.add( listener );
        }
    }

    @Override
    public void removeWindowSystemListener( WindowSystemListener listener ) {
        synchronized( listeners ) {
            listeners.remove( listener );
        }
    }

    private String currentRole = null;
    
    @Override
    public String getRole() {
        return currentRole;
    }

    @Override
    public void setRole( String roleName ) {
        setRole( roleName, false );
    }
    
    public void setRole( String roleName, boolean keepDocumentWindows ) {
        if( null != roleName && roleName.isEmpty() )
            throw new IllegalArgumentException( "Role name cannot be empty."); //NOI18N
        
        if( !PersistenceHandler.getDefault().isLoaded() ) {
            currentRole = roleName;
            PersistenceManager.getDefault().setRole( currentRole );
        } else {
            if( currentRole == null ? roleName == null : currentRole.equals( roleName ) ) {
                //nothing to do
                return;
            }
            switchRole( roleName, keepDocumentWindows );
        }
    }
    
    /*private*/ boolean switchRole( String newRole, boolean keepDocumentWindows ) {
        final WindowSystemImpl ws = Lookup.getDefault().lookup( WindowSystemImpl.class );
        assert null != ws;
        final PersistenceManager pm = PersistenceManager.getDefault();
        
        PersistenceHandler.getDefault().finishLazyLoading();

        //cancel full-screen mode
        MainWindow.getInstance().setFullScreenMode(false);
        
        //get a list of editor windows that should stay open even after the reset
        final TopComponent[] editors = getEditorTopComponents();
        if( !keepDocumentWindows ) {
            for( TopComponent tc : editors ) {
                if( !tc.canClose() )
                    return false;
            }
        }
        
        final TopComponent prevActiveEditor = getArbitrarySelectedEditorTopComponent();

        Set<TopComponent> openedBefore = new HashSet<TopComponent>( getRegistry().getOpened() );

        boolean hideAndShowWhileSwitching = Switches.isShowAndHideMainWindowWhileSwitchingRole();
        if( hideAndShowWhileSwitching ) {
            ws.hide();
        } else {
            getMainWindow().setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
        }
        
        //save current layout under current role
        ws.save();
        
        deselectEditorTopComponents();

        //reset
        resetModel();
        pm.reset(); //keep mappings to TopComponents created so far
        PersistenceHandler.getDefault().clear();

        this.currentRole = newRole;
        pm.setRole( newRole );

        //load from new role
        ws.load();

        if( keepDocumentWindows ) {
            ModeImpl editorMode = (ModeImpl) findMode("editor"); //NOI18N
            //re-open editor windows that were opened before the reset
            for( int i=0; i<editors.length && null != editorMode; i++ ) {
                TopComponent editor = editors[i];
                if( editor.getPersistenceType() == TopComponent.PERSISTENCE_NEVER )
                    continue;
                ModeImpl mode = ( ModeImpl ) findMode( editor );
                if( null == mode )
                    mode = editorMode;
                if( null != mode ) {
                    String tcId = findTopComponentID( editor );
                    if( !(mode.getOpenedTopComponents().contains( editor ) || (null != tcId && mode.getOpenedTopComponentsIDs().contains( tcId ))) )
                        mode.addOpenedTopComponentNoNotify(editor);
                    openedBefore.remove( editor );
                }
            }
        }
        
        Set<TopComponent> openedAfter = getRegistry().getOpened();
        openedBefore.removeAll( openedAfter );
        for( TopComponent tc : openedBefore ) {
            componentCloseNotify( tc );
        }

        if( hideAndShowWhileSwitching ) {
            ws.show();
        } else {
            getMainWindow().setCursor( null );
        }

        TopComponent editorToActivate = prevActiveEditor;
        if( null != editorToActivate && !editorToActivate.isOpened() )
            editorToActivate = getArbitrarySelectedEditorTopComponent();
        if( null != editorToActivate )
            editorToActivate.requestActive();
        
        SwingUtilities.invokeLater( new Runnable() {
            @Override
            public void run() {
                Frame mainWindow = getMainWindow();
                mainWindow.invalidate();
                mainWindow.repaint();
            }
        });
        
        return true;
    }
    
    //roles
    
    /**
     * 
     * @param draggable 
     * @since 2.37
     */
    public void userStartedKeyboardDragAndDrop( TopComponentDraggable draggable ) {
        central.userStartedKeyboardDragAndDrop( draggable );
    }

    private static final Object BUSY_FLAG = new Object();
    private static final String BUSY_PROP_NAME = "nbwinsys.tc.isbusy"; //NOI18N

    @Override
    protected void topComponentMakeBusy( TopComponent tc, boolean busy ) {
        boolean wasBusy = isTopComponentBusy( tc );
        tc.putClientProperty( BUSY_PROP_NAME, busy ? BUSY_FLAG : null );
        if( busy != wasBusy ) {
            //update winsys
            ModeImpl mode = (ModeImpl) findMode(tc);
            if( null != mode )
                central.topComponentMakeBusy(mode, tc, busy);
        }
    }

    /**
     * Check if the given TopComponent is 'busy'
     * @param tc
     * @return
     * @since 2.45
     */
    public boolean isTopComponentBusy( TopComponent tc ) {
        return tc.getClientProperty( BUSY_PROP_NAME ) == BUSY_FLAG;
    }
    
    void fireEvent( WindowSystemEventType type ) {
        assertEventDispatchThread();
        
        Collection<WindowSystemListener> copy = null;
        synchronized( listeners ) {
            copy = new ArrayList<WindowSystemListener>( listeners );
        }
        WindowSystemEvent e = new WindowSystemEvent( this );
        for( WindowSystemListener listener : copy ) {
            switch( type ) {
                case beforeLoad:
                    listener.beforeLoad( e );
                    break;
                case beforeSave:
                    listener.beforeSave( e );
                    break;
                case afterLoad:
                    listener.afterLoad( e );
                    break;
                case afterSave:
                    listener.afterSave( e );
                    break;
            }
        }
    }

    public boolean isHeavyWeightShowing() {
        TopComponent.Registry registry = TopComponent.getRegistry();
        Set<TopComponent> opened = new HashSet<TopComponent>(registry.getOpened());
        for( TopComponent tc : opened ) {
            if( !tc.isShowing() )
                continue;
            if( isHeavyWeight( tc ) )
                return true;
        }
        return false;
    }

    private boolean isHeavyWeight( java.awt.Component c ) {
        if( null != c && !c.isLightweight() )
            return true;
        if( c instanceof Container ) {
            for( java.awt.Component child : ((Container)c).getComponents() ) {
                if( isHeavyWeight(child) )
                    return true;
            }
        }
        return false;
    }

    /**
     * Checks the floating status of given TopComponent.
     * @return True if the given TopComponent is separated from the main window.
     * @since 2.51
     */
    @Override
    public boolean isTopComponentFloating( TopComponent tc ) {
        assertEventDispatchThread();
        return !isDocked( tc );
    }

    /**
     * Floats the given TopComponent or docks it back to the main window.
     * @param tc
     * @param floating True to separate the given TopComponent from the main window,
     * false to dock it back to the main window.
     * @since 2.51
     */
    @Override
    public void setTopComponentFloating(  TopComponent tc, boolean floating ) {
        assertEventDispatchThread();
        boolean isFloating = !isDocked( tc );
        if( isFloating == floating )
            return;
        ModeImpl mode = (ModeImpl)findMode( tc );
        if( null == mode )
            throw new IllegalStateException( "Cannot find Mode for TopComponent: " + tc );
        if( floating ) {
            userUndockedTopComponent( tc, mode );
        } else {
            userDockedTopComponent( tc, mode );
        }
    }

    /**
     * An empty TopComponent needed for deselectEditorTopComponents()
     */
    private static class DummyTopComponent extends TopComponent {
        @Override
        protected String preferredID() {
            return "temp";
        }

        @Override
        public int getPersistenceType() {
            return PERSISTENCE_NEVER;
        }
    }
}

