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