| /* ContainerOrderFocusTraversalPolicy.java -- | |
| Copyright (C) 2002, 2005 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.awt; | |
| import java.io.Serializable; | |
| /** | |
| * ContainerOrderFocusTraversalPolicy defines a focus traversal order | |
| * based on the order in which Components were packed in a Container. | |
| * This policy performs a pre-order traversal of the Component | |
| * hierarchy starting from a given focus cycle root. Portions of the | |
| * hierarchy that are not visible and displayable are skipped. | |
| * | |
| * By default, this policy transfers focus down-cycle implicitly. | |
| * That is, if a forward traversal is requested on a focus cycle root | |
| * and the focus cycle root has focusable children, the focus will | |
| * automatically be transfered down to the lower focus cycle. | |
| * | |
| * The default implementation of accept accepts only Components that | |
| * are visible, displayable, enabled and focusable. Derived classes | |
| * can override these acceptance criteria by overriding accept. | |
| * | |
| * @author Michael Koch | |
| * @author Thomas Fitzsimmons (fitzsim@redhat.com) | |
| * @since 1.4 | |
| */ | |
| public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy | |
| implements Serializable | |
| { | |
| /** | |
| * Compatible to JDK 1.4+ | |
| */ | |
| static final long serialVersionUID = 486933713763926351L; | |
| /** | |
| * True if implicit down cycling is enabled. | |
| */ | |
| private boolean implicitDownCycleTraversal = true; | |
| /** | |
| * Creates the <code>ContainerOrderFocusTraversalPolicy</code> object. | |
| */ | |
| public ContainerOrderFocusTraversalPolicy () | |
| { | |
| // Nothing to do here | |
| } | |
| /** | |
| * Returns the Component that should receive the focus after current. | |
| * root must be a focus cycle root of current. | |
| * | |
| * @param root a focus cycle root of current | |
| * @param current a (possibly indirect) child of root, or root itself | |
| * | |
| * @return the next Component in the focus traversal order for root, | |
| * or null if no acceptable Component exists. | |
| * | |
| * @exception IllegalArgumentException If root is not a focus cycle | |
| * root of current, or if either root or current is null. | |
| */ | |
| public Component getComponentAfter (Container root, Component current) | |
| { | |
| if (root == null) | |
| throw new IllegalArgumentException ("focus cycle root is null"); | |
| if (current == null) | |
| throw new IllegalArgumentException ("current component is null"); | |
| if (!root.isFocusCycleRoot ()) | |
| throw new IllegalArgumentException ("root is not a focus cycle root"); | |
| Container ancestor = current.getFocusCycleRootAncestor (); | |
| Container prevAncestor = ancestor; | |
| while (ancestor != root) | |
| { | |
| ancestor = current.getFocusCycleRootAncestor (); | |
| if (ancestor == prevAncestor) | |
| { | |
| // We've reached the top focus cycle root ancestor. Check | |
| // if it is root. | |
| if (ancestor == null) | |
| ancestor = root; | |
| else if (ancestor != root) | |
| throw new IllegalArgumentException ("the given container is not" | |
| + " a focus cycle root of the" | |
| + " current component"); | |
| else | |
| break; | |
| } | |
| prevAncestor = ancestor; | |
| } | |
| // FIXME: is this the right thing to do here? It moves the context | |
| // for traversal up one focus traversal cycle. We'll need a test | |
| // for this. | |
| if ((Component) root == current) | |
| root = current.getFocusCycleRootAncestor (); | |
| // Check if we've reached the top of the component hierarchy. If | |
| // so then we want to loop around to the first component in the | |
| // focus traversal cycle. | |
| if (current instanceof Window) | |
| return getFirstComponent ((Container) current); | |
| Container parent = current.getParent (); | |
| synchronized (parent.getTreeLock ()) | |
| { | |
| Component[] components = parent.getComponents (); | |
| int componentIndex = 0; | |
| int numComponents = parent.getComponentCount (); | |
| // Find component's index. | |
| for (int i = 0; i < numComponents; i++) | |
| { | |
| if (components[i].equals(current)) | |
| componentIndex = i; | |
| } | |
| // Search forward for the next acceptable component. | |
| // Search through all components at least one time | |
| // i.e. start at componentIndex + 1 --> nComponents -1 --> 0 ---> componentIndex | |
| int i = componentIndex + 1; | |
| int end = numComponents - 1; | |
| Component next = getNextAvailableComponent(components, i, end); | |
| if (next != null) | |
| return next; | |
| // Now check remainder of components from 0 to componentIndex | |
| i = 0; | |
| end = componentIndex; | |
| next = getNextAvailableComponent(components, i, end); | |
| if (next != null) | |
| return next; | |
| // No focusable components after current in its Container. So go | |
| // to the next Component after current's Container (parent). | |
| Component result = getComponentAfter (root, parent); | |
| return result; | |
| } | |
| } | |
| /** | |
| * Gets the next available component in the array between the given range. | |
| * | |
| * @param components - the array of components. | |
| * @param start - where to start | |
| * @param end - where to end | |
| * @return next component if found | |
| */ | |
| private Component getNextAvailableComponent(Component[] components, int start, int end) | |
| { | |
| while (start <= end) | |
| { | |
| Component c = components[start]; | |
| if (c.visible && c.isDisplayable() && c.enabled && c.focusable) | |
| return c; | |
| if (c instanceof Container) | |
| { | |
| Component result = getFirstComponent((Container) c); | |
| if (result != null && implicitDownCycleTraversal && result.visible | |
| && result.isDisplayable() && result.enabled && result.focusable) | |
| return result; | |
| } | |
| start++; | |
| } | |
| return null; | |
| } | |
| /** | |
| * Gets the previous available component in the array between the given range. | |
| * | |
| * @param components - the array of components. | |
| * @param start - where to start | |
| * @param end - where to end | |
| * @return previous component if found | |
| */ | |
| Component getPrevAvailableComponent(Component[] components, int start, int end) | |
| { | |
| while (start >= end) | |
| { | |
| Component c = components[start]; | |
| if (c.visible && c.isDisplayable() && c.enabled && c.focusable) | |
| return c; | |
| if (c instanceof Container) | |
| { | |
| Component result = getLastComponent((Container) c); | |
| if (result != null | |
| && (result.visible && result.isDisplayable() && result.enabled && result.focusable)) | |
| return result; | |
| } | |
| start--; | |
| } | |
| return null; | |
| } | |
| /** | |
| * Returns the Component that should receive the focus before | |
| * <code>current</code>. <code>root</code> must be a focus cycle root of | |
| * current. | |
| * | |
| * @param root a focus cycle root of current | |
| * @param current a (possibly indirect) child of root, or root itself | |
| * @return the previous Component in the focus traversal order for root, or | |
| * null if no acceptable Component exists. | |
| * @exception IllegalArgumentException If root is not a focus cycle root of | |
| * current, or if either root or current is null. | |
| */ | |
| public Component getComponentBefore (Container root, Component current) | |
| { | |
| if (root == null) | |
| throw new IllegalArgumentException ("focus cycle root is null"); | |
| if (current == null) | |
| throw new IllegalArgumentException ("current component is null"); | |
| if (!root.isFocusCycleRoot ()) | |
| throw new IllegalArgumentException ("root is not a focus cycle root"); | |
| Container ancestor = current.getFocusCycleRootAncestor (); | |
| Container prevAncestor = ancestor; | |
| while (ancestor != root) | |
| { | |
| ancestor = current.getFocusCycleRootAncestor (); | |
| if (ancestor == prevAncestor) | |
| { | |
| // We've reached the top focus cycle root ancestor. Check | |
| // if it is root. | |
| if (ancestor == null) | |
| ancestor = root; | |
| else if (ancestor != root) | |
| throw new IllegalArgumentException ("the given container is not" | |
| + " a focus cycle root of the" | |
| + " current component"); | |
| else | |
| break; | |
| } | |
| prevAncestor = ancestor; | |
| } | |
| // FIXME: is this the right thing to do here? It moves the context | |
| // for traversal up one focus traversal cycle. We'll need a test | |
| // for this. | |
| if ((Component) root == current) | |
| root = current.getFocusCycleRootAncestor (); | |
| // Check if we've reached the top of the component hierarchy. If | |
| // so then we want to loop around to the last component in the | |
| // focus traversal cycle. | |
| if (current instanceof Window) | |
| return getLastComponent ((Container) current); | |
| Container parent = current.getParent (); | |
| synchronized (parent.getTreeLock ()) | |
| { | |
| Component[] components = parent.getComponents (); | |
| int componentIndex = 0; | |
| int numComponents = parent.getComponentCount (); | |
| // Find component's index. | |
| for (int i = 0; i < numComponents; i++) | |
| { | |
| if (components[i] == current) | |
| componentIndex = i; | |
| } | |
| // Search through all components at least one time | |
| // i.e. start at componentIndex - 1 --> 0 --> numComponents -1 ---> componentIndex | |
| int i = componentIndex - 1; | |
| int end = 0; | |
| Component prev = getPrevAvailableComponent(components, i, end); | |
| if (prev != null) | |
| return prev; | |
| // Now check remainder of components | |
| i = numComponents -1; | |
| end = componentIndex; | |
| prev = getPrevAvailableComponent(components, i, end); | |
| if (prev != null) | |
| return prev; | |
| // No focusable components before current in its Container. So go | |
| // to the previous Component before current's Container (parent). | |
| Component result = getComponentBefore (root, parent); | |
| return result; | |
| } | |
| } | |
| /** | |
| * Returns the first Component of root that should receive the focus. | |
| * | |
| * @param root a focus cycle root | |
| * | |
| * @return the first Component in the focus traversal order for | |
| * root, or null if no acceptable Component exists. | |
| * | |
| * @exception IllegalArgumentException If root is null. | |
| */ | |
| public Component getFirstComponent(Container root) | |
| { | |
| if (root == null) | |
| throw new IllegalArgumentException (); | |
| if (!root.isVisible () | |
| || !root.isDisplayable ()) | |
| return null; | |
| if (accept(root)) | |
| return root; | |
| int ncomponents = root.getComponentCount(); | |
| for (int i = 0; i < ncomponents; i++) | |
| { | |
| Component component = root.getComponent(i); | |
| if (component instanceof Container | |
| && !((Container) component).isFocusCycleRoot()) | |
| { | |
| Component first = null; | |
| Container cont = (Container) component; | |
| if (cont.isFocusTraversalPolicyProvider()) | |
| { | |
| FocusTraversalPolicy childPol = cont.getFocusTraversalPolicy(); | |
| first = childPol.getFirstComponent(cont); | |
| } | |
| else | |
| first = getFirstComponent(cont); | |
| if (first != null) | |
| return first; | |
| } | |
| else if (accept(component)) | |
| return component; | |
| } | |
| return null; | |
| } | |
| /** | |
| * Returns the last Component of root that should receive the focus. | |
| * | |
| * @param root a focus cycle root | |
| * | |
| * @return the last Component in the focus traversal order for | |
| * root, or null if no acceptable Component exists. | |
| * | |
| * @exception IllegalArgumentException If root is null. | |
| */ | |
| public Component getLastComponent (Container root) | |
| { | |
| if (root == null) | |
| throw new IllegalArgumentException (); | |
| if (!root.isVisible () | |
| || !root.isDisplayable ()) | |
| return null; | |
| if (root.visible && root.isDisplayable() && root.enabled | |
| && root.focusable) | |
| return root; | |
| Component[] componentArray = root.getComponents (); | |
| for (int i = componentArray.length - 1; i >= 0; i--) | |
| { | |
| Component component = componentArray [i]; | |
| if (component.visible && component.isDisplayable() && component.enabled | |
| && component.focusable) | |
| return component; | |
| if (component instanceof Container) | |
| { | |
| Component result = getLastComponent ((Container) component); | |
| if (result != null && | |
| result.visible && result.isDisplayable() && result.enabled | |
| && result.focusable) | |
| return result; | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Returns the default Component of root that should receive the focus. | |
| * | |
| * @param root a focus cycle root | |
| * | |
| * @return the default Component in the focus traversal order for | |
| * root, or null if no acceptable Component exists. | |
| * | |
| * @exception IllegalArgumentException If root is null. | |
| */ | |
| public Component getDefaultComponent (Container root) | |
| { | |
| return getFirstComponent (root); | |
| } | |
| /** | |
| * Set whether or not implicit down cycling is enabled. If it is, | |
| * then initiating a forward focus traversal operation onto a focus | |
| * cycle root, the focus will be implicitly transferred into the | |
| * root container's focus cycle. | |
| * | |
| * @param value the setting for implicit down cycling | |
| */ | |
| public void setImplicitDownCycleTraversal (boolean value) | |
| { | |
| implicitDownCycleTraversal = value; | |
| } | |
| /** | |
| * Check whether or not implicit down cycling is enabled. If it is, | |
| * then initiating a forward focus traversal operation onto a focus | |
| * cycle root, the focus will be implicitly transferred into the | |
| * root container's focus cycle. | |
| * | |
| * @return true if the focus will be transferred down-cycle | |
| * implicitly | |
| */ | |
| public boolean getImplicitDownCycleTraversal () | |
| { | |
| return implicitDownCycleTraversal; | |
| } | |
| /** | |
| * Check whether the given Component is an acceptable target for the | |
| * keyboard input focus. | |
| * | |
| * @param current the Component to check | |
| * | |
| * @return true if current is acceptable, false otherwise | |
| */ | |
| protected boolean accept (Component current) | |
| { | |
| return (current.visible | |
| && current.isDisplayable () | |
| && current.enabled | |
| && current.focusable); | |
| } | |
| } |