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