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 (root.visible && root.isDisplayable() && root.enabled 350: && root.focusable) 351: return root; 352: 353: Component[] componentArray = root.getComponents (); 354: 355: for (int i = 0; i < componentArray.length; i++) 356: { 357: Component component = componentArray [i]; 358: 359: if (component.visible && component.isDisplayable() && component.enabled 360: && component.focusable) 361: return component; 362: 363: if (component instanceof Container) 364: { 365: Component result = getFirstComponent ((Container) component); 366: 367: if (result != null 368: && (result.visible && result.isDisplayable() && result.enabled && result.focusable)) 369: return result; 370: } 371: } 372: 373: return null; 374: } 375: 376: /** 377: * Returns the last Component of root that should receive the focus. 378: * 379: * @param root a focus cycle root 380: * 381: * @return the last Component in the focus traversal order for 382: * root, or null if no acceptable Component exists. 383: * 384: * @exception IllegalArgumentException If root is null. 385: */ 386: public Component getLastComponent (Container root) 387: { 388: if (root == null) 389: throw new IllegalArgumentException (); 390: 391: if (!root.isVisible () 392: || !root.isDisplayable ()) 393: return null; 394: 395: if (root.visible && root.isDisplayable() && root.enabled 396: && root.focusable) 397: return root; 398: 399: Component[] componentArray = root.getComponents (); 400: 401: for (int i = componentArray.length - 1; i >= 0; i--) 402: { 403: Component component = componentArray [i]; 404: 405: if (component.visible && component.isDisplayable() && component.enabled 406: && component.focusable) 407: return component; 408: 409: if (component instanceof Container) 410: { 411: Component result = getLastComponent ((Container) component); 412: 413: if (result != null && 414: result.visible && result.isDisplayable() && result.enabled 415: && result.focusable) 416: return result; 417: } 418: } 419: 420: return null; 421: } 422: 423: /** 424: * Returns the default Component of root that should receive the focus. 425: * 426: * @param root a focus cycle root 427: * 428: * @return the default Component in the focus traversal order for 429: * root, or null if no acceptable Component exists. 430: * 431: * @exception IllegalArgumentException If root is null. 432: */ 433: public Component getDefaultComponent (Container root) 434: { 435: return getFirstComponent (root); 436: } 437: 438: /** 439: * Set whether or not implicit down cycling is enabled. If it is, 440: * then initiating a forward focus traversal operation onto a focus 441: * cycle root, the focus will be implicitly transferred into the 442: * root container's focus cycle. 443: * 444: * @param value the setting for implicit down cycling 445: */ 446: public void setImplicitDownCycleTraversal (boolean value) 447: { 448: implicitDownCycleTraversal = value; 449: } 450: 451: /** 452: * Check whether or not implicit down cycling is enabled. If it is, 453: * then initiating a forward focus traversal operation onto a focus 454: * cycle root, the focus will be implicitly transferred into the 455: * root container's focus cycle. 456: * 457: * @return true if the focus will be transferred down-cycle 458: * implicitly 459: */ 460: public boolean getImplicitDownCycleTraversal () 461: { 462: return implicitDownCycleTraversal; 463: } 464: 465: /** 466: * Check whether the given Component is an acceptable target for the 467: * keyboard input focus. 468: * 469: * @param current the Component to check 470: * 471: * @return true if current is acceptable, false otherwise 472: */ 473: protected boolean accept (Component current) 474: { 475: return (current.visible 476: && current.isDisplayable () 477: && current.enabled 478: && current.focusable); 479: } 480: }