/*
* Created on May 31, 2005
*/
package org.flexdock.docking.floating.policy;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import org.flexdock.docking.Dockable;
import org.flexdock.docking.drag.DragManager;
import org.flexdock.docking.event.DockingEvent;
import org.flexdock.docking.event.DockingListener;
/**
* This class provides centralized control over the framework's floating
* behavior. This includes global behavior and behavior local to each individual
* docking operation.
* <p>
* This class contains a method {@code isGlobalFloatingEnabled()} that indicates
* whether global floating support is enabled. If global floating support is
* disabled, then the setting governs all docking operations and blocks floating
* in a global sense. If global floating support is enabled, then floating is
* allowed or disallowed on an individual operation-by-operation basis through a
* set of {@code FloatPolicy} implementations.
* <p>
* The default setting for global floating support is {@code false}. This,
* however, may be controlled by the system property {@code FLOATING_ALLOWED}.
* If the framework starts up with a case-sensitive {@code String} value of
* {@code "true"} for this system property, then global floating support will be
* turned on by default. Otherwise, global floating support may be modified via
* {@code setGlobalFloatingEnabled(boolean globalFloatingEnabled)}.
* <p>
* This class provides methods {@code addPolicy(FloatPolicy policy)} and
* {@code removePolicy(FloatPolicy policy)}, allowing the user to implement
* custom behavior to control floating support for individual docking operations
* on an event-by-event basis. By default, the {@code FloatPolicyManager} has a
* single {@code FloatPolicy} installed of type {@code DefaultFloatPolicy}.
*
* @author Christopher Butler
*/
public class FloatPolicyManager extends DockingListener.Stub {
private static final FloatPolicyManager SINGLETON = new FloatPolicyManager();
/**
* Key constant used within the drag context {@code Map} to indicate whether
* floating is allowed for a given drag operation.
*
* @see DragManager#getDragContext(Dockable)
*/
public static final String FLOATING_ALLOWED = "FloatPolicyManager.FLOATING_ALLOWED";
/**
* System property key used during framework initialization to determine the
* default setting for global floating support.
*/
public static final String GLOBAL_FLOATING_ENABLED = "global.floating.enabled";
private Vector policies;
private boolean globalFloatingEnabled;
/**
* Returns a singleton instance of the {@code FloatPolicyManager} class.
*
* @return a singleton instance of the {@code FloatPolicyManager} class.
*/
public static FloatPolicyManager getInstance() {
return SINGLETON;
}
private FloatPolicyManager() {
policies = new Vector();
addPolicy(DefaultFloatPolicy.getInstance());
globalFloatingEnabled = Boolean.getBoolean(GLOBAL_FLOATING_ENABLED);
}
/**
* This method catches {@code DockingEvents} per the {@code DockingListener}
* interface at the start of a drag operation and initializes floating
* support within the the context {@code Map} of the drag operation. This
* method retrieves the {@code Dockable} for the event via its
* {@code getDockable()} method. It also retrieves the drag context
* {@code Map} for the {@code DockingEvent} by invoking its
* {@code getDragContext()} method. This map is the same {@code Map}
* returned by {@code DragManager.getDragContext(Dockable dockable)}. It
* then calls {@code isPolicyFloatingSupported(Dockable dockable)} for the
* {@code Dockable} and places either {@code Boolean.TRUE} or
* {@code Boolean.FALSE} within the drag context {@code Map}, caching the
* value for use throughout the drag operation to avoid successive
* iterations through the entire installed {@code FloatPolicy} collection.
* The {@code Map}-key used is {@code FLOATING_ALLOWED}.
*
* @param evt
* the {@code DockingEvent} whose drag context is to be
* initialized for floating support
* @see DockingEvent#getDragContext()
* @see DockingEvent#getDockable()
* @see #isPolicyFloatingSupported(Dockable)
* @see #FLOATING_ALLOWED
*/
public void dragStarted(DockingEvent evt) {
Map context = evt.getDragContext();
Dockable d = evt.getDockable();
Boolean allowed = isPolicyFloatingSupported(d) ? Boolean.TRUE
: Boolean.FALSE;
context.put(FLOATING_ALLOWED, allowed);
}
/**
* This method catches {@code DockingEvents} per the {@code DockingListener}
* interface at the end of a drag operation and determines whether or not to
* block attempts to float within the docking operation.
* <p>
* If {@code evt.isOverWindow()} returns {@code true}, then the drop
* operation is over an existing window and will be interpreted as an
* attempt to dock within the window, not an attempt to float into a new
* dialog. In this case, this method returns immediately with no action
* taken.
* <p>
* This method calls {@code isFloatingAllowed(Dockable dockable)} using the
* {@code DockingEvent's} {@code Dockable}, retrieved from
* {@code getDockable()}. If this method returns {@code false}, then the
* {@code DockingEvent} is consumed and this method returns.
* <p>
* If {@code isFloatingAllowed(Dockable dockable)} returns {@code true},
* then the internal {@code FloatPolicy} collection is iterated through,
* allowing each installed {@code FloatPolicy} to confirm the drop operation
* via {@code isFloatDropAllowed(DockingEvent evt)}. If any of the
* installed {@code FloatPolicies} returns {@code false} for
* {@code isFloatDropAllowed(DockingEvent evt)}, then the
* {@code DockingEvent} is consumed and the method exits.
* <p>
* If this method completes without the {@code DockingEvent} being consumed,
* then the docking operation will proceed and attempts to float will be
* allowed.
*
* @param evt
* the {@code DockingEvent} to be examined for floating support
* @see DockingEvent#isOverWindow()
* @see DockingEvent#getDockable()
* @see DockingEvent#consume()
* @see #isFloatingAllowed(Dockable)
* @see FloatPolicy#isFloatDropAllowed(DockingEvent)
*/
public void dropStarted(DockingEvent evt) {
if (evt.isOverWindow())
return;
if (!isFloatingAllowed(evt.getDockable())) {
evt.consume();
return;
}
for (Iterator it = policies.iterator(); it.hasNext();) {
FloatPolicy policy = (FloatPolicy) it.next();
if (!policy.isFloatDropAllowed(evt)) {
evt.consume();
return;
}
}
}
/**
* Indicates whether floating is allowed for the specified {@code Dockable}.
* If {@code dockable} is {@code null}, this method returns {@code false}.
* <p>
* This method first calls
* {@code DragManager.getDragContext(Dockable dockable)} to see if a drag
* operation is in progress. If so, it returns the {@code boolean} value
* contained within the drag context map using the key
* {@code FLOATING_ALLOWED}. If no mapping exists for the specified key,
* this method returns {@code false}.
* <p>
* If no drag operation is currently in progress and no drag context can be
* found, this method dispatches to
* {@code isPolicyFloatingSupported(Dockable dockable)}, which iterates
* through all installed {@code FloatPolicies} to determine whether floating
* support is allowed.
*
* @param dockable
* the {@code Dockable} whose floating support is to be checked
* @return {@code true} if floating is allowed for the specified
* {@code Dockable}; {@code false} otherwise.
* @see DragManager#getDragContext(Dockable)
* @see #getInstance()
* @see #isPolicyFloatingSupported(Dockable)
* @see #FLOATING_ALLOWED
*/
public static boolean isFloatingAllowed(Dockable dockable) {
if (dockable == null)
return false;
Map context = DragManager.getDragContext(dockable);
if (context == null)
return getInstance().isPolicyFloatingSupported(dockable);
Boolean floatAllowed = (Boolean) context.get(FLOATING_ALLOWED);
return floatAllowed == null ? true : floatAllowed.booleanValue();
}
/**
* Indicates whether floating is allowed for the specified {@code Dockable}
* strictly by checking the installed {@code FloatPolicies}. If
* {@code dockable} is {@code null}, this method returns {@code false}
* immediately without checking through the installed {@code FloatPolicies}.
* <p>
* This method iterates through all installed {@code FloatPolicies} to
* determine whether floating support is allowed. If any {@code FloatPolicy}
* within the internal collection returns {@code false} from its
* {@code isFloatingAllowed(Dockable dockable)} method, this method returns
* {@code false}. Otherwise, this method returns {@code true}.
*
* @param dockable
* the {@code Dockable} whose floating support is to be checked
* @return {@code true} if floating is allowed for the specified
* {@code Dockable}; {@code false} otherwise.
* @see FloatPolicy#isFloatingAllowed(Dockable)
*/
public boolean isPolicyFloatingSupported(Dockable dockable) {
if (dockable == null)
return false;
for (Iterator it = policies.iterator(); it.hasNext();) {
FloatPolicy policy = (FloatPolicy) it.next();
if (!policy.isFloatingAllowed(dockable))
return false;
}
return true;
}
/**
* Adds the specified {@code FloatPolicy} to the internal policy collection.
* This {@code FloatPolicy} will now take part in framework determinations
* as to whether floating should be supported during docking operations. If
* {@code policy} is {@code null}, no action is taken.
*
* @param policy
* the {@code FloatPolicy} to add to the system
* @see #removePolicy(FloatPolicy)
*/
public void addPolicy(FloatPolicy policy) {
if (policy != null)
policies.add(policy);
}
/**
* Removes the specified {@code FloatPolicy} from the internal policy
* collection. {@code FloatPolicy} will no longer take part in framework
* determinations as to whether floating should be supported during docking
* operations. If {@code policy} is {@code null} or was not previously
* installed, no action is taken.
*
* @param policy
* the {@code FloatPolicy} to remove from the system
* @see #addPolicy(FloatPolicy)
*/
public void removePolicy(FloatPolicy policy) {
if (policy != null)
policies.remove(policy);
}
/**
* Returns a global setting used to control default framework floating
* behavior. If this method returns {@code false}, all floating support for
* the entire framework is turned off. If this method returns {@code true},
* then floating support for individual docking operations is deferred to
* the installed {@code FloatPolicies}.
*
* @return {@code true} if global floating support is enabled; {@code false}
* otherwise.
* @see #setGlobalFloatingEnabled(boolean)
*/
public static boolean isGlobalFloatingEnabled() {
return getInstance().globalFloatingEnabled;
}
/**
* Sets the global setting used to control default framework floating
* behavior. If {@code globalFloatingEnabled} is {@code false}, all
* floating support for the entire framework is turned off. If
* {@code globalFloatingEnabled} is {@code true}, then floating support for
* individual docking operations is deferred to the installed
* {@code FloatPolicies}.
*
* @param globalFloatingEnabled
* {@code true} if global floating support is to be enabled;
* {@code false} otherwise.
* @see #isGlobalFloatingEnabled()
*/
public static void setGlobalFloatingEnabled(boolean globalFloatingEnabled) {
getInstance().globalFloatingEnabled = globalFloatingEnabled;
}
}