Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/awt/ContainerOrderFocusTraversalPolicy.java
38829 views
/*1* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package java.awt;2526import java.util.List;27import java.util.ArrayList;28import sun.util.logging.PlatformLogger;2930/**31* A FocusTraversalPolicy that determines traversal order based on the order32* of child Components in a Container. From a particular focus cycle root, the33* policy makes a pre-order traversal of the Component hierarchy, and traverses34* a Container's children according to the ordering of the array returned by35* <code>Container.getComponents()</code>. Portions of the hierarchy that are36* not visible and displayable will not be searched.37* <p>38* By default, ContainerOrderFocusTraversalPolicy implicitly transfers focus39* down-cycle. That is, during normal forward focus traversal, the Component40* traversed after a focus cycle root will be the focus-cycle-root's default41* Component to focus. This behavior can be disabled using the42* <code>setImplicitDownCycleTraversal</code> method.43* <p>44* By default, methods of this class will return a Component only if it is45* visible, displayable, enabled, and focusable. Subclasses can modify this46* behavior by overriding the <code>accept</code> method.47* <p>48* This policy takes into account <a49* href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal50* policy providers</a>. When searching for first/last/next/previous Component,51* if a focus traversal policy provider is encountered, its focus traversal52* policy is used to perform the search operation.53*54* @author David Mendenhall55*56* @see Container#getComponents57* @since 1.458*/59public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy60implements java.io.Serializable61{62private static final PlatformLogger log = PlatformLogger.getLogger("java.awt.ContainerOrderFocusTraversalPolicy");6364final private int FORWARD_TRAVERSAL = 0;65final private int BACKWARD_TRAVERSAL = 1;6667/*68* JDK 1.4 serialVersionUID69*/70private static final long serialVersionUID = 486933713763926351L;7172private boolean implicitDownCycleTraversal = true;7374/**75* Used by getComponentAfter and getComponentBefore for efficiency. In76* order to maintain compliance with the specification of77* FocusTraversalPolicy, if traversal wraps, we should invoke78* getFirstComponent or getLastComponent. These methods may be overriden in79* subclasses to behave in a non-generic way. However, in the generic case,80* these methods will simply return the first or last Components of the81* sorted list, respectively. Since getComponentAfter and82* getComponentBefore have already built the list before determining83* that they need to invoke getFirstComponent or getLastComponent, the84* list should be reused if possible.85*/86transient private Container cachedRoot;87transient private List<Component> cachedCycle;8889/*90* We suppose to use getFocusTraversalCycle & getComponentIndex methods in order91* to divide the policy into two parts:92* 1) Making the focus traversal cycle.93* 2) Traversing the cycle.94* The 1st point assumes producing a list of components representing the focus95* traversal cycle. The two methods mentioned above should implement this logic.96* The 2nd point assumes implementing the common concepts of operating on the97* cycle: traversing back and forth, retrieving the initial/default/first/last98* component. These concepts are described in the AWT Focus Spec and they are99* applied to the FocusTraversalPolicy in general.100* Thus, a descendant of this policy may wish to not reimplement the logic of101* the 2nd point but just override the implementation of the 1st one.102* A striking example of such a descendant is the javax.swing.SortingFocusTraversalPolicy.103*/104/*protected*/ private List<Component> getFocusTraversalCycle(Container aContainer) {105List<Component> cycle = new ArrayList<Component>();106enumerateCycle(aContainer, cycle);107return cycle;108}109/*protected*/ private int getComponentIndex(List<Component> cycle, Component aComponent) {110return cycle.indexOf(aComponent);111}112113private void enumerateCycle(Container container, List<Component> cycle) {114if (!(container.isVisible() && container.isDisplayable())) {115return;116}117118cycle.add(container);119120Component[] components = container.getComponents();121for (int i = 0; i < components.length; i++) {122Component comp = components[i];123if (comp instanceof Container) {124Container cont = (Container)comp;125126if (!cont.isFocusCycleRoot() && !cont.isFocusTraversalPolicyProvider()) {127enumerateCycle(cont, cycle);128continue;129}130}131cycle.add(comp);132}133}134135private Container getTopmostProvider(Container focusCycleRoot, Component aComponent) {136Container aCont = aComponent.getParent();137Container ftp = null;138while (aCont != focusCycleRoot && aCont != null) {139if (aCont.isFocusTraversalPolicyProvider()) {140ftp = aCont;141}142aCont = aCont.getParent();143}144if (aCont == null) {145return null;146}147return ftp;148}149150/*151* Checks if a new focus cycle takes place and returns a Component to traverse focus to.152* @param comp a possible focus cycle root or policy provider153* @param traversalDirection the direction of the traversal154* @return a Component to traverse focus to if {@code comp} is a root or provider155* and implicit down-cycle is set, otherwise {@code null}156*/157private Component getComponentDownCycle(Component comp, int traversalDirection) {158Component retComp = null;159160if (comp instanceof Container) {161Container cont = (Container)comp;162163if (cont.isFocusCycleRoot()) {164if (getImplicitDownCycleTraversal()) {165retComp = cont.getFocusTraversalPolicy().getDefaultComponent(cont);166167if (retComp != null && log.isLoggable(PlatformLogger.Level.FINE)) {168log.fine("### Transfered focus down-cycle to " + retComp +169" in the focus cycle root " + cont);170}171} else {172return null;173}174} else if (cont.isFocusTraversalPolicyProvider()) {175retComp = (traversalDirection == FORWARD_TRAVERSAL ?176cont.getFocusTraversalPolicy().getDefaultComponent(cont) :177cont.getFocusTraversalPolicy().getLastComponent(cont));178179if (retComp != null && log.isLoggable(PlatformLogger.Level.FINE)) {180log.fine("### Transfered focus to " + retComp + " in the FTP provider " + cont);181}182}183}184return retComp;185}186187/**188* Returns the Component that should receive the focus after aComponent.189* aContainer must be a focus cycle root of aComponent or a focus traversal policy provider.190* <p>191* By default, ContainerOrderFocusTraversalPolicy implicitly transfers192* focus down-cycle. That is, during normal forward focus traversal, the193* Component traversed after a focus cycle root will be the focus-cycle-194* root's default Component to focus. This behavior can be disabled using195* the <code>setImplicitDownCycleTraversal</code> method.196* <p>197* If aContainer is <a href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus198* traversal policy provider</a>, the focus is always transferred down-cycle.199*200* @param aContainer a focus cycle root of aComponent or a focus traversal policy provider201* @param aComponent a (possibly indirect) child of aContainer, or202* aContainer itself203* @return the Component that should receive the focus after aComponent, or204* null if no suitable Component can be found205* @throws IllegalArgumentException if aContainer is not a focus cycle206* root of aComponent or focus traversal policy provider, or if either aContainer or207* aComponent is null208*/209public Component getComponentAfter(Container aContainer, Component aComponent) {210if (log.isLoggable(PlatformLogger.Level.FINE)) {211log.fine("### Searching in " + aContainer + " for component after " + aComponent);212}213214if (aContainer == null || aComponent == null) {215throw new IllegalArgumentException("aContainer and aComponent cannot be null");216}217if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {218throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");219220} else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {221throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");222}223224synchronized(aContainer.getTreeLock()) {225226if (!(aContainer.isVisible() && aContainer.isDisplayable())) {227return null;228}229230// Before all the ckecks below we first see if it's an FTP provider or a focus cycle root.231// If it's the case just go down cycle (if it's set to "implicit").232Component comp = getComponentDownCycle(aComponent, FORWARD_TRAVERSAL);233if (comp != null) {234return comp;235}236237// See if the component is inside of policy provider.238Container provider = getTopmostProvider(aContainer, aComponent);239if (provider != null) {240if (log.isLoggable(PlatformLogger.Level.FINE)) {241log.fine("### Asking FTP " + provider + " for component after " + aComponent);242}243244// FTP knows how to find component after the given. We don't.245FocusTraversalPolicy policy = provider.getFocusTraversalPolicy();246Component afterComp = policy.getComponentAfter(provider, aComponent);247248// Null result means that we overstepped the limit of the FTP's cycle.249// In that case we must quit the cycle, otherwise return the component found.250if (afterComp != null) {251if (log.isLoggable(PlatformLogger.Level.FINE)) {252log.fine("### FTP returned " + afterComp);253}254return afterComp;255}256aComponent = provider;257}258259List<Component> cycle = getFocusTraversalCycle(aContainer);260261if (log.isLoggable(PlatformLogger.Level.FINE)) {262log.fine("### Cycle is " + cycle + ", component is " + aComponent);263}264265int index = getComponentIndex(cycle, aComponent);266267if (index < 0) {268if (log.isLoggable(PlatformLogger.Level.FINE)) {269log.fine("### Didn't find component " + aComponent + " in a cycle " + aContainer);270}271return getFirstComponent(aContainer);272}273274for (index++; index < cycle.size(); index++) {275comp = cycle.get(index);276if (accept(comp)) {277return comp;278} else if ((comp = getComponentDownCycle(comp, FORWARD_TRAVERSAL)) != null) {279return comp;280}281}282283if (aContainer.isFocusCycleRoot()) {284this.cachedRoot = aContainer;285this.cachedCycle = cycle;286287comp = getFirstComponent(aContainer);288289this.cachedRoot = null;290this.cachedCycle = null;291292return comp;293}294}295return null;296}297298/**299* Returns the Component that should receive the focus before aComponent.300* aContainer must be a focus cycle root of aComponent or a <a301* href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal policy302* provider</a>.303*304* @param aContainer a focus cycle root of aComponent or focus traversal policy provider305* @param aComponent a (possibly indirect) child of aContainer, or306* aContainer itself307* @return the Component that should receive the focus before aComponent,308* or null if no suitable Component can be found309* @throws IllegalArgumentException if aContainer is not a focus cycle310* root of aComponent or focus traversal policy provider, or if either aContainer or311* aComponent is null312*/313public Component getComponentBefore(Container aContainer, Component aComponent) {314if (aContainer == null || aComponent == null) {315throw new IllegalArgumentException("aContainer and aComponent cannot be null");316}317if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {318throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");319320} else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {321throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");322}323324synchronized(aContainer.getTreeLock()) {325326if (!(aContainer.isVisible() && aContainer.isDisplayable())) {327return null;328}329330// See if the component is inside of policy provider.331Container provider = getTopmostProvider(aContainer, aComponent);332if (provider != null) {333if (log.isLoggable(PlatformLogger.Level.FINE)) {334log.fine("### Asking FTP " + provider + " for component after " + aComponent);335}336337// FTP knows how to find component after the given. We don't.338FocusTraversalPolicy policy = provider.getFocusTraversalPolicy();339Component beforeComp = policy.getComponentBefore(provider, aComponent);340341// Null result means that we overstepped the limit of the FTP's cycle.342// In that case we must quit the cycle, otherwise return the component found.343if (beforeComp != null) {344if (log.isLoggable(PlatformLogger.Level.FINE)) {345log.fine("### FTP returned " + beforeComp);346}347return beforeComp;348}349aComponent = provider;350351// If the provider is traversable it's returned.352if (accept(aComponent)) {353return aComponent;354}355}356357List<Component> cycle = getFocusTraversalCycle(aContainer);358359if (log.isLoggable(PlatformLogger.Level.FINE)) {360log.fine("### Cycle is " + cycle + ", component is " + aComponent);361}362363int index = getComponentIndex(cycle, aComponent);364365if (index < 0) {366if (log.isLoggable(PlatformLogger.Level.FINE)) {367log.fine("### Didn't find component " + aComponent + " in a cycle " + aContainer);368}369return getLastComponent(aContainer);370}371372Component comp = null;373Component tryComp = null;374375for (index--; index>=0; index--) {376comp = cycle.get(index);377if (comp != aContainer && (tryComp = getComponentDownCycle(comp, BACKWARD_TRAVERSAL)) != null) {378return tryComp;379} else if (accept(comp)) {380return comp;381}382}383384if (aContainer.isFocusCycleRoot()) {385this.cachedRoot = aContainer;386this.cachedCycle = cycle;387388comp = getLastComponent(aContainer);389390this.cachedRoot = null;391this.cachedCycle = null;392393return comp;394}395}396return null;397}398399/**400* Returns the first Component in the traversal cycle. This method is used401* to determine the next Component to focus when traversal wraps in the402* forward direction.403*404* @param aContainer the focus cycle root or focus traversal policy provider whose first405* Component is to be returned406* @return the first Component in the traversal cycle of aContainer,407* or null if no suitable Component can be found408* @throws IllegalArgumentException if aContainer is null409*/410public Component getFirstComponent(Container aContainer) {411List<Component> cycle;412413if (log.isLoggable(PlatformLogger.Level.FINE)) {414log.fine("### Getting first component in " + aContainer);415}416if (aContainer == null) {417throw new IllegalArgumentException("aContainer cannot be null");418419}420421synchronized(aContainer.getTreeLock()) {422423if (!(aContainer.isVisible() && aContainer.isDisplayable())) {424return null;425}426427if (this.cachedRoot == aContainer) {428cycle = this.cachedCycle;429} else {430cycle = getFocusTraversalCycle(aContainer);431}432433if (cycle.size() == 0) {434if (log.isLoggable(PlatformLogger.Level.FINE)) {435log.fine("### Cycle is empty");436}437return null;438}439if (log.isLoggable(PlatformLogger.Level.FINE)) {440log.fine("### Cycle is " + cycle);441}442443for (Component comp : cycle) {444if (accept(comp)) {445return comp;446} else if (comp != aContainer &&447(comp = getComponentDownCycle(comp, FORWARD_TRAVERSAL)) != null)448{449return comp;450}451}452}453return null;454}455456/**457* Returns the last Component in the traversal cycle. This method is used458* to determine the next Component to focus when traversal wraps in the459* reverse direction.460*461* @param aContainer the focus cycle root or focus traversal policy provider whose last462* Component is to be returned463* @return the last Component in the traversal cycle of aContainer,464* or null if no suitable Component can be found465* @throws IllegalArgumentException if aContainer is null466*/467public Component getLastComponent(Container aContainer) {468List<Component> cycle;469if (log.isLoggable(PlatformLogger.Level.FINE)) {470log.fine("### Getting last component in " + aContainer);471}472473if (aContainer == null) {474throw new IllegalArgumentException("aContainer cannot be null");475}476477synchronized(aContainer.getTreeLock()) {478479if (!(aContainer.isVisible() && aContainer.isDisplayable())) {480return null;481}482483if (this.cachedRoot == aContainer) {484cycle = this.cachedCycle;485} else {486cycle = getFocusTraversalCycle(aContainer);487}488489if (cycle.size() == 0) {490if (log.isLoggable(PlatformLogger.Level.FINE)) {491log.fine("### Cycle is empty");492}493return null;494}495if (log.isLoggable(PlatformLogger.Level.FINE)) {496log.fine("### Cycle is " + cycle);497}498499for (int i= cycle.size() - 1; i >= 0; i--) {500Component comp = cycle.get(i);501if (accept(comp)) {502return comp;503} else if (comp instanceof Container && comp != aContainer) {504Container cont = (Container)comp;505if (cont.isFocusTraversalPolicyProvider()) {506Component retComp = cont.getFocusTraversalPolicy().getLastComponent(cont);507if (retComp != null) {508return retComp;509}510}511}512}513}514return null;515}516517/**518* Returns the default Component to focus. This Component will be the first519* to receive focus when traversing down into a new focus traversal cycle520* rooted at aContainer. The default implementation of this method521* returns the same Component as <code>getFirstComponent</code>.522*523* @param aContainer the focus cycle root or focus traversal policy provider whose default524* Component is to be returned525* @return the default Component in the traversal cycle of aContainer,526* or null if no suitable Component can be found527* @see #getFirstComponent528* @throws IllegalArgumentException if aContainer is null529*/530public Component getDefaultComponent(Container aContainer) {531return getFirstComponent(aContainer);532}533534/**535* Sets whether this ContainerOrderFocusTraversalPolicy transfers focus536* down-cycle implicitly. If <code>true</code>, during normal forward focus537* traversal, the Component traversed after a focus cycle root will be the538* focus-cycle-root's default Component to focus. If <code>false</code>,539* the next Component in the focus traversal cycle rooted at the specified540* focus cycle root will be traversed instead. The default value for this541* property is <code>true</code>.542*543* @param implicitDownCycleTraversal whether this544* ContainerOrderFocusTraversalPolicy transfers focus down-cycle545* implicitly546* @see #getImplicitDownCycleTraversal547* @see #getFirstComponent548*/549public void setImplicitDownCycleTraversal(boolean implicitDownCycleTraversal) {550this.implicitDownCycleTraversal = implicitDownCycleTraversal;551}552553/**554* Returns whether this ContainerOrderFocusTraversalPolicy transfers focus555* down-cycle implicitly. If <code>true</code>, during normal forward focus556* traversal, the Component traversed after a focus cycle root will be the557* focus-cycle-root's default Component to focus. If <code>false</code>,558* the next Component in the focus traversal cycle rooted at the specified559* focus cycle root will be traversed instead.560*561* @return whether this ContainerOrderFocusTraversalPolicy transfers focus562* down-cycle implicitly563* @see #setImplicitDownCycleTraversal564* @see #getFirstComponent565*/566public boolean getImplicitDownCycleTraversal() {567return implicitDownCycleTraversal;568}569570/**571* Determines whether a Component is an acceptable choice as the new572* focus owner. By default, this method will accept a Component if and573* only if it is visible, displayable, enabled, and focusable.574*575* @param aComponent the Component whose fitness as a focus owner is to576* be tested577* @return <code>true</code> if aComponent is visible, displayable,578* enabled, and focusable; <code>false</code> otherwise579*/580protected boolean accept(Component aComponent) {581if (!aComponent.canBeFocusOwner()) {582return false;583}584585// Verify that the Component is recursively enabled. Disabling a586// heavyweight Container disables its children, whereas disabling587// a lightweight Container does not.588if (!(aComponent instanceof Window)) {589for (Container enableTest = aComponent.getParent();590enableTest != null;591enableTest = enableTest.getParent())592{593if (!(enableTest.isEnabled() || enableTest.isLightweight())) {594return false;595}596if (enableTest instanceof Window) {597break;598}599}600}601602return true;603}604}605606607