001: /*
002: *
003: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025: package java.awt;
026:
027: /**
028: * A FocusTraversalPolicy that determines traversal order based on the order
029: * of child Components in a Container. From a particular focus cycle root, the
030: * policy makes a pre-order traversal of the Component hierarchy, and traverses
031: * a Container's children according to the ordering of the array returned by
032: * <code>Container.getComponents()</code>. Portions of the hierarchy that are
033: * not visible and displayable will not be searched.
034: * <p>
035: * By default, ContainerOrderFocusTraversalPolicy implicitly transfers focus
036: * down-cycle. That is, during normal forward focus traversal, the Component
037: * traversed after a focus cycle root will be the focus-cycle-root's default
038: * Component to focus. This behavior can be disabled using the
039: * <code>setImplicitDownCycleTraversal</code> method.
040: * <p>
041: * By default, methods of this class with return a Component only if it is
042: * visible, displayable, enabled, and focusable. Subclasses can modify this
043: * behavior by overriding the <code>accept</code> method.
044: *
045: * @author David Mendenhall
046: * @version 1.3, 01/23/03
047: *
048: * @see Container#getComponents
049: * @since 1.4
050: */
051: public class ContainerOrderFocusTraversalPolicy extends
052: FocusTraversalPolicy implements java.io.Serializable {
053:
054: private static class MutableBoolean {
055: boolean value = false;
056: }
057:
058: private static final MutableBoolean found = new MutableBoolean();
059: private boolean implicitDownCycleTraversal = true;
060:
061: /**
062: * Returns the Component that should receive the focus after aComponent.
063: * focusCycleRoot must be a focus cycle root of aComponent.
064: * <p>
065: * By default, ContainerOrderFocusTraversalPolicy implicitly transfers
066: * focus down-cycle. That is, during normal forward focus traversal, the
067: * Component traversed after a focus cycle root will be the focus-cycle-
068: * root's default Component to focus. This behavior can be disabled using
069: * the <code>setImplicitDownCycleTraversal</code> method.
070: *
071: * @param focusCycleRoot a focus cycle root of aComponent
072: * @param aComponent a (possibly indirect) child of focusCycleRoot, or
073: * focusCycleRoot itself
074: * @return the Component that should receive the focus after aComponent, or
075: * null if no suitable Component can be found
076: * @throws IllegalArgumentException if focusCycleRoot is not a focus cycle
077: * root of aComponent, or if either focusCycleRoot or aComponent is
078: * null
079: */
080: public Component getComponentAfter(Container focusCycleRoot,
081: Component aComponent) {
082: if (focusCycleRoot == null || aComponent == null) {
083: throw new IllegalArgumentException(
084: "focusCycleRoot and aComponent cannot be null");
085: }
086: if (!aComponent.isFocusCycleRoot(focusCycleRoot)) {
087: throw new IllegalArgumentException(
088: "focusCycleRoot is not a focus cyle root of aComponent");
089: }
090: synchronized (focusCycleRoot.getTreeLock()) {
091: found.value = false;
092: Component retval = getComponentAfter(focusCycleRoot,
093: aComponent, found);
094: if (retval != null) {
095: return retval;
096: } else if (found.value) {
097: return getFirstComponent(focusCycleRoot);
098: } else {
099: return null;
100: }
101: }
102: }
103:
104: private Component getComponentAfter(Container aContainer,
105: Component aComponent, MutableBoolean found) {
106: if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
107: return null;
108: }
109: if (found.value) {
110: if (accept(aContainer)) {
111: return aContainer;
112: }
113: } else if (aContainer == aComponent) {
114: found.value = true;
115: }
116: for (int i = 0; i < aContainer.ncomponents; i++) {
117: Component comp = aContainer.component[i];
118: if ((comp instanceof Container)
119: && !((Container) comp).isFocusCycleRoot()) {
120: Component retval = getComponentAfter((Container) comp,
121: aComponent, found);
122: if (retval != null) {
123: return retval;
124: }
125: } else if (found.value) {
126: if (accept(comp)) {
127: return comp;
128: }
129: } else if (comp == aComponent) {
130: found.value = true;
131: }
132: if (found.value && getImplicitDownCycleTraversal()
133: && (comp instanceof Container)
134: && ((Container) comp).isFocusCycleRoot()) {
135: Container cont = (Container) comp;
136: Component retval = cont.getFocusTraversalPolicy()
137: .getDefaultComponent(cont);
138: if (retval != null) {
139: return retval;
140: }
141: }
142: }
143: return null;
144: }
145:
146: /**
147: * Returns the Component that should receive the focus before aComponent.
148: * focusCycleRoot must be a focus cycle root of aComponent.
149: *
150: * @param focusCycleRoot a focus cycle root of aComponent
151: * @param aComponent a (possibly indirect) child of focusCycleRoot, or
152: * focusCycleRoot itself
153: * @return the Component that should receive the focus before aComponent,
154: * or null if no suitable Component can be found
155: * @throws IllegalArgumentException if focusCycleRoot is not a focus cycle
156: * root of aComponent, or if either focusCycleRoot or aComponent is
157: * null
158: */
159: public Component getComponentBefore(Container focusCycleRoot,
160: Component aComponent) {
161: if (focusCycleRoot == null || aComponent == null) {
162: throw new IllegalArgumentException(
163: "focusCycleRoot and aComponent cannot be null");
164: }
165: if (!aComponent.isFocusCycleRoot(focusCycleRoot)) {
166: throw new IllegalArgumentException(
167: "focusCycleRoot is not a focus cyle root of aComponent");
168: }
169: synchronized (focusCycleRoot.getTreeLock()) {
170: found.value = false;
171: Component retval = getComponentBefore(focusCycleRoot,
172: aComponent, found);
173: if (retval != null) {
174: return retval;
175: } else if (found.value) {
176: return getLastComponent(focusCycleRoot);
177: } else {
178: return null;
179: }
180: }
181: }
182:
183: private Component getComponentBefore(Container aContainer,
184: Component aComponent, MutableBoolean found) {
185: if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
186: return null;
187: }
188: for (int i = aContainer.ncomponents - 1; i >= 0; i--) {
189: Component comp = aContainer.component[i];
190: if (comp == aComponent) {
191: found.value = true;
192: } else if ((comp instanceof Container)
193: && !((Container) comp).isFocusCycleRoot()) {
194: Component retval = getComponentBefore((Container) comp,
195: aComponent, found);
196: if (retval != null) {
197: return retval;
198: }
199: } else if (found.value) {
200: if (accept(comp)) {
201: return comp;
202: }
203: }
204: }
205: if (found.value) {
206: if (accept(aContainer)) {
207: return aContainer;
208: }
209: } else if (aContainer == aComponent) {
210: found.value = true;
211: }
212: return null;
213: }
214:
215: /**
216: * Returns the first Component in the traversal cycle. This method is used
217: * to determine the next Component to focus when traversal wraps in the
218: * forward direction.
219: *
220: * @param focusCycleRoot the focus cycle root whose first Component is to
221: * be returned
222: * @return the first Component in the traversal cycle when focusCycleRoot
223: * is the focus cycle root, or null if no suitable Component can be
224: * found
225: * @throws IllegalArgumentException if focusCycleRoot is null
226: */
227: public Component getFirstComponent(Container focusCycleRoot) {
228: if (focusCycleRoot == null) {
229: throw new IllegalArgumentException(
230: "focusCycleRoot cannot be null");
231: }
232: synchronized (focusCycleRoot.getTreeLock()) {
233: if (!(focusCycleRoot.isVisible() && focusCycleRoot
234: .isDisplayable())) {
235: return null;
236: }
237: if (accept(focusCycleRoot)) {
238: return focusCycleRoot;
239: }
240: for (int i = 0; i < focusCycleRoot.ncomponents; i++) {
241: Component comp = focusCycleRoot.component[i];
242: if (comp instanceof Container
243: && !((Container) comp).isFocusCycleRoot()) {
244: Component retval = getFirstComponent((Container) comp);
245: if (retval != null) {
246: return retval;
247: }
248: } else if (accept(comp)) {
249: return comp;
250: }
251: }
252: }
253: return null;
254: }
255:
256: /**
257: * Returns the last Component in the traversal cycle. This method is used
258: * to determine the next Component to focus when traversal wraps in the
259: * reverse direction.
260: *
261: * @param focusCycleRoot the focus cycle root whose last Component is to be
262: * returned
263: * @return the last Component in the traversal cycle when focusCycleRoot is
264: * the focus cycle root, or null if no suitable Component can be
265: * found
266: * @throws IllegalArgumentException if focusCycleRoot is null
267: */
268: public Component getLastComponent(Container focusCycleRoot) {
269: if (focusCycleRoot == null) {
270: throw new IllegalArgumentException(
271: "focusCycleRoot cannot be null");
272: }
273: synchronized (focusCycleRoot.getTreeLock()) {
274: if (!(focusCycleRoot.isVisible() && focusCycleRoot
275: .isDisplayable())) {
276: return null;
277: }
278: for (int i = focusCycleRoot.ncomponents - 1; i >= 0; i--) {
279: Component comp = focusCycleRoot.component[i];
280: if (comp instanceof Container
281: && !((Container) comp).isFocusCycleRoot()) {
282: Component retval = getLastComponent((Container) comp);
283: if (retval != null) {
284: return retval;
285: }
286: } else if (accept(comp)) {
287: return comp;
288: }
289: }
290: if (accept(focusCycleRoot)) {
291: return focusCycleRoot;
292: }
293: }
294: return null;
295: }
296:
297: /**
298: * Returns the default Component to focus. This Component will be the first
299: * to receive focus when traversing down into a new focus traversal cycle
300: * rooted at focusCycleRoot. The default implementation of this method
301: * returns the same Component as <code>getFirstComponent</code>.
302: *
303: * @param focusCycleRoot the focus cycle root whose default Component is to
304: * be returned
305: * @return the default Component in the traversal cycle when focusCycleRoot
306: * is the focus cycle root, or null if no suitable Component can be
307: * found
308: * @see #getFirstComponent
309: * @throws IllegalArgumentException if focusCycleRoot is null
310: */
311: public Component getDefaultComponent(Container focusCycleRoot) {
312: return getFirstComponent(focusCycleRoot);
313: }
314:
315: /**
316: * Sets whether this ContainerOrderFocusTraversalPolicy transfers focus
317: * down-cycle implicitly. If <code>true</code>, during normal forward focus
318: * traversal, the Component traversed after a focus cycle root will be the
319: * focus-cycle-root's default Component to focus. If <code>false</code>,
320: * the next Component in the focus traversal cycle rooted at the specified
321: * focus cycle root will be traversed instead. The default value for this
322: * property is <code>true</code>.
323: *
324: * @param implicitDownCycleTraversal whether this
325: * ContainerOrderFocusTraversalPolicy transfers focus down-cycle
326: * implicitly
327: * @see #getImplicitDownCycleTraversal
328: * @see #getFirstComponent
329: */
330: public void setImplicitDownCycleTraversal(
331: boolean implicitDownCycleTraversal) {
332: this .implicitDownCycleTraversal = implicitDownCycleTraversal;
333: }
334:
335: /**
336: * Returns whether this ContainerOrderFocusTraversalPolicy transfers focus
337: * down-cycle implicitly. If <code>true</code>, during normal forward focus
338: * traversal, the Component traversed after a focus cycle root will be the
339: * focus-cycle-root's default Component to focus. If <code>false</code>,
340: * the next Component in the focus traversal cycle rooted at the specified
341: * focus cycle root will be traversed instead.
342: *
343: * @return whether this ContainerOrderFocusTraversalPolicy transfers focus
344: * down-cycle implicitly
345: * @see #setImplicitDownCycleTraversal
346: * @see #getFirstComponent
347: */
348: public boolean getImplicitDownCycleTraversal() {
349: return implicitDownCycleTraversal;
350: }
351:
352: /**
353: * Determines whether a Component is an acceptable choice as the new
354: * focus owner. By default, this method will accept a Component if and
355: * only if it is visible, displayable, enabled, and focusable.
356: *
357: * @param aComponent the Component whose fitness as a focus owner is to
358: * be tested
359: * @return <code>true</code> if aComponent is visible, displayable,
360: * enabled, and focusable; <code>false</code> otherwise
361: */
362: protected boolean accept(Component aComponent) {
363: if (!(aComponent.isVisible() && aComponent.isDisplayable()
364: && aComponent.isFocusable() && aComponent.isEnabled())) {
365: return false;
366: }
367: // Verify that the Component is recursively enabled. Disabling a
368: // heavyweight Container disables its children, whereas disabling
369: // a lightweight Container does not.
370: if (!(aComponent instanceof Window)) {
371: for (Container enableTest = aComponent.getParent(); enableTest != null; enableTest = enableTest
372: .getParent()) {
373: if (!(enableTest.isEnabled() || enableTest
374: .isLightweight())) {
375: return false;
376: }
377: if (enableTest instanceof Window) {
378: break;
379: }
380: }
381: }
382: return true;
383: }
384: }
|