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    */
ContainerOrderFocusTraversalPolicy()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    */
getComponentAfter(Container root, Component current)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    */
getComponentBefore(Container root, Component current)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    */
getFirstComponent(Container root)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    */
getLastComponent(Container root)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    */
getDefaultComponent(Container root)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    */
setImplicitDownCycleTraversal(boolean value)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    */
getImplicitDownCycleTraversal()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    */
accept(Component current)406   protected boolean accept (Component current)
407   {
408     return (current.visible
409             && current.isDisplayable ()
410             && current.enabled
411             && current.focusable);
412   }
413 }
414