Frames | No Frames |
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: */ 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: */ 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 == null) 117: ancestor = root; 118: else if (ancestor != root) 119: throw new IllegalArgumentException ("the given container is not" 120: + " a focus cycle root of the" 121: + " current component"); 122: else 123: break; 124: } 125: prevAncestor = ancestor; 126: } 127: 128: // FIXME: is this the right thing to do here? It moves the context 129: // for traversal up one focus traversal cycle. We'll need a test 130: // for this. 131: if ((Component) root == current) 132: root = current.getFocusCycleRootAncestor (); 133: 134: // Check if we've reached the top of the component hierarchy. If 135: // so then we want to loop around to the first component in the 136: // focus traversal cycle. 137: if (current instanceof Window) 138: return getFirstComponent ((Container) current); 139: 140: Container parent = current.getParent (); 141: synchronized (parent.getTreeLock ()) 142: { 143: Component[] components = parent.getComponents (); 144: int componentIndex = 0; 145: int numComponents = parent.getComponentCount (); 146: 147: // Find component's index. 148: for (int i = 0; i < numComponents; i++) 149: { 150: if (components[i].equals(current)) 151: componentIndex = i; 152: } 153: 154: // Search forward for the next acceptable component. 155: // Search through all components at least one time 156: // i.e. start at componentIndex + 1 --> nComponents -1 --> 0 ---> componentIndex 157: int i = componentIndex + 1; 158: int end = numComponents - 1; 159: Component next = getNextAvailableComponent(components, i, end); 160: if (next != null) 161: return next; 162: 163: // Now check remainder of components from 0 to componentIndex 164: i = 0; 165: end = componentIndex; 166: next = getNextAvailableComponent(components, i, end); 167: if (next != null) 168: return next; 169: 170: // No focusable components after current in its Container. So go 171: // to the next Component after current's Container (parent). 172: Component result = getComponentAfter (root, parent); 173: return result; 174: } 175: } 176: 177: /** 178: * Gets the next available component in the array between the given range. 179: * 180: * @param components - the array of components. 181: * @param start - where to start 182: * @param end - where to end 183: * @return next component if found 184: */ 185: private Component getNextAvailableComponent(Component[] components, int start, int end) 186: { 187: while (start <= end) 188: { 189: Component c = components[start]; 190: 191: if (c.visible && c.isDisplayable() && c.enabled && c.focusable) 192: return c; 193: 194: if (c instanceof Container) 195: { 196: Component result = getFirstComponent((Container) c); 197: 198: if (result != null && implicitDownCycleTraversal && result.visible 199: && result.isDisplayable() && result.enabled && result.focusable) 200: return result; 201: } 202: start++; 203: } 204: 205: return null; 206: } 207: 208: /** 209: * Gets the previous available component in the array between the given range. 210: * 211: * @param components - the array of components. 212: * @param start - where to start 213: * @param end - where to end 214: * @return previous component if found 215: */ 216: Component getPrevAvailableComponent(Component[] components, int start, int end) 217: { 218: while (start >= end) 219: { 220: Component c = components[start]; 221: if (c.visible && c.isDisplayable() && c.enabled && c.focusable) 222: return c; 223: 224: if (c instanceof Container) 225: { 226: Component result = getLastComponent((Container) c); 227: 228: if (result != null 229: && (result.visible && result.isDisplayable() && result.enabled && result.focusable)) 230: return result; 231: } 232: start--; 233: } 234: return null; 235: } 236: 237: /** 238: * Returns the Component that should receive the focus before 239: * <code>current</code>. <code>root</code> must be a focus cycle root of 240: * current. 241: * 242: * @param root a focus cycle root of current 243: * @param current a (possibly indirect) child of root, or root itself 244: * @return the previous Component in the focus traversal order for root, or 245: * null if no acceptable Component exists. 246: * @exception IllegalArgumentException If root is not a focus cycle root of 247: * current, or if either root or current is null. 248: */ 249: public Component getComponentBefore (Container root, Component current) 250: { 251: if (root == null) 252: throw new IllegalArgumentException ("focus cycle root is null"); 253: if (current == null) 254: throw new IllegalArgumentException ("current component is null"); 255: 256: if (!root.isFocusCycleRoot ()) 257: throw new IllegalArgumentException ("root is not a focus cycle root"); 258: 259: Container ancestor = current.getFocusCycleRootAncestor (); 260: Container prevAncestor = ancestor; 261: while (ancestor != root) 262: { 263: ancestor = current.getFocusCycleRootAncestor (); 264: if (ancestor == prevAncestor) 265: { 266: // We've reached the top focus cycle root ancestor. Check 267: // if it is root. 268: if (ancestor == null) 269: ancestor = root; 270: else if (ancestor != root) 271: throw new IllegalArgumentException ("the given container is not" 272: + " a focus cycle root of the" 273: + " current component"); 274: else 275: break; 276: } 277: prevAncestor = ancestor; 278: } 279: 280: // FIXME: is this the right thing to do here? It moves the context 281: // for traversal up one focus traversal cycle. We'll need a test 282: // for this. 283: if ((Component) root == current) 284: root = current.getFocusCycleRootAncestor (); 285: 286: // Check if we've reached the top of the component hierarchy. If 287: // so then we want to loop around to the last component in the 288: // focus traversal cycle. 289: if (current instanceof Window) 290: return getLastComponent ((Container) current); 291: 292: Container parent = current.getParent (); 293: 294: synchronized (parent.getTreeLock ()) 295: { 296: Component[] components = parent.getComponents (); 297: int componentIndex = 0; 298: int numComponents = parent.getComponentCount (); 299: 300: // Find component's index. 301: for (int i = 0; i < numComponents; i++) 302: { 303: if (components[i] == current) 304: componentIndex = i; 305: } 306: 307: // Search through all components at least one time 308: // i.e. start at componentIndex - 1 --> 0 --> numComponents -1 ---> componentIndex 309: int i = componentIndex - 1; 310: int end = 0; 311: Component prev = getPrevAvailableComponent(components, i, end); 312: if (prev != null) 313: return prev; 314: 315: // Now check remainder of components 316: i = numComponents -1; 317: end = componentIndex; 318: prev = getPrevAvailableComponent(components, i, end); 319: if (prev != null) 320: return prev; 321: 322: // No focusable components before current in its Container. So go 323: // to the previous Component before current's Container (parent). 324: Component result = getComponentBefore (root, parent); 325: 326: return result; 327: } 328: } 329: 330: /** 331: * Returns the first Component of root that should receive the focus. 332: * 333: * @param root a focus cycle root 334: * 335: * @return the first Component in the focus traversal order for 336: * root, or null if no acceptable Component exists. 337: * 338: * @exception IllegalArgumentException If root is null. 339: */ 340: public Component getFirstComponent(Container root) 341: { 342: if (root == null) 343: throw new IllegalArgumentException (); 344: 345: if (!root.isVisible () 346: || !root.isDisplayable ()) 347: return null; 348: 349: if (accept(root)) 350: return root; 351: 352: int ncomponents = root.getComponentCount(); 353: for (int i = 0; i < ncomponents; i++) 354: { 355: Component component = root.getComponent(i); 356: if (component instanceof Container 357: && !((Container) component).isFocusCycleRoot()) 358: { 359: Component first = null; 360: Container cont = (Container) component; 361: if (cont.isFocusTraversalPolicyProvider()) 362: { 363: FocusTraversalPolicy childPol = cont.getFocusTraversalPolicy(); 364: first = childPol.getFirstComponent(cont); 365: } 366: else 367: first = getFirstComponent(cont); 368: if (first != null) 369: return first; 370: } 371: else if (accept(component)) 372: return component; 373: } 374: 375: return null; 376: } 377: 378: /** 379: * Returns the last Component of root that should receive the focus. 380: * 381: * @param root a focus cycle root 382: * 383: * @return the last Component in the focus traversal order for 384: * root, or null if no acceptable Component exists. 385: * 386: * @exception IllegalArgumentException If root is null. 387: */ 388: public Component getLastComponent (Container root) 389: { 390: if (root == null) 391: throw new IllegalArgumentException (); 392: 393: if (!root.isVisible () 394: || !root.isDisplayable ()) 395: return null; 396: 397: if (root.visible && root.isDisplayable() && root.enabled 398: && root.focusable) 399: return root; 400: 401: Component[] componentArray = root.getComponents (); 402: 403: for (int i = componentArray.length - 1; i >= 0; i--) 404: { 405: Component component = componentArray [i]; 406: 407: if (component.visible && component.isDisplayable() && component.enabled 408: && component.focusable) 409: return component; 410: 411: if (component instanceof Container) 412: { 413: Component result = getLastComponent ((Container) component); 414: 415: if (result != null && 416: result.visible && result.isDisplayable() && result.enabled 417: && result.focusable) 418: return result; 419: } 420: } 421: 422: return null; 423: } 424: 425: /** 426: * Returns the default Component of root that should receive the focus. 427: * 428: * @param root a focus cycle root 429: * 430: * @return the default Component in the focus traversal order for 431: * root, or null if no acceptable Component exists. 432: * 433: * @exception IllegalArgumentException If root is null. 434: */ 435: public Component getDefaultComponent (Container root) 436: { 437: return getFirstComponent (root); 438: } 439: 440: /** 441: * Set whether or not implicit down cycling is enabled. If it is, 442: * then initiating a forward focus traversal operation onto a focus 443: * cycle root, the focus will be implicitly transferred into the 444: * root container's focus cycle. 445: * 446: * @param value the setting for implicit down cycling 447: */ 448: public void setImplicitDownCycleTraversal (boolean value) 449: { 450: implicitDownCycleTraversal = value; 451: } 452: 453: /** 454: * Check whether or not implicit down cycling is enabled. If it is, 455: * then initiating a forward focus traversal operation onto a focus 456: * cycle root, the focus will be implicitly transferred into the 457: * root container's focus cycle. 458: * 459: * @return true if the focus will be transferred down-cycle 460: * implicitly 461: */ 462: public boolean getImplicitDownCycleTraversal () 463: { 464: return implicitDownCycleTraversal; 465: } 466: 467: /** 468: * Check whether the given Component is an acceptable target for the 469: * keyboard input focus. 470: * 471: * @param current the Component to check 472: * 473: * @return true if current is acceptable, false otherwise 474: */ 475: protected boolean accept (Component current) 476: { 477: return (current.visible 478: && current.isDisplayable () 479: && current.enabled 480: && current.focusable); 481: } 482: }