Source for java.awt.ContainerOrderFocusTraversalPolicy

   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: }