1 /* 2 * Copyright (c) 2000, 2006, 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 java.util.logging.*; 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 Logger log = Logger.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 private Container cachedRoot; 82 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 /** 90 * Constructs a SortingFocusTraversalPolicy without a Comparator. 91 * Subclasses must set the Comparator using <code>setComparator</code> 92 * before installing this FocusTraversalPolicy on a focus cycle root or 93 * KeyboardFocusManager. 94 */ 95 protected SortingFocusTraversalPolicy() { 96 } 97 98 /** 99 * Constructs a SortingFocusTraversalPolicy with the specified Comparator. 100 */ 101 public SortingFocusTraversalPolicy(Comparator<? super Component> comparator) { 102 this.comparator = comparator; 103 } 104 105 private void enumerateAndSortCycle(Container focusCycleRoot, 106 List<Component> cycle, Map defaults) { 107 List defaultRoots = null; 108 109 if (!focusCycleRoot.isShowing()) { 110 return; 111 } 112 113 enumerateCycle(focusCycleRoot, cycle); 114 115 boolean addDefaultComponents = 116 (defaults != null && getImplicitDownCycleTraversal()); 117 118 if (log.isLoggable(Level.FINE)) { 119 log.fine("### Will add defaults: " + addDefaultComponents); 120 } 121 122 // Create a list of all default Components which should be added 123 // to the list 124 if (addDefaultComponents) { 125 defaultRoots = new ArrayList(); 126 for (Iterator iter = cycle.iterator(); iter.hasNext(); ) { 127 Component comp = (Component)iter.next(); 128 if ((comp instanceof Container) && 129 ((Container)comp).isFocusCycleRoot()) 130 { 131 defaultRoots.add(comp); 132 } 133 } 134 Collections.sort(defaultRoots, comparator); 135 } 136 137 // Sort the Components in the cycle 138 Collections.sort(cycle, comparator); 139 140 // Find all of the roots in the cycle and place their default 141 // Components after them. Note that the roots may have been removed 142 // from the list because they were unfit. In that case, insert the 143 // default Components as though the roots were still in the list. 144 if (addDefaultComponents) { 145 for (ListIterator defaultRootsIter = 146 defaultRoots.listIterator(defaultRoots.size()); 147 defaultRootsIter.hasPrevious(); ) 148 { 149 Container root = (Container)defaultRootsIter.previous(); 150 Component defComp = 151 root.getFocusTraversalPolicy().getDefaultComponent(root); 152 153 if (defComp != null && defComp.isShowing()) { 154 int index = Collections.binarySearch(cycle, root, 155 comparator); 156 if (index < 0) { 157 // If root is not in the list, then binarySearch 158 // returns (-(insertion point) - 1). defComp follows 159 // the index one less than the insertion point. 160 161 index = -index - 2; 162 } 163 164 defaults.put(new Integer(index), defComp); 165 } 166 } 167 } 168 } 169 170 private void enumerateCycle(Container container, List<Component> cycle) { 171 if (!(container.isVisible() && container.isDisplayable())) { 172 return; 173 } 174 175 cycle.add(container); 176 177 Component[] components = container.getComponents(); 178 for (Component comp : components) { 179 if ((comp instanceof Container) 180 && !((Container)comp).isFocusTraversalPolicyProvider() 181 && !((Container)comp).isFocusCycleRoot() 182 && !((comp instanceof JComponent) 183 && ((JComponent)comp).isManagingFocus())) 184 { 185 enumerateCycle((Container)comp, cycle); 186 } else { 187 cycle.add(comp); 188 } 189 } 190 } 191 192 Container getTopmostProvider(Container focusCycleRoot, Component aComponent) { 193 Container aCont = aComponent.getParent(); 194 Container ftp = null; 195 while (aCont != focusCycleRoot && aCont != null) { 196 if (aCont.isFocusTraversalPolicyProvider()) { 197 ftp = aCont; 198 } 199 aCont = aCont.getParent(); 200 } 201 if (aCont == null) { 202 return null; 203 } 204 return ftp; 205 } 206 207 /** 208 * Returns the Component that should receive the focus after aComponent. 209 * aContainer must be a focus cycle root of aComponent or a focus traversal policy provider. 210 * <p> 211 * By default, SortingFocusTraversalPolicy implicitly transfers focus down- 212 * cycle. That is, during normal focus traversal, the Component 213 * traversed after a focus cycle root will be the focus-cycle-root's 214 * default Component to focus. This behavior can be disabled using the 215 * <code>setImplicitDownCycleTraversal</code> method. 216 * <p> 217 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus 218 * traversal policy provider</a>, the focus is always transferred down-cycle. 219 * 220 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider 221 * @param aComponent a (possibly indirect) child of aContainer, or 222 * aContainer itself 223 * @return the Component that should receive the focus after aComponent, or 224 * null if no suitable Component can be found 225 * @throws IllegalArgumentException if aContainer is not a focus cycle 226 * root of aComponent or a focus traversal policy provider, or if either aContainer or 227 * aComponent is null 228 */ 229 public Component getComponentAfter(Container aContainer, 230 Component aComponent) { 231 if (log.isLoggable(Level.FINE)) { 232 log.fine("### Searching in " + aContainer.getName() + " for component after " + aComponent.getName()); 233 } 234 235 if (aContainer == null || aComponent == null) { 236 throw new IllegalArgumentException("aContainer and aComponent cannot be null"); 237 } 238 if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) { 239 throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider"); 240 } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) { 241 throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent"); 242 } 243 244 // See if the component is inside of policy provider 245 Container ftp = getTopmostProvider(aContainer, aComponent); 246 if (ftp != null) { 247 if (log.isLoggable(Level.FINE)) { 248 log.fine("### Asking FTP " + ftp.getName() + " for component after " + aComponent.getName()); 249 } 250 // FTP knows how to find component after the given. We don't. 251 FocusTraversalPolicy policy = ftp.getFocusTraversalPolicy(); 252 Component retval = policy.getComponentAfter(ftp, aComponent); 253 if (retval == policy.getFirstComponent(ftp)) { 254 retval = null; 255 } 256 257 if (retval != null) { 258 if (log.isLoggable(Level.FINE)) { 259 log.fine("### FTP returned " + retval.getName()); 260 } 261 return retval; 262 } 263 aComponent = ftp; 264 } 265 266 List cycle = new ArrayList(); 267 Map defaults = new HashMap(); 268 enumerateAndSortCycle(aContainer, cycle, defaults); 269 270 int index; 271 try { 272 index = Collections.binarySearch(cycle, aComponent, comparator); 273 } catch (ClassCastException e) { 274 if (log.isLoggable(Level.FINE)) { 275 log.fine("### Didn't find component " + aComponent.getName() + " in a cycle " + aContainer.getName()); 276 } 277 return getFirstComponent(aContainer); 278 } 279 280 if (index < 0) { 281 // Fix for 5070991. 282 // A workaround for a transitivity problem caused by ROW_TOLERANCE, 283 // because of that the component may be missed in the binary search. 284 // Try to search it again directly. 285 int i = cycle.indexOf(aComponent); 286 if (i >= 0) { 287 index = i; 288 } else { 289 // If we're not in the cycle, then binarySearch returns 290 // (-(insertion point) - 1). The next element is our insertion 291 // point. 292 293 index = -index - 2; 294 } 295 } 296 297 Component defComp = (Component)defaults.get(new Integer(index)); 298 if (defComp != null) { 299 return defComp; 300 } 301 302 do { 303 index++; 304 305 if (index >= cycle.size()) { 306 if (aContainer.isFocusCycleRoot()) { 307 this.cachedRoot = aContainer; 308 this.cachedCycle = cycle; 309 310 Component retval = getFirstComponent(aContainer); 311 312 this.cachedRoot = null; 313 this.cachedCycle = null; 314 315 return retval; 316 } else { 317 return null; 318 } 319 } else { 320 Component comp = (Component)cycle.get(index); 321 if (accept(comp)) { 322 return comp; 323 } else if (comp instanceof Container && ((Container)comp).isFocusTraversalPolicyProvider()) { 324 return ((Container)comp).getFocusTraversalPolicy().getDefaultComponent((Container)comp); 325 } 326 } 327 } while (true); 328 } 329 330 /** 331 * Returns the Component that should receive the focus before aComponent. 332 * aContainer must be a focus cycle root of aComponent or a focus traversal policy provider. 333 * <p> 334 * By default, SortingFocusTraversalPolicy implicitly transfers focus down- 335 * cycle. That is, during normal focus traversal, the Component 336 * traversed after a focus cycle root will be the focus-cycle-root's 337 * default Component to focus. This behavior can be disabled using the 338 * <code>setImplicitDownCycleTraversal</code> method. 339 * <p> 340 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus 341 * traversal policy provider</a>, the focus is always transferred down-cycle. 342 * 343 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider 344 * @param aComponent a (possibly indirect) child of aContainer, or 345 * aContainer itself 346 * @return the Component that should receive the focus before aComponent, 347 * or null if no suitable Component can be found 348 * @throws IllegalArgumentException if aContainer is not a focus cycle 349 * root of aComponent or a focus traversal policy provider, or if either aContainer or 350 * aComponent is null 351 */ 352 public Component getComponentBefore(Container aContainer, 353 Component aComponent) { 354 if (aContainer == null || aComponent == null) { 355 throw new IllegalArgumentException("aContainer and aComponent cannot be null"); 356 } 357 if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) { 358 throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider"); 359 } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) { 360 throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent"); 361 } 362 363 // See if the component is inside of policy provider 364 Container ftp = getTopmostProvider(aContainer, aComponent); 365 if (ftp != null) { 366 if (log.isLoggable(Level.FINE)) { 367 log.fine("### Asking FTP " + ftp.getName() + " for component after " + aComponent.getName()); 368 } 369 // FTP knows how to find component after the given. We don't. 370 FocusTraversalPolicy policy = ftp.getFocusTraversalPolicy(); 371 Component retval = policy.getComponentBefore(ftp, aComponent); 372 if (retval == policy.getLastComponent(ftp)) { 373 retval = null; 374 } 375 if (retval != null) { 376 if (log.isLoggable(Level.FINE)) { 377 log.fine("### FTP returned " + retval.getName()); 378 } 379 return retval; 380 } 381 aComponent = ftp; 382 } 383 384 385 List cycle = new ArrayList(); 386 Map defaults = new HashMap(); 387 enumerateAndSortCycle(aContainer, cycle, defaults); 388 389 if (log.isLoggable(Level.FINE)) { 390 log.fine("### Cycle is " + cycle + ", component is " + aComponent); 391 } 392 393 int index; 394 try { 395 index = Collections.binarySearch(cycle, aComponent, comparator); 396 } catch (ClassCastException e) { 397 return getLastComponent(aContainer); 398 } 399 400 if (index < 0) { 401 // If we're not in the cycle, then binarySearch returns 402 // (-(insertion point) - 1). The previous element is our insertion 403 // point - 1. 404 405 index = -index - 2; 406 } else { 407 index--; 408 } 409 410 if (log.isLoggable(Level.FINE)) { 411 log.fine("### Index is " + index); 412 } 413 414 if (index >= 0) { 415 Component defComp = (Component)defaults.get(new Integer(index)); 416 if (defComp != null && cycle.get(index) != aContainer) { 417 if (log.isLoggable(Level.FINE)) { 418 log.fine("### Returning default " + defComp.getName() + " at " + index); 419 } 420 return defComp; 421 } 422 } 423 424 do { 425 if (index < 0) { 426 this.cachedRoot = aContainer; 427 this.cachedCycle = cycle; 428 429 Component retval = getLastComponent(aContainer); 430 431 this.cachedRoot = null; 432 this.cachedCycle = null; 433 434 return retval; 435 } else { 436 Component comp = (Component)cycle.get(index); 437 if (accept(comp)) { 438 return comp; 439 } else if (comp instanceof Container && ((Container)comp).isFocusTraversalPolicyProvider()) { 440 return ((Container)comp).getFocusTraversalPolicy().getLastComponent((Container)comp); 441 } 442 } 443 index--; 444 } while (true); 445 } 446 447 /** 448 * Returns the first Component in the traversal cycle. This method is used 449 * to determine the next Component to focus when traversal wraps in the 450 * forward direction. 451 * 452 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose 453 * first Component is to be returned 454 * @return the first Component in the traversal cycle of aContainer, 455 * or null if no suitable Component can be found 456 * @throws IllegalArgumentException if aContainer is null 457 */ 458 public Component getFirstComponent(Container aContainer) { 459 List<Component> cycle; 460 461 if (log.isLoggable(Level.FINE)) { 462 log.fine("### Getting first component in " + aContainer.getName()); 463 } 464 if (aContainer == null) { 465 throw new IllegalArgumentException("aContainer cannot be null"); 466 } 467 468 if (this.cachedRoot == aContainer) { 469 cycle = this.cachedCycle; 470 } else { 471 cycle = new ArrayList<Component>(); 472 enumerateAndSortCycle(aContainer, cycle, null); 473 } 474 475 int size = cycle.size(); 476 if (size == 0) { 477 return null; 478 } 479 480 for (Component comp : cycle) { 481 if (accept(comp)) { 482 return comp; 483 } else if (comp instanceof Container && !(comp == aContainer) && ((Container)comp).isFocusTraversalPolicyProvider()) { 484 return ((Container)comp).getFocusTraversalPolicy().getDefaultComponent((Container)comp); 485 } 486 } 487 return null; 488 } 489 490 /** 491 * Returns the last Component in the traversal cycle. This method is used 492 * to determine the next Component to focus when traversal wraps in the 493 * reverse direction. 494 * 495 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose 496 * last Component is to be returned 497 * @return the last Component in the traversal cycle of aContainer, 498 * or null if no suitable Component can be found 499 * @throws IllegalArgumentException if aContainer is null 500 */ 501 public Component getLastComponent(Container aContainer) { 502 List<Component> cycle; 503 if (log.isLoggable(Level.FINE)) { 504 log.fine("### Getting last component in " + aContainer.getName()); 505 } 506 507 if (aContainer == null) { 508 throw new IllegalArgumentException("aContainer cannot be null"); 509 } 510 511 if (this.cachedRoot == aContainer) { 512 cycle = this.cachedCycle; 513 } else { 514 cycle = new ArrayList<Component>(); 515 enumerateAndSortCycle(aContainer, cycle, null); 516 } 517 518 int size = cycle.size(); 519 if (size == 0) { 520 if (log.isLoggable(Level.FINE)) { 521 log.fine("### Cycle is empty"); 522 } 523 return null; 524 } 525 if (log.isLoggable(Level.FINE)) { 526 log.fine("### Cycle is " + cycle); 527 } 528 529 for (int i= cycle.size()-1; i >= 0; i--) { 530 Component comp = (Component)cycle.get(i); 531 if (accept(comp)) { 532 return comp; 533 } else if (comp instanceof Container && !(comp == aContainer) && ((Container)comp).isFocusTraversalPolicyProvider()) { 534 return ((Container)comp).getFocusTraversalPolicy().getLastComponent((Container)comp); 535 } 536 } 537 return null; 538 } 539 540 /** 541 * Returns the default Component to focus. This Component will be the first 542 * to receive focus when traversing down into a new focus traversal cycle 543 * rooted at aContainer. The default implementation of this method 544 * returns the same Component as <code>getFirstComponent</code>. 545 * 546 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose 547 * default Component is to be returned 548 * @return the default Component in the traversal cycle of aContainer, 549 * or null if no suitable Component can be found 550 * @see #getFirstComponent 551 * @throws IllegalArgumentException if aContainer is null 552 */ 553 public Component getDefaultComponent(Container aContainer) { 554 return getFirstComponent(aContainer); 555 } 556 557 /** 558 * Sets whether this SortingFocusTraversalPolicy transfers focus down-cycle 559 * implicitly. If <code>true</code>, during normal focus traversal, 560 * the Component traversed after a focus cycle root will be the focus- 561 * cycle-root's default Component to focus. If <code>false</code>, the 562 * next Component in the focus traversal cycle rooted at the specified 563 * focus cycle root will be traversed instead. The default value for this 564 * property is <code>true</code>. 565 * 566 * @param implicitDownCycleTraversal whether this 567 * SortingFocusTraversalPolicy transfers focus down-cycle implicitly 568 * @see #getImplicitDownCycleTraversal 569 * @see #getFirstComponent 570 */ 571 public void setImplicitDownCycleTraversal(boolean 572 implicitDownCycleTraversal) { 573 this.implicitDownCycleTraversal = implicitDownCycleTraversal; 574 } 575 576 /** 577 * Returns whether this SortingFocusTraversalPolicy transfers focus down- 578 * cycle implicitly. If <code>true</code>, during normal focus 579 * traversal, the Component traversed after a focus cycle root will be the 580 * focus-cycle-root's default Component to focus. If <code>false</code>, 581 * the next Component in the focus traversal cycle rooted at the specified 582 * focus cycle root will be traversed instead. 583 * 584 * @return whether this SortingFocusTraversalPolicy transfers focus down- 585 * cycle implicitly 586 * @see #setImplicitDownCycleTraversal 587 * @see #getFirstComponent 588 */ 589 public boolean getImplicitDownCycleTraversal() { 590 return implicitDownCycleTraversal; 591 } 592 593 /** 594 * Sets the Comparator which will be used to sort the Components in a 595 * focus traversal cycle. 596 * 597 * @param comparator the Comparator which will be used for sorting 598 */ 599 protected void setComparator(Comparator<? super Component> comparator) { 600 this.comparator = comparator; 601 } 602 603 /** 604 * Returns the Comparator which will be used to sort the Components in a 605 * focus traversal cycle. 606 * 607 * @return the Comparator which will be used for sorting 608 */ 609 protected Comparator<? super Component> getComparator() { 610 return comparator; 611 } 612 613 /** 614 * Determines whether a Component is an acceptable choice as the new 615 * focus owner. By default, this method will accept a Component if and 616 * only if it is visible, displayable, enabled, and focusable. 617 * 618 * @param aComponent the Component whose fitness as a focus owner is to 619 * be tested 620 * @return <code>true</code> if aComponent is visible, displayable, 621 * enabled, and focusable; <code>false</code> otherwise 622 */ 623 protected boolean accept(Component aComponent) { 624 return fitnessTestPolicy.accept(aComponent); 625 } 626 } 627 628 // Create our own subclass and change accept to public so that we can call 629 // accept. 630 class SwingContainerOrderFocusTraversalPolicy 631 extends java.awt.ContainerOrderFocusTraversalPolicy 632 { 633 public boolean accept(Component aComponent) { 634 return super.accept(aComponent); 635 } 636 }