62 extends InternalFrameFocusTraversalPolicy
63 {
64 private Comparator<? super Component> comparator;
65 private boolean implicitDownCycleTraversal = true;
66
67 private Logger log = Logger.getLogger("javax.swing.SortingFocusTraversalPolicy");
68
69 /**
70 * Used by getComponentAfter and getComponentBefore for efficiency. In
71 * order to maintain compliance with the specification of
72 * FocusTraversalPolicy, if traversal wraps, we should invoke
73 * getFirstComponent or getLastComponent. These methods may be overriden in
74 * subclasses to behave in a non-generic way. However, in the generic case,
75 * these methods will simply return the first or last Components of the
76 * sorted list, respectively. Since getComponentAfter and
77 * getComponentBefore have already built the sorted list before determining
78 * that they need to invoke getFirstComponent or getLastComponent, the
79 * sorted list should be reused if possible.
80 */
81 private Container cachedRoot;
82 private List cachedCycle;
83
84 // Delegate our fitness test to ContainerOrder so that we only have to
85 // code the algorithm once.
86 private static final SwingContainerOrderFocusTraversalPolicy
87 fitnessTestPolicy = new SwingContainerOrderFocusTraversalPolicy();
88
89 /**
90 * Constructs a SortingFocusTraversalPolicy without a Comparator.
91 * Subclasses must set the Comparator using <code>setComparator</code>
92 * before installing this FocusTraversalPolicy on a focus cycle root or
93 * KeyboardFocusManager.
94 */
95 protected SortingFocusTraversalPolicy() {
96 }
97
98 /**
99 * Constructs a SortingFocusTraversalPolicy with the specified Comparator.
100 */
101 public SortingFocusTraversalPolicy(Comparator<? super Component> comparator) {
102 this.comparator = comparator;
103 }
104
105 private void enumerateAndSortCycle(Container focusCycleRoot,
106 List cycle, Map defaults) {
107 List defaultRoots = null;
108
109 if (!focusCycleRoot.isShowing()) {
110 return;
111 }
112
113 enumerateCycle(focusCycleRoot, cycle);
114
115 boolean addDefaultComponents =
116 (defaults != null && getImplicitDownCycleTraversal());
117
118 if (log.isLoggable(Level.FINE)) log.fine("### Will add defaults: " + addDefaultComponents);
119
120 // Create a list of all default Components which should be added
121 // to the list
122 if (addDefaultComponents) {
123 defaultRoots = new ArrayList();
124 for (Iterator iter = cycle.iterator(); iter.hasNext(); ) {
125 Component comp = (Component)iter.next();
126 if ((comp instanceof Container) &&
127 ((Container)comp).isFocusCycleRoot())
128 {
129 defaultRoots.add(comp);
130 }
131 }
132 Collections.sort(defaultRoots, comparator);
133 }
134
135 // Sort the Components in the cycle
136 Collections.sort(cycle, comparator);
137
138 // Find all of the roots in the cycle and place their default
148 Component defComp =
149 root.getFocusTraversalPolicy().getDefaultComponent(root);
150
151 if (defComp != null && defComp.isShowing()) {
152 int index = Collections.binarySearch(cycle, root,
153 comparator);
154 if (index < 0) {
155 // If root is not in the list, then binarySearch
156 // returns (-(insertion point) - 1). defComp follows
157 // the index one less than the insertion point.
158
159 index = -index - 2;
160 }
161
162 defaults.put(new Integer(index), defComp);
163 }
164 }
165 }
166 }
167
168 private void enumerateCycle(Container container, List cycle) {
169 if (!(container.isVisible() && container.isDisplayable())) {
170 return;
171 }
172
173 cycle.add(container);
174
175 Component[] components = container.getComponents();
176 for (int i = 0; i < components.length; i++) {
177 Component comp = components[i];
178 if ((comp instanceof Container)
179 && !((Container)comp).isFocusTraversalPolicyProvider()
180 && !((Container)comp).isFocusCycleRoot()
181 && !((comp instanceof JComponent)
182 && ((JComponent)comp).isManagingFocus()))
183 {
184 enumerateCycle((Container)comp, cycle);
185 } else {
186 cycle.add(comp);
187 }
188 }
189 }
190
191 Container getTopmostProvider(Container focusCycleRoot, Component aComponent) {
192 Container aCont = aComponent.getParent();
193 Container ftp = null;
194 while (aCont != focusCycleRoot && aCont != null) {
195 if (aCont.isFocusTraversalPolicyProvider()) {
196 ftp = aCont;
197 }
210 * By default, SortingFocusTraversalPolicy implicitly transfers focus down-
211 * cycle. That is, during normal focus traversal, the Component
212 * traversed after a focus cycle root will be the focus-cycle-root's
213 * default Component to focus. This behavior can be disabled using the
214 * <code>setImplicitDownCycleTraversal</code> method.
215 * <p>
216 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus
217 * traversal policy provider</a>, the focus is always transferred down-cycle.
218 *
219 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider
220 * @param aComponent a (possibly indirect) child of aContainer, or
221 * aContainer itself
222 * @return the Component that should receive the focus after aComponent, or
223 * null if no suitable Component can be found
224 * @throws IllegalArgumentException if aContainer is not a focus cycle
225 * root of aComponent or a focus traversal policy provider, or if either aContainer or
226 * aComponent is null
227 */
228 public Component getComponentAfter(Container aContainer,
229 Component aComponent) {
230 if (log.isLoggable(Level.FINE)) log.fine("### Searching in " + aContainer.getName() + " for component after " + aComponent.getName());
231
232 if (aContainer == null || aComponent == null) {
233 throw new IllegalArgumentException("aContainer and aComponent cannot be null");
234 }
235 if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {
236 throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");
237 } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
238 throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
239 }
240
241 // See if the component is inside of policy provider
242 Container ftp = getTopmostProvider(aContainer, aComponent);
243 if (ftp != null) {
244 if (log.isLoggable(Level.FINE)) log.fine("### Asking FTP " + ftp.getName() + " for component after " + aComponent.getName());
245 // FTP knows how to find component after the given. We don't.
246 FocusTraversalPolicy policy = ftp.getFocusTraversalPolicy();
247 Component retval = policy.getComponentAfter(ftp, aComponent);
248 if (retval == policy.getFirstComponent(ftp)) {
249 retval = null;
250 }
251
252 if (retval != null) {
253 if (log.isLoggable(Level.FINE)) log.fine("### FTP returned " + retval.getName());
254 return retval;
255 }
256 aComponent = ftp;
257 }
258
259 List cycle = new ArrayList();
260 Map defaults = new HashMap();
261 enumerateAndSortCycle(aContainer, cycle, defaults);
262
263 int index;
264 try {
265 index = Collections.binarySearch(cycle, aComponent, comparator);
266 } catch (ClassCastException e) {
267 if (log.isLoggable(Level.FINE)) log.fine("### Didn't find component " + aComponent.getName() + " in a cycle " + aContainer.getName());
268 return getFirstComponent(aContainer);
269 }
270
271 if (index < 0) {
272 // Fix for 5070991.
273 // A workaround for a transitivity problem caused by ROW_TOLERANCE,
274 // because of that the component may be missed in the binary search.
275 // Try to search it again directly.
276 int i = cycle.indexOf(aComponent);
277 if (i >= 0) {
278 index = i;
279 } else {
280 // If we're not in the cycle, then binarySearch returns
281 // (-(insertion point) - 1). The next element is our insertion
282 // point.
283
284 index = -index - 2;
285 }
286 }
287
337 * @return the Component that should receive the focus before aComponent,
338 * or null if no suitable Component can be found
339 * @throws IllegalArgumentException if aContainer is not a focus cycle
340 * root of aComponent or a focus traversal policy provider, or if either aContainer or
341 * aComponent is null
342 */
343 public Component getComponentBefore(Container aContainer,
344 Component aComponent) {
345 if (aContainer == null || aComponent == null) {
346 throw new IllegalArgumentException("aContainer and aComponent cannot be null");
347 }
348 if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {
349 throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");
350 } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
351 throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
352 }
353
354 // See if the component is inside of policy provider
355 Container ftp = getTopmostProvider(aContainer, aComponent);
356 if (ftp != null) {
357 if (log.isLoggable(Level.FINE)) log.fine("### Asking FTP " + ftp.getName() + " for component after " + aComponent.getName());
358 // FTP knows how to find component after the given. We don't.
359 FocusTraversalPolicy policy = ftp.getFocusTraversalPolicy();
360 Component retval = policy.getComponentBefore(ftp, aComponent);
361 if (retval == policy.getLastComponent(ftp)) {
362 retval = null;
363 }
364 if (retval != null) {
365 if (log.isLoggable(Level.FINE)) log.fine("### FTP returned " + retval.getName());
366 return retval;
367 }
368 aComponent = ftp;
369 }
370
371
372 List cycle = new ArrayList();
373 Map defaults = new HashMap();
374 enumerateAndSortCycle(aContainer, cycle, defaults);
375
376 if (log.isLoggable(Level.FINE)) log.fine("### Cycle is " + cycle + ", component is " + aComponent);
377
378 int index;
379 try {
380 index = Collections.binarySearch(cycle, aComponent, comparator);
381 } catch (ClassCastException e) {
382 return getLastComponent(aContainer);
383 }
384
385 if (index < 0) {
386 // If we're not in the cycle, then binarySearch returns
387 // (-(insertion point) - 1). The previous element is our insertion
388 // point - 1.
389
390 index = -index - 2;
391 } else {
392 index--;
393 }
394
395 if (log.isLoggable(Level.FINE)) log.fine("### Index is " + index);
396
397 if (index >= 0) {
398 Component defComp = (Component)defaults.get(new Integer(index));
399 if (defComp != null && cycle.get(index) != aContainer) {
400 if (log.isLoggable(Level.FINE)) log.fine("### Returning default " + defComp.getName() + " at " + index);
401 return defComp;
402 }
403 }
404
405 do {
406 if (index < 0) {
407 this.cachedRoot = aContainer;
408 this.cachedCycle = cycle;
409
410 Component retval = getLastComponent(aContainer);
411
412 this.cachedRoot = null;
413 this.cachedCycle = null;
414
415 return retval;
416 } else {
417 Component comp = (Component)cycle.get(index);
418 if (accept(comp)) {
419 return comp;
420 } else if (comp instanceof Container && ((Container)comp).isFocusTraversalPolicyProvider()) {
421 return ((Container)comp).getFocusTraversalPolicy().getLastComponent((Container)comp);
422 }
423 }
424 index--;
425 } while (true);
426 }
427
428 /**
429 * Returns the first Component in the traversal cycle. This method is used
430 * to determine the next Component to focus when traversal wraps in the
431 * forward direction.
432 *
433 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose
434 * first Component is to be returned
435 * @return the first Component in the traversal cycle of aContainer,
436 * or null if no suitable Component can be found
437 * @throws IllegalArgumentException if aContainer is null
438 */
439 public Component getFirstComponent(Container aContainer) {
440 List cycle;
441
442 if (log.isLoggable(Level.FINE)) log.fine("### Getting first component in " + aContainer.getName());
443 if (aContainer == null) {
444 throw new IllegalArgumentException("aContainer cannot be null");
445 }
446
447 if (this.cachedRoot == aContainer) {
448 cycle = this.cachedCycle;
449 } else {
450 cycle = new ArrayList();
451 enumerateAndSortCycle(aContainer, cycle, null);
452 }
453
454 int size = cycle.size();
455 if (size == 0) {
456 return null;
457 }
458
459 for (int i= 0; i < cycle.size(); i++) {
460 Component comp = (Component)cycle.get(i);
461 if (accept(comp)) {
462 return comp;
463 } else if (comp instanceof Container && !(comp == aContainer) && ((Container)comp).isFocusTraversalPolicyProvider()) {
464 return ((Container)comp).getFocusTraversalPolicy().getDefaultComponent((Container)comp);
465 }
466 }
467 return null;
468 }
469
470 /**
471 * Returns the last Component in the traversal cycle. This method is used
472 * to determine the next Component to focus when traversal wraps in the
473 * reverse direction.
474 *
475 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose
476 * last Component is to be returned
477 * @return the last Component in the traversal cycle of aContainer,
478 * or null if no suitable Component can be found
479 * @throws IllegalArgumentException if aContainer is null
480 */
481 public Component getLastComponent(Container aContainer) {
482 List cycle;
483 if (log.isLoggable(Level.FINE)) log.fine("### Getting last component in " + aContainer.getName());
484
485 if (aContainer == null) {
486 throw new IllegalArgumentException("aContainer cannot be null");
487 }
488
489 if (this.cachedRoot == aContainer) {
490 cycle = this.cachedCycle;
491 } else {
492 cycle = new ArrayList();
493 enumerateAndSortCycle(aContainer, cycle, null);
494 }
495
496 int size = cycle.size();
497 if (size == 0) {
498 if (log.isLoggable(Level.FINE)) log.fine("### Cycle is empty");
499 return null;
500 }
501 if (log.isLoggable(Level.FINE)) log.fine("### Cycle is " + cycle);
502
503 for (int i= cycle.size()-1; i >= 0; i--) {
504 Component comp = (Component)cycle.get(i);
505 if (accept(comp)) {
506 return comp;
507 } else if (comp instanceof Container && !(comp == aContainer) && ((Container)comp).isFocusTraversalPolicyProvider()) {
508 return ((Container)comp).getFocusTraversalPolicy().getLastComponent((Container)comp);
509 }
510 }
511 return null;
512 }
513
514 /**
515 * Returns the default Component to focus. This Component will be the first
516 * to receive focus when traversing down into a new focus traversal cycle
517 * rooted at aContainer. The default implementation of this method
518 * returns the same Component as <code>getFirstComponent</code>.
519 *
520 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose
521 * default Component is to be returned
|
62 extends InternalFrameFocusTraversalPolicy
63 {
64 private Comparator<? super Component> comparator;
65 private boolean implicitDownCycleTraversal = true;
66
67 private Logger log = Logger.getLogger("javax.swing.SortingFocusTraversalPolicy");
68
69 /**
70 * Used by getComponentAfter and getComponentBefore for efficiency. In
71 * order to maintain compliance with the specification of
72 * FocusTraversalPolicy, if traversal wraps, we should invoke
73 * getFirstComponent or getLastComponent. These methods may be overriden in
74 * subclasses to behave in a non-generic way. However, in the generic case,
75 * these methods will simply return the first or last Components of the
76 * sorted list, respectively. Since getComponentAfter and
77 * getComponentBefore have already built the sorted list before determining
78 * that they need to invoke getFirstComponent or getLastComponent, the
79 * sorted list should be reused if possible.
80 */
81 private Container cachedRoot;
82 private List<Component> cachedCycle;
83
84 // Delegate our fitness test to ContainerOrder so that we only have to
85 // code the algorithm once.
86 private static final SwingContainerOrderFocusTraversalPolicy
87 fitnessTestPolicy = new SwingContainerOrderFocusTraversalPolicy();
88
89 /**
90 * Constructs a SortingFocusTraversalPolicy without a Comparator.
91 * Subclasses must set the Comparator using <code>setComparator</code>
92 * before installing this FocusTraversalPolicy on a focus cycle root or
93 * KeyboardFocusManager.
94 */
95 protected SortingFocusTraversalPolicy() {
96 }
97
98 /**
99 * Constructs a SortingFocusTraversalPolicy with the specified Comparator.
100 */
101 public SortingFocusTraversalPolicy(Comparator<? super Component> comparator) {
102 this.comparator = comparator;
103 }
104
105 private void enumerateAndSortCycle(Container focusCycleRoot,
106 List<Component> cycle, Map defaults) {
107 List defaultRoots = null;
108
109 if (!focusCycleRoot.isShowing()) {
110 return;
111 }
112
113 enumerateCycle(focusCycleRoot, cycle);
114
115 boolean addDefaultComponents =
116 (defaults != null && getImplicitDownCycleTraversal());
117
118 if (log.isLoggable(Level.FINE)) {
119 log.fine("### Will add defaults: " + addDefaultComponents);
120 }
121
122 // Create a list of all default Components which should be added
123 // to the list
124 if (addDefaultComponents) {
125 defaultRoots = new ArrayList();
126 for (Iterator iter = cycle.iterator(); iter.hasNext(); ) {
127 Component comp = (Component)iter.next();
128 if ((comp instanceof Container) &&
129 ((Container)comp).isFocusCycleRoot())
130 {
131 defaultRoots.add(comp);
132 }
133 }
134 Collections.sort(defaultRoots, comparator);
135 }
136
137 // Sort the Components in the cycle
138 Collections.sort(cycle, comparator);
139
140 // Find all of the roots in the cycle and place their default
150 Component defComp =
151 root.getFocusTraversalPolicy().getDefaultComponent(root);
152
153 if (defComp != null && defComp.isShowing()) {
154 int index = Collections.binarySearch(cycle, root,
155 comparator);
156 if (index < 0) {
157 // If root is not in the list, then binarySearch
158 // returns (-(insertion point) - 1). defComp follows
159 // the index one less than the insertion point.
160
161 index = -index - 2;
162 }
163
164 defaults.put(new Integer(index), defComp);
165 }
166 }
167 }
168 }
169
170 private void enumerateCycle(Container container, List<Component> cycle) {
171 if (!(container.isVisible() && container.isDisplayable())) {
172 return;
173 }
174
175 cycle.add(container);
176
177 Component[] components = container.getComponents();
178 for (Component comp : components) {
179 if ((comp instanceof Container)
180 && !((Container)comp).isFocusTraversalPolicyProvider()
181 && !((Container)comp).isFocusCycleRoot()
182 && !((comp instanceof JComponent)
183 && ((JComponent)comp).isManagingFocus()))
184 {
185 enumerateCycle((Container)comp, cycle);
186 } else {
187 cycle.add(comp);
188 }
189 }
190 }
191
192 Container getTopmostProvider(Container focusCycleRoot, Component aComponent) {
193 Container aCont = aComponent.getParent();
194 Container ftp = null;
195 while (aCont != focusCycleRoot && aCont != null) {
196 if (aCont.isFocusTraversalPolicyProvider()) {
197 ftp = aCont;
198 }
211 * By default, SortingFocusTraversalPolicy implicitly transfers focus down-
212 * cycle. That is, during normal focus traversal, the Component
213 * traversed after a focus cycle root will be the focus-cycle-root's
214 * default Component to focus. This behavior can be disabled using the
215 * <code>setImplicitDownCycleTraversal</code> method.
216 * <p>
217 * If aContainer is <a href="../../java/awt/doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus
218 * traversal policy provider</a>, the focus is always transferred down-cycle.
219 *
220 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider
221 * @param aComponent a (possibly indirect) child of aContainer, or
222 * aContainer itself
223 * @return the Component that should receive the focus after aComponent, or
224 * null if no suitable Component can be found
225 * @throws IllegalArgumentException if aContainer is not a focus cycle
226 * root of aComponent or a focus traversal policy provider, or if either aContainer or
227 * aComponent is null
228 */
229 public Component getComponentAfter(Container aContainer,
230 Component aComponent) {
231 if (log.isLoggable(Level.FINE)) {
232 log.fine("### Searching in " + aContainer.getName() + " for component after " + aComponent.getName());
233 }
234
235 if (aContainer == null || aComponent == null) {
236 throw new IllegalArgumentException("aContainer and aComponent cannot be null");
237 }
238 if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {
239 throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");
240 } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
241 throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
242 }
243
244 // See if the component is inside of policy provider
245 Container ftp = getTopmostProvider(aContainer, aComponent);
246 if (ftp != null) {
247 if (log.isLoggable(Level.FINE)) {
248 log.fine("### Asking FTP " + ftp.getName() + " for component after " + aComponent.getName());
249 }
250 // FTP knows how to find component after the given. We don't.
251 FocusTraversalPolicy policy = ftp.getFocusTraversalPolicy();
252 Component retval = policy.getComponentAfter(ftp, aComponent);
253 if (retval == policy.getFirstComponent(ftp)) {
254 retval = null;
255 }
256
257 if (retval != null) {
258 if (log.isLoggable(Level.FINE)) {
259 log.fine("### FTP returned " + retval.getName());
260 }
261 return retval;
262 }
263 aComponent = ftp;
264 }
265
266 List cycle = new ArrayList();
267 Map defaults = new HashMap();
268 enumerateAndSortCycle(aContainer, cycle, defaults);
269
270 int index;
271 try {
272 index = Collections.binarySearch(cycle, aComponent, comparator);
273 } catch (ClassCastException e) {
274 if (log.isLoggable(Level.FINE)) {
275 log.fine("### Didn't find component " + aComponent.getName() + " in a cycle " + aContainer.getName());
276 }
277 return getFirstComponent(aContainer);
278 }
279
280 if (index < 0) {
281 // Fix for 5070991.
282 // A workaround for a transitivity problem caused by ROW_TOLERANCE,
283 // because of that the component may be missed in the binary search.
284 // Try to search it again directly.
285 int i = cycle.indexOf(aComponent);
286 if (i >= 0) {
287 index = i;
288 } else {
289 // If we're not in the cycle, then binarySearch returns
290 // (-(insertion point) - 1). The next element is our insertion
291 // point.
292
293 index = -index - 2;
294 }
295 }
296
346 * @return the Component that should receive the focus before aComponent,
347 * or null if no suitable Component can be found
348 * @throws IllegalArgumentException if aContainer is not a focus cycle
349 * root of aComponent or a focus traversal policy provider, or if either aContainer or
350 * aComponent is null
351 */
352 public Component getComponentBefore(Container aContainer,
353 Component aComponent) {
354 if (aContainer == null || aComponent == null) {
355 throw new IllegalArgumentException("aContainer and aComponent cannot be null");
356 }
357 if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {
358 throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");
359 } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
360 throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
361 }
362
363 // See if the component is inside of policy provider
364 Container ftp = getTopmostProvider(aContainer, aComponent);
365 if (ftp != null) {
366 if (log.isLoggable(Level.FINE)) {
367 log.fine("### Asking FTP " + ftp.getName() + " for component after " + aComponent.getName());
368 }
369 // FTP knows how to find component after the given. We don't.
370 FocusTraversalPolicy policy = ftp.getFocusTraversalPolicy();
371 Component retval = policy.getComponentBefore(ftp, aComponent);
372 if (retval == policy.getLastComponent(ftp)) {
373 retval = null;
374 }
375 if (retval != null) {
376 if (log.isLoggable(Level.FINE)) {
377 log.fine("### FTP returned " + retval.getName());
378 }
379 return retval;
380 }
381 aComponent = ftp;
382 }
383
384
385 List cycle = new ArrayList();
386 Map defaults = new HashMap();
387 enumerateAndSortCycle(aContainer, cycle, defaults);
388
389 if (log.isLoggable(Level.FINE)) {
390 log.fine("### Cycle is " + cycle + ", component is " + aComponent);
391 }
392
393 int index;
394 try {
395 index = Collections.binarySearch(cycle, aComponent, comparator);
396 } catch (ClassCastException e) {
397 return getLastComponent(aContainer);
398 }
399
400 if (index < 0) {
401 // If we're not in the cycle, then binarySearch returns
402 // (-(insertion point) - 1). The previous element is our insertion
403 // point - 1.
404
405 index = -index - 2;
406 } else {
407 index--;
408 }
409
410 if (log.isLoggable(Level.FINE)) {
411 log.fine("### Index is " + index);
412 }
413
414 if (index >= 0) {
415 Component defComp = (Component)defaults.get(new Integer(index));
416 if (defComp != null && cycle.get(index) != aContainer) {
417 if (log.isLoggable(Level.FINE)) {
418 log.fine("### Returning default " + defComp.getName() + " at " + index);
419 }
420 return defComp;
421 }
422 }
423
424 do {
425 if (index < 0) {
426 this.cachedRoot = aContainer;
427 this.cachedCycle = cycle;
428
429 Component retval = getLastComponent(aContainer);
430
431 this.cachedRoot = null;
432 this.cachedCycle = null;
433
434 return retval;
435 } else {
436 Component comp = (Component)cycle.get(index);
437 if (accept(comp)) {
438 return comp;
439 } else if (comp instanceof Container && ((Container)comp).isFocusTraversalPolicyProvider()) {
440 return ((Container)comp).getFocusTraversalPolicy().getLastComponent((Container)comp);
441 }
442 }
443 index--;
444 } while (true);
445 }
446
447 /**
448 * Returns the first Component in the traversal cycle. This method is used
449 * to determine the next Component to focus when traversal wraps in the
450 * forward direction.
451 *
452 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose
453 * first Component is to be returned
454 * @return the first Component in the traversal cycle of aContainer,
455 * or null if no suitable Component can be found
456 * @throws IllegalArgumentException if aContainer is null
457 */
458 public Component getFirstComponent(Container aContainer) {
459 List<Component> cycle;
460
461 if (log.isLoggable(Level.FINE)) {
462 log.fine("### Getting first component in " + aContainer.getName());
463 }
464 if (aContainer == null) {
465 throw new IllegalArgumentException("aContainer cannot be null");
466 }
467
468 if (this.cachedRoot == aContainer) {
469 cycle = this.cachedCycle;
470 } else {
471 cycle = new ArrayList<Component>();
472 enumerateAndSortCycle(aContainer, cycle, null);
473 }
474
475 int size = cycle.size();
476 if (size == 0) {
477 return null;
478 }
479
480 for (Component comp : cycle) {
481 if (accept(comp)) {
482 return comp;
483 } else if (comp instanceof Container && !(comp == aContainer) && ((Container)comp).isFocusTraversalPolicyProvider()) {
484 return ((Container)comp).getFocusTraversalPolicy().getDefaultComponent((Container)comp);
485 }
486 }
487 return null;
488 }
489
490 /**
491 * Returns the last Component in the traversal cycle. This method is used
492 * to determine the next Component to focus when traversal wraps in the
493 * reverse direction.
494 *
495 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose
496 * last Component is to be returned
497 * @return the last Component in the traversal cycle of aContainer,
498 * or null if no suitable Component can be found
499 * @throws IllegalArgumentException if aContainer is null
500 */
501 public Component getLastComponent(Container aContainer) {
502 List<Component> cycle;
503 if (log.isLoggable(Level.FINE)) {
504 log.fine("### Getting last component in " + aContainer.getName());
505 }
506
507 if (aContainer == null) {
508 throw new IllegalArgumentException("aContainer cannot be null");
509 }
510
511 if (this.cachedRoot == aContainer) {
512 cycle = this.cachedCycle;
513 } else {
514 cycle = new ArrayList<Component>();
515 enumerateAndSortCycle(aContainer, cycle, null);
516 }
517
518 int size = cycle.size();
519 if (size == 0) {
520 if (log.isLoggable(Level.FINE)) {
521 log.fine("### Cycle is empty");
522 }
523 return null;
524 }
525 if (log.isLoggable(Level.FINE)) {
526 log.fine("### Cycle is " + cycle);
527 }
528
529 for (int i= cycle.size()-1; i >= 0; i--) {
530 Component comp = (Component)cycle.get(i);
531 if (accept(comp)) {
532 return comp;
533 } else if (comp instanceof Container && !(comp == aContainer) && ((Container)comp).isFocusTraversalPolicyProvider()) {
534 return ((Container)comp).getFocusTraversalPolicy().getLastComponent((Container)comp);
535 }
536 }
537 return null;
538 }
539
540 /**
541 * Returns the default Component to focus. This Component will be the first
542 * to receive focus when traversing down into a new focus traversal cycle
543 * rooted at aContainer. The default implementation of this method
544 * returns the same Component as <code>getFirstComponent</code>.
545 *
546 * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose
547 * default Component is to be returned
|