001/* ContainerOrderFocusTraversalPolicy.java -- 
002   Copyright (C) 2002, 2005  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.awt;
040
041import 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 */
063public 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}