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 }