jdk/src/java.desktop/share/classes/java/awt/DefaultKeyboardFocusManager.java
changeset 25859 3317bb8137f4
parent 23276 add6f5c93bc6
child 26749 b6598aa90114
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/java/awt/DefaultKeyboardFocusManager.java	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,1419 @@
+/*
+ * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.awt;
+
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowEvent;
+import java.awt.peer.ComponentPeer;
+import java.awt.peer.LightweightPeer;
+import java.lang.ref.WeakReference;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Set;
+
+import sun.util.logging.PlatformLogger;
+
+import sun.awt.AppContext;
+import sun.awt.SunToolkit;
+import sun.awt.AWTAccessor;
+import sun.awt.CausedFocusEvent;
+import sun.awt.TimedWindowEvent;
+
+/**
+ * The default KeyboardFocusManager for AWT applications. Focus traversal is
+ * done in response to a Component's focus traversal keys, and using a
+ * Container's FocusTraversalPolicy.
+ * <p>
+ * Please see
+ * <a href="http://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html">
+ * How to Use the Focus Subsystem</a>,
+ * a section in <em>The Java Tutorial</em>, and the
+ * <a href="../../java/awt/doc-files/FocusSpec.html">Focus Specification</a>
+ * for more information.
+ *
+ * @author David Mendenhall
+ *
+ * @see FocusTraversalPolicy
+ * @see Component#setFocusTraversalKeys
+ * @see Component#getFocusTraversalKeys
+ * @since 1.4
+ */
+public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
+    private static final PlatformLogger focusLog = PlatformLogger.getLogger("java.awt.focus.DefaultKeyboardFocusManager");
+
+    // null weak references to not create too many objects
+    private static final WeakReference<Window> NULL_WINDOW_WR =
+        new WeakReference<Window>(null);
+    private static final WeakReference<Component> NULL_COMPONENT_WR =
+        new WeakReference<Component>(null);
+    private WeakReference<Window> realOppositeWindowWR = NULL_WINDOW_WR;
+    private WeakReference<Component> realOppositeComponentWR = NULL_COMPONENT_WR;
+    private int inSendMessage;
+    private LinkedList<KeyEvent> enqueuedKeyEvents = new LinkedList<KeyEvent>();
+    private LinkedList<TypeAheadMarker> typeAheadMarkers = new LinkedList<TypeAheadMarker>();
+    private boolean consumeNextKeyTyped;
+
+    static {
+        AWTAccessor.setDefaultKeyboardFocusManagerAccessor(
+            new AWTAccessor.DefaultKeyboardFocusManagerAccessor() {
+                public void consumeNextKeyTyped(DefaultKeyboardFocusManager dkfm, KeyEvent e) {
+                    dkfm.consumeNextKeyTyped(e);
+                }
+            });
+    }
+
+    private static class TypeAheadMarker {
+        long after;
+        Component untilFocused;
+
+        TypeAheadMarker(long after, Component untilFocused) {
+            this.after = after;
+            this.untilFocused = untilFocused;
+        }
+        /**
+         * Returns string representation of the marker
+         */
+        public String toString() {
+            return ">>> Marker after " + after + " on " + untilFocused;
+        }
+    }
+
+    private Window getOwningFrameDialog(Window window) {
+        while (window != null && !(window instanceof Frame ||
+                                   window instanceof Dialog)) {
+            window = (Window)window.getParent();
+        }
+        return window;
+    }
+
+    /*
+     * This series of restoreFocus methods is used for recovering from a
+     * rejected focus or activation change. Rejections typically occur when
+     * the user attempts to focus a non-focusable Component or Window.
+     */
+    private void restoreFocus(FocusEvent fe, Window newFocusedWindow) {
+        Component realOppositeComponent = this.realOppositeComponentWR.get();
+        Component vetoedComponent = fe.getComponent();
+
+        if (newFocusedWindow != null && restoreFocus(newFocusedWindow,
+                                                     vetoedComponent, false))
+        {
+        } else if (realOppositeComponent != null &&
+                   doRestoreFocus(realOppositeComponent, vetoedComponent, false)) {
+        } else if (fe.getOppositeComponent() != null &&
+                   doRestoreFocus(fe.getOppositeComponent(), vetoedComponent, false)) {
+        } else {
+            clearGlobalFocusOwnerPriv();
+        }
+    }
+    private void restoreFocus(WindowEvent we) {
+        Window realOppositeWindow = this.realOppositeWindowWR.get();
+        if (realOppositeWindow != null
+            && restoreFocus(realOppositeWindow, null, false))
+        {
+            // do nothing, everything is done in restoreFocus()
+        } else if (we.getOppositeWindow() != null &&
+                   restoreFocus(we.getOppositeWindow(), null, false))
+        {
+            // do nothing, everything is done in restoreFocus()
+        } else {
+            clearGlobalFocusOwnerPriv();
+        }
+    }
+    private boolean restoreFocus(Window aWindow, Component vetoedComponent,
+                                 boolean clearOnFailure) {
+        Component toFocus =
+            KeyboardFocusManager.getMostRecentFocusOwner(aWindow);
+
+        if (toFocus != null && toFocus != vetoedComponent && doRestoreFocus(toFocus, vetoedComponent, false)) {
+            return true;
+        } else if (clearOnFailure) {
+            clearGlobalFocusOwnerPriv();
+            return true;
+        } else {
+            return false;
+        }
+    }
+    private boolean restoreFocus(Component toFocus, boolean clearOnFailure) {
+        return doRestoreFocus(toFocus, null, clearOnFailure);
+    }
+    private boolean doRestoreFocus(Component toFocus, Component vetoedComponent,
+                                   boolean clearOnFailure)
+    {
+        if (toFocus != vetoedComponent && toFocus.isShowing() && toFocus.canBeFocusOwner() &&
+            toFocus.requestFocus(false, CausedFocusEvent.Cause.ROLLBACK))
+        {
+            return true;
+        } else {
+            Component nextFocus = toFocus.getNextFocusCandidate();
+            if (nextFocus != null && nextFocus != vetoedComponent &&
+                nextFocus.requestFocusInWindow(CausedFocusEvent.Cause.ROLLBACK))
+            {
+                return true;
+            } else if (clearOnFailure) {
+                clearGlobalFocusOwnerPriv();
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * A special type of SentEvent which updates a counter in the target
+     * KeyboardFocusManager if it is an instance of
+     * DefaultKeyboardFocusManager.
+     */
+    private static class DefaultKeyboardFocusManagerSentEvent
+        extends SentEvent
+    {
+        /*
+         * serialVersionUID
+         */
+        private static final long serialVersionUID = -2924743257508701758L;
+
+        public DefaultKeyboardFocusManagerSentEvent(AWTEvent nested,
+                                                    AppContext toNotify) {
+            super(nested, toNotify);
+        }
+        public final void dispatch() {
+            KeyboardFocusManager manager =
+                KeyboardFocusManager.getCurrentKeyboardFocusManager();
+            DefaultKeyboardFocusManager defaultManager =
+                (manager instanceof DefaultKeyboardFocusManager)
+                ? (DefaultKeyboardFocusManager)manager
+                : null;
+
+            if (defaultManager != null) {
+                synchronized (defaultManager) {
+                    defaultManager.inSendMessage++;
+                }
+            }
+
+            super.dispatch();
+
+            if (defaultManager != null) {
+                synchronized (defaultManager) {
+                    defaultManager.inSendMessage--;
+                }
+            }
+        }
+    }
+
+    /**
+     * Sends a synthetic AWTEvent to a Component. If the Component is in
+     * the current AppContext, then the event is immediately dispatched.
+     * If the Component is in a different AppContext, then the event is
+     * posted to the other AppContext's EventQueue, and this method blocks
+     * until the event is handled or target AppContext is disposed.
+     * Returns true if successfuly dispatched event, false if failed
+     * to dispatch.
+     */
+    static boolean sendMessage(Component target, AWTEvent e) {
+        e.isPosted = true;
+        AppContext myAppContext = AppContext.getAppContext();
+        final AppContext targetAppContext = target.appContext;
+        final SentEvent se =
+            new DefaultKeyboardFocusManagerSentEvent(e, myAppContext);
+
+        if (myAppContext == targetAppContext) {
+            se.dispatch();
+        } else {
+            if (targetAppContext.isDisposed()) {
+                return false;
+            }
+            SunToolkit.postEvent(targetAppContext, se);
+            if (EventQueue.isDispatchThread()) {
+                EventDispatchThread edt = (EventDispatchThread)
+                    Thread.currentThread();
+                edt.pumpEvents(SentEvent.ID, new Conditional() {
+                        public boolean evaluate() {
+                            return !se.dispatched && !targetAppContext.isDisposed();
+                        }
+                    });
+            } else {
+                synchronized (se) {
+                    while (!se.dispatched && !targetAppContext.isDisposed()) {
+                        try {
+                            se.wait(1000);
+                        } catch (InterruptedException ie) {
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return se.dispatched;
+    }
+
+    /*
+     * Checks if the focus window event follows key events waiting in the type-ahead
+     * queue (if any). This may happen when a user types ahead in the window, the client
+     * listeners hang EDT for a while, and the user switches b/w toplevels. In that
+     * case the focus window events may be dispatched before the type-ahead events
+     * get handled. This may lead to wrong focus behavior and in order to avoid it,
+     * the focus window events are reposted to the end of the event queue. See 6981400.
+     */
+    private boolean repostIfFollowsKeyEvents(WindowEvent e) {
+        if (!(e instanceof TimedWindowEvent)) {
+            return false;
+        }
+        TimedWindowEvent we = (TimedWindowEvent)e;
+        long time = we.getWhen();
+        synchronized (this) {
+            KeyEvent ke = enqueuedKeyEvents.isEmpty() ? null : enqueuedKeyEvents.getFirst();
+            if (ke != null && time >= ke.getWhen()) {
+                TypeAheadMarker marker = typeAheadMarkers.isEmpty() ? null : typeAheadMarkers.getFirst();
+                if (marker != null) {
+                    Window toplevel = marker.untilFocused.getContainingWindow();
+                    // Check that the component awaiting focus belongs to
+                    // the current focused window. See 8015454.
+                    if (toplevel != null && toplevel.isFocused()) {
+                        SunToolkit.postEvent(AppContext.getAppContext(), new SequencedEvent(e));
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * This method is called by the AWT event dispatcher requesting that the
+     * current KeyboardFocusManager dispatch the specified event on its behalf.
+     * DefaultKeyboardFocusManagers dispatch all FocusEvents, all WindowEvents
+     * related to focus, and all KeyEvents. These events are dispatched based
+     * on the KeyboardFocusManager's notion of the focus owner and the focused
+     * and active Windows, sometimes overriding the source of the specified
+     * AWTEvent. If this method returns <code>false</code>, then the AWT event
+     * dispatcher will attempt to dispatch the event itself.
+     *
+     * @param e the AWTEvent to be dispatched
+     * @return <code>true</code> if this method dispatched the event;
+     *         <code>false</code> otherwise
+     */
+    public boolean dispatchEvent(AWTEvent e) {
+        if (focusLog.isLoggable(PlatformLogger.Level.FINE) && (e instanceof WindowEvent || e instanceof FocusEvent)) {
+            focusLog.fine("" + e);
+        }
+        switch (e.getID()) {
+            case WindowEvent.WINDOW_GAINED_FOCUS: {
+                if (repostIfFollowsKeyEvents((WindowEvent)e)) {
+                    break;
+                }
+
+                WindowEvent we = (WindowEvent)e;
+                Window oldFocusedWindow = getGlobalFocusedWindow();
+                Window newFocusedWindow = we.getWindow();
+                if (newFocusedWindow == oldFocusedWindow) {
+                    break;
+                }
+
+                if (!(newFocusedWindow.isFocusableWindow()
+                      && newFocusedWindow.isVisible()
+                      && newFocusedWindow.isDisplayable()))
+                {
+                    // we can not accept focus on such window, so reject it.
+                    restoreFocus(we);
+                    break;
+                }
+                // If there exists a current focused window, then notify it
+                // that it has lost focus.
+                if (oldFocusedWindow != null) {
+                    boolean isEventDispatched =
+                        sendMessage(oldFocusedWindow,
+                                new WindowEvent(oldFocusedWindow,
+                                                WindowEvent.WINDOW_LOST_FOCUS,
+                                                newFocusedWindow));
+                    // Failed to dispatch, clear by ourselfves
+                    if (!isEventDispatched) {
+                        setGlobalFocusOwner(null);
+                        setGlobalFocusedWindow(null);
+                    }
+                }
+
+                // Because the native libraries do not post WINDOW_ACTIVATED
+                // events, we need to synthesize one if the active Window
+                // changed.
+                Window newActiveWindow =
+                    getOwningFrameDialog(newFocusedWindow);
+                Window currentActiveWindow = getGlobalActiveWindow();
+                if (newActiveWindow != currentActiveWindow) {
+                    sendMessage(newActiveWindow,
+                                new WindowEvent(newActiveWindow,
+                                                WindowEvent.WINDOW_ACTIVATED,
+                                                currentActiveWindow));
+                    if (newActiveWindow != getGlobalActiveWindow()) {
+                        // Activation change was rejected. Unlikely, but
+                        // possible.
+                        restoreFocus(we);
+                        break;
+                    }
+                }
+
+                setGlobalFocusedWindow(newFocusedWindow);
+
+                if (newFocusedWindow != getGlobalFocusedWindow()) {
+                    // Focus change was rejected. Will happen if
+                    // newFocusedWindow is not a focusable Window.
+                    restoreFocus(we);
+                    break;
+                }
+
+                // Restore focus to the Component which last held it. We do
+                // this here so that client code can override our choice in
+                // a WINDOW_GAINED_FOCUS handler.
+                //
+                // Make sure that the focus change request doesn't change the
+                // focused Window in case we are no longer the focused Window
+                // when the request is handled.
+                if (inSendMessage == 0) {
+                    // Identify which Component should initially gain focus
+                    // in the Window.
+                    //
+                    // * If we're in SendMessage, then this is a synthetic
+                    //   WINDOW_GAINED_FOCUS message which was generated by a
+                    //   the FOCUS_GAINED handler. Allow the Component to
+                    //   which the FOCUS_GAINED message was targeted to
+                    //   receive the focus.
+                    // * Otherwise, look up the correct Component here.
+                    //   We don't use Window.getMostRecentFocusOwner because
+                    //   window is focused now and 'null' will be returned
+
+
+                    // Calculating of most recent focus owner and focus
+                    // request should be synchronized on KeyboardFocusManager.class
+                    // to prevent from thread race when user will request
+                    // focus between calculation and our request.
+                    // But if focus transfer is synchronous, this synchronization
+                    // may cause deadlock, thus we don't synchronize this block.
+                    Component toFocus = KeyboardFocusManager.
+                        getMostRecentFocusOwner(newFocusedWindow);
+                    if ((toFocus == null) &&
+                        newFocusedWindow.isFocusableWindow())
+                    {
+                        toFocus = newFocusedWindow.getFocusTraversalPolicy().
+                            getInitialComponent(newFocusedWindow);
+                    }
+                    Component tempLost = null;
+                    synchronized(KeyboardFocusManager.class) {
+                        tempLost = newFocusedWindow.setTemporaryLostComponent(null);
+                    }
+
+                    // The component which last has the focus when this window was focused
+                    // should receive focus first
+                    if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
+                        focusLog.finer("tempLost {0}, toFocus {1}",
+                                       tempLost, toFocus);
+                    }
+                    if (tempLost != null) {
+                        tempLost.requestFocusInWindow(CausedFocusEvent.Cause.ACTIVATION);
+                    }
+
+                    if (toFocus != null && toFocus != tempLost) {
+                        // If there is a component which requested focus when this window
+                        // was inactive it expects to receive focus after activation.
+                        toFocus.requestFocusInWindow(CausedFocusEvent.Cause.ACTIVATION);
+                    }
+                }
+
+                Window realOppositeWindow = this.realOppositeWindowWR.get();
+                if (realOppositeWindow != we.getOppositeWindow()) {
+                    we = new WindowEvent(newFocusedWindow,
+                                         WindowEvent.WINDOW_GAINED_FOCUS,
+                                         realOppositeWindow);
+                }
+                return typeAheadAssertions(newFocusedWindow, we);
+            }
+
+            case WindowEvent.WINDOW_ACTIVATED: {
+                WindowEvent we = (WindowEvent)e;
+                Window oldActiveWindow = getGlobalActiveWindow();
+                Window newActiveWindow = we.getWindow();
+                if (oldActiveWindow == newActiveWindow) {
+                    break;
+                }
+
+                // If there exists a current active window, then notify it that
+                // it has lost activation.
+                if (oldActiveWindow != null) {
+                    boolean isEventDispatched =
+                        sendMessage(oldActiveWindow,
+                                new WindowEvent(oldActiveWindow,
+                                                WindowEvent.WINDOW_DEACTIVATED,
+                                                newActiveWindow));
+                    // Failed to dispatch, clear by ourselfves
+                    if (!isEventDispatched) {
+                        setGlobalActiveWindow(null);
+                    }
+                    if (getGlobalActiveWindow() != null) {
+                        // Activation change was rejected. Unlikely, but
+                        // possible.
+                        break;
+                    }
+                }
+
+                setGlobalActiveWindow(newActiveWindow);
+
+                if (newActiveWindow != getGlobalActiveWindow()) {
+                    // Activation change was rejected. Unlikely, but
+                    // possible.
+                    break;
+                }
+
+                return typeAheadAssertions(newActiveWindow, we);
+            }
+
+            case FocusEvent.FOCUS_GAINED: {
+                FocusEvent fe = (FocusEvent)e;
+                CausedFocusEvent.Cause cause = (fe instanceof CausedFocusEvent) ?
+                    ((CausedFocusEvent)fe).getCause() : CausedFocusEvent.Cause.UNKNOWN;
+                Component oldFocusOwner = getGlobalFocusOwner();
+                Component newFocusOwner = fe.getComponent();
+                if (oldFocusOwner == newFocusOwner) {
+                    if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
+                        focusLog.fine("Skipping {0} because focus owner is the same", e);
+                    }
+                    // We can't just drop the event - there could be
+                    // type-ahead markers associated with it.
+                    dequeueKeyEvents(-1, newFocusOwner);
+                    break;
+                }
+
+                // If there exists a current focus owner, then notify it that
+                // it has lost focus.
+                if (oldFocusOwner != null) {
+                    boolean isEventDispatched =
+                        sendMessage(oldFocusOwner,
+                                    new CausedFocusEvent(oldFocusOwner,
+                                                   FocusEvent.FOCUS_LOST,
+                                                   fe.isTemporary(),
+                                                   newFocusOwner, cause));
+                    // Failed to dispatch, clear by ourselfves
+                    if (!isEventDispatched) {
+                        setGlobalFocusOwner(null);
+                        if (!fe.isTemporary()) {
+                            setGlobalPermanentFocusOwner(null);
+                        }
+                    }
+                }
+
+                // Because the native windowing system has a different notion
+                // of the current focus and activation states, it is possible
+                // that a Component outside of the focused Window receives a
+                // FOCUS_GAINED event. We synthesize a WINDOW_GAINED_FOCUS
+                // event in that case.
+                final Window newFocusedWindow = SunToolkit.getContainingWindow(newFocusOwner);
+                final Window currentFocusedWindow = getGlobalFocusedWindow();
+                if (newFocusedWindow != null &&
+                    newFocusedWindow != currentFocusedWindow)
+                {
+                    sendMessage(newFocusedWindow,
+                                new WindowEvent(newFocusedWindow,
+                                        WindowEvent.WINDOW_GAINED_FOCUS,
+                                                currentFocusedWindow));
+                    if (newFocusedWindow != getGlobalFocusedWindow()) {
+                        // Focus change was rejected. Will happen if
+                        // newFocusedWindow is not a focusable Window.
+
+                        // Need to recover type-ahead, but don't bother
+                        // restoring focus. That was done by the
+                        // WINDOW_GAINED_FOCUS handler
+                        dequeueKeyEvents(-1, newFocusOwner);
+                        break;
+                    }
+                }
+
+                if (!(newFocusOwner.isFocusable() && newFocusOwner.isShowing() &&
+                    // Refuse focus on a disabled component if the focus event
+                    // isn't of UNKNOWN reason (i.e. not a result of a direct request
+                    // but traversal, activation or system generated).
+                    (newFocusOwner.isEnabled() || cause.equals(CausedFocusEvent.Cause.UNKNOWN))))
+                {
+                    // we should not accept focus on such component, so reject it.
+                    dequeueKeyEvents(-1, newFocusOwner);
+                    if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
+                        // If FOCUS_GAINED is for a disposed component (however
+                        // it shouldn't happen) its toplevel parent is null. In this
+                        // case we have to try to restore focus in the current focused
+                        // window (for the details: 6607170).
+                        if (newFocusedWindow == null) {
+                            restoreFocus(fe, currentFocusedWindow);
+                        } else {
+                            restoreFocus(fe, newFocusedWindow);
+                        }
+                        setMostRecentFocusOwner(newFocusedWindow, null); // see: 8013773
+                    }
+                    break;
+                }
+
+                setGlobalFocusOwner(newFocusOwner);
+
+                if (newFocusOwner != getGlobalFocusOwner()) {
+                    // Focus change was rejected. Will happen if
+                    // newFocusOwner is not focus traversable.
+                    dequeueKeyEvents(-1, newFocusOwner);
+                    if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
+                        restoreFocus(fe, newFocusedWindow);
+                    }
+                    break;
+                }
+
+                if (!fe.isTemporary()) {
+                    setGlobalPermanentFocusOwner(newFocusOwner);
+
+                    if (newFocusOwner != getGlobalPermanentFocusOwner()) {
+                        // Focus change was rejected. Unlikely, but possible.
+                        dequeueKeyEvents(-1, newFocusOwner);
+                        if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
+                            restoreFocus(fe, newFocusedWindow);
+                        }
+                        break;
+                    }
+                }
+
+                setNativeFocusOwner(getHeavyweight(newFocusOwner));
+
+                Component realOppositeComponent = this.realOppositeComponentWR.get();
+                if (realOppositeComponent != null &&
+                    realOppositeComponent != fe.getOppositeComponent()) {
+                    fe = new CausedFocusEvent(newFocusOwner,
+                                        FocusEvent.FOCUS_GAINED,
+                                        fe.isTemporary(),
+                                        realOppositeComponent, cause);
+                    ((AWTEvent) fe).isPosted = true;
+                }
+                return typeAheadAssertions(newFocusOwner, fe);
+            }
+
+            case FocusEvent.FOCUS_LOST: {
+                FocusEvent fe = (FocusEvent)e;
+                Component currentFocusOwner = getGlobalFocusOwner();
+                if (currentFocusOwner == null) {
+                    if (focusLog.isLoggable(PlatformLogger.Level.FINE))
+                        focusLog.fine("Skipping {0} because focus owner is null", e);
+                    break;
+                }
+                // Ignore cases where a Component loses focus to itself.
+                // If we make a mistake because of retargeting, then the
+                // FOCUS_GAINED handler will correct it.
+                if (currentFocusOwner == fe.getOppositeComponent()) {
+                    if (focusLog.isLoggable(PlatformLogger.Level.FINE))
+                        focusLog.fine("Skipping {0} because current focus owner is equal to opposite", e);
+                    break;
+                }
+
+                setGlobalFocusOwner(null);
+
+                if (getGlobalFocusOwner() != null) {
+                    // Focus change was rejected. Unlikely, but possible.
+                    restoreFocus(currentFocusOwner, true);
+                    break;
+                }
+
+                if (!fe.isTemporary()) {
+                    setGlobalPermanentFocusOwner(null);
+
+                    if (getGlobalPermanentFocusOwner() != null) {
+                        // Focus change was rejected. Unlikely, but possible.
+                        restoreFocus(currentFocusOwner, true);
+                        break;
+                    }
+                } else {
+                    Window owningWindow = currentFocusOwner.getContainingWindow();
+                    if (owningWindow != null) {
+                        owningWindow.setTemporaryLostComponent(currentFocusOwner);
+                    }
+                }
+
+                setNativeFocusOwner(null);
+
+                fe.setSource(currentFocusOwner);
+
+                realOppositeComponentWR = (fe.getOppositeComponent() != null)
+                    ? new WeakReference<Component>(currentFocusOwner)
+                    : NULL_COMPONENT_WR;
+
+                return typeAheadAssertions(currentFocusOwner, fe);
+            }
+
+            case WindowEvent.WINDOW_DEACTIVATED: {
+                WindowEvent we = (WindowEvent)e;
+                Window currentActiveWindow = getGlobalActiveWindow();
+                if (currentActiveWindow == null) {
+                    break;
+                }
+
+                if (currentActiveWindow != e.getSource()) {
+                    // The event is lost in time.
+                    // Allow listeners to precess the event but do not
+                    // change any global states
+                    break;
+                }
+
+                setGlobalActiveWindow(null);
+                if (getGlobalActiveWindow() != null) {
+                    // Activation change was rejected. Unlikely, but possible.
+                    break;
+                }
+
+                we.setSource(currentActiveWindow);
+                return typeAheadAssertions(currentActiveWindow, we);
+            }
+
+            case WindowEvent.WINDOW_LOST_FOCUS: {
+                if (repostIfFollowsKeyEvents((WindowEvent)e)) {
+                    break;
+                }
+
+                WindowEvent we = (WindowEvent)e;
+                Window currentFocusedWindow = getGlobalFocusedWindow();
+                Window losingFocusWindow = we.getWindow();
+                Window activeWindow = getGlobalActiveWindow();
+                Window oppositeWindow = we.getOppositeWindow();
+                if (focusLog.isLoggable(PlatformLogger.Level.FINE))
+                    focusLog.fine("Active {0}, Current focused {1}, losing focus {2} opposite {3}",
+                                  activeWindow, currentFocusedWindow,
+                                  losingFocusWindow, oppositeWindow);
+                if (currentFocusedWindow == null) {
+                    break;
+                }
+
+                // Special case -- if the native windowing system posts an
+                // event claiming that the active Window has lost focus to the
+                // focused Window, then discard the event. This is an artifact
+                // of the native windowing system not knowing which Window is
+                // really focused.
+                if (inSendMessage == 0 && losingFocusWindow == activeWindow &&
+                    oppositeWindow == currentFocusedWindow)
+                {
+                    break;
+                }
+
+                Component currentFocusOwner = getGlobalFocusOwner();
+                if (currentFocusOwner != null) {
+                    // The focus owner should always receive a FOCUS_LOST event
+                    // before the Window is defocused.
+                    Component oppositeComp = null;
+                    if (oppositeWindow != null) {
+                        oppositeComp = oppositeWindow.getTemporaryLostComponent();
+                        if (oppositeComp == null) {
+                            oppositeComp = oppositeWindow.getMostRecentFocusOwner();
+                        }
+                    }
+                    if (oppositeComp == null) {
+                        oppositeComp = oppositeWindow;
+                    }
+                    sendMessage(currentFocusOwner,
+                                new CausedFocusEvent(currentFocusOwner,
+                                               FocusEvent.FOCUS_LOST,
+                                               true,
+                                               oppositeComp, CausedFocusEvent.Cause.ACTIVATION));
+                }
+
+                setGlobalFocusedWindow(null);
+                if (getGlobalFocusedWindow() != null) {
+                    // Focus change was rejected. Unlikely, but possible.
+                    restoreFocus(currentFocusedWindow, null, true);
+                    break;
+                }
+
+                we.setSource(currentFocusedWindow);
+                realOppositeWindowWR = (oppositeWindow != null)
+                    ? new WeakReference<Window>(currentFocusedWindow)
+                    : NULL_WINDOW_WR;
+                typeAheadAssertions(currentFocusedWindow, we);
+
+                if (oppositeWindow == null) {
+                    // Then we need to deactive the active Window as well.
+                    // No need to synthesize in other cases, because
+                    // WINDOW_ACTIVATED will handle it if necessary.
+                    sendMessage(activeWindow,
+                                new WindowEvent(activeWindow,
+                                                WindowEvent.WINDOW_DEACTIVATED,
+                                                null));
+                    if (getGlobalActiveWindow() != null) {
+                        // Activation change was rejected. Unlikely,
+                        // but possible.
+                        restoreFocus(currentFocusedWindow, null, true);
+                    }
+                }
+                break;
+            }
+
+            case KeyEvent.KEY_TYPED:
+            case KeyEvent.KEY_PRESSED:
+            case KeyEvent.KEY_RELEASED:
+                return typeAheadAssertions(null, e);
+
+            default:
+                return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Called by <code>dispatchEvent</code> if no other
+     * KeyEventDispatcher in the dispatcher chain dispatched the KeyEvent, or
+     * if no other KeyEventDispatchers are registered. If the event has not
+     * been consumed, its target is enabled, and the focus owner is not null,
+     * this method dispatches the event to its target. This method will also
+     * subsequently dispatch the event to all registered
+     * KeyEventPostProcessors. After all this operations are finished,
+     * the event is passed to peers for processing.
+     * <p>
+     * In all cases, this method returns <code>true</code>, since
+     * DefaultKeyboardFocusManager is designed so that neither
+     * <code>dispatchEvent</code>, nor the AWT event dispatcher, should take
+     * further action on the event in any situation.
+     *
+     * @param e the KeyEvent to be dispatched
+     * @return <code>true</code>
+     * @see Component#dispatchEvent
+     */
+    public boolean dispatchKeyEvent(KeyEvent e) {
+        Component focusOwner = (((AWTEvent)e).isPosted) ? getFocusOwner() : e.getComponent();
+
+        if (focusOwner != null && focusOwner.isShowing() && focusOwner.canBeFocusOwner()) {
+            if (!e.isConsumed()) {
+                Component comp = e.getComponent();
+                if (comp != null && comp.isEnabled()) {
+                    redispatchEvent(comp, e);
+                }
+            }
+        }
+        boolean stopPostProcessing = false;
+        java.util.List<KeyEventPostProcessor> processors = getKeyEventPostProcessors();
+        if (processors != null) {
+            for (java.util.Iterator<KeyEventPostProcessor> iter = processors.iterator();
+                 !stopPostProcessing && iter.hasNext(); )
+            {
+                stopPostProcessing = iter.next().
+                            postProcessKeyEvent(e);
+            }
+        }
+        if (!stopPostProcessing) {
+            postProcessKeyEvent(e);
+        }
+
+        // Allow the peer to process KeyEvent
+        Component source = e.getComponent();
+        ComponentPeer peer = source.getPeer();
+
+        if (peer == null || peer instanceof LightweightPeer) {
+            // if focus owner is lightweight then its native container
+            // processes event
+            Container target = source.getNativeContainer();
+            if (target != null) {
+                peer = target.getPeer();
+            }
+        }
+        if (peer != null) {
+            peer.handleEvent(e);
+        }
+
+        return true;
+    }
+
+    /**
+     * This method will be called by <code>dispatchKeyEvent</code>. It will
+     * handle any unconsumed KeyEvents that map to an AWT
+     * <code>MenuShortcut</code> by consuming the event and activating the
+     * shortcut.
+     *
+     * @param e the KeyEvent to post-process
+     * @return <code>true</code>
+     * @see #dispatchKeyEvent
+     * @see MenuShortcut
+     */
+    public boolean postProcessKeyEvent(KeyEvent e) {
+        if (!e.isConsumed()) {
+            Component target = e.getComponent();
+            Container p = (Container)
+                (target instanceof Container ? target : target.getParent());
+            if (p != null) {
+                p.postProcessKeyEvent(e);
+            }
+        }
+        return true;
+    }
+
+    private void pumpApprovedKeyEvents() {
+        KeyEvent ke;
+        do {
+            ke = null;
+            synchronized (this) {
+                if (enqueuedKeyEvents.size() != 0) {
+                    ke = enqueuedKeyEvents.getFirst();
+                    if (typeAheadMarkers.size() != 0) {
+                        TypeAheadMarker marker = typeAheadMarkers.getFirst();
+                        // Fixed 5064013: may appears that the events have the same time
+                        // if (ke.getWhen() >= marker.after) {
+                        // The fix is rolled out.
+
+                        if (ke.getWhen() > marker.after) {
+                            ke = null;
+                        }
+                    }
+                    if (ke != null) {
+                        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
+                            focusLog.finer("Pumping approved event {0}", ke);
+                        }
+                        enqueuedKeyEvents.removeFirst();
+                    }
+                }
+            }
+            if (ke != null) {
+                preDispatchKeyEvent(ke);
+            }
+        } while (ke != null);
+    }
+
+    /**
+     * Dumps the list of type-ahead queue markers to stderr
+     */
+    void dumpMarkers() {
+        if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
+            focusLog.finest(">>> Markers dump, time: {0}", System.currentTimeMillis());
+            synchronized (this) {
+                if (typeAheadMarkers.size() != 0) {
+                    Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator();
+                    while (iter.hasNext()) {
+                        TypeAheadMarker marker = iter.next();
+                        focusLog.finest("    {0}", marker);
+                    }
+                }
+            }
+        }
+    }
+
+    private boolean typeAheadAssertions(Component target, AWTEvent e) {
+
+        // Clear any pending events here as well as in the FOCUS_GAINED
+        // handler. We need this call here in case a marker was removed in
+        // response to a call to dequeueKeyEvents.
+        pumpApprovedKeyEvents();
+
+        switch (e.getID()) {
+            case KeyEvent.KEY_TYPED:
+            case KeyEvent.KEY_PRESSED:
+            case KeyEvent.KEY_RELEASED: {
+                KeyEvent ke = (KeyEvent)e;
+                synchronized (this) {
+                    if (e.isPosted && typeAheadMarkers.size() != 0) {
+                        TypeAheadMarker marker = typeAheadMarkers.getFirst();
+                        // Fixed 5064013: may appears that the events have the same time
+                        // if (ke.getWhen() >= marker.after) {
+                        // The fix is rolled out.
+
+                        if (ke.getWhen() > marker.after) {
+                            if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
+                                focusLog.finer("Storing event {0} because of marker {1}", ke, marker);
+                            }
+                            enqueuedKeyEvents.addLast(ke);
+                            return true;
+                        }
+                    }
+                }
+
+                // KeyEvent was posted before focus change request
+                return preDispatchKeyEvent(ke);
+            }
+
+            case FocusEvent.FOCUS_GAINED:
+                if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
+                    focusLog.finest("Markers before FOCUS_GAINED on {0}", target);
+                }
+                dumpMarkers();
+                // Search the marker list for the first marker tied to
+                // the Component which just gained focus. Then remove
+                // that marker, any markers which immediately follow
+                // and are tied to the same component, and all markers
+                // that preceed it. This handles the case where
+                // multiple focus requests were made for the same
+                // Component in a row and when we lost some of the
+                // earlier requests. Since FOCUS_GAINED events will
+                // not be generated for these additional requests, we
+                // need to clear those markers too.
+                synchronized (this) {
+                    boolean found = false;
+                    if (hasMarker(target)) {
+                        for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator();
+                             iter.hasNext(); )
+                        {
+                            if (iter.next().untilFocused == target) {
+                                found = true;
+                            } else if (found) {
+                                break;
+                            }
+                            iter.remove();
+                        }
+                    } else {
+                        // Exception condition - event without marker
+                        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
+                            focusLog.finer("Event without marker {0}", e);
+                        }
+                    }
+                }
+                focusLog.finest("Markers after FOCUS_GAINED");
+                dumpMarkers();
+
+                redispatchEvent(target, e);
+
+                // Now, dispatch any pending KeyEvents which have been
+                // released because of the FOCUS_GAINED event so that we don't
+                // have to wait for another event to be posted to the queue.
+                pumpApprovedKeyEvents();
+                return true;
+
+            default:
+                redispatchEvent(target, e);
+                return true;
+        }
+    }
+
+    /**
+     * Returns true if there are some marker associated with component <code>comp</code>
+     * in a markers' queue
+     * @since 1.5
+     */
+    private boolean hasMarker(Component comp) {
+        for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
+            if (iter.next().untilFocused == comp) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Clears markers queue
+     * @since 1.5
+     */
+    void clearMarkers() {
+        synchronized(this) {
+            typeAheadMarkers.clear();
+        }
+    }
+
+    private boolean preDispatchKeyEvent(KeyEvent ke) {
+        if (((AWTEvent) ke).isPosted) {
+            Component focusOwner = getFocusOwner();
+            ke.setSource(((focusOwner != null) ? focusOwner : getFocusedWindow()));
+        }
+        if (ke.getSource() == null) {
+            return true;
+        }
+
+        // Explicitly set the key event timestamp here (not in Component.dispatchEventImpl):
+        // - A key event is anyway passed to this method which starts its actual dispatching.
+        // - If a key event is put to the type ahead queue, its time stamp should not be registered
+        //   until its dispatching actually starts (by this method).
+        EventQueue.setCurrentEventAndMostRecentTime(ke);
+
+        /**
+         * Fix for 4495473.
+         * This fix allows to correctly dispatch events when native
+         * event proxying mechanism is active.
+         * If it is active we should redispatch key events after
+         * we detected its correct target.
+         */
+        if (KeyboardFocusManager.isProxyActive(ke)) {
+            Component source = (Component)ke.getSource();
+            Container target = source.getNativeContainer();
+            if (target != null) {
+                ComponentPeer peer = target.getPeer();
+                if (peer != null) {
+                    peer.handleEvent(ke);
+                    /**
+                     * Fix for 4478780 - consume event after it was dispatched by peer.
+                     */
+                    ke.consume();
+                }
+            }
+            return true;
+        }
+
+        java.util.List<KeyEventDispatcher> dispatchers = getKeyEventDispatchers();
+        if (dispatchers != null) {
+            for (java.util.Iterator<KeyEventDispatcher> iter = dispatchers.iterator();
+                 iter.hasNext(); )
+             {
+                 if (iter.next().
+                     dispatchKeyEvent(ke))
+                 {
+                     return true;
+                 }
+             }
+        }
+        return dispatchKeyEvent(ke);
+    }
+
+    /*
+     * @param e is a KEY_PRESSED event that can be used
+     *          to track the next KEY_TYPED related.
+     */
+    private void consumeNextKeyTyped(KeyEvent e) {
+        consumeNextKeyTyped = true;
+    }
+
+    private void consumeTraversalKey(KeyEvent e) {
+        e.consume();
+        consumeNextKeyTyped = (e.getID() == KeyEvent.KEY_PRESSED) &&
+                              !e.isActionKey();
+    }
+
+    /*
+     * return true if event was consumed
+     */
+    private boolean consumeProcessedKeyEvent(KeyEvent e) {
+        if ((e.getID() == KeyEvent.KEY_TYPED) && consumeNextKeyTyped) {
+            e.consume();
+            consumeNextKeyTyped = false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * This method initiates a focus traversal operation if and only if the
+     * KeyEvent represents a focus traversal key for the specified
+     * focusedComponent. It is expected that focusedComponent is the current
+     * focus owner, although this need not be the case. If it is not,
+     * focus traversal will nevertheless proceed as if focusedComponent
+     * were the focus owner.
+     *
+     * @param focusedComponent the Component that is the basis for a focus
+     *        traversal operation if the specified event represents a focus
+     *        traversal key for the Component
+     * @param e the event that may represent a focus traversal key
+     */
+    public void processKeyEvent(Component focusedComponent, KeyEvent e) {
+        // consume processed event if needed
+        if (consumeProcessedKeyEvent(e)) {
+            return;
+        }
+
+        // KEY_TYPED events cannot be focus traversal keys
+        if (e.getID() == KeyEvent.KEY_TYPED) {
+            return;
+        }
+
+        if (focusedComponent.getFocusTraversalKeysEnabled() &&
+            !e.isConsumed())
+        {
+            AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e),
+                oppStroke = AWTKeyStroke.getAWTKeyStroke(stroke.getKeyCode(),
+                                                 stroke.getModifiers(),
+                                                 !stroke.isOnKeyRelease());
+            Set<AWTKeyStroke> toTest;
+            boolean contains, containsOpp;
+
+            toTest = focusedComponent.getFocusTraversalKeys(
+                KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
+            contains = toTest.contains(stroke);
+            containsOpp = toTest.contains(oppStroke);
+
+            if (contains || containsOpp) {
+                consumeTraversalKey(e);
+                if (contains) {
+                    focusNextComponent(focusedComponent);
+                }
+                return;
+            } else if (e.getID() == KeyEvent.KEY_PRESSED) {
+                // Fix for 6637607: consumeNextKeyTyped should be reset.
+                consumeNextKeyTyped = false;
+            }
+
+            toTest = focusedComponent.getFocusTraversalKeys(
+                KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
+            contains = toTest.contains(stroke);
+            containsOpp = toTest.contains(oppStroke);
+
+            if (contains || containsOpp) {
+                consumeTraversalKey(e);
+                if (contains) {
+                    focusPreviousComponent(focusedComponent);
+                }
+                return;
+            }
+
+            toTest = focusedComponent.getFocusTraversalKeys(
+                KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
+            contains = toTest.contains(stroke);
+            containsOpp = toTest.contains(oppStroke);
+
+            if (contains || containsOpp) {
+                consumeTraversalKey(e);
+                if (contains) {
+                    upFocusCycle(focusedComponent);
+                }
+                return;
+            }
+
+            if (!((focusedComponent instanceof Container) &&
+                  ((Container)focusedComponent).isFocusCycleRoot())) {
+                return;
+            }
+
+            toTest = focusedComponent.getFocusTraversalKeys(
+                KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
+            contains = toTest.contains(stroke);
+            containsOpp = toTest.contains(oppStroke);
+
+            if (contains || containsOpp) {
+                consumeTraversalKey(e);
+                if (contains) {
+                    downFocusCycle((Container)focusedComponent);
+                }
+            }
+        }
+    }
+
+    /**
+     * Delays dispatching of KeyEvents until the specified Component becomes
+     * the focus owner. KeyEvents with timestamps later than the specified
+     * timestamp will be enqueued until the specified Component receives a
+     * FOCUS_GAINED event, or the AWT cancels the delay request by invoking
+     * <code>dequeueKeyEvents</code> or <code>discardKeyEvents</code>.
+     *
+     * @param after timestamp of current event, or the current, system time if
+     *        the current event has no timestamp, or the AWT cannot determine
+     *        which event is currently being handled
+     * @param untilFocused Component which will receive a FOCUS_GAINED event
+     *        before any pending KeyEvents
+     * @see #dequeueKeyEvents
+     * @see #discardKeyEvents
+     */
+    protected synchronized void enqueueKeyEvents(long after,
+                                                 Component untilFocused) {
+        if (untilFocused == null) {
+            return;
+        }
+
+        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
+            focusLog.finer("Enqueue at {0} for {1}",
+                       after, untilFocused);
+        }
+
+        int insertionIndex = 0,
+            i = typeAheadMarkers.size();
+        ListIterator<TypeAheadMarker> iter = typeAheadMarkers.listIterator(i);
+
+        for (; i > 0; i--) {
+            TypeAheadMarker marker = iter.previous();
+            if (marker.after <= after) {
+                insertionIndex = i;
+                break;
+            }
+        }
+
+        typeAheadMarkers.add(insertionIndex,
+                             new TypeAheadMarker(after, untilFocused));
+    }
+
+    /**
+     * Releases for normal dispatching to the current focus owner all
+     * KeyEvents which were enqueued because of a call to
+     * <code>enqueueKeyEvents</code> with the same timestamp and Component.
+     * If the given timestamp is less than zero, the outstanding enqueue
+     * request for the given Component with the <b>oldest</b> timestamp (if
+     * any) should be cancelled.
+     *
+     * @param after the timestamp specified in the call to
+     *        <code>enqueueKeyEvents</code>, or any value &lt; 0
+     * @param untilFocused the Component specified in the call to
+     *        <code>enqueueKeyEvents</code>
+     * @see #enqueueKeyEvents
+     * @see #discardKeyEvents
+     */
+    protected synchronized void dequeueKeyEvents(long after,
+                                                 Component untilFocused) {
+        if (untilFocused == null) {
+            return;
+        }
+
+        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
+            focusLog.finer("Dequeue at {0} for {1}",
+                       after, untilFocused);
+        }
+
+        TypeAheadMarker marker;
+        ListIterator<TypeAheadMarker> iter = typeAheadMarkers.listIterator
+            ((after >= 0) ? typeAheadMarkers.size() : 0);
+
+        if (after < 0) {
+            while (iter.hasNext()) {
+                marker = iter.next();
+                if (marker.untilFocused == untilFocused)
+                {
+                    iter.remove();
+                    return;
+                }
+            }
+        } else {
+            while (iter.hasPrevious()) {
+                marker = iter.previous();
+                if (marker.untilFocused == untilFocused &&
+                    marker.after == after)
+                {
+                    iter.remove();
+                    return;
+                }
+            }
+        }
+    }
+
+    /**
+     * Discards all KeyEvents which were enqueued because of one or more calls
+     * to <code>enqueueKeyEvents</code> with the specified Component, or one of
+     * its descendants.
+     *
+     * @param comp the Component specified in one or more calls to
+     *        <code>enqueueKeyEvents</code>, or a parent of such a Component
+     * @see #enqueueKeyEvents
+     * @see #dequeueKeyEvents
+     */
+    protected synchronized void discardKeyEvents(Component comp) {
+        if (comp == null) {
+            return;
+        }
+
+        long start = -1;
+
+        for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
+            TypeAheadMarker marker = iter.next();
+            Component toTest = marker.untilFocused;
+            boolean match = (toTest == comp);
+            while (!match && toTest != null && !(toTest instanceof Window)) {
+                toTest = toTest.getParent();
+                match = (toTest == comp);
+            }
+            if (match) {
+                if (start < 0) {
+                    start = marker.after;
+                }
+                iter.remove();
+            } else if (start >= 0) {
+                purgeStampedEvents(start, marker.after);
+                start = -1;
+            }
+        }
+
+        purgeStampedEvents(start, -1);
+    }
+
+    // Notes:
+    //   * must be called inside a synchronized block
+    //   * if 'start' is < 0, then this function does nothing
+    //   * if 'end' is < 0, then all KeyEvents from 'start' to the end of the
+    //     queue will be removed
+    private void purgeStampedEvents(long start, long end) {
+        if (start < 0) {
+            return;
+        }
+
+        for (Iterator<KeyEvent> iter = enqueuedKeyEvents.iterator(); iter.hasNext(); ) {
+            KeyEvent ke = iter.next();
+            long time = ke.getWhen();
+
+            if (start < time && (end < 0 || time <= end)) {
+                iter.remove();
+            }
+
+            if (end >= 0 && time > end) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Focuses the Component before aComponent, typically based on a
+     * FocusTraversalPolicy.
+     *
+     * @param aComponent the Component that is the basis for the focus
+     *        traversal operation
+     * @see FocusTraversalPolicy
+     * @see Component#transferFocusBackward
+     */
+    public void focusPreviousComponent(Component aComponent) {
+        if (aComponent != null) {
+            aComponent.transferFocusBackward();
+        }
+    }
+
+    /**
+     * Focuses the Component after aComponent, typically based on a
+     * FocusTraversalPolicy.
+     *
+     * @param aComponent the Component that is the basis for the focus
+     *        traversal operation
+     * @see FocusTraversalPolicy
+     * @see Component#transferFocus
+     */
+    public void focusNextComponent(Component aComponent) {
+        if (aComponent != null) {
+            aComponent.transferFocus();
+        }
+    }
+
+    /**
+     * Moves the focus up one focus traversal cycle. Typically, the focus owner
+     * is set to aComponent's focus cycle root, and the current focus cycle
+     * root is set to the new focus owner's focus cycle root. If, however,
+     * aComponent's focus cycle root is a Window, then the focus owner is set
+     * to the focus cycle root's default Component to focus, and the current
+     * focus cycle root is unchanged.
+     *
+     * @param aComponent the Component that is the basis for the focus
+     *        traversal operation
+     * @see Component#transferFocusUpCycle
+     */
+    public void upFocusCycle(Component aComponent) {
+        if (aComponent != null) {
+            aComponent.transferFocusUpCycle();
+        }
+    }
+
+    /**
+     * Moves the focus down one focus traversal cycle. If aContainer is a focus
+     * cycle root, then the focus owner is set to aContainer's default
+     * Component to focus, and the current focus cycle root is set to
+     * aContainer. If aContainer is not a focus cycle root, then no focus
+     * traversal operation occurs.
+     *
+     * @param aContainer the Container that is the basis for the focus
+     *        traversal operation
+     * @see Container#transferFocusDownCycle
+     */
+    public void downFocusCycle(Container aContainer) {
+        if (aContainer != null && aContainer.isFocusCycleRoot()) {
+            aContainer.transferFocusDownCycle();
+        }
+    }
+}