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