1 /* 2 * Copyright (c) 2000, 2003, 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.util.logging.*; 28 29 /** 30 * A FocusTraversalPolicy that determines traversal order based on the order 31 * of child Components in a Container. From a particular focus cycle root, the 32 * policy makes a pre-order traversal of the Component hierarchy, and traverses 33 * a Container's children according to the ordering of the array returned by 34 * <code>Container.getComponents()</code>. Portions of the hierarchy that are 35 * not visible and displayable will not be searched. 36 * <p> 37 * By default, ContainerOrderFocusTraversalPolicy implicitly transfers focus 38 * down-cycle. That is, during normal forward focus traversal, the Component 39 * traversed after a focus cycle root will be the focus-cycle-root's default 40 * Component to focus. This behavior can be disabled using the 41 * <code>setImplicitDownCycleTraversal</code> method. 42 * <p> 43 * By default, methods of this class with return a Component only if it is 44 * visible, displayable, enabled, and focusable. Subclasses can modify this 45 * behavior by overriding the <code>accept</code> method. 46 * <p> 47 * This policy takes into account <a 48 * href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal 49 * policy providers</a>. When searching for first/last/next/previous Component, 50 * if a focus traversal policy provider is encountered, its focus traversal 51 * policy is used to perform the search operation. 52 * 53 * @author David Mendenhall 54 * 55 * @see Container#getComponents 56 * @since 1.4 57 */ 58 public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy 59 implements java.io.Serializable 60 { 61 private static final MutableBoolean found = new MutableBoolean(); 62 63 private static final Logger log = Logger.getLogger("java.awt.ContainerOrderFocusTraversalPolicy"); 64 65 /* 66 * JDK 1.4 serialVersionUID 67 */ 68 private static final long serialVersionUID = 486933713763926351L; 69 70 private boolean implicitDownCycleTraversal = true; 71 72 /** 73 * Returns the Component that should receive the focus after aComponent. 74 * aContainer must be a focus cycle root of aComponent or a focus traversal policy provider. 75 * <p> 76 * By default, ContainerOrderFocusTraversalPolicy implicitly transfers 77 * focus down-cycle. That is, during normal forward focus traversal, the 78 * Component traversed after a focus cycle root will be the focus-cycle- 79 * root's default Component to focus. This behavior can be disabled using 80 * the <code>setImplicitDownCycleTraversal</code> method. 81 * <p> 82 * If aContainer is <a href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus 83 * traversal policy provider</a>, the focus is always transferred down-cycle. 84 * 85 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider 86 * @param aComponent a (possibly indirect) child of aContainer, or 87 * aContainer itself 88 * @return the Component that should receive the focus after aComponent, or 89 * null if no suitable Component can be found 90 * @throws IllegalArgumentException if aContainer is not a focus cycle 91 * root of aComponent or focus traversal policy provider, or if either aContainer or 92 * aComponent is null 93 */ 94 public Component getComponentAfter(Container aContainer, Component aComponent) { 95 if (log.isLoggable(Level.FINE)) { 96 log.fine("Looking for next component in " + aContainer + " for " + aComponent); 97 } 98 if (aContainer == null || aComponent == null) { 99 throw new IllegalArgumentException("aContainer and aComponent cannot be null"); 100 } 101 if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) { 102 throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider"); 103 } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) { 104 throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent"); 105 } 106 107 synchronized(aContainer.getTreeLock()) { 108 found.value = false; 109 Component retval = getComponentAfter(aContainer, aComponent, 110 found); 111 if (retval != null) { 112 if (log.isLoggable(Level.FINE)) { 113 log.fine("After component is " + retval); 114 } 115 return retval; 116 } else if (found.value) { 117 if (log.isLoggable(Level.FINE)) { 118 log.fine("Didn't find next component in " + aContainer + " - falling back to the first "); 119 } 120 return getFirstComponent(aContainer); 121 } else { 122 if (log.isLoggable(Level.FINE)) { 123 log.fine("After component is null"); 124 } 125 return null; 126 } 127 } 128 } 129 130 private Component getComponentAfter(Container aContainer, 131 Component aComponent, 132 MutableBoolean found) { 133 if (!(aContainer.isVisible() && aContainer.isDisplayable())) { 134 return null; 135 } 136 137 if (found.value) { 138 if (accept(aContainer)) { 139 return aContainer; 140 } 141 } else if (aContainer == aComponent) { 142 found.value = true; 143 } 144 145 for (int i = 0; i < aContainer.getComponentCount(); i++) { 146 Component comp = aContainer.getComponent(i); 147 if ((comp instanceof Container) && 148 !((Container)comp).isFocusCycleRoot()) { 149 Component retval = null; 150 if (((Container)comp).isFocusTraversalPolicyProvider()) { 151 if (log.isLoggable(Level.FINE)) { 152 log.fine("Entering FTP " + comp); 153 } 154 Container cont = (Container) comp; 155 FocusTraversalPolicy policy = cont.getFocusTraversalPolicy(); 156 if (log.isLoggable(Level.FINE)) { 157 log.fine("FTP contains " + aComponent + ": " + cont.isAncestorOf(aComponent)); 158 } 159 if (found.value) { 160 retval = policy.getDefaultComponent(cont); 161 if (log.isLoggable(Level.FINE)) { 162 log.fine("Used FTP for getting default component: " + retval); 163 } 164 } else { 165 found.value = cont.isAncestorOf(aComponent); 166 if (found.value) { 167 if (aComponent == policy.getLastComponent(cont)) { 168 // Reached last component, going to wrap - should switch to next provider 169 retval = null; 170 } else { 171 retval = policy.getComponentAfter(cont, aComponent); 172 if (log.isLoggable(Level.FINE)) { 173 log.fine("FTP found next for the component : " + retval); 174 } 175 } 176 } 177 } 178 } else { 179 retval = getComponentAfter((Container)comp, 180 aComponent, 181 found); 182 } 183 if (retval != null) { 184 return retval; 185 } 186 } else if (found.value) { 187 if (accept(comp)) { 188 return comp; 189 } 190 } else if (comp == aComponent) { 191 found.value = true; 192 } 193 194 if (found.value && 195 getImplicitDownCycleTraversal() && 196 (comp instanceof Container) && 197 ((Container)comp).isFocusCycleRoot()) 198 { 199 Container cont = (Container)comp; 200 Component retval = cont.getFocusTraversalPolicy(). 201 getDefaultComponent(cont); 202 if (retval != null) { 203 return retval; 204 } 205 } 206 } 207 208 return null; 209 } 210 211 /** 212 * Returns the Component that should receive the focus before aComponent. 213 * aContainer must be a focus cycle root of aComponent or a <a 214 * href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal policy 215 * provider</a>. 216 * 217 * @param aContainer a focus cycle root of aComponent or focus traversal policy provider 218 * @param aComponent a (possibly indirect) child of aContainer, or 219 * aContainer itself 220 * @return the Component that should receive the focus before aComponent, 221 * or null if no suitable Component can be found 222 * @throws IllegalArgumentException if aContainer is not a focus cycle 223 * root of aComponent or focus traversal policy provider, or if either aContainer or 224 * aComponent is null 225 */ 226 public Component getComponentBefore(Container aContainer, 227 Component aComponent) { 228 if (aContainer == null || aComponent == null) { 229 throw new IllegalArgumentException("aContainer and aComponent cannot be null"); 230 } 231 if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) { 232 throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider"); 233 } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) { 234 throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent"); 235 } 236 synchronized(aContainer.getTreeLock()) { 237 found.value = false; 238 Component retval = getComponentBefore(aContainer, aComponent, 239 found); 240 if (retval != null) { 241 if (log.isLoggable(Level.FINE)) { 242 log.fine("Before component is " + retval); 243 } 244 return retval; 245 } else if (found.value) { 246 if (log.isLoggable(Level.FINE)) { 247 log.fine("Didn't find before component in " + aContainer + " - falling back to the first "); 248 } 249 return getLastComponent(aContainer); 250 } else { 251 if (log.isLoggable(Level.FINE)) { 252 log.fine("Before component is null"); 253 } 254 return null; 255 } 256 } 257 } 258 259 private Component getComponentBefore(Container aContainer, 260 Component aComponent, 261 MutableBoolean found) { 262 if (!(aContainer.isVisible() && aContainer.isDisplayable())) { 263 return null; 264 } 265 266 for (int i = aContainer.getComponentCount() - 1; i >= 0; i--) { 267 Component comp = aContainer.getComponent(i); 268 if (comp == aComponent) { 269 found.value = true; 270 } else if ((comp instanceof Container) && 271 !((Container)comp).isFocusCycleRoot()) { 272 Component retval = null; 273 if (((Container)comp).isFocusTraversalPolicyProvider()) { 274 if (log.isLoggable(Level.FINE)) { 275 log.fine("Entering FTP " + comp); 276 } 277 Container cont = (Container) comp; 278 FocusTraversalPolicy policy = cont.getFocusTraversalPolicy(); 279 if (log.isLoggable(Level.FINE)) { 280 log.fine("FTP contains " + aComponent + ": " + cont.isAncestorOf(aComponent)); 281 } 282 if (found.value) { 283 retval = policy.getLastComponent(cont); 284 if (log.isLoggable(Level.FINE)) { 285 log.fine("Used FTP for getting last component: " + retval); 286 } 287 } else { 288 found.value = cont.isAncestorOf(aComponent); 289 if (found.value) { 290 if (aComponent == policy.getFirstComponent(cont)) { 291 retval = null; 292 } else { 293 retval = policy.getComponentBefore(cont, aComponent); 294 if (log.isLoggable(Level.FINE)) { 295 log.fine("FTP found previous for the component : " + retval); 296 } 297 } 298 } 299 } 300 } else { 301 retval = getComponentBefore((Container)comp, 302 aComponent, 303 found); 304 } 305 if (retval != null) { 306 return retval; 307 } 308 } else if (found.value) { 309 if (accept(comp)) { 310 return comp; 311 } 312 } 313 } 314 315 if (found.value) { 316 if (accept(aContainer)) { 317 return aContainer; 318 } 319 } else if (aContainer == aComponent) { 320 found.value = true; 321 } 322 323 return null; 324 } 325 326 /** 327 * Returns the first Component in the traversal cycle. This method is used 328 * to determine the next Component to focus when traversal wraps in the 329 * forward direction. 330 * 331 * @param aContainer the focus cycle root or focus traversal policy provider whose first 332 * Component is to be returned 333 * @return the first Component in the traversal cycle of aContainer, 334 * or null if no suitable Component can be found 335 * @throws IllegalArgumentException if aContainer is null 336 */ 337 public Component getFirstComponent(Container aContainer) { 338 if (aContainer == null) { 339 throw new IllegalArgumentException("aContainer cannot be null"); 340 } 341 342 synchronized(aContainer.getTreeLock()) { 343 if (!(aContainer.isVisible() && 344 aContainer.isDisplayable())) 345 { 346 return null; 347 } 348 349 if (accept(aContainer)) { 350 return aContainer; 351 } 352 353 for (int i = 0; i < aContainer.getComponentCount(); i++) { 354 Component comp = aContainer.getComponent(i); 355 if (comp instanceof Container && 356 !((Container)comp).isFocusCycleRoot()) 357 { 358 Component retval = null; 359 Container cont = (Container)comp; 360 if (cont.isFocusTraversalPolicyProvider()) { 361 FocusTraversalPolicy policy = cont.getFocusTraversalPolicy(); 362 retval = policy.getDefaultComponent(cont); 363 } else { 364 retval = getFirstComponent((Container)comp); 365 } 366 if (retval != null) { 367 return retval; 368 } 369 } else if (accept(comp)) { 370 return comp; 371 } 372 } 373 } 374 375 return null; 376 } 377 378 /** 379 * Returns the last Component in the traversal cycle. This method is used 380 * to determine the next Component to focus when traversal wraps in the 381 * reverse direction. 382 * 383 * @param aContainer the focus cycle root or focus traversal policy provider whose last 384 * Component is to be returned 385 * @return the last Component in the traversal cycle of aContainer, 386 * or null if no suitable Component can be found 387 * @throws IllegalArgumentException if aContainer is null 388 */ 389 public Component getLastComponent(Container aContainer) { 390 if (aContainer == null) { 391 throw new IllegalArgumentException("aContainer cannot be null"); 392 } 393 if (log.isLoggable(Level.FINE)) { 394 log.fine("Looking for the last component in " + aContainer); 395 } 396 397 synchronized(aContainer.getTreeLock()) { 398 if (!(aContainer.isVisible() && 399 aContainer.isDisplayable())) 400 { 401 return null; 402 } 403 404 for (int i = aContainer.getComponentCount() - 1; i >= 0; i--) { 405 Component comp = aContainer.getComponent(i); 406 if (comp instanceof Container && 407 !((Container)comp).isFocusCycleRoot()) 408 { 409 Component retval = null; 410 Container cont = (Container)comp; 411 if (cont.isFocusTraversalPolicyProvider()) { 412 if (log.isLoggable(Level.FINE)) { 413 log.fine("\tEntering FTP " + cont); 414 } 415 FocusTraversalPolicy policy = cont.getFocusTraversalPolicy(); 416 retval = policy.getLastComponent(cont); 417 } else { 418 if (log.isLoggable(Level.FINE)) { 419 log.fine("\tEntering sub-container"); 420 } 421 retval = getLastComponent((Container)comp); 422 } 423 if (retval != null) { 424 if (log.isLoggable(Level.FINE)) { 425 log.fine("\tFound last component : " + retval); 426 } 427 return retval; 428 } 429 } else if (accept(comp)) { 430 return comp; 431 } 432 } 433 434 if (accept(aContainer)) { 435 return aContainer; 436 } 437 } 438 439 return null; 440 } 441 442 /** 443 * Returns the default Component to focus. This Component will be the first 444 * to receive focus when traversing down into a new focus traversal cycle 445 * rooted at aContainer. The default implementation of this method 446 * returns the same Component as <code>getFirstComponent</code>. 447 * 448 * @param aContainer the focus cycle root or focus traversal policy provider whose default 449 * Component is to be returned 450 * @return the default Component in the traversal cycle of aContainer, 451 * or null if no suitable Component can be found 452 * @see #getFirstComponent 453 * @throws IllegalArgumentException if aContainer is null 454 */ 455 public Component getDefaultComponent(Container aContainer) { 456 return getFirstComponent(aContainer); 457 } 458 459 /** 460 * Sets whether this ContainerOrderFocusTraversalPolicy transfers focus 461 * down-cycle implicitly. If <code>true</code>, during normal forward focus 462 * traversal, the Component traversed after a focus cycle root will be the 463 * focus-cycle-root's default Component to focus. If <code>false</code>, 464 * the next Component in the focus traversal cycle rooted at the specified 465 * focus cycle root will be traversed instead. The default value for this 466 * property is <code>true</code>. 467 * 468 * @param implicitDownCycleTraversal whether this 469 * ContainerOrderFocusTraversalPolicy transfers focus down-cycle 470 * implicitly 471 * @see #getImplicitDownCycleTraversal 472 * @see #getFirstComponent 473 */ 474 public void setImplicitDownCycleTraversal(boolean 475 implicitDownCycleTraversal) { 476 this.implicitDownCycleTraversal = implicitDownCycleTraversal; 477 } 478 479 /** 480 * Returns whether this ContainerOrderFocusTraversalPolicy transfers focus 481 * down-cycle implicitly. If <code>true</code>, during normal forward focus 482 * traversal, the Component traversed after a focus cycle root will be the 483 * focus-cycle-root's default Component to focus. If <code>false</code>, 484 * the next Component in the focus traversal cycle rooted at the specified 485 * focus cycle root will be traversed instead. 486 * 487 * @return whether this ContainerOrderFocusTraversalPolicy transfers focus 488 * down-cycle implicitly 489 * @see #setImplicitDownCycleTraversal 490 * @see #getFirstComponent 491 */ 492 public boolean getImplicitDownCycleTraversal() { 493 return implicitDownCycleTraversal; 494 } 495 496 /** 497 * Determines whether a Component is an acceptable choice as the new 498 * focus owner. By default, this method will accept a Component if and 499 * only if it is visible, displayable, enabled, and focusable. 500 * 501 * @param aComponent the Component whose fitness as a focus owner is to 502 * be tested 503 * @return <code>true</code> if aComponent is visible, displayable, 504 * enabled, and focusable; <code>false</code> otherwise 505 */ 506 protected boolean accept(Component aComponent) { 507 if (!aComponent.canBeFocusOwner()) { 508 return false; 509 } 510 511 // Verify that the Component is recursively enabled. Disabling a 512 // heavyweight Container disables its children, whereas disabling 513 // a lightweight Container does not. 514 if (!(aComponent instanceof Window)) { 515 for (Container enableTest = aComponent.getParent(); 516 enableTest != null; 517 enableTest = enableTest.getParent()) 518 { 519 if (!(enableTest.isEnabled() || enableTest.isLightweight())) { 520 return false; 521 } 522 if (enableTest instanceof Window) { 523 break; 524 } 525 } 526 } 527 528 return true; 529 } 530 531 } 532 533 534 class MutableBoolean { 535 boolean value = false; 536 }