/*
* 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:
*
* <dl>
* <dt>Maintaining a component repository.</dt>
* <dd> All {@code Dockables} and {@code DockingPorts} are cached within an and
* accessible through and internal registry. </dd>
* <dt>Maintaining framework state.</dt>
* <dd>{@code DockingManager} provides APIs for managing various different
* global framework settings, including application-key, floating support,
* auto-persistence, {@code LayoutManagers}, and {@code MinimizationManagers}.
* </dd>
* <dt>Behavioral auto-configuration.</dt>
* <dd> {@code DockingManager} automatically adds and removes necessary event
* listeners to enable/disable drag-to-dock behavior as components are
* registered and unregistered. </dd>
* <dt>Programmatic access to docking operations.</dt>
* <dd> {@code DockingManager} provides public APIs for programmatically dock,
* undock, minimize, persist, and load {@code Dockables} from storage. </dd>
* </dl>
*
* @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 maximizedStatesByRootPort = 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;
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 CENTER region.
* This is a global default setting and applies to any <cod>DockingPort}
* that does not have a specific contradictory local setting.
* <p>
* 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.
*
* @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)}.
* <p>
* 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.
* <p>
* 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)}.
* <p>
* {@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 <i>strict</i> in the sense that
* only superclasses are checked. Implemented interfaces are ignored.
* <p>
* 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}.
* <p>
* {@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 <i>strict</i> in the sense that only
* superclasses are checked. Implemented interfaces are ignored.
* <p>
* 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 <b>not</b> 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.
* <p>
* 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()}.
* <p>
* 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.
* <p>
* 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)}.
* <p>
* 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}.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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}.
* <p>
* 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
* @throws PersisterException
* @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.
* <p>
* 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
* @throws PersisterException
* @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.
* <p>
* 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
* @throws PersisterException
* @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.
* <p>
* 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.
* @throws PersisterException
* @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}.
* <p>
* 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 PersisterException
* @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);
}
// if we couldn't find a dockable from the factory, then try getting
// a component.
else {
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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* If the {@code dockable} parameter is {@code null}, then this method
* returns a {@code null} reference.
* <p>
* 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.
* <p>
* 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)}.
* <p>
* 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.
* <p>
* 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)}.
* <p>
* 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" (<i>width</i> for horizontal split, <i>height</i>
* 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.
* <p>
* 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).
* <p>
* 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.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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" (<i>width</i> for horizontal split, <i>height</i>
* 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.
* <p>
* 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).
* <p>
* 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.
* <p>
* 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.
* <p>
* 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 <b>within</b> the
* specified {@code DockingPort}, whereas the other methods modify the
* split layout <b>containing</b> their respective {@code Dockable}
* parameters.
* <p>
* 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" (<i>width</i> for horizontal split, <i>height</i>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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}.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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}.
* <p>
* 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}.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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)}.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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}.
* <p>
* {@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.
* <p>
*
* @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.
* <p>
* 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 <b>after</b> calling
* {@code registerDockable(Dockable dockable)}, since
* {@code registerDockable(Dockable dockable)} will automatically initialize
* each drag source for the specified {@code Dockable}.
* <p>
* 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
* @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.
* <p>
* The scope of maximization is the <i>root</i> {@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
*/
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);
maximizedStatesByRootPort.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();
maximizedStatesByRootPort.remove(rootPort);
}
private static MaximizedState getMaximizedState(DockingPort rootPort) {
return (MaximizedState) maximizedStatesByRootPort.get(rootPort);
}
}