1 /* 2 * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing; 26 27 import java.awt.Component; 28 import java.awt.Container; 29 import java.awt.ComponentOrientation; 30 import java.util.Comparator; 31 import java.io.*; 32 import sun.awt.SunToolkit; 33 34 35 /** 36 * A SortingFocusTraversalPolicy which sorts Components based on their size, 37 * position, and orientation. Based on their size and position, Components are 38 * roughly categorized into rows and columns. For a Container with horizontal 39 * orientation, columns run left-to-right or right-to-left, and rows run top- 40 * to-bottom. For a Container with vertical orientation, columns run top-to- 41 * bottom and rows run left-to-right or right-to-left. See 42 * <code>ComponentOrientation</code> for more information. All columns in a 43 * row are fully traversed before proceeding to the next row. 44 * 45 * @author David Mendenhall 46 * 47 * @see java.awt.ComponentOrientation 48 * @since 1.4 49 */ 50 public class LayoutFocusTraversalPolicy extends SortingFocusTraversalPolicy 51 implements Serializable 52 { 53 // Delegate most of our fitness test to Default so that we only have to 54 // code the algorithm once. 55 private static final SwingDefaultFocusTraversalPolicy fitnessTestPolicy = 56 new SwingDefaultFocusTraversalPolicy(); 57 58 /** 59 * Constructs a LayoutFocusTraversalPolicy. 60 */ 61 public LayoutFocusTraversalPolicy() { 62 super(new LayoutComparator()); 63 } 64 65 /** 66 * Constructs a LayoutFocusTraversalPolicy with the passed in 67 * <code>Comparator</code>. 68 */ 69 LayoutFocusTraversalPolicy(Comparator<? super Component> c) { 70 super(c); 71 } 72 73 /** 74 * Returns the Component that should receive the focus after aComponent. 75 * aContainer must be a focus cycle root of aComponent. 76 * <p> 77 * By default, LayoutFocusTraversalPolicy implicitly transfers focus down- 78 * cycle. That is, during normal focus traversal, the Component 79 * traversed after a focus cycle root will be the focus-cycle-root's 80 * default Component to focus. This behavior can be disabled using the 81 * <code>setImplicitDownCycleTraversal</code> method. 82 * <p> 83 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus 84 * traversal policy provider</a>, the focus is always transferred down-cycle. 85 * 86 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider 87 * @param aComponent a (possibly indirect) child of aContainer, or 88 * aContainer itself 89 * @return the Component that should receive the focus after aComponent, or 90 * null if no suitable Component can be found 91 * @throws IllegalArgumentException if aContainer is not a focus cycle 92 * root of aComponent or a focus traversal policy provider, or if either aContainer or 93 * aComponent is null 94 */ 95 public Component getComponentAfter(Container aContainer, 96 Component aComponent) { 97 if (aContainer == null || aComponent == null) { 98 throw new IllegalArgumentException("aContainer and aComponent cannot be null"); 99 } 100 Comparator comparator = getComparator(); 101 if (comparator instanceof LayoutComparator) { 102 ((LayoutComparator)comparator). 103 setComponentOrientation(aContainer. 104 getComponentOrientation()); 105 } 106 return super.getComponentAfter(aContainer, aComponent); 107 } 108 109 /** 110 * Returns the Component that should receive the focus before aComponent. 111 * aContainer must be a focus cycle root of aComponent. 112 * <p> 113 * By default, LayoutFocusTraversalPolicy implicitly transfers focus down- 114 * cycle. That is, during normal focus traversal, the Component 115 * traversed after a focus cycle root will be the focus-cycle-root's 116 * default Component to focus. This behavior can be disabled using the 117 * <code>setImplicitDownCycleTraversal</code> method. 118 * <p> 119 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus 120 * traversal policy provider</a>, the focus is always transferred down-cycle. 121 * 122 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider 123 * @param aComponent a (possibly indirect) child of aContainer, or 124 * aContainer itself 125 * @return the Component that should receive the focus before aComponent, 126 * or null if no suitable Component can be found 127 * @throws IllegalArgumentException if aContainer is not a focus cycle 128 * root of aComponent or a focus traversal policy provider, or if either aContainer or 129 * aComponent is null 130 */ 131 public Component getComponentBefore(Container aContainer, 132 Component aComponent) { 133 if (aContainer == null || aComponent == null) { 134 throw new IllegalArgumentException("aContainer and aComponent cannot be null"); 135 } 136 Comparator comparator = getComparator(); 137 if (comparator instanceof LayoutComparator) { 138 ((LayoutComparator)comparator). 139 setComponentOrientation(aContainer. 140 getComponentOrientation()); 141 } 142 return super.getComponentBefore(aContainer, aComponent); 143 } 144 145 /** 146 * Returns the first Component in the traversal cycle. This method is used 147 * to determine the next Component to focus when traversal wraps in the 148 * forward direction. 149 * 150 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose 151 * first Component is to be returned 152 * @return the first Component in the traversal cycle of aContainer, 153 * or null if no suitable Component can be found 154 * @throws IllegalArgumentException if aContainer is null 155 */ 156 public Component getFirstComponent(Container aContainer) { 157 if (aContainer == null) { 158 throw new IllegalArgumentException("aContainer cannot be null"); 159 } 160 Comparator comparator = getComparator(); 161 if (comparator instanceof LayoutComparator) { 162 ((LayoutComparator)comparator). 163 setComponentOrientation(aContainer. 164 getComponentOrientation()); 165 } 166 return super.getFirstComponent(aContainer); 167 } 168 169 /** 170 * Returns the last Component in the traversal cycle. This method is used 171 * to determine the next Component to focus when traversal wraps in the 172 * reverse direction. 173 * 174 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose 175 * last Component is to be returned 176 * @return the last Component in the traversal cycle of aContainer, 177 * or null if no suitable Component can be found 178 * @throws IllegalArgumentException if aContainer is null 179 */ 180 public Component getLastComponent(Container aContainer) { 181 if (aContainer == null) { 182 throw new IllegalArgumentException("aContainer cannot be null"); 183 } 184 Comparator comparator = getComparator(); 185 if (comparator instanceof LayoutComparator) { 186 ((LayoutComparator)comparator). 187 setComponentOrientation(aContainer. 188 getComponentOrientation()); 189 } 190 return super.getLastComponent(aContainer); 191 } 192 193 /** 194 * Determines whether the specified <code>Component</code> 195 * is an acceptable choice as the new focus owner. 196 * This method performs the following sequence of operations: 197 * <ol> 198 * <li>Checks whether <code>aComponent</code> is visible, displayable, 199 * enabled, and focusable. If any of these properties is 200 * <code>false</code>, this method returns <code>false</code>. 201 * <li>If <code>aComponent</code> is an instance of <code>JTable</code>, 202 * returns <code>true</code>. 203 * <li>If <code>aComponent</code> is an instance of <code>JComboBox</code>, 204 * then returns the value of 205 * <code>aComponent.getUI().isFocusTraversable(aComponent)</code>. 206 * <li>If <code>aComponent</code> is a <code>JComponent</code> 207 * with a <code>JComponent.WHEN_FOCUSED</code> 208 * <code>InputMap</code> that is neither <code>null</code> 209 * nor empty, returns <code>true</code>. 210 * <li>Returns the value of 211 * <code>DefaultFocusTraversalPolicy.accept(aComponent)</code>. 212 * </ol> 213 * 214 * @param aComponent the <code>Component</code> whose fitness 215 * as a focus owner is to be tested 216 * @see java.awt.Component#isVisible 217 * @see java.awt.Component#isDisplayable 218 * @see java.awt.Component#isEnabled 219 * @see java.awt.Component#isFocusable 220 * @see javax.swing.plaf.ComboBoxUI#isFocusTraversable 221 * @see javax.swing.JComponent#getInputMap 222 * @see java.awt.DefaultFocusTraversalPolicy#accept 223 * @return <code>true</code> if <code>aComponent</code> is a valid choice 224 * for a focus owner; 225 * otherwise <code>false</code> 226 */ 227 protected boolean accept(Component aComponent) { 228 if (!super.accept(aComponent)) { 229 return false; 230 } else if (SunToolkit.isInstanceOf(aComponent, "javax.swing.JTable")) { 231 // JTable only has ancestor focus bindings, we thus force it 232 // to be focusable by returning true here. 233 return true; 234 } else if (SunToolkit.isInstanceOf(aComponent, "javax.swing.JComboBox")) { 235 JComboBox box = (JComboBox)aComponent; 236 return box.getUI().isFocusTraversable(box); 237 } else if (aComponent instanceof JComponent) { 238 JComponent jComponent = (JComponent)aComponent; 239 InputMap inputMap = jComponent.getInputMap(JComponent.WHEN_FOCUSED, 240 false); 241 while (inputMap != null && inputMap.size() == 0) { 242 inputMap = inputMap.getParent(); 243 } 244 if (inputMap != null) { 245 return true; 246 } 247 // Delegate to the fitnessTestPolicy, this will test for the 248 // case where the developer has overriden isFocusTraversable to 249 // return true. 250 } 251 return fitnessTestPolicy.accept(aComponent); 252 } 253 254 private void writeObject(ObjectOutputStream out) throws IOException { 255 out.writeObject(getComparator()); 256 out.writeBoolean(getImplicitDownCycleTraversal()); 257 } 258 private void readObject(ObjectInputStream in) 259 throws IOException, ClassNotFoundException 260 { 261 setComparator((Comparator)in.readObject()); 262 setImplicitDownCycleTraversal(in.readBoolean()); 263 } 264 } 265 266 // Create our own subclass and change accept to public so that we can call 267 // accept. 268 class SwingDefaultFocusTraversalPolicy 269 extends java.awt.DefaultFocusTraversalPolicy 270 { 271 public boolean accept(Component aComponent) { 272 return super.accept(aComponent); 273 } 274 }