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