001 /* ContainerOrderFocusTraversalPolicy.java --
002 Copyright (C) 2002, 2005 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.awt;
040
041 import java.io.Serializable;
042
043 /**
044 * ContainerOrderFocusTraversalPolicy defines a focus traversal order
045 * based on the order in which Components were packed in a Container.
046 * This policy performs a pre-order traversal of the Component
047 * hierarchy starting from a given focus cycle root. Portions of the
048 * hierarchy that are not visible and displayable are skipped.
049 *
050 * By default, this policy transfers focus down-cycle implicitly.
051 * That is, if a forward traversal is requested on a focus cycle root
052 * and the focus cycle root has focusable children, the focus will
053 * automatically be transfered down to the lower focus cycle.
054 *
055 * The default implementation of accept accepts only Components that
056 * are visible, displayable, enabled and focusable. Derived classes
057 * can override these acceptance criteria by overriding accept.
058 *
059 * @author Michael Koch
060 * @author Thomas Fitzsimmons (fitzsim@redhat.com)
061 * @since 1.4
062 */
063 public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy
064 implements Serializable
065 {
066 /**
067 * Compatible to JDK 1.4+
068 */
069 static final long serialVersionUID = 486933713763926351L;
070
071 /**
072 * True if implicit down cycling is enabled.
073 */
074 private boolean implicitDownCycleTraversal = true;
075
076 /**
077 * Creates the <code>ContainerOrderFocusTraversalPolicy</code> object.
078 */
079 public ContainerOrderFocusTraversalPolicy ()
080 {
081 // Nothing to do here
082 }
083
084 /**
085 * Returns the Component that should receive the focus after current.
086 * root must be a focus cycle root of current.
087 *
088 * @param root a focus cycle root of current
089 * @param current a (possibly indirect) child of root, or root itself
090 *
091 * @return the next Component in the focus traversal order for root,
092 * or null if no acceptable Component exists.
093 *
094 * @exception IllegalArgumentException If root is not a focus cycle
095 * root of current, or if either root or current is null.
096 */
097 public Component getComponentAfter (Container root, Component current)
098 {
099 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 }