Index: gnu/java/awt/peer/gtk/GtkComponentPeer.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/gnu/java/awt/peer/gtk/GtkComponentPeer.java,v retrieving revision 1.16.2.3 diff -u -r1.16.2.3 GtkComponentPeer.java --- gnu/java/awt/peer/gtk/GtkComponentPeer.java 10 Mar 2004 15:48:01 -0000 1.16.2.3 +++ gnu/java/awt/peer/gtk/GtkComponentPeer.java 22 Apr 2004 16:01:10 -0000 @@ -54,12 +54,14 @@ import java.awt.Image; import java.awt.Insets; import java.awt.ItemSelectable; +import java.awt.KeyboardFocusManager; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.FocusEvent; import java.awt.event.ItemEvent; +import java.awt.event.FocusEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.PaintEvent; @@ -91,6 +93,9 @@ native void gtkWidgetSetCursor (int type); native void gtkWidgetSetBackground (int red, int green, int blue); native void gtkWidgetSetForeground (int red, int green, int blue); + native void gtkWidgetRequestFocus (); + native void gtkWidgetDispatchKeyEvent (int id, long when, int mods, + int keyCode, char keyChar, int keyLocation); native void gtkSetFont (String name, int style, int size); native void gtkWidgetQueueDrawArea(int x, int y, int width, int height); native void addExposeFilter(); @@ -222,6 +227,7 @@ public void handleEvent (AWTEvent event) { int id = event.getID(); + KeyEvent ke = null; switch (id) { @@ -251,6 +257,16 @@ } } break; + case KeyEvent.KEY_PRESSED: + ke = (KeyEvent) event; + gtkWidgetDispatchKeyEvent (ke.getID (), ke.getWhen (), ke.getModifiers (), + ke.getKeyCode (), ke.getKeyChar (), ke.getKeyLocation ()); + break; + case KeyEvent.KEY_RELEASED: + ke = (KeyEvent) event; + gtkWidgetDispatchKeyEvent (ke.getID (), ke.getWhen (), ke.getModifiers (), + ke.getKeyCode (), ke.getKeyChar (), ke.getKeyLocation ()); + break; } } @@ -335,7 +351,12 @@ new Rectangle (x, y, width, height))); } - native public void requestFocus (); + public void requestFocus () + { + System.out.println ("In GtkComponentPeer.requestFocus"); + gtkWidgetRequestFocus (); + postFocusEvent (FocusEvent.FOCUS_GAINED, false); + } public void reshape (int x, int y, int width, int height) { @@ -456,11 +477,12 @@ int keyCode, char keyChar, int keyLocation) { q.postEvent (new KeyEvent (awtComponent, id, when, mods, - keyCode, keyChar, keyLocation)); + keyCode, keyChar, keyLocation)); } protected void postFocusEvent (int id, boolean temporary) { + System.out.println ("GtkComponentPeer.postFocusEvent: " + awtComponent); q.postEvent (new FocusEvent (awtComponent, id, temporary)); } Index: java/awt/Component.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/awt/Component.java,v retrieving revision 1.37.2.7 diff -u -r1.37.2.7 Component.java --- java/awt/Component.java 2 Apr 2004 15:52:54 -0000 1.37.2.7 +++ java/awt/Component.java 22 Apr 2004 16:01:10 -0000 @@ -382,18 +382,18 @@ boolean focusable = true; /** - * Tracks whether this component uses default focus traversal, or has a - * different policy. + * Tracks whether this component's address@hidden #isFocusTraversable} + * method has been overridden. * - * @see #isFocusTraversableOverridden() * @since 1.4 */ int isFocusTraversableOverridden; /** - * The focus traversal keys, if not inherited from the parent or default - * keyboard manager. These sets will contain only AWTKeyStrokes that - * represent press and release events to use as focus control. + * The focus traversal keys, if not inherited from the parent or + * default keyboard focus manager. These sets will contain only + * AWTKeyStrokes that represent press and release events to use as + * focus control. * * @see #getFocusTraversalKeys(int) * @see #setFocusTraversalKeys(int, Set) @@ -556,6 +556,12 @@ transient BufferStrategy bufferStrategy; /** + * true if requestFocus was called on this component when its + * top-level ancestor was not focusable. + */ + private transient FocusEvent pendingFocusRequest = null; + + /** * The system properties that affect image updating. */ private static transient boolean incrementalDraw; @@ -2962,6 +2968,7 @@ { if (focusListener == null) return; + switch (e.id) { case FocusEvent.FOCUS_GAINED: @@ -3410,13 +3417,18 @@ } /** - * Sets the focus traversal keys for a given type of focus events. Normally, - * the default values should match the operating system's native choices. To - * disable a given traversal, use Collections.EMPTY_SET. The - * event dispatcher will consume PRESSED, RELEASED, and TYPED events for the - * specified key, although focus can only transfer on PRESSED or RELEASED. + * Sets the focus traversal keys for one of the three focus + * traversal directions supported by Components: address@hidden + * #KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS}, address@hidden + * #KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS}, or address@hidden + * #KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS}. Normally, the + * default values should match the operating system's native + * choices. To disable a given traversal, use + * Collections.EMPTY_SET. The event dispatcher will + * consume PRESSED, RELEASED, and TYPED events for the specified + * key, although focus can only transfer on PRESSED or RELEASED. * - *

The defauts are: + *

The defaults are: * * * @@ -3429,10 +3441,13 @@ * *
IdentifierMeaningDefault
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYSGo up a traversal cycleNone
* - *

Specifying null allows inheritance from the parent, or from the current - * KeyboardFocusManager default set. If not null, the set must contain only - * AWTKeyStrokes that are not already focus keys and are not KEY_TYPED - * events. + * If keystrokes is null, this component's focus traversal key set + * is inherited from one of its ancestors. If none of its ancestors + * has its own set of focus traversal keys, the focus traversal keys + * are set to the defaults retrieved from the current + * KeyboardFocusManager. If not null, the set must contain only + * AWTKeyStrokes that are not already focus keys and are not + * KEY_TYPED events. * * @param id one of FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, or * UP_CYCLE_TRAVERSAL_KEYS @@ -3447,7 +3462,24 @@ public void setFocusTraversalKeys(int id, Set keystrokes) { if (keystrokes == null) - throw new IllegalArgumentException(); + { + Container parent = getParent (); + + while (parent != null) + { + if (parent.areFocusTraversalKeysSet (id)) + { + keystrokes = parent.getFocusTraversalKeys (id); + break; + } + parent = parent.getParent (); + } + + if (keystrokes == null) + keystrokes = KeyboardFocusManager.getCurrentKeyboardFocusManager (). + getDefaultFocusTraversalKeys (id); + } + Set sa; Set sb; String name; @@ -3475,50 +3507,60 @@ name = "upCycleFocusTraversalKeys"; 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) + Object o = iter.next (); + if (!(o instanceof AWTKeyStroke) + || sa.contains (o) || sb.contains (o) || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED) - throw new IllegalArgumentException(); + throw new IllegalArgumentException (); } + if (focusTraversalKeys == null) focusTraversalKeys = new Set[3]; - keystrokes = Collections.unmodifiableSet(new HashSet(keystrokes)); - firePropertyChange(name, focusTraversalKeys[id], keystrokes); + + keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes)); + firePropertyChange (name, focusTraversalKeys[id], keystrokes); + focusTraversalKeys[id] = keystrokes; } /** - * Returns the set of keys for a given focus traversal action, as defined - * in setFocusTraversalKeys. If not set, this is inherited from - * the parent component, which may have gotten it from the - * KeyboardFocusManager. + * Returns the set of keys for a given focus traversal action, as + * defined in setFocusTraversalKeys. If not set, this + * is inherited from the parent component, which may have gotten it + * from the KeyboardFocusManager. * - * @param id one of FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, or - * UP_CYCLE_TRAVERSAL_KEYS + * @param id one of FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, + * or UP_CYCLE_TRAVERSAL_KEYS * @throws IllegalArgumentException if id is invalid - * @see #setFocusTraversalKeys(int, Set) + * @see #setFocusTraversalKeys (int, Set) * @see KeyboardFocusManager#FORWARD_TRAVERSAL_KEYS * @see KeyboardFocusManager#BACKWARD_TRAVERSAL_KEYS * @see KeyboardFocusManager#UP_CYCLE_TRAVERSAL_KEYS * @since 1.4 */ - public Set getFocusTraversalKeys(int id) + public Set getFocusTraversalKeys (int id) { - if (id < KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS - || id > KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS) + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS) throw new IllegalArgumentException(); + Set s = null; + if (focusTraversalKeys != null) s = focusTraversalKeys[id]; + if (s == null && parent != null) - s = parent.getFocusTraversalKeys(id); + s = parent.getFocusTraversalKeys (id); + return s == null ? (KeyboardFocusManager.getCurrentKeyboardFocusManager() .getDefaultFocusTraversalKeys(id)) : s; } @@ -3527,269 +3569,405 @@ * Tests whether the focus traversal keys for a given action are explicitly * set or inherited. * - * @param id one of FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, or - * UP_CYCLE_TRAVERSAL_KEYS + * @param id one of FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, + * or UP_CYCLE_TRAVERSAL_KEYS * @return true if that set is explicitly specified * @throws IllegalArgumentException if id is invalid - * @see #getFocusTraversalKeys(int) + * @see #getFocusTraversalKeys (int) * @see KeyboardFocusManager#FORWARD_TRAVERSAL_KEYS * @see KeyboardFocusManager#BACKWARD_TRAVERSAL_KEYS * @see KeyboardFocusManager#UP_CYCLE_TRAVERSAL_KEYS * @since 1.4 */ - public boolean areFocusTraversalKeysSet(int id) + public boolean areFocusTraversalKeysSet (int id) { - if (id < KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS - || id > KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS) - throw new IllegalArgumentException(); + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + return focusTraversalKeys != null && focusTraversalKeys[id] != null; } /** - * Sets whether focus traversal keys are enabled, which consumes traversal - * keys and performs the focus event automatically. + * Enable or disable focus traversal keys on this Component. If + * they are, then the keyboard focus manager consumes and acts on + * key press and release events that trigger focus traversal, and + * discards the corresponding key typed events. If focus traversal + * keys are disabled, then all key events that would otherwise + * trigger focus traversal are sent to this Component. * * @param focusTraversalKeysEnabled the new value of the flag - * @see #getFocusTraversalKeysEnabled() - * @see #setFocusTraversalKeys(int, Set) - * @see #getFocusTraversalKeys(int) + * @see #getFocusTraversalKeysEnabled () + * @see #setFocusTraversalKeys (int, Set) + * @see #getFocusTraversalKeys (int) * @since 1.4 */ - public void setFocusTraversalKeysEnabled(boolean focusTraversalKeysEnabled) + public void setFocusTraversalKeysEnabled (boolean focusTraversalKeysEnabled) { - firePropertyChange("focusTraversalKeysEnabled", - this.focusTraversalKeysEnabled, - focusTraversalKeysEnabled); + firePropertyChange ("focusTraversalKeysEnabled", + this.focusTraversalKeysEnabled, + focusTraversalKeysEnabled); this.focusTraversalKeysEnabled = focusTraversalKeysEnabled; } /** - * Tests whether focus traversal keys are enabled. If they are, then focus - * traversal keys are consumed and focus events performed automatically, - * without the component seeing the keystrokes. - * - * @return true if focus traversal is enabled - * @see #setFocusTraversalKeysEnabled(boolean) - * @see #setFocusTraversalKeys(int, Set) - * @see #getFocusTraversalKeys(int) + * Check whether or not focus traversal keys are enabled on this + * Component. If they are, then the keyboard focus manager consumes + * and acts on key press and release events that trigger focus + * traversal, and discards the corresponding key typed events. If + * focus traversal keys are disabled, then all key events that would + * otherwise trigger focus traversal are sent to this Component. + * + * @return true if focus traversal keys are enabled + * @see #setFocusTraversalKeysEnabled (boolean) + * @see #setFocusTraversalKeys (int, Set) + * @see #getFocusTraversalKeys (int) * @since 1.4 */ - public boolean getFocusTraversalKeysEnabled() + public boolean getFocusTraversalKeysEnabled () { return focusTraversalKeysEnabled; } /** - * Requests that this component be given focus. A FOCUS_GAINED - * event will be fired if and only if this request is successful. To be - * successful, the component must be displayable, visible, and focusable, - * and the top-level Window must be able to receive focus. Thus, this - * request may fail, or be delayed until the window receives focus. It is - * recommended that requestFocusInWindow be used where - * possible to be more platform-independent. + * Request that this Component be given the keyboard input focus and + * that its top-level ancestor become the focused Window. + * + * For the request to be granted, the Component must be focusable, + * displayable and visible and the top-level Window to which it + * belongs must be focusable. If the request is initially denied on + * the basis that the top-level Window is not focusable, the request + * will be remembered and granted when the Window does become + * focused. + * + * Never assume that this Component is the focus owner until it + * receives a FOCUS_GAINED event. * - * @see #requestFocusInWindow() + * The behaviour of this method is platform-dependent. + * address@hidden #requestFocusInWindow} should be used instead. + * + * @see #requestFocusInWindow () * @see FocusEvent - * @see #addFocusListener(FocusListener) - * @see #isFocusable() - * @see #isDisplayable() - * @see KeyboardFocusManager#clearGlobalFocusOwner() - */ - public void requestFocus() - { - // If there's no peer then this component can't get the focus. We - // treat it as a silent rejection of the request. - if (peer != null) - peer.requestFocus(); + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () + */ + public void requestFocus () + { + if (isDisplayable () + && isVisible () + && isFocusable ()) + { + // FIXME: need to get the tree lock here. + // Find this Component's top-level ancestor. + Container parent = getParent (); + + while (parent != null + && !(parent instanceof Window)) + parent = parent.getParent (); + + Window toplevel = (Window) parent; + if (toplevel.isFocusableWindow ()) + { + if (peer != null) + // This call will cause a FOCUS_GAINED event to be + // posted to the system event queue if the native + // windowing system grants the focus request. + peer.requestFocus (); + else + { + // Either our peer hasn't been created yet or we're a + // lightweight component. In either case we want to + // post a FOCUS_GAINED event. + EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED)); + } + } + else + pendingFocusRequest = new FocusEvent(this, FocusEvent.FOCUS_GAINED); + } } /** - * Requests that this component be given focus. A FOCUS_GAINED - * event will be fired if and only if this request is successful. To be - * successful, the component must be displayable, visible, and focusable, - * and the top-level Window must be able to receive focus. Thus, this - * request may fail, or be delayed until the window receives focus. It is - * recommended that requestFocusInWindow be used where - * possible to be more platform-independent. - * - *

If the return value is false, the request is guaranteed to fail. If - * it is true, it will likely succeed unless the action is vetoed or - * something in the native windowing system intervenes. The temporary flag, - * and thus this method in general, is not designed for public use; rather - * it is a hook for lightweight components to notify their container in - * an attempt to reduce the amount of repainting necessary. + * Request that this Component be given the keyboard input focus and + * that its top-level ancestor become the focused Window. + * + * For the request to be granted, the Component must be focusable, + * displayable and visible and the top-level Window to which it + * belongs must be focusable. If the request is initially denied on + * the basis that the top-level Window is not focusable, the request + * will be remembered and granted when the Window does become + * focused. + * + * Never assume that this Component is the focus owner until it + * receives a FOCUS_GAINED event. + * + * The behaviour of this method is platform-dependent. + * address@hidden #requestFocusInWindow} should be used instead. + * + * If the return value is false, the request is guaranteed to fail. + * If the return value is true, the request will succeed unless it + * is vetoed or something in the native windowing system intervenes, + * preventing this Component's top-level ancestor from becoming + * focused. This method is meant to be called by derived + * lightweight Components that want to avoid unnecessary repainting + * when they know a given focus transfer need only be temporary. * * @param temporary true if the focus request is temporary * @return true if the request has a chance of success - * @see #requestFocusInWindow() + * @see #requestFocusInWindow () * @see FocusEvent - * @see #addFocusListener(FocusListener) - * @see #isFocusable() - * @see #isDisplayable() - * @see KeyboardFocusManager#clearGlobalFocusOwner() + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () * @since 1.4 */ - protected boolean requestFocus(boolean temporary) + protected boolean requestFocus (boolean temporary) { - // XXX Implement correctly. - requestFocus(); + if (isDisplayable () + && isVisible () + && isFocusable ()) + { + // FIXME: need to get the tree lock here. + // Find this Component's top-level ancestor. + Container parent = getParent (); + + while (parent != null + && !(parent instanceof Window)) + parent = parent.getParent (); + + Window toplevel = (Window) parent; + if (toplevel.isFocusableWindow ()) + { + if (peer != null) + // This call will cause a FOCUS_GAINED event to be + // posted to the system event queue if the native + // windowing system grants the focus request. + peer.requestFocus (); + else + { + // Either our peer hasn't been created yet or we're a + // lightweight component. In either case we want to + // post a FOCUS_GAINED event. + EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary)); + } + } + else + // FIXME: need to add a focus listener to our top-level + // ancestor, so that we can post this event when it becomes + // the focused window. + pendingFocusRequest = new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary); + } + // Always return true. return true; } /** - * Requests that this component be given focus, if it resides in the - * top-level window which already has focus. A FOCUS_GAINED - * event will be fired if and only if this request is successful. To be - * successful, the component must be displayable, visible, and focusable, - * and the top-level Window must be focused. - * - *

If the return value is false, the request is guaranteed to fail. If - * it is true, it will likely succeed unless the action is vetoed or - * something in the native windowing system intervenes. The temporary flag, - * and thus this method in general, is not designed for public use; rather - * it is a hook for lightweight components to notify their container in - * an attempt to reduce the amount of repainting necessary. + * Request that this component be given the keyboard input focus, if + * its top-level ancestor is the currently focused Window. A + * FOCUS_GAINED event will be fired if and only if this + * request is successful. To be successful, the component must be + * displayable, visible, and focusable, and its ancestor top-level + * Window must be focused. + * + * If the return value is false, the request is guaranteed to fail. + * If the return value is true, the request will succeed unless it + * is vetoed or something in the native windowing system intervenes, + * preventing this Component's top-level ancestor from becoming + * focused. * * @return true if the request has a chance of success - * @see #requestFocus() + * @see #requestFocus () * @see FocusEvent - * @see #addFocusListener(FocusListener) - * @see #isFocusable() - * @see #isDisplayable() - * @see KeyboardFocusManager#clearGlobalFocusOwner() + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () * @since 1.4 */ - public boolean requestFocusInWindow() + public boolean requestFocusInWindow () { - // XXX Implement correctly. - requestFocus(); - return true; + return requestFocusInWindow (false); } /** - * Requests that this component be given focus, if it resides in the - * top-level window which already has focus. A FOCUS_GAINED - * event will be fired if and only if this request is successful. To be - * successful, the component must be displayable, visible, and focusable, - * and the top-level Window must be focused. - * - *

If the return value is false, the request is guaranteed to fail. If - * it is true, it will likely succeed unless the action is vetoed or - * something in the native windowing system intervenes. The temporary flag, - * and thus this method in general, is not designed for public use; rather - * it is a hook for lightweight components to notify their container in - * an attempt to reduce the amount of repainting necessary. + * Request that this component be given the keyboard input focus, if + * its top-level ancestor is the currently focused Window. A + * FOCUS_GAINED event will be fired if and only if this + * request is successful. To be successful, the component must be + * displayable, visible, and focusable, and its ancestor top-level + * Window must be focused. + * + * If the return value is false, the request is guaranteed to fail. + * If the return value is true, the request will succeed unless it + * is vetoed or something in the native windowing system intervenes, + * preventing this Component's top-level ancestor from becoming + * focused. This method is meant to be called by derived + * lightweight Components that want to avoid unnecessary repainting + * when they know a given focus transfer need only be temporary. * * @param temporary true if the focus request is temporary * @return true if the request has a chance of success - * @see #requestFocus() + * @see #requestFocus () * @see FocusEvent - * @see #addFocusListener(FocusListener) - * @see #isFocusable() - * @see #isDisplayable() - * @see KeyboardFocusManager#clearGlobalFocusOwner() + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () * @since 1.4 */ - protected boolean requestFocusInWindow(boolean temporary) + protected boolean requestFocusInWindow (boolean temporary) { - // XXX Implement correctly. - requestFocus(); + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + Window focusedWindow = manager.getFocusedWindow (); + + if (focusedWindow != null) + { + // FIXME: get tree lock here. + Container parent = getParent (); + + while (parent != null + && !(parent instanceof Window)) + parent = parent.getParent (); + + Window toplevel = (Window) parent; + + // Check if top-level ancestor is currently focused window. + if (focusedWindow == toplevel) + { + if (peer != null) + // This call will cause a FOCUS_GAINED event to be + // posted to the system event queue if the native + // windowing system grants the focus request. + peer.requestFocus (); + else + { + // Either our peer hasn't been created yet or we're a + // lightweight component. In either case we want to + // post a FOCUS_GAINED event. + EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary)); + } + } + else + return false; + } + return true; } /** - * Transfers focus to the next component in the focus traversal order, as - * though this were the current focus owner. + * Transfers focus to the next component in the focus traversal + * order, as though this were the current focus owner. * * @see #requestFocus() * @since 1.1 */ - public void transferFocus() + public void transferFocus () { - Component next; - if (parent == null) - next = findNextFocusComponent(null); - else - next = parent.findNextFocusComponent(this); - if (next != null && next != this) - next.requestFocus(); + nextFocus (); } /** - * Returns the root container that owns the focus cycle where this component - * resides. A focus cycle root is in two cycles, one as the ancestor, and - * one as the focusable element; this call always returns the ancestor. + * Returns the root container that owns the focus cycle where this + * component resides. A focus cycle root is in two cycles, one as + * the ancestor, and one as the focusable element; this call always + * returns the ancestor. * * @return the ancestor container that owns the focus cycle * @since 1.4 */ - public Container getFocusCycleRootAncestor() + public Container getFocusCycleRootAncestor () { - // XXX Implement. - throw new Error("not implemented"); + if (this instanceof Window + && ((Container) this).isFocusCycleRoot ()) + return (Container) this; + + Container parent = getParent (); + + while (parent != null + && !parent.isFocusCycleRoot ()) + parent = parent.getParent (); + + return parent; } /** - * Tests if the container is the ancestor of the focus cycle that this - * component belongs to. + * Tests if the container is the ancestor of the focus cycle that + * this component belongs to. * * @param c the container to test * @return true if c is the focus cycle root * @since 1.4 */ - public boolean isFocusCycleRoot(Container c) + public boolean isFocusCycleRoot (Container c) { - return c == getFocusCycleRootAncestor(); + return c == getFocusCycleRootAncestor (); } /** - * AWT 1.0 focus event processor. + * AWT 1.0 focus event processor. Transfers focus to the next + * component in the focus traversal order, as though this were the + * current focus owner. * - * @deprecated use address@hidden #transferFocus()} instead + * @deprecated use address@hidden #transferFocus ()} instead */ - public void nextFocus() + public void nextFocus () { - transferFocus(); + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + manager.focusNextComponent (this); } /** - * Transfers focus to the previous component in the focus traversal order, as - * though this were the current focus owner. + * Transfers focus to the previous component in the focus traversal + * order, as though this were the current focus owner. * - * @see #requestFocus() + * @see #requestFocus () * @since 1.4 */ - public void transferFocusBackward() + public void transferFocusBackward () { - // XXX Implement. - throw new Error("not implemented"); + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + manager.focusPreviousComponent (this); } /** - * Transfers focus to the focus cycle root of this component. However, if - * this is a Window, the default focus owner in the window in the current - * focus cycle is focused instead. + * Transfers focus to the focus cycle root of this component. + * However, if this is a Window, the default focus owner in the + * window in the current focus cycle is focused instead. * - * @see #requestFocus() - * @see #isFocusCycleRoot() + * @see #requestFocus () + * @see #isFocusCycleRoot () * @since 1.4 */ - public void transferFocusUpCycle() + public void transferFocusUpCycle () { - // XXX Implement. - throw new Error("not implemented"); + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + manager.upFocusCycle (this); } /** - * Tests if this component is the focus owner. Use address@hidden #isFocusOwner()} - * instead. + * Tests if this component is the focus owner. Use address@hidden + * #isFocusOwner ()} instead. * * @return true if this component owns focus * @since 1.2 */ - public boolean hasFocus() + public boolean hasFocus () { - return isFocusOwner(); + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + Component focusOwner = manager.getFocusOwner (); + + return this == focusOwner; } /** @@ -3800,8 +3978,7 @@ */ public boolean isFocusOwner() { - // XXX Implement. - throw new Error("not implemented"); + return hasFocus (); } /** @@ -4336,6 +4513,8 @@ * making the request. This is overridden by Container; when called for an * ordinary component there is no child and so we always return null. * + * FIXME: is this still needed, in light of focus traversal policies? + * * @param child the component making the request * @return the next component to focus on */ @@ -4896,23 +5075,23 @@ * Tests whether this component can accept focus. * * @return true if this is focus traversable - * @see #getAccessibleStateSet() + * @see #getAccessibleStateSet () * @see AccessibleState#FOCUSABLE * @see AccessibleState#FOCUSED */ - public boolean isFocusTraversable() + public boolean isFocusTraversable () { - return Component.this.isFocusTraversable(); + return Component.this.isFocusTraversable (); } /** * Requests focus for this component. * - * @see #isFocusTraversable() + * @see #isFocusTraversable () */ - public void requestFocus() + public void requestFocus () { - Component.this.requestFocus(); + Component.this.requestFocus (); } /** Index: java/awt/Container.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/awt/Container.java,v retrieving revision 1.34.2.6 diff -u -r1.34.2.6 Container.java --- java/awt/Container.java 10 Mar 2004 21:43:01 -0000 1.34.2.6 +++ java/awt/Container.java 22 Apr 2004 16:01:10 -0000 @@ -46,10 +46,16 @@ import java.awt.peer.LightweightPeer; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; +import java.io.ObjectInputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Serializable; +import java.util.Collections; import java.util.EventListener; +import java.util.Iterator; +import java.util.HashSet; import java.util.Set; import javax.accessibility.Accessible; import javax.swing.SwingUtilities; @@ -93,6 +99,21 @@ transient ContainerListener containerListener; transient PropertyChangeSupport changeSupport; + /** The focus traversal policy that determines how focus is + transferred between this Container and its children. */ + private FocusTraversalPolicy focusTraversalPolicy; + + /** + * The focus traversal keys, if not inherited from the parent or default + * keyboard manager. These sets will contain only AWTKeyStrokes that + * represent press and release events to use as focus control. + * + * @see #getFocusTraversalKeys(int) + * @see #setFocusTraversalKeys(int, Set) + * @since 1.4 + */ + transient Set[] focusTraversalKeys; + /** * Default constructor for subclasses. */ @@ -1068,9 +1089,89 @@ throw new IllegalArgumentException (); if (keystrokes == null) - throw new IllegalArgumentException (); + { + Container parent = getParent (); + + while (parent != null) + { + if (parent.areFocusTraversalKeysSet (id)) + { + keystrokes = parent.getFocusTraversalKeys (id); + break; + } + parent = parent.getParent (); + } + + if (keystrokes == null) + keystrokes = KeyboardFocusManager.getCurrentKeyboardFocusManager (). + getDefaultFocusTraversalKeys (id); + } + + Set sa; + Set sb; + Set sc; + String name; + switch (id) + { + case KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + name = "forwardFocusTraversalKeys"; + break; + case KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + name = "backwardFocusTraversalKeys"; + break; + case KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + name = "upCycleFocusTraversalKeys"; + break; + case KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + name = "downCycleFocusTraversalKeys"; + break; + default: + throw new IllegalArgumentException (); + } + + 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) + || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED) + throw new IllegalArgumentException (); + } + + if (focusTraversalKeys == null) + focusTraversalKeys = new Set[3]; + + keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes)); + firePropertyChange (name, focusTraversalKeys[id], keystrokes); - throw new Error ("not implemented"); + focusTraversalKeys[id] = keystrokes; } /** @@ -1085,7 +1186,7 @@ * * @since 1.4 */ - public Set getFocusTraversalKeys(int id) + public Set getFocusTraversalKeys (int id) { if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && @@ -1093,9 +1194,18 @@ id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) throw new IllegalArgumentException (); - return null; + Set s = null; + + if (focusTraversalKeys != null) + s = focusTraversalKeys[id]; + + if (s == null && parent != null) + s = parent.getFocusTraversalKeys (id); + + return s == null ? (KeyboardFocusManager.getCurrentKeyboardFocusManager() + .getDefaultFocusTraversalKeys(id)) : s; } - + /** * Returns whether the Set of focus traversal keys for the given focus * traversal operation has been explicitly defined for this Container. @@ -1110,7 +1220,7 @@ * * @since 1.4 */ - public boolean areFocusTraversalKeysSet(int id) + public boolean areFocusTraversalKeysSet (int id) { if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && @@ -1118,43 +1228,148 @@ id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) throw new IllegalArgumentException (); - return false; + return focusTraversalKeys != null && focusTraversalKeys[id] != null; } - - public boolean isFocusCycleRoot(Container c) + + /** + * Check whether the given Container is the focus cycle root of this + * Container's focus traversal cycle. If this Container is a focus + * cycle root itself, then it will be in two different focus cycles + * -- it's own, and that of its ancestor focus cycle root's. In + * that case, if c is either of those containers, this + * method will return true. + * + * @param c the candidate Container + * + * @return true if c is the focus cycle root of the focus traversal + * cycle to which this Container belongs, false otherwise + * + * @since 1.4 + */ + public boolean isFocusCycleRoot (Container c) { + if (this == c + && isFocusCycleRoot ()) + return true; + + Container ancestor = getFocusCycleRootAncestor (); + + if (c == ancestor) + return true; + return false; } - - public void transferFocusBackward() - { - } - - public void setFocusTraversalPolicy(FocusTraversalPolicy policy) + + /** + * If this Container is a focus cycle root, set the focus traversal + * policy that determines the focus traversal order for its + * children. If non-null, this policy will be inherited by all + * inferior focus cycle roots. If policy is null, this + * Container will inherit its policy from the closest ancestor focus + * cycle root that's had its policy set. + * + * @param policy the new focus traversal policy for this Container or null + * + * @since 1.4 + */ + public void setFocusTraversalPolicy (FocusTraversalPolicy policy) { + focusTraversalPolicy = policy; } - - public FocusTraversalPolicy getFocusTraversalPolicy() + + /** + * Return the focus traversal policy that determines the focus + * traversal order for this Container's children. This method + * returns null if this Container is not a focus cycle root. If the + * focus traversal policy has not been set explicitly, then this + * method will return an ancestor focus cycle root's policy instead. + * + * @return this Container's focus traversal policy or null + * + * @since 1.4 + */ + public FocusTraversalPolicy getFocusTraversalPolicy () { - return null; + if (!isFocusCycleRoot ()) + return null; + + if (focusTraversalPolicy == null) + { + Container ancestor = getFocusCycleRootAncestor (); + + if (ancestor != this) + return ancestor.getFocusTraversalPolicy (); + else + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + return manager.getDefaultFocusTraversalPolicy (); + } + } + else + return focusTraversalPolicy; } - - public boolean isFocusTraversalPolicySet() + + /** + * Check whether this Container's focus traversal policy has been + * explicitly set. If it has not, then this Container will inherit + * its focus traversal policy from one of its ancestor focus cycle + * roots. + * + * @return true if focus traversal policy is set, false otherwise + */ + public boolean isFocusTraversalPolicySet () { - return false; + return focusTraversalPolicy == null; } - - public void setFocusCycleRoot(boolean focusCycleRoot) + + /** + * Set whether or not this Container is the root of a focus + * traversal cycle. This Container's focus traversal policy + * determines the order of focus traversal. Some policies prevent + * the focus from being transferred between two traversal cycles + * until an up or down traversal operation is performed. In that + * case, normal traversal (not up or down) is limited to this + * Container and all of this Container's descendents that are not + * descendents of inferior focus cycle roots. In the default case + * however, ContainerOrderFocusTraversalPolicy is in effect, and it + * supports implicit down-cycle traversal operations. + * + * @return true if this is a focus cycle root, false otherwise + * + * @since 1.4 + */ + public void setFocusCycleRoot (boolean focusCycleRoot) { + this.focusCycleRoot = focusCycleRoot; } - - public boolean isFocusCycleRoot() + + /** + * Check whether this Container is a focus cycle root. + * + * @return true if this is a focus cycle root, false otherwise + * + * @since 1.4 + */ + public boolean isFocusCycleRoot () { - return false; + return focusCycleRoot; } - - public void transferFocusDownCycle() + + /** + * Transfer focus down one focus traversal cycle. If this Container + * is a focus cycle root, then its default component becomes the + * focus owner, and this Container becomes the current focus cycle + * root. No traversal will occur if this Container is not a focus + * cycle root. + * + * @since 1.4 + */ + public void transferFocusDownCycle () { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + manager.downFocusCycle (this); } /** @@ -1369,6 +1584,61 @@ } } } + } + + /** + * Deserialize this Container: + *

    + *
  1. Read from the stream the default serializable fields.
  2. + *
  3. Read a list of serializable ContainerListeners as optional + * data. If the list is null, no listeners will be registered.
  4. + *
  5. Read this Container's FocusTraversalPolicy as optional data. + * If this is null, then this Container will use a + * DefaultFocusTraversalPolicy.
  6. + *
+ * + * @param s the stream to read from + * @throws ClassNotFoundException if deserialization fails + * @throws IOException if the stream fails + */ + private void readObject (ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject (); + String key = (String) s.readObject (); + while (key != null) + { + Object object = s.readObject (); + if ("containerL".equals (key)) + addContainerListener((ContainerListener) object); + // FIXME: under what key is the focus traversal policy stored? + else if ("focusTraversalPolicy".equals (key)) + setFocusTraversalPolicy ((FocusTraversalPolicy) object); + + key = (String) s.readObject(); + } + } + + /** + * Serialize this Container: + *
    + *
  1. Write to the stream the default serializable fields.
  2. + *
  3. Write the list of serializable ContainerListeners as optional + * data.
  4. + *
  5. Write this Container's FocusTraversalPolicy as optional data.
  6. + *
+ * + * @param s the stream to write to + * @throws IOException if the stream fails + */ + private void writeObject (ObjectOutputStream s) throws IOException + { + s.defaultWriteObject (); + AWTEventMulticaster.save (s, "containerL", containerListener); + if (focusTraversalPolicy instanceof Serializable) + s.writeObject (focusTraversalPolicy); + else + s.writeObject (null); } // Nested classes. Index: java/awt/ContainerOrderFocusTraversalPolicy.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/awt/ContainerOrderFocusTraversalPolicy.java,v retrieving revision 1.5 diff -u -r1.5 ContainerOrderFocusTraversalPolicy.java --- java/awt/ContainerOrderFocusTraversalPolicy.java 24 Mar 2003 13:50:32 -0000 1.5 +++ java/awt/ContainerOrderFocusTraversalPolicy.java 22 Apr 2004 16:01:10 -0000 @@ -41,7 +41,23 @@ import java.io.Serializable; /** + * ContainerOrderFocusTraversalPolicy defines a focus traversal order + * based on the order in which Components were packed in a Container. + * This policy performs a pre-order traversal of the Component + * hierarchy starting from a given focus cycle root. Portions of the + * hierarchy that are not visible and displayable are skipped. + * + * By default, this policy transfers focus down-cycle implicitly. + * That is, if a forward traversal is requested on a focus cycle root + * and the focus cycle root has focusable children, the focus will + * automatically be transfered down to the lower focus cycle. + * + * The default implementation of accept accepts only Components that + * are visible, displayable, enabled and focusable. Derived classes + * can override these acceptance criteria by overriding accept. + * * @author Michael Koch + * @author Thomas Fitzsimmons * @since 1.4 */ public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy @@ -52,12 +68,15 @@ */ static final long serialVersionUID = 486933713763926351L; + /** + * True if implicit down cycling is enabled. + */ private boolean implicitDownCycleTraversal = true; /** * Creates the ContainerOrderFocusTraversalPolicy object. */ - public ContainerOrderFocusTraversalPolicy() + public ContainerOrderFocusTraversalPolicy () { // Nothing to do here } @@ -66,37 +85,193 @@ * Returns the Component that should receive the focus after current. * root must be a focus cycle root of current. * + * @param root a focus cycle root of current + * @param current a (possibly indirect) child of root, or root itself + * + * @return the next Component in the focus traversal order for root, + * or null if no acceptable Component exists. + * * @exception IllegalArgumentException If root is not a focus cycle * root of current, or if either root or current is null. */ - public Component getComponentAfter(Container root, Component current) + public Component getComponentAfter (Container root, Component current) { - if (root == null - || current == null) - throw new IllegalArgumentException (); - + if (root == null) + throw new IllegalArgumentException ("focus cycle root is null"); + if (current == null) + throw new IllegalArgumentException ("current component is null"); + + // FIXME: is this the right thing to do here? it move the context + // for traversal up one focus traversal cycle. + if ((Component) root == current) + root = current.getFocusCycleRootAncestor (); + + Container ancestor = current.getFocusCycleRootAncestor (); + Container prevAncestor = ancestor; + while (ancestor != root) + { + ancestor = current.getFocusCycleRootAncestor (); + if (ancestor == prevAncestor) + { + // We've reached the top focus cycle root ancestor. Check + // if it is root. + if (ancestor != root) + throw new IllegalArgumentException ("the given container is not" + + " a focus cycle root of the" + + " current component"); + else + break; + } + prevAncestor = ancestor; + } + + // FIXME: need locking on root. + Component[] components = root.getComponents (); + int componentIndex = 0; + int numComponents = root.getComponentCount (); + + // Find component's index. + for (int i = 0; i < numComponents; i++) + { + if (components[i] == current) + componentIndex = i; + } + + // Search forward for the next acceptable component. + for (int i = componentIndex + 1; i < numComponents; i++) + { + if (components[i] instanceof Container) + { + Component result = getFirstComponent ((Container) components[i]); + + if (result != null + && implicitDownCycleTraversal) + return result; + } + + if (accept (components[i])) + return components[i]; + } + + for (int i = 0; i < componentIndex; i++) + { + if (components[i] instanceof Container) + { + Component result = getFirstComponent ((Container) components[i]); + + if (result != null + && implicitDownCycleTraversal) + return result; + } + + // FIXME: see below. + if (accept (components[i])) + return components[i]; + } + + // No acceptable component found. return null; } /** - * Returns the Component that should receive the focus before current. - * root must be a focus cycle root of current. + * Returns the Component that should receive the focus before + * current. root must be a focus cycle + * root of current. + * + * @param root a focus cycle root of current + * @param current a (possibly indirect) child of root, or root itself + * + * @return the previous Component in the focus traversal order for + * root, or null if no acceptable Component exists. * * @exception IllegalArgumentException If root is not a focus cycle * root of current, or if either root or current is null. */ - public Component getComponentBefore(Container root, Component current) + public Component getComponentBefore (Container root, Component current) { - if (root == null - || current == null) - throw new IllegalArgumentException (); + if (root == null) + throw new IllegalArgumentException ("focus cycle root is null"); + if (current == null) + throw new IllegalArgumentException ("current component is null"); + + // FIXME: is this the right thing to do here? it move the context + // for traversal up one focus traversal cycle. + if ((Component) root == current) + root = current.getFocusCycleRootAncestor (); + + Container ancestor = current.getFocusCycleRootAncestor (); + Container prevAncestor = ancestor; + while (ancestor != root) + { + ancestor = current.getFocusCycleRootAncestor (); + if (ancestor == prevAncestor) + { + // We've reached the top focus cycle root ancestor. Check + // if it is root. + if (ancestor != root) + throw new IllegalArgumentException ("the given container is not" + + " a focus cycle root of the" + + " current component"); + else + break; + } + prevAncestor = ancestor; + } + + // FIXME: need locking on root. + Component[] components = root.getComponents (); + int componentIndex = 0; + int numComponents = root.getComponentCount (); + + // Find component's index. + for (int i = 0; i < numComponents; i++) + { + if (components[i] == current) + componentIndex = i; + } + // Search backward for the next acceptable component. + for (int i = componentIndex - 1; i >= 0; i--) + { + if (components[i] instanceof Container) + { + Component result = getLastComponent ((Container) components[i]); + + if (result != null) + return result; + } + + if (accept (components[i])) + return components[i]; + } + + for (int i = numComponents - 1; i > componentIndex; i--) + { + if (components[i] instanceof Container) + { + Component result = getLastComponent ((Container) components[i]); + + if (result != null) + return result; + } + + // FIXME: see below. + if (accept (components[i])) + return components[i]; + } + + // No acceptable component found. return null; } /** * Returns the first Component of root that should receive the focus. * + * @param root a focus cycle root + * + * @return the first Component in the focus traversal order for + * root, or null if no acceptable Component exists. + * * @exception IllegalArgumentException If root is null. */ public Component getFirstComponent(Container root) @@ -119,16 +294,17 @@ if (component instanceof Container) { - Component result = getLastComponent ((Container) component); + Component result = getFirstComponent ((Container) component); if (result != null) return result; } - else - { - if (accept (component)) - return component; - } + + // FIXME: can the container itself be focused? if not, then + // this part needs to be in the else clause of the if + // statement above. + if (accept (component)) + return component; } return null; @@ -137,9 +313,14 @@ /** * Returns the last Component of root that should receive the focus. * + * @param root a focus cycle root + * + * @return the last Component in the focus traversal order for + * root, or null if no acceptable Component exists. + * * @exception IllegalArgumentException If root is null. */ - public Component getLastComponent(Container root) + public Component getLastComponent (Container root) { if (root == null) throw new IllegalArgumentException (); @@ -164,11 +345,10 @@ if (result != null) return result; } - else - { - if (accept (component)) - return component; - } + + // FIXME: see above. + if (accept (component)) + return component; } return null; @@ -177,28 +357,58 @@ /** * Returns the default Component of root that should receive the focus. * + * @param root a focus cycle root + * + * @return the default Component in the focus traversal order for + * root, or null if no acceptable Component exists. + * * @exception IllegalArgumentException If root is null. */ - public Component getDefaultComponent(Container root) + public Component getDefaultComponent (Container root) { return getFirstComponent (root); } - public void setImplicitDownCycleTraversal(boolean value) + /** + * Set whether or not implicit down cycling is enabled. If it is, + * then initiating a forward focus traversal operation onto a focus + * cycle root, the focus will be implicitly transferred into the + * root container's focus cycle. + * + * @param value the setting for implicit down cycling + */ + public void setImplicitDownCycleTraversal (boolean value) { implicitDownCycleTraversal = value; } - public boolean getImplicitDownCycleTraversal() + /** + * Check whether or not implicit down cycling is enabled. If it is, + * then initiating a forward focus traversal operation onto a focus + * cycle root, the focus will be implicitly transferred into the + * root container's focus cycle. + * + * @return true if the focus will be transferred down-cycle + * implicitly + */ + public boolean getImplicitDownCycleTraversal () { return implicitDownCycleTraversal; } - protected boolean accept(Component current) + /** + * Check whether the given Component is an acceptable target for the + * keyboard input focus. + * + * @param current the Component to check + * + * @return true if current is acceptable, false otherwise + */ + protected boolean accept (Component current) { return (current.visible - && current.isDisplayable() + && current.isDisplayable () && current.enabled && current.focusable); } -} // class ContainerOrderFocusTraversalPolicy +} Index: java/awt/DefaultFocusTraversalPolicy.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/awt/DefaultFocusTraversalPolicy.java,v retrieving revision 1.1 diff -u -r1.1 DefaultFocusTraversalPolicy.java --- java/awt/DefaultFocusTraversalPolicy.java 9 Aug 2002 04:26:14 -0000 1.1 +++ java/awt/DefaultFocusTraversalPolicy.java 22 Apr 2004 16:01:10 -0000 @@ -39,17 +39,73 @@ package java.awt; /** - * STUB CLASS ONLY + * DefaultFocusTraversalPolicy is the default focus traversal policy + * used by Containers. + * + * This policy sharpens ContainerOrderFocusTraversalPolicy's + * acceptance criteria, to reject those Components that have + * unfocusable peers. Despite this extra strictness, this policy will + * always accept a Component that has explicitly been set focusable by + * any means. + * + * This AWT implementation assumes that the peers of the following + * Components are not focusable: Canvas, Panel, Label, ScrollPane, + * Scrollbar, Window, and any lightweight Component. + * + * A Component's focusability is independent of the focusability of + * its peer. + * + * @author Thomas Fitzsimmons + * @since 1.4 */ public class DefaultFocusTraversalPolicy extends ContainerOrderFocusTraversalPolicy { - public DefaultFocusTraversalPolicy() + /** + * Construct a default focus traversal policy. + */ + public DefaultFocusTraversalPolicy () { } - protected boolean accept(Component comp) + /** + * Check whether a given Component would be acceptable as a focus + * owner. The Component must be displayable, visible and enabled to + * be acceptable. If the Component's focus traversability has been + * overridden, by overriding Component.isFocusTraversable or + * Component.isFocusable, or by calling Component.setFocusable, then + * the Component will be accepted if it is focusable. If the + * Component uses the default focus traversable behaviour, then + * comp will always be rejected if it is a Canvas, + * Panel, Label, ScrollPane, Scrollbar, Window or lightweight + * Component. + * + * @param comp the Component to check + * + * @return true if the Component is an acceptable target for + * keyboard input focus, false otherwise + */ + protected boolean accept (Component comp) { - throw new Error("not implemented"); + if (comp.visible + && comp.isDisplayable () + && comp.enabled) + { + if (comp.isFocusTraversableOverridden != 0 + && comp.isFocusTraversable ()) + return true; + else + { + if (!(comp instanceof Canvas + || comp instanceof Panel + || comp instanceof Label + || comp instanceof ScrollPane + || comp instanceof Scrollbar + || comp instanceof Window + || comp.isLightweight ())) + return true; + } + } + return false; } -} // class DefaultFocusTraversalPolicy +} Index: java/awt/DefaultKeyboardFocusManager.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/awt/DefaultKeyboardFocusManager.java,v retrieving revision 1.1 diff -u -r1.1 DefaultKeyboardFocusManager.java --- java/awt/DefaultKeyboardFocusManager.java 9 Aug 2002 04:26:14 -0000 1.1 +++ java/awt/DefaultKeyboardFocusManager.java 22 Apr 2004 16:01:10 -0000 @@ -38,59 +38,434 @@ package java.awt; -import java.awt.event.KeyEvent; +import java.util.*; +import java.awt.event.*; -/** - * STUB CLASS ONLY - */ +// FIXME: finish documentation public class DefaultKeyboardFocusManager extends KeyboardFocusManager { - public DefaultKeyboardFocusManager() + /** + * This class models a request to delay the dispatch of events that + * arrive after a certain time, until a certain component becomes + * the focus owner. + */ + private class EventDelayRequest implements Comparable + { + /** A address@hidden java.util.List} of address@hidden java.awt.event.KeyEvent}s + that are being delayed, pending this request's address@hidden + Component} receiving the keyboard focus. */ + private LinkedList enqueuedKeyEvents = new LinkedList (); + + /** An event timestamp. All events that arrive after this time + should be queued in the address@hidden #enqueuedKeyEvents} address@hidden + java.util.List}. */ + public long timestamp; + /** When this address@hidden Component} becomes focused, all events + between this EventDelayRequest and the next one in will be + dispatched from address@hidden #enqueuedKeyEvents}. */ + public Component focusedComp; + + /** + * Construct a new EventDelayRequest. + * + * @param timestamp events that arrive after this time will be + * delayed + * @param focusedComp the Component that needs to receive focus + * before events are dispatched + */ + public EventDelayRequest (long timestamp, Component focusedComp) + { + this.timestamp = timestamp; + this.focusedComp = focusedComp; + } + + public int compareTo (Object o) + { + if (!(o instanceof EventDelayRequest)) + throw new ClassCastException (); + + EventDelayRequest request = (EventDelayRequest) o; + + if (request.timestamp < timestamp) + return -1; + else if (request.timestamp == timestamp) + return 0; + else + return 1; + } + + public boolean equals (Object o) + { + if (!(o instanceof EventDelayRequest) || o == null) + return false; + + EventDelayRequest request = (EventDelayRequest) o; + + return (request.timestamp == timestamp + && request.focusedComp == focusedComp); + } + + public void enqueueEvent (KeyEvent e) + { + KeyEvent last = (KeyEvent) enqueuedKeyEvents.getLast (); + if (last != null && e.getWhen () < last.getWhen ()) + throw new RuntimeException ("KeyEvents enqueued out-of-order"); + + if (e.getWhen () <= timestamp) + throw new RuntimeException ("KeyEvents enqueued before starting timestamp"); + + enqueuedKeyEvents.add (e); + } + + public void dispatchEvents () + { + int size = enqueuedKeyEvents.size (); + for (int i = 0; i < size; i++) + { + KeyEvent e = (KeyEvent) enqueuedKeyEvents.remove (0); + dispatchKeyEvent (e); + } + } + + public void discardEvents () + { + enqueuedKeyEvents.clear (); + } + } + + /** The address@hidden java.util.SortedSet} of current address@hidden + #EventDelayRequest}s. */ + private SortedSet delayRequests = new TreeSet (); + + public DefaultKeyboardFocusManager () { } - public boolean dispatchEvent(AWTEvent e) + public boolean dispatchEvent (AWTEvent e) { - throw new Error("not implemented"); + if (e instanceof WindowEvent) + { + Window target = (Window) e.getSource (); + + if (e.id == WindowEvent.WINDOW_ACTIVATED) + setGlobalActiveWindow (target); + else if (e.id == WindowEvent.WINDOW_GAINED_FOCUS) + setGlobalFocusedWindow (target); + else if (e.id != WindowEvent.WINDOW_LOST_FOCUS + && e.id != WindowEvent.WINDOW_DEACTIVATED) + return false; + + target.dispatchEvent (e); + return true; + } + else if (e instanceof FocusEvent) + { + Component target = (Component) e.getSource (); + + if (e.id == FocusEvent.FOCUS_GAINED) + { + if (((FocusEvent) e).isTemporary ()) + setGlobalFocusOwner (target); + else + { + System.out.println ("SETTING FOCUS TO: " + target); + setGlobalPermanentFocusOwner (target); + } + } + + target.dispatchEvent (e); + return true; + } + else if (e instanceof KeyEvent) + { + // Loop through all registered KeyEventDispatchers, giving + // each a chance to handle this event. + Iterator i = keyEventDispatchers.iterator (); + + while (i.hasNext ()) + { + KeyEventDispatcher dispatcher = (KeyEventDispatcher) i.next (); + if (dispatcher.dispatchKeyEvent ((KeyEvent) e)) + return true; + } + + // processKeyEvent checks if this event represents a focus + // traversal key stroke. + Component focusOwner = getGlobalPermanentFocusOwner (); + processKeyEvent (focusOwner, (KeyEvent) e); + + if (e.isConsumed ()) + return true; + + if (enqueueKeyEvent ((KeyEvent) e)) + // This event was enqueued for dispatch at a later time. + return true; + else + // This event wasn't handled by any of the registered + // KeyEventDispatchers, and wasn't enqueued for dispatch + // later, so send it to the default dispatcher. + return dispatchKeyEvent ((KeyEvent) e); + } + + return false; } - public boolean dispatchKeyEvent(KeyEvent e) + + private boolean enqueueKeyEvent (KeyEvent e) { - throw new Error("not implemented"); + Iterator i = delayRequests.iterator (); + boolean oneEnqueued = false; + while (i.hasNext ()) + { + EventDelayRequest request = (EventDelayRequest) i.next (); + if (e.getWhen () > request.timestamp) + { + request.enqueueEvent (e); + oneEnqueued = true; + } + } + return oneEnqueued; + } + + public boolean dispatchKeyEvent (KeyEvent e) + { + // System.out.println ("DefaultKeyboardFocusManager.dispatchKeyEvent: " + e); + + Component focusOwner = getGlobalPermanentFocusOwner (); + + focusOwner.dispatchEvent (e); + + // Loop through all registered KeyEventPostProcessors, giving + // each a chance to process this event. + Iterator i = keyEventPostProcessors.iterator (); + + while (i.hasNext ()) + { + KeyEventPostProcessor processor = (KeyEventPostProcessor) i.next (); + if (processor.postProcessKeyEvent ((KeyEvent) e)) + return true; + } + + // The event hasn't been consumed yet. Check if it is an + // MenuShortcut. + if (postProcessKeyEvent (e)) + return true; + + // FIXME: how do we "pass the event to the peers for processing"? + + // Always return true. + return true; } - public boolean postProcessKeyEvent(KeyEvent e) + + public boolean postProcessKeyEvent (KeyEvent e) { - throw new Error("not implemented"); + // System.out.println ("DefaultKeyboardFocusManager.postProcessKeyEvent: " + e); + + // Check if this event represents a menu shortcut. + + // MenuShortcuts are activated by Ctrl- KeyEvents. + int modifiers = e.getModifiers (); + if ((modifiers & KeyEvent.CTRL_MASK) != 0 + || (modifiers & KeyEvent.CTRL_DOWN_MASK) != 0) + { + Window focusedWindow = getGlobalFocusedWindow (); + if (focusedWindow instanceof Frame) + { + MenuBar menubar = ((Frame) focusedWindow).getMenuBar (); + + if (menubar != null) + { + // If there's a menubar, loop through all menu items, + // checking whether each one has a shortcut, and if + // so, whether this key event should activate it. + int numMenus = menubar.getMenuCount (); + + for (int i = 0; i < numMenus; i++) + { + Menu menu = menubar.getMenu (i); + int numItems = menu.getItemCount (); + + for (int j = 0; j < numItems; j++) + { + MenuItem item = menu.getItem (j); + MenuShortcut shortcut = item.getShortcut (); + + if (shortcut != null) + { + // Dispatch a new ActionEvent if this is a + // Shift- KeyEvent and the shortcut requires + // the Shift modifier, or if the shortcut + // doesn't require the Shift modifier. + if ((shortcut.usesShiftModifier () + && ((modifiers & KeyEvent.SHIFT_MASK) != 0 + || (modifiers & KeyEvent.SHIFT_DOWN_MASK) != 0) + || !shortcut.usesShiftModifier ()) + && shortcut.getKey () == e.getKeyCode ()) + { + item.dispatchEvent (new ActionEvent (item, + ActionEvent.ACTION_PERFORMED, + item.getActionCommand (), + modifiers)); + // The event was dispatched. + return true; + } + } + } + } + } + } + } + return false; } - public void processKeyEvent(Component comp, KeyEvent e) + + public void processKeyEvent (Component comp, KeyEvent e) { - throw new Error("not implemented"); + // System.out.println ("DefaultKeyboardFocusManager.processKeyEvent: " + e); + + AWTKeyStroke keystroke = AWTKeyStroke.getAWTKeyStrokeForEvent (e); + + Set forwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + Set backwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + Set upKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + Set downKeystrokes = null; + if (comp instanceof Container) + downKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + + if (forwardKeystrokes.contains (keystroke)) + { + focusNextComponent (comp); + e.consume (); + } + else if (backwardKeystrokes.contains (keystroke)) + { + focusPreviousComponent (comp); + e.consume (); + } + else if (upKeystrokes.contains (keystroke)) + { + upFocusCycle (comp); + e.consume (); + } + else if (comp instanceof Container + && downKeystrokes.contains (keystroke)) + { + downFocusCycle ((Container) comp); + e.consume (); + } } - protected void enqueueKeyEvents(long after, Component comp) + + protected void enqueueKeyEvents (long after, Component untilFocused) { - throw new Error("not implemented"); + System.out.println ("DefaultKeyboardFocusManager.enqueueKeyEvents: " + untilFocused); + + delayRequests.add (new EventDelayRequest (after, untilFocused)); } - protected void dequeueKeyEvents(long after, Component comp) + + protected void dequeueKeyEvents (long after, Component untilFocused) { - throw new Error("not implemented"); + System.out.println ("DefaultKeyboardFocusManager.dequeueKeyEvents: " + untilFocused); + + // FIXME: need synchronization on delayRequests and enqueuedKeyEvents. + + // Remove the KeyEvent with the oldest timestamp, which should be + // the first element in the SortedSet. + if (after < 0) + { + int size = delayRequests.size (); + if (size > 0) + delayRequests.remove (delayRequests.first ()); + } + else + { + EventDelayRequest template = new EventDelayRequest (after, untilFocused); + if (delayRequests.contains (template)) + { + EventDelayRequest actual = (EventDelayRequest) delayRequests.tailSet (template).first (); + delayRequests.remove (actual); + actual.dispatchEvents (); + } + } } - protected void discardKeyEvents(Component comp) + + protected void discardKeyEvents (Component comp) { - throw new Error("not implemented"); + System.out.println ("DefaultKeyboardFocusManager.discardKeyEvents: " + comp); + + // FIXME: need synchronization on delayRequests and enqueuedKeyEvents. + + Iterator i = delayRequests.iterator (); + + while (i.hasNext ()) + { + EventDelayRequest request = (EventDelayRequest) i.next (); + + if (request.focusedComp == comp + || (comp instanceof Container + && ((Container) comp).isAncestorOf (request.focusedComp))) + request.discardEvents (); + } } - public void focusPreviousComponent(Component comp) + + public void focusPreviousComponent (Component comp) { - throw new Error("not implemented"); + System.out.println ("DefaultKeyboardFocusManager.focusPreviousComponent: " + comp); + + Component focusComp = (comp == null) ? getGlobalFocusOwner () : comp; + Container focusCycleRoot = focusComp.getFocusCycleRootAncestor (); + FocusTraversalPolicy policy = focusCycleRoot.getFocusTraversalPolicy (); + + Component previous = policy.getComponentBefore (focusCycleRoot, focusComp); + previous.requestFocus (); } - public void focusNextComponent(Component comp) + + public void focusNextComponent (Component comp) { - throw new Error("not implemented"); + System.out.println ("DefaultKeyboardFocusManager.focusNextComponent: " + comp); + + Component focusComp = (comp == null) ? getGlobalFocusOwner () : comp; + Container focusCycleRoot = focusComp.getFocusCycleRootAncestor (); + FocusTraversalPolicy policy = focusCycleRoot.getFocusTraversalPolicy (); + + Component next = policy.getComponentAfter (focusCycleRoot, focusComp); + System.out.println ("Got next component: " + next); + System.out.println ("Got focus comp: " + focusComp); + next.requestFocus (); } - public void upFocusCycle(Component comp) + + public void upFocusCycle (Component comp) { - throw new Error("not implemented"); + System.out.println ("DefaultKeyboardFocusManager.upFocusCycle: " + comp); + + Component focusComp = (comp == null) ? getGlobalFocusOwner () : comp; + Container focusCycleRoot = focusComp.getFocusCycleRootAncestor (); + + if (focusCycleRoot instanceof Window) + { + FocusTraversalPolicy policy = focusCycleRoot.getFocusTraversalPolicy (); + Component defaultComponent = policy.getDefaultComponent (focusCycleRoot); + defaultComponent.requestFocus (); + } + else + { + Container parentFocusCycleRoot = focusCycleRoot.getFocusCycleRootAncestor (); + + focusCycleRoot.requestFocus (); + setGlobalCurrentFocusCycleRoot (parentFocusCycleRoot); + } } - public void downFocusCycle(Container cont) + + public void downFocusCycle (Container cont) { - throw new Error("not implemented"); + System.out.println ("DefaultKeyboardFocusManager.downFocusCycle: " + cont); + + if (cont == null) + return; + + if (cont.isFocusCycleRoot (cont)) + { + FocusTraversalPolicy policy = cont.getFocusTraversalPolicy (); + Component defaultComponent = policy.getDefaultComponent (cont); + defaultComponent.requestFocus (); + setGlobalCurrentFocusCycleRoot (cont); + } } } // class DefaultKeyboardFocusManager Index: java/awt/EventDispatchThread.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/awt/EventDispatchThread.java,v retrieving revision 1.5 diff -u -r1.5 EventDispatchThread.java --- java/awt/EventDispatchThread.java 16 Jan 2004 16:15:49 -0000 1.5 +++ java/awt/EventDispatchThread.java 22 Apr 2004 16:01:10 -0000 @@ -57,6 +57,9 @@ public void run() { + // Set the default KeyboardFocusManager. + KeyboardFocusManager.setCurrentKeyboardFocusManager (null); + while (true) { try @@ -67,7 +70,23 @@ // We are interrupted when we should finish executing return; } - queue.dispatchEvent(evt); + + KeyboardFocusManager manager; + manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + // Try to dispatch this event to the current keyboard focus + // manager. It will dispatch all FocusEvents, all + // WindowEvents related to focus, and all KeyEvents, + // returning true. Otherwise, it returns false and we + // dispatch the event normally. + if (!manager.dispatchEvent (evt)) + { + queue.dispatchEvent(evt); + } + else + { + // System.out.println ("Dispatched " + evt + " to DefaultKeyboardFocusManager"); + } } catch (InterruptedException ie) { Index: java/awt/KeyboardFocusManager.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/awt/KeyboardFocusManager.java,v retrieving revision 1.2 diff -u -r1.2 KeyboardFocusManager.java --- java/awt/KeyboardFocusManager.java 13 Feb 2003 07:02:12 -0000 1.2 +++ java/awt/KeyboardFocusManager.java 22 Apr 2004 16:01:10 -0000 @@ -39,34 +39,66 @@ 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 + * @author Thomas Fitzsimmons * @since 1.4 * @status partially updated to 1.4, needs documentation. */ public abstract class KeyboardFocusManager implements KeyEventDispatcher, KeyEventPostProcessor { + /** Identifies address@hidden AWTKeyStroke}s that move the focus forward in + the focus cycle. */ public static final int FORWARD_TRAVERSAL_KEYS = 0; + + /** Identifies address@hidden AWTKeyStroke}s that move the focus backward in + the focus cycle. */ public static final int BACKWARD_TRAVERSAL_KEYS = 1; + + /** Identifies address@hidden AWTKeyStroke}s that move the focus up to the + parent focus cycle root. */ public static final int UP_CYCLE_TRAVERSAL_KEYS = 2; + + /** Identifies address@hidden 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 address@hidden 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 address@hidden 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 + address@hidden java.util.Set}s. */ static { Set s = new HashSet(); @@ -83,232 +115,403 @@ 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 address@hidden java.util.Map}s. */ + /** For security reasons, address@hidden java.applet.Applet}s in different + codebases must be insulated from one another. Since address@hidden + KeyboardFocusManager}s have the ability to return address@hidden + Component}s from a given address@hidden java.applet.Applet}, each + codebase must have an independent address@hidden KeyboardFocusManager}. + Since each codebase has its own address@hidden ThreadGroup} in which its + address@hidden Applet}s run, it makes sense to partition address@hidden + KeyboardFocusManager}s according to address@hidden + java.lang.ThreadGroup}. Thus, currentKeyboardFocusManagers is a + address@hidden java.util.Map} keyed on address@hidden java.lang.ThreadGroup}. */ + private static Map currentKeyboardFocusManagers = new HashMap (); + + /** address@hidden java.applet.Applet}s in one codebase must not be allowed + to access address@hidden Component}s in address@hidden java.applet.Applet}s in + other codebases. To enforce this restriction, we key the + following address@hidden java.util.Map}s on address@hidden java.lang.ThreadGroup}s (which + are per-codebase). For example, if address@hidden + java.lang.ThreadGroup} A calls address@hidden #setGlobalFocusOwner}, + passing address@hidden Component} C, currentFocusOwners[A] is assigned + C, and all other currentFocusOwners values are nullified. Then + if address@hidden java.lang.ThreadGroup} A subsequently calls address@hidden + #getGlobalFocusOwner}, it will return currentFocusOwners[A], + that is, address@hidden Component} C. If another address@hidden + java.lang.ThreadGroup} K calls address@hidden #getGlobalFocusOwner}, it + will return currentFocusOwners[K], that is, null. + + Since this is a static field, we ensure that there is only one + focused address@hidden Component} per class loader. */ + private static Map currentFocusOwners = new HashMap (); + + /** A address@hidden java.util.Map} keyed on address@hidden java.lang.ThreadGroup}s + that stores the address@hidden Component} that owns the permanent + keyboard focus. @see currentFocusOwners */ + private static Map currentPermanentFocusOwners = new HashMap (); + + /** A address@hidden java.util.Map} keyed on address@hidden java.lang.ThreadGroup}s + that stores the focused address@hidden Window}. @see + currentFocusOwners */ + private static Map currentFocusedWindows = new HashMap (); + + /** A address@hidden java.util.Map} keyed on address@hidden java.lang.ThreadGroup}s + that stores the active address@hidden Window}. @see + currentFocusOwners */ + private static Map currentActiveWindows = new HashMap (); + + /** A address@hidden java.util.Map} keyed on address@hidden java.lang.ThreadGroup}s + that stores the focus cycle root address@hidden Container}. @see + currentFocusOwners */ + private static Map currentFocusCycleRoots = new HashMap (); + + /** The default address@hidden FocusTraveralPolicy} that focus-managing + address@hidden Container}s will use to define their initial focus + traversal policy. */ private FocusTraversalPolicy defaultPolicy; - private Set[] defaultFocusKeys = new Set[] { + + /** An array that stores the address@hidden #FORWARD_TRAVERSAL_KEYS}, address@hidden + #BACKWARD_TRAVERSAL_KEYS}, address@hidden #UP_CYCLE_TRAVERSAL_KEYS} and + address@hidden #DOWN_CYCLE_TRAVERSAL_KEYS} address@hidden AWTKeyStroke}s address@hidden + 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 address@hidden KeyEventDispatcher}s that process address@hidden + KeyEvent}s before they are processed the default keyboard focus + manager. */ private final ArrayList keyEventDispatchers = new ArrayList(); - private final ArrayList keyEventPostProcessors = new ArrayList(); - - public KeyboardFocusManager() - { - } + /** A list of address@hidden KeyEventPostProcessor}s that process unconsumed + address@hidden KeyEvent}s. */ + private final ArrayList keyEventPostProcessors = new ArrayList(); - public static KeyboardFocusManager getCurrentKeyboardFocusManager() + /** + * Construct a KeyboardFocusManager. + */ + public KeyboardFocusManager () { - // XXX Need a way to divide this into contexts. - return current; } - public static void setCurrentKeyboardFocusManager(KeyboardFocusManager m) + /** + * Retrieve the keyboard focus manager associated with the address@hidden + * java.lang.ThreadGroup} to which the calling thread belongs. + * + * @return the keyboard focus manager associated with the current + * thread group + */ + public static KeyboardFocusManager getCurrentKeyboardFocusManager () + { + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + return (KeyboardFocusManager) currentKeyboardFocusManagers.get (currentGroup); + } + + /** + * Set the keyboard focus manager associated with the address@hidden + * 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; - } + sm.checkPermission (new AWTPermission ("replaceKeyboardFocusManager")); - public Component getFocusOwner() - { - // 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; - } + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + KeyboardFocusManager manager; - protected void setGlobalFocusOwner(Component owner) + if (m == null) + manager = new DefaultKeyboardFocusManager (); + else + manager = m; + + currentKeyboardFocusManagers.put (currentGroup, manager); + } + + /** + * Retrieve the address@hidden Component} that has the keyboard focus, or + * null if the focus owner was not set by a thread in the current + * address@hidden java.lang.ThreadGroup}. + * + * @return the keyboard focus owner or null + */ + public Component getFocusOwner () + { + return (Component) getObject (currentFocusOwners); + } + + /** + * Retrieve the address@hidden Component} that has the keyboard focus, + * regardless of whether or not it was set by a thread in the + * current address@hidden java.lang.ThreadGroup}. If there is no temporary + * focus owner in effect then this method will return the same value + * as address@hidden #getGlobalPermanentFocusOwner}. + * + * @return the keyboard focus owner + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current address@hidden 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 address@hidden Component} that will be returned by address@hidden + * #getFocusOwner} (when it is called from the current address@hidden + * java.lang.ThreadGroup}) and address@hidden #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 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) - { - } - } + setGlobalObject (currentFocusOwners, owner, "focusOwner"); } - public void clearGlobalFocusOwner() + /** + * Clear the global focus owner and deliver a FOCUS_LOST event to + * the previously-focused address@hidden Component}. Until another address@hidden + * Component} becomes the keyboard focus owner, key events will be + * discarded by top-level windows. + */ + public void clearGlobalFocusOwner () { - // XXX Is this enough? - setGlobalFocusOwner(null); - } - - public Component getPermanentFocusOwner() - { - // 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 address@hidden Component} that has the permanent keyboard + * focus, or null if the focus owner was not set by a thread in the + * current address@hidden java.lang.ThreadGroup}. + * + * @return the keyboard focus owner or null + */ + public Component getPermanentFocusOwner () + { + return (Component) getObject (currentPermanentFocusOwners); + } + + /** + * Retrieve the address@hidden Component} that has the permanent keyboard + * focus, regardless of whether or not it was set by a thread in the + * current address@hidden java.lang.ThreadGroup}. + * + * @return the keyboard focus owner + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current address@hidden java.lang.ThreadGroup} + */ + protected Component getGlobalPermanentFocusOwner () + { + return (Component) getGlobalObject (currentPermanentFocusOwners); + } + + /** + * Set the address@hidden Component} that will be returned by address@hidden + * #getPermanentFocusOwner} (when it is called from the current + * address@hidden java.lang.ThreadGroup}) and address@hidden + * #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; + System.out.println ("in setGlobalFocusOwner: " + focusOwner); + if (focusOwner == null || focusOwner.focusable) + setGlobalObject (currentPermanentFocusOwners, focusOwner, + "permanentFocusOwner"); } - protected void setGlobalFocusedWindow(Window window) + /** + * Retrieve the address@hidden Window} that is or contains the keyboard + * focus owner, or null if the focused window was not set by a + * thread in the current address@hidden java.lang.ThreadGroup}. + * + * @return the focused window or null + */ + public Window getFocusedWindow () + { + return (Window) getObject (currentFocusedWindows); + } + + /** + * Retrieve the address@hidden Window} that is or contains the focus owner, + * regardless of whether or not the address@hidden Window} was set focused + * by a thread in the current address@hidden java.lang.ThreadGroup}. + * + * @return the focused window + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current address@hidden java.lang.ThreadGroup} + */ + protected Window getGlobalFocusedWindow () + { + return (Window) getGlobalObject (currentFocusedWindows); + } + + /** + * Set the address@hidden Window} that will be returned by address@hidden + * #getFocusedWindow} (when it is called from the current address@hidden + * java.lang.ThreadGroup}) and address@hidden #getGlobalFocusedWindow}. + * This method does not actually cause window to become + * the focused address@hidden 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 address@hidden Window}, or null if the active window + * was not set by a thread in the current address@hidden + * 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 address@hidden Window}, regardless of whether or not + * the address@hidden Window} was made active by a thread in the current + * address@hidden java.lang.ThreadGroup}. + * + * @return the active window + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current address@hidden 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 address@hidden Window} that will be returned by address@hidden + * #getActiveWindow} (when it is called from the current address@hidden + * java.lang.ThreadGroup}) and address@hidden #getGlobalActiveWindow}. This + * method does not actually cause window 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 address@hidden FocusTraversalPolicy}. + * Focus-managing address@hidden 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 address@hidden FocusTraversalPolicy} returned by address@hidden + * #getDefaultFocusTraversalPolicy}. Focus-managing address@hidden + * Container}s created after this call will use policy as their + * initial focus traversal policy. Existing address@hidden 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 address@hidden 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 +543,82 @@ 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 address@hidden 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() - { - // 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; - } - } - - 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; - } - - public void setGlobalCurrentFocusCycleRoot(Container cycleRoot) + /** + * Retrieve the current focus cycle root, or null if the focus owner + * was not set by a thread in the current address@hidden + * java.lang.ThreadGroup}. + * + * @return the current focus cycle root or null + */ + public Container getCurrentFocusCycleRoot () + { + return (Container) getObject (currentFocusCycleRoots); + } + + /** + * Retrieve the current focus cycle root, regardless of whether or + * not it was made set by a thread in the current address@hidden + * java.lang.ThreadGroup}. + * + * @return the current focus cycle root + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current address@hidden java.lang.ThreadGroup} + */ + protected Container getGlobalCurrentFocusCycleRoot () + { + return (Container) getGlobalObject (currentFocusCycleRoots); + } + + /** + * Set the address@hidden Container} that will be returned by address@hidden + * #getCurrentFocusCycleRoot} (when it is called from the current + * address@hidden java.lang.ThreadGroup}) and address@hidden + * #getGlobalCurrentFocusCycleRoot}. This method does not actually + * make cycleRoot 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 +713,196 @@ 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 address@hidden + * java.util.Map}s, if the object was set by the a thread in the + * current address@hidden 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 address@hidden + * java.util.Map}s, regardless of whether or not the object was set + * by a thread in the current address@hidden 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 address@hidden 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 address@hidden java.util.Map}s, + * that will be returned by subsequent calls to getGlobalObject on + * the same address@hidden 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 +} Index: java/awt/Window.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/awt/Window.java,v retrieving revision 1.32.12.2 diff -u -r1.32.12.2 Window.java --- java/awt/Window.java 17 Feb 2004 21:26:32 -0000 1.32.12.2 +++ java/awt/Window.java 22 Apr 2004 16:01:10 -0000 @@ -83,6 +83,8 @@ private transient GraphicsConfiguration graphicsConfiguration; private transient AccessibleContext accessibleContext; + private transient boolean shown; + /** * This (package access) constructor is used by subclasses that want * to build windows that do not have parents. Eg. toplevel @@ -92,6 +94,9 @@ Window() { visible = false; + // Windows are the only Containers that default to being focus + // cycle roots. + focusCycleRoot = true; setLayout(new BorderLayout()); } @@ -242,6 +247,24 @@ validate(); super.show(); toFront(); + + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + manager.setGlobalFocusedWindow (this); + + if (!shown) + { + FocusTraversalPolicy policy = getFocusTraversalPolicy (); + Component initialFocusOwner = null; + + if (policy != null) + initialFocusOwner = policy.getInitialComponent (this); + + if (initialFocusOwner != null) + initialFocusOwner.requestFocusInWindow (false); + + System.out.println ("GOT INITIAL FOCUS OWNER: " + initialFocusOwner); + shown = true; + } } public void hide() @@ -627,9 +650,16 @@ * @return The component that has focus, or null if no * component has focus. */ - public Component getFocusOwner() + public Component getFocusOwner () { - // FIXME + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + Window activeWindow = manager.getActiveWindow (); + + // The currently-focused Component belongs to the active Window. + if (activeWindow == this) + return manager.getFocusOwner (); + return null; } Index: jni/gtk-peer/gnu_java_awt_peer_gtk_GtkComponentPeer.c =================================================================== RCS file: /cvs/gcc/gcc/libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkComponentPeer.c,v retrieving revision 1.15.2.3 diff -u -r1.15.2.3 gnu_java_awt_peer_gtk_GtkComponentPeer.c --- jni/gtk-peer/gnu_java_awt_peer_gtk_GtkComponentPeer.c 26 Mar 2004 20:30:37 -0000 1.15.2.3 +++ jni/gtk-peer/gnu_java_awt_peer_gtk_GtkComponentPeer.c 22 Apr 2004 16:01:10 -0000 @@ -42,6 +42,12 @@ static GtkWidget *find_fg_color_widget (GtkWidget *widget); static GtkWidget *find_bg_color_widget (GtkWidget *widget); +static gboolean focus_in_cb (GtkWidget *widget, + GdkEventFocus *event, + jobject peer); +static gboolean focus_out_cb (GtkWidget *widget, + GdkEventFocus *event, + jobject peer); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkGenericPeer_dispose (JNIEnv *env, jobject obj) @@ -128,7 +134,7 @@ gdk_threads_leave (); } -JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkComponentPeer_requestFocus +JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkComponentPeer_gtkWidgetRequestFocus (JNIEnv *env, jobject obj) { void *ptr; @@ -136,7 +142,59 @@ ptr = NSA_GET_PTR (env, obj); gdk_threads_enter (); - // gtk_widget_grab_focus (GTK_WIDGET (ptr)); + gtk_widget_grab_focus (GTK_WIDGET (ptr)); + gdk_threads_leave (); +} + +/* + * Translate a Java KeyEvent object into a GdkEventKey event, then + * pass it to the GTK main loop for processing. + */ +JNIEXPORT void JNICALL +Java_gnu_java_awt_peer_gtk_GtkComponentPeer_gtkWidgetDispatchKeyEvent + (JNIEnv *env, jobject obj, jint id, jlong when, jint mods, + jint keyCode, jchar keyChar, jint keyLocation) +{ + void *ptr; + GdkEvent *event = gdk_event_new (GDK_MOTION_NOTIFY); + + ptr = NSA_GET_PTR (env, obj); + + // FIXME: need to flesh this out. + // Translate + if (id == AWT_KEY_PRESSED) + event->type = GDK_KEY_PRESS; + else if (id == AWT_KEY_RELEASED) + event->type = GDK_KEY_RELEASE; + else + // Don't send AWT KEY_TYPED events to GTK. + return; + + // FIXME: this won't work for TextAreas or Lists, which are packed + // in GtkScrolledWindows. + if (GTK_IS_BUTTON (ptr)) + event->key.window = GTK_BUTTON (ptr)->event_window; + else + event->key.window = GTK_WIDGET (ptr)->window; + + event->key.send_event = TRUE; + + event->key.time = (guint32) when; + // Translate state + event->key.state = 0; + // Translate keyval + event->key.keyval = keyCode; + event->key.length = 1; + // Translate keyChar + event->key.string = g_strdup ("a"); + event->key.hardware_keycode = 0; + event->key.group = 0; + + gdk_threads_enter (); + + g_printerr ("sending event\n"); + gtk_main_do_event (event); + gdk_threads_leave (); } @@ -775,6 +833,12 @@ g_signal_connect (GTK_OBJECT (ptr), "event", G_CALLBACK (pre_event_handler), *gref); + g_signal_connect (G_OBJECT (ptr), "focus-in-event", + G_CALLBACK (focus_in_cb), *gref); + + g_signal_connect (G_OBJECT (ptr), "focus-out-event", + G_CALLBACK (focus_out_cb), *gref); + gdk_threads_leave (); } @@ -813,3 +877,26 @@ return bg_color_widget; } +static gboolean focus_in_cb (GtkWidget *widget, + GdkEventFocus *event, + jobject peer) +{ + g_printerr (" focus in: %p\n", peer); + (*gdk_env)->CallVoidMethod (gdk_env, peer, + postFocusEventID, + AWT_FOCUS_GAINED, + JNI_FALSE); + return FALSE; +} + +static gboolean focus_out_cb (GtkWidget *widget, + GdkEventFocus *event, + jobject peer) +{ + g_printerr (" focus out: %p\n", peer); + (*gdk_env)->CallVoidMethod (gdk_env, peer, + postFocusEventID, + AWT_FOCUS_LOST, + JNI_FALSE); + return FALSE; +} Index: jni/gtk-peer/gnu_java_awt_peer_gtk_GtkEvents.c =================================================================== RCS file: /cvs/gcc/gcc/libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkEvents.c,v retrieving revision 1.19.2.1 diff -u -r1.19.2.1 gnu_java_awt_peer_gtk_GtkEvents.c --- jni/gtk-peer/gnu_java_awt_peer_gtk_GtkEvents.c 5 Mar 2004 22:24:50 -0000 1.19.2.1 +++ jni/gtk-peer/gnu_java_awt_peer_gtk_GtkEvents.c 22 Apr 2004 16:01:10 -0000 @@ -1055,104 +1055,54 @@ } } break; + case GDK_FOCUS_CHANGE: (*gdk_env)->CallVoidMethod (gdk_env, peer, postFocusEventID, - (jint) (event->focus_change.in) ? + (jint) (event->focus_change.in) ? AWT_FOCUS_GAINED : AWT_FOCUS_LOST, JNI_FALSE); break; case GDK_KEY_PRESS: - case GDK_KEY_RELEASE: - { - GdkWindow *obj_window; - jobject *focus_obj_ptr = NULL; - int generates_key_typed = 0; - - /* A widget with a grab will get key events */ - if (!GTK_IS_WINDOW (widget)) - focus_obj_ptr = &peer; - else - { - GtkWindow *window; - - /* Check if we have an enabled focused widget in this window. - If not don't handle the event. */ - window = GTK_WINDOW (widget); - if (!window->focus_widget - || !GTK_WIDGET_IS_SENSITIVE (window->focus_widget) - || !window->focus_widget->window) - return FALSE; - - /* TextArea peers are attached to the scrolled window - that contains the GtkTextView, not to the text view - itself. Same for List. */ - if (GTK_IS_TEXT_VIEW (window->focus_widget) - || GTK_IS_CLIST (window->focus_widget)) - { - obj_window = gtk_widget_get_parent (window->focus_widget)->window; - } - else if (GTK_IS_BUTTON (window->focus_widget)) - /* GtkButton events go to the "event_window" and this is what - we registered when the button was created. */ - obj_window = GTK_BUTTON (window->focus_widget)->event_window; - else - obj_window = window->focus_widget->window; - - gdk_property_get (obj_window, - gdk_atom_intern ("_GNU_GTKAWT_ADDR", FALSE), - gdk_atom_intern ("CARDINAL", FALSE), - 0, - sizeof (jobject), - FALSE, - NULL, - NULL, - NULL, - (guchar **)&focus_obj_ptr); - - /* If the window has no jobject attached we can't send anything */ - if (!focus_obj_ptr) - return FALSE; - - /* Should we generate an AWT_KEY_TYPED event? */ - generates_key_typed = generates_key_typed_event (event, window->focus_widget); - } + if (GTK_IS_WINDOW (widget)) + { + g_print("got key event on widget %s\n", gtk_widget_get_name (widget)); - if (event->type == GDK_KEY_PRESS) - { - (*gdk_env)->CallVoidMethod (gdk_env, *focus_obj_ptr, - postKeyEventID, - (jint) AWT_KEY_PRESSED, - (jlong) event->key.time, + (*gdk_env)->CallVoidMethod (gdk_env, peer, + postKeyEventID, + (jint) AWT_KEY_PRESSED, + (jlong) event->key.time, keyevent_state_to_awt_mods (event), keysym_to_awt_keycode (event), keyevent_to_awt_keychar (event), keysym_to_awt_keylocation (event)); - - if (generates_key_typed) - { - (*gdk_env)->CallVoidMethod (gdk_env, *focus_obj_ptr, - postKeyEventID, - (jint) AWT_KEY_TYPED, - (jlong) event->key.time, - state_to_awt_mods (event->key.state), - VK_UNDEFINED, - keyevent_to_awt_keychar (event), - AWT_KEY_LOCATION_UNKNOWN); - } + return TRUE; } - else /* GDK_KEY_RELEASE */ - { - (*gdk_env)->CallVoidMethod (gdk_env, *focus_obj_ptr, - postKeyEventID, - (jint) AWT_KEY_RELEASED, - (jlong) event->key.time, - keyevent_state_to_awt_mods (event), - keysym_to_awt_keycode (event), + else + return FALSE; + break; + case GDK_KEY_RELEASE: + if (GTK_IS_WINDOW (widget)) + { + g_print("got key event on widget %s\n", gtk_widget_get_name (widget)); + + (*gdk_env)->CallVoidMethod (gdk_env, peer, + postKeyEventID, + (jint) AWT_KEY_RELEASED, + (jlong) event->key.time, + keyevent_state_to_awt_mods (event), + keysym_to_awt_keycode (event), keyevent_to_awt_keychar (event), keysym_to_awt_keylocation (event)); - } - } + return TRUE; + } + else + { + // We generated this event after Java processed the + // corresponding event on the window. + if (event->key.send_event) + return FALSE; + } break; default: break; Index: jni/gtk-peer/gtkpeer.h =================================================================== RCS file: /cvs/gcc/gcc/libjava/jni/gtk-peer/gtkpeer.h,v retrieving revision 1.12 diff -u -r1.12 gtkpeer.h --- jni/gtk-peer/gtkpeer.h 23 Dec 2003 19:24:00 -0000 1.12 +++ jni/gtk-peer/gtkpeer.h 22 Apr 2004 16:01:10 -0000 @@ -361,8 +361,8 @@ #define VK_ALT_GRAPH 65406 #define VK_UNDEFINED 0 -#define AWT_FOCUS_LOST 1004 -#define AWT_FOCUS_GAINED 1005 +#define AWT_FOCUS_GAINED 1004 +#define AWT_FOCUS_LOST 1005 #define AWT_WINDOW_OPENED 200 #define AWT_WINDOW_CLOSING 201