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