1 /* 2 * Copyright (c) 2000, 2014, 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 @SuppressWarnings("serial") // Parts of superclass are not serializable across versions 51 public class LayoutFocusTraversalPolicy extends SortingFocusTraversalPolicy 52 implements Serializable 53 { 54 // Delegate most of our fitness test to Default so that we only have to 55 // code the algorithm once. 56 private static final SwingDefaultFocusTraversalPolicy fitnessTestPolicy = 57 new SwingDefaultFocusTraversalPolicy(); 58 59 /** 60 * Constructs a LayoutFocusTraversalPolicy. 61 */ 62 public LayoutFocusTraversalPolicy() { 63 super(new LayoutComparator()); 64 } 65 66 /** 67 * Constructs a LayoutFocusTraversalPolicy with the passed in 68 * <code>Comparator</code>. 69 */ 70 LayoutFocusTraversalPolicy(Comparator<? super Component> c) { 71 super(c); 72 } 73 74 /** 75 * Returns the Component that should receive the focus after aComponent. 76 * aContainer must be a focus cycle root of aComponent. 77 * <p> 78 * By default, LayoutFocusTraversalPolicy implicitly transfers focus down- 79 * cycle. That is, during normal focus traversal, the Component 80 * traversed after a focus cycle root will be the focus-cycle-root's 81 * default Component to focus. This behavior can be disabled using the 82 * <code>setImplicitDownCycleTraversal</code> method. 83 * <p> 84 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus 85 * traversal policy provider</a>, the focus is always transferred down-cycle. 86 * 87 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider 88 * @param aComponent a (possibly indirect) child of aContainer, or 89 * aContainer itself 90 * @return the Component that should receive the focus after aComponent, or 91 * null if no suitable Component can be found 92 * @throws IllegalArgumentException if aContainer is not a focus cycle 93 * root of aComponent or a focus traversal policy provider, or if either aContainer or 94 * aComponent is null 95 */ 96 public Component getComponentAfter(Container aContainer, 97 Component aComponent) { 98 if (aContainer == null || aComponent == null) { 99 throw new IllegalArgumentException("aContainer and aComponent cannot be null"); 100 } 101 Comparator comparator = getComparator(); 102 if (comparator instanceof LayoutComparator) { 103 ((LayoutComparator)comparator). 104 setComponentOrientation(aContainer. 105 getComponentOrientation()); 106 } 107 return super.getComponentAfter(aContainer, aComponent); 108 } 109 110 /** 111 * Returns the Component that should receive the focus before aComponent. 112 * aContainer must be a focus cycle root of aComponent. 113 * <p> 114 * By default, LayoutFocusTraversalPolicy implicitly transfers focus down- 115 * cycle. That is, during normal focus traversal, the Component 116 * traversed after a focus cycle root will be the focus-cycle-root's 117 * default Component to focus. This behavior can be disabled using the 118 * <code>setImplicitDownCycleTraversal</code> method. 119 * <p> 120 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus 121 * traversal policy provider</a>, the focus is always transferred down-cycle. 122 * 123 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider 124 * @param aComponent a (possibly indirect) child of aContainer, or 125 * aContainer itself 126 * @return the Component that should receive the focus before aComponent, 127 * or null if no suitable Component can be found 128 * @throws IllegalArgumentException if aContainer is not a focus cycle 129 * root of aComponent or a focus traversal policy provider, or if either aContainer or 130 * aComponent is null 131 */ 132 public Component getComponentBefore(Container aContainer, 133 Component aComponent) { 134 if (aContainer == null || aComponent == null) { 135 throw new IllegalArgumentException("aContainer and aComponent cannot be null"); 136 } 137 Comparator comparator = getComparator(); 138 if (comparator instanceof LayoutComparator) { 139 ((LayoutComparator)comparator). 140 setComponentOrientation(aContainer. 141 getComponentOrientation()); 142 } 143 return super.getComponentBefore(aContainer, aComponent); 144 } 145 146 /** 147 * Returns the first Component in the traversal cycle. This method is used 148 * to determine the next Component to focus when traversal wraps in the 149 * forward direction. 150 * 151 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose 152 * first Component is to be returned 153 * @return the first Component in the traversal cycle of aContainer, 154 * or null if no suitable Component can be found 155 * @throws IllegalArgumentException if aContainer is null 156 */ 157 public Component getFirstComponent(Container aContainer) { 158 if (aContainer == null) { 159 throw new IllegalArgumentException("aContainer cannot be null"); 160 } 161 Comparator comparator = getComparator(); 162 if (comparator instanceof LayoutComparator) { 163 ((LayoutComparator)comparator). 164 setComponentOrientation(aContainer. 165 getComponentOrientation()); 166 } 167 return super.getFirstComponent(aContainer); 168 } 169 170 /** 171 * Returns the last Component in the traversal cycle. This method is used 172 * to determine the next Component to focus when traversal wraps in the 173 * reverse direction. 174 * 175 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose 176 * last Component is to be returned 177 * @return the last Component in the traversal cycle of aContainer, 178 * or null if no suitable Component can be found 179 * @throws IllegalArgumentException if aContainer is null 180 */ 181 public Component getLastComponent(Container aContainer) { 182 if (aContainer == null) { 183 throw new IllegalArgumentException("aContainer cannot be null"); 184 } 185 Comparator comparator = getComparator(); 186 if (comparator instanceof LayoutComparator) { 187 ((LayoutComparator)comparator). 188 setComponentOrientation(aContainer. 189 getComponentOrientation()); 190 } 191 return super.getLastComponent(aContainer); 192 } 193 194 /** 195 * Determines whether the specified <code>Component</code> 196 * is an acceptable choice as the new focus owner. 197 * This method performs the following sequence of operations: 198 * <ol> 199 * <li>Checks whether <code>aComponent</code> is visible, displayable, 200 * enabled, and focusable. If any of these properties is 201 * <code>false</code>, this method returns <code>false</code>. 202 * <li>If <code>aComponent</code> is an instance of <code>JTable</code>, 203 * returns <code>true</code>. 204 * <li>If <code>aComponent</code> is an instance of <code>JComboBox</code>, 205 * then returns the value of 206 * <code>aComponent.getUI().isFocusTraversable(aComponent)</code>. 207 * <li>If <code>aComponent</code> is a <code>JComponent</code> 208 * with a <code>JComponent.WHEN_FOCUSED</code> 209 * <code>InputMap</code> that is neither <code>null</code> 210 * nor empty, returns <code>true</code>. 211 * <li>Returns the value of 212 * <code>DefaultFocusTraversalPolicy.accept(aComponent)</code>. 213 * </ol> 214 * 215 * @param aComponent the <code>Component</code> whose fitness 216 * as a focus owner is to be tested 217 * @see java.awt.Component#isVisible 218 * @see java.awt.Component#isDisplayable 219 * @see java.awt.Component#isEnabled 220 * @see java.awt.Component#isFocusable 221 * @see javax.swing.plaf.ComboBoxUI#isFocusTraversable 222 * @see javax.swing.JComponent#getInputMap 223 * @see java.awt.DefaultFocusTraversalPolicy#accept 224 * @return <code>true</code> if <code>aComponent</code> is a valid choice 225 * for a focus owner; 226 * otherwise <code>false</code> 227 */ 228 protected boolean accept(Component aComponent) { 229 if (!super.accept(aComponent)) { 230 return false; 231 } else if (SunToolkit.isInstanceOf(aComponent, "javax.swing.JTable")) { 232 // JTable only has ancestor focus bindings, we thus force it 233 // to be focusable by returning true here. 234 return true; 235 } else if (SunToolkit.isInstanceOf(aComponent, "javax.swing.JComboBox")) { 236 JComboBox box = (JComboBox)aComponent; 237 return box.getUI().isFocusTraversable(box); 238 } else if (aComponent instanceof JComponent) { 239 JComponent jComponent = (JComponent)aComponent; 240 InputMap inputMap = jComponent.getInputMap(JComponent.WHEN_FOCUSED, 241 false); 242 while (inputMap != null && inputMap.size() == 0) { 243 inputMap = inputMap.getParent(); 244 } 245 if (inputMap != null) { 246 return true; 247 } 248 // Delegate to the fitnessTestPolicy, this will test for the 249 // case where the developer has overriden isFocusTraversable to 250 // return true. 251 } 252 return fitnessTestPolicy.accept(aComponent); 253 } 254 255 private void writeObject(ObjectOutputStream out) throws IOException { 256 out.writeObject(getComparator()); 257 out.writeBoolean(getImplicitDownCycleTraversal()); 258 } 259 private void readObject(ObjectInputStream in) 260 throws IOException, ClassNotFoundException 261 { 262 setComparator((Comparator)in.readObject()); 263 setImplicitDownCycleTraversal(in.readBoolean()); 264 } 265 } 266 267 // Create our own subclass and change accept to public so that we can call 268 // accept. 269 @SuppressWarnings("serial") // JDK-implementation class 270 class SwingDefaultFocusTraversalPolicy 271 extends java.awt.DefaultFocusTraversalPolicy 272 { 273 public boolean accept(Component aComponent) { 274 return super.accept(aComponent); 275 } 276 }