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