1. /*
  2. * @(#)ContainerOrderFocusTraversalPolicy.java 1.8 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt;
  8. import java.util.logging.*;
  9. /**
  10. * A FocusTraversalPolicy that determines traversal order based on the order
  11. * of child Components in a Container. From a particular focus cycle root, the
  12. * policy makes a pre-order traversal of the Component hierarchy, and traverses
  13. * a Container's children according to the ordering of the array returned by
  14. * <code>Container.getComponents()</code>. Portions of the hierarchy that are
  15. * not visible and displayable will not be searched.
  16. * <p>
  17. * By default, ContainerOrderFocusTraversalPolicy implicitly transfers focus
  18. * down-cycle. That is, during normal forward focus traversal, the Component
  19. * traversed after a focus cycle root will be the focus-cycle-root's default
  20. * Component to focus. This behavior can be disabled using the
  21. * <code>setImplicitDownCycleTraversal</code> method.
  22. * <p>
  23. * By default, methods of this class with return a Component only if it is
  24. * visible, displayable, enabled, and focusable. Subclasses can modify this
  25. * behavior by overriding the <code>accept</code> method.
  26. * <p>
  27. * This policy takes into account <a
  28. * href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal
  29. * policy providers</a>. When searching for first/last/next/previous Component,
  30. * if a focus traversal policy provider is encountered, its focus traversal
  31. * policy is used to perform the search operation.
  32. *
  33. * @author David Mendenhall
  34. * @version 1.8, 12/19/03
  35. *
  36. * @see Container#getComponents
  37. * @since 1.4
  38. */
  39. public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy
  40. implements java.io.Serializable
  41. {
  42. private static final MutableBoolean found = new MutableBoolean();
  43. private static final Logger log = Logger.getLogger("java.awt.ContainerOrderFocusTraversalPolicy");
  44. /*
  45. * JDK 1.4 serialVersionUID
  46. */
  47. private static final long serialVersionUID = 486933713763926351L;
  48. private boolean implicitDownCycleTraversal = true;
  49. /**
  50. * Returns the Component that should receive the focus after aComponent.
  51. * aContainer must be a focus cycle root of aComponent or a focus traversal policy provider.
  52. * <p>
  53. * By default, ContainerOrderFocusTraversalPolicy implicitly transfers
  54. * focus down-cycle. That is, during normal forward focus traversal, the
  55. * Component traversed after a focus cycle root will be the focus-cycle-
  56. * root's default Component to focus. This behavior can be disabled using
  57. * the <code>setImplicitDownCycleTraversal</code> method.
  58. * <p>
  59. * If aContainer is <a href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus
  60. * traversal policy provider</a>, the focus is always transferred down-cycle.
  61. *
  62. * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider
  63. * @param aComponent a (possibly indirect) child of aContainer, or
  64. * aContainer itself
  65. * @return the Component that should receive the focus after aComponent, or
  66. * null if no suitable Component can be found
  67. * @throws IllegalArgumentException if aContainer is not a focus cycle
  68. * root of aComponent or focus traversal policy provider, or if either aContainer or
  69. * aComponent is null
  70. */
  71. public Component getComponentAfter(Container aContainer,
  72. Component aComponent) {
  73. if (log.isLoggable(Level.FINE)) log.fine("Looking for next component in " + aContainer + " for " + aComponent);
  74. if (aContainer == null || aComponent == null) {
  75. throw new IllegalArgumentException("aContainer and aComponent cannot be null");
  76. }
  77. if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {
  78. throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");
  79. } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
  80. throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
  81. }
  82. synchronized(aContainer.getTreeLock()) {
  83. found.value = false;
  84. Component retval = getComponentAfter(aContainer, aComponent,
  85. found);
  86. if (retval != null) {
  87. if (log.isLoggable(Level.FINE)) log.fine("After component is " + retval);
  88. return retval;
  89. } else if (found.value) {
  90. if (log.isLoggable(Level.FINE)) log.fine("Didn't find next component in " + aContainer + " - falling back to the first ");
  91. return getFirstComponent(aContainer);
  92. } else {
  93. if (log.isLoggable(Level.FINE)) log.fine("After component is null");
  94. return null;
  95. }
  96. }
  97. }
  98. private Component getComponentAfter(Container aContainer,
  99. Component aComponent,
  100. MutableBoolean found) {
  101. if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
  102. return null;
  103. }
  104. if (found.value) {
  105. if (accept(aContainer)) {
  106. return aContainer;
  107. }
  108. } else if (aContainer == aComponent) {
  109. found.value = true;
  110. }
  111. for (int i = 0; i < aContainer.ncomponents; i++) {
  112. Component comp = aContainer.component[i];
  113. if ((comp instanceof Container) &&
  114. !((Container)comp).isFocusCycleRoot()) {
  115. Component retval = null;
  116. if (((Container)comp).isFocusTraversalPolicyProvider()) {
  117. if (log.isLoggable(Level.FINE)) log.fine("Entering FTP " + comp);
  118. Container cont = (Container) comp;
  119. FocusTraversalPolicy policy = cont.getFocusTraversalPolicy();
  120. if (log.isLoggable(Level.FINE)) log.fine("FTP contains " + aComponent + ": " + cont.isAncestorOf(aComponent));
  121. if (found.value) {
  122. retval = policy.getDefaultComponent(cont);
  123. if (log.isLoggable(Level.FINE)) log.fine("Used FTP for getting default component: " + retval);
  124. } else {
  125. found.value = cont.isAncestorOf(aComponent);
  126. if (found.value) {
  127. if (aComponent == policy.getLastComponent(cont)) {
  128. // Reached last component, going to wrap - should switch to next provider
  129. retval = null;
  130. } else {
  131. retval = policy.getComponentAfter(cont, aComponent);
  132. if (log.isLoggable(Level.FINE)) log.fine("FTP found next for the component : " + retval);
  133. }
  134. }
  135. }
  136. } else {
  137. retval = getComponentAfter((Container)comp,
  138. aComponent,
  139. found);
  140. }
  141. if (retval != null) {
  142. return retval;
  143. }
  144. } else if (found.value) {
  145. if (accept(comp)) {
  146. return comp;
  147. }
  148. } else if (comp == aComponent) {
  149. found.value = true;
  150. }
  151. if (found.value &&
  152. getImplicitDownCycleTraversal() &&
  153. (comp instanceof Container) &&
  154. ((Container)comp).isFocusCycleRoot())
  155. {
  156. Container cont = (Container)comp;
  157. Component retval = cont.getFocusTraversalPolicy().
  158. getDefaultComponent(cont);
  159. if (retval != null) {
  160. return retval;
  161. }
  162. }
  163. }
  164. return null;
  165. }
  166. /**
  167. * Returns the Component that should receive the focus before aComponent.
  168. * aContainer must be a focus cycle root of aComponent or a <a
  169. * href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal policy
  170. * provider</a>.
  171. *
  172. * @param aContainer a focus cycle root of aComponent or focus traversal policy provider
  173. * @param aComponent a (possibly indirect) child of aContainer, or
  174. * aContainer itself
  175. * @return the Component that should receive the focus before aComponent,
  176. * or null if no suitable Component can be found
  177. * @throws IllegalArgumentException if aContainer is not a focus cycle
  178. * root of aComponent or focus traversal policy provider, or if either aContainer or
  179. * aComponent is null
  180. */
  181. public Component getComponentBefore(Container aContainer,
  182. Component aComponent) {
  183. if (aContainer == null || aComponent == null) {
  184. throw new IllegalArgumentException("aContainer and aComponent cannot be null");
  185. }
  186. if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {
  187. throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");
  188. } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
  189. throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
  190. }
  191. synchronized(aContainer.getTreeLock()) {
  192. found.value = false;
  193. Component retval = getComponentBefore(aContainer, aComponent,
  194. found);
  195. if (retval != null) {
  196. if (log.isLoggable(Level.FINE)) log.fine("Before component is " + retval);
  197. return retval;
  198. } else if (found.value) {
  199. if (log.isLoggable(Level.FINE)) log.fine("Didn't find before component in " + aContainer + " - falling back to the first ");
  200. return getLastComponent(aContainer);
  201. } else {
  202. if (log.isLoggable(Level.FINE)) log.fine("Before component is null");
  203. return null;
  204. }
  205. }
  206. }
  207. private Component getComponentBefore(Container aContainer,
  208. Component aComponent,
  209. MutableBoolean found) {
  210. if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
  211. return null;
  212. }
  213. for (int i = aContainer.ncomponents - 1; i >= 0; i--) {
  214. Component comp = aContainer.component[i];
  215. if (comp == aComponent) {
  216. found.value = true;
  217. } else if ((comp instanceof Container) &&
  218. !((Container)comp).isFocusCycleRoot()) {
  219. Component retval = null;
  220. if (((Container)comp).isFocusTraversalPolicyProvider()) {
  221. if (log.isLoggable(Level.FINE)) log.fine("Entering FTP " + comp);
  222. Container cont = (Container) comp;
  223. FocusTraversalPolicy policy = cont.getFocusTraversalPolicy();
  224. if (log.isLoggable(Level.FINE)) log.fine("FTP contains " + aComponent + ": " + cont.isAncestorOf(aComponent));
  225. if (found.value) {
  226. retval = policy.getLastComponent(cont);
  227. if (log.isLoggable(Level.FINE)) log.fine("Used FTP for getting last component: " + retval);
  228. } else {
  229. found.value = cont.isAncestorOf(aComponent);
  230. if (found.value) {
  231. if (aComponent == policy.getFirstComponent(cont)) {
  232. retval = null;
  233. } else {
  234. retval = policy.getComponentBefore(cont, aComponent);
  235. if (log.isLoggable(Level.FINE)) log.fine("FTP found previous for the component : " + retval);
  236. }
  237. }
  238. }
  239. } else {
  240. retval = getComponentBefore((Container)comp,
  241. aComponent,
  242. found);
  243. }
  244. if (retval != null) {
  245. return retval;
  246. }
  247. } else if (found.value) {
  248. if (accept(comp)) {
  249. return comp;
  250. }
  251. }
  252. }
  253. if (found.value) {
  254. if (accept(aContainer)) {
  255. return aContainer;
  256. }
  257. } else if (aContainer == aComponent) {
  258. found.value = true;
  259. }
  260. return null;
  261. }
  262. /**
  263. * Returns the first Component in the traversal cycle. This method is used
  264. * to determine the next Component to focus when traversal wraps in the
  265. * forward direction.
  266. *
  267. * @param aContainer the focus cycle root or focus traversal policy provider whose first
  268. * Component is to be returned
  269. * @return the first Component in the traversal cycle of aContainer,
  270. * or null if no suitable Component can be found
  271. * @throws IllegalArgumentException if aContainer is null
  272. */
  273. public Component getFirstComponent(Container aContainer) {
  274. if (aContainer == null) {
  275. throw new IllegalArgumentException("aContainer cannot be null");
  276. }
  277. synchronized(aContainer.getTreeLock()) {
  278. if (!(aContainer.isVisible() &&
  279. aContainer.isDisplayable()))
  280. {
  281. return null;
  282. }
  283. if (accept(aContainer)) {
  284. return aContainer;
  285. }
  286. for (int i = 0; i < aContainer.ncomponents; i++) {
  287. Component comp = aContainer.component[i];
  288. if (comp instanceof Container &&
  289. !((Container)comp).isFocusCycleRoot())
  290. {
  291. Component retval = null;
  292. Container cont = (Container)comp;
  293. if (cont.isFocusTraversalPolicyProvider()) {
  294. FocusTraversalPolicy policy = cont.getFocusTraversalPolicy();
  295. retval = policy.getDefaultComponent(cont);
  296. } else {
  297. retval = getFirstComponent((Container)comp);
  298. }
  299. if (retval != null) {
  300. return retval;
  301. }
  302. } else if (accept(comp)) {
  303. return comp;
  304. }
  305. }
  306. }
  307. return null;
  308. }
  309. /**
  310. * Returns the last Component in the traversal cycle. This method is used
  311. * to determine the next Component to focus when traversal wraps in the
  312. * reverse direction.
  313. *
  314. * @param aContainer the focus cycle root or focus traversal policy provider whose last
  315. * Component is to be returned
  316. * @return the last Component in the traversal cycle of aContainer,
  317. * or null if no suitable Component can be found
  318. * @throws IllegalArgumentException if aContainer is null
  319. */
  320. public Component getLastComponent(Container aContainer) {
  321. if (aContainer == null) {
  322. throw new IllegalArgumentException("aContainer cannot be null");
  323. }
  324. if (log.isLoggable(Level.FINE)) log.fine("Looking for the last component in " + aContainer);
  325. synchronized(aContainer.getTreeLock()) {
  326. if (!(aContainer.isVisible() &&
  327. aContainer.isDisplayable()))
  328. {
  329. return null;
  330. }
  331. for (int i = aContainer.ncomponents - 1; i >= 0; i--) {
  332. Component comp = aContainer.component[i];
  333. if (comp instanceof Container &&
  334. !((Container)comp).isFocusCycleRoot())
  335. {
  336. Component retval = null;
  337. Container cont = (Container)comp;
  338. if (cont.isFocusTraversalPolicyProvider()) {
  339. if (log.isLoggable(Level.FINE)) log.fine("\tEntering FTP " + cont);
  340. FocusTraversalPolicy policy = cont.getFocusTraversalPolicy();
  341. retval = policy.getLastComponent(cont);
  342. } else {
  343. if (log.isLoggable(Level.FINE)) log.fine("\tEntering sub-container");
  344. retval = getLastComponent((Container)comp);
  345. }
  346. if (retval != null) {
  347. if (log.isLoggable(Level.FINE)) log.fine("\tFound last component : " + retval);
  348. return retval;
  349. }
  350. } else if (accept(comp)) {
  351. return comp;
  352. }
  353. }
  354. if (accept(aContainer)) {
  355. return aContainer;
  356. }
  357. }
  358. return null;
  359. }
  360. /**
  361. * Returns the default Component to focus. This Component will be the first
  362. * to receive focus when traversing down into a new focus traversal cycle
  363. * rooted at aContainer. The default implementation of this method
  364. * returns the same Component as <code>getFirstComponent</code>.
  365. *
  366. * @param aContainer the focus cycle root or focus traversal policy provider whose default
  367. * Component is to be returned
  368. * @return the default Component in the traversal cycle of aContainer,
  369. * or null if no suitable Component can be found
  370. * @see #getFirstComponent
  371. * @throws IllegalArgumentException if aContainer is null
  372. */
  373. public Component getDefaultComponent(Container aContainer) {
  374. return getFirstComponent(aContainer);
  375. }
  376. /**
  377. * Sets whether this ContainerOrderFocusTraversalPolicy transfers focus
  378. * down-cycle implicitly. If <code>true</code>, during normal forward focus
  379. * traversal, the Component traversed after a focus cycle root will be the
  380. * focus-cycle-root's default Component to focus. If <code>false</code>,
  381. * the next Component in the focus traversal cycle rooted at the specified
  382. * focus cycle root will be traversed instead. The default value for this
  383. * property is <code>true</code>.
  384. *
  385. * @param implicitDownCycleTraversal whether this
  386. * ContainerOrderFocusTraversalPolicy transfers focus down-cycle
  387. * implicitly
  388. * @see #getImplicitDownCycleTraversal
  389. * @see #getFirstComponent
  390. */
  391. public void setImplicitDownCycleTraversal(boolean
  392. implicitDownCycleTraversal) {
  393. this.implicitDownCycleTraversal = implicitDownCycleTraversal;
  394. }
  395. /**
  396. * Returns whether this ContainerOrderFocusTraversalPolicy transfers focus
  397. * down-cycle implicitly. If <code>true</code>, during normal forward focus
  398. * traversal, the Component traversed after a focus cycle root will be the
  399. * focus-cycle-root's default Component to focus. If <code>false</code>,
  400. * the next Component in the focus traversal cycle rooted at the specified
  401. * focus cycle root will be traversed instead.
  402. *
  403. * @return whether this ContainerOrderFocusTraversalPolicy transfers focus
  404. * down-cycle implicitly
  405. * @see #setImplicitDownCycleTraversal
  406. * @see #getFirstComponent
  407. */
  408. public boolean getImplicitDownCycleTraversal() {
  409. return implicitDownCycleTraversal;
  410. }
  411. /**
  412. * Determines whether a Component is an acceptable choice as the new
  413. * focus owner. By default, this method will accept a Component if and
  414. * only if it is visible, displayable, enabled, and focusable.
  415. *
  416. * @param aComponent the Component whose fitness as a focus owner is to
  417. * be tested
  418. * @return <code>true</code> if aComponent is visible, displayable,
  419. * enabled, and focusable; <code>false</code> otherwise
  420. */
  421. protected boolean accept(Component aComponent) {
  422. if (!(aComponent.isVisible() && aComponent.isDisplayable() &&
  423. aComponent.isFocusable() && aComponent.isEnabled())) {
  424. return false;
  425. }
  426. // Verify that the Component is recursively enabled. Disabling a
  427. // heavyweight Container disables its children, whereas disabling
  428. // a lightweight Container does not.
  429. if (!(aComponent instanceof Window)) {
  430. for (Container enableTest = aComponent.getParent();
  431. enableTest != null;
  432. enableTest = enableTest.getParent())
  433. {
  434. if (!(enableTest.isEnabled() || enableTest.isLightweight())) {
  435. return false;
  436. }
  437. if (enableTest instanceof Window) {
  438. break;
  439. }
  440. }
  441. }
  442. return true;
  443. }
  444. }
  445. class MutableBoolean {
  446. boolean value = false;
  447. }