1 /* ContainerOrderFocusTraversalPolicy.java --
2 Copyright (C) 2002, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
41 import java.io.Serializable;
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.
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.
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.
59 * @author Michael Koch
60 * @author Thomas Fitzsimmons (fitzsim@redhat.com)
63 public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy
64 implements Serializable
67 * Compatible to JDK 1.4+
69 static final long serialVersionUID = 486933713763926351L;
72 * True if implicit down cycling is enabled.
74 private boolean implicitDownCycleTraversal = true;
77 * Creates the <code>ContainerOrderFocusTraversalPolicy</code> object.
79 public ContainerOrderFocusTraversalPolicy ()
85 * Returns the Component that should receive the focus after current.
86 * root must be a focus cycle root of current.
88 * @param root a focus cycle root of current
89 * @param current a (possibly indirect) child of root, or root itself
91 * @return the next Component in the focus traversal order for root,
92 * or null if no acceptable Component exists.
94 * @exception IllegalArgumentException If root is not a focus cycle
95 * root of current, or if either root or current is null.
97 public Component getComponentAfter (Container root, Component current)
100 throw new IllegalArgumentException ("focus cycle root is null");
102 throw new IllegalArgumentException ("current component is null");
104 if (!root.isFocusCycleRoot ())
105 throw new IllegalArgumentException ("root is not a focus cycle root");
107 Container ancestor = current.getFocusCycleRootAncestor ();
108 Container prevAncestor = ancestor;
109 while (ancestor != root)
111 ancestor = current.getFocusCycleRootAncestor ();
112 if (ancestor == prevAncestor)
114 // We've reached the top focus cycle root ancestor. Check
116 if (ancestor == null)
118 else if (ancestor != root)
119 throw new IllegalArgumentException ("the given container is not"
120 + " a focus cycle root of the"
121 + " current component");
125 prevAncestor = ancestor;
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
131 if ((Component) root == current)
132 root = current.getFocusCycleRootAncestor ();
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);
140 Container parent = current.getParent ();
141 synchronized (parent.getTreeLock ())
143 Component[] components = parent.getComponents ();
144 int componentIndex = 0;
145 int numComponents = parent.getComponentCount ();
147 // Find component's index.
148 for (int i = 0; i < numComponents; i++)
150 if (components[i].equals(current))
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);
163 // Now check remainder of components from 0 to componentIndex
165 end = componentIndex;
166 next = getNextAvailableComponent(components, i, end);
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);
178 * Gets the next available component in the array between the given range.
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
185 private Component getNextAvailableComponent(Component[] components, int start, int end)
189 Component c = components[start];
191 if (c.visible && c.isDisplayable() && c.enabled && c.focusable)
194 if (c instanceof Container)
196 Component result = getFirstComponent((Container) c);
198 if (result != null && implicitDownCycleTraversal && result.visible
199 && result.isDisplayable() && result.enabled && result.focusable)
209 * Gets the previous available component in the array between the given range.
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
216 Component getPrevAvailableComponent(Component[] components, int start, int end)
220 Component c = components[start];
221 if (c.visible && c.isDisplayable() && c.enabled && c.focusable)
224 if (c instanceof Container)
226 Component result = getLastComponent((Container) c);
229 && (result.visible && result.isDisplayable() && result.enabled && result.focusable))
238 * Returns the Component that should receive the focus before
239 * <code>current</code>. <code>root</code> must be a focus cycle root of
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.
249 public Component getComponentBefore (Container root, Component current)
252 throw new IllegalArgumentException ("focus cycle root is null");
254 throw new IllegalArgumentException ("current component is null");
256 if (!root.isFocusCycleRoot ())
257 throw new IllegalArgumentException ("root is not a focus cycle root");
259 Container ancestor = current.getFocusCycleRootAncestor ();
260 Container prevAncestor = ancestor;
261 while (ancestor != root)
263 ancestor = current.getFocusCycleRootAncestor ();
264 if (ancestor == prevAncestor)
266 // We've reached the top focus cycle root ancestor. Check
268 if (ancestor == null)
270 else if (ancestor != root)
271 throw new IllegalArgumentException ("the given container is not"
272 + " a focus cycle root of the"
273 + " current component");
277 prevAncestor = ancestor;
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
283 if ((Component) root == current)
284 root = current.getFocusCycleRootAncestor ();
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);
292 Container parent = current.getParent ();
294 synchronized (parent.getTreeLock ())
296 Component[] components = parent.getComponents ();
297 int componentIndex = 0;
298 int numComponents = parent.getComponentCount ();
300 // Find component's index.
301 for (int i = 0; i < numComponents; i++)
303 if (components[i] == current)
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;
311 Component prev = getPrevAvailableComponent(components, i, end);
315 // Now check remainder of components
316 i = numComponents -1;
317 end = componentIndex;
318 prev = getPrevAvailableComponent(components, i, end);
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);
331 * Returns the first Component of root that should receive the focus.
333 * @param root a focus cycle root
335 * @return the first Component in the focus traversal order for
336 * root, or null if no acceptable Component exists.
338 * @exception IllegalArgumentException If root is null.
340 public Component getFirstComponent(Container root)
343 throw new IllegalArgumentException ();
345 if (!root.isVisible ()
346 || !root.isDisplayable ())
352 int ncomponents = root.getComponentCount();
353 for (int i = 0; i < ncomponents; i++)
355 Component component = root.getComponent(i);
356 if (component instanceof Container
357 && !((Container) component).isFocusCycleRoot())
359 Component first = null;
360 Container cont = (Container) component;
361 if (cont.isFocusTraversalPolicyProvider())
363 FocusTraversalPolicy childPol = cont.getFocusTraversalPolicy();
364 first = childPol.getFirstComponent(cont);
367 first = getFirstComponent(cont);
371 else if (accept(component))
379 * Returns the last Component of root that should receive the focus.
381 * @param root a focus cycle root
383 * @return the last Component in the focus traversal order for
384 * root, or null if no acceptable Component exists.
386 * @exception IllegalArgumentException If root is null.
388 public Component getLastComponent (Container root)
391 throw new IllegalArgumentException ();
393 if (!root.isVisible ()
394 || !root.isDisplayable ())
397 if (root.visible && root.isDisplayable() && root.enabled
401 Component[] componentArray = root.getComponents ();
403 for (int i = componentArray.length - 1; i >= 0; i--)
405 Component component = componentArray [i];
407 if (component.visible && component.isDisplayable() && component.enabled
408 && component.focusable)
411 if (component instanceof Container)
413 Component result = getLastComponent ((Container) component);
415 if (result != null &&
416 result.visible && result.isDisplayable() && result.enabled
426 * Returns the default Component of root that should receive the focus.
428 * @param root a focus cycle root
430 * @return the default Component in the focus traversal order for
431 * root, or null if no acceptable Component exists.
433 * @exception IllegalArgumentException If root is null.
435 public Component getDefaultComponent (Container root)
437 return getFirstComponent (root);
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.
446 * @param value the setting for implicit down cycling
448 public void setImplicitDownCycleTraversal (boolean value)
450 implicitDownCycleTraversal = value;
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.
459 * @return true if the focus will be transferred down-cycle
462 public boolean getImplicitDownCycleTraversal ()
464 return implicitDownCycleTraversal;
468 * Check whether the given Component is an acceptable target for the
469 * keyboard input focus.
471 * @param current the Component to check
473 * @return true if current is acceptable, false otherwise
475 protected boolean accept (Component current)
477 return (current.visible
478 && current.isDisplayable ()
480 && current.focusable);