Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/java/awt/ContainerOrderFocusTraversalPolicy.java
41152 views
1
/*
2
* Copyright (c) 2000, 2021, 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
26
package java.awt;
27
28
import java.io.Serial;
29
import java.util.ArrayList;
30
import java.util.List;
31
32
import sun.util.logging.PlatformLogger;
33
34
/**
35
* A FocusTraversalPolicy that determines traversal order based on the order
36
* of child Components in a Container. From a particular focus cycle root, the
37
* policy makes a pre-order traversal of the Component hierarchy, and traverses
38
* a Container's children according to the ordering of the array returned by
39
* {@code Container.getComponents()}. Portions of the hierarchy that are
40
* not visible and displayable will not be searched.
41
* <p>
42
* By default, ContainerOrderFocusTraversalPolicy implicitly transfers focus
43
* down-cycle. That is, during normal forward focus traversal, the Component
44
* traversed after a focus cycle root will be the focus-cycle-root's default
45
* Component to focus. This behavior can be disabled using the
46
* {@code setImplicitDownCycleTraversal} method.
47
* <p>
48
* By default, methods of this class will return a Component only if it is
49
* visible, displayable, enabled, and focusable. Subclasses can modify this
50
* behavior by overriding the {@code accept} method.
51
* <p>
52
* This policy takes into account <a
53
* href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal
54
* policy providers</a>. When searching for first/last/next/previous Component,
55
* if a focus traversal policy provider is encountered, its focus traversal
56
* policy is used to perform the search operation.
57
*
58
* @author David Mendenhall
59
*
60
* @see Container#getComponents
61
* @since 1.4
62
*/
63
public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy
64
implements java.io.Serializable
65
{
66
private static final PlatformLogger log = PlatformLogger.getLogger("java.awt.ContainerOrderFocusTraversalPolicy");
67
68
/**
69
* This constant is used when the forward focus traversal order is active.
70
*/
71
private final int FORWARD_TRAVERSAL = 0;
72
73
/**
74
* This constant is used when the backward focus traversal order is active.
75
*/
76
private final int BACKWARD_TRAVERSAL = 1;
77
78
/**
79
* Use serialVersionUID from JDK 1.4 for interoperability.
80
*/
81
@Serial
82
private static final long serialVersionUID = 486933713763926351L;
83
84
/**
85
* Whether this {@code ContainerOrderFocusTraversalPolicy} transfers focus
86
* down-cycle implicitly.
87
*/
88
private boolean implicitDownCycleTraversal = true;
89
90
/**
91
* Used by getComponentAfter and getComponentBefore for efficiency. In
92
* order to maintain compliance with the specification of
93
* FocusTraversalPolicy, if traversal wraps, we should invoke
94
* getFirstComponent or getLastComponent. These methods may be overriden in
95
* subclasses to behave in a non-generic way. However, in the generic case,
96
* these methods will simply return the first or last Components of the
97
* sorted list, respectively. Since getComponentAfter and
98
* getComponentBefore have already built the list before determining
99
* that they need to invoke getFirstComponent or getLastComponent, the
100
* list should be reused if possible.
101
*/
102
private transient Container cachedRoot;
103
private transient List<Component> cachedCycle;
104
105
/**
106
* Constructs a {@code ContainerOrderFocusTraversalPolicy}.
107
*/
108
public ContainerOrderFocusTraversalPolicy() {}
109
110
/*
111
* We suppose to use getFocusTraversalCycle & getComponentIndex methods in order
112
* to divide the policy into two parts:
113
* 1) Making the focus traversal cycle.
114
* 2) Traversing the cycle.
115
* The 1st point assumes producing a list of components representing the focus
116
* traversal cycle. The two methods mentioned above should implement this logic.
117
* The 2nd point assumes implementing the common concepts of operating on the
118
* cycle: traversing back and forth, retrieving the initial/default/first/last
119
* component. These concepts are described in the AWT Focus Spec and they are
120
* applied to the FocusTraversalPolicy in general.
121
* Thus, a descendant of this policy may wish to not reimplement the logic of
122
* the 2nd point but just override the implementation of the 1st one.
123
* A striking example of such a descendant is the javax.swing.SortingFocusTraversalPolicy.
124
*/
125
/*protected*/ private List<Component> getFocusTraversalCycle(Container aContainer) {
126
List<Component> cycle = new ArrayList<Component>();
127
enumerateCycle(aContainer, cycle);
128
return cycle;
129
}
130
/*protected*/ private int getComponentIndex(List<Component> cycle, Component aComponent) {
131
return cycle.indexOf(aComponent);
132
}
133
134
private void enumerateCycle(Container container, List<Component> cycle) {
135
if (!(container.isVisible() && container.isDisplayable())) {
136
return;
137
}
138
139
cycle.add(container);
140
141
Component[] components = container.getComponents();
142
for (int i = 0; i < components.length; i++) {
143
Component comp = components[i];
144
if (comp instanceof Container) {
145
Container cont = (Container)comp;
146
147
if (!cont.isFocusCycleRoot() && !cont.isFocusTraversalPolicyProvider()) {
148
enumerateCycle(cont, cycle);
149
continue;
150
}
151
}
152
cycle.add(comp);
153
}
154
}
155
156
private Container getTopmostProvider(Container focusCycleRoot, Component aComponent) {
157
Container aCont = aComponent.getParent();
158
Container ftp = null;
159
while (aCont != focusCycleRoot && aCont != null) {
160
if (aCont.isFocusTraversalPolicyProvider()) {
161
ftp = aCont;
162
}
163
aCont = aCont.getParent();
164
}
165
if (aCont == null) {
166
return null;
167
}
168
return ftp;
169
}
170
171
/*
172
* Checks if a new focus cycle takes place and returns a Component to traverse focus to.
173
* @param comp a possible focus cycle root or policy provider
174
* @param traversalDirection the direction of the traversal
175
* @return a Component to traverse focus to if {@code comp} is a root or provider
176
* and implicit down-cycle is set, otherwise {@code null}
177
*/
178
private Component getComponentDownCycle(Component comp, int traversalDirection) {
179
Component retComp = null;
180
181
if (comp instanceof Container) {
182
Container cont = (Container)comp;
183
184
if (cont.isFocusCycleRoot()) {
185
if (getImplicitDownCycleTraversal()) {
186
retComp = cont.getFocusTraversalPolicy().getDefaultComponent(cont);
187
188
if (retComp != null && log.isLoggable(PlatformLogger.Level.FINE)) {
189
log.fine("### Transferred focus down-cycle to " + retComp +
190
" in the focus cycle root " + cont);
191
}
192
} else {
193
return null;
194
}
195
} else if (cont.isFocusTraversalPolicyProvider()) {
196
retComp = (traversalDirection == FORWARD_TRAVERSAL ?
197
cont.getFocusTraversalPolicy().getDefaultComponent(cont) :
198
cont.getFocusTraversalPolicy().getLastComponent(cont));
199
200
if (retComp != null && log.isLoggable(PlatformLogger.Level.FINE)) {
201
log.fine("### Transferred focus to " + retComp + " in the FTP provider " + cont);
202
}
203
}
204
}
205
return retComp;
206
}
207
208
/**
209
* Returns the Component that should receive the focus after aComponent.
210
* aContainer must be a focus cycle root of aComponent or a focus traversal policy provider.
211
* <p>
212
* By default, ContainerOrderFocusTraversalPolicy implicitly transfers
213
* focus down-cycle. That is, during normal forward focus traversal, the
214
* Component traversed after a focus cycle root will be the focus-cycle-
215
* root's default Component to focus. This behavior can be disabled using
216
* the {@code setImplicitDownCycleTraversal} method.
217
* <p>
218
* If aContainer is <a href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus
219
* traversal policy provider</a>, the focus is always transferred down-cycle.
220
*
221
* @param aContainer a focus cycle root of aComponent or a focus traversal policy provider
222
* @param aComponent a (possibly indirect) child of aContainer, or
223
* aContainer itself
224
* @return the Component that should receive the focus after aComponent, or
225
* null if no suitable Component can be found
226
* @throws IllegalArgumentException if aContainer is not a focus cycle
227
* root of aComponent or focus traversal policy provider, or if either aContainer or
228
* aComponent is null
229
*/
230
public Component getComponentAfter(Container aContainer, Component aComponent) {
231
if (log.isLoggable(PlatformLogger.Level.FINE)) {
232
log.fine("### Searching in " + aContainer + " for component after " + aComponent);
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
241
} else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
242
throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
243
}
244
245
synchronized(aContainer.getTreeLock()) {
246
247
if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
248
return null;
249
}
250
251
// Before all the checks below we first see if it's an FTP provider or a focus cycle root.
252
// If it's the case just go down cycle (if it's set to "implicit").
253
Component comp = getComponentDownCycle(aComponent, FORWARD_TRAVERSAL);
254
// Check if aComponent is focus-cycle-root's default Component, i.e.
255
// focus cycle root & focus-cycle-root's default Component is same.
256
if (comp != null && comp != aComponent) {
257
return comp;
258
}
259
260
// See if the component is inside of policy provider.
261
Container provider = getTopmostProvider(aContainer, aComponent);
262
if (provider != null) {
263
if (log.isLoggable(PlatformLogger.Level.FINE)) {
264
log.fine("### Asking FTP " + provider + " for component after " + aComponent);
265
}
266
267
// FTP knows how to find component after the given. We don't.
268
FocusTraversalPolicy policy = provider.getFocusTraversalPolicy();
269
Component afterComp = policy.getComponentAfter(provider, aComponent);
270
271
// Null result means that we overstepped the limit of the FTP's cycle.
272
// In that case we must quit the cycle, otherwise return the component found.
273
if (afterComp != null) {
274
if (log.isLoggable(PlatformLogger.Level.FINE)) {
275
log.fine("### FTP returned " + afterComp);
276
}
277
return afterComp;
278
}
279
aComponent = provider;
280
}
281
282
List<Component> cycle = getFocusTraversalCycle(aContainer);
283
284
if (log.isLoggable(PlatformLogger.Level.FINE)) {
285
log.fine("### Cycle is " + cycle + ", component is " + aComponent);
286
}
287
288
int index = getComponentIndex(cycle, aComponent);
289
290
if (index < 0) {
291
if (log.isLoggable(PlatformLogger.Level.FINE)) {
292
log.fine("### Didn't find component " + aComponent + " in a cycle " + aContainer);
293
}
294
return getFirstComponent(aContainer);
295
}
296
297
for (index++; index < cycle.size(); index++) {
298
comp = cycle.get(index);
299
if (accept(comp)) {
300
return comp;
301
} else if ((comp = getComponentDownCycle(comp, FORWARD_TRAVERSAL)) != null) {
302
return comp;
303
}
304
}
305
306
if (aContainer.isFocusCycleRoot()) {
307
this.cachedRoot = aContainer;
308
this.cachedCycle = cycle;
309
310
comp = getFirstComponent(aContainer);
311
312
this.cachedRoot = null;
313
this.cachedCycle = null;
314
315
return comp;
316
}
317
}
318
return null;
319
}
320
321
/**
322
* Returns the Component that should receive the focus before aComponent.
323
* aContainer must be a focus cycle root of aComponent or a <a
324
* href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal policy
325
* provider</a>.
326
*
327
* @param aContainer a focus cycle root of aComponent or focus traversal policy provider
328
* @param aComponent a (possibly indirect) child of aContainer, or
329
* aContainer itself
330
* @return the Component that should receive the focus before aComponent,
331
* or null if no suitable Component can be found
332
* @throws IllegalArgumentException if aContainer is not a focus cycle
333
* root of aComponent or focus traversal policy provider, or if either aContainer or
334
* aComponent is null
335
*/
336
public Component getComponentBefore(Container aContainer, Component aComponent) {
337
if (aContainer == null || aComponent == null) {
338
throw new IllegalArgumentException("aContainer and aComponent cannot be null");
339
}
340
if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {
341
throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");
342
343
} else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
344
throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
345
}
346
347
synchronized(aContainer.getTreeLock()) {
348
349
if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
350
return null;
351
}
352
353
// See if the component is inside of policy provider.
354
Container provider = getTopmostProvider(aContainer, aComponent);
355
if (provider != null) {
356
if (log.isLoggable(PlatformLogger.Level.FINE)) {
357
log.fine("### Asking FTP " + provider + " for component after " + aComponent);
358
}
359
360
// FTP knows how to find component after the given. We don't.
361
FocusTraversalPolicy policy = provider.getFocusTraversalPolicy();
362
Component beforeComp = policy.getComponentBefore(provider, aComponent);
363
364
// Null result means that we overstepped the limit of the FTP's cycle.
365
// In that case we must quit the cycle, otherwise return the component found.
366
if (beforeComp != null) {
367
if (log.isLoggable(PlatformLogger.Level.FINE)) {
368
log.fine("### FTP returned " + beforeComp);
369
}
370
return beforeComp;
371
}
372
aComponent = provider;
373
374
// If the provider is traversable it's returned.
375
if (accept(aComponent)) {
376
return aComponent;
377
}
378
}
379
380
List<Component> cycle = getFocusTraversalCycle(aContainer);
381
382
if (log.isLoggable(PlatformLogger.Level.FINE)) {
383
log.fine("### Cycle is " + cycle + ", component is " + aComponent);
384
}
385
386
int index = getComponentIndex(cycle, aComponent);
387
388
if (index < 0) {
389
if (log.isLoggable(PlatformLogger.Level.FINE)) {
390
log.fine("### Didn't find component " + aComponent + " in a cycle " + aContainer);
391
}
392
return getLastComponent(aContainer);
393
}
394
395
Component comp = null;
396
Component tryComp = null;
397
398
for (index--; index>=0; index--) {
399
comp = cycle.get(index);
400
if (comp != aContainer && (tryComp = getComponentDownCycle(comp, BACKWARD_TRAVERSAL)) != null) {
401
return tryComp;
402
} else if (accept(comp)) {
403
return comp;
404
}
405
}
406
407
if (aContainer.isFocusCycleRoot()) {
408
this.cachedRoot = aContainer;
409
this.cachedCycle = cycle;
410
411
comp = getLastComponent(aContainer);
412
413
this.cachedRoot = null;
414
this.cachedCycle = null;
415
416
return comp;
417
}
418
}
419
return null;
420
}
421
422
/**
423
* Returns the first Component in the traversal cycle. This method is used
424
* to determine the next Component to focus when traversal wraps in the
425
* forward direction.
426
*
427
* @param aContainer the focus cycle root or focus traversal policy provider whose first
428
* Component is to be returned
429
* @return the first Component in the traversal cycle of aContainer,
430
* or null if no suitable Component can be found
431
* @throws IllegalArgumentException if aContainer is null
432
*/
433
public Component getFirstComponent(Container aContainer) {
434
List<Component> cycle;
435
436
if (log.isLoggable(PlatformLogger.Level.FINE)) {
437
log.fine("### Getting first component in " + aContainer);
438
}
439
if (aContainer == null) {
440
throw new IllegalArgumentException("aContainer cannot be null");
441
442
}
443
444
synchronized(aContainer.getTreeLock()) {
445
446
if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
447
return null;
448
}
449
450
if (this.cachedRoot == aContainer) {
451
cycle = this.cachedCycle;
452
} else {
453
cycle = getFocusTraversalCycle(aContainer);
454
}
455
456
if (cycle.size() == 0) {
457
if (log.isLoggable(PlatformLogger.Level.FINE)) {
458
log.fine("### Cycle is empty");
459
}
460
return null;
461
}
462
if (log.isLoggable(PlatformLogger.Level.FINE)) {
463
log.fine("### Cycle is " + cycle);
464
}
465
466
for (Component comp : cycle) {
467
if (accept(comp)) {
468
return comp;
469
} else if (comp != aContainer &&
470
(comp = getComponentDownCycle(comp, FORWARD_TRAVERSAL)) != null)
471
{
472
return comp;
473
}
474
}
475
}
476
return null;
477
}
478
479
/**
480
* Returns the last Component in the traversal cycle. This method is used
481
* to determine the next Component to focus when traversal wraps in the
482
* reverse direction.
483
*
484
* @param aContainer the focus cycle root or focus traversal policy provider whose last
485
* Component is to be returned
486
* @return the last Component in the traversal cycle of aContainer,
487
* or null if no suitable Component can be found
488
* @throws IllegalArgumentException if aContainer is null
489
*/
490
public Component getLastComponent(Container aContainer) {
491
List<Component> cycle;
492
if (log.isLoggable(PlatformLogger.Level.FINE)) {
493
log.fine("### Getting last component in " + aContainer);
494
}
495
496
if (aContainer == null) {
497
throw new IllegalArgumentException("aContainer cannot be null");
498
}
499
500
synchronized(aContainer.getTreeLock()) {
501
502
if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
503
return null;
504
}
505
506
if (this.cachedRoot == aContainer) {
507
cycle = this.cachedCycle;
508
} else {
509
cycle = getFocusTraversalCycle(aContainer);
510
}
511
512
if (cycle.size() == 0) {
513
if (log.isLoggable(PlatformLogger.Level.FINE)) {
514
log.fine("### Cycle is empty");
515
}
516
return null;
517
}
518
if (log.isLoggable(PlatformLogger.Level.FINE)) {
519
log.fine("### Cycle is " + cycle);
520
}
521
522
for (int i= cycle.size() - 1; i >= 0; i--) {
523
Component comp = cycle.get(i);
524
if (accept(comp)) {
525
return comp;
526
} else if (comp instanceof Container && comp != aContainer) {
527
Container cont = (Container)comp;
528
if (cont.isFocusTraversalPolicyProvider()) {
529
Component retComp = cont.getFocusTraversalPolicy().getLastComponent(cont);
530
if (retComp != null) {
531
return retComp;
532
}
533
}
534
}
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}.
545
*
546
* @param aContainer the focus cycle root or focus traversal policy provider whose default
547
* Component is to be returned
548
* @return the default Component in the traversal cycle of aContainer,
549
* or null if no suitable Component can be found
550
* @see #getFirstComponent
551
* @throws IllegalArgumentException if aContainer is null
552
*/
553
public Component getDefaultComponent(Container aContainer) {
554
return getFirstComponent(aContainer);
555
}
556
557
/**
558
* Sets whether this ContainerOrderFocusTraversalPolicy transfers focus
559
* down-cycle implicitly. If {@code true}, during normal forward focus
560
* traversal, the Component traversed after a focus cycle root will be the
561
* focus-cycle-root's default Component to focus. If {@code false},
562
* the next Component in the focus traversal cycle rooted at the specified
563
* focus cycle root will be traversed instead. The default value for this
564
* property is {@code true}.
565
*
566
* @param implicitDownCycleTraversal whether this
567
* ContainerOrderFocusTraversalPolicy transfers focus down-cycle
568
* implicitly
569
* @see #getImplicitDownCycleTraversal
570
* @see #getFirstComponent
571
*/
572
public void setImplicitDownCycleTraversal(boolean implicitDownCycleTraversal) {
573
this.implicitDownCycleTraversal = implicitDownCycleTraversal;
574
}
575
576
/**
577
* Returns whether this ContainerOrderFocusTraversalPolicy transfers focus
578
* down-cycle implicitly. If {@code true}, during normal forward focus
579
* traversal, the Component traversed after a focus cycle root will be the
580
* focus-cycle-root's default Component to focus. If {@code false},
581
* the next Component in the focus traversal cycle rooted at the specified
582
* focus cycle root will be traversed instead.
583
*
584
* @return whether this ContainerOrderFocusTraversalPolicy transfers focus
585
* down-cycle implicitly
586
* @see #setImplicitDownCycleTraversal
587
* @see #getFirstComponent
588
*/
589
public boolean getImplicitDownCycleTraversal() {
590
return implicitDownCycleTraversal;
591
}
592
593
/**
594
* Determines whether a Component is an acceptable choice as the new
595
* focus owner. By default, this method will accept a Component if and
596
* only if it is visible, displayable, enabled, and focusable.
597
*
598
* @param aComponent the Component whose fitness as a focus owner is to
599
* be tested
600
* @return {@code true} if aComponent is visible, displayable,
601
* enabled, and focusable; {@code false} otherwise
602
*/
603
protected boolean accept(Component aComponent) {
604
if (!aComponent.canBeFocusOwner()) {
605
return false;
606
}
607
608
// Verify that the Component is recursively enabled. Disabling a
609
// heavyweight Container disables its children, whereas disabling
610
// a lightweight Container does not.
611
if (!(aComponent instanceof Window)) {
612
for (Container enableTest = aComponent.getParent();
613
enableTest != null;
614
enableTest = enableTest.getParent())
615
{
616
if (!(enableTest.isEnabled() || enableTest.isLightweight())) {
617
return false;
618
}
619
if (enableTest instanceof Window) {
620
break;
621
}
622
}
623
}
624
625
return true;
626
}
627
}
628
629