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 javax.swing; 26 27 import java.awt.Component; 28 import java.awt.Container; 29 import java.awt.Window; 30 import java.util.*; 31 import java.awt.FocusTraversalPolicy; 32 import sun.util.logging.PlatformLogger; 33 34 /** 35 * A FocusTraversalPolicy that determines traversal order by sorting the 36 * Components of a focus traversal cycle based on a given Comparator. Portions 37 * of the Component hierarchy that are not visible and displayable will not be 38 * included. 39 * <p> 40 * By default, SortingFocusTraversalPolicy implicitly transfers focus down- 41 * cycle. That is, during normal focus traversal, the Component 42 * traversed after a focus cycle root will be the focus-cycle-root's default 43 * Component to focus. This behavior can be disabled using the 44 * <code>setImplicitDownCycleTraversal</code> method. 45 * <p> 46 * By default, methods of this class with return a Component only if it is 47 * visible, displayable, enabled, and focusable. Subclasses can modify this 48 * behavior by overriding the <code>accept</code> method. 49 * <p> 50 * This policy takes into account <a 51 * href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal 52 * policy providers</a>. When searching for first/last/next/previous Component, 53 * if a focus traversal policy provider is encountered, its focus traversal 54 * policy is used to perform the search operation. 55 * 56 * @author David Mendenhall 57 * 58 * @see java.util.Comparator 59 * @since 1.4 60 */ 61 public class SortingFocusTraversalPolicy 62 extends InternalFrameFocusTraversalPolicy 63 { 64 private Comparator<? super Component> comparator; 65 private boolean implicitDownCycleTraversal = true; 66 67 private PlatformLogger log = PlatformLogger.getLogger("javax.swing.SortingFocusTraversalPolicy"); 68 69 /** 70 * Used by getComponentAfter and getComponentBefore for efficiency. In 71 * order to maintain compliance with the specification of 72 * FocusTraversalPolicy, if traversal wraps, we should invoke 73 * getFirstComponent or getLastComponent. These methods may be overriden in 74 * subclasses to behave in a non-generic way. However, in the generic case, 75 * these methods will simply return the first or last Components of the 76 * sorted list, respectively. Since getComponentAfter and 77 * getComponentBefore have already built the sorted list before determining 78 * that they need to invoke getFirstComponent or getLastComponent, the 79 * sorted list should be reused if possible. 80 */ 81 transient private Container cachedRoot; 82 transient private List<Component> cachedCycle; 83 84 // Delegate our fitness test to ContainerOrder so that we only have to 85 // code the algorithm once. 86 private static final SwingContainerOrderFocusTraversalPolicy 87 fitnessTestPolicy = new SwingContainerOrderFocusTraversalPolicy(); 88 89 final private int FORWARD_TRAVERSAL = 0; 90 final private int BACKWARD_TRAVERSAL = 1; 91 92 /** 93 * Constructs a SortingFocusTraversalPolicy without a Comparator. 94 * Subclasses must set the Comparator using <code>setComparator</code> 95 * before installing this FocusTraversalPolicy on a focus cycle root or 96 * KeyboardFocusManager. 97 */ 98 protected SortingFocusTraversalPolicy() { 99 } 100 101 /** 102 * Constructs a SortingFocusTraversalPolicy with the specified Comparator. 103 */ 104 public SortingFocusTraversalPolicy(Comparator<? super Component> comparator) { 105 this.comparator = comparator; 106 } 107 108 private List<Component> getFocusTraversalCycle(Container aContainer) { 109 List<Component> cycle = new ArrayList<Component>(); 110 enumerateAndSortCycle(aContainer, cycle); 111 return cycle; 112 } 113 private int getComponentIndex(List<Component> cycle, Component aComponent) { 114 int index; 115 try { 116 index = Collections.binarySearch(cycle, aComponent, comparator); 117 } catch (ClassCastException e) { 118 if (log.isLoggable(PlatformLogger.Level.FINE)) { 119 log.fine("### During the binary search for " + aComponent + " the exception occurred: ", e); 120 } 121 return -1; 122 } 123 if (index < 0) { 124 // Fix for 5070991. 125 // A workaround for a transitivity problem caused by ROW_TOLERANCE, 126 // because of that the component may be missed in the binary search. 127 // Try to search it again directly. 128 index = cycle.indexOf(aComponent); 129 } 130 return index; 131 } 132 133 private void enumerateAndSortCycle(Container focusCycleRoot, List<Component> cycle) { 134 if (focusCycleRoot.isShowing()) { 135 enumerateCycle(focusCycleRoot, cycle); 136 Collections.sort(cycle, comparator); 137 } 138 } 139 140 private void enumerateCycle(Container container, List<Component> cycle) { 141 if (!(container.isVisible() && container.isDisplayable())) { 142 return; 143 } 144 145 cycle.add(container); 146 147 Component[] components = container.getComponents(); 148 for (Component comp : components) { 149 if (comp instanceof Container) { 150 Container cont = (Container)comp; 151 152 if (!cont.isFocusCycleRoot() && 153 !cont.isFocusTraversalPolicyProvider() && 154 !((cont instanceof JComponent) && ((JComponent)cont).isManagingFocus())) 155 { 156 enumerateCycle(cont, cycle); 157 continue; 158 } 159 } 160 cycle.add(comp); 161 } 162 } 163 164 Container getTopmostProvider(Container focusCycleRoot, Component aComponent) { 165 Container aCont = aComponent.getParent(); 166 Container ftp = null; 167 while (aCont != focusCycleRoot && aCont != null) { 168 if (aCont.isFocusTraversalPolicyProvider()) { 169 ftp = aCont; 170 } 171 aCont = aCont.getParent(); 172 } 173 if (aCont == null) { 174 return null; 175 } 176 return ftp; 177 } 178 179 /* 180 * Checks if a new focus cycle takes place and returns a Component to traverse focus to. 181 * @param comp a possible focus cycle root or policy provider 182 * @param traversalDirection the direction of the traversal 183 * @return a Component to traverse focus to if {@code comp} is a root or provider 184 * and implicit down-cycle is set, otherwise {@code null} 185 */ 186 private Component getComponentDownCycle(Component comp, int traversalDirection) { 187 Component retComp = null; 188 189 if (comp instanceof Container) { 190 Container cont = (Container)comp; 191 192 if (cont.isFocusCycleRoot()) { 193 if (getImplicitDownCycleTraversal()) { 194 retComp = cont.getFocusTraversalPolicy().getDefaultComponent(cont); 195 196 if (retComp != null && log.isLoggable(PlatformLogger.Level.FINE)) { 197 log.fine("### Transfered focus down-cycle to " + retComp + 198 " in the focus cycle root " + cont); 199 } 200 } else { 201 return null; 202 } 203 } else if (cont.isFocusTraversalPolicyProvider()) { 204 retComp = (traversalDirection == FORWARD_TRAVERSAL ? 205 cont.getFocusTraversalPolicy().getDefaultComponent(cont) : 206 cont.getFocusTraversalPolicy().getLastComponent(cont)); 207 208 if (retComp != null && log.isLoggable(PlatformLogger.Level.FINE)) { 209 log.fine("### Transfered focus to " + retComp + " in the FTP provider " + cont); 210 } 211 } 212 } 213 return retComp; 214 } 215 216 /** 217 * Returns the Component that should receive the focus after aComponent. 218 * aContainer must be a focus cycle root of aComponent or a focus traversal policy provider. 219 * <p> 220 * By default, SortingFocusTraversalPolicy implicitly transfers focus down- 221 * cycle. That is, during normal focus traversal, the Component 222 * traversed after a focus cycle root will be the focus-cycle-root's 223 * default Component to focus. This behavior can be disabled using the 224 * <code>setImplicitDownCycleTraversal</code> method. 225 * <p> 226 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus 227 * traversal policy provider</a>, the focus is always transferred down-cycle. 228 * 229 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider 230 * @param aComponent a (possibly indirect) child of aContainer, or 231 * aContainer itself 232 * @return the Component that should receive the focus after aComponent, or 233 * null if no suitable Component can be found 234 * @throws IllegalArgumentException if aContainer is not a focus cycle 235 * root of aComponent or a focus traversal policy provider, or if either aContainer or 236 * aComponent is null 237 */ 238 public Component getComponentAfter(Container aContainer, Component aComponent) { 239 if (log.isLoggable(PlatformLogger.Level.FINE)) { 240 log.fine("### Searching in " + aContainer + " for component after " + aComponent); 241 } 242 243 if (aContainer == null || aComponent == null) { 244 throw new IllegalArgumentException("aContainer and aComponent cannot be null"); 245 } 246 if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) { 247 throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider"); 248 249 } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) { 250 throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent"); 251 } 252 253 // Before all the ckecks below we first see if it's an FTP provider or a focus cycle root. 254 // If it's the case just go down cycle (if it's set to "implicit"). 255 Component comp = getComponentDownCycle(aComponent, FORWARD_TRAVERSAL); 256 if (comp != null) { 257 return comp; 258 } 259 260 // See if the component is inside of policy provider. 261 Container provider = getTopmostProvider(aContainer, aComponent); 262 if (provider != null) { 263 if (log.isLoggable(PlatformLogger.Level.FINE)) { 264 log.fine("### Asking FTP " + provider + " for component after " + aComponent); 265 } 266 267 // FTP knows how to find component after the given. We don't. 268 FocusTraversalPolicy policy = provider.getFocusTraversalPolicy(); 269 Component afterComp = policy.getComponentAfter(provider, aComponent); 270 271 // Null result means that we overstepped the limit of the FTP's cycle. 272 // In that case we must quit the cycle, otherwise return the component found. 273 if (afterComp != null) { 274 if (log.isLoggable(PlatformLogger.Level.FINE)) { 275 log.fine("### FTP returned " + afterComp); 276 } 277 return afterComp; 278 } 279 aComponent = provider; 280 } 281 282 List<Component> cycle = getFocusTraversalCycle(aContainer); 283 284 if (log.isLoggable(PlatformLogger.Level.FINE)) { 285 log.fine("### Cycle is " + cycle + ", component is " + aComponent); 286 } 287 288 int index = getComponentIndex(cycle, aComponent); 289 290 if (index < 0) { 291 if (log.isLoggable(PlatformLogger.Level.FINE)) { 292 log.fine("### Didn't find component " + aComponent + " in a cycle " + aContainer); 293 } 294 return getFirstComponent(aContainer); 295 } 296 297 for (index++; index < cycle.size(); index++) { 298 comp = cycle.get(index); 299 if (accept(comp)) { 300 return comp; 301 } else if ((comp = getComponentDownCycle(comp, FORWARD_TRAVERSAL)) != null) { 302 return comp; 303 } 304 } 305 306 if (aContainer.isFocusCycleRoot()) { 307 this.cachedRoot = aContainer; 308 this.cachedCycle = cycle; 309 310 comp = getFirstComponent(aContainer); 311 312 this.cachedRoot = null; 313 this.cachedCycle = null; 314 315 return comp; 316 } 317 return null; 318 } 319 320 /** 321 * Returns the Component that should receive the focus before aComponent. 322 * aContainer must be a focus cycle root of aComponent or a focus traversal policy provider. 323 * <p> 324 * By default, SortingFocusTraversalPolicy implicitly transfers focus down- 325 * cycle. That is, during normal focus traversal, the Component 326 * traversed after a focus cycle root will be the focus-cycle-root's 327 * default Component to focus. This behavior can be disabled using the 328 * <code>setImplicitDownCycleTraversal</code> method. 329 * <p> 330 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus 331 * traversal policy provider</a>, the focus is always transferred down-cycle. 332 * 333 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider 334 * @param aComponent a (possibly indirect) child of aContainer, or 335 * aContainer itself 336 * @return the Component that should receive the focus before aComponent, 337 * or null if no suitable Component can be found 338 * @throws IllegalArgumentException if aContainer is not a focus cycle 339 * root of aComponent or a focus traversal policy provider, or if either aContainer or 340 * aComponent is null 341 */ 342 public Component getComponentBefore(Container aContainer, Component aComponent) { 343 if (aContainer == null || aComponent == null) { 344 throw new IllegalArgumentException("aContainer and aComponent cannot be null"); 345 } 346 if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) { 347 throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider"); 348 349 } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) { 350 throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent"); 351 } 352 353 // See if the component is inside of policy provider. 354 Container provider = getTopmostProvider(aContainer, aComponent); 355 if (provider != null) { 356 if (log.isLoggable(PlatformLogger.Level.FINE)) { 357 log.fine("### Asking FTP " + provider + " for component after " + aComponent); 358 } 359 360 // FTP knows how to find component after the given. We don't. 361 FocusTraversalPolicy policy = provider.getFocusTraversalPolicy(); 362 Component beforeComp = policy.getComponentBefore(provider, aComponent); 363 364 // Null result means that we overstepped the limit of the FTP's cycle. 365 // In that case we must quit the cycle, otherwise return the component found. 366 if (beforeComp != null) { 367 if (log.isLoggable(PlatformLogger.Level.FINE)) { 368 log.fine("### FTP returned " + beforeComp); 369 } 370 return beforeComp; 371 } 372 aComponent = provider; 373 374 // If the provider is traversable it's returned. 375 if (accept(aComponent)) { 376 return aComponent; 377 } 378 } 379 380 List<Component> cycle = getFocusTraversalCycle(aContainer); 381 382 if (log.isLoggable(PlatformLogger.Level.FINE)) { 383 log.fine("### Cycle is " + cycle + ", component is " + aComponent); 384 } 385 386 int index = getComponentIndex(cycle, aComponent); 387 388 if (index < 0) { 389 if (log.isLoggable(PlatformLogger.Level.FINE)) { 390 log.fine("### Didn't find component " + aComponent + " in a cycle " + aContainer); 391 } 392 return getLastComponent(aContainer); 393 } 394 395 Component comp; 396 Component tryComp; 397 398 for (index--; index>=0; index--) { 399 comp = cycle.get(index); 400 if (comp != aContainer && (tryComp = getComponentDownCycle(comp, BACKWARD_TRAVERSAL)) != null) { 401 return tryComp; 402 } else if (accept(comp)) { 403 return comp; 404 } 405 } 406 407 if (aContainer.isFocusCycleRoot()) { 408 this.cachedRoot = aContainer; 409 this.cachedCycle = cycle; 410 411 comp = getLastComponent(aContainer); 412 413 this.cachedRoot = null; 414 this.cachedCycle = null; 415 416 return comp; 417 } 418 return null; 419 } 420 421 /** 422 * Returns the first Component in the traversal cycle. This method is used 423 * to determine the next Component to focus when traversal wraps in the 424 * forward direction. 425 * 426 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose 427 * first Component is to be returned 428 * @return the first Component in the traversal cycle of aContainer, 429 * or null if no suitable Component can be found 430 * @throws IllegalArgumentException if aContainer is null 431 */ 432 public Component getFirstComponent(Container aContainer) { 433 List<Component> cycle; 434 435 if (log.isLoggable(PlatformLogger.Level.FINE)) { 436 log.fine("### Getting first component in " + aContainer); 437 } 438 if (aContainer == null) { 439 throw new IllegalArgumentException("aContainer cannot be null"); 440 } 441 442 if (this.cachedRoot == aContainer) { 443 cycle = this.cachedCycle; 444 } else { 445 cycle = getFocusTraversalCycle(aContainer); 446 } 447 448 if (cycle.size() == 0) { 449 if (log.isLoggable(PlatformLogger.Level.FINE)) { 450 log.fine("### Cycle is empty"); 451 } 452 return null; 453 } 454 if (log.isLoggable(PlatformLogger.Level.FINE)) { 455 log.fine("### Cycle is " + cycle); 456 } 457 458 for (Component comp : cycle) { 459 if (accept(comp)) { 460 return comp; 461 } else if (comp != aContainer && 462 (comp = getComponentDownCycle(comp, FORWARD_TRAVERSAL)) != null) 463 { 464 return comp; 465 } 466 } 467 return null; 468 } 469 470 /** 471 * Returns the last Component in the traversal cycle. This method is used 472 * to determine the next Component to focus when traversal wraps in the 473 * reverse direction. 474 * 475 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose 476 * last Component is to be returned 477 * @return the last Component in the traversal cycle of aContainer, 478 * or null if no suitable Component can be found 479 * @throws IllegalArgumentException if aContainer is null 480 */ 481 public Component getLastComponent(Container aContainer) { 482 List<Component> cycle; 483 if (log.isLoggable(PlatformLogger.Level.FINE)) { 484 log.fine("### Getting last component in " + aContainer); 485 } 486 487 if (aContainer == null) { 488 throw new IllegalArgumentException("aContainer cannot be null"); 489 } 490 491 if (this.cachedRoot == aContainer) { 492 cycle = this.cachedCycle; 493 } else { 494 cycle = getFocusTraversalCycle(aContainer); 495 } 496 497 if (cycle.size() == 0) { 498 if (log.isLoggable(PlatformLogger.Level.FINE)) { 499 log.fine("### Cycle is empty"); 500 } 501 return null; 502 } 503 if (log.isLoggable(PlatformLogger.Level.FINE)) { 504 log.fine("### Cycle is " + cycle); 505 } 506 507 for (int i= cycle.size() - 1; i >= 0; i--) { 508 Component comp = cycle.get(i); 509 if (accept(comp)) { 510 return comp; 511 } else if (comp instanceof Container && comp != aContainer) { 512 Container cont = (Container)comp; 513 if (cont.isFocusTraversalPolicyProvider()) { 514 return cont.getFocusTraversalPolicy().getLastComponent(cont); 515 } 516 } 517 } 518 return null; 519 } 520 521 /** 522 * Returns the default Component to focus. This Component will be the first 523 * to receive focus when traversing down into a new focus traversal cycle 524 * rooted at aContainer. The default implementation of this method 525 * returns the same Component as <code>getFirstComponent</code>. 526 * 527 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose 528 * default Component is to be returned 529 * @return the default Component in the traversal cycle of aContainer, 530 * or null if no suitable Component can be found 531 * @see #getFirstComponent 532 * @throws IllegalArgumentException if aContainer is null 533 */ 534 public Component getDefaultComponent(Container aContainer) { 535 return getFirstComponent(aContainer); 536 } 537 538 /** 539 * Sets whether this SortingFocusTraversalPolicy transfers focus down-cycle 540 * implicitly. If <code>true</code>, during normal focus traversal, 541 * the Component traversed after a focus cycle root will be the focus- 542 * cycle-root's default Component to focus. If <code>false</code>, the 543 * next Component in the focus traversal cycle rooted at the specified 544 * focus cycle root will be traversed instead. The default value for this 545 * property is <code>true</code>. 546 * 547 * @param implicitDownCycleTraversal whether this 548 * SortingFocusTraversalPolicy transfers focus down-cycle implicitly 549 * @see #getImplicitDownCycleTraversal 550 * @see #getFirstComponent 551 */ 552 public void setImplicitDownCycleTraversal(boolean implicitDownCycleTraversal) { 553 this.implicitDownCycleTraversal = implicitDownCycleTraversal; 554 } 555 556 /** 557 * Returns whether this SortingFocusTraversalPolicy transfers focus down- 558 * cycle implicitly. If <code>true</code>, during normal focus 559 * traversal, the Component traversed after a focus cycle root will be the 560 * focus-cycle-root's default Component to focus. If <code>false</code>, 561 * the next Component in the focus traversal cycle rooted at the specified 562 * focus cycle root will be traversed instead. 563 * 564 * @return whether this SortingFocusTraversalPolicy transfers focus down- 565 * cycle implicitly 566 * @see #setImplicitDownCycleTraversal 567 * @see #getFirstComponent 568 */ 569 public boolean getImplicitDownCycleTraversal() { 570 return implicitDownCycleTraversal; 571 } 572 573 /** 574 * Sets the Comparator which will be used to sort the Components in a 575 * focus traversal cycle. 576 * 577 * @param comparator the Comparator which will be used for sorting 578 */ 579 protected void setComparator(Comparator<? super Component> comparator) { 580 this.comparator = comparator; 581 } 582 583 /** 584 * Returns the Comparator which will be used to sort the Components in a 585 * focus traversal cycle. 586 * 587 * @return the Comparator which will be used for sorting 588 */ 589 protected Comparator<? super Component> getComparator() { 590 return comparator; 591 } 592 593 /** 594 * Determines whether a Component is an acceptable choice as the new 595 * focus owner. By default, this method will accept a Component if and 596 * only if it is visible, displayable, enabled, and focusable. 597 * 598 * @param aComponent the Component whose fitness as a focus owner is to 599 * be tested 600 * @return <code>true</code> if aComponent is visible, displayable, 601 * enabled, and focusable; <code>false</code> otherwise 602 */ 603 protected boolean accept(Component aComponent) { 604 return fitnessTestPolicy.accept(aComponent); 605 } 606 } 607 608 // Create our own subclass and change accept to public so that we can call 609 // accept. 610 class SwingContainerOrderFocusTraversalPolicy 611 extends java.awt.ContainerOrderFocusTraversalPolicy 612 { 613 public boolean accept(Component aComponent) { 614 return super.accept(aComponent); 615 } 616 }