DefaultKeyboardFocusManager.java revision 13629:5bb70b2df494
1/*
2 * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package java.awt;
26
27import java.awt.event.FocusEvent;
28import java.awt.event.KeyEvent;
29import java.awt.event.WindowEvent;
30import java.awt.peer.ComponentPeer;
31import java.awt.peer.LightweightPeer;
32import java.lang.ref.WeakReference;
33import java.util.LinkedList;
34import java.util.Iterator;
35import java.util.ListIterator;
36import java.util.Set;
37
38import sun.util.logging.PlatformLogger;
39
40import sun.awt.AppContext;
41import sun.awt.SunToolkit;
42import sun.awt.AWTAccessor;
43import sun.awt.CausedFocusEvent;
44import sun.awt.TimedWindowEvent;
45
46/**
47 * The default KeyboardFocusManager for AWT applications. Focus traversal is
48 * done in response to a Component's focus traversal keys, and using a
49 * Container's FocusTraversalPolicy.
50 * <p>
51 * Please see
52 * <a href="http://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html">
53 * How to Use the Focus Subsystem</a>,
54 * a section in <em>The Java Tutorial</em>, and the
55 * <a href="../../java/awt/doc-files/FocusSpec.html">Focus Specification</a>
56 * for more information.
57 *
58 * @author David Mendenhall
59 *
60 * @see FocusTraversalPolicy
61 * @see Component#setFocusTraversalKeys
62 * @see Component#getFocusTraversalKeys
63 * @since 1.4
64 */
65public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
66    private static final PlatformLogger focusLog = PlatformLogger.getLogger("java.awt.focus.DefaultKeyboardFocusManager");
67
68    // null weak references to not create too many objects
69    private static final WeakReference<Window> NULL_WINDOW_WR =
70        new WeakReference<Window>(null);
71    private static final WeakReference<Component> NULL_COMPONENT_WR =
72        new WeakReference<Component>(null);
73    private WeakReference<Window> realOppositeWindowWR = NULL_WINDOW_WR;
74    private WeakReference<Component> realOppositeComponentWR = NULL_COMPONENT_WR;
75    private int inSendMessage;
76    private LinkedList<KeyEvent> enqueuedKeyEvents = new LinkedList<KeyEvent>();
77    private LinkedList<TypeAheadMarker> typeAheadMarkers = new LinkedList<TypeAheadMarker>();
78    private boolean consumeNextKeyTyped;
79
80    static {
81        AWTAccessor.setDefaultKeyboardFocusManagerAccessor(
82            new AWTAccessor.DefaultKeyboardFocusManagerAccessor() {
83                public void consumeNextKeyTyped(DefaultKeyboardFocusManager dkfm, KeyEvent e) {
84                    dkfm.consumeNextKeyTyped(e);
85                }
86            });
87    }
88
89    private static class TypeAheadMarker {
90        long after;
91        Component untilFocused;
92
93        TypeAheadMarker(long after, Component untilFocused) {
94            this.after = after;
95            this.untilFocused = untilFocused;
96        }
97        /**
98         * Returns string representation of the marker
99         */
100        public String toString() {
101            return ">>> Marker after " + after + " on " + untilFocused;
102        }
103    }
104
105    private Window getOwningFrameDialog(Window window) {
106        while (window != null && !(window instanceof Frame ||
107                                   window instanceof Dialog)) {
108            window = (Window)window.getParent();
109        }
110        return window;
111    }
112
113    /*
114     * This series of restoreFocus methods is used for recovering from a
115     * rejected focus or activation change. Rejections typically occur when
116     * the user attempts to focus a non-focusable Component or Window.
117     */
118    private void restoreFocus(FocusEvent fe, Window newFocusedWindow) {
119        Component realOppositeComponent = this.realOppositeComponentWR.get();
120        Component vetoedComponent = fe.getComponent();
121
122        if (newFocusedWindow != null && restoreFocus(newFocusedWindow,
123                                                     vetoedComponent, false))
124        {
125        } else if (realOppositeComponent != null &&
126                   doRestoreFocus(realOppositeComponent, vetoedComponent, false)) {
127        } else if (fe.getOppositeComponent() != null &&
128                   doRestoreFocus(fe.getOppositeComponent(), vetoedComponent, false)) {
129        } else {
130            clearGlobalFocusOwnerPriv();
131        }
132    }
133    private void restoreFocus(WindowEvent we) {
134        Window realOppositeWindow = this.realOppositeWindowWR.get();
135        if (realOppositeWindow != null
136            && restoreFocus(realOppositeWindow, null, false))
137        {
138            // do nothing, everything is done in restoreFocus()
139        } else if (we.getOppositeWindow() != null &&
140                   restoreFocus(we.getOppositeWindow(), null, false))
141        {
142            // do nothing, everything is done in restoreFocus()
143        } else {
144            clearGlobalFocusOwnerPriv();
145        }
146    }
147    private boolean restoreFocus(Window aWindow, Component vetoedComponent,
148                                 boolean clearOnFailure) {
149        Component toFocus =
150            KeyboardFocusManager.getMostRecentFocusOwner(aWindow);
151
152        if (toFocus != null && toFocus != vetoedComponent && doRestoreFocus(toFocus, vetoedComponent, false)) {
153            return true;
154        } else if (clearOnFailure) {
155            clearGlobalFocusOwnerPriv();
156            return true;
157        } else {
158            return false;
159        }
160    }
161    private boolean restoreFocus(Component toFocus, boolean clearOnFailure) {
162        return doRestoreFocus(toFocus, null, clearOnFailure);
163    }
164    private boolean doRestoreFocus(Component toFocus, Component vetoedComponent,
165                                   boolean clearOnFailure)
166    {
167        if (toFocus != vetoedComponent && toFocus.isShowing() && toFocus.canBeFocusOwner() &&
168            toFocus.requestFocus(false, CausedFocusEvent.Cause.ROLLBACK))
169        {
170            return true;
171        } else {
172            Component nextFocus = toFocus.getNextFocusCandidate();
173            if (nextFocus != null && nextFocus != vetoedComponent &&
174                nextFocus.requestFocusInWindow(CausedFocusEvent.Cause.ROLLBACK))
175            {
176                return true;
177            } else if (clearOnFailure) {
178                clearGlobalFocusOwnerPriv();
179                return true;
180            } else {
181                return false;
182            }
183        }
184    }
185
186    /**
187     * A special type of SentEvent which updates a counter in the target
188     * KeyboardFocusManager if it is an instance of
189     * DefaultKeyboardFocusManager.
190     */
191    private static class DefaultKeyboardFocusManagerSentEvent
192        extends SentEvent
193    {
194        /*
195         * serialVersionUID
196         */
197        private static final long serialVersionUID = -2924743257508701758L;
198
199        public DefaultKeyboardFocusManagerSentEvent(AWTEvent nested,
200                                                    AppContext toNotify) {
201            super(nested, toNotify);
202        }
203        public final void dispatch() {
204            KeyboardFocusManager manager =
205                KeyboardFocusManager.getCurrentKeyboardFocusManager();
206            DefaultKeyboardFocusManager defaultManager =
207                (manager instanceof DefaultKeyboardFocusManager)
208                ? (DefaultKeyboardFocusManager)manager
209                : null;
210
211            if (defaultManager != null) {
212                synchronized (defaultManager) {
213                    defaultManager.inSendMessage++;
214                }
215            }
216
217            super.dispatch();
218
219            if (defaultManager != null) {
220                synchronized (defaultManager) {
221                    defaultManager.inSendMessage--;
222                }
223            }
224        }
225    }
226
227    /**
228     * Sends a synthetic AWTEvent to a Component. If the Component is in
229     * the current AppContext, then the event is immediately dispatched.
230     * If the Component is in a different AppContext, then the event is
231     * posted to the other AppContext's EventQueue, and this method blocks
232     * until the event is handled or target AppContext is disposed.
233     * Returns true if successfully dispatched event, false if failed
234     * to dispatch.
235     */
236    static boolean sendMessage(Component target, AWTEvent e) {
237        e.isPosted = true;
238        AppContext myAppContext = AppContext.getAppContext();
239        final AppContext targetAppContext = target.appContext;
240        final SentEvent se =
241            new DefaultKeyboardFocusManagerSentEvent(e, myAppContext);
242
243        if (myAppContext == targetAppContext) {
244            se.dispatch();
245        } else {
246            if (targetAppContext.isDisposed()) {
247                return false;
248            }
249            SunToolkit.postEvent(targetAppContext, se);
250            if (EventQueue.isDispatchThread()) {
251                EventDispatchThread edt = (EventDispatchThread)
252                    Thread.currentThread();
253                edt.pumpEvents(SentEvent.ID, new Conditional() {
254                        public boolean evaluate() {
255                            return !se.dispatched && !targetAppContext.isDisposed();
256                        }
257                    });
258            } else {
259                synchronized (se) {
260                    while (!se.dispatched && !targetAppContext.isDisposed()) {
261                        try {
262                            se.wait(1000);
263                        } catch (InterruptedException ie) {
264                            break;
265                        }
266                    }
267                }
268            }
269        }
270        return se.dispatched;
271    }
272
273    /*
274     * Checks if the focus window event follows key events waiting in the type-ahead
275     * queue (if any). This may happen when a user types ahead in the window, the client
276     * listeners hang EDT for a while, and the user switches b/w toplevels. In that
277     * case the focus window events may be dispatched before the type-ahead events
278     * get handled. This may lead to wrong focus behavior and in order to avoid it,
279     * the focus window events are reposted to the end of the event queue. See 6981400.
280     */
281    private boolean repostIfFollowsKeyEvents(WindowEvent e) {
282        if (!(e instanceof TimedWindowEvent)) {
283            return false;
284        }
285        TimedWindowEvent we = (TimedWindowEvent)e;
286        long time = we.getWhen();
287        synchronized (this) {
288            KeyEvent ke = enqueuedKeyEvents.isEmpty() ? null : enqueuedKeyEvents.getFirst();
289            if (ke != null && time >= ke.getWhen()) {
290                TypeAheadMarker marker = typeAheadMarkers.isEmpty() ? null : typeAheadMarkers.getFirst();
291                if (marker != null) {
292                    Window toplevel = marker.untilFocused.getContainingWindow();
293                    // Check that the component awaiting focus belongs to
294                    // the current focused window. See 8015454.
295                    if (toplevel != null && toplevel.isFocused()) {
296                        SunToolkit.postEvent(AppContext.getAppContext(), new SequencedEvent(e));
297                        return true;
298                    }
299                }
300            }
301        }
302        return false;
303    }
304
305    /**
306     * This method is called by the AWT event dispatcher requesting that the
307     * current KeyboardFocusManager dispatch the specified event on its behalf.
308     * DefaultKeyboardFocusManagers dispatch all FocusEvents, all WindowEvents
309     * related to focus, and all KeyEvents. These events are dispatched based
310     * on the KeyboardFocusManager's notion of the focus owner and the focused
311     * and active Windows, sometimes overriding the source of the specified
312     * AWTEvent. If this method returns {@code false}, then the AWT event
313     * dispatcher will attempt to dispatch the event itself.
314     *
315     * @param e the AWTEvent to be dispatched
316     * @return {@code true} if this method dispatched the event;
317     *         {@code false} otherwise
318     */
319    public boolean dispatchEvent(AWTEvent e) {
320        if (focusLog.isLoggable(PlatformLogger.Level.FINE) && (e instanceof WindowEvent || e instanceof FocusEvent)) {
321            focusLog.fine("" + e);
322        }
323        switch (e.getID()) {
324            case WindowEvent.WINDOW_GAINED_FOCUS: {
325                if (repostIfFollowsKeyEvents((WindowEvent)e)) {
326                    break;
327                }
328
329                WindowEvent we = (WindowEvent)e;
330                Window oldFocusedWindow = getGlobalFocusedWindow();
331                Window newFocusedWindow = we.getWindow();
332                if (newFocusedWindow == oldFocusedWindow) {
333                    break;
334                }
335
336                if (!(newFocusedWindow.isFocusableWindow()
337                      && newFocusedWindow.isVisible()
338                      && newFocusedWindow.isDisplayable()))
339                {
340                    // we can not accept focus on such window, so reject it.
341                    restoreFocus(we);
342                    break;
343                }
344                // If there exists a current focused window, then notify it
345                // that it has lost focus.
346                if (oldFocusedWindow != null) {
347                    boolean isEventDispatched =
348                        sendMessage(oldFocusedWindow,
349                                new WindowEvent(oldFocusedWindow,
350                                                WindowEvent.WINDOW_LOST_FOCUS,
351                                                newFocusedWindow));
352                    // Failed to dispatch, clear by ourselves
353                    if (!isEventDispatched) {
354                        setGlobalFocusOwner(null);
355                        setGlobalFocusedWindow(null);
356                    }
357                }
358
359                // Because the native libraries do not post WINDOW_ACTIVATED
360                // events, we need to synthesize one if the active Window
361                // changed.
362                Window newActiveWindow =
363                    getOwningFrameDialog(newFocusedWindow);
364                Window currentActiveWindow = getGlobalActiveWindow();
365                if (newActiveWindow != currentActiveWindow) {
366                    sendMessage(newActiveWindow,
367                                new WindowEvent(newActiveWindow,
368                                                WindowEvent.WINDOW_ACTIVATED,
369                                                currentActiveWindow));
370                    if (newActiveWindow != getGlobalActiveWindow()) {
371                        // Activation change was rejected. Unlikely, but
372                        // possible.
373                        restoreFocus(we);
374                        break;
375                    }
376                }
377
378                setGlobalFocusedWindow(newFocusedWindow);
379
380                if (newFocusedWindow != getGlobalFocusedWindow()) {
381                    // Focus change was rejected. Will happen if
382                    // newFocusedWindow is not a focusable Window.
383                    restoreFocus(we);
384                    break;
385                }
386
387                // Restore focus to the Component which last held it. We do
388                // this here so that client code can override our choice in
389                // a WINDOW_GAINED_FOCUS handler.
390                //
391                // Make sure that the focus change request doesn't change the
392                // focused Window in case we are no longer the focused Window
393                // when the request is handled.
394                if (inSendMessage == 0) {
395                    // Identify which Component should initially gain focus
396                    // in the Window.
397                    //
398                    // * If we're in SendMessage, then this is a synthetic
399                    //   WINDOW_GAINED_FOCUS message which was generated by a
400                    //   the FOCUS_GAINED handler. Allow the Component to
401                    //   which the FOCUS_GAINED message was targeted to
402                    //   receive the focus.
403                    // * Otherwise, look up the correct Component here.
404                    //   We don't use Window.getMostRecentFocusOwner because
405                    //   window is focused now and 'null' will be returned
406
407
408                    // Calculating of most recent focus owner and focus
409                    // request should be synchronized on KeyboardFocusManager.class
410                    // to prevent from thread race when user will request
411                    // focus between calculation and our request.
412                    // But if focus transfer is synchronous, this synchronization
413                    // may cause deadlock, thus we don't synchronize this block.
414                    Component toFocus = KeyboardFocusManager.
415                        getMostRecentFocusOwner(newFocusedWindow);
416                    if ((toFocus == null) &&
417                        newFocusedWindow.isFocusableWindow())
418                    {
419                        toFocus = newFocusedWindow.getFocusTraversalPolicy().
420                            getInitialComponent(newFocusedWindow);
421                    }
422                    Component tempLost = null;
423                    synchronized(KeyboardFocusManager.class) {
424                        tempLost = newFocusedWindow.setTemporaryLostComponent(null);
425                    }
426
427                    // The component which last has the focus when this window was focused
428                    // should receive focus first
429                    if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
430                        focusLog.finer("tempLost {0}, toFocus {1}",
431                                       tempLost, toFocus);
432                    }
433                    if (tempLost != null) {
434                        tempLost.requestFocusInWindow(CausedFocusEvent.Cause.ACTIVATION);
435                    }
436
437                    if (toFocus != null && toFocus != tempLost) {
438                        // If there is a component which requested focus when this window
439                        // was inactive it expects to receive focus after activation.
440                        toFocus.requestFocusInWindow(CausedFocusEvent.Cause.ACTIVATION);
441                    }
442                }
443
444                Window realOppositeWindow = this.realOppositeWindowWR.get();
445                if (realOppositeWindow != we.getOppositeWindow()) {
446                    we = new WindowEvent(newFocusedWindow,
447                                         WindowEvent.WINDOW_GAINED_FOCUS,
448                                         realOppositeWindow);
449                }
450                return typeAheadAssertions(newFocusedWindow, we);
451            }
452
453            case WindowEvent.WINDOW_ACTIVATED: {
454                WindowEvent we = (WindowEvent)e;
455                Window oldActiveWindow = getGlobalActiveWindow();
456                Window newActiveWindow = we.getWindow();
457                if (oldActiveWindow == newActiveWindow) {
458                    break;
459                }
460
461                // If there exists a current active window, then notify it that
462                // it has lost activation.
463                if (oldActiveWindow != null) {
464                    boolean isEventDispatched =
465                        sendMessage(oldActiveWindow,
466                                new WindowEvent(oldActiveWindow,
467                                                WindowEvent.WINDOW_DEACTIVATED,
468                                                newActiveWindow));
469                    // Failed to dispatch, clear by ourselves
470                    if (!isEventDispatched) {
471                        setGlobalActiveWindow(null);
472                    }
473                    if (getGlobalActiveWindow() != null) {
474                        // Activation change was rejected. Unlikely, but
475                        // possible.
476                        break;
477                    }
478                }
479
480                setGlobalActiveWindow(newActiveWindow);
481
482                if (newActiveWindow != getGlobalActiveWindow()) {
483                    // Activation change was rejected. Unlikely, but
484                    // possible.
485                    break;
486                }
487
488                return typeAheadAssertions(newActiveWindow, we);
489            }
490
491            case FocusEvent.FOCUS_GAINED: {
492                FocusEvent fe = (FocusEvent)e;
493                CausedFocusEvent.Cause cause = (fe instanceof CausedFocusEvent) ?
494                    ((CausedFocusEvent)fe).getCause() : CausedFocusEvent.Cause.UNKNOWN;
495                Component oldFocusOwner = getGlobalFocusOwner();
496                Component newFocusOwner = fe.getComponent();
497                if (oldFocusOwner == newFocusOwner) {
498                    if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
499                        focusLog.fine("Skipping {0} because focus owner is the same", e);
500                    }
501                    // We can't just drop the event - there could be
502                    // type-ahead markers associated with it.
503                    dequeueKeyEvents(-1, newFocusOwner);
504                    break;
505                }
506
507                // If there exists a current focus owner, then notify it that
508                // it has lost focus.
509                if (oldFocusOwner != null) {
510                    boolean isEventDispatched =
511                        sendMessage(oldFocusOwner,
512                                    new CausedFocusEvent(oldFocusOwner,
513                                                   FocusEvent.FOCUS_LOST,
514                                                   fe.isTemporary(),
515                                                   newFocusOwner, cause));
516                    // Failed to dispatch, clear by ourselves
517                    if (!isEventDispatched) {
518                        setGlobalFocusOwner(null);
519                        if (!fe.isTemporary()) {
520                            setGlobalPermanentFocusOwner(null);
521                        }
522                    }
523                }
524
525                // Because the native windowing system has a different notion
526                // of the current focus and activation states, it is possible
527                // that a Component outside of the focused Window receives a
528                // FOCUS_GAINED event. We synthesize a WINDOW_GAINED_FOCUS
529                // event in that case.
530                final Window newFocusedWindow = SunToolkit.getContainingWindow(newFocusOwner);
531                final Window currentFocusedWindow = getGlobalFocusedWindow();
532                if (newFocusedWindow != null &&
533                    newFocusedWindow != currentFocusedWindow)
534                {
535                    sendMessage(newFocusedWindow,
536                                new WindowEvent(newFocusedWindow,
537                                        WindowEvent.WINDOW_GAINED_FOCUS,
538                                                currentFocusedWindow));
539                    if (newFocusedWindow != getGlobalFocusedWindow()) {
540                        // Focus change was rejected. Will happen if
541                        // newFocusedWindow is not a focusable Window.
542
543                        // Need to recover type-ahead, but don't bother
544                        // restoring focus. That was done by the
545                        // WINDOW_GAINED_FOCUS handler
546                        dequeueKeyEvents(-1, newFocusOwner);
547                        break;
548                    }
549                }
550
551                if (!(newFocusOwner.isFocusable() && newFocusOwner.isShowing() &&
552                    // Refuse focus on a disabled component if the focus event
553                    // isn't of UNKNOWN reason (i.e. not a result of a direct request
554                    // but traversal, activation or system generated).
555                    (newFocusOwner.isEnabled() || cause.equals(CausedFocusEvent.Cause.UNKNOWN))))
556                {
557                    // we should not accept focus on such component, so reject it.
558                    dequeueKeyEvents(-1, newFocusOwner);
559                    if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
560                        // If FOCUS_GAINED is for a disposed component (however
561                        // it shouldn't happen) its toplevel parent is null. In this
562                        // case we have to try to restore focus in the current focused
563                        // window (for the details: 6607170).
564                        if (newFocusedWindow == null) {
565                            restoreFocus(fe, currentFocusedWindow);
566                        } else {
567                            restoreFocus(fe, newFocusedWindow);
568                        }
569                        setMostRecentFocusOwner(newFocusedWindow, null); // see: 8013773
570                    }
571                    break;
572                }
573
574                setGlobalFocusOwner(newFocusOwner);
575
576                if (newFocusOwner != getGlobalFocusOwner()) {
577                    // Focus change was rejected. Will happen if
578                    // newFocusOwner is not focus traversable.
579                    dequeueKeyEvents(-1, newFocusOwner);
580                    if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
581                        restoreFocus(fe, newFocusedWindow);
582                    }
583                    break;
584                }
585
586                if (!fe.isTemporary()) {
587                    setGlobalPermanentFocusOwner(newFocusOwner);
588
589                    if (newFocusOwner != getGlobalPermanentFocusOwner()) {
590                        // Focus change was rejected. Unlikely, but possible.
591                        dequeueKeyEvents(-1, newFocusOwner);
592                        if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
593                            restoreFocus(fe, newFocusedWindow);
594                        }
595                        break;
596                    }
597                }
598
599                setNativeFocusOwner(getHeavyweight(newFocusOwner));
600
601                Component realOppositeComponent = this.realOppositeComponentWR.get();
602                if (realOppositeComponent != null &&
603                    realOppositeComponent != fe.getOppositeComponent()) {
604                    fe = new CausedFocusEvent(newFocusOwner,
605                                        FocusEvent.FOCUS_GAINED,
606                                        fe.isTemporary(),
607                                        realOppositeComponent, cause);
608                    ((AWTEvent) fe).isPosted = true;
609                }
610                return typeAheadAssertions(newFocusOwner, fe);
611            }
612
613            case FocusEvent.FOCUS_LOST: {
614                FocusEvent fe = (FocusEvent)e;
615                Component currentFocusOwner = getGlobalFocusOwner();
616                if (currentFocusOwner == null) {
617                    if (focusLog.isLoggable(PlatformLogger.Level.FINE))
618                        focusLog.fine("Skipping {0} because focus owner is null", e);
619                    break;
620                }
621                // Ignore cases where a Component loses focus to itself.
622                // If we make a mistake because of retargeting, then the
623                // FOCUS_GAINED handler will correct it.
624                if (currentFocusOwner == fe.getOppositeComponent()) {
625                    if (focusLog.isLoggable(PlatformLogger.Level.FINE))
626                        focusLog.fine("Skipping {0} because current focus owner is equal to opposite", e);
627                    break;
628                }
629
630                setGlobalFocusOwner(null);
631
632                if (getGlobalFocusOwner() != null) {
633                    // Focus change was rejected. Unlikely, but possible.
634                    restoreFocus(currentFocusOwner, true);
635                    break;
636                }
637
638                if (!fe.isTemporary()) {
639                    setGlobalPermanentFocusOwner(null);
640
641                    if (getGlobalPermanentFocusOwner() != null) {
642                        // Focus change was rejected. Unlikely, but possible.
643                        restoreFocus(currentFocusOwner, true);
644                        break;
645                    }
646                } else {
647                    Window owningWindow = currentFocusOwner.getContainingWindow();
648                    if (owningWindow != null) {
649                        owningWindow.setTemporaryLostComponent(currentFocusOwner);
650                    }
651                }
652
653                setNativeFocusOwner(null);
654
655                fe.setSource(currentFocusOwner);
656
657                realOppositeComponentWR = (fe.getOppositeComponent() != null)
658                    ? new WeakReference<Component>(currentFocusOwner)
659                    : NULL_COMPONENT_WR;
660
661                return typeAheadAssertions(currentFocusOwner, fe);
662            }
663
664            case WindowEvent.WINDOW_DEACTIVATED: {
665                WindowEvent we = (WindowEvent)e;
666                Window currentActiveWindow = getGlobalActiveWindow();
667                if (currentActiveWindow == null) {
668                    break;
669                }
670
671                if (currentActiveWindow != e.getSource()) {
672                    // The event is lost in time.
673                    // Allow listeners to precess the event but do not
674                    // change any global states
675                    break;
676                }
677
678                setGlobalActiveWindow(null);
679                if (getGlobalActiveWindow() != null) {
680                    // Activation change was rejected. Unlikely, but possible.
681                    break;
682                }
683
684                we.setSource(currentActiveWindow);
685                return typeAheadAssertions(currentActiveWindow, we);
686            }
687
688            case WindowEvent.WINDOW_LOST_FOCUS: {
689                if (repostIfFollowsKeyEvents((WindowEvent)e)) {
690                    break;
691                }
692
693                WindowEvent we = (WindowEvent)e;
694                Window currentFocusedWindow = getGlobalFocusedWindow();
695                Window losingFocusWindow = we.getWindow();
696                Window activeWindow = getGlobalActiveWindow();
697                Window oppositeWindow = we.getOppositeWindow();
698                if (focusLog.isLoggable(PlatformLogger.Level.FINE))
699                    focusLog.fine("Active {0}, Current focused {1}, losing focus {2} opposite {3}",
700                                  activeWindow, currentFocusedWindow,
701                                  losingFocusWindow, oppositeWindow);
702                if (currentFocusedWindow == null) {
703                    break;
704                }
705
706                // Special case -- if the native windowing system posts an
707                // event claiming that the active Window has lost focus to the
708                // focused Window, then discard the event. This is an artifact
709                // of the native windowing system not knowing which Window is
710                // really focused.
711                if (inSendMessage == 0 && losingFocusWindow == activeWindow &&
712                    oppositeWindow == currentFocusedWindow)
713                {
714                    break;
715                }
716
717                Component currentFocusOwner = getGlobalFocusOwner();
718                if (currentFocusOwner != null) {
719                    // The focus owner should always receive a FOCUS_LOST event
720                    // before the Window is defocused.
721                    Component oppositeComp = null;
722                    if (oppositeWindow != null) {
723                        oppositeComp = oppositeWindow.getTemporaryLostComponent();
724                        if (oppositeComp == null) {
725                            oppositeComp = oppositeWindow.getMostRecentFocusOwner();
726                        }
727                    }
728                    if (oppositeComp == null) {
729                        oppositeComp = oppositeWindow;
730                    }
731                    sendMessage(currentFocusOwner,
732                                new CausedFocusEvent(currentFocusOwner,
733                                               FocusEvent.FOCUS_LOST,
734                                               true,
735                                               oppositeComp, CausedFocusEvent.Cause.ACTIVATION));
736                }
737
738                setGlobalFocusedWindow(null);
739                if (getGlobalFocusedWindow() != null) {
740                    // Focus change was rejected. Unlikely, but possible.
741                    restoreFocus(currentFocusedWindow, null, true);
742                    break;
743                }
744
745                we.setSource(currentFocusedWindow);
746                realOppositeWindowWR = (oppositeWindow != null)
747                    ? new WeakReference<Window>(currentFocusedWindow)
748                    : NULL_WINDOW_WR;
749                typeAheadAssertions(currentFocusedWindow, we);
750
751                if (oppositeWindow == null) {
752                    // Then we need to deactivate the active Window as well.
753                    // No need to synthesize in other cases, because
754                    // WINDOW_ACTIVATED will handle it if necessary.
755                    sendMessage(activeWindow,
756                                new WindowEvent(activeWindow,
757                                                WindowEvent.WINDOW_DEACTIVATED,
758                                                null));
759                    if (getGlobalActiveWindow() != null) {
760                        // Activation change was rejected. Unlikely,
761                        // but possible.
762                        restoreFocus(currentFocusedWindow, null, true);
763                    }
764                }
765                break;
766            }
767
768            case KeyEvent.KEY_TYPED:
769            case KeyEvent.KEY_PRESSED:
770            case KeyEvent.KEY_RELEASED:
771                return typeAheadAssertions(null, e);
772
773            default:
774                return false;
775        }
776
777        return true;
778    }
779
780    /**
781     * Called by {@code dispatchEvent} if no other
782     * KeyEventDispatcher in the dispatcher chain dispatched the KeyEvent, or
783     * if no other KeyEventDispatchers are registered. If the event has not
784     * been consumed, its target is enabled, and the focus owner is not null,
785     * this method dispatches the event to its target. This method will also
786     * subsequently dispatch the event to all registered
787     * KeyEventPostProcessors. After all this operations are finished,
788     * the event is passed to peers for processing.
789     * <p>
790     * In all cases, this method returns {@code true}, since
791     * DefaultKeyboardFocusManager is designed so that neither
792     * {@code dispatchEvent}, nor the AWT event dispatcher, should take
793     * further action on the event in any situation.
794     *
795     * @param e the KeyEvent to be dispatched
796     * @return {@code true}
797     * @see Component#dispatchEvent
798     */
799    public boolean dispatchKeyEvent(KeyEvent e) {
800        Component focusOwner = (((AWTEvent)e).isPosted) ? getFocusOwner() : e.getComponent();
801
802        if (focusOwner != null && focusOwner.isShowing() && focusOwner.canBeFocusOwner()) {
803            if (!e.isConsumed()) {
804                Component comp = e.getComponent();
805                if (comp != null && comp.isEnabled()) {
806                    redispatchEvent(comp, e);
807                }
808            }
809        }
810        boolean stopPostProcessing = false;
811        java.util.List<KeyEventPostProcessor> processors = getKeyEventPostProcessors();
812        if (processors != null) {
813            for (java.util.Iterator<KeyEventPostProcessor> iter = processors.iterator();
814                 !stopPostProcessing && iter.hasNext(); )
815            {
816                stopPostProcessing = iter.next().
817                            postProcessKeyEvent(e);
818            }
819        }
820        if (!stopPostProcessing) {
821            postProcessKeyEvent(e);
822        }
823
824        // Allow the peer to process KeyEvent
825        Component source = e.getComponent();
826        ComponentPeer peer = source.peer;
827
828        if (peer == null || peer instanceof LightweightPeer) {
829            // if focus owner is lightweight then its native container
830            // processes event
831            Container target = source.getNativeContainer();
832            if (target != null) {
833                peer = target.peer;
834            }
835        }
836        if (peer != null) {
837            peer.handleEvent(e);
838        }
839
840        return true;
841    }
842
843    /**
844     * This method will be called by {@code dispatchKeyEvent}. It will
845     * handle any unconsumed KeyEvents that map to an AWT
846     * {@code MenuShortcut} by consuming the event and activating the
847     * shortcut.
848     *
849     * @param e the KeyEvent to post-process
850     * @return {@code true}
851     * @see #dispatchKeyEvent
852     * @see MenuShortcut
853     */
854    public boolean postProcessKeyEvent(KeyEvent e) {
855        if (!e.isConsumed()) {
856            Component target = e.getComponent();
857            Container p = (Container)
858                (target instanceof Container ? target : target.getParent());
859            if (p != null) {
860                p.postProcessKeyEvent(e);
861            }
862        }
863        return true;
864    }
865
866    private void pumpApprovedKeyEvents() {
867        KeyEvent ke;
868        do {
869            ke = null;
870            synchronized (this) {
871                if (enqueuedKeyEvents.size() != 0) {
872                    ke = enqueuedKeyEvents.getFirst();
873                    if (typeAheadMarkers.size() != 0) {
874                        TypeAheadMarker marker = typeAheadMarkers.getFirst();
875                        // Fixed 5064013: may appears that the events have the same time
876                        // if (ke.getWhen() >= marker.after) {
877                        // The fix is rolled out.
878
879                        if (ke.getWhen() > marker.after) {
880                            ke = null;
881                        }
882                    }
883                    if (ke != null) {
884                        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
885                            focusLog.finer("Pumping approved event {0}", ke);
886                        }
887                        enqueuedKeyEvents.removeFirst();
888                    }
889                }
890            }
891            if (ke != null) {
892                preDispatchKeyEvent(ke);
893            }
894        } while (ke != null);
895    }
896
897    /**
898     * Dumps the list of type-ahead queue markers to stderr
899     */
900    void dumpMarkers() {
901        if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
902            focusLog.finest(">>> Markers dump, time: {0}", System.currentTimeMillis());
903            synchronized (this) {
904                if (typeAheadMarkers.size() != 0) {
905                    Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator();
906                    while (iter.hasNext()) {
907                        TypeAheadMarker marker = iter.next();
908                        focusLog.finest("    {0}", marker);
909                    }
910                }
911            }
912        }
913    }
914
915    private boolean typeAheadAssertions(Component target, AWTEvent e) {
916
917        // Clear any pending events here as well as in the FOCUS_GAINED
918        // handler. We need this call here in case a marker was removed in
919        // response to a call to dequeueKeyEvents.
920        pumpApprovedKeyEvents();
921
922        switch (e.getID()) {
923            case KeyEvent.KEY_TYPED:
924            case KeyEvent.KEY_PRESSED:
925            case KeyEvent.KEY_RELEASED: {
926                KeyEvent ke = (KeyEvent)e;
927                synchronized (this) {
928                    if (e.isPosted && typeAheadMarkers.size() != 0) {
929                        TypeAheadMarker marker = typeAheadMarkers.getFirst();
930                        // Fixed 5064013: may appears that the events have the same time
931                        // if (ke.getWhen() >= marker.after) {
932                        // The fix is rolled out.
933
934                        if (ke.getWhen() > marker.after) {
935                            if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
936                                focusLog.finer("Storing event {0} because of marker {1}", ke, marker);
937                            }
938                            enqueuedKeyEvents.addLast(ke);
939                            return true;
940                        }
941                    }
942                }
943
944                // KeyEvent was posted before focus change request
945                return preDispatchKeyEvent(ke);
946            }
947
948            case FocusEvent.FOCUS_GAINED:
949                if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
950                    focusLog.finest("Markers before FOCUS_GAINED on {0}", target);
951                }
952                dumpMarkers();
953                // Search the marker list for the first marker tied to
954                // the Component which just gained focus. Then remove
955                // that marker, any markers which immediately follow
956                // and are tied to the same component, and all markers
957                // that precede it. This handles the case where
958                // multiple focus requests were made for the same
959                // Component in a row and when we lost some of the
960                // earlier requests. Since FOCUS_GAINED events will
961                // not be generated for these additional requests, we
962                // need to clear those markers too.
963                synchronized (this) {
964                    boolean found = false;
965                    if (hasMarker(target)) {
966                        for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator();
967                             iter.hasNext(); )
968                        {
969                            if (iter.next().untilFocused == target) {
970                                found = true;
971                            } else if (found) {
972                                break;
973                            }
974                            iter.remove();
975                        }
976                    } else {
977                        // Exception condition - event without marker
978                        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
979                            focusLog.finer("Event without marker {0}", e);
980                        }
981                    }
982                }
983                focusLog.finest("Markers after FOCUS_GAINED");
984                dumpMarkers();
985
986                redispatchEvent(target, e);
987
988                // Now, dispatch any pending KeyEvents which have been
989                // released because of the FOCUS_GAINED event so that we don't
990                // have to wait for another event to be posted to the queue.
991                pumpApprovedKeyEvents();
992                return true;
993
994            default:
995                redispatchEvent(target, e);
996                return true;
997        }
998    }
999
1000    /**
1001     * Returns true if there are some marker associated with component {@code comp}
1002     * in a markers' queue
1003     * @since 1.5
1004     */
1005    private boolean hasMarker(Component comp) {
1006        for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
1007            if (iter.next().untilFocused == comp) {
1008                return true;
1009            }
1010        }
1011        return false;
1012    }
1013
1014    /**
1015     * Clears markers queue
1016     * @since 1.5
1017     */
1018    void clearMarkers() {
1019        synchronized(this) {
1020            typeAheadMarkers.clear();
1021        }
1022    }
1023
1024    @SuppressWarnings("deprecation")
1025    private boolean preDispatchKeyEvent(KeyEvent ke) {
1026        if (((AWTEvent) ke).isPosted) {
1027            Component focusOwner = getFocusOwner();
1028            ke.setSource(((focusOwner != null) ? focusOwner : getFocusedWindow()));
1029        }
1030        if (ke.getSource() == null) {
1031            return true;
1032        }
1033
1034        // Explicitly set the key event timestamp here (not in Component.dispatchEventImpl):
1035        // - A key event is anyway passed to this method which starts its actual dispatching.
1036        // - If a key event is put to the type ahead queue, its time stamp should not be registered
1037        //   until its dispatching actually starts (by this method).
1038        EventQueue.setCurrentEventAndMostRecentTime(ke);
1039
1040        /**
1041         * Fix for 4495473.
1042         * This fix allows to correctly dispatch events when native
1043         * event proxying mechanism is active.
1044         * If it is active we should redispatch key events after
1045         * we detected its correct target.
1046         */
1047        if (KeyboardFocusManager.isProxyActive(ke)) {
1048            Component source = (Component)ke.getSource();
1049            Container target = source.getNativeContainer();
1050            if (target != null) {
1051                ComponentPeer peer = target.peer;
1052                if (peer != null) {
1053                    peer.handleEvent(ke);
1054                    /**
1055                     * Fix for 4478780 - consume event after it was dispatched by peer.
1056                     */
1057                    ke.consume();
1058                }
1059            }
1060            return true;
1061        }
1062
1063        java.util.List<KeyEventDispatcher> dispatchers = getKeyEventDispatchers();
1064        if (dispatchers != null) {
1065            for (java.util.Iterator<KeyEventDispatcher> iter = dispatchers.iterator();
1066                 iter.hasNext(); )
1067             {
1068                 if (iter.next().
1069                     dispatchKeyEvent(ke))
1070                 {
1071                     return true;
1072                 }
1073             }
1074        }
1075        return dispatchKeyEvent(ke);
1076    }
1077
1078    /*
1079     * @param e is a KEY_PRESSED event that can be used
1080     *          to track the next KEY_TYPED related.
1081     */
1082    private void consumeNextKeyTyped(KeyEvent e) {
1083        consumeNextKeyTyped = true;
1084    }
1085
1086    private void consumeTraversalKey(KeyEvent e) {
1087        e.consume();
1088        consumeNextKeyTyped = (e.getID() == KeyEvent.KEY_PRESSED) &&
1089                              !e.isActionKey();
1090    }
1091
1092    /*
1093     * return true if event was consumed
1094     */
1095    private boolean consumeProcessedKeyEvent(KeyEvent e) {
1096        if ((e.getID() == KeyEvent.KEY_TYPED) && consumeNextKeyTyped) {
1097            e.consume();
1098            consumeNextKeyTyped = false;
1099            return true;
1100        }
1101        return false;
1102    }
1103
1104    /**
1105     * This method initiates a focus traversal operation if and only if the
1106     * KeyEvent represents a focus traversal key for the specified
1107     * focusedComponent. It is expected that focusedComponent is the current
1108     * focus owner, although this need not be the case. If it is not,
1109     * focus traversal will nevertheless proceed as if focusedComponent
1110     * were the focus owner.
1111     *
1112     * @param focusedComponent the Component that is the basis for a focus
1113     *        traversal operation if the specified event represents a focus
1114     *        traversal key for the Component
1115     * @param e the event that may represent a focus traversal key
1116     */
1117    public void processKeyEvent(Component focusedComponent, KeyEvent e) {
1118        // consume processed event if needed
1119        if (consumeProcessedKeyEvent(e)) {
1120            return;
1121        }
1122
1123        // KEY_TYPED events cannot be focus traversal keys
1124        if (e.getID() == KeyEvent.KEY_TYPED) {
1125            return;
1126        }
1127
1128        if (focusedComponent.getFocusTraversalKeysEnabled() &&
1129            !e.isConsumed())
1130        {
1131            AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e),
1132                oppStroke = AWTKeyStroke.getAWTKeyStroke(stroke.getKeyCode(),
1133                                                 stroke.getModifiers(),
1134                                                 !stroke.isOnKeyRelease());
1135            Set<AWTKeyStroke> toTest;
1136            boolean contains, containsOpp;
1137
1138            toTest = focusedComponent.getFocusTraversalKeys(
1139                KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
1140            contains = toTest.contains(stroke);
1141            containsOpp = toTest.contains(oppStroke);
1142
1143            if (contains || containsOpp) {
1144                consumeTraversalKey(e);
1145                if (contains) {
1146                    focusNextComponent(focusedComponent);
1147                }
1148                return;
1149            } else if (e.getID() == KeyEvent.KEY_PRESSED) {
1150                // Fix for 6637607: consumeNextKeyTyped should be reset.
1151                consumeNextKeyTyped = false;
1152            }
1153
1154            toTest = focusedComponent.getFocusTraversalKeys(
1155                KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
1156            contains = toTest.contains(stroke);
1157            containsOpp = toTest.contains(oppStroke);
1158
1159            if (contains || containsOpp) {
1160                consumeTraversalKey(e);
1161                if (contains) {
1162                    focusPreviousComponent(focusedComponent);
1163                }
1164                return;
1165            }
1166
1167            toTest = focusedComponent.getFocusTraversalKeys(
1168                KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
1169            contains = toTest.contains(stroke);
1170            containsOpp = toTest.contains(oppStroke);
1171
1172            if (contains || containsOpp) {
1173                consumeTraversalKey(e);
1174                if (contains) {
1175                    upFocusCycle(focusedComponent);
1176                }
1177                return;
1178            }
1179
1180            if (!((focusedComponent instanceof Container) &&
1181                  ((Container)focusedComponent).isFocusCycleRoot())) {
1182                return;
1183            }
1184
1185            toTest = focusedComponent.getFocusTraversalKeys(
1186                KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
1187            contains = toTest.contains(stroke);
1188            containsOpp = toTest.contains(oppStroke);
1189
1190            if (contains || containsOpp) {
1191                consumeTraversalKey(e);
1192                if (contains) {
1193                    downFocusCycle((Container)focusedComponent);
1194                }
1195            }
1196        }
1197    }
1198
1199    /**
1200     * Delays dispatching of KeyEvents until the specified Component becomes
1201     * the focus owner. KeyEvents with timestamps later than the specified
1202     * timestamp will be enqueued until the specified Component receives a
1203     * FOCUS_GAINED event, or the AWT cancels the delay request by invoking
1204     * {@code dequeueKeyEvents} or {@code discardKeyEvents}.
1205     *
1206     * @param after timestamp of current event, or the current, system time if
1207     *        the current event has no timestamp, or the AWT cannot determine
1208     *        which event is currently being handled
1209     * @param untilFocused Component which will receive a FOCUS_GAINED event
1210     *        before any pending KeyEvents
1211     * @see #dequeueKeyEvents
1212     * @see #discardKeyEvents
1213     */
1214    protected synchronized void enqueueKeyEvents(long after,
1215                                                 Component untilFocused) {
1216        if (untilFocused == null) {
1217            return;
1218        }
1219
1220        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
1221            focusLog.finer("Enqueue at {0} for {1}",
1222                       after, untilFocused);
1223        }
1224
1225        int insertionIndex = 0,
1226            i = typeAheadMarkers.size();
1227        ListIterator<TypeAheadMarker> iter = typeAheadMarkers.listIterator(i);
1228
1229        for (; i > 0; i--) {
1230            TypeAheadMarker marker = iter.previous();
1231            if (marker.after <= after) {
1232                insertionIndex = i;
1233                break;
1234            }
1235        }
1236
1237        typeAheadMarkers.add(insertionIndex,
1238                             new TypeAheadMarker(after, untilFocused));
1239    }
1240
1241    /**
1242     * Releases for normal dispatching to the current focus owner all
1243     * KeyEvents which were enqueued because of a call to
1244     * {@code enqueueKeyEvents} with the same timestamp and Component.
1245     * If the given timestamp is less than zero, the outstanding enqueue
1246     * request for the given Component with the <b>oldest</b> timestamp (if
1247     * any) should be cancelled.
1248     *
1249     * @param after the timestamp specified in the call to
1250     *        {@code enqueueKeyEvents}, or any value &lt; 0
1251     * @param untilFocused the Component specified in the call to
1252     *        {@code enqueueKeyEvents}
1253     * @see #enqueueKeyEvents
1254     * @see #discardKeyEvents
1255     */
1256    protected synchronized void dequeueKeyEvents(long after,
1257                                                 Component untilFocused) {
1258        if (untilFocused == null) {
1259            return;
1260        }
1261
1262        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
1263            focusLog.finer("Dequeue at {0} for {1}",
1264                       after, untilFocused);
1265        }
1266
1267        TypeAheadMarker marker;
1268        ListIterator<TypeAheadMarker> iter = typeAheadMarkers.listIterator
1269            ((after >= 0) ? typeAheadMarkers.size() : 0);
1270
1271        if (after < 0) {
1272            while (iter.hasNext()) {
1273                marker = iter.next();
1274                if (marker.untilFocused == untilFocused)
1275                {
1276                    iter.remove();
1277                    return;
1278                }
1279            }
1280        } else {
1281            while (iter.hasPrevious()) {
1282                marker = iter.previous();
1283                if (marker.untilFocused == untilFocused &&
1284                    marker.after == after)
1285                {
1286                    iter.remove();
1287                    return;
1288                }
1289            }
1290        }
1291    }
1292
1293    /**
1294     * Discards all KeyEvents which were enqueued because of one or more calls
1295     * to {@code enqueueKeyEvents} with the specified Component, or one of
1296     * its descendants.
1297     *
1298     * @param comp the Component specified in one or more calls to
1299     *        {@code enqueueKeyEvents}, or a parent of such a Component
1300     * @see #enqueueKeyEvents
1301     * @see #dequeueKeyEvents
1302     */
1303    protected synchronized void discardKeyEvents(Component comp) {
1304        if (comp == null) {
1305            return;
1306        }
1307
1308        long start = -1;
1309
1310        for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
1311            TypeAheadMarker marker = iter.next();
1312            Component toTest = marker.untilFocused;
1313            boolean match = (toTest == comp);
1314            while (!match && toTest != null && !(toTest instanceof Window)) {
1315                toTest = toTest.getParent();
1316                match = (toTest == comp);
1317            }
1318            if (match) {
1319                if (start < 0) {
1320                    start = marker.after;
1321                }
1322                iter.remove();
1323            } else if (start >= 0) {
1324                purgeStampedEvents(start, marker.after);
1325                start = -1;
1326            }
1327        }
1328
1329        purgeStampedEvents(start, -1);
1330    }
1331
1332    // Notes:
1333    //   * must be called inside a synchronized block
1334    //   * if 'start' is < 0, then this function does nothing
1335    //   * if 'end' is < 0, then all KeyEvents from 'start' to the end of the
1336    //     queue will be removed
1337    private void purgeStampedEvents(long start, long end) {
1338        if (start < 0) {
1339            return;
1340        }
1341
1342        for (Iterator<KeyEvent> iter = enqueuedKeyEvents.iterator(); iter.hasNext(); ) {
1343            KeyEvent ke = iter.next();
1344            long time = ke.getWhen();
1345
1346            if (start < time && (end < 0 || time <= end)) {
1347                iter.remove();
1348            }
1349
1350            if (end >= 0 && time > end) {
1351                break;
1352            }
1353        }
1354    }
1355
1356    /**
1357     * Focuses the Component before aComponent, typically based on a
1358     * FocusTraversalPolicy.
1359     *
1360     * @param aComponent the Component that is the basis for the focus
1361     *        traversal operation
1362     * @see FocusTraversalPolicy
1363     * @see Component#transferFocusBackward
1364     */
1365    public void focusPreviousComponent(Component aComponent) {
1366        if (aComponent != null) {
1367            aComponent.transferFocusBackward();
1368        }
1369    }
1370
1371    /**
1372     * Focuses the Component after aComponent, typically based on a
1373     * FocusTraversalPolicy.
1374     *
1375     * @param aComponent the Component that is the basis for the focus
1376     *        traversal operation
1377     * @see FocusTraversalPolicy
1378     * @see Component#transferFocus
1379     */
1380    public void focusNextComponent(Component aComponent) {
1381        if (aComponent != null) {
1382            aComponent.transferFocus();
1383        }
1384    }
1385
1386    /**
1387     * Moves the focus up one focus traversal cycle. Typically, the focus owner
1388     * is set to aComponent's focus cycle root, and the current focus cycle
1389     * root is set to the new focus owner's focus cycle root. If, however,
1390     * aComponent's focus cycle root is a Window, then the focus owner is set
1391     * to the focus cycle root's default Component to focus, and the current
1392     * focus cycle root is unchanged.
1393     *
1394     * @param aComponent the Component that is the basis for the focus
1395     *        traversal operation
1396     * @see Component#transferFocusUpCycle
1397     */
1398    public void upFocusCycle(Component aComponent) {
1399        if (aComponent != null) {
1400            aComponent.transferFocusUpCycle();
1401        }
1402    }
1403
1404    /**
1405     * Moves the focus down one focus traversal cycle. If aContainer is a focus
1406     * cycle root, then the focus owner is set to aContainer's default
1407     * Component to focus, and the current focus cycle root is set to
1408     * aContainer. If aContainer is not a focus cycle root, then no focus
1409     * traversal operation occurs.
1410     *
1411     * @param aContainer the Container that is the basis for the focus
1412     *        traversal operation
1413     * @see Container#transferFocusDownCycle
1414     */
1415    public void downFocusCycle(Container aContainer) {
1416        if (aContainer != null && aContainer.isFocusCycleRoot()) {
1417            aContainer.transferFocusDownCycle();
1418        }
1419    }
1420}
1421