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 != 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 406:   protected boolean accept (Component current)
 407:   {
 408:     return (current.visible
 409:             && current.isDisplayable ()
 410:             && current.enabled
 411:             && current.focusable);
 412:   }
 413: }