1 /* ContainerOrderFocusTraversalPolicy.java -- 2 Copyright (C) 2002, 2005 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package java.awt; 40 41 import java.io.Serializable; 42 43 /** 44 * ContainerOrderFocusTraversalPolicy defines a focus traversal order 45 * based on the order in which Components were packed in a Container. 46 * This policy performs a pre-order traversal of the Component 47 * hierarchy starting from a given focus cycle root. Portions of the 48 * hierarchy that are not visible and displayable are skipped. 49 * 50 * By default, this policy transfers focus down-cycle implicitly. 51 * That is, if a forward traversal is requested on a focus cycle root 52 * and the focus cycle root has focusable children, the focus will 53 * automatically be transfered down to the lower focus cycle. 54 * 55 * The default implementation of accept accepts only Components that 56 * are visible, displayable, enabled and focusable. Derived classes 57 * can override these acceptance criteria by overriding accept. 58 * 59 * @author Michael Koch 60 * @author Thomas Fitzsimmons (fitzsim@redhat.com) 61 * @since 1.4 62 */ 63 public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy 64 implements Serializable 65 { 66 /** 67 * Compatible to JDK 1.4+ 68 */ 69 static final long serialVersionUID = 486933713763926351L; 70 71 /** 72 * True if implicit down cycling is enabled. 73 */ 74 private boolean implicitDownCycleTraversal = true; 75 76 /** 77 * Creates the <code>ContainerOrderFocusTraversalPolicy</code> object. 78 */ ContainerOrderFocusTraversalPolicy()79 public ContainerOrderFocusTraversalPolicy () 80 { 81 // Nothing to do here 82 } 83 84 /** 85 * Returns the Component that should receive the focus after current. 86 * root must be a focus cycle root of current. 87 * 88 * @param root a focus cycle root of current 89 * @param current a (possibly indirect) child of root, or root itself 90 * 91 * @return the next Component in the focus traversal order for root, 92 * or null if no acceptable Component exists. 93 * 94 * @exception IllegalArgumentException If root is not a focus cycle 95 * root of current, or if either root or current is null. 96 */ getComponentAfter(Container root, Component current)97 public Component getComponentAfter (Container root, Component current) 98 { 99 if (root == null) 100 throw new IllegalArgumentException ("focus cycle root is null"); 101 if (current == null) 102 throw new IllegalArgumentException ("current component is null"); 103 104 if (!root.isFocusCycleRoot ()) 105 throw new IllegalArgumentException ("root is not a focus cycle root"); 106 107 Container ancestor = current.getFocusCycleRootAncestor (); 108 Container prevAncestor = ancestor; 109 while (ancestor != root) 110 { 111 ancestor = current.getFocusCycleRootAncestor (); 112 if (ancestor == prevAncestor) 113 { 114 // We've reached the top focus cycle root ancestor. Check 115 // if it is root. 116 if (ancestor != root) 117 throw new IllegalArgumentException ("the given container is not" 118 + " a focus cycle root of the" 119 + " current component"); 120 else 121 break; 122 } 123 prevAncestor = ancestor; 124 } 125 126 // FIXME: is this the right thing to do here? It moves the context 127 // for traversal up one focus traversal cycle. We'll need a test 128 // for this. 129 if ((Component) root == current) 130 root = current.getFocusCycleRootAncestor (); 131 132 // Check if we've reached the top of the component hierarchy. If 133 // so then we want to loop around to the first component in the 134 // focus traversal cycle. 135 if (current instanceof Window) 136 return getFirstComponent ((Container) current); 137 138 Container parent = current.getParent (); 139 140 synchronized (parent.getTreeLock ()) 141 { 142 Component[] components = parent.getComponents (); 143 int componentIndex = 0; 144 int numComponents = parent.getComponentCount (); 145 146 // Find component's index. 147 for (int i = 0; i < numComponents; i++) 148 { 149 if (components[i] == current) 150 componentIndex = i; 151 } 152 153 // Search forward for the next acceptable component. 154 for (int i = componentIndex + 1; i < numComponents; i++) 155 { 156 if (accept (components[i])) 157 return components[i]; 158 159 if (components[i] instanceof Container) 160 { 161 Component result = getFirstComponent ((Container) components[i]); 162 163 if (result != null 164 && implicitDownCycleTraversal) 165 return result; 166 } 167 } 168 169 // No focusable components after current in its Container. So go 170 // to the next Component after current's Container (parent). 171 Component result = getComponentAfter (root, parent); 172 173 return result; 174 } 175 } 176 177 /** 178 * Returns the Component that should receive the focus before 179 * <code>current</code>. <code>root</code> must be a focus cycle 180 * root of current. 181 * 182 * @param root a focus cycle root of current 183 * @param current a (possibly indirect) child of root, or root itself 184 * 185 * @return the previous Component in the focus traversal order for 186 * root, or null if no acceptable Component exists. 187 * 188 * @exception IllegalArgumentException If root is not a focus cycle 189 * root of current, or if either root or current is null. 190 */ getComponentBefore(Container root, Component current)191 public Component getComponentBefore (Container root, Component current) 192 { 193 if (root == null) 194 throw new IllegalArgumentException ("focus cycle root is null"); 195 if (current == null) 196 throw new IllegalArgumentException ("current component is null"); 197 198 if (!root.isFocusCycleRoot ()) 199 throw new IllegalArgumentException ("root is not a focus cycle root"); 200 201 Container ancestor = current.getFocusCycleRootAncestor (); 202 Container prevAncestor = ancestor; 203 while (ancestor != root) 204 { 205 ancestor = current.getFocusCycleRootAncestor (); 206 if (ancestor == prevAncestor) 207 { 208 // We've reached the top focus cycle root ancestor. Check 209 // if it is root. 210 if (ancestor != root) 211 throw new IllegalArgumentException ("the given container is not" 212 + " a focus cycle root of the" 213 + " current component"); 214 else 215 break; 216 } 217 prevAncestor = ancestor; 218 } 219 220 // FIXME: is this the right thing to do here? It moves the context 221 // for traversal up one focus traversal cycle. We'll need a test 222 // for this. 223 if ((Component) root == current) 224 root = current.getFocusCycleRootAncestor (); 225 226 // Check if we've reached the top of the component hierarchy. If 227 // so then we want to loop around to the last component in the 228 // focus traversal cycle. 229 if (current instanceof Window) 230 return getLastComponent ((Container) current); 231 232 Container parent = current.getParent (); 233 234 synchronized (parent.getTreeLock ()) 235 { 236 Component[] components = parent.getComponents (); 237 int componentIndex = 0; 238 int numComponents = parent.getComponentCount (); 239 240 // Find component's index. 241 for (int i = 0; i < numComponents; i++) 242 { 243 if (components[i] == current) 244 componentIndex = i; 245 } 246 247 // Search backward for the next acceptable component. 248 for (int i = componentIndex - 1; i >= 0; i--) 249 { 250 if (accept (components[i])) 251 return components[i]; 252 253 if (components[i] instanceof Container) 254 { 255 Component result = getLastComponent ((Container) components[i]); 256 257 if (result != null) 258 return result; 259 } 260 } 261 262 // No focusable components before current in its Container. So go 263 // to the previous Component before current's Container (parent). 264 Component result = getComponentBefore (root, parent); 265 266 return result; 267 } 268 } 269 270 /** 271 * Returns the first Component of root that should receive the focus. 272 * 273 * @param root a focus cycle root 274 * 275 * @return the first Component in the focus traversal order for 276 * root, or null if no acceptable Component exists. 277 * 278 * @exception IllegalArgumentException If root is null. 279 */ getFirstComponent(Container root)280 public Component getFirstComponent(Container root) 281 { 282 if (root == null) 283 throw new IllegalArgumentException (); 284 285 if (!root.isVisible () 286 || !root.isDisplayable ()) 287 return null; 288 289 if (accept (root)) 290 return root; 291 292 Component[] componentArray = root.getComponents (); 293 294 for (int i = 0; i < componentArray.length; i++) 295 { 296 Component component = componentArray [i]; 297 298 if (accept (component)) 299 return component; 300 301 if (component instanceof Container) 302 { 303 Component result = getFirstComponent ((Container) component); 304 305 if (result != null) 306 return result; 307 } 308 } 309 310 return null; 311 } 312 313 /** 314 * Returns the last Component of root that should receive the focus. 315 * 316 * @param root a focus cycle root 317 * 318 * @return the last Component in the focus traversal order for 319 * root, or null if no acceptable Component exists. 320 * 321 * @exception IllegalArgumentException If root is null. 322 */ getLastComponent(Container root)323 public Component getLastComponent (Container root) 324 { 325 if (root == null) 326 throw new IllegalArgumentException (); 327 328 if (!root.isVisible () 329 || !root.isDisplayable ()) 330 return null; 331 332 if (accept (root)) 333 return root; 334 335 Component[] componentArray = root.getComponents (); 336 337 for (int i = componentArray.length - 1; i >= 0; i--) 338 { 339 Component component = componentArray [i]; 340 341 if (accept (component)) 342 return component; 343 344 if (component instanceof Container) 345 { 346 Component result = getLastComponent ((Container) component); 347 348 if (result != null) 349 return result; 350 } 351 } 352 353 return null; 354 } 355 356 /** 357 * Returns the default Component of root that should receive the focus. 358 * 359 * @param root a focus cycle root 360 * 361 * @return the default Component in the focus traversal order for 362 * root, or null if no acceptable Component exists. 363 * 364 * @exception IllegalArgumentException If root is null. 365 */ getDefaultComponent(Container root)366 public Component getDefaultComponent (Container root) 367 { 368 return getFirstComponent (root); 369 } 370 371 /** 372 * Set whether or not implicit down cycling is enabled. If it is, 373 * then initiating a forward focus traversal operation onto a focus 374 * cycle root, the focus will be implicitly transferred into the 375 * root container's focus cycle. 376 * 377 * @param value the setting for implicit down cycling 378 */ setImplicitDownCycleTraversal(boolean value)379 public void setImplicitDownCycleTraversal (boolean value) 380 { 381 implicitDownCycleTraversal = value; 382 } 383 384 /** 385 * Check whether or not implicit down cycling is enabled. If it is, 386 * then initiating a forward focus traversal operation onto a focus 387 * cycle root, the focus will be implicitly transferred into the 388 * root container's focus cycle. 389 * 390 * @return true if the focus will be transferred down-cycle 391 * implicitly 392 */ getImplicitDownCycleTraversal()393 public boolean getImplicitDownCycleTraversal () 394 { 395 return implicitDownCycleTraversal; 396 } 397 398 /** 399 * Check whether the given Component is an acceptable target for the 400 * keyboard input focus. 401 * 402 * @param current the Component to check 403 * 404 * @return true if current is acceptable, false otherwise 405 */ accept(Component current)406 protected boolean accept (Component current) 407 { 408 return (current.visible 409 && current.isDisplayable () 410 && current.enabled 411 && current.focusable); 412 } 413 } 414