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

org.flexdock.docking.DockingManager Maven / Gradle / Ivy

/*
 * Copyright (c) 2004 Christopher M Butler
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.flexdock.docking;

import java.awt.Component;
import java.awt.Window;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.IOException;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.WeakHashMap;

import javax.swing.SwingUtilities;

import org.flexdock.docking.activation.ActiveDockableListener;
import org.flexdock.docking.adapter.AdapterFactory;
import org.flexdock.docking.adapter.DockingAdapter;
import org.flexdock.docking.defaults.DefaultDockingStrategy;
import org.flexdock.docking.defaults.DockableComponentWrapper;
import org.flexdock.docking.drag.DragManager;
import org.flexdock.docking.drag.effects.DragPreview;
import org.flexdock.docking.drag.effects.EffectsManager;
import org.flexdock.docking.drag.effects.RubberBand;
import org.flexdock.docking.event.DockingEventHandler;
import org.flexdock.docking.event.hierarchy.DockingPortTracker;
import org.flexdock.docking.event.hierarchy.RootDockingPortInfo;
import org.flexdock.docking.floating.policy.FloatPolicyManager;
import org.flexdock.docking.props.DockablePropertySet;
import org.flexdock.docking.props.PropertyManager;
import org.flexdock.docking.state.DockingState;
import org.flexdock.docking.state.FloatManager;
import org.flexdock.docking.state.LayoutManager;
import org.flexdock.docking.state.MinimizationManager;
import org.flexdock.docking.state.PersistenceException;
import org.flexdock.event.EventManager;
import org.flexdock.event.RegistrationEvent;
import org.flexdock.util.ClassMapping;
import org.flexdock.util.DockingUtility;
import org.flexdock.util.ResourceManager;
import org.flexdock.util.RootWindow;
import org.flexdock.util.SwingUtility;
import org.flexdock.util.Utilities;

/**
 * This class is used as a public facade into the framework docking system. It
 * provides a straightforward public API for managing and manipulating the
 * various different subcomponents that make up the docking framework through a
 * single class. {@code DockingManager} cannot be instantiated. Rather, its
 * methods are accessed statically from within application code and it generally
 * defers processing to a set of abstract handlers hidden from the application
 * layer.
 *
 * Among {@code DockingManager's} responsibilities are as follows:
 *
 * 
*
Maintaining a component repository.
*
All {@code Dockables} and {@code DockingPorts} are cached within an and * accessible through and internal registry.
*
Maintaining framework state.
*
{@code DockingManager} provides APIs for managing various different * global framework settings, including application-key, floating support, * auto-persistence, {@code LayoutManagers}, and {@code MinimizationManagers}. *
*
Behavioral auto-configuration.
*
{@code DockingManager} automatically adds and removes necessary event * listeners to enable/disable drag-to-dock behavior as components are * registered and unregistered.
*
Programmatic access to docking operations.
*
{@code DockingManager} provides public APIs for programmatically dock, * undock, minimize, persist, and load {@code Dockables} from storage.
*
* * @author Christopher Butler */ public class DockingManager implements DockingConstants { public static final String MINIMIZE_MANAGER = "minimize.manager"; public static final String LAYOUT_MANAGER = "layout.manager"; private static final String DEV_PROPS = "org/flexdock/util/dev-props.properties"; private static final String CONFIG_PROPS = "org/flexdock/docking/flexdock-core.properties"; private static final DockingManager SINGLETON = new DockingManager(); private static final HashMap DOCKABLES_BY_ID = new HashMap(); private static final WeakHashMap DOCKABLES_BY_COMPONENT = new WeakHashMap(); private static final ClassMapping DOCKING_STRATEGIES = new ClassMapping( DefaultDockingStrategy.class, new DefaultDockingStrategy()); // Map(DockingPort -> MaximizedState) private static final Map MAXIMIZED_STATES_BY_ROOT_PORT = new HashMap(); private static Object persistentIdLock = new Object(); private String defaultLayoutManagerClass; private String defaultMinimizeManagerClass; private DockingStrategy defaultDocker; private LayoutManager layoutManager; private MinimizationManager minimizeManager; private DockableFactory dockableFactory; private AutoPersist autoPersister; private float defaultSiblingSize; static { // call this method to preload any framework resources // we might need later init(); } private static class AutoPersist extends Thread { private boolean enabled; @Override public void run() { store(); } private synchronized void store() { try { if (isEnabled()) { storeLayoutModel(); } } catch (IOException e) { e.printStackTrace(); } catch (PersistenceException e) { e.printStackTrace(); } } public synchronized boolean isEnabled() { return enabled; } public synchronized void setEnabled(boolean enabled) { this.enabled = enabled; } } private static class MaximizedState { private final Dockable dockable; private final DockingPort originalPort; public MaximizedState(Dockable dockable, DockingPort originalDockingPort) { this.dockable = dockable; this.originalPort = originalDockingPort; } public Dockable getDockable() { return dockable; } public DockingPort getOriginalPort() { return originalPort; } } private static void init() { // load the dev system properties Properties p = ResourceManager.getProperties(DEV_PROPS, true); if (p != null) { System.getProperties().putAll(p); } // prime the drag manager for use DragManager.prime(); // ensure our Dockable adapters have been loaded AdapterFactory.prime(); // make sure dockingEvents are properly intercepted EventManager.addHandler(new DockingEventHandler()); EventManager.addListener(FloatPolicyManager.getInstance()); Properties config = ResourceManager.getProperties(CONFIG_PROPS, true); DockingManager mgr = getDockingManager(); // set the minimization manager mgr.defaultMinimizeManagerClass = config.getProperty(MINIMIZE_MANAGER); setMinimizeManager(mgr.defaultMinimizeManagerClass); // set the layout manager mgr.defaultLayoutManagerClass = config.getProperty(LAYOUT_MANAGER); setLayoutManager(mgr.defaultLayoutManagerClass); // setup tracking for the currently active dockable ActiveDockableListener.prime(); // setup the default sibling size float siblingSize = Utilities.getFloat(System .getProperty(RegionChecker.DEFAULT_SIBLING_SIZE_KEY), RegionChecker.DEFAULT_SIBLING_SIZE); setDefaultSiblingSize(siblingSize); // setup auto-persistence Runtime.getRuntime().addShutdownHook(getDockingManager().autoPersister); } private DockingManager() { defaultDocker = new DefaultDockingStrategy(); autoPersister = new AutoPersist(); } private static DockingManager getDockingManager() { return SINGLETON; } public static void addDragSource(Dockable dockable, Component dragSrc) { List sources = dockable == null ? null : dockable.getDragSources(); if (sources == null || dragSrc == null) { return; } if (!sources.contains(dragSrc)) { updateDragListeners(dockable); } } /** * Convenience method that removes the specified {@code Dockable} from the * layout. If the {@code Dockable}is embedded within the main application * frame or a floating dialog, it is removed from the container hierarchy. * If the {@code Dockable} is presently minimized, it is removed from the * current minimization manager. If the {@code Dockable} is already "closed" * or is {@code null}, no operation is performed. "Closing" a * {@code Dockable} only removes it from the visual layout. It does not * remove it from the internal {@code Dockable} registry and all underlying * {@code DockingState} information remains consistent so that the * {@code Dockable} may later be restored to its original location within * the application. * * @param dockable * the {@code Dockable} to be closed. */ public static void close(Dockable dockable) { if (dockable == null) { return; } if (isMaximized(dockable)) { toggleMaximized(dockable); } if (isDocked(dockable)) { undock(dockable); } else if (DockingUtility.isMinimized(dockable)) { getMinimizeManager().close(dockable); } } /** * Docks the specified {@code Component} into the CENTER region of the * specified {@code DockingPort}. If the {@code DockingManager} finds a * valid {@code Dockable} instance mapped to the specified {@code Component}, * the {@code Dockable} will be docked into the {@code DockingPort}. If the * {@code Component} or {@code DockingPort} is {@code null}, or a valid * {@code Dockable} cannot be found for the specified {@code Component}, * this method returns {@code false}. Otherwise, this method returns * {@code true} if the docking operation was successful and {@code false} if * the docking operation cannot be completed. This method defers processing * to {@code dock(Component dockable , DockingPort port, String region)}. * * @param dockable * the {@code Component} to be docked. * @param port * the {@code DockingPort} into which the specified * {@code Component} will be docked. * @return {@code true} if the docking operation was successful, * {@code false} otherwise. * @see #dock(Component, DockingPort, String) */ public static boolean dock(Component dockable, DockingPort port) { return dock(dockable, port, CENTER_REGION); } /** * Docks the specified {@code Component} into the supplied region of the * specified {@code DockingPort}. If the {@code DockingManager} finds a * valid {@code Dockable} instance mapped to the specified {@code Component}, * the {@code Dockable} will be docked into the {@code DockingPort}. If the * {@code Component} or {@code DockingPort} is {@code null}, or a valid * {@code Dockable} cannot be found for the specified {@code Component}, * this method returns {@code false}. Otherwise, this method returns * {@code true} if the docking operation was successful and {@code false} if * the docking operation cannot be completed. This method defers processing * to {@code dock(Dockable dockable, DockingPort port, String region)}. * * @param dockable * the {@code Component} to be docked. * @param port * the {@code DockingPort} into which the specified * {@code Component} will be docked. * @param region * the region into which to dock the specified {@code Component} * @return {@code true} if the docking operation was successful, * {@code false} if the docking operation cannot be completed. * @see #dock(Dockable, DockingPort, String) */ public static boolean dock(Component dockable, DockingPort port, String region) { Dockable d = resolveDockable(dockable); return dock(d, port, region); } /** * Docks the specified {@code Dockable} into the supplied region of the * specified {@code DockingPort}. If the {@code Dockable} or * {@code DockingPort} is {@code null}, this method returns {@code false}. * Otherwise, this method returns {@code true} if the docking operation was * successful and {@code false} if the docking operation cannot be * completed. * * This method determines the {@code DockingStrategy} to be used for the * specified {@code DockingPort} and defers processing to the * {@code DockingStrategy}. This method's return value will be based upon * the {@code DockingStrategy} implementation and is subject to conditions * such as whether the supplied region is deemed valid, whether the * {@code DockingStrategy} allows this particular {@code Dockable} to be * docked into the supplied region of the specified {@code DockingPort}, * and so on. The {@code DockingStrategy} used is obtained by a call to * {@code getDockingStrategy(Object obj)} and may be controlled via * {@code setDockingStrategy(Class c, DockingStrategy strategy)}, supplying * a {@code DockingPort} implementation class and a customized * {@code DockingStrategy}. * * @param dockable * the {@code Dockable} to be docked. * @param port * the {@code DockingPort} into which the specified * {@code Component} will be docked. * @param region * the region into which to dock the specified {@code Dockable} * @return {@code true} if the docking operation was successful, * {@code false} otherwise. * @see DockingStrategy#dock(Dockable, DockingPort, String) * @see #getDockingStrategy(Object) * @see #setDockingStrategy(Class, DockingStrategy) */ public static boolean dock(Dockable dockable, DockingPort port, String region) { if (dockable == null) { return false; } DockingStrategy strategy = getDockingStrategy(port); if (strategy != null) { return strategy.dock(dockable, port, region); } return false; // TODO think of changing it to runtime exception I // don't see a situation when there would be no docker. } private static Dockable resolveDockable(Component comp) { if (comp == null) { return null; } Dockable d = getDockable(comp); if (d == null) { d = registerDockable(comp); } return d; } /** * Docks the specified {@code Component} relative to another already-docked * {@code Component} in the CENTER region. Valid {@code Dockable} instances * are looked up for both {@code Component} parameters and processing is * deferred to {@code dock(Dockable dockable, Dockable parent)}. If a valid * {@code Dockable} cannot be resolved for either {@code Component}, then * this method returns {@code false}. The "parent" {@code Dockable} must * currently be docked. If not, this method will return {@code false}. * Otherwise, its parent {@code DockingPort} will be resolved and the new * {@code Dockable} will be docked into the {@code DockingPort} relative to * the "parent" {@code Dockable}. * * @param dockable * the {@code Component} to be docked * @param parent * the {@code Component} used as a reference point for docking * @return {@code true} if the docking operation was successful; * {@code false} otherwise. * @see DockingManager#dock(Dockable, Dockable) */ public static boolean dock(Component dockable, Component parent) { return dock(resolveDockable(dockable), resolveDockable(parent)); } /** * Docks the specified {@code Dockable} relative to another already-docked * {@code Dockable} in the CENTER region. The "parent" {@code Dockable} must * currently be docked. If not, this method will return {@code false}. * Otherwise, its parent {@code DockingPort} will be resolved and the new * {@code Dockable} will be docked into the {@code DockingPort} relative to * the "parent" {@code Dockable}. This method defers processing to * {@code dock(Dockable dockable, Dockable parent, String region)} and * returns {@code false} if any of the input parameters are {@code null}. * * @param dockable * the {@code Dockable} to be docked * @param parent * the {@code Dockable} used as a reference point for docking * @return {@code true} if the docking operation was successful; * {@code false} otherwise. * @see #dock(Dockable, Dockable, String) */ public static boolean dock(Dockable dockable, Dockable parent) { return dock(dockable, parent, CENTER_REGION); } /** * Docks the specified {@code Component} relative to another already-docked * {@code Component} in the specified region. Valid {@code Dockable} * instances will be looked up for each of the {@code Component} parameters. * If a valid {@code Dockable} is not found for either {@code Component}, * then this method returns {@code false}. The "parent" {@code Dockable} * must currently be docked. If not, this method will return {@code false}. * Otherwise, its parent {@code DockingPort} will be resolved and the new * {@code Dockable} will be docked into the {@code DockingPort} relative to * the "parent" {@code Dockable}. This method defers processing to * {@code dock(Component dockable, Component parent, String region, float proportion)} * and returns {@code false} if any of the input parameters are {@code null}. * If the specified region is other than CENTER, then a split layout should * result. This method supplies a split proportion of 0.5F, resulting in * equal distribution of space between the dockable and parent parameters if * docking is successful. * * @param dockable * the {@code Component} to be docked * @param parent * the {@code Component} used as a reference point for docking * @param region * the relative docking region into which {@code dockable} will * be docked * @return {@code true} if the docking operation was successful; * {@code false} otherwise. * @see #dock(Component, Component, String, float) */ public static boolean dock(Component dockable, Component parent, String region) { return dock(dockable, parent, region, 0.5f); } /** * Docks the specified {@code Dockable} relative to another already-docked * {@code Dockable} in the specified region. The "parent" {@code Dockable} * must currently be docked. If not, this method will return {@code false}. * Otherwise, its parent {@code DockingPort} will be resolved and the new * {@code Dockable} will be docked into the {@code DockingPort} relative to * the "parent" {@code Dockable}. This method defers processing to * {@code dock(Dockable dockable, Dockable parent, String region, float proportion)} * and returns {@code false} if any of the input parameters are {@code null}. * If the specified region is other than CENTER, then a split layout should * result. This method supplies a split proportion of 0.5F, resulting in * equal distribution of space between the dockable and parent parameters if * docking is successful. * * @param dockable * the {@code Dockable} to be docked * @param parent * the {@code Dockable} used as a reference point for docking * @param region * the docking region into which {@code dockable} will be docked * @return {@code true} if the docking operation was successful; * {@code false} otherwise. * @see #dock(Dockable, Dockable, String, float) */ public static boolean dock(Dockable dockable, Dockable parent, String region) { return dock(dockable, parent, region, 0.5f); } /** * Docks the specified {@code Component} relative to another already-docked * {@code Component} in the specified region with the specified split * proportion. Valid {@code Dockable} instances will be looked up for each * of the {@code Component} parameters. If a valid {@code Dockable} is not * found for either {@code Component}, then this method returns * {@code false}. The "parent" {@code Dockable} must currently be docked. * If not, this method will return {@code false}. Otherwise, its parent * {@code DockingPort} will be resolved and the new {@code Dockable} will be * docked into the {@code DockingPort} relative to the "parent" * {@code Dockable}. If the specified region is CENTER, then the * {@code proportion} parameter is ignored. Otherwise, a split layout should * result with the proportional space specified in the {@code proportion} * parameter allotted to the {@code dockable} argument. This method defers * processing to * {@code dock(Dockable dockable, Dockable parent, String region, float proportion)}. * * @param dockable * the {@code Component} to be docked * @param parent * the {@code Component} used as a reference point for docking * @param region * the relative docking region into which {@code dockable} will * be docked * @param proportion * the proportional space to allot the {@code dockable} argument * if the docking operation results in a split layout. * @return {@code true} if the docking operation was successful; * {@code false} otherwise. */ public static boolean dock(Component dockable, Component parent, String region, float proportion) { Dockable newDockable = resolveDockable(dockable); Dockable parentDockable = resolveDockable(parent); return dock(newDockable, parentDockable, region, proportion); } /** * Docks the specified {@code Dockable} relative to another already-docked * {@code Dockable} in the specified region with the specified split * proportion. The "parent" {@code Dockable} must currently be docked. If * not, this method will return {@code false}. Otherwise, its parent * {@code DockingPort} will be resolved and the new {@code Dockable} will be * docked into the {@code DockingPort} relative to the "parent" * {@code Dockable}. If the specified region is CENTER, then the * {@code proportion} parameter is ignored. Otherwise, a split layout should * result with the proportional space specified in the {@code proportion} * parameter allotted to the {@code dockable} argument. * * @param dockable * the {@code Dockable} to be docked * @param parent * the {@code Dockable} used as a reference point for docking * @param region * the docking region into which {@code dockable} will be docked * @param proportion * the proportional space to allot the {@code dockable} argument * if the docking operation results in a split layout. * @return {@code true} if the docking operation was successful; * {@code false} otherwise. */ public static boolean dock(Dockable dockable, Dockable parent, String region, float proportion) { return DockingUtility .dockRelative(dockable, parent, region, proportion); } private static DockingStrategy findDockingStrategy(Dockable dockable) { DockingPort port = dockable.getDockingPort(); DockingStrategy strategy = port == null ? null : port .getDockingStrategy(); if (strategy == null) { DockingManager mgr = getDockingManager(); strategy = mgr == null ? null : mgr.defaultDocker; } return strategy; } /** * Indicates whether the specified {@code Component} is currently docked. * This method looks up a parent {@code DockingPort} for the specified * {@code Component} via a call to * {@code getDockingPort(Component dockable)}. This method returns * {@code true} if a parent {@code DockingPort} is found and {@code false} * if no parent {@code DockingPort} is present. This method returns * {@code false} if the {@code Component} parameter is {@code null}. * * @param component * the {@code Component} whose docking status is to be examined * @return {@code true} if the {@code Component} is currently docked; * otherwise {@code false}. */ public static boolean isDocked(Component component) { return getDockingPort(component) != null; } /** * Indicates whether the specified {@code Dockable} is currently docked. * This method looks up a parent {@code DockingPort} for the specified * {@code Dockable} via a call to {@code getDockingPort(Dockable dockable)}. * This method returns {@code true} if a parent {@code DockingPort} is found * and {@code false} if no parent {@code DockingPort} is present. This * method returns {@code false} if the {@code Dockable} parameter is * {@code null}. * * @param dockable * the {@code Dockable} whose docking status is to be examined * @return {@code true} if the {@code Dockable} is currently docked; * otherwise {@code false}. */ public static boolean isDocked(Dockable dockable) { return getDockingPort(dockable) != null; } /** * Checks whether a supplied {@code Dockable} is docked within a supplied * {@code DockingPort} instance. Returns {@code true} if the * {@code DockingPort} contains the specified {@code Dockable}; * {@code false} otherwise. This method returns {@code false} if either of * the input parameters are {@code null}. * * @param dockingPort * the {@code DockingPort} to be tested * @param dockable * the {@code Dockable} instance to be examined * @return {@code true} if the supplied {@code DockingPort} contains the * specified {@code Dockable}; {@code false} otherwise. */ public static boolean isDocked(DockingPort dockingPort, Dockable dockable) { return dockingPort == null || dockable == null ? false : dockingPort .isParentDockingPort(dockable.getComponent()); } /** * Indicates whether global floating support is currently enabled. Defers * processing to {@code FloatPolicyManager.isGlobalFloatingEnabled()}. * * @return {@code true} if global floating support is enabled, {@code false} * otherwise. * @see FloatPolicyManager#isGlobalFloatingEnabled() */ public static boolean isFloatingEnabled() { return FloatPolicyManager.isGlobalFloatingEnabled(); } /** * Indicates whether tabbed layouts are supported by default for * {@code DockingPorts} with a single {@code Dockable} in the {@link DockingConstants#CENTER} region. * This is a global default setting and applies to any {@code DockingPort} * that does not have a specific contradictory local setting. *

* This method defers processing to * {@link org.flexdock.docking.props.PropertyManager#getDockingPortRoot()}. * As such, there are multiple "scopes" at which this property may be * overridden. * * @return {@code true} if the default setting for {@code DockingPorts} * allows a tabbed layout for a single {@code Dockable} in the * CENTER region; {@code false} otherwise. * @see PropertyManager#getDockingPortRoot() * @see org.flexdock.docking.props.DockingPortPropertySet#isSingleTabsAllowed() */ public static boolean isSingleTabsAllowed() { return PropertyManager.getDockingPortRoot().isSingleTabsAllowed() .booleanValue(); } /** * Indicates whether the supplied parameter is considered a valid docking * region. Valid values are those defined in {@code DockingConstants} and * include {@code NORTH_REGION}, {@code SOUTH_REGION}, {@code EAST_REGION}, * {@code WEST_REGION}, and {@code CENTER_REGION}. This method returns * {@code true} if the supplied parameter is equal to one of these values. * * @param region * the region value to be tested * @return {@code true} if the supplied parameter is a valid docking region; * {@code false} otherwise. */ public static boolean isValidDockingRegion(String region) { return CENTER_REGION.equals(region) || NORTH_REGION.equals(region) || SOUTH_REGION.equals(region) || EAST_REGION.equals(region) || WEST_REGION.equals(region); } private static void updateDragListeners(Component dragSrc, DragManager listener) { MouseMotionListener motionListener = null; EventListener[] listeners = dragSrc.getMouseMotionListeners(); for (int i = 0; i < listeners.length; i++) { if (listeners[i] instanceof DragManager) { motionListener = (MouseMotionListener) listeners[i]; break; } } if (motionListener != listener) { if (motionListener != null) { dragSrc.removeMouseMotionListener(motionListener); } dragSrc.addMouseMotionListener(listener); } MouseListener mouseListener = null; listeners = dragSrc.getMouseListeners(); for (int i = 0; i < listeners.length; i++) { if (listeners[i] instanceof DragManager) { mouseListener = (MouseListener) listeners[i]; break; } } if (mouseListener != listener) { if (mouseListener != null) { dragSrc.removeMouseListener(mouseListener); } dragSrc.addMouseListener(listener); } } /** * Creates, registers, and returns a {@code Dockable} for the specified * {@code Component}. If the specified {@code Component} implements the * {@code Dockable} interface, then this method dispatches to * {@code registerDockable(Dockable dockable)}. Otherwise, this method * dispatches to {@code registerDockable(Component comp, String tabText)}. *

* This method attempts to resolve an appropriate value for {@code tabText} * by calling {@code getName()} on the specified {@code Component}. If the * resolved value is {@code null} or empty, then the value {@code "null"} is * used. *

* If {@code comp} is {@code null}, no exception is thrown and no action is * performed. * * @param comp * the target component for the {@code Dockable}. * @return the {@code Dockable} that has been registered for the supplied * {@code Component} * @see #registerDockable(Dockable) * @see #registerDockable(Component, String) */ public static Dockable registerDockable(Component comp) { if (comp == null) { return null; } if (comp instanceof Dockable) { return registerDockable((Dockable) comp); } return registerDockable(comp, null, null); } private static String determineTabText(Component comp, String persistId) { String tabText = null; // if 'comp' is a DockingStub, then we may be able to // pull the tab text from it if (comp instanceof DockingStub) { tabText = ((DockingStub) comp).getTabText(); } else { // if we can find an adapter mapping, then try to pull // the tab text from there DockingAdapter adapter = AdapterFactory.getAdapter(comp); if (adapter != null) { tabText = adapter.getTabText(); } } // if 'comp' wasn't a DockingStub, or the stub returned a null tabText, // then try the component name if (tabText == null) { tabText = comp.getName(); } // if tabText is still null, then use the persistentId if (tabText == null) { tabText = persistId; } // get rid of null and empty cases. use the string "null" if nothing // else can be found tabText = tabText == null ? "null" : tabText.trim(); if (tabText.length() == 0) { tabText = "null"; } return tabText; } /** * Creates a {@code Dockable} for the specified {@code Component} and * dispatches to {@code registerDockable(Dockable init)}. If {@code comp} * is {@code null}, no exception is thrown and no action is performed. * * @param comp * the target component for the Dockable, both drag-starter and * docking source * @param tabText * the description of the docking source. Used as the tab-title * of docked in a tabbed pane * @return the {@code Dockable} that has been registered for the supplied * {@code Component} * @see #registerDockable(Dockable) */ public static Dockable registerDockable(Component comp, String tabText) { return registerDockable(comp, tabText, null); } private static Dockable registerDockable(Component comp, String tabText, String dockingId) { if (comp == null) { return null; } if (tabText == null) { tabText = determineTabText(comp, dockingId); } Dockable dockable = getDockableForComponent(comp, tabText, dockingId); return registerDockable(dockable); } /** * Registers and initializes the specified {@code Dockable}. All * {@code Dockables} managed by the framework must, at some point, be * registered via this method. This method adds the {@code Dockable} to the * internal registry, allowing querying by ID and {@code Component}. Drag * listeners are added to the {@code Dockable} to enable drag-n-drop docking * support. Docking properties are also initialized for the {@code Dockable}. * This method fires a {@code RegistrationEvent} once the {@code Dockable} * has been registered. If the {@code Dockable} is {@code null}, no * {@code Exception} is thrown and no action is taken. The {@code Dockable} * returned by this method will be the same object passed in as an argument. * * @param dockable * the Dockable that is being registered. * @return the {@code Dockable} that has been registered. * @see org.flexdock.event.RegistrationEvent */ public static Dockable registerDockable(Dockable dockable) { if (dockable == null || dockable.getComponent() == null || dockable.getDragSources() == null) { return null; } if (dockable.getPersistentId() == null) { throw new IllegalArgumentException( "Dockable must have a non-null persistent ID."); } DOCKABLES_BY_COMPONENT.put(dockable.getComponent(), dockable); // flag the component as dockable, in case it doesn't // implement the interface directly Component c = dockable.getComponent(); SwingUtility.putClientProperty(c, Dockable.DOCKABLE_INDICATOR, Boolean.TRUE); // add drag listeners updateDragListeners(dockable); // add the dockable as its own listener dockable.addDockingListener(dockable); // cache the dockable by ID DOCKABLES_BY_ID.put(dockable.getPersistentId(), dockable); // make sure we have docking-properties initialized (must come after // ID-caching) DockablePropertySet props = PropertyManager .getDockablePropertySet(dockable); // dispatch a registration event EventManager.dispatch(new RegistrationEvent(dockable, DockingManager.SINGLETON, true)); // return the dockable return dockable; } public static void unregisterDockable(Component comp) { Dockable dockable = getDockable(comp); unregisterDockable(dockable); } public static void unregisterDockable(String dockingId) { Dockable dockable = getDockableImpl(dockingId); unregisterDockable(dockable); } public static void unregisterDockable(Dockable dockable) { if (dockable == null) { return; } synchronized (DOCKABLES_BY_COMPONENT) { DOCKABLES_BY_COMPONENT.remove(dockable.getComponent()); } // flag the component as dockable, in case it doesn't // implement the interface directly Component c = dockable.getComponent(); SwingUtility.removeClientProperty(c, Dockable.DOCKABLE_INDICATOR); // remove the drag listeners removeDragListeners(dockable); // remove the dockable as its own listener dockable.removeDockingListener(dockable); // unlink the propertySet PropertyManager.removePropertySet(dockable); // remove the dockable by ID synchronized (DOCKABLES_BY_ID) { DOCKABLES_BY_ID.remove(dockable.getPersistentId()); } // dispatch a registration event EventManager.dispatch(new RegistrationEvent(dockable, DockingManager.SINGLETON, false)); } /** * Removes the event listeners that manage drag-n-drop docking operations * from the specified {@code Component}. If the specific listeners are not * present, then no action is taken. Drag listeners used by the docking * system are of type {@code org.flexdock.docking.drag.DragManager}. * * @param comp * the {@code Component} from which to remove drag listeners. * @see DragManager */ public static void removeDragListeners(Component comp) { if (comp == null) { return; } MouseMotionListener motionListener = null; EventListener[] listeners = comp.getMouseMotionListeners(); for (int i = 0; i < listeners.length; i++) { if (listeners[i] instanceof DragManager) { motionListener = (MouseMotionListener) listeners[i]; break; } } if (motionListener != null) { comp.removeMouseMotionListener(motionListener); } MouseListener mouseListener = null; listeners = comp.getMouseListeners(); for (int i = 0; i < listeners.length; i++) { if (listeners[i] instanceof DragManager) { mouseListener = (MouseListener) listeners[i]; break; } } if (mouseListener != null) { comp.removeMouseListener(mouseListener); } } /** * Displays the specified {@code Dockable} in the application's docking * layout. If the {@code Dockable} has not previously been docked, a * suitable location is determined within the layout and the * {@code Dockable} is docked to that location. If the {@code Dockable} has * previously been docked within the layout and subsequently removed, as * with a call to {@code DockingManager.close()}, the {@code Dockable} will * be restored to its prior state within the layout. This method defers * processing to the {@code display(Dockable dockable)} method for the * currently installed {@code org.flexdock.docking.state.LayoutManager}. * The {@code LayoutManager} implementation is responsible for handling the * semantics of determining an initial docking location or restoring a * {@code Dockable} to its previous layout state. If the {@code Dockable} * parameter is {@code null}, no {@code Exception} is thrown and no action * is taken. * * @param dockable * the {@code Dockable} to be displayed. * @return {@code true} if the {@code Dockable} was successfully displayed; * {@code false} otherwise. * @see #getLayoutManager() * @see LayoutManager#display(Dockable) */ public static boolean display(Dockable dockable) { return getLayoutManager().display(dockable); } /** * Displays the {@code Dockable} with the specified ID within the * application's docking layout. A valid {@code Dockable} is looked up for * the supplied ID. If none is found, this method returns {@code false}. * Otherwise, processing is dispatched to {@code display(Dockable dockable)}. * If the {@code Dockable} has not previously been docked, a suitable * location is determined within the layout and the {@code Dockable} is * docked to that location. If the {@code Dockable} has previously been * docked within the layout and subsequently removed, as with a call to * {@code DockingManager.close()}, the {@code Dockable} will be restored to * its prior state within the layout. This method defers processing to the * {@code display(Dockable dockable)} method for the currently installed * {@code org.flexdock.docking.state.LayoutManager}. The * {@code LayoutManager} implementation is responsible for handling the * semantics of determining an initial docking location or restoring a * {@code Dockable} to its previous layout state. If the {@code Dockable} * parameter is {@code null}, no {@code Exception} is thrown and no action * is taken. * * @param dockable * the ID of the {@code Dockable} to be displayed. * @return {@code true} if the {@code Dockable} was successfully displayed; * {@code false} otherwise. * @see #display(Dockable) * @see #getLayoutManager() * @see LayoutManager#display(Dockable) */ public static boolean display(String dockable) { return getLayoutManager().display(getDockable(dockable)); } private static String generatePersistentId(Object obj) { return generatePersistentId(obj, null); } private static String generatePersistentId(Object obj, String desiredId) { if (obj == null) { return null; } synchronized (persistentIdLock) { String pId = desiredId == null ? obj.getClass().getName() : desiredId; StringBuffer baseId = new StringBuffer(pId); for (int i = 1; hasRegisteredDockableId(pId); i++) { baseId.append("_").append(i); pId = baseId.toString(); } return pId; } } private static boolean hasRegisteredDockableId(String id) { return DOCKABLES_BY_ID.containsKey(id); } /** * Returns the {@code DockingStrategy} associated with the {@code Class} of * the {@code Object} parameter. This method returns {@code null} if the * parameter is {@code null}. Otherwise, the method retrieves the * {@code Object's} {@code Class} and dispatches to * {@code getDockingStrategy(Class classKey)}. *

* {@code DockingStrategy} association follows a strict inheritance chain * using {@code org.flexdock.util.ClassMapping}. If a mapping for * {@code obj.getClass()} is not found, then the superclass is tested, and * so on until {@code java.lang.Object} is reached. Thus, if a * {@code DockingStrategy} mapping of {@code Foo} exists for class * {@code Bar}, and class {@code Baz} extends {@code Bar}, then calling * this method for an instance of {@code Baz} will return an instance of * {@code Foo}. The inheritance chain is strict in the sense that * only superclasses are checked. Implemented interfaces are ignored. *

* If a class association is never found, then an instance of * {@code DefaultDockingStrategy} is returned. * * @param obj * the object whose {@code DockingStrategy} association we wish * to test * @return the {@code DockingStrategy} associated with the {@code Class} * type of the {@code Object} parameter. * @see #getDockingStrategy(Class) * @see #setDockingStrategy(Class, DockingStrategy) * @see ClassMapping#getClassInstance(Class) */ public static DockingStrategy getDockingStrategy(Object obj) { Class key = obj == null ? null : obj.getClass(); return getDockingStrategy(key); } /** * Returns the {@code DockingStrategy} associated with specified * {@code Class}. This method returns {@code null} if the parameter is * {@code null}. *

* {@code DockingStrategy} association follows a strict inheritance chain * using {@code org.flexdock.util.ClassMapping}. If a mapping for * {@code classKey} is not found, then the superclass is tested, and so on * until {@code java.lang.Object} is reached. Thus, if a * {@code DockingStrategy} mapping of {@code Foo} exists for class * {@code Bar}, and class {@code Baz} extends {@code Bar}, then calling * this method for class {@code Baz} will return an instance of {@code Foo}. * The inheritance chain is strict in the sense that only * superclasses are checked. Implemented interfaces are ignored. *

* If a class association is never found, then an instance of * {@code DefaultDockingStrategy} is returned. * * @param classKey * the {@code Class} whose {@code DockingStrategy} association we * wish to test * @return the {@code DockingStrategy} associated with the specified * {@code Class}. * @see #setDockingStrategy(Class, DockingStrategy) * @see ClassMapping#getClassInstance(Class) */ public static DockingStrategy getDockingStrategy(Class classKey) { DockingStrategy strategy = (DockingStrategy) DOCKING_STRATEGIES .getClassInstance(classKey); return strategy; } /** * Returns an array of {@code RootWindows} known to the docking framework * that contain {@code DockingPorts}. Any {@code Frame}, {@code Applet}, * {@code Dialog}, or {@code Window} that has a {@code DockingPort} added * as a descendent {@code Component} will automatically have an * {@code org.flexdock.util.RootWindow} wrapper instance associated with it. * This method will return an array of all known RootWindows that contain * {@code DockingPorts}. Ordering of the array may be based off of a * {@code java.util.Set} and is not guaranteed. * * @return an array of all known {@code RootWindows} that contain * {@code DockingPorts} * @see RootWindow * @see DockingPortTracker#getDockingWindows() */ public static RootWindow[] getDockingWindows() { Set windowSet = DockingPortTracker.getDockingWindows(); return windowSet == null ? new RootWindow[0] : (RootWindow[]) windowSet .toArray(new RootWindow[0]); } /** * Returns the {@code DockingPort} with the specified ID. If the * {@code portId} parameter is {@code null}, or a {@code DockingPort} with * the specified ID is not found, a {@code null} reference is returned. This * method internally dispatches to * {@code org.flexdock.docking.event.hierarchy.DockingPortTracker.findById(String portId)}. * {@code portId} should match the value returned by a {@code DockingPort's} * {@code getPersistentId()} method. * * @param portId * the ID of the {@code DockingPort} to be looked up * @return the {@code DockingPort} with the specified ID * @see DockingPort#getPersistentId() * @see DockingPortTracker#findById(String) */ public static DockingPort getDockingPort(String portId) { return DockingPortTracker.findById(portId); } /** * Returns the "main" {@code DockingPort} within the application window * containing the specified {@code Component}. Just as desktop applications * will tend to have a "main" application window, perhaps surrounded with * satellite windows or dialogs, the "main" {@code DockingPort} within a * given window will be considered by the application developer to contain * the primary docking layout used by the enclosing window. *

* The {@code Component} parameter may or may not be a root window * container. If not, the ancestor window of {@code comp} is determined and * a set of docking ports encapsulated by a {@code RootDockingPortInfo} * instance is returned by a call to * {@code getRootDockingPortInfo(Component comp)}. The resolved * {@code RootDockingPortInfo} instance's main {@code DockingPort} is * returned via its method {@code getMainPort()}. *

* By default, the "main" {@code DockingPort} assigned to any * {@code RootDockingPortInfo} instance associated with a window will happen * to be the first root {@code DockingPort} detected for that window. In * essence, the default settings make this method identical to * {@code getRootDockingPort(Component comp)}. This, however, may be * altered by {@code RootDockingPortInfo's} * {@code setMainPort(String portId)} method based upon the needs of the * application developer. In contrast, * {@code getMainDockingPort(Component comp)} will always return the first * root {@code DockingPort} found within a window. *

* If {@code comp} is {@code null} or the root window cannot be resolved, * then this method returns a {@code null} reference. A {@code null} * reference is also returned if the root window does not contain any * {@code DockingPorts}. * * @param comp * the {@code Component} whose root window will be checked for a * main {@code DockingPort} * @return the main {@code DockingPort} within the root window that contains * {@code comp} * @see #getRootDockingPortInfo(Component) * @see #getRootDockingPort(Component) * @see DockingPortTracker#getRootDockingPortInfo(Component) * @see RootDockingPortInfo#getMainPort() * @see RootDockingPortInfo#setMainPort(String) */ public static DockingPort getMainDockingPort(Component comp) { RootDockingPortInfo info = getRootDockingPortInfo(comp); return info == null ? null : info.getMainPort(); } /** * Returns the first root {@code DockingPort} found within the application * window containing the specified {@code Component}. A "root" * {@code DockingPort} is a {@code DockingPort} embedded within a * window/frame/applet/dialog that is not nested within any other parent * {@code DockingPorts}. The {@code Component} parameter may or may not be * a root window container itself. If not, the root window containing * {@code comp} is resolved and the first root {@code DockingPort} found * within it is returned. This method defers actual processing to * {@code org.flexdock.docking.event.hierarchy.DockingPortTracker.findByWindow(Component comp)}. *

* If {@code comp} is {@code null} or the root window cannot be resolved, * then this method returns a {@code null} reference. A {@code null} * reference is also returned if the root window does not contain any * {@code DockingPorts}. *

* This method differs from {@code getMainDockingPort(Component comp)} in * that the "main" {@code DockingPort} for a given window is configurable by * the application developer, whereas this method will always return the * "first" {@code DockingPort} found within the window. However, if the * "main" {@code DockingPort} has not been manually configured by the * application developer, then this method and * {@code getMainDockingPort(Component comp)} will exhibit identical * behavior. * * @param comp * the {@code Component} whose root window will be checked for a * root {@code DockingPort} * @return the first root {@code DockingPort} found within the root window * that contains {@code comp} * @see #getMainDockingPort(Component) * @see DockingPortTracker#findByWindow(Component) * @see RootDockingPortInfo */ public static DockingPort getRootDockingPort(Component comp) { return DockingPortTracker.findByWindow(comp); } /** * Returns the {@code RootDockingPortInfo} instance associated with the root * window containing the specified {@code Component}. The {@code Component} * parameter may or may not be a root window container itself. If not, the * root window containing {@code comp} is resolved and the * {@code RootDockingPortInfo} instance associated with the window is * returned. {@code RootDockingPortInfo} will contain information regarding * all of the "root" {@code DockingPorts} embedded within a root window * where a "root" {@code DockingPort} is any {@code DockingPort} embedded * within the window that does not have any other {@code DockingPort} * ancestors in it's container hierarchy. *

* If {@code comp} is {@code null} or the root window cannot be resolved, * then this method returns a {@code null} reference. A {@code null} * reference is also returned if the root window does not contain any * {@code DockingPorts}. *

* This method dispatches internally to * {@code org.flexdock.docking.event.hierarchy.DockingPortTracker.getRootDockingPortInfo(Component comp)}. * * @param comp * the {@code Component} whose root window will be checked for an * associated {@code RootDockingPortInfo}. * @return the {@code RootDockingPortInfo} instance associated with the root * window containing {@code comp}. * @see RootDockingPortInfo * @see DockingPortTracker#getRootDockingPortInfo(Component) */ public static RootDockingPortInfo getRootDockingPortInfo(Component comp) { return DockingPortTracker.getRootDockingPortInfo(comp); } /** * Sends the application's current layout model to external storage. This * method defers processing to the currently installed * {@code org.flexdock.docking.state.LayoutManager} by invoking its * {@code store()} method. If there is no {@code LayoutManager} installed, * then this method returns {@code false}. *

* The layout model itself, along with storage mechanism, is abstract and * dependent upon the particular {@code LayoutManager} implementation. As * such, it may be possible that the {@code LayoutManager} is unable to * persist the current layout state for non-Exceptional reasons. This method * returns {@code true} if the layout model was successfully stored and * {@code false} if the layout model could not be stored under circumstances * that do not generate an {@code Exception} (for instance, if there is no * persistence implementation currently installed). If a problem occurs * during the persistence process, an {@code IOException} is thrown. * * @return {@code true} if the current layout model was succesfully stored, * {@code false} otherwise. * @throws IOException if an I/O exception occurs during storage * @throws PersistenceException wraps all specific exceptions which can occur during storage * @see #getLayoutManager() * @see #setLayoutManager(LayoutManager) * @see LayoutManager#store() */ public static boolean storeLayoutModel() throws IOException, PersistenceException { LayoutManager mgr = getLayoutManager(); return mgr == null ? false : mgr.store(); } /** * Loads a previously stored layout model into the currently installed * {@code LayoutManager}. This method defers processing to * {@code loadLayoutModel(boolean restore)} with an argument of * {@code false} to indicate that the stored data model should merely be * loaded into memory and the {@code LayoutManager} should not attempt to * subsequently restore the application view by synchronizing it against the * newly loaded data model. *

* The layout model itself, along with storage mechanism, is abstract and * dependent upon the particular {@code LayoutManager} implementation. As * such, it may be possible that the {@code LayoutManager} is unable to load * the previous layout state for non-Exceptional reasons. This method * returns {@code true} if the layout model was successfully loaded and * {@code false} if the layout model could not be loaded under circumstances * that do not generate an {@code Exception} (for instance, if there was no * previous layout model found in storage). If a problem occurs during the * loading process, an {@code IOException} is thrown. * * @return {@code true} if the current layout model was succesfully loaded, * {@code false} otherwise. * @throws IOException if an I/O exception occurs during loading * @throws PersistenceException wraps all specific exceptions which can occur during loading * @see #loadLayoutModel(boolean) * @see LayoutManager#load() */ public static boolean loadLayoutModel() throws IOException, PersistenceException { return loadLayoutModel(false); } /** * Loads a previously stored layout model into the currently installed * {@code LayoutManager} and attempts to synchronize the application view * with the newly loaded layout model if the {@code restore} parameter is * {@code true}. If there is no currently installed {@code LayoutManager}, * then this method returns {@code false}. If the {@code restore} parameter * is {@code true}, then this method defers processing to * {@code restoreLayout(boolean loadFromStorage)} with an argument of * {@code true}. Otherwise, this method defers processing to the currently * installed {@code org.flexdock.docking.state.LayoutManager} by invoking * its {@code load()} method. *

* The layout model itself, along with storage mechanism, is abstract and * dependent upon the particular {@code LayoutManager} implementation. As * such, it may be possible that the {@code LayoutManager} is unable to load * the previous layout state for non-Exceptional reasons. This method * returns {@code true} if the layout model was successfully loaded and * {@code false} if the layout model could not be loaded under circumstances * that do not generate an {@code Exception} (for instance, if there was no * previous layout model found in storage). If a problem occurs during the * loading process, an {@code IOException} is thrown. * * @param restore whether or not to restore the loaded layout * @return {@code true} if the current layout model was succesfully loaded, * {@code false} otherwise. * @throws IOException if an I/O exception occurs during loading * @throws PersistenceException wraps all specific exceptions which can occur during loading * @see #getLayoutManager() * @see #setLayoutManager(LayoutManager) * @see #restoreLayout(boolean) * @see LayoutManager#load() */ public static boolean loadLayoutModel(boolean restore) throws IOException, PersistenceException { LayoutManager mgr = getLayoutManager(); if (mgr == null) { return false; } return restore ? restoreLayout(true) : mgr.load(); } /** * Synchronizes the application view with the current in-memory layout * model. This method defers processing to * {@code restoreLayout(boolean loadFromStorage)} with an argument of * {@code false}. This instructs the currently installed * {@code LayoutManager} to restore the application view to match the * current in-memory layout model without reloading from storage prior to * restoration. This method is useful for developers who choose to construct * a layout model programmatically and wish to "commit" it to the * application view, restoring their own in-memory layout model rather than * a model persisted in external storage. *

* If there is no {@code LayoutManager} currently installed, then this * method returns {@code false}. * * @return {@code true} if the in-memory layout model was properly restored * to the application view, {@code false} otherwise. * @see #restoreLayout(boolean) * @see #getLayoutManager() * @see #setLayoutManager(LayoutManager) * @see LayoutManager#restore(boolean) */ public static boolean restoreLayout() { try { return restoreLayout(false); } catch (IOException e) { // shouldn't happen since we're not intending to load from storage System.err.println("Exception: " +e.getMessage()); e.printStackTrace(); return false; } catch (PersistenceException e) { // TODO Auto-generated catch block System.err.println("Exception: " +e.getMessage()); e.printStackTrace(); return false; } } /** * Synchronizes the application view with the current in-memory layout * model. This method defers processing to the currently installed * {@code org.flexdock.docking.state.LayoutManager} by invoking its * {@code restore(boolean loadFromStorage)} method. If there is no * {@code LayoutManager} currently installed, then this method returns * {@code false}. *

* If the {@code loadFromStorage} parameter is {@code true}, then the * {@code LayoutManager} is instructed to load any persisted layout model * from external storage into memory before synchronizing the application * view. If a problem occurs while loading from exernal storage, this method * throws an {@code IOException}. * * @param loadFromStorage * instructs whether to load any layout model from external * storage into memory before synchronizing the application view. * @return {@code true} if the in-memory layout model was properly restored * to the application view, {@code false} otherwise. * @throws IOException if an I/O exception occurs during restoring * @throws PersistenceException wraps all specific exceptions which can occur during restoring * @see #getLayoutManager() * @see #setLayoutManager(LayoutManager) * @see LayoutManager#restore(boolean) */ public static boolean restoreLayout(boolean loadFromStorage) throws IOException, PersistenceException { LayoutManager mgr = getLayoutManager(); return mgr == null ? false : mgr.restore(loadFromStorage); } private static Dockable loadAndRegister(String id) { DockableFactory factory = id == null ? null : getDockingManager().dockableFactory; if (factory == null) { return null; } // the getDockableComponent() implementation may or may not // automatically register a dockable before returning. // first, try to get a Dockable from the factory Dockable dockable = factory.getDockable(id); if (dockable != null) { // check to see if the dockable is already registered. Dockable tmp = getDockableImpl(dockable.getPersistentId()); if (tmp == null) { registerDockable(dockable); } } else { // if we couldn't find a dockable from the factory, then try getting // a component. Component comp = factory.getDockableComponent(id); // we already weren't able to get a Dockable from the factory. If // we couldn't get a Component either, then give up. if (comp == null) { return null; } // if the newly created dockable has not yet been registered, // then register it. dockable = getDockable(comp); if (dockable == null) { dockable = registerDockable(comp, null, id); } } return dockable; } private static Dockable getDragInitiator(Component c) { return getDockableForComponent(c, null, null); } private static Dockable getDockableForComponent(Component c, String desc, String dockingId) { if (c == null) { return null; } // return the dockable if it has already been registered Dockable dockable = getDockable(c); if (dockable != null) { return dockable; } // if we need to create a dockable, first try to do it with an adapter DockingAdapter adapter = AdapterFactory.getAdapter(c); if (adapter != null) { dockable = DockableComponentWrapper.create(adapter); } // if we weren't able to create from an adapter, then create the // dockable manually if (dockable == null) { if (c instanceof DockingStub) { dockable = DockableComponentWrapper.create((DockingStub) c); } else { String persistentId = dockingId == null ? generatePersistentId(c) : dockingId; dockable = DockableComponentWrapper.create(c, persistentId, desc); } } // make sure the specified description is applied if (desc != null) { dockable.getDockingProperties().setDockableDesc(desc); } // cache the dockable for future use DOCKABLES_BY_COMPONENT.put(c, dockable); // now we can return return dockable; } /** * Returns the {@code DockingPort} that contains the specified * {@code Component}. If the {@code Component} is {@code null}, then a * {@code null} reference is returned. *

* This method will only return the immediate parent {@code DockingPort} of * the specified {@code Component} This means that the {@code DockingPort} * returned by this method will not only be an ancestor {@code Container} of * the specified {@code Component}, but invoking its * {@code isParentDockingPort(Component comp)} with the specified * {@code Component} will also return {@code true}. If both of these * conditions cannot be satisfied, then this method returns a {@code null} * reference. * * @param dockable * the {@code Component} whose parent {@code DockingPort} is to * be returned. * @return the imediate parent {@code DockingPort} that contains the * specified {@code Component}. */ public static DockingPort getDockingPort(Component dockable) { return DockingUtility.getParentDockingPort(dockable); } /** * Returns the {@code DockingPort} that contains the specified * {@code Dockable}. If the {@code Dockable} is {@code null}, then a * {@code null} reference is returned. *

* This method will only return the immediate parent {@code DockingPort} of * the specified {@code Dockable} This means that a check is performed for * the {@code Component} returned by the {@code Dockable's} * {@code getComponent()} method. The {@code DockingPort} returned by this * method will not only be an ancestor {@code Container} of this * {@code Component}, but invoking the {@code DockingPort's} * {@code isParentDockingPort(Component comp)} with the this * {@code Component} will also return {@code true}. If both of these * conditions cannot be satisfied, then this method returns a {@code null} * reference. * * @param dockable * the {@code Dockable} whose parent {@code DockingPort} is to be * returned. * @return the imediate parent {@code DockingPort} that contains the * specified {@code Dockable}. */ public static DockingPort getDockingPort(Dockable dockable) { return DockingUtility.getParentDockingPort(dockable); } /** * Returns the {@code Dockable} instance that models the specified * {@code Component}. The {@code Dockable} returned by this method will * return a reference to {@code comp} when its {@code getComponent()} method * is called. If {@code comp} is {@code null}, then this method will return * a {@code null} reference. *

* The association between {@code Dockable} and {@code Component} is * established internally during {@code registerDockable(Dockable dockable)}. * Thus, {@code registerDockable(Dockable dockable)} must have been called * previously for a mapping to be found and a {@code Dockable} to be * returned by this method. If no mapping is found for the specified * {@code Component}, then this method returns a {@code null} reference. * * @param comp * the {@code Component} whose {@code Dockable} instance is to be * returned. * @return the {@code Dockable} that models the specified {@code Component} * @see #registerDockable(Dockable) * @see Dockable#getComponent() */ public static Dockable getDockable(Component comp) { return comp == null ? null : (Dockable) DOCKABLES_BY_COMPONENT .get(comp); } /** * Returns the {@code Dockable} instance with the specified ID. The * {@code Dockable} returned by this method will return a String equal * {@code id} when its {@code getPersistentId()} method is called. If * {@code id} is {@code null}, then this method will return a {@code null} * reference. *

* The association between {@code Dockable} and {@code id} is established * internally during {@code registerDockable(Dockable dockable)}. Thus, * {@code registerDockable(Dockable dockable)} must have been called * previously for a mapping to be found and a {@code Dockable} to be * returned by this method. If no mapping is found for the specified * {@code id}, then this method returns a {@code null} reference. * * @param id * the persistent ID of the {@code Dockable} instance is to be * returned. * @return the {@code Dockable} that has the specified perstent ID. * @see #registerDockable(Dockable) * @see Dockable#getPersistentId() */ public static Dockable getDockable(String id) { if (id == null) { return null; } Dockable dockable = getDockableImpl(id); if (dockable == null) { dockable = loadAndRegister(id); } return dockable; } private static Dockable getDockableImpl(String id) { synchronized (DOCKABLES_BY_ID) { return id == null ? null : (Dockable) DOCKABLES_BY_ID.get(id); } } /** * Returns a {@code Set} of {@code String} IDs for all {@code Dockables} * registered with the framework. The IDs returned by this method will * correspond to the values returned for the {@code getPersistentId()} * method for each {@code Dockable} registered with the framework. * * {@code Dockable} IDs are cached during * {@code registerDockable(Dockable dockable)}. Thus, for an ID to appear * within the {@code Set} returned by this method, the corresponding * {@code Dockable} must have first been registered via * {@code registerDockable(Dockable dockable)}. * * If no {@code Dockables} have been registered with the framework, then an * empty {@code Set} is returned. This method will never return a * {@code null} reference. * * @return a {@code Set} of {@code String} IDs for all {@code Dockables} * registered with the framework. * @see #registerDockable(Dockable) * @see Dockable#getPersistentId() */ public static Set getDockableIds() { synchronized (DOCKABLES_BY_ID) { return new HashSet(DOCKABLES_BY_ID.keySet()); } } /** * Returns the listener object responsible for managing drag-to-dock mouse * events for the specified {@code Dockable}. During registration, the * listener is added to each of the {@code Components} within the * {@code Dockable's} {@code getDragSources()} {@code List}. Thus, for this * method to return a valid {@code DragManager} instance, the * {@code Dockable} must first have been registered via * {@code registerDockable(Dockable dockable)}. If the specified * {@code Dockable} is {@code null} or its {@code getDragSources()} method * returns a {@code null}, or if the {@code Dockable} has not previously * been registered, this method will return a {@code null} reference. * * @param dockable * the {@code Dockable} whose drag listener is to be returned. * @return the {@code DragManager} responsible for listening to an managing * drag-related mouse events for the specified {@code Dockable}. * @see DragManager * @see Dockable#getDragSources() * @see #registerDockable(Dockable) */ public static DragManager getDragListener(Dockable dockable) { if (dockable == null || dockable.getDragSources() == null) { return null; } for (Iterator it = dockable.getDragSources().iterator(); it.hasNext();) { Object obj = it.next(); if (obj instanceof Component) { DragManager listener = getDragListener((Component) obj); if (listener != null) { return listener; } } } return null; } private static DragManager getDragListener(Component c) { EventListener[] listeners = c.getMouseMotionListeners(); for (int i = 0; i < listeners.length; i++) { if (listeners[i] instanceof DragManager) { return (DragManager) listeners[i]; } } return null; } /** * Returns the currently installed {@code LayoutManager}. The * {@code LayoutManager} is responsible for managing docking layout state. * This includes tracking the state for all {@code Dockables} as they are * embedded, minimized, floated, or hidden. If a {@code Dockable} is * embedded, the {@code LayoutManager} is responsible for tracking its * position and size relative to other embedded {@code Dockables}. If * floating, the {@code LayoutManager} is responsible for supplying a * {@code FloatManager} to maintain {@code Dockable} groupings within * dialogs as well as dialog size and positioning. *

* The {@code LayoutManager} is responsible for providing a persistence * mechanism to save and restore layout states. Depending on the * {@code LayoutManager} implementation, it may or may not support multiple * layout models that may be loaded and switched between at runtime. *

* Because the {@code LayoutManager} is a critical piece of the docking * infrastructure, it is not possible to install a {@code null} * {@code LayoutManager}. Therefore, this method will always return a valid * {@code LayoutManager} and never a {@code null} reference. * * @return the currently installed {@code LayoutManager} * @see LayoutManager * @see #setLayoutManager(LayoutManager) * @see #setLayoutManager(String) */ public static LayoutManager getLayoutManager() { return getDockingManager().layoutManager; } /** * Returns the currently installed {@code MinimizationManager}. The * {@code MinimizationManager} is responsible for minimizing and * unminimizing {@code Dockables}, removing from and restoring to the * embedded docking layout through the currently installed * {@code LayoutManager}. *

* The visual representation of a "minimized" {@code Dockable} is somewhat * abstract, although it is commonly expressed in user interfaces with the * disappearance of the {@code Dockable} from the layout and the addition of * a tab or label on one or more edges of the application window. The * {@code MinimizationManager} implementation itself is responsible for * interpreting the visual characteristics and behavior of a minimized * {@code Dockable}, but it must provide a "preview" feature to allow * viewing of minimized {@code Dockables}, on demand without actually * restoring them to the embedded docking layout. {@code Dockables} may or * may not have limited docking functionality while in minimized and/or * preview state, depending upon the {@code MinimizationManager} * implementation. *

* Because the {@code MinimizationManager} is a critical piece of the * docking infrastructure, it cannot be set to {@code null}. Therefore, * this method will always return a valid {@code MinimizationManager} and * never a {@code null} reference. * * @return the currently installed {@code MinimizationManager}. * @see MinimizationManager * @see #setMinimizeManager(MinimizationManager) * @see #setMinimizeManager(String) */ public static MinimizationManager getMinimizeManager() { MinimizationManager mgr = getDockingManager().minimizeManager; return mgr == null ? MinimizationManager.DEFAULT_STUB : mgr; } /** * Returns the currently installed {@code FloatManager}. The * {@code FloatManager} is actually provided by the currently installed * {@code LayoutManager}. As such, this method is merely for convenience. * It internally obtains the installed {@code LayoutManager} via * {@code getLayoutManager()} and invokes its {@code getFloatManager()} * method. *

* The {@code FloatManager} maintains information relevant to floating * {@code Dockables} including grouping them together within dialogs and * tracking dialog size and position. The {@code FloatManager} is * responsible for generating new dialogs, parenting on the proper * application window(s), and sending {@code Dockables} to the proper * dialogs. It may be used by the {@code LayoutManager} to restore hidden * {@code Dockables} to proper floating state as needed. *

* Since the {@code FloatManager} is provided by the currently installed * {@code LayoutManager}, it cannot be set from within the * {@code DockingManager}. To change the installed {@code FloatManager}, * one must work directly with the installed {@code LayoutManager} * implementation per its particular custom API. *

* Since the {@code FloatManager} is a critical piece of the docking * insfrastructure, this method will never return a {@code null} reference. * * @return the {@code FloatManager} provided by the currently installed * {@code LayoutManager} * @see #getLayoutManager() * @see #setLayoutManager(LayoutManager) * @see LayoutManager#getFloatManager() */ public static FloatManager getFloatManager() { return getLayoutManager().getFloatManager(); } /** * Returns the {@code DockingState} for the {@code Dockable} with the * specified ID. The {@code DockingState} is used by the currently installed * {@code LayoutManager} to track information regarding a {@code Dockable's} * current state in the docking layout. This includes relative size and * positioning to other {@code Dockables}, minimization status, floating * status, and any other information used to track and potentially restore a * the {@code Dockable} to the layout if it is currently hidden. *

* The {@code Dockable} whose current {@code DockingState} is resolved will * map to the specified {@code dockableId} via its {@code getPersistentId()} * method. The semantics of this mapping relationship are the same as * {@code DockingManager.getDockable(String id)}. If a valid * {@code Dockable} cannot be found for the specified ID, then this method * returns a {@code null} reference. *

* The {@code DockingState} for any given {@code Dockable} is ultimately * managed by the currently installed {@code LayoutManager}. Therefore, * this method resolves the {@code LayoutManager} via * {@code getLayoutManager()} and defers processing to its * {@code getDockingState(String dockableId)} method. *

* The underlying {@code LayoutManager} does not provide any guarantees that * the same {@code DockingState} reference always will be returned for a * given {@code Dockable}; only that the returned {@code DockingState} will * accurately reflect the current state maintained by the * {@code LayoutManager} for that {@code Dockable}. For instance, if the * {@code LayoutManager} is capable of maintaining multiple layouts for an * application (as Eclipse does between perspectives), then the * {@code LayoutManager} may or may not maintain multiple * {@code DockingState} instances for a single {@code Dockable}, one within * each layout context. Therefore, it is not a good idea to cache references * to the {@code DockingState} instance returned by this method for future * use as the reference itself may possibly become stale over time depending * on the {@code LayoutManager} implementation. * * @param dockableId * the persistent ID of the {@code Dockable} whose current * {@code DockingState} is to be returned * @return the current {@code DockingState} maintained by the * {@code LayoutManager} for the specified {@code Dockable} * @see DockingState * @see #getLayoutManager() * @see LayoutManager#getDockingState(String) * @see #getDockable(String) * @see Dockable#getPersistentId() */ public static DockingState getDockingState(String dockableId) { return getLayoutManager().getDockingState(dockableId); } /** * Returns the {@code DockingState} for the specified {@code Dockable}. The * {@code DockingState} is used by the currently installed * {@code LayoutManager} to track information regarding a {@code Dockable's} * current state in the docking layout. This includes relative size and * positioning to other {@code Dockables}, minimization status, floating * status, and any other information used to track and potentially restore a * the {@code Dockable} to the layout if it is currently hidden. *

* If the {@code dockable} parameter is {@code null}, then this method * returns a {@code null} reference. *

* The {@code DockingState} for any given {@code Dockable} is ultimately * managed by the currently installed {@code LayoutManager}. Therefore, * this method resolves the {@code LayoutManager} via * {@code getLayoutManager()} and defers processing to its * {@code getDockingState(String dockableId)} method. *

* The underlying {@code LayoutManager} does not provide any guarantees that * the same {@code DockingState} reference always will be returned for a * given {@code Dockable}; only that the returned {@code DockingState} will * accurately reflect the current state maintained by the * {@code LayoutManager} for that {@code Dockable}. For instance, if the * {@code LayoutManager} is capable of maintaining multiple layouts for an * application (as Eclipse does between perspectives), then the * {@code LayoutManager} may or may not maintain multiple * {@code DockingState} instances for a single {@code Dockable}, one within * each layout context. Therefore, it is not a good idea to cache references * to the {@code DockingState} instance returned by this method for future * use as the reference itself may possibly become stale over time depending * on the {@code LayoutManager} implementation. * * @param dockable * the {@code Dockable} whose current {@code DockingState} is to * be returned * @return the current {@code DockingState} maintained by the * {@code LayoutManager} for the specified {@code Dockable} * @see #getLayoutManager() * @see LayoutManager#getDockingState(String) */ public static DockingState getDockingState(Dockable dockable) { return getLayoutManager().getDockingState(dockable); } /** * Returns the currently installed {@code DockableFactory}. The * {@code DockableFactory} installed by default is {@code null}. Therefore, * this method will return a {@code null} reference until the application * developer explicitly provides a {@code DockableFactory} implementation * via {@code setDockableFactory(DockableFactory factory)}. *

* Installing a {@code DockableFactory} allows FlexDock to seamlessly create * and register {@code Dockables} within {@code getDockable(String id)}. * Generally, {@code getDockable(String id)} will lookup the requested * {@code Dockable} within the internal registry. If not found, and there is * no {@code DockableFactory} installed, {@code getDockable(String id)} * returns a {@code null} reference. When a {@code DockableFactory} is * installed, however, failure to lookup a valid {@code Dockable} will cause * {@code getDockable(String id)} to invoke the installed * {@code DockableFactory's} {@code getDockable(String dockableId)} method, * transparently registering and returning the newly created * {@code Dockable} from {@code getDockable(String id)}. * * @return the currently installed {@code DockableFactory} * @see #getDockable(String) * @see DockableFactory#getDockable(String) */ public static DockableFactory getDockableFactory() { return getDockingManager().dockableFactory; } /** * Enables and disables auto-persistence of the current docking layout model * when the application exits. Auto-persistence is disabled by default. *

* The {@code storeLayoutModel()} provides a means of manually sending the * docking layout model to some type of external storage. When the * {@code DockingManager} class loads, a shutdown hook is added to the * {@code Runtime}. If auto-persist is enabled when the JVM exits, the * shutdown hook automatically calls {@code storeLayoutModel()}, catching * and reporting any {@code IOExceptions} that may occur. * * @param enabled * {@code true} if automatic persistence is desired; * {@code false} otherwise. * @see #storeLayoutModel() * @see Runtime#addShutdownHook(java.lang.Thread) */ public static void setAutoPersist(boolean enabled) { getDockingManager().autoPersister.setEnabled(enabled); } /** * Sets the divider location of the split layout containing the specified * dockable {@code Component}. The {@code Dockable} instance associated * with the specified {@code Component} is resolved via * {@code getDockable(Component comp)} and processing is dispatched to * {@code setSplitProportion(Dockable dockable, float proportion)}. *

* The resulting divider location will be a percentage of the split layout * size based upon the {@code proportion} parameter. Valid values for * {@code proportion} range from {@code 0.0F{@code to {@code 1.0F}. For * example, a {@code proportion} of {@code 0.3F} will move the divider to * 30% of the "size" (width for horizontal split, height * for vertical split) of the split container that contains the specified * {@code Component}. If a {@code proportion} of less than {@code 0.0F} is * supplied, the value }0.0F} is used. If a {@code proportion} greater than * {@code 1.0F} is supplied, the value }1.0F} is used. *

* It is important to note that the split divider location is only a * percentage of the container size from left to right or top to bottom. A * {@code proportion} of {@code 0.3F} does not imply that {@code dockable} * itself will be allotted 30% of the available space. The split divider * will be moved to the 30% position of the split container regardless of * the region in which the specified {@code Component} resides (which may * possibly result in {@code dockable} being allotted 70% of the available * space). *

* This method should be effective regardless of whether the split layout in * question has been fully realized and is currently visible on the screen. * This should alleviate common problems associated with setting percentages * of unrealized {@code Component} dimensions, which are initially * {@code 0x0} before the {@code Component} has been rendered to the screen. *

* If the specified {@code Component} is {@code null}, then no * {@code Exception} is thrown and no action is taken. Identical behavior * occurs if a valid {@code Dockable} cannot be resolved for the specified * {@code Component}, or the {@code Dockable} does not reside within a * split layout. *

* If the {@code Dockable} resides within a tabbed layout, a check is done * to see if the tabbed layout resides within a parent split layout. If so, * the resolved split layout is resized. Otherwise no action is taken. * * @param dockable * the {@code Component} whose containing split layout is to be * resized. * @param proportion * the percentage of containing split layout size to which the * split divider should be set. * @see #setSplitProportion(Dockable, float) * @see #getDockable(Component) */ public static void setSplitProportion(Component dockable, float proportion) { setSplitProportion(getDockable(dockable), proportion); } /** * Sets the divider location of the split layout containing the specified * dockable {@code Component}. *

* The resulting divider location will be a percentage of the split layout * size based upon the {@code proportion} parameter. Valid values for * {@code proportion} range from {@code 0.0F{@code to {@code 1.0F}. For * example, a {@code proportion} of {@code 0.3F} will move the divider to * 30% of the "size" (width for horizontal split, height * for vertical split) of the split container that contains the specified * {@code Dockable}. If a {@code proportion} of less than {@code 0.0F} is * supplied, the value }0.0F} is used. If a {@code proportion} greater than * {@code 1.0F} is supplied, the value }1.0F} is used. *

* It is important to note that the split divider location is only a * percentage of the container size from left to right or top to bottom. A * {@code proportion} of {@code 0.3F} does not imply that {@code dockable} * itself will be allotted 30% of the available space. The split divider * will be moved to the 30% position of the split container regardless of * the region in which the specified {@code Dockable} resides (which may * possibly result in {@code dockable} being allotted 70% of the available * space). *

* This method should be effective regardless of whether the split layout in * question has been fully realized and is currently visible on the screen. * This should alleviate common problems associated with setting percentages * of unrealized {@code Component} dimensions, which are initially * {@code 0x0} before the {@code Component} has been rendered to the screen. *

* If the specified {@code Dockable} is {@code null}, then no * {@code Exception} is thrown and no action is taken. Identical behavior * occurs if the {@code Dockable} does not reside within a split layout. *

* If the {@code Dockable} resides within a tabbed layout, a check is done * to see if the tabbed layout resides within a parent split layout. If so, * the resolved split layout is resized. Otherwise no action is taken. * * @param dockable * the {@code Dockable} whose containing split layout is to be * resized. * @param proportion * the percentage of containing split layout size to which the * split divider should be set. * @see #getDockable(Component) */ public static void setSplitProportion(Dockable dockable, float proportion) { DockingUtility.setSplitProportion(dockable, proportion); } /** * Sets the divider location of the split layout embedded within the * specified {@code DockingPort}. This method differs from both * {@code setSplitProportion(Component dockable, float proportion)} and * {@code setSplitProportion(Dockable dockable, float proportion)} in that * this method resolves the split layout embedded within the * specified {@code DockingPort}, whereas the other methods modify the * split layout containing their respective {@code Dockable} * parameters. *

* The resulting divider location will be a percentage of the split layout * size based upon the {@code proportion} parameter. Valid values for * {@code proportion} range from {@code 0.0F{@code to {@code 1.0F}. For * example, a {@code proportion} of {@code 0.3F} will move the divider to * 30% of the "size" (width for horizontal split, height * for vertical split) of the split container embedded within the specified * {@code DockingPort}. If a {@code proportion} of less than {@code 0.0F} * is supplied, the value }0.0F} is used. If a {@code proportion} greater * than {@code 1.0F} is supplied, the value }1.0F} is used. *

* This method should be effective regardless of whether the split layout in * question has been fully realized and is currently visible on the screen. * This should alleviate common problems associated with setting percentages * of unrealized {@code Component} dimensions, which are initially * {@code 0x0} before the {@code Component} has been rendered to the screen. *

* If the specified {@code DockingPort} is {@code null}, then no * {@code Exception} is thrown and no action is taken. Identical behavior * occurs if the {@code DockingPort} does not contain split layout. * * @param port * the {@code DockingPort} containing the split layout is to be * resized. * @param proportion * the percentage of split layout size to which the split divider * should be set. */ public static void setSplitProportion(DockingPort port, float proportion) { DockingUtility.setSplitProportion(port, proportion); } /** * Sets the currently installed {@code DockableFactory}. {@code null} * values for the {@code factory} parameter are acceptable. *

* Installing a {@code DockableFactory} allows FlexDock to seamlessly create * and register {@code Dockables} within {@code getDockable(String id)}. * Generally, {@code getDockable(String id)} will lookup the requested * {@code Dockable} within the internal registry. If not found, and there is * no {@code DockableFactory} installed, {@code getDockable(String id)} * returns a {@code null} reference. When a {@code DockableFactory} is * installed, however, failure to lookup a valid {@code Dockable} will cause * {@code getDockable(String id)} to invoke the installed * {@code DockableFactory's} {@code getDockable(String dockableId)} method, * transparently registering and returning the newly created * {@code Dockable} from {@code getDockable(String id)}. * * @param factory * the {@code DockableFactory} to install * @see #getDockableFactory() * @see #getDockable(String) * @see DockableFactory#getDockable(String) */ public static void setDockableFactory(DockableFactory factory) { getDockingManager().dockableFactory = factory; } /** * Sets the minimized state for the specified {@code Dockable}. This method * defers processing to * {@code setMinimized(Dockable dockable, boolean minimized, Component window)}, * passing the current {@code Window} ancestor of the specified * {@code Dockable} as the {@code window} parameter. Minimization * processessing is ultimately deferred to the currently installed * {@code MinimizationManager} with a constraint of * {@code MinimizationManager.UNSPECIFIED_LAYOUT_CONSTRAINT}. *

* The current {@code MinimizationManager} is responsible for updating the * underlying {@code DockingState} model for the specified {@code Dockable} * as well as rendering its own interpretation of the corresponding visual * state on the screen. If the supplied {@code minimized} parameter matches * the current {@code DockingState}, the {@code MinimizationManager} is * responsible for providing the appropriate visual indications, or lack * thereof. If the specified {@code Dockable} is {@code null}, no * {@code Exception} is thrown and no action is taken. * * @param dockable * the {@code Dockable} whose minimzed state is to be modified * @param minimized * {@code true} if the specified {@code Dockable} should be * minimized, {@code false} otherwise. * @see #setMinimized(Dockable, boolean, Component) * @see #getMinimizeManager() * @see MinimizationManager#setMinimized(Dockable, boolean, Component, int) * @see DockingState#getMinimizedConstraint() */ public static void setMinimized(Dockable dockable, boolean minimized) { Component cmp = dockable == null ? null : dockable.getComponent(); Window window = cmp == null ? null : SwingUtilities .getWindowAncestor(cmp); setMinimized(dockable, minimized, window); } /** * Sets the minimized state for the specified {@code Dockable}. This method * defers processing to * {@code setMinimized(Dockable dockable, boolean minimizing, Component window, int constraint)}, * passing {@code MinimizationManager.UNSPECIFIED_LAYOUT_CONSTRAINT} for the * {@code constraint} parameter. Minimization processessing is ultimately * deferred to the currently installed {@code MinimizationManager}. *

* The {@code window} parameter is passed to the {@code MinimizationManager} * to indicate that minimization should be handled with respect to the * specified root window, or the root window containing the specified * {@code Component}. {@code null} values are acceptable for this * parameter. *

* The current {@code MinimizationManager} is responsible for updating the * underlying {@code DockingState} model for the specified {@code Dockable} * as well as rendering its own interpretation of the corresponding visual * state on the screen. If the supplied {@code minimized} parameter matches * the current {@code DockingState}, the {@code MinimizationManager} is * responsible for providing the appropriate visual indications, or lack * thereof. If the specified {@code Dockable} is {@code null}, no * {@code Exception} is thrown and no action is taken. * * @param dockable * the {@code Dockable} whose minimzed state is to be modified * @param minimized * {@code true} if the specified {@code Dockable} should be * minimized, {@code false} otherwise. * @param window * the {@code Component} whose root window will be used by the * underlying {@code MinimizationManager} for rendering the * {@code Dockable} in its new minimized state. * @see #setMinimized(Dockable, boolean, Component, int) * @see #getMinimizeManager() * @see MinimizationManager#setMinimized(Dockable, boolean, Component, int) * @see DockingState#getMinimizedConstraint() */ public static void setMinimized(Dockable dockable, boolean minimized, Component window) { setMinimized(dockable, minimized, window, MinimizationManager.UNSPECIFIED_LAYOUT_CONSTRAINT); } /** * Sets the minimized state for the specified {@code Dockable}. This method * defers processing to * {@code setMinimized(Dockable dockable, boolean minimizing, Component window, int constraint)}, * passing {@code null} for the {@code window} parameter. Minimization * processessing is ultimately deferred to the currently installed * {@code MinimizationManager}. *

* Valid values for the {@code constraint} parameter may be found on the * {@code MinimizationManager} interface and include * UNSPECIFIED_LAYOUT_CONSTRAINT, TOP, LEFT, BOTTOM, RIGHT, and CENTER. * However, constraint values must ultimately be interpreted by the current * {@code MinimizationManager} implementation and, thus any integer value * may theoretically be valid for {@code constraint}. *

* The current {@code MinimizationManager} is responsible for updating the * underlying {@code DockingState} model for the specified {@code Dockable} * as well as rendering its own interpretation of the corresponding visual * state on the screen. If the supplied {@code minimized} parameter matches * the current {@code DockingState}, the {@code MinimizationManager} is * responsible for providing the appropriate visual indications, or lack * thereof. If the specified {@code Dockable} is {@code null}, no * {@code Exception} is thrown and no action is taken. * * @param dockable * the {@code Dockable} whose minimzed state is to be modified * @param minimizing * {@code true} if the specified {@code Dockable} should be * minimized, {@code false} otherwise. * @param constraint * a value to indicate to the {@code MinimizationManager} desired * rendering of the minimized {@code Dockable} * @see #setMinimized(Dockable, boolean, Component, int) * @see #getMinimizeManager() * @see MinimizationManager#setMinimized(Dockable, boolean, Component, int) * @see DockingState#getMinimizedConstraint() */ public static void setMinimized(Dockable dockable, boolean minimizing, int constraint) { setMinimized(dockable, minimizing, null, constraint); } /** * Sets the minimized state for the specified {@code Dockable}. This method * defers processing to the currently installed {@code MinimizationManager}. *

* The {@code window} parameter is passed to the {@code MinimizationManager} * to indicate that minimization should be handled with respect to the * specified root window, or the root window containing the specified * {@code Component}. If a {@code null} values is supplied for this * parameter, the currently active window is used. If no currently active * window can be determined, then this method exits with no action taken. *

* The current {@code MinimizationManager} is responsible for updating the * underlying {@code DockingState} model for the specified {@code Dockable} * as well as rendering its own interpretation of the corresponding visual * state on the screen. If the supplied {@code minimized} parameter matches * the current {@code DockingState}, the {@code MinimizationManager} is * responsible for providing the appropriate visual indications, or lack * thereof. If the specified {@code Dockable} is {@code null}, no * {@code Exception} is thrown and no action is taken. *

* Valid values for the {@code constraint} parameter may be found on the * {@code MinimizationManager} interface and include * UNSPECIFIED_LAYOUT_CONSTRAINT, TOP, LEFT, BOTTOM, RIGHT, and CENTER. * However, constraint values must ultimately be interpreted by the current * {@code MinimizationManager} implementation and, thus any integer value * may theoretically be valid for {@code constraint}. * * @param dockable * the {@code Dockable} whose minimzed state is to be modified * @param minimizing * {@code true} if the specified {@code Dockable} should be * minimized, {@code false} otherwise. * @param window * the {@code Component} whose root window will be used by the * underlying {@code MinimizationManager} for rendering the * {@code Dockable} in its new minimized state. * @param constraint * a value to indicate to the {@code MinimizationManager} desired * rendering of the minimized {@code Dockable} * @see #getMinimizeManager() * @see MinimizationManager#setMinimized(Dockable, boolean, Component, int) * @see DockingState#getMinimizedConstraint() */ public static void setMinimized(Dockable dockable, boolean minimizing, Component window, int constraint) { if (dockable == null) { return; } if (window == null) { window = SwingUtility.getActiveWindow(); } if (window == null) { return; } getMinimizeManager().setMinimized(dockable, minimizing, window, constraint); } /** * Sets the "main" {@code DockingPort} within the application window * containing the specified {@code Component}. Just as desktop applications * will tend to have a "main" application window, perhaps surrounded with * satellite windows or dialogs, the "main" {@code DockingPort} within a * given window will be considered by the application developer to contain * the primary docking layout used by the enclosing window. *

* The {@code Component} parameter may or may not be a root window * container. If not, the ancestor window of {@code comp} is determined and * a set of docking ports encapsulated by a {@code RootDockingPortInfo} * instance is returned by a call to * {@code getRootDockingPortInfo(Component comp)}. The resolved * {@code RootDockingPortInfo} instance's main {@code DockingPort} is set * via its method {@code setMainPort(String portId)}. *

* By default, the "main" {@code DockingPort} assigned to any * {@code RootDockingPortInfo} instance associated with a window will happen * to be the first root {@code DockingPort} detected for that window. This * method is used to alter that setting. *

* If {@code comp} is {@code null} or the root window cannot be resolved, * then this method returns with no action taken. * * @param window * the {@code Component} whose root window will be checked for a * main {@code DockingPort} * @param portId * the persistent ID of the {@code DockingPort} to use as the * main {@code DockingPort} for the specified window. * @see #getRootDockingPortInfo(Component) * @see #getRootDockingPort(Component) * @see DockingPortTracker#getRootDockingPortInfo(Component) * @see RootDockingPortInfo#getMainPort() * @see RootDockingPortInfo#setMainPort(String) */ public static void setMainDockingPort(Component window, String portId) { RootDockingPortInfo info = getRootDockingPortInfo(window); if (info != null) { info.setMainPort(portId); } } /** * Sets the currently installed {@code MinimizationManager}. The * {@code MinimizationManager} is responsible for minimizing and * unminimizing {@code Dockables}, removing from and restoring to the * embedded docking layout through the currently installed * {@code LayoutManager}. *

* The visual representation of a "minimized" {@code Dockable} is somewhat * abstract, although it is commonly expressed in user interfaces with the * disappearance of the {@code Dockable} from the layout and the addition of * a tab or label on one or more edges of the application window. The * {@code MinimizationManager} implementation itself is responsible for * interpreting the visual characteristics and behavior of a minimized * {@code Dockable}, but it must provide a "preview" feature to allow * viewing of minimized {@code Dockables}, on demand without actually * restoring them to the embedded docking layout. {@code Dockables} may or * may not have limited docking functionality while in minimized and/or * preview state, depending upon the {@code MinimizationManager} * implementation. *

* Because the {@code MinimizationManager} is a critical piece of the * docking infrastructure, it cannot be set to {@code null}. If a * {@code null} value is passed into this method, the default * {@code MinimizationManager} provided by the framework is used instead. * * @param mgr * the {@code MinimizationManager} to be installed * @see MinimizationManager * @see #getMinimizeManager() * @see #setMinimizeManager(String) */ public static void setMinimizeManager(MinimizationManager mgr) { DockingManager dockingManager = getDockingManager(); if (mgr == null) { // do not allow null minimization managers setMinimizeManager(dockingManager.defaultMinimizeManagerClass); } else { dockingManager.minimizeManager = mgr; } } /** * Sets the currently installed {@code MinimizationManager} using the * specfied class name. An attempt is make to instantiate a * {@code MinimizationManager} based upon the supplied class name * {@code String}. If the class cannot be instaniated, a stacktrace is * reported to the System.err and the default {@code MinimizationManager} * supplied by the framework is used. If the {@code String} parameter is * {@code null}, no error occurs and the default * {@code MinimizationManager} is used. If the instantiated class is not a * valid instance of {@code MinimizationManager}, then a * {@code ClassCastException} is thrown. *

* The {@code MinimizationManager} is responsible for minimizing and * unminimizing {@code Dockables}, removing from and restoring to the * embedded docking layout through the currently installed * {@code LayoutManager}. *

* The visual representation of a "minimized" {@code Dockable} is somewhat * abstract, although it is commonly expressed in user interfaces with the * disappearance of the {@code Dockable} from the layout and the addition of * a tab or label on one or more edges of the application window. The * {@code MinimizationManager} implementation itself is responsible for * interpreting the visual characteristics and behavior of a minimized * {@code Dockable}, but it must provide a "preview" feature to allow * viewing of minimized {@code Dockables}, on demand without actually * restoring them to the embedded docking layout. {@code Dockables} may or * may not have limited docking functionality while in minimized and/or * preview state, depending upon the {@code MinimizationManager} * implementation. *

* Because the {@code MinimizationManager} is a critical piece of the * docking infrastructure, it cannot be set to {@code null}. If a * {@code null} value is passed into this method, the default * {@code MinimizationManager} provided by the framework is used instead. * * @param mgrClass * the class name of the {@code MinimizationManager} to be * installed * @see MinimizationManager * @see #getMinimizeManager() * @see #setMinimizeManager(String) */ public static void setMinimizeManager(String mgrClass) { Object instance = Utilities.getInstance(mgrClass); setMinimizeManager((MinimizationManager) instance); } /** * Sets whether global floating support should be enabled. Defers processing * to * {@code FloatPolicyManager.setGlobalFloatingEnabled(boolean globalFloatingEnabled)}. * * @param enabled * {@code true} if global floating support should be enabled, * {@code false} otherwise. * @see FloatPolicyManager#setGlobalFloatingEnabled(boolean) * @see FloatPolicyManager#isGlobalFloatingEnabled() */ public static void setFloatingEnabled(boolean enabled) { FloatPolicyManager.setGlobalFloatingEnabled(enabled); } public static void setDefaultPersistenceKey(String key) { getLayoutManager().setDefaultPersistenceKey(key); } public static String getDefaultPersistenceKey() { return getLayoutManager().getDefaultPersistenceKey(); } /** * Sets whether tabbed layouts are supported by default for * {@code DockingPorts} with a single {@code Dockable} in the CENTER region. * This is a global default setting and applies to any {@code DockingPort} * that does not have a specific contradictory local setting. *

* This method defers processing to * {@code org.flexdock.docking.props.PropertyManager.getDockingPortRoot()}. * As such, there are multiple "scopes" at which this property may be * overridden. * * @param allowed * {@code true} if the default setting for {@code DockingPorts} * should allow a tabbed layout for a single {@code Dockable} in * the CENTER region; {@code false} otherwise. * @see PropertyManager#getDockingPortRoot() * @see org.flexdock.docking.props.DockingPortPropertySet#setSingleTabsAllowed(boolean) */ public static void setSingleTabsAllowed(boolean allowed) { PropertyManager.getDockingPortRoot().setSingleTabsAllowed(allowed); } /** * Sets the currently installed {@code LayoutManager}. The * {@code LayoutManager} is responsible for managing docking layout state. * This includes tracking the state for all {@code Dockables} as they are * embedded, minimized, floated, or hidden. If a {@code Dockable} is * embedded, the {@code LayoutManager} is responsible for tracking its * position and size relative to other embedded {@code Dockables}. If * floating, the {@code LayoutManager} is responsible for supplying a * {@code FloatManager} to maintain {@code Dockable} groupings within * dialogs as well as dialog size and positioning. *

* The {@code LayoutManager} is responsible for providing a persistence * mechanism to save and restore layout states. Depending on the * {@code LayoutManager} implementation, it may or may not support multiple * layout models that may be loaded and switched between at runtime. *

* Because the {@code LayoutManager} is a critical piece of the docking * infrastructure, it is not possible to install a {@code null} * {@code LayoutManager}. FlexDock provides a default {@code LayoutManager} * implementation. If this method is passed a {@code null} argument, the * default {@code LayoutManager} is used instead. * * @param mgr * the {@code LayoutManager} to install. * @see LayoutManager * @see #setLayoutManager(String) * @see #getLayoutManager() */ public static void setLayoutManager(LayoutManager mgr) { DockingManager dockingManager = getDockingManager(); if (mgr == null) { // do not allow a null layout manager. setLayoutManager(dockingManager.defaultLayoutManagerClass); } else { getDockingManager().layoutManager = mgr; } } /** * Sets the currently installed {@code LayoutManager} using the specified * class name. An attempt is make to instantiate a {@code LayoutManager} * based upon the supplied class name {@code String}. If the class cannot * be instaniated, a stacktrace is reported to the System.err and the * default {@code LayoutManager} supplied by the framework is used. If the * {@code String} parameter is {@code null}, no error occurs and the * default {@code LayoutManager} is used. If the instantiated class is not a * valid instance of {@code LayoutManager}, then a * {@code ClassCastException} is thrown. *

* The {@code LayoutManager} is responsible for managing docking layout * state. This includes tracking the state for all {@code Dockables} as they * are embedded, minimized, floated, or hidden. If a {@code Dockable} is * embedded, the {@code LayoutManager} is responsible for tracking its * position and size relative to other embedded {@code Dockables}. If * floating, the {@code LayoutManager} is responsible for supplying a * {@code FloatManager} to maintain {@code Dockable} groupings within * dialogs as well as dialog size and positioning. *

* The {@code LayoutManager} is responsible for providing a persistence * mechanism to save and restore layout states. Depending on the * {@code LayoutManager} implementation, it may or may not support multiple * layout models that may be loaded and switched between at runtime. *

* Because the {@code LayoutManager} is a critical piece of the docking * infrastructure, it is not possible to install a {@code null} * {@code LayoutManager}. FlexDock provides a default {@code LayoutManager} * implementation. If this method is passed a {@code null} argument, the * default {@code LayoutManager} is used instead. * * @param mgrClass * the class name of the {@code LayoutManager} to install. * @see LayoutManager * @see #setLayoutManager(LayoutManager) * @see #getLayoutManager() */ public static void setLayoutManager(String mgrClass) { Object instance = Utilities.getInstance(mgrClass); setLayoutManager((LayoutManager) instance); } /** * Sets the {@code DockingStrategy} associated with specified {@code Class}. * This method returns with no action taken if the specified {@code Class} * paramter is {@code null}. If the {@code strategy} parameter is * {@code null} then any existing {@code DockingStrategy} association with * the specified }Class} is removed. Otherwise, a new * {@code DockingStrategy} association is added for the specified * {@code Class}. *

* {@code DockingStrategy} association follows a strict inheritance chain * using {@code org.flexdock.util.ClassMapping}. This means that the * association created by this method applies for the specified * {@code Class} and all direct subclasses, but associations for interfaces * are ignored. Associations also do not apply for subclasses that have * their own specific {@code DockingStrategy} mapping. *

* * @param classKey * the {@code Class} whose {@code DockingStrategy} association we * wish to set * @param strategy * the {@code DockingStrategy} to be associated with the * specified {@code Class}. * @see #getDockingStrategy(Class) * @see #getDockingStrategy(Object) * @see ClassMapping#addClassMapping(Class, Class, Object) * @see ClassMapping#removeClassMapping(Class) */ public static void setDockingStrategy(Class classKey, DockingStrategy strategy) { if (classKey == null) { return; } if (strategy == null) { DOCKING_STRATEGIES.removeClassMapping(classKey); } else { DOCKING_STRATEGIES.addClassMapping(classKey, strategy.getClass(), strategy); } } /** * Undocks the specified {@code Dockable} from its parent * {@code DockingPort}. If the {@code Dockable} is {@code null}, or it * does not currently reside within a {@code DockingPort}, then this method * returns {@code false} with no action taken. Otherwise, this method * returns {@code true} if the undocking operation was successful and * {@code false} if the undocking operation could not be completed. * * This method determines the {@code DockingStrategy} to be used for * {@code DockingPort} containing the specified {@code Dockable} and defers * processing to the {@code undock(Dockable dockable)} method on the * {@code DockingStrategy}. This method's return value will be based upon * the {@code DockingStrategy} implementation returned by a call to * {@code getDockingStrategy(Object obj)}. The {@code DockingStrategy} used * may be controlled via * {@code setDockingStrategy(Class c, DockingStrategy strategy)}, supplying * a {@code DockingPort} implementation class and a customized * {@code DockingStrategy}. * * @param dockable * the {@code Dockable} to be undocked. * @return {@code true} if the undocking operation was successful, * {@code false} otherwise. * @see DockingStrategy#undock(Dockable) * @see #getDockingStrategy(Object) * @see #setDockingStrategy(Class, DockingStrategy) */ public static boolean undock(Dockable dockable) { if (dockable == null) { return false; } DockingStrategy strategy = findDockingStrategy(dockable); if (strategy != null) { return strategy.undock(dockable); } return false; // TODO think of changing it to runtime exception I // don't see a situation when there would be no default docker. } public static boolean undock(final Component dockable) { return undock(resolveDockable(dockable)); } /** * Ensures that a valid {@code DragManager} has been installed as a listener * for all of the specified {@code Dockable's} drag source * {@code Components}. This method invokes the {@code getDragSources()} * method on the specified {@code Dockable} and iterates over each * {@code Component} in the returned {@code List}. If any {@code Component} * does not have a valid {@code DragManager} listener installed, an * appropriate listener is added to enable drag-to-dock functionality. *

* This method is useful to application developers who manually attempt to * add new {@code Components} to a {@code Dockable's} drag source * {@code List}. However, it is not necessary to call this method unless * the drag source list has been updated after calling * {@code registerDockable(Dockable dockable)}, since * {@code registerDockable(Dockable dockable)} will automatically initialize * each drag source for the specified {@code Dockable}. *

* If the specified {@code Dockable} is {@code null}, then no * {@code Exception} is thrown and no action is taken. * * @param dockable * the {@code Dockable} whose drag sources are to be checked for * {@code DragManagers} and updated accordingly. * @see #registerDockable(Dockable) * @see Dockable#getDragSources() * @see DragManager */ public static void updateDragListeners(Dockable dockable) { if (dockable == null) { return; } DragManager dragListener = getDragListener(dockable); if (dragListener == null) { dragListener = new DragManager(dockable); } for (Iterator it = dockable.getDragSources().iterator(); it.hasNext();) { Object obj = it.next(); if (obj instanceof Component) { updateDragListeners((Component) obj, dragListener); } } } private static void removeDragListeners(Dockable dockable) { if (dockable == null) { return; } for (Iterator it = dockable.getDragSources().iterator(); it.hasNext();) { Object obj = it.next(); if (obj instanceof Component) { removeDragListeners((Component) obj); } } } public static float getDefaultSiblingSize() { return getDockingManager().defaultSiblingSize; } public static void setDefaultSiblingSize(float size) { size = Math.max(size, 0); size = Math.min(size, 1); getDockingManager().defaultSiblingSize = size; } public static void setRubberBand(RubberBand rubberBand) { EffectsManager.setRubberBand(rubberBand); } public static void setDragPreview(DragPreview dragPreview) { EffectsManager.setPreview(dragPreview); } /** * Maximizes the {@code Dockable} associated with the specified component or * restores the {@code Dockable} if it is currently maximized. This method * forwards the request to {@link #toggleMaximized(Dockable)} after * obtaining the {@code Dockable} associated to the component via * {@link #getDockable(Component)}. * * @param comp the component to toggle maximization for * @see #toggleMaximized(Dockable) */ public static void toggleMaximized(Component comp) { Dockable dockable = getDockable(comp); if (dockable == null) { return; } toggleMaximized(dockable); } /** * Maximizes the specified {@code Dockable} or restores the specified * {@code Dockable} if it is already maximized. *

* The scope of maximization is the root {@code DockingPort}. The * specified {@code Dockable}'s current {@code DockingPort} is asked to * temporarily lend the {@code Dockable} for maximization and the root * {@code DockingPort} is asked to temorarily host the {@code Dockable} and * display it such that it occupies all (or the majority) of its screen * resources. If the {@code Dockable} is already maximized, the root * {@code DockingPort} is asked to return to its original state and the * {@code Dockable} is returned to its original {@code DockingPort}. * * @param dockable the dockable to toggle maximization for */ public static void toggleMaximized(Dockable dockable) { DockingPort rootPort = getRootDockingPort(dockable.getComponent()); MaximizedState state = getMaximizedState(rootPort); if (state != null) { if (state.getDockable() != dockable) { throw new IllegalStateException( "Can't maximize while different dockable is maximized"); // maybe silently switch maximized dockables instead? } restoreFromMaximized(dockable, rootPort, state); } else { maximize(dockable, rootPort); } } public static boolean isMaximized(Dockable dockable) { DockingPort rootPort = getRootDockingPort(dockable.getComponent()); MaximizedState state = getMaximizedState(rootPort); return state != null && state.getDockable().equals(dockable); } private static void maximize(Dockable dockable, DockingPort rootPort) { DockingPort originalPort = dockable.getDockingPort(); MaximizedState state = new MaximizedState(dockable, originalPort); originalPort.releaseForMaximization(dockable); rootPort.installMaximizedDockable(dockable); MAXIMIZED_STATES_BY_ROOT_PORT.put(rootPort, state); } private static void restoreFromMaximized(Dockable dockable, DockingPort rootPort, MaximizedState state) { // restore original state in reverse order than maximizing it // (otherwise this will not work if original port and root port are // identical) rootPort.uninstallMaximizedDockable(); state.getOriginalPort().returnFromMaximization(); MAXIMIZED_STATES_BY_ROOT_PORT.remove(rootPort); } private static MaximizedState getMaximizedState(DockingPort rootPort) { return (MaximizedState) MAXIMIZED_STATES_BY_ROOT_PORT.get(rootPort); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy