001    /* SortingFocusTraversalPolicy.java --
002       Copyright (C) 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 javax.swing;
040    
041    import java.awt.Component;
042    import java.awt.Container;
043    import java.util.Comparator;
044    import java.util.Iterator;
045    import java.util.TreeSet;
046    
047    /**
048     * @author Graydon Hoare
049     * @author Michael Koch
050     *
051     * @since 1.4
052     */
053    public class SortingFocusTraversalPolicy
054      extends InternalFrameFocusTraversalPolicy
055    {
056      /**
057       * The comparator used to sort elements in the focus traversal cycle
058       * managed by this class.
059       */
060      Comparator comparator;
061    
062      /**
063       * <p>Whether or not to perform an "implicit DownCycle" when selecting
064       * successor components within a focus cycle.</p>
065       *
066       * <p>When this is true, requesting the "next" component following a
067       * component which is a focus cycle root (and, necessarily, a container)
068       * will enter the focus cycle root of that container, and return its
069       * default focus.</p>
070       *
071       * <p>When this property is false, requesting the "next" component will
072       * simply advance within the containing focus cycle, subject to the
073       * {@link #comparator} order and the {@link #accept} judgment.</p>
074       *
075       * @see #getImplicitDownCycleTraversal()
076       */
077      boolean implicitDownCycleTraversal = true;
078    
079      /**
080       * Creates a new <code>SortingFocusTraversalPolicy</code> with no
081       * comparator set.
082       */
083      protected SortingFocusTraversalPolicy()
084      {
085        // Do nothing here.
086      }
087    
088      /**
089       * Creates a new <code>SortingFocusTraversalPolicy</code> with the given
090       * comparator set.
091       *
092       * @param comparator the comparator to set
093       */
094      public SortingFocusTraversalPolicy(Comparator<? super Component> comparator)
095      {
096        this.comparator = comparator;
097      }
098    
099      /**
100       * Decide whether a component is an acceptable focus owner.
101       *
102       * @param comp The component which is a candidate for focus ownership.
103       *
104       * @return true if the component is focusable, displayable, visible, and
105       * enabled; otherwise false
106       */
107      protected boolean accept(Component comp)
108      {
109        return (comp.isVisible()
110                && comp.isDisplayable()
111                && comp.isEnabled()
112                && comp.isFocusable());
113      }
114    
115      /**
116       * Get the current value of the {@link #comparator} property.
117       *
118       * @return the current value of the property
119       *
120       * @see #setComparator
121       */
122      protected Comparator<? super Component> getComparator()
123      {
124        return comparator;
125      }
126    
127      /**
128       * Set the current value of the {@link #comparator} property.
129       *
130       * @param comparator the new value of the property
131       *
132       * @see #getComparator
133       */
134      protected void setComparator(Comparator<? super Component> comparator)
135      {
136        this.comparator = comparator;
137      }
138    
139      private TreeSet getSortedCycle(Container root, TreeSet set)
140      {
141        if (set == null)
142          set = (getComparator() == null
143                 ? new TreeSet()
144                 : new TreeSet(getComparator()));
145    
146        if (root != null)
147          {
148            Component[] comps = root.getComponents();
149            for (int i = 0; i < comps.length; ++i)
150              {
151                Component c = comps[i];
152                if (accept(c))
153                  set.add(c);
154                if (c instanceof Container)
155                  getSortedCycle((Container) c, set);
156              }
157          }
158        return set;
159      }
160    
161      /**
162       * Return the component which follows the specified component in this
163       * focus cycle, relative to the order imposed by {@link
164       * #comparator}. Candidate components are only considered if they are
165       * accepted by the {@link #accept} method.
166       *
167       * If {@link #getImplicitDownCycleTraversal} is <code>true</code> and the
168       * <code>comp</code> is a focus cycle root, an "implicit DownCycle"
169       * occurs and the method returns the
170       * <code>getDefaultComponent(comp)</code>.
171       *
172       * @param root the focus cycle root to search for a successor within
173       * @param comp the component to search for the successor of
174       *
175       * @return the component following the specified component under
176       * the specified root, or null if no such component is found
177       *
178       * @throws IllegalArgumentException if either argument is null, or
179       * if the root is not a focus cycle root of the component
180       */
181      public Component getComponentAfter(Container root,
182                                         Component comp)
183      {
184        if (comp == null || root == null || !comp.isFocusCycleRoot(root))
185          throw new IllegalArgumentException();
186    
187        if (getImplicitDownCycleTraversal()
188            && comp instanceof Container
189            && ((Container)comp).isFocusCycleRoot())
190          {
191            return getDefaultComponent((Container) comp);
192          }
193    
194        TreeSet set = getSortedCycle(root, null);
195        Iterator i = set.iterator();
196        while (i.hasNext())
197          {
198            Component c = (Component) i.next();
199            if (c != null && c.equals(comp))
200              {
201                if (i.hasNext())
202                  return (Component) i.next();
203                break;
204              }
205          }
206        return null;
207      }
208    
209    
210      /**
211       * Return the component which precedes the specified component in this
212       * focus cycle, relative to the order imposed by {@link
213       * #comparator}. Candidate components are only considered if they are
214       * accepted by the {@link #accept} method.
215       *
216       * @param root the focus cycle root to search for a predecessor within
217       * @param comp the component to search for the predecessor of
218       *
219       * @return the component preceding the specified component under the
220       * specified root, or null if no such component is found
221       *
222       * @throws IllegalArgumentException if either argument is null, or
223       * if the root is not a focus cycle root of the component
224       */
225      public Component getComponentBefore(Container root,
226                                          Component comp)
227      {
228        if (comp == null || root == null || !comp.isFocusCycleRoot(root))
229          throw new IllegalArgumentException();
230        TreeSet set = getSortedCycle(root, null);
231        Iterator i = set.iterator();
232        Component prev = null;
233        while (i.hasNext())
234          {
235            Component c = (Component) i.next();
236            if (c != null && c.equals(comp))
237              break;
238            prev = c;
239          }
240        return prev;
241      }
242    
243      /**
244       * Return the default component of <code>root</code>, which is by default
245       * the same as the first component, returned by {@link
246       * #getFirstComponent}.
247       *
248       * @param root the focus cycle root to return the default component of
249       *
250       * @return the default focus component for <code>root</code>
251       *
252       * @throws IllegalArgumentException if root is null
253       */
254      public Component getDefaultComponent(Container root)
255      {
256        return getFirstComponent(root);
257      }
258    
259      /**
260       * Return the first focusable component of the focus cycle root
261       * <code>comp</code> under the ordering imposed by the {@link
262       * #comparator} property. Candidate components are only considered if
263       * they are accepted by the {@link #accept} method.
264       *
265       * @param root the focus cycle root to search for the first component of
266       *
267       * @return the first component under <code>root</code>, or null if
268       * no components are found.
269       *
270       * @throws IllegalArgumentException if root is null
271       */
272      public Component getFirstComponent(Container root)
273      {
274        if (root == null)
275          throw new IllegalArgumentException();
276        TreeSet set = getSortedCycle(root, null);
277        Iterator i = set.iterator();
278        if (i.hasNext())
279          return (Component) i.next();
280        return null;
281      }
282    
283      /**
284       * Return the last focusable component of the focus cycle root
285       * <code>comp</code> under the ordering imposed by the {@link
286       * #comparator} property. Candidate components are only considered if
287       * they are accepted by the {@link #accept} method.
288       *
289       * @param root the focus cycle root to search for the last component of
290       *
291       * @return the last component under <code>root</code>, or null if
292       * no components are found.
293       *
294       * @throws IllegalArgumentException if root is null
295       */
296      public Component getLastComponent(Container root)
297      {
298        if (root == null)
299          throw new IllegalArgumentException();
300        TreeSet set = getSortedCycle(root, null);
301        Iterator i = set.iterator();
302        Component last = null;
303        while (i.hasNext())
304          last = (Component) i.next();
305        return last;
306      }
307    
308      /**
309       * Return the current value of the {@link #implicitDownCycleTraversal}
310       * property.
311       *
312       * @return the current value of the property
313       *
314       * @see #setImplicitDownCycleTraversal
315       */
316      public boolean getImplicitDownCycleTraversal()
317      {
318        return implicitDownCycleTraversal;
319      }
320    
321      /**
322       * Set the current value of the {@link #implicitDownCycleTraversal}
323       * property.
324       *
325       * @param down the new value of the property
326       *
327       * @see #getImplicitDownCycleTraversal
328       */
329      public void setImplicitDownCycleTraversal(boolean down)
330      {
331        implicitDownCycleTraversal = down;
332      }
333    }