001 /*
002 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing;
026
027 import java.awt.Component;
028 import java.awt.Container;
029 import java.awt.ComponentOrientation;
030 import java.util.Comparator;
031 import java.io.*;
032
033 /**
034 * A SortingFocusTraversalPolicy which sorts Components based on their size,
035 * position, and orientation. Based on their size and position, Components are
036 * roughly categorized into rows and columns. For a Container with horizontal
037 * orientation, columns run left-to-right or right-to-left, and rows run top-
038 * to-bottom. For a Container with vertical orientation, columns run top-to-
039 * bottom and rows run left-to-right or right-to-left. See
040 * <code>ComponentOrientation</code> for more information. All columns in a
041 * row are fully traversed before proceeding to the next row.
042 *
043 * @version 1.18, 05/05/07
044 * @author David Mendenhall
045 *
046 * @see java.awt.ComponentOrientation
047 * @since 1.4
048 */
049 public class LayoutFocusTraversalPolicy extends
050 SortingFocusTraversalPolicy implements Serializable {
051 // Delegate most of our fitness test to Default so that we only have to
052 // code the algorithm once.
053 private static final SwingDefaultFocusTraversalPolicy fitnessTestPolicy = new SwingDefaultFocusTraversalPolicy();
054
055 /**
056 * Constructs a LayoutFocusTraversalPolicy.
057 */
058 public LayoutFocusTraversalPolicy() {
059 super (new LayoutComparator());
060 }
061
062 /**
063 * Constructs a LayoutFocusTraversalPolicy with the passed in
064 * <code>Comparator</code>.
065 */
066 LayoutFocusTraversalPolicy(Comparator c) {
067 super (c);
068 }
069
070 /**
071 * Returns the Component that should receive the focus after aComponent.
072 * aContainer must be a focus cycle root of aComponent.
073 * <p>
074 * By default, LayoutFocusTraversalPolicy implicitly transfers focus down-
075 * cycle. That is, during normal focus traversal, the Component
076 * traversed after a focus cycle root will be the focus-cycle-root's
077 * default Component to focus. This behavior can be disabled using the
078 * <code>setImplicitDownCycleTraversal</code> method.
079 * <p>
080 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus
081 * traversal policy provider</a>, the focus is always transferred down-cycle.
082 *
083 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider
084 * @param aComponent a (possibly indirect) child of aContainer, or
085 * aContainer itself
086 * @return the Component that should receive the focus after aComponent, or
087 * null if no suitable Component can be found
088 * @throws IllegalArgumentException if aContainer is not a focus cycle
089 * root of aComponent or a focus traversal policy provider, or if either aContainer or
090 * aComponent is null
091 */
092 public Component getComponentAfter(Container aContainer,
093 Component aComponent) {
094 if (aContainer == null || aComponent == null) {
095 throw new IllegalArgumentException(
096 "aContainer and aComponent cannot be null");
097 }
098 Comparator comparator = getComparator();
099 if (comparator instanceof LayoutComparator) {
100 ((LayoutComparator) comparator)
101 .setComponentOrientation(aContainer
102 .getComponentOrientation());
103 }
104 return super .getComponentAfter(aContainer, aComponent);
105 }
106
107 /**
108 * Returns the Component that should receive the focus before aComponent.
109 * aContainer must be a focus cycle root of aComponent.
110 * <p>
111 * By default, LayoutFocusTraversalPolicy implicitly transfers focus down-
112 * cycle. That is, during normal focus traversal, the Component
113 * traversed after a focus cycle root will be the focus-cycle-root's
114 * default Component to focus. This behavior can be disabled using the
115 * <code>setImplicitDownCycleTraversal</code> method.
116 * <p>
117 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus
118 * traversal policy provider</a>, the focus is always transferred down-cycle.
119 *
120 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider
121 * @param aComponent a (possibly indirect) child of aContainer, or
122 * aContainer itself
123 * @return the Component that should receive the focus before aComponent,
124 * or null if no suitable Component can be found
125 * @throws IllegalArgumentException if aContainer is not a focus cycle
126 * root of aComponent or a focus traversal policy provider, or if either aContainer or
127 * aComponent is null
128 */
129 public Component getComponentBefore(Container aContainer,
130 Component aComponent) {
131 if (aContainer == null || aComponent == null) {
132 throw new IllegalArgumentException(
133 "aContainer and aComponent cannot be null");
134 }
135 Comparator comparator = getComparator();
136 if (comparator instanceof LayoutComparator) {
137 ((LayoutComparator) comparator)
138 .setComponentOrientation(aContainer
139 .getComponentOrientation());
140 }
141 return super .getComponentBefore(aContainer, aComponent);
142 }
143
144 /**
145 * Returns the first Component in the traversal cycle. This method is used
146 * to determine the next Component to focus when traversal wraps in the
147 * forward direction.
148 *
149 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose
150 * first Component is to be returned
151 * @return the first Component in the traversal cycle of aContainer,
152 * or null if no suitable Component can be found
153 * @throws IllegalArgumentException if aContainer is null
154 */
155 public Component getFirstComponent(Container aContainer) {
156 if (aContainer == null) {
157 throw new IllegalArgumentException(
158 "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(
183 "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 (aComponent instanceof 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 (aComponent instanceof 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(
241 JComponent.WHEN_FOCUSED, 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
260 private void readObject(ObjectInputStream in) throws IOException,
261 ClassNotFoundException {
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 class SwingDefaultFocusTraversalPolicy extends
270 java.awt.DefaultFocusTraversalPolicy {
271 public boolean accept(Component aComponent) {
272 return super.accept(aComponent);
273 }
274 }
|