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    }