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