OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / awt / ContainerOrderFocusTraversalPolicy.java
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 }