diff options
Diffstat (limited to 'libjava/java/awt/KeyboardFocusManager.java')
-rw-r--r-- | libjava/java/awt/KeyboardFocusManager.java | 807 |
1 files changed, 579 insertions, 228 deletions
diff --git a/libjava/java/awt/KeyboardFocusManager.java b/libjava/java/awt/KeyboardFocusManager.java index 867316b..8ebd9e1 100644 --- a/libjava/java/awt/KeyboardFocusManager.java +++ b/libjava/java/awt/KeyboardFocusManager.java @@ -39,34 +39,66 @@ exception statement from your version. */ package java.awt; import java.awt.event.KeyEvent; +import java.awt.event.FocusEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.beans.VetoableChangeSupport; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; +// FIXME: finish documentation + /** * + * FIXME: discuss applet contexts and thread groups and codebases + * being insulated. + * + * FIXME: discuss where default focus traversal key sets apply + * (inherited by child Components etc.) + * * @author Eric Blake <ebb9@email.byu.edu> + * @author Thomas Fitzsimmons <fitzsim@redhat.com> * @since 1.4 * @status partially updated to 1.4, needs documentation. */ public abstract class KeyboardFocusManager implements KeyEventDispatcher, KeyEventPostProcessor { + /** Identifies {@link AWTKeyStroke}s that move the focus forward in + the focus cycle. */ public static final int FORWARD_TRAVERSAL_KEYS = 0; + + /** Identifies {@link AWTKeyStroke}s that move the focus backward in + the focus cycle. */ public static final int BACKWARD_TRAVERSAL_KEYS = 1; + + /** Identifies {@link AWTKeyStroke}s that move the focus up to the + parent focus cycle root. */ public static final int UP_CYCLE_TRAVERSAL_KEYS = 2; + + /** Identifies {@link AWTKeyStroke}s that move the focus down to the + child focus cycle root. */ public static final int DOWN_CYCLE_TRAVERSAL_KEYS = 3; + /** The set of {@link AWTKeyStroke}s that cause focus to be moved to + the next focusable Component in the focus cycle. */ private static final Set DEFAULT_FORWARD_KEYS; + + /** The set of {@link AWTKeyStroke}s that cause focus to be moved to + the previous focusable Component in the focus cycle. */ private static final Set DEFAULT_BACKWARD_KEYS; + + /** Populate the DEFAULT_FORWARD_KEYS and DEFAULT_BACKWARD_KEYS + {@link java.util.Set}s. */ static { Set s = new HashSet(); @@ -83,232 +115,402 @@ public abstract class KeyboardFocusManager DEFAULT_BACKWARD_KEYS = Collections.unmodifiableSet(s); } - private static KeyboardFocusManager current - = new DefaultKeyboardFocusManager(); - - // XXX Not implemented correctly. I think a good implementation here may - // be to have permanentFocusOwner be null, and fall back to focusOwner, - // unless a temporary focus change is in effect. - private static Component focusOwner; - private static Component permanentFocusOwner; - - private static Window focusedWindow; - private static Window activeWindow; - private static Container focusCycleRoot; - + /** The global object {@link java.util.Map}s. */ + + /** For security reasons, {@link java.applet.Applet}s in different + codebases must be insulated from one another. Since {@link + KeyboardFocusManager}s have the ability to return {@link + Component}s from a given {@link java.applet.Applet}, each + codebase must have an independent {@link KeyboardFocusManager}. + Since each codebase has its own {@link ThreadGroup} in which its + {@link Applet}s run, it makes sense to partition {@link + KeyboardFocusManager}s according to {@link + java.lang.ThreadGroup}. Thus, currentKeyboardFocusManagers is a + {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}. */ + private static Map currentKeyboardFocusManagers = new HashMap (); + + /** {@link java.applet.Applet}s in one codebase must not be allowed + to access {@link Component}s in {@link java.applet.Applet}s in + other codebases. To enforce this restriction, we key the + following {@link java.util.Map}s on {@link java.lang.ThreadGroup}s (which + are per-codebase). For example, if {@link + java.lang.ThreadGroup} A calls {@link #setGlobalFocusOwner}, + passing {@link Component} C, currentFocusOwners[A] is assigned + C, and all other currentFocusOwners values are nullified. Then + if {@link java.lang.ThreadGroup} A subsequently calls {@link + #getGlobalFocusOwner}, it will return currentFocusOwners[A], + that is, {@link Component} C. If another {@link + java.lang.ThreadGroup} K calls {@link #getGlobalFocusOwner}, it + will return currentFocusOwners[K], that is, null. + + Since this is a static field, we ensure that there is only one + focused {@link Component} per class loader. */ + private static Map currentFocusOwners = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the {@link Component} that owns the permanent + keyboard focus. @see currentFocusOwners */ + private static Map currentPermanentFocusOwners = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the focused {@link Window}. @see + currentFocusOwners */ + private static Map currentFocusedWindows = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the active {@link Window}. @see + currentFocusOwners */ + private static Map currentActiveWindows = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the focus cycle root {@link Container}. @see + currentFocusOwners */ + private static Map currentFocusCycleRoots = new HashMap (); + + /** The default {@link FocusTraveralPolicy} that focus-managing + {@link Container}s will use to define their initial focus + traversal policy. */ private FocusTraversalPolicy defaultPolicy; - private Set[] defaultFocusKeys = new Set[] { + + /** An array that stores the {@link #FORWARD_TRAVERSAL_KEYS}, {@link + #BACKWARD_TRAVERSAL_KEYS}, {@link #UP_CYCLE_TRAVERSAL_KEYS} and + {@link #DOWN_CYCLE_TRAVERSAL_KEYS} {@link AWTKeyStroke}s {@link + java.util.Set}s. */ + private Set[] defaultFocusKeys = new Set[] + { DEFAULT_FORWARD_KEYS, DEFAULT_BACKWARD_KEYS, Collections.EMPTY_SET, Collections.EMPTY_SET }; - private final PropertyChangeSupport propertyChangeSupport - = new PropertyChangeSupport(this); - private final VetoableChangeSupport vetoableChangeSupport - = new VetoableChangeSupport(this); + private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport (this); + private final VetoableChangeSupport vetoableChangeSupport = new VetoableChangeSupport (this); + + /** A list of {@link KeyEventDispatcher}s that process {@link + KeyEvent}s before they are processed the default keyboard focus + manager. */ private final ArrayList keyEventDispatchers = new ArrayList(); + + /** A list of {@link KeyEventPostProcessor}s that process unconsumed + {@link KeyEvent}s. */ private final ArrayList keyEventPostProcessors = new ArrayList(); - - public KeyboardFocusManager() + /** + * Construct a KeyboardFocusManager. + */ + public KeyboardFocusManager () { } - public static KeyboardFocusManager getCurrentKeyboardFocusManager() + /** + * Retrieve the keyboard focus manager associated with the {@link + * java.lang.ThreadGroup} to which the calling thread belongs. + * + * @return the keyboard focus manager associated with the current + * thread group + */ + public static KeyboardFocusManager getCurrentKeyboardFocusManager () { - // XXX Need a way to divide this into contexts. - return current; + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + return (KeyboardFocusManager) currentKeyboardFocusManagers.get (currentGroup); } - public static void setCurrentKeyboardFocusManager(KeyboardFocusManager m) + /** + * Set the keyboard focus manager associated with the {@link + * java.lang.ThreadGroup} to which the calling thread belongs. + * + * @param m the keyboard focus manager for the current thread group + */ + public static void setCurrentKeyboardFocusManager (KeyboardFocusManager m) { - SecurityManager sm = System.getSecurityManager(); + SecurityManager sm = System.getSecurityManager (); if (sm != null) - sm.checkPermission(new AWTPermission("replaceKeyboardFocusManager")); - // XXX Need a way to divide this into contexts. - current = m == null ? new DefaultKeyboardFocusManager() : m; - } - - public Component getFocusOwner() + sm.checkPermission (new AWTPermission ("replaceKeyboardFocusManager")); + + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + KeyboardFocusManager manager; + + if (m == null) + manager = new DefaultKeyboardFocusManager (); + else + manager = m; + + currentKeyboardFocusManagers.put (currentGroup, manager); + } + + /** + * Retrieve the {@link Component} that has the keyboard focus, or + * null if the focus owner was not set by a thread in the current + * {@link java.lang.ThreadGroup}. + * + * @return the keyboard focus owner or null + */ + public Component getFocusOwner () + { + return (Component) getObject (currentFocusOwners); + } + + /** + * Retrieve the {@link Component} that has the keyboard focus, + * regardless of whether or not it was set by a thread in the + * current {@link java.lang.ThreadGroup}. If there is no temporary + * focus owner in effect then this method will return the same value + * as {@link #getGlobalPermanentFocusOwner}. + * + * @return the keyboard focus owner + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Component getGlobalFocusOwner () + { + // Check if there is a temporary focus owner. + Component focusOwner = (Component) getGlobalObject (currentFocusOwners); + + return (focusOwner == null) ? getGlobalPermanentFocusOwner () : focusOwner; + } + + /** + * Set the {@link Component} that will be returned by {@link + * #getFocusOwner} (when it is called from the current {@link + * java.lang.ThreadGroup}) and {@link #getGlobalFocusOwner}. This + * method does not actually transfer the keyboard focus. + * + * @param owner the Component to return from getFocusOwner and + * getGlobalFocusOwner + * + * @see Component.requestFocus () + * @see Component.requestFocusInWindow () + */ + protected void setGlobalFocusOwner (Component owner) { - // XXX Need an easy way to test if this thread is in the context of the - // global focus owner, to avoid creating the exception in the first place. - try - { - return getGlobalFocusOwner(); - } - catch (SecurityException e) - { - return null; - } - } - - protected Component getGlobalFocusOwner() - { - // XXX Need a way to test if this thread is in the context of the focus - // owner, and throw a SecurityException if that is the case. - // XXX Implement. - return focusOwner; - } - - protected void setGlobalFocusOwner(Component owner) - { - // XXX Should this send focus events to the components involved? if (owner == null || owner.focusable) - { - firePropertyChange("focusOwner", focusOwner, owner); - try - { - fireVetoableChange("focusOwner", focusOwner, owner); - focusOwner = owner; - } - catch (PropertyVetoException e) - { - } - } - } - - public void clearGlobalFocusOwner() - { - // XXX Is this enough? - setGlobalFocusOwner(null); + setGlobalObject (currentFocusOwners, owner, "focusOwner"); } - public Component getPermanentFocusOwner() + /** + * Clear the global focus owner and deliver a FOCUS_LOST event to + * the previously-focused {@link Component}. Until another {@link + * Component} becomes the keyboard focus owner, key events will be + * discarded by top-level windows. + */ + public void clearGlobalFocusOwner () { - // XXX Need an easy way to test if this thread is in the context of the - // global focus owner, to avoid creating the exception in the first place. - try + synchronized (currentFocusOwners) { - return getGlobalPermanentFocusOwner(); - } - catch (SecurityException e) - { - return null; - } - } + Component focusOwner = getGlobalFocusOwner (); + Component permanentFocusOwner = getGlobalPermanentFocusOwner (); - protected Component getGlobalPermanentFocusOwner() - { - // XXX Need a way to test if this thread is in the context of the focus - // owner, and throw a SecurityException if that is the case. - // XXX Implement. - return permanentFocusOwner == null ? focusOwner : permanentFocusOwner; - } + setGlobalFocusOwner (null); + setGlobalPermanentFocusOwner (null); - protected void setGlobalPermanentFocusOwner(Component focusOwner) - { - // XXX Should this send focus events to the components involved? - if (focusOwner == null || focusOwner.focusable) - { - firePropertyChange("permanentFocusOwner", permanentFocusOwner, - focusOwner); - try + // Inform the old focus owner that it has lost permanent + // focus. + if (focusOwner != null) { - fireVetoableChange("permanentFocusOwner", permanentFocusOwner, - focusOwner); - permanentFocusOwner = focusOwner; + // We can't cache the event queue, because of + // bootstrapping issues. We need to set the default + // KeyboardFocusManager in EventQueue before the event + // queue is started. + EventQueue q = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + if (focusOwner != permanentFocusOwner) + q.postEvent (new FocusEvent (focusOwner, FocusEvent.FOCUS_LOST, true)); + else + q.postEvent (new FocusEvent (focusOwner, FocusEvent.FOCUS_LOST, false)); } - catch (PropertyVetoException e) + + if (focusOwner != permanentFocusOwner) { + EventQueue q = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + q.postEvent (new FocusEvent (permanentFocusOwner, FocusEvent.FOCUS_LOST, false)); } } } - public Window getFocusedWindow() + /** + * Retrieve the {@link Component} that has the permanent keyboard + * focus, or null if the focus owner was not set by a thread in the + * current {@link java.lang.ThreadGroup}. + * + * @return the keyboard focus owner or null + */ + public Component getPermanentFocusOwner () + { + return (Component) getObject (currentPermanentFocusOwners); + } + + /** + * Retrieve the {@link Component} that has the permanent keyboard + * focus, regardless of whether or not it was set by a thread in the + * current {@link java.lang.ThreadGroup}. + * + * @return the keyboard focus owner + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Component getGlobalPermanentFocusOwner () + { + return (Component) getGlobalObject (currentPermanentFocusOwners); + } + + /** + * Set the {@link Component} that will be returned by {@link + * #getPermanentFocusOwner} (when it is called from the current + * {@link java.lang.ThreadGroup}) and {@link + * #getGlobalPermanentFocusOwner}. This method does not actually + * transfer the keyboard focus. + * + * @param focusOwner the Component to return from + * getPermanentFocusOwner and getGlobalPermanentFocusOwner + * + * @see Component.requestFocus () + * @see Component.requestFocusInWindow () + */ + protected void setGlobalPermanentFocusOwner (Component focusOwner) { - // XXX Need an easy way to test if this thread is in the context of the - // global focus owner, to avoid creating the exception in the first place. - try - { - return getGlobalFocusedWindow(); - } - catch (SecurityException e) - { - return null; - } - } - - protected Window getGlobalFocusedWindow() - { - // XXX Need a way to test if this thread is in the context of the focus - // owner, and throw a SecurityException if that is the case. - // XXX Implement. - return focusedWindow; - } - - protected void setGlobalFocusedWindow(Window window) + if (focusOwner == null || focusOwner.focusable) + setGlobalObject (currentPermanentFocusOwners, focusOwner, + "permanentFocusOwner"); + } + + /** + * Retrieve the {@link Window} that is or contains the keyboard + * focus owner, or null if the focused window was not set by a + * thread in the current {@link java.lang.ThreadGroup}. + * + * @return the focused window or null + */ + public Window getFocusedWindow () + { + return (Window) getObject (currentFocusedWindows); + } + + /** + * Retrieve the {@link Window} that is or contains the focus owner, + * regardless of whether or not the {@link Window} was set focused + * by a thread in the current {@link java.lang.ThreadGroup}. + * + * @return the focused window + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Window getGlobalFocusedWindow () + { + return (Window) getGlobalObject (currentFocusedWindows); + } + + /** + * Set the {@link Window} that will be returned by {@link + * #getFocusedWindow} (when it is called from the current {@link + * java.lang.ThreadGroup}) and {@link #getGlobalFocusedWindow}. + * This method does not actually cause <code>window</code> to become + * the focused {@link Window}. + * + * @param window the Window to return from getFocusedWindow and + * getGlobalFocusedWindow + */ + protected void setGlobalFocusedWindow (Window window) { - // XXX Should this send focus events to the windows involved? if (window == null || window.focusable) - { - firePropertyChange("focusedWindow", focusedWindow, window); - try - { - fireVetoableChange("focusedWindow", focusedWindow, window); - focusedWindow = window; - } - catch (PropertyVetoException e) - { - } - } + setGlobalObject (currentFocusedWindows, window, "focusedWindow"); } + /** + * Retrieve the active {@link Window}, or null if the active window + * was not set by a thread in the current {@link + * java.lang.ThreadGroup}. + * + * @return the active window or null + */ public Window getActiveWindow() { - // XXX Need an easy way to test if this thread is in the context of the - // global focus owner, to avoid creating the exception in the first place. - try - { - return getGlobalActiveWindow(); - } - catch (SecurityException e) - { - return null; - } + return (Window) getObject (currentActiveWindows); } + /** + * Retrieve the active {@link Window}, regardless of whether or not + * the {@link Window} was made active by a thread in the current + * {@link java.lang.ThreadGroup}. + * + * @return the active window + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ protected Window getGlobalActiveWindow() { - // XXX Need a way to test if this thread is in the context of the focus - // owner, and throw a SecurityException if that is the case. - // XXX Implement. - return activeWindow; + return (Window) getGlobalObject (currentActiveWindows); } + /** + * Set the {@link Window} that will be returned by {@link + * #getActiveWindow} (when it is called from the current {@link + * java.lang.ThreadGroup}) and {@link #getGlobalActiveWindow}. This + * method does not actually cause <code>window</code> to be made + * active. + * + * @param window the Window to return from getActiveWindow and + * getGlobalActiveWindow + */ protected void setGlobalActiveWindow(Window window) { - // XXX Should this send focus events to the windows involved? - firePropertyChange("activeWindow", activeWindow, window); - try - { - fireVetoableChange("activeWindow", activeWindow, window); - activeWindow = window; - } - catch (PropertyVetoException e) - { - } + setGlobalObject (currentActiveWindows, window, "activeWindow"); } - public FocusTraversalPolicy getDefaultFocusTraversalPolicy() + /** + * Retrieve the default {@link FocusTraversalPolicy}. + * Focus-managing {@link Container}s use the returned object to + * define their initial focus traversal policy. + * + * @return a non-null default FocusTraversalPolicy object + */ + public FocusTraversalPolicy getDefaultFocusTraversalPolicy () { if (defaultPolicy == null) - defaultPolicy = new DefaultFocusTraversalPolicy(); + defaultPolicy = new DefaultFocusTraversalPolicy (); return defaultPolicy; } - public void setDefaultFocusTraversalPolicy(FocusTraversalPolicy policy) + /** + * Set the {@link FocusTraversalPolicy} returned by {@link + * #getDefaultFocusTraversalPolicy}. Focus-managing {@link + * Container}s created after this call will use policy as their + * initial focus traversal policy. Existing {@link Container}s' + * focus traversal policies will not be affected by calls to this + * method. + * + * @param policy the FocusTraversalPolicy that will be returned by + * subsequent calls to getDefaultFocusTraversalPolicy + * @throws IllegalArgumentException if policy is null + */ + public void setDefaultFocusTraversalPolicy (FocusTraversalPolicy policy) { if (policy == null) - throw new IllegalArgumentException(); - firePropertyChange("defaultFocusTraversalPolicy", defaultPolicy, policy); + throw new IllegalArgumentException (); + firePropertyChange ("defaultFocusTraversalPolicy", defaultPolicy, policy); defaultPolicy = policy; } - public void setDefaultFocusTraversalKeys(int id, Set keystrokes) - { + /** + * Set the default {@link java.util.Set} of focus traversal keys for + * one of the focus traversal directions. + * + * @param id focus traversal direction identifier + * @param keystrokes set of AWTKeyStrokes + * + * @see #FORWARD_TRAVERSAL_KEYS + * @see #BACKWARD_TRAVERSAL_KEYS + * @see #UP_CYCLE_TRAVERSAL_KEYS + * @see #DOWN_CYCLE_TRAVERSAL_KEYS + */ + public void setDefaultFocusTraversalKeys (int id, Set keystrokes) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && + id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + if (keystrokes == null) - throw new IllegalArgumentException(); + throw new IllegalArgumentException (); + Set sa; Set sb; Set sc; @@ -340,56 +542,82 @@ public abstract class KeyboardFocusManager type = "downCycleDefaultFocusTraversalKeys"; break; default: - throw new IllegalArgumentException(); + throw new IllegalArgumentException (); } - int i = keystrokes.size(); - Iterator iter = keystrokes.iterator(); + int i = keystrokes.size (); + Iterator iter = keystrokes.iterator (); while (--i >= 0) { - Object o = iter.next(); - if (! (o instanceof AWTKeyStroke) - || sa.contains(o) || sb.contains(o) || sc.contains(o) + Object o = iter.next (); + if (!(o instanceof AWTKeyStroke) + || sa.contains (o) || sb.contains (o) || sc.contains (o) || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED) - throw new IllegalArgumentException(); + throw new IllegalArgumentException (); } - keystrokes = Collections.unmodifiableSet(new HashSet(keystrokes)); - firePropertyChange(type, defaultFocusKeys[id], keystrokes); + keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes)); + firePropertyChange (type, defaultFocusKeys[id], keystrokes); defaultFocusKeys[id] = keystrokes; } - public Set getDefaultFocusTraversalKeys(int id) + /** + * Retrieve the default {@link java.util.Set} of focus traversal + * keys for one of the focus traversal directions. + * + * @param id focus traversal direction identifier + * + * @return the default set of AWTKeyStrokes + * + * @see #FORWARD_TRAVERSAL_KEYS + * @see #BACKWARD_TRAVERSAL_KEYS + * @see #UP_CYCLE_TRAVERSAL_KEYS + * @see #DOWN_CYCLE_TRAVERSAL_KEYS + */ + public Set getDefaultFocusTraversalKeys (int id) { if (id < FORWARD_TRAVERSAL_KEYS || id > DOWN_CYCLE_TRAVERSAL_KEYS) - throw new IllegalArgumentException(); + throw new IllegalArgumentException (); return defaultFocusKeys[id]; } - public Container getCurrentFocusCycleRoot() + /** + * Retrieve the current focus cycle root, or null if the focus owner + * was not set by a thread in the current {@link + * java.lang.ThreadGroup}. + * + * @return the current focus cycle root or null + */ + public Container getCurrentFocusCycleRoot () { - // XXX Need an easy way to test if this thread is in the context of the - // global focus owner, to avoid creating the exception in the first place. - try - { - return getGlobalCurrentFocusCycleRoot(); - } - catch (SecurityException e) - { - return null; - } + return (Container) getObject (currentFocusCycleRoots); } - protected Container getGlobalCurrentFocusCycleRoot() + /** + * Retrieve the current focus cycle root, regardless of whether or + * not it was made set by a thread in the current {@link + * java.lang.ThreadGroup}. + * + * @return the current focus cycle root + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Container getGlobalCurrentFocusCycleRoot () { - // XXX Need a way to test if this thread is in the context of the focus - // owner, and throw a SecurityException if that is the case. - // XXX Implement. - return focusCycleRoot; + return (Container) getGlobalObject (currentFocusCycleRoots); } - public void setGlobalCurrentFocusCycleRoot(Container cycleRoot) + /** + * Set the {@link Container} that will be returned by {@link + * #getCurrentFocusCycleRoot} (when it is called from the current + * {@link java.lang.ThreadGroup}) and {@link + * #getGlobalCurrentFocusCycleRoot}. This method does not actually + * make <code>cycleRoot</code> the current focus cycle root. + * + * @param cycleRoot the focus cycle root to return from + * getCurrentFocusCycleRoot and getGlobalCurrentFocusCycleRoot + */ + public void setGlobalCurrentFocusCycleRoot (Container cycleRoot) { - firePropertyChange("currentFocusCycleRoot", focusCycleRoot, cycleRoot); - focusCycleRoot = cycleRoot; + setGlobalObject (currentFocusCycleRoots, cycleRoot, "currentFocusCycleRoot"); } public void addPropertyChangeListener(PropertyChangeListener l) @@ -484,73 +712,196 @@ public abstract class KeyboardFocusManager keyEventDispatchers.remove(dispatcher); } - protected List getKeyEventDispatchers() + protected List getKeyEventDispatchers () { - return (List) keyEventDispatchers.clone(); + return (List) keyEventDispatchers.clone (); } - public void addKeyEventPostProcessor(KeyEventPostProcessor postProcessor) + public void addKeyEventPostProcessor (KeyEventPostProcessor postProcessor) { if (postProcessor != null) - keyEventPostProcessors.add(postProcessor); + keyEventPostProcessors.add (postProcessor); } - public void removeKeyEventPostProcessor(KeyEventPostProcessor postProcessor) + public void removeKeyEventPostProcessor (KeyEventPostProcessor postProcessor) { - keyEventPostProcessors.remove(postProcessor); + keyEventPostProcessors.remove (postProcessor); } - protected List getKeyEventPostProcessors() + protected List getKeyEventPostProcessors () { - return (List) keyEventPostProcessors.clone(); + return (List) keyEventPostProcessors.clone (); } - public abstract boolean dispatchEvent(AWTEvent e); + public abstract boolean dispatchEvent (AWTEvent e); - public final void redispatchEvent(Component target, AWTEvent e) + public final void redispatchEvent (Component target, AWTEvent e) { - throw new Error("not implemented"); + e.setSource (target); + dispatchEvent (e); } - public abstract boolean dispatchKeyEvent(KeyEvent e); + public abstract boolean dispatchKeyEvent (KeyEvent e); - public abstract boolean postProcessKeyEvent(KeyEvent e); + public abstract boolean postProcessKeyEvent (KeyEvent e); - public abstract void processKeyEvent(Component focused, KeyEvent e); + public abstract void processKeyEvent (Component focused, KeyEvent e); - protected abstract void enqueueKeyEvents(long after, Component untilFocused); + protected abstract void enqueueKeyEvents (long after, Component untilFocused); - protected abstract void dequeueKeyEvents(long after, Component untilFocused); + protected abstract void dequeueKeyEvents (long after, Component untilFocused); - protected abstract void discardKeyEvents(Component comp); + protected abstract void discardKeyEvents (Component comp); - public abstract void focusNextComponent(Component comp); + public abstract void focusNextComponent (Component comp); - public abstract void focusPreviousComponent(Component comp); + public abstract void focusPreviousComponent (Component comp); - public abstract void upFocusCycle(Component comp); + public abstract void upFocusCycle (Component comp); - public abstract void downFocusCycle(Container cont); + public abstract void downFocusCycle (Container cont); - public final void focusNextComponent() + public final void focusNextComponent () { - focusNextComponent(focusOwner); + focusNextComponent (null); } - public final void focusPreviousComponent() + public final void focusPreviousComponent () { - focusPreviousComponent(focusOwner); + focusPreviousComponent (null); } - public final void upFocusCycle() + public final void upFocusCycle () { - upFocusCycle(focusOwner); + upFocusCycle (null); } - public final void downFocusCycle() + public final void downFocusCycle () { + Component focusOwner = getGlobalFocusOwner (); if (focusOwner instanceof Container - && ((Container) focusOwner).isFocusCycleRoot()) - downFocusCycle((Container) focusOwner); + && ((Container) focusOwner).isFocusCycleRoot ()) + downFocusCycle ((Container) focusOwner); + } + + /** + * Retrieve an object from one of the global object {@link + * java.util.Map}s, if the object was set by the a thread in the + * current {@link java.lang.ThreadGroup}. Otherwise, return null. + * + * @param globalMap one of the global object Maps + * + * @return a global object set by the current ThreadGroup, or null + * + * @see getFocusOwner + * @see getPermanentFocusOwner + * @see getFocusedWindow + * @see getActiveWindow + * @see getCurrentFocusCycleRoot + */ + private Object getObject (Map globalMap) + { + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + return globalMap.get (currentGroup); + } + + /** + * Retrieve an object from one of the global object {@link + * java.util.Map}s, regardless of whether or not the object was set + * by a thread in the current {@link java.lang.ThreadGroup}. + * + * @param globalMap one of the global object Maps + * + * @return a global object set by the current ThreadGroup, or null + * + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + * + * @see getGlobalFocusOwner + * @see getGlobalPermanentFocusOwner + * @see getGlobalFocusedWindow + * @see getGlobalActiveWindow + * @see getGlobalCurrentFocusCycleRoot + */ + private Object getGlobalObject (Map globalMap) + { + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + KeyboardFocusManager managerForCallingThread + = (KeyboardFocusManager) currentKeyboardFocusManagers.get (currentGroup); + + if (this != managerForCallingThread) + throw new SecurityException ("Attempted to retrieve an object from a " + + "keyboard focus manager that isn't " + + "associated with the current thread group."); + + synchronized (globalMap) + { + Collection globalObjects = globalMap.values (); + Iterator i = globalObjects.iterator (); + Component globalObject; + + while (i.hasNext ()) + { + globalObject = (Component) i.next (); + if (globalObject != null) + return globalObject; + } + } + + // No Object was found. + return null; + } + + /** + * Set an object in one of the global object {@link java.util.Map}s, + * that will be returned by subsequent calls to getGlobalObject on + * the same {@link java.util.Map}. + * + * @param globalMap one of the global object Maps + * @param newObject the object to set + * @param property the property that will change + * + * @see setGlobalFocusOwner + * @see setGlobalPermanentFocusOwner + * @see setGlobalFocusedWindow + * @see setGlobalActiveWindow + * @see setGlobalCurrentFocusCycleRoot + */ + private void setGlobalObject (Map globalMap, + Object newObject, + String property) + { + synchronized (globalMap) + { + // Save old object. + Object oldObject = getGlobalObject (globalMap); + + // Nullify old object. + Collection threadGroups = globalMap.keySet (); + Iterator i = threadGroups.iterator (); + while (i.hasNext ()) + { + ThreadGroup oldThreadGroup = (ThreadGroup) i.next (); + if (globalMap.get (oldThreadGroup) != null) + { + globalMap.put (oldThreadGroup, null); + // There should only be one object set at a time, so + // we can short circuit. + break; + } + } + + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + firePropertyChange (property, oldObject, newObject); + try + { + fireVetoableChange (property, oldObject, newObject); + // Set new object. + globalMap.put (currentGroup, newObject); + } + catch (PropertyVetoException e) + { + } + } } -} // class KeyboardFocusManager +} |