1. /*
  2. * @(#)SortingFocusTraversalPolicy.java 1.4 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.awt.Component;
  9. import java.awt.Container;
  10. import java.awt.Window;
  11. import java.util.*;
  12. /**
  13. * A FocusTraversalPolicy that determines traversal order by sorting the
  14. * Components of a focus traversal cycle based on a given Comparator. Portions
  15. * of the Component hierarchy that are not visible and displayable will not be
  16. * included.
  17. * <p>
  18. * By default, SortingFocusTraversalPolicy implicitly transfers focus down-
  19. * cycle. That is, during normal focus traversal, the Component
  20. * traversed after a focus cycle root will be the focus-cycle-root's default
  21. * Component to focus. This behavior can be disabled using the
  22. * <code>setImplicitDownCycleTraversal</code> method.
  23. * <p>
  24. * By default, methods of this class with return a Component only if it is
  25. * visible, displayable, enabled, and focusable. Subclasses can modify this
  26. * behavior by overriding the <code>accept</code> method.
  27. *
  28. * @author David Mendenhall
  29. * @version 1.4, 01/23/03
  30. *
  31. * @see java.util.Comparator
  32. * @since 1.4
  33. */
  34. public class SortingFocusTraversalPolicy
  35. extends InternalFrameFocusTraversalPolicy
  36. {
  37. private Comparator comparator;
  38. private boolean implicitDownCycleTraversal = true;
  39. /**
  40. * Used by getComponentAfter and getComponentBefore for efficiency. In
  41. * order to maintain compliance with the specification of
  42. * FocusTraversalPolicy, if traversal wraps, we should invoke
  43. * getFirstComponent or getLastComponent. These methods may be overriden in
  44. * subclasses to behave in a non-generic way. However, in the generic case,
  45. * these methods will simply return the first or last Components of the
  46. * sorted list, respectively. Since getComponentAfter and
  47. * getComponentBefore have already built the sorted list before determining
  48. * that they need to invoke getFirstComponent or getLastComponent, the
  49. * sorted list should be reused if possible.
  50. */
  51. private Container cachedRoot;
  52. private List cachedCycle;
  53. // Delegate our fitness test to ContainerOrder so that we only have to
  54. // code the algorithm once.
  55. private static final SwingContainerOrderFocusTraversalPolicy
  56. fitnessTestPolicy = new SwingContainerOrderFocusTraversalPolicy();
  57. /**
  58. * Constructs a SortingFocusTraversalPolicy without a Comparator.
  59. * Subclasses must set the Comparator using <code>setComparator</code>
  60. * before installing this FocusTraversalPolicy on a focus cycle root or
  61. * KeyboardFocusManager.
  62. */
  63. protected SortingFocusTraversalPolicy() {
  64. }
  65. /**
  66. * Constructs a SortingFocusTraversalPolicy with the specified Comparator.
  67. */
  68. public SortingFocusTraversalPolicy(Comparator comparator) {
  69. this.comparator = comparator;
  70. }
  71. private void enumerateAndSortCycle(Container focusCycleRoot,
  72. List cycle, Map defaults) {
  73. List defaultRoots = null;
  74. if (!focusCycleRoot.isShowing()) {
  75. return;
  76. }
  77. enumerateCycle(focusCycleRoot, cycle);
  78. boolean addDefaultComponents =
  79. (defaults != null && getImplicitDownCycleTraversal());
  80. // Create a list of all default Components which should be added
  81. // to the list
  82. if (addDefaultComponents) {
  83. defaultRoots = new ArrayList();
  84. for (Iterator iter = cycle.iterator(); iter.hasNext(); ) {
  85. Component comp = (Component)iter.next();
  86. if ((comp instanceof Container) &&
  87. ((Container)comp).isFocusCycleRoot())
  88. {
  89. defaultRoots.add(comp);
  90. }
  91. }
  92. Collections.sort(defaultRoots, comparator);
  93. }
  94. // Test all Components in the cycle for fitness. Remove unfit
  95. // Components. Do not test default Components of other cycles.
  96. for (Iterator iter = cycle.iterator(); iter.hasNext(); ) {
  97. Component comp = (Component)iter.next();
  98. if (!accept(comp)) {
  99. iter.remove();
  100. }
  101. }
  102. // Sort the Components in the cycle
  103. Collections.sort(cycle, comparator);
  104. // Find all of the roots in the cycle and place their default
  105. // Components after them. Note that the roots may have been removed
  106. // from the list because they were unfit. In that case, insert the
  107. // default Components as though the roots were still in the list.
  108. if (addDefaultComponents) {
  109. for (ListIterator defaultRootsIter =
  110. defaultRoots.listIterator(defaultRoots.size());
  111. defaultRootsIter.hasPrevious(); )
  112. {
  113. Container root = (Container)defaultRootsIter.previous();
  114. Component defComp =
  115. root.getFocusTraversalPolicy().getDefaultComponent(root);
  116. if (defComp != null && defComp.isShowing()) {
  117. int index = Collections.binarySearch(cycle, root,
  118. comparator);
  119. if (index < 0) {
  120. // If root is not in the list, then binarySearch
  121. // returns (-(insertion point) - 1). defComp follows
  122. // the index one less than the insertion point.
  123. index = -index - 2;
  124. }
  125. defaults.put(new Integer(index), defComp);
  126. }
  127. }
  128. }
  129. }
  130. private void enumerateCycle(Container container, List cycle) {
  131. if (!(container.isVisible() && container.isDisplayable())) {
  132. return;
  133. }
  134. cycle.add(container);
  135. Component[] components = container.getComponents();
  136. for (int i = 0; i < components.length; i++) {
  137. Component comp = components[i];
  138. if ((comp instanceof Container) &&
  139. !((Container)comp).isFocusCycleRoot() &&
  140. !((comp instanceof JComponent) &&
  141. ((JComponent)comp).isManagingFocus())) {
  142. enumerateCycle((Container)comp, cycle);
  143. } else {
  144. cycle.add(comp);
  145. }
  146. }
  147. }
  148. /**
  149. * Returns the Component that should receive the focus after aComponent.
  150. * focusCycleRoot must be a focus cycle root of aComponent.
  151. * <p>
  152. * By default, SortingFocusTraversalPolicy implicitly transfers focus down-
  153. * cycle. That is, during normal focus traversal, the Component
  154. * traversed after a focus cycle root will be the focus-cycle-root's
  155. * default Component to focus. This behavior can be disabled using the
  156. * <code>setImplicitDownCycleTraversal</code> method.
  157. *
  158. * @param focusCycleRoot a focus cycle root of aComponent
  159. * @param aComponent a (possibly indirect) child of focusCycleRoot, or
  160. * focusCycleRoot itself
  161. * @return the Component that should receive the focus after aComponent, or
  162. * null if no suitable Component can be found
  163. * @throws IllegalArgumentException if focusCycleRoot is not a focus cycle
  164. * root of aComponent, or if either focusCycleRoot or aComponent is
  165. * null
  166. */
  167. public Component getComponentAfter(Container focusCycleRoot,
  168. Component aComponent) {
  169. if (focusCycleRoot == null || aComponent == null) {
  170. throw new IllegalArgumentException("focusCycleRoot and aComponent cannot be null");
  171. }
  172. if (!aComponent.isFocusCycleRoot(focusCycleRoot)) {
  173. throw new IllegalArgumentException("focusCycleRoot is not a focus cyle root of aComponent");
  174. }
  175. List cycle = new ArrayList();
  176. Map defaults = new HashMap();
  177. enumerateAndSortCycle(focusCycleRoot, cycle, defaults);
  178. int index;
  179. try {
  180. index = Collections.binarySearch(cycle, aComponent, comparator);
  181. } catch (ClassCastException e) {
  182. return getFirstComponent(focusCycleRoot);
  183. }
  184. if (index < 0) {
  185. // If we're not in the cycle, then binarySearch returns
  186. // (-(insertion point) - 1). The next element is our insertion
  187. // point.
  188. index = -index - 2;
  189. }
  190. Component defComp = (Component)defaults.get(new Integer(index));
  191. if (defComp != null) {
  192. return defComp;
  193. }
  194. index++;
  195. if (index >= cycle.size()) {
  196. this.cachedRoot = focusCycleRoot;
  197. this.cachedCycle = cycle;
  198. Component retval = getFirstComponent(focusCycleRoot);
  199. this.cachedRoot = null;
  200. this.cachedCycle = null;
  201. return retval;
  202. } else {
  203. return (Component)cycle.get(index);
  204. }
  205. }
  206. /**
  207. * Returns the Component that should receive the focus before aComponent.
  208. * focusCycleRoot must be a focus cycle root of aComponent.
  209. * <p>
  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. *
  216. * @param focusCycleRoot a focus cycle root of aComponent
  217. * @param aComponent a (possibly indirect) child of focusCycleRoot, or
  218. * focusCycleRoot itself
  219. * @return the Component that should receive the focus before aComponent,
  220. * or null if no suitable Component can be found
  221. * @throws IllegalArgumentException if focusCycleRoot is not a focus cycle
  222. * root of aComponent, or if either focusCycleRoot or aComponent is
  223. * null
  224. */
  225. public Component getComponentBefore(Container focusCycleRoot,
  226. Component aComponent) {
  227. if (focusCycleRoot == null || aComponent == null) {
  228. throw new IllegalArgumentException("focusCycleRoot and aComponent cannot be null");
  229. }
  230. if (!aComponent.isFocusCycleRoot(focusCycleRoot)) {
  231. throw new IllegalArgumentException("focusCycleRoot is not a focus cyle root of aComponent");
  232. }
  233. List cycle = new ArrayList();
  234. Map defaults = new HashMap();
  235. enumerateAndSortCycle(focusCycleRoot, cycle, defaults);
  236. int index;
  237. try {
  238. index = Collections.binarySearch(cycle, aComponent, comparator);
  239. } catch (ClassCastException e) {
  240. return getLastComponent(focusCycleRoot);
  241. }
  242. if (index < 0) {
  243. // If we're not in the cycle, then binarySearch returns
  244. // (-(insertion point) - 1). The previous element is our insertion
  245. // point - 1.
  246. index = -index - 2;
  247. } else {
  248. index--;
  249. }
  250. if (index < 0) {
  251. this.cachedRoot = focusCycleRoot;
  252. this.cachedCycle = cycle;
  253. Component retval = getLastComponent(focusCycleRoot);
  254. this.cachedRoot = null;
  255. this.cachedCycle = null;
  256. return retval;
  257. } else {
  258. Component defComp = (Component)defaults.get(new Integer(index));
  259. if (defComp != null) {
  260. return defComp;
  261. }
  262. return (Component)cycle.get(index);
  263. }
  264. }
  265. /**
  266. * Returns the first Component in the traversal cycle. This method is used
  267. * to determine the next Component to focus when traversal wraps in the
  268. * forward direction.
  269. *
  270. * @param focusCycleRoot the focus cycle root whose first Component is to
  271. * be returned
  272. * @return the first Component in the traversal cycle when focusCycleRoot
  273. * is the focus cycle root, or null if no suitable Component can be
  274. * found
  275. * @throws IllegalArgumentException if focusCycleRoot is null
  276. */
  277. public Component getFirstComponent(Container focusCycleRoot) {
  278. List cycle;
  279. if (focusCycleRoot == null) {
  280. throw new IllegalArgumentException("focusCycleRoot cannot be null");
  281. }
  282. if (this.cachedRoot == focusCycleRoot) {
  283. cycle = this.cachedCycle;
  284. } else {
  285. cycle = new ArrayList();
  286. enumerateAndSortCycle(focusCycleRoot, cycle, null);
  287. }
  288. int size = cycle.size();
  289. if (size == 0) {
  290. return null;
  291. }
  292. return (Component)cycle.get(0);
  293. }
  294. /**
  295. * Returns the last Component in the traversal cycle. This method is used
  296. * to determine the next Component to focus when traversal wraps in the
  297. * reverse direction.
  298. *
  299. * @param focusCycleRoot the focus cycle root whose last Component is to be
  300. * returned
  301. * @return the last Component in the traversal cycle when focusCycleRoot is
  302. * the focus cycle root, or null if no suitable Component can be
  303. * found
  304. * @throws IllegalArgumentException if focusCycleRoot is null
  305. */
  306. public Component getLastComponent(Container focusCycleRoot) {
  307. List cycle;
  308. if (focusCycleRoot == null) {
  309. throw new IllegalArgumentException("focusCycleRoot cannot be null");
  310. }
  311. if (this.cachedRoot == focusCycleRoot) {
  312. cycle = this.cachedCycle;
  313. } else {
  314. cycle = new ArrayList();
  315. enumerateAndSortCycle(focusCycleRoot, cycle, null);
  316. }
  317. int size = cycle.size();
  318. if (size == 0) {
  319. return null;
  320. }
  321. return (Component)cycle.get(size - 1);
  322. }
  323. /**
  324. * Returns the default Component to focus. This Component will be the first
  325. * to receive focus when traversing down into a new focus traversal cycle
  326. * rooted at focusCycleRoot. The default implementation of this method
  327. * returns the same Component as <code>getFirstComponent</code>.
  328. *
  329. * @param focusCycleRoot the focus cycle root whose default Component is to
  330. * be returned
  331. * @return the default Component in the traversal cycle when focusCycleRoot
  332. * is the focus cycle root, or null if no suitable Component can be
  333. * found
  334. * @see #getFirstComponent
  335. * @throws IllegalArgumentException if focusCycleRoot is null
  336. */
  337. public Component getDefaultComponent(Container focusCycleRoot) {
  338. return getFirstComponent(focusCycleRoot);
  339. }
  340. /**
  341. * Sets whether this SortingFocusTraversalPolicy transfers focus down-cycle
  342. * implicitly. If <code>true</code>, during normal focus traversal,
  343. * the Component traversed after a focus cycle root will be the focus-
  344. * cycle-root's default Component to focus. If <code>false</code>, the
  345. * next Component in the focus traversal cycle rooted at the specified
  346. * focus cycle root will be traversed instead. The default value for this
  347. * property is <code>true</code>.
  348. *
  349. * @param implicitDownCycleTraversal whether this
  350. * SortingFocusTraversalPolicy transfers focus down-cycle implicitly
  351. * @see #getImplicitDownCycleTraversal
  352. * @see #getFirstComponent
  353. */
  354. public void setImplicitDownCycleTraversal(boolean
  355. implicitDownCycleTraversal) {
  356. this.implicitDownCycleTraversal = implicitDownCycleTraversal;
  357. }
  358. /**
  359. * Returns whether this SortingFocusTraversalPolicy transfers focus down-
  360. * cycle implicitly. If <code>true</code>, during normal focus
  361. * traversal, the Component traversed after a focus cycle root will be the
  362. * focus-cycle-root's default Component to focus. If <code>false</code>,
  363. * the next Component in the focus traversal cycle rooted at the specified
  364. * focus cycle root will be traversed instead.
  365. *
  366. * @return whether this SortingFocusTraversalPolicy transfers focus down-
  367. * cycle implicitly
  368. * @see #setImplicitDownCycleTraversal
  369. * @see #getFirstComponent
  370. */
  371. public boolean getImplicitDownCycleTraversal() {
  372. return implicitDownCycleTraversal;
  373. }
  374. /**
  375. * Sets the Comparator which will be used to sort the Components in a
  376. * focus traversal cycle.
  377. *
  378. * @param comparator the Comparator which will be used for sorting
  379. */
  380. protected void setComparator(Comparator comparator) {
  381. this.comparator = comparator;
  382. }
  383. /**
  384. * Returns the Comparator which will be used to sort the Components in a
  385. * focus traversal cycle.
  386. *
  387. * @return the Comparator which will be used for sorting
  388. */
  389. protected Comparator getComparator() {
  390. return comparator;
  391. }
  392. /**
  393. * Determines whether a Component is an acceptable choice as the new
  394. * focus owner. By default, this method will accept a Component if and
  395. * only if it is visible, displayable, enabled, and focusable.
  396. *
  397. * @param aComponent the Component whose fitness as a focus owner is to
  398. * be tested
  399. * @return <code>true</code> if aComponent is visible, displayable,
  400. * enabled, and focusable; <code>false</code> otherwise
  401. */
  402. protected boolean accept(Component aComponent) {
  403. return fitnessTestPolicy.accept(aComponent);
  404. }
  405. }
  406. // Create our own subclass and change accept to public so that we can call
  407. // accept.
  408. class SwingContainerOrderFocusTraversalPolicy
  409. extends java.awt.ContainerOrderFocusTraversalPolicy
  410. {
  411. public boolean accept(Component aComponent) {
  412. return super.accept(aComponent);
  413. }
  414. }