001 /*
002 * Copyright 2000-2003 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 java.awt;
026
027 import java.util.logging.*;
028
029 /**
030 * A FocusTraversalPolicy that determines traversal order based on the order
031 * of child Components in a Container. From a particular focus cycle root, the
032 * policy makes a pre-order traversal of the Component hierarchy, and traverses
033 * a Container's children according to the ordering of the array returned by
034 * <code>Container.getComponents()</code>. Portions of the hierarchy that are
035 * not visible and displayable will not be searched.
036 * <p>
037 * By default, ContainerOrderFocusTraversalPolicy implicitly transfers focus
038 * down-cycle. That is, during normal forward focus traversal, the Component
039 * traversed after a focus cycle root will be the focus-cycle-root's default
040 * Component to focus. This behavior can be disabled using the
041 * <code>setImplicitDownCycleTraversal</code> method.
042 * <p>
043 * By default, methods of this class with return a Component only if it is
044 * visible, displayable, enabled, and focusable. Subclasses can modify this
045 * behavior by overriding the <code>accept</code> method.
046 * <p>
047 * This policy takes into account <a
048 * href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal
049 * policy providers</a>. When searching for first/last/next/previous Component,
050 * if a focus traversal policy provider is encountered, its focus traversal
051 * policy is used to perform the search operation.
052 *
053 * @author David Mendenhall
054 * @version 1.15, 05/05/07
055 *
056 * @see Container#getComponents
057 * @since 1.4
058 */
059 public class ContainerOrderFocusTraversalPolicy extends
060 FocusTraversalPolicy implements java.io.Serializable {
061 private static final MutableBoolean found = new MutableBoolean();
062
063 private static final Logger log = Logger
064 .getLogger("java.awt.ContainerOrderFocusTraversalPolicy");
065
066 /*
067 * JDK 1.4 serialVersionUID
068 */
069 private static final long serialVersionUID = 486933713763926351L;
070
071 private boolean implicitDownCycleTraversal = true;
072
073 /**
074 * Returns the Component that should receive the focus after aComponent.
075 * aContainer must be a focus cycle root of aComponent or a focus traversal policy provider.
076 * <p>
077 * By default, ContainerOrderFocusTraversalPolicy implicitly transfers
078 * focus down-cycle. That is, during normal forward focus traversal, the
079 * Component traversed after a focus cycle root will be the focus-cycle-
080 * root's default Component to focus. This behavior can be disabled using
081 * the <code>setImplicitDownCycleTraversal</code> method.
082 * <p>
083 * If aContainer is <a href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus
084 * traversal policy provider</a>, the focus is always transferred down-cycle.
085 *
086 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider
087 * @param aComponent a (possibly indirect) child of aContainer, or
088 * aContainer itself
089 * @return the Component that should receive the focus after aComponent, or
090 * null if no suitable Component can be found
091 * @throws IllegalArgumentException if aContainer is not a focus cycle
092 * root of aComponent or focus traversal policy provider, or if either aContainer or
093 * aComponent is null
094 */
095 public Component getComponentAfter(Container aContainer,
096 Component aComponent) {
097 if (log.isLoggable(Level.FINE))
098 log.fine("Looking for next component in " + aContainer
099 + " for " + aComponent);
100 if (aContainer == null || aComponent == null) {
101 throw new IllegalArgumentException(
102 "aContainer and aComponent cannot be null");
103 }
104 if (!aContainer.isFocusTraversalPolicyProvider()
105 && !aContainer.isFocusCycleRoot()) {
106 throw new IllegalArgumentException(
107 "aContainer should be focus cycle root or focus traversal policy provider");
108 } else if (aContainer.isFocusCycleRoot()
109 && !aComponent.isFocusCycleRoot(aContainer)) {
110 throw new IllegalArgumentException(
111 "aContainer is not a focus cycle root of aComponent");
112 }
113
114 synchronized (aContainer.getTreeLock()) {
115 found.value = false;
116 Component retval = getComponentAfter(aContainer,
117 aComponent, found);
118 if (retval != null) {
119 if (log.isLoggable(Level.FINE))
120 log.fine("After component is " + retval);
121 return retval;
122 } else if (found.value) {
123 if (log.isLoggable(Level.FINE))
124 log.fine("Didn't find next component in "
125 + aContainer
126 + " - falling back to the first ");
127 return getFirstComponent(aContainer);
128 } else {
129 if (log.isLoggable(Level.FINE))
130 log.fine("After component is null");
131 return null;
132 }
133 }
134 }
135
136 private Component getComponentAfter(Container aContainer,
137 Component aComponent, MutableBoolean found) {
138 if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
139 return null;
140 }
141
142 if (found.value) {
143 if (accept(aContainer)) {
144 return aContainer;
145 }
146 } else if (aContainer == aComponent) {
147 found.value = true;
148 }
149
150 for (int i = 0; i < aContainer.ncomponents; i++) {
151 Component comp = aContainer.component[i];
152 if ((comp instanceof Container)
153 && !((Container) comp).isFocusCycleRoot()) {
154 Component retval = null;
155 if (((Container) comp).isFocusTraversalPolicyProvider()) {
156 if (log.isLoggable(Level.FINE))
157 log.fine("Entering FTP " + comp);
158 Container cont = (Container) comp;
159 FocusTraversalPolicy policy = cont
160 .getFocusTraversalPolicy();
161 if (log.isLoggable(Level.FINE))
162 log.fine("FTP contains " + aComponent + ": "
163 + cont.isAncestorOf(aComponent));
164 if (found.value) {
165 retval = policy.getDefaultComponent(cont);
166 if (log.isLoggable(Level.FINE))
167 log
168 .fine("Used FTP for getting default component: "
169 + retval);
170 } else {
171 found.value = cont.isAncestorOf(aComponent);
172 if (found.value) {
173 if (aComponent == policy
174 .getLastComponent(cont)) {
175 // Reached last component, going to wrap - should switch to next provider
176 retval = null;
177 } else {
178 retval = policy.getComponentAfter(cont,
179 aComponent);
180 if (log.isLoggable(Level.FINE))
181 log
182 .fine("FTP found next for the component : "
183 + retval);
184 }
185 }
186 }
187 } else {
188 retval = getComponentAfter((Container) comp,
189 aComponent, found);
190 }
191 if (retval != null) {
192 return retval;
193 }
194 } else if (found.value) {
195 if (accept(comp)) {
196 return comp;
197 }
198 } else if (comp == aComponent) {
199 found.value = true;
200 }
201
202 if (found.value && getImplicitDownCycleTraversal()
203 && (comp instanceof Container)
204 && ((Container) comp).isFocusCycleRoot()) {
205 Container cont = (Container) comp;
206 Component retval = cont.getFocusTraversalPolicy()
207 .getDefaultComponent(cont);
208 if (retval != null) {
209 return retval;
210 }
211 }
212 }
213
214 return null;
215 }
216
217 /**
218 * Returns the Component that should receive the focus before aComponent.
219 * aContainer must be a focus cycle root of aComponent or a <a
220 * href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal policy
221 * provider</a>.
222 *
223 * @param aContainer a focus cycle root of aComponent or focus traversal policy provider
224 * @param aComponent a (possibly indirect) child of aContainer, or
225 * aContainer itself
226 * @return the Component that should receive the focus before aComponent,
227 * or null if no suitable Component can be found
228 * @throws IllegalArgumentException if aContainer is not a focus cycle
229 * root of aComponent or focus traversal policy provider, or if either aContainer or
230 * aComponent is null
231 */
232 public Component getComponentBefore(Container aContainer,
233 Component aComponent) {
234 if (aContainer == null || aComponent == null) {
235 throw new IllegalArgumentException(
236 "aContainer and aComponent cannot be null");
237 }
238 if (!aContainer.isFocusTraversalPolicyProvider()
239 && !aContainer.isFocusCycleRoot()) {
240 throw new IllegalArgumentException(
241 "aContainer should be focus cycle root or focus traversal policy provider");
242 } else if (aContainer.isFocusCycleRoot()
243 && !aComponent.isFocusCycleRoot(aContainer)) {
244 throw new IllegalArgumentException(
245 "aContainer is not a focus cycle root of aComponent");
246 }
247 synchronized (aContainer.getTreeLock()) {
248 found.value = false;
249 Component retval = getComponentBefore(aContainer,
250 aComponent, found);
251 if (retval != null) {
252 if (log.isLoggable(Level.FINE))
253 log.fine("Before component is " + retval);
254 return retval;
255 } else if (found.value) {
256 if (log.isLoggable(Level.FINE))
257 log.fine("Didn't find before component in "
258 + aContainer
259 + " - falling back to the first ");
260 return getLastComponent(aContainer);
261 } else {
262 if (log.isLoggable(Level.FINE))
263 log.fine("Before component is null");
264 return null;
265 }
266 }
267 }
268
269 private Component getComponentBefore(Container aContainer,
270 Component aComponent, MutableBoolean found) {
271 if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
272 return null;
273 }
274
275 for (int i = aContainer.ncomponents - 1; i >= 0; i--) {
276 Component comp = aContainer.component[i];
277 if (comp == aComponent) {
278 found.value = true;
279 } else if ((comp instanceof Container)
280 && !((Container) comp).isFocusCycleRoot()) {
281 Component retval = null;
282 if (((Container) comp).isFocusTraversalPolicyProvider()) {
283 if (log.isLoggable(Level.FINE))
284 log.fine("Entering FTP " + comp);
285 Container cont = (Container) comp;
286 FocusTraversalPolicy policy = cont
287 .getFocusTraversalPolicy();
288 if (log.isLoggable(Level.FINE))
289 log.fine("FTP contains " + aComponent + ": "
290 + cont.isAncestorOf(aComponent));
291 if (found.value) {
292 retval = policy.getLastComponent(cont);
293 if (log.isLoggable(Level.FINE))
294 log
295 .fine("Used FTP for getting last component: "
296 + retval);
297 } else {
298 found.value = cont.isAncestorOf(aComponent);
299 if (found.value) {
300 if (aComponent == policy
301 .getFirstComponent(cont)) {
302 retval = null;
303 } else {
304 retval = policy.getComponentBefore(
305 cont, aComponent);
306 if (log.isLoggable(Level.FINE))
307 log
308 .fine("FTP found previous for the component : "
309 + retval);
310 }
311 }
312 }
313 } else {
314 retval = getComponentBefore((Container) comp,
315 aComponent, found);
316 }
317 if (retval != null) {
318 return retval;
319 }
320 } else if (found.value) {
321 if (accept(comp)) {
322 return comp;
323 }
324 }
325 }
326
327 if (found.value) {
328 if (accept(aContainer)) {
329 return aContainer;
330 }
331 } else if (aContainer == aComponent) {
332 found.value = true;
333 }
334
335 return null;
336 }
337
338 /**
339 * Returns the first Component in the traversal cycle. This method is used
340 * to determine the next Component to focus when traversal wraps in the
341 * forward direction.
342 *
343 * @param aContainer the focus cycle root or focus traversal policy provider whose first
344 * Component is to be returned
345 * @return the first Component in the traversal cycle of aContainer,
346 * or null if no suitable Component can be found
347 * @throws IllegalArgumentException if aContainer is null
348 */
349 public Component getFirstComponent(Container aContainer) {
350 if (aContainer == null) {
351 throw new IllegalArgumentException(
352 "aContainer cannot be null");
353 }
354
355 synchronized (aContainer.getTreeLock()) {
356 if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
357 return null;
358 }
359
360 if (accept(aContainer)) {
361 return aContainer;
362 }
363
364 for (int i = 0; i < aContainer.ncomponents; i++) {
365 Component comp = aContainer.component[i];
366 if (comp instanceof Container
367 && !((Container) comp).isFocusCycleRoot()) {
368 Component retval = null;
369 Container cont = (Container) comp;
370 if (cont.isFocusTraversalPolicyProvider()) {
371 FocusTraversalPolicy policy = cont
372 .getFocusTraversalPolicy();
373 retval = policy.getDefaultComponent(cont);
374 } else {
375 retval = getFirstComponent((Container) comp);
376 }
377 if (retval != null) {
378 return retval;
379 }
380 } else if (accept(comp)) {
381 return comp;
382 }
383 }
384 }
385
386 return null;
387 }
388
389 /**
390 * Returns the last Component in the traversal cycle. This method is used
391 * to determine the next Component to focus when traversal wraps in the
392 * reverse direction.
393 *
394 * @param aContainer the focus cycle root or focus traversal policy provider whose last
395 * Component is to be returned
396 * @return the last Component in the traversal cycle of aContainer,
397 * or null if no suitable Component can be found
398 * @throws IllegalArgumentException if aContainer is null
399 */
400 public Component getLastComponent(Container aContainer) {
401 if (aContainer == null) {
402 throw new IllegalArgumentException(
403 "aContainer cannot be null");
404 }
405 if (log.isLoggable(Level.FINE))
406 log.fine("Looking for the last component in " + aContainer);
407
408 synchronized (aContainer.getTreeLock()) {
409 if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
410 return null;
411 }
412
413 for (int i = aContainer.ncomponents - 1; i >= 0; i--) {
414 Component comp = aContainer.component[i];
415 if (comp instanceof Container
416 && !((Container) comp).isFocusCycleRoot()) {
417 Component retval = null;
418 Container cont = (Container) comp;
419 if (cont.isFocusTraversalPolicyProvider()) {
420 if (log.isLoggable(Level.FINE))
421 log.fine("\tEntering FTP " + cont);
422 FocusTraversalPolicy policy = cont
423 .getFocusTraversalPolicy();
424 retval = policy.getLastComponent(cont);
425 } else {
426 if (log.isLoggable(Level.FINE))
427 log.fine("\tEntering sub-container");
428 retval = getLastComponent((Container) comp);
429 }
430 if (retval != null) {
431 if (log.isLoggable(Level.FINE))
432 log.fine("\tFound last component : "
433 + retval);
434 return retval;
435 }
436 } else if (accept(comp)) {
437 return comp;
438 }
439 }
440
441 if (accept(aContainer)) {
442 return aContainer;
443 }
444 }
445
446 return null;
447 }
448
449 /**
450 * Returns the default Component to focus. This Component will be the first
451 * to receive focus when traversing down into a new focus traversal cycle
452 * rooted at aContainer. The default implementation of this method
453 * returns the same Component as <code>getFirstComponent</code>.
454 *
455 * @param aContainer the focus cycle root or focus traversal policy provider whose default
456 * Component is to be returned
457 * @return the default Component in the traversal cycle of aContainer,
458 * or null if no suitable Component can be found
459 * @see #getFirstComponent
460 * @throws IllegalArgumentException if aContainer is null
461 */
462 public Component getDefaultComponent(Container aContainer) {
463 return getFirstComponent(aContainer);
464 }
465
466 /**
467 * Sets whether this ContainerOrderFocusTraversalPolicy transfers focus
468 * down-cycle implicitly. If <code>true</code>, during normal forward focus
469 * traversal, the Component traversed after a focus cycle root will be the
470 * focus-cycle-root's default Component to focus. If <code>false</code>,
471 * the next Component in the focus traversal cycle rooted at the specified
472 * focus cycle root will be traversed instead. The default value for this
473 * property is <code>true</code>.
474 *
475 * @param implicitDownCycleTraversal whether this
476 * ContainerOrderFocusTraversalPolicy transfers focus down-cycle
477 * implicitly
478 * @see #getImplicitDownCycleTraversal
479 * @see #getFirstComponent
480 */
481 public void setImplicitDownCycleTraversal(
482 boolean implicitDownCycleTraversal) {
483 this .implicitDownCycleTraversal = implicitDownCycleTraversal;
484 }
485
486 /**
487 * Returns whether this ContainerOrderFocusTraversalPolicy transfers focus
488 * down-cycle implicitly. If <code>true</code>, during normal forward focus
489 * traversal, the Component traversed after a focus cycle root will be the
490 * focus-cycle-root's default Component to focus. If <code>false</code>,
491 * the next Component in the focus traversal cycle rooted at the specified
492 * focus cycle root will be traversed instead.
493 *
494 * @return whether this ContainerOrderFocusTraversalPolicy transfers focus
495 * down-cycle implicitly
496 * @see #setImplicitDownCycleTraversal
497 * @see #getFirstComponent
498 */
499 public boolean getImplicitDownCycleTraversal() {
500 return implicitDownCycleTraversal;
501 }
502
503 /**
504 * Determines whether a Component is an acceptable choice as the new
505 * focus owner. By default, this method will accept a Component if and
506 * only if it is visible, displayable, enabled, and focusable.
507 *
508 * @param aComponent the Component whose fitness as a focus owner is to
509 * be tested
510 * @return <code>true</code> if aComponent is visible, displayable,
511 * enabled, and focusable; <code>false</code> otherwise
512 */
513 protected boolean accept(Component aComponent) {
514 if (!(aComponent.isVisible() && aComponent.isDisplayable()
515 && aComponent.isFocusable() && aComponent.isEnabled())) {
516 return false;
517 }
518
519 // Verify that the Component is recursively enabled. Disabling a
520 // heavyweight Container disables its children, whereas disabling
521 // a lightweight Container does not.
522 if (!(aComponent instanceof Window)) {
523 for (Container enableTest = aComponent.getParent(); enableTest != null; enableTest = enableTest
524 .getParent()) {
525 if (!(enableTest.isEnabled() || enableTest
526 .isLightweight())) {
527 return false;
528 }
529 if (enableTest instanceof Window) {
530 break;
531 }
532 }
533 }
534
535 return true;
536 }
537
538 }
539
540 class MutableBoolean {
541 boolean value = false;
542 }
|