1 /* SortingFocusTraversalPolicy.java -- 2 Copyright (C) 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 javax.swing; 40 41 import java.awt.Component; 42 import java.awt.Container; 43 import java.util.Comparator; 44 import java.util.Iterator; 45 import java.util.TreeSet; 46 47 /** 48 * @author Graydon Hoare 49 * @author Michael Koch 50 * 51 * @since 1.4 52 */ 53 public class SortingFocusTraversalPolicy 54 extends InternalFrameFocusTraversalPolicy 55 { 56 /** 57 * The comparator used to sort elements in the focus traversal cycle 58 * managed by this class. 59 */ 60 Comparator comparator; 61 62 /** 63 * <p>Whether or not to perform an "implicit DownCycle" when selecting 64 * successor components within a focus cycle.</p> 65 * 66 * <p>When this is true, requesting the "next" component following a 67 * component which is a focus cycle root (and, necessarily, a container) 68 * will enter the focus cycle root of that container, and return its 69 * default focus.</p> 70 * 71 * <p>When this property is false, requesting the "next" component will 72 * simply advance within the containing focus cycle, subject to the 73 * {@link #comparator} order and the {@link #accept} judgment.</p> 74 * 75 * @see #getImplicitDownCycleTraversal() 76 */ 77 boolean implicitDownCycleTraversal = true; 78 79 /** 80 * Creates a new <code>SortingFocusTraversalPolicy</code> with no 81 * comparator set. 82 */ SortingFocusTraversalPolicy()83 protected SortingFocusTraversalPolicy() 84 { 85 // Do nothing here. 86 } 87 88 /** 89 * Creates a new <code>SortingFocusTraversalPolicy</code> with the given 90 * comparator set. 91 * 92 * @param comparator the comparator to set 93 */ SortingFocusTraversalPolicy(Comparator<? super Component> comparator)94 public SortingFocusTraversalPolicy(Comparator<? super Component> comparator) 95 { 96 this.comparator = comparator; 97 } 98 99 /** 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 */ accept(Component comp)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 */ getComparator()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 */ setComparator(Comparator<? super Component> comparator)134 protected void setComparator(Comparator<? super Component> comparator) 135 { 136 this.comparator = comparator; 137 } 138 getSortedCycle(Container root, TreeSet set)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 */ getComponentAfter(Container root, Component comp)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 */ getComponentBefore(Container root, Component comp)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 */ getDefaultComponent(Container root)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 */ getFirstComponent(Container root)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 */ getLastComponent(Container root)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 */ getImplicitDownCycleTraversal()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 */ setImplicitDownCycleTraversal(boolean down)329 public void setImplicitDownCycleTraversal(boolean down) 330 { 331 implicitDownCycleTraversal = down; 332 } 333 } 334