1. /*
  2. * @(#)SortingFocusTraversalPolicy.java 1.7 04/05/05
  3. *
  4. * Copyright 2004 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. import java.awt.FocusTraversalPolicy;
  13. import java.util.logging.*;
  14. /**
  15. * A FocusTraversalPolicy that determines traversal order by sorting the
  16. * Components of a focus traversal cycle based on a given Comparator. Portions
  17. * of the Component hierarchy that are not visible and displayable will not be
  18. * included.
  19. * <p>
  20. * By default, SortingFocusTraversalPolicy implicitly transfers focus down-
  21. * cycle. That is, during normal focus traversal, the Component
  22. * traversed after a focus cycle root will be the focus-cycle-root's default
  23. * Component to focus. This behavior can be disabled using the
  24. * <code>setImplicitDownCycleTraversal</code> method.
  25. * <p>
  26. * By default, methods of this class with return a Component only if it is
  27. * visible, displayable, enabled, and focusable. Subclasses can modify this
  28. * behavior by overriding the <code>accept</code> method.
  29. * <p>
  30. * This policy takes into account <a
  31. * href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal
  32. * policy providers</a>. When searching for first/last/next/previous Component,
  33. * if a focus traversal policy provider is encountered, its focus traversal
  34. * policy is used to perform the search operation.
  35. *
  36. * @author David Mendenhall
  37. * @version 1.7, 05/05/04
  38. *
  39. * @see java.util.Comparator
  40. * @since 1.4
  41. */
  42. public class SortingFocusTraversalPolicy
  43. extends InternalFrameFocusTraversalPolicy
  44. {
  45. private Comparator<? super Component> comparator;
  46. private boolean implicitDownCycleTraversal = true;
  47. private Logger log = Logger.getLogger("javax.swing.SortingFocusTraversalPolicy");
  48. /**
  49. * Used by getComponentAfter and getComponentBefore for efficiency. In
  50. * order to maintain compliance with the specification of
  51. * FocusTraversalPolicy, if traversal wraps, we should invoke
  52. * getFirstComponent or getLastComponent. These methods may be overriden in
  53. * subclasses to behave in a non-generic way. However, in the generic case,
  54. * these methods will simply return the first or last Components of the
  55. * sorted list, respectively. Since getComponentAfter and
  56. * getComponentBefore have already built the sorted list before determining
  57. * that they need to invoke getFirstComponent or getLastComponent, the
  58. * sorted list should be reused if possible.
  59. */
  60. private Container cachedRoot;
  61. private List cachedCycle;
  62. // Delegate our fitness test to ContainerOrder so that we only have to
  63. // code the algorithm once.
  64. private static final SwingContainerOrderFocusTraversalPolicy
  65. fitnessTestPolicy = new SwingContainerOrderFocusTraversalPolicy();
  66. /**
  67. * Constructs a SortingFocusTraversalPolicy without a Comparator.
  68. * Subclasses must set the Comparator using <code>setComparator</code>
  69. * before installing this FocusTraversalPolicy on a focus cycle root or
  70. * KeyboardFocusManager.
  71. */
  72. protected SortingFocusTraversalPolicy() {
  73. }
  74. /**
  75. * Constructs a SortingFocusTraversalPolicy with the specified Comparator.
  76. */
  77. public SortingFocusTraversalPolicy(Comparator<? super Component> comparator) {
  78. this.comparator = comparator;
  79. }
  80. private void enumerateAndSortCycle(Container focusCycleRoot,
  81. List cycle, Map defaults) {
  82. List defaultRoots = null;
  83. if (!focusCycleRoot.isShowing()) {
  84. return;
  85. }
  86. enumerateCycle(focusCycleRoot, cycle);
  87. boolean addDefaultComponents =
  88. (defaults != null && getImplicitDownCycleTraversal());
  89. if (log.isLoggable(Level.FINE)) log.fine("### Will add defaults: " + addDefaultComponents);
  90. // Create a list of all default Components which should be added
  91. // to the list
  92. if (addDefaultComponents) {
  93. defaultRoots = new ArrayList();
  94. for (Iterator iter = cycle.iterator(); iter.hasNext(); ) {
  95. Component comp = (Component)iter.next();
  96. if ((comp instanceof Container) &&
  97. ((Container)comp).isFocusCycleRoot())
  98. {
  99. defaultRoots.add(comp);
  100. }
  101. }
  102. Collections.sort(defaultRoots, comparator);
  103. }
  104. // Sort the Components in the cycle
  105. Collections.sort(cycle, comparator);
  106. // Find all of the roots in the cycle and place their default
  107. // Components after them. Note that the roots may have been removed
  108. // from the list because they were unfit. In that case, insert the
  109. // default Components as though the roots were still in the list.
  110. if (addDefaultComponents) {
  111. for (ListIterator defaultRootsIter =
  112. defaultRoots.listIterator(defaultRoots.size());
  113. defaultRootsIter.hasPrevious(); )
  114. {
  115. Container root = (Container)defaultRootsIter.previous();
  116. Component defComp =
  117. root.getFocusTraversalPolicy().getDefaultComponent(root);
  118. if (defComp != null && defComp.isShowing()) {
  119. int index = Collections.binarySearch(cycle, root,
  120. comparator);
  121. if (index < 0) {
  122. // If root is not in the list, then binarySearch
  123. // returns (-(insertion point) - 1). defComp follows
  124. // the index one less than the insertion point.
  125. index = -index - 2;
  126. }
  127. defaults.put(new Integer(index), defComp);
  128. }
  129. }
  130. }
  131. }
  132. private void enumerateCycle(Container container, List cycle) {
  133. if (!(container.isVisible() && container.isDisplayable())) {
  134. return;
  135. }
  136. cycle.add(container);
  137. Component[] components = container.getComponents();
  138. for (int i = 0; i < components.length; i++) {
  139. Component comp = components[i];
  140. if ((comp instanceof Container)
  141. && !((Container)comp).isFocusTraversalPolicyProvider()
  142. && !((Container)comp).isFocusCycleRoot()
  143. && !((comp instanceof JComponent)
  144. && ((JComponent)comp).isManagingFocus()))
  145. {
  146. enumerateCycle((Container)comp, cycle);
  147. } else {
  148. cycle.add(comp);
  149. }
  150. }
  151. }
  152. Container getTopmostProvider(Container focusCycleRoot, Component aComponent) {
  153. Container aCont = aComponent.getParent();
  154. Container ftp = null;
  155. while (aCont != focusCycleRoot && aCont != null) {
  156. if (aCont.isFocusTraversalPolicyProvider()) {
  157. ftp = aCont;
  158. }
  159. aCont = aCont.getParent();
  160. }
  161. if (aCont == null) {
  162. return null;
  163. }
  164. return ftp;
  165. }
  166. /**
  167. * Returns the Component that should receive the focus after aComponent.
  168. * aContainer must be a focus cycle root of aComponent or a focus traversal policy provider.
  169. * <p>
  170. * By default, SortingFocusTraversalPolicy implicitly transfers focus down-
  171. * cycle. That is, during normal focus traversal, the Component
  172. * traversed after a focus cycle root will be the focus-cycle-root's
  173. * default Component to focus. This behavior can be disabled using the
  174. * <code>setImplicitDownCycleTraversal</code> method.
  175. * <p>
  176. * If aContainer is <a href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus
  177. * traversal policy provider</a>, the focus is always transferred down-cycle.
  178. *
  179. * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider
  180. * @param aComponent a (possibly indirect) child of aContainer, or
  181. * aContainer itself
  182. * @return the Component that should receive the focus after aComponent, or
  183. * null if no suitable Component can be found
  184. * @throws IllegalArgumentException if aContainer is not a focus cycle
  185. * root of aComponent or a focus traversal policy provider, or if either aContainer or
  186. * aComponent is null
  187. */
  188. public Component getComponentAfter(Container aContainer,
  189. Component aComponent) {
  190. if (log.isLoggable(Level.FINE)) log.fine("### Searching in " + aContainer.getName() + " for component after " + aComponent.getName());
  191. if (aContainer == null || aComponent == null) {
  192. throw new IllegalArgumentException("aContainer and aComponent cannot be null");
  193. }
  194. if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {
  195. throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");
  196. } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
  197. throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
  198. }
  199. // See if the component is inside of policy provider
  200. Container ftp = getTopmostProvider(aContainer, aComponent);
  201. if (ftp != null) {
  202. if (log.isLoggable(Level.FINE)) log.fine("### Asking FTP " + ftp.getName() + " for component after " + aComponent.getName());
  203. // FTP knows how to find component after the given. We don't.
  204. FocusTraversalPolicy policy = ftp.getFocusTraversalPolicy();
  205. Component retval = policy.getComponentAfter(ftp, aComponent);
  206. if (retval == policy.getFirstComponent(ftp)) {
  207. retval = null;
  208. }
  209. if (retval != null) {
  210. if (log.isLoggable(Level.FINE)) log.fine("### FTP returned " + retval.getName());
  211. return retval;
  212. }
  213. aComponent = ftp;
  214. }
  215. List cycle = new ArrayList();
  216. Map defaults = new HashMap();
  217. enumerateAndSortCycle(aContainer, cycle, defaults);
  218. int index;
  219. try {
  220. index = Collections.binarySearch(cycle, aComponent, comparator);
  221. } catch (ClassCastException e) {
  222. if (log.isLoggable(Level.FINE)) log.fine("### Didn't find component " + aComponent.getName() + " in a cycle " + aContainer.getName());
  223. return getFirstComponent(aContainer);
  224. }
  225. if (index < 0) {
  226. // If we're not in the cycle, then binarySearch returns
  227. // (-(insertion point) - 1). The next element is our insertion
  228. // point.
  229. index = -index - 2;
  230. }
  231. Component defComp = (Component)defaults.get(new Integer(index));
  232. if (defComp != null) {
  233. return defComp;
  234. }
  235. do {
  236. index++;
  237. if (index >= cycle.size()) {
  238. if (aContainer.isFocusCycleRoot()) {
  239. this.cachedRoot = aContainer;
  240. this.cachedCycle = cycle;
  241. Component retval = getFirstComponent(aContainer);
  242. this.cachedRoot = null;
  243. this.cachedCycle = null;
  244. return retval;
  245. } else {
  246. return null;
  247. }
  248. } else {
  249. Component comp = (Component)cycle.get(index);
  250. if (accept(comp)) {
  251. return comp;
  252. } else if (comp instanceof Container && ((Container)comp).isFocusTraversalPolicyProvider()) {
  253. return ((Container)comp).getFocusTraversalPolicy().getDefaultComponent((Container)comp);
  254. }
  255. }
  256. } while (true);
  257. }
  258. /**
  259. * Returns the Component that should receive the focus before aComponent.
  260. * aContainer must be a focus cycle root of aComponent or a focus traversal policy provider.
  261. * <p>
  262. * By default, SortingFocusTraversalPolicy implicitly transfers focus down-
  263. * cycle. That is, during normal focus traversal, the Component
  264. * traversed after a focus cycle root will be the focus-cycle-root's
  265. * default Component to focus. This behavior can be disabled using the
  266. * <code>setImplicitDownCycleTraversal</code> method.
  267. * <p>
  268. * If aContainer is <a href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus
  269. * traversal policy provider</a>, the focus is always transferred down-cycle.
  270. *
  271. * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider
  272. * @param aComponent a (possibly indirect) child of aContainer, or
  273. * aContainer itself
  274. * @return the Component that should receive the focus before aComponent,
  275. * or null if no suitable Component can be found
  276. * @throws IllegalArgumentException if aContainer is not a focus cycle
  277. * root of aComponent or a focus traversal policy provider, or if either aContainer or
  278. * aComponent is null
  279. */
  280. public Component getComponentBefore(Container aContainer,
  281. Component aComponent) {
  282. if (aContainer == null || aComponent == null) {
  283. throw new IllegalArgumentException("aContainer and aComponent cannot be null");
  284. }
  285. if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {
  286. throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");
  287. } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
  288. throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
  289. }
  290. // See if the component is inside of policy provider
  291. Container ftp = getTopmostProvider(aContainer, aComponent);
  292. if (ftp != null) {
  293. if (log.isLoggable(Level.FINE)) log.fine("### Asking FTP " + ftp.getName() + " for component after " + aComponent.getName());
  294. // FTP knows how to find component after the given. We don't.
  295. FocusTraversalPolicy policy = ftp.getFocusTraversalPolicy();
  296. Component retval = policy.getComponentBefore(ftp, aComponent);
  297. if (retval == policy.getLastComponent(ftp)) {
  298. retval = null;
  299. }
  300. if (retval != null) {
  301. if (log.isLoggable(Level.FINE)) log.fine("### FTP returned " + retval.getName());
  302. return retval;
  303. }
  304. aComponent = ftp;
  305. }
  306. List cycle = new ArrayList();
  307. Map defaults = new HashMap();
  308. enumerateAndSortCycle(aContainer, cycle, defaults);
  309. if (log.isLoggable(Level.FINE)) log.fine("### Cycle is " + cycle + ", component is " + aComponent);
  310. int index;
  311. try {
  312. index = Collections.binarySearch(cycle, aComponent, comparator);
  313. } catch (ClassCastException e) {
  314. return getLastComponent(aContainer);
  315. }
  316. if (index < 0) {
  317. // If we're not in the cycle, then binarySearch returns
  318. // (-(insertion point) - 1). The previous element is our insertion
  319. // point - 1.
  320. index = -index - 2;
  321. } else {
  322. index--;
  323. }
  324. if (log.isLoggable(Level.FINE)) log.fine("### Index is " + index);
  325. if (index >= 0) {
  326. Component defComp = (Component)defaults.get(new Integer(index));
  327. if (defComp != null && cycle.get(index) != aContainer) {
  328. if (log.isLoggable(Level.FINE)) log.fine("### Returning default " + defComp.getName() + " at " + index);
  329. return defComp;
  330. }
  331. }
  332. do {
  333. if (index < 0) {
  334. this.cachedRoot = aContainer;
  335. this.cachedCycle = cycle;
  336. Component retval = getLastComponent(aContainer);
  337. this.cachedRoot = null;
  338. this.cachedCycle = null;
  339. return retval;
  340. } else {
  341. Component comp = (Component)cycle.get(index);
  342. if (accept(comp)) {
  343. return comp;
  344. } else if (comp instanceof Container && ((Container)comp).isFocusTraversalPolicyProvider()) {
  345. return ((Container)comp).getFocusTraversalPolicy().getLastComponent((Container)comp);
  346. }
  347. }
  348. index--;
  349. } while (true);
  350. }
  351. /**
  352. * Returns the first Component in the traversal cycle. This method is used
  353. * to determine the next Component to focus when traversal wraps in the
  354. * forward direction.
  355. *
  356. * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose
  357. * first Component is to be returned
  358. * @return the first Component in the traversal cycle of aContainer,
  359. * or null if no suitable Component can be found
  360. * @throws IllegalArgumentException if aContainer is null
  361. */
  362. public Component getFirstComponent(Container aContainer) {
  363. List cycle;
  364. if (log.isLoggable(Level.FINE)) log.fine("### Getting first component in " + aContainer.getName());
  365. if (aContainer == null) {
  366. throw new IllegalArgumentException("aContainer cannot be null");
  367. }
  368. if (this.cachedRoot == aContainer) {
  369. cycle = this.cachedCycle;
  370. } else {
  371. cycle = new ArrayList();
  372. enumerateAndSortCycle(aContainer, cycle, null);
  373. }
  374. int size = cycle.size();
  375. if (size == 0) {
  376. return null;
  377. }
  378. for (int i= 0; i < cycle.size(); i++) {
  379. Component comp = (Component)cycle.get(i);
  380. if (accept(comp)) {
  381. return comp;
  382. } else if (comp instanceof Container && !(comp == aContainer) && ((Container)comp).isFocusTraversalPolicyProvider()) {
  383. return ((Container)comp).getFocusTraversalPolicy().getDefaultComponent((Container)comp);
  384. }
  385. }
  386. return null;
  387. }
  388. /**
  389. * Returns the last Component in the traversal cycle. This method is used
  390. * to determine the next Component to focus when traversal wraps in the
  391. * reverse direction.
  392. *
  393. * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose
  394. * last Component is to be returned
  395. * @return the last Component in the traversal cycle of aContainer,
  396. * or null if no suitable Component can be found
  397. * @throws IllegalArgumentException if aContainer is null
  398. */
  399. public Component getLastComponent(Container aContainer) {
  400. List cycle;
  401. if (log.isLoggable(Level.FINE)) log.fine("### Getting last component in " + aContainer.getName());
  402. if (aContainer == null) {
  403. throw new IllegalArgumentException("aContainer cannot be null");
  404. }
  405. if (this.cachedRoot == aContainer) {
  406. cycle = this.cachedCycle;
  407. } else {
  408. cycle = new ArrayList();
  409. enumerateAndSortCycle(aContainer, cycle, null);
  410. }
  411. int size = cycle.size();
  412. if (size == 0) {
  413. if (log.isLoggable(Level.FINE)) log.fine("### Cycle is empty");
  414. return null;
  415. }
  416. if (log.isLoggable(Level.FINE)) log.fine("### Cycle is " + cycle);
  417. for (int i= cycle.size()-1; i >= 0; i--) {
  418. Component comp = (Component)cycle.get(i);
  419. if (accept(comp)) {
  420. return comp;
  421. } else if (comp instanceof Container && !(comp == aContainer) && ((Container)comp).isFocusTraversalPolicyProvider()) {
  422. return ((Container)comp).getFocusTraversalPolicy().getLastComponent((Container)comp);
  423. }
  424. }
  425. return null;
  426. }
  427. /**
  428. * Returns the default Component to focus. This Component will be the first
  429. * to receive focus when traversing down into a new focus traversal cycle
  430. * rooted at aContainer. The default implementation of this method
  431. * returns the same Component as <code>getFirstComponent</code>.
  432. *
  433. * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider whose
  434. * default Component is to be returned
  435. * @return the default Component in the traversal cycle of aContainer,
  436. * or null if no suitable Component can be found
  437. * @see #getFirstComponent
  438. * @throws IllegalArgumentException if aContainer is null
  439. */
  440. public Component getDefaultComponent(Container aContainer) {
  441. return getFirstComponent(aContainer);
  442. }
  443. /**
  444. * Sets whether this SortingFocusTraversalPolicy transfers focus down-cycle
  445. * implicitly. If <code>true</code>, during normal focus traversal,
  446. * the Component traversed after a focus cycle root will be the focus-
  447. * cycle-root's default Component to focus. If <code>false</code>, the
  448. * next Component in the focus traversal cycle rooted at the specified
  449. * focus cycle root will be traversed instead. The default value for this
  450. * property is <code>true</code>.
  451. *
  452. * @param implicitDownCycleTraversal whether this
  453. * SortingFocusTraversalPolicy transfers focus down-cycle implicitly
  454. * @see #getImplicitDownCycleTraversal
  455. * @see #getFirstComponent
  456. */
  457. public void setImplicitDownCycleTraversal(boolean
  458. implicitDownCycleTraversal) {
  459. this.implicitDownCycleTraversal = implicitDownCycleTraversal;
  460. }
  461. /**
  462. * Returns whether this SortingFocusTraversalPolicy transfers focus down-
  463. * cycle implicitly. If <code>true</code>, during normal focus
  464. * traversal, the Component traversed after a focus cycle root will be the
  465. * focus-cycle-root's default Component to focus. If <code>false</code>,
  466. * the next Component in the focus traversal cycle rooted at the specified
  467. * focus cycle root will be traversed instead.
  468. *
  469. * @return whether this SortingFocusTraversalPolicy transfers focus down-
  470. * cycle implicitly
  471. * @see #setImplicitDownCycleTraversal
  472. * @see #getFirstComponent
  473. */
  474. public boolean getImplicitDownCycleTraversal() {
  475. return implicitDownCycleTraversal;
  476. }
  477. /**
  478. * Sets the Comparator which will be used to sort the Components in a
  479. * focus traversal cycle.
  480. *
  481. * @param comparator the Comparator which will be used for sorting
  482. */
  483. protected void setComparator(Comparator<? super Component> comparator) {
  484. this.comparator = comparator;
  485. }
  486. /**
  487. * Returns the Comparator which will be used to sort the Components in a
  488. * focus traversal cycle.
  489. *
  490. * @return the Comparator which will be used for sorting
  491. */
  492. protected Comparator<? super Component> getComparator() {
  493. return comparator;
  494. }
  495. /**
  496. * Determines whether a Component is an acceptable choice as the new
  497. * focus owner. By default, this method will accept a Component if and
  498. * only if it is visible, displayable, enabled, and focusable.
  499. *
  500. * @param aComponent the Component whose fitness as a focus owner is to
  501. * be tested
  502. * @return <code>true</code> if aComponent is visible, displayable,
  503. * enabled, and focusable; <code>false</code> otherwise
  504. */
  505. protected boolean accept(Component aComponent) {
  506. return fitnessTestPolicy.accept(aComponent);
  507. }
  508. }
  509. // Create our own subclass and change accept to public so that we can call
  510. // accept.
  511. class SwingContainerOrderFocusTraversalPolicy
  512. extends java.awt.ContainerOrderFocusTraversalPolicy
  513. {
  514. public boolean accept(Component aComponent) {
  515. return super.accept(aComponent);
  516. }
  517. }