1. /*
  2. * @(#)BasicSplitPaneUI.java 1.80 04/05/18
  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.plaf.basic;
  8. import sun.swing.DefaultLookup;
  9. import sun.swing.UIAction;
  10. import javax.swing.*;
  11. import javax.swing.border.Border;
  12. import javax.swing.event.*;
  13. import java.awt.*;
  14. import java.awt.event.*;
  15. import java.awt.peer.ComponentPeer;
  16. import java.awt.peer.LightweightPeer;
  17. import java.beans.*;
  18. import java.util.*;
  19. import javax.swing.plaf.ActionMapUIResource;
  20. import javax.swing.plaf.SplitPaneUI;
  21. import javax.swing.plaf.ComponentUI;
  22. import javax.swing.plaf.UIResource;
  23. /**
  24. * A Basic L&F implementation of the SplitPaneUI.
  25. *
  26. * @version 1.80 05/18/04
  27. * @author Scott Violet
  28. * @author Steve Wilson
  29. * @author Ralph Kar
  30. */
  31. public class BasicSplitPaneUI extends SplitPaneUI
  32. {
  33. /**
  34. * The divider used for non-continuous layout is added to the split pane
  35. * with this object.
  36. */
  37. protected static final String NON_CONTINUOUS_DIVIDER =
  38. "nonContinuousDivider";
  39. /**
  40. * How far (relative) the divider does move when it is moved around by
  41. * the cursor keys on the keyboard.
  42. */
  43. protected static int KEYBOARD_DIVIDER_MOVE_OFFSET = 3;
  44. /**
  45. * JSplitPane instance this instance is providing
  46. * the look and feel for.
  47. */
  48. protected JSplitPane splitPane;
  49. /**
  50. * LayoutManager that is created and placed into the split pane.
  51. */
  52. protected BasicHorizontalLayoutManager layoutManager;
  53. /**
  54. * Instance of the divider for this JSplitPane.
  55. */
  56. protected BasicSplitPaneDivider divider;
  57. /**
  58. * Instance of the PropertyChangeListener for this JSplitPane.
  59. */
  60. protected PropertyChangeListener propertyChangeListener;
  61. /**
  62. * Instance of the FocusListener for this JSplitPane.
  63. */
  64. protected FocusListener focusListener;
  65. private Handler handler;
  66. /**
  67. * Keys to use for forward focus traversal when the JComponent is
  68. * managing focus.
  69. */
  70. private static Set managingFocusForwardTraversalKeys;
  71. /**
  72. * Keys to use for backward focus traversal when the JComponent is
  73. * managing focus.
  74. */
  75. private static Set managingFocusBackwardTraversalKeys;
  76. /**
  77. * The size of the divider while the dragging session is valid.
  78. */
  79. protected int dividerSize;
  80. /**
  81. * Instance for the shadow of the divider when non continuous layout
  82. * is being used.
  83. */
  84. protected Component nonContinuousLayoutDivider;
  85. /**
  86. * Set to true in startDragging if any of the children
  87. * (not including the nonContinuousLayoutDivider) are heavy weights.
  88. */
  89. protected boolean draggingHW;
  90. /**
  91. * Location of the divider when the dragging session began.
  92. */
  93. protected int beginDragDividerLocation;
  94. /**
  95. * As of Java 2 platform v1.3 this previously undocumented field is no
  96. * longer used.
  97. * Key bindings are now defined by the LookAndFeel, please refer to
  98. * the key bindings specification for further details.
  99. *
  100. * @deprecated As of Java 2 platform v1.3.
  101. */
  102. @Deprecated
  103. protected KeyStroke upKey;
  104. /**
  105. * As of Java 2 platform v1.3 this previously undocumented field is no
  106. * longer used.
  107. * Key bindings are now defined by the LookAndFeel, please refer to
  108. * the key bindings specification for further details.
  109. *
  110. * @deprecated As of Java 2 platform v1.3.
  111. */
  112. @Deprecated
  113. protected KeyStroke downKey;
  114. /**
  115. * As of Java 2 platform v1.3 this previously undocumented field is no
  116. * longer used.
  117. * Key bindings are now defined by the LookAndFeel, please refer to
  118. * the key bindings specification for further details.
  119. *
  120. * @deprecated As of Java 2 platform v1.3.
  121. */
  122. @Deprecated
  123. protected KeyStroke leftKey;
  124. /**
  125. * As of Java 2 platform v1.3 this previously undocumented field is no
  126. * longer used.
  127. * Key bindings are now defined by the LookAndFeel, please refer to
  128. * the key bindings specification for further details.
  129. *
  130. * @deprecated As of Java 2 platform v1.3.
  131. */
  132. @Deprecated
  133. protected KeyStroke rightKey;
  134. /**
  135. * As of Java 2 platform v1.3 this previously undocumented field is no
  136. * longer used.
  137. * Key bindings are now defined by the LookAndFeel, please refer to
  138. * the key bindings specification for further details.
  139. *
  140. * @deprecated As of Java 2 platform v1.3.
  141. */
  142. @Deprecated
  143. protected KeyStroke homeKey;
  144. /**
  145. * As of Java 2 platform v1.3 this previously undocumented field is no
  146. * longer used.
  147. * Key bindings are now defined by the LookAndFeel, please refer to
  148. * the key bindings specification for further details.
  149. *
  150. * @deprecated As of Java 2 platform v1.3.
  151. */
  152. @Deprecated
  153. protected KeyStroke endKey;
  154. /**
  155. * As of Java 2 platform v1.3 this previously undocumented field is no
  156. * longer used.
  157. * Key bindings are now defined by the LookAndFeel, please refer to
  158. * the key bindings specification for further details.
  159. *
  160. * @deprecated As of Java 2 platform v1.3.
  161. */
  162. @Deprecated
  163. protected KeyStroke dividerResizeToggleKey;
  164. /**
  165. * As of Java 2 platform v1.3 this previously undocumented field is no
  166. * longer used.
  167. * Key bindings are now defined by the LookAndFeel, please refer to
  168. * the key bindings specification for further details.
  169. *
  170. * @deprecated As of Java 2 platform v1.3.
  171. */
  172. @Deprecated
  173. protected ActionListener keyboardUpLeftListener;
  174. /**
  175. * As of Java 2 platform v1.3 this previously undocumented field is no
  176. * longer used.
  177. * Key bindings are now defined by the LookAndFeel, please refer to
  178. * the key bindings specification for further details.
  179. *
  180. * @deprecated As of Java 2 platform v1.3.
  181. */
  182. @Deprecated
  183. protected ActionListener keyboardDownRightListener;
  184. /**
  185. * As of Java 2 platform v1.3 this previously undocumented field is no
  186. * longer used.
  187. * Key bindings are now defined by the LookAndFeel, please refer to
  188. * the key bindings specification for further details.
  189. *
  190. * @deprecated As of Java 2 platform v1.3.
  191. */
  192. @Deprecated
  193. protected ActionListener keyboardHomeListener;
  194. /**
  195. * As of Java 2 platform v1.3 this previously undocumented field is no
  196. * longer used.
  197. * Key bindings are now defined by the LookAndFeel, please refer to
  198. * the key bindings specification for further details.
  199. *
  200. * @deprecated As of Java 2 platform v1.3.
  201. */
  202. @Deprecated
  203. protected ActionListener keyboardEndListener;
  204. /**
  205. * As of Java 2 platform v1.3 this previously undocumented field is no
  206. * longer used.
  207. * Key bindings are now defined by the LookAndFeel, please refer to
  208. * the key bindings specification for further details.
  209. *
  210. * @deprecated As of Java 2 platform v1.3.
  211. */
  212. @Deprecated
  213. protected ActionListener keyboardResizeToggleListener;
  214. // Private data of the instance
  215. private int orientation;
  216. private int lastDragLocation;
  217. private boolean continuousLayout;
  218. private boolean dividerKeyboardResize;
  219. private boolean dividerLocationIsSet; // needed for tracking
  220. // the first occurrence of
  221. // setDividerLocation()
  222. private Color dividerDraggingColor;
  223. private boolean rememberPaneSizes;
  224. // Indicates wether the one of splitpane sides is expanded
  225. private boolean keepHidden = false;
  226. /** Indicates that we have painted once. */
  227. // This is used by the LayoutManager to determine when it should use
  228. // the divider location provided by the JSplitPane. This is used as there
  229. // is no way to determine when the layout process has completed.
  230. boolean painted;
  231. /** If true, setDividerLocation does nothing. */
  232. boolean ignoreDividerLocationChange;
  233. /**
  234. * Creates a new BasicSplitPaneUI instance
  235. */
  236. public static ComponentUI createUI(JComponent x) {
  237. return new BasicSplitPaneUI();
  238. }
  239. static void loadActionMap(LazyActionMap map) {
  240. map.put(new Actions(Actions.NEGATIVE_INCREMENT));
  241. map.put(new Actions(Actions.POSITIVE_INCREMENT));
  242. map.put(new Actions(Actions.SELECT_MIN));
  243. map.put(new Actions(Actions.SELECT_MAX));
  244. map.put(new Actions(Actions.START_RESIZE));
  245. map.put(new Actions(Actions.TOGGLE_FOCUS));
  246. map.put(new Actions(Actions.FOCUS_OUT_FORWARD));
  247. map.put(new Actions(Actions.FOCUS_OUT_BACKWARD));
  248. }
  249. /**
  250. * Installs the UI.
  251. */
  252. public void installUI(JComponent c) {
  253. splitPane = (JSplitPane) c;
  254. dividerLocationIsSet = false;
  255. dividerKeyboardResize = false;
  256. keepHidden = false;
  257. installDefaults();
  258. installListeners();
  259. installKeyboardActions();
  260. setLastDragLocation(-1);
  261. }
  262. /**
  263. * Installs the UI defaults.
  264. */
  265. protected void installDefaults(){
  266. LookAndFeel.installBorder(splitPane, "SplitPane.border");
  267. LookAndFeel.installColors(splitPane, "SplitPane.background",
  268. "SplitPane.foreground");
  269. LookAndFeel.installProperty(splitPane, "opaque", Boolean.TRUE);
  270. if (divider == null) divider = createDefaultDivider();
  271. divider.setBasicSplitPaneUI(this);
  272. Border b = divider.getBorder();
  273. if (b == null || !(b instanceof UIResource)) {
  274. divider.setBorder(UIManager.getBorder("SplitPaneDivider.border"));
  275. }
  276. dividerDraggingColor = UIManager.getColor("SplitPaneDivider.draggingColor");
  277. setOrientation(splitPane.getOrientation());
  278. // This plus 2 here is to provide backwards consistancy. Previously,
  279. // the old size did not include the 2 pixel border around the divider,
  280. // it now does.
  281. LookAndFeel.installProperty(splitPane, "dividerSize",
  282. UIManager.get("SplitPane.dividerSize"));
  283. divider.setDividerSize(splitPane.getDividerSize());
  284. dividerSize = divider.getDividerSize();
  285. splitPane.add(divider, JSplitPane.DIVIDER);
  286. setContinuousLayout(splitPane.isContinuousLayout());
  287. resetLayoutManager();
  288. /* Install the nonContinuousLayoutDivider here to avoid having to
  289. add/remove everything later. */
  290. if(nonContinuousLayoutDivider == null) {
  291. setNonContinuousLayoutDivider(
  292. createDefaultNonContinuousLayoutDivider(),
  293. true);
  294. } else {
  295. setNonContinuousLayoutDivider(nonContinuousLayoutDivider, true);
  296. }
  297. // focus forward traversal key
  298. if (managingFocusForwardTraversalKeys==null) {
  299. managingFocusForwardTraversalKeys = new TreeSet();
  300. managingFocusForwardTraversalKeys.add(
  301. KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
  302. }
  303. splitPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
  304. managingFocusForwardTraversalKeys);
  305. // focus backward traversal key
  306. if (managingFocusBackwardTraversalKeys==null) {
  307. managingFocusBackwardTraversalKeys = new TreeSet();
  308. managingFocusBackwardTraversalKeys.add(
  309. KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK));
  310. }
  311. splitPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
  312. managingFocusBackwardTraversalKeys);
  313. }
  314. /**
  315. * Installs the event listeners for the UI.
  316. */
  317. protected void installListeners() {
  318. if ((propertyChangeListener = createPropertyChangeListener()) !=
  319. null) {
  320. splitPane.addPropertyChangeListener(propertyChangeListener);
  321. }
  322. if ((focusListener = createFocusListener()) != null) {
  323. splitPane.addFocusListener(focusListener);
  324. }
  325. }
  326. /**
  327. * Installs the keyboard actions for the UI.
  328. */
  329. protected void installKeyboardActions() {
  330. InputMap km = getInputMap(JComponent.
  331. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  332. SwingUtilities.replaceUIInputMap(splitPane, JComponent.
  333. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  334. km);
  335. LazyActionMap.installLazyActionMap(splitPane, BasicSplitPaneUI.class,
  336. "SplitPane.actionMap");
  337. }
  338. InputMap getInputMap(int condition) {
  339. if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
  340. return (InputMap)DefaultLookup.get(splitPane, this,
  341. "SplitPane.ancestorInputMap");
  342. }
  343. return null;
  344. }
  345. /**
  346. * Uninstalls the UI.
  347. */
  348. public void uninstallUI(JComponent c) {
  349. uninstallKeyboardActions();
  350. uninstallListeners();
  351. uninstallDefaults();
  352. dividerLocationIsSet = false;
  353. dividerKeyboardResize = false;
  354. splitPane = null;
  355. }
  356. /**
  357. * Uninstalls the UI defaults.
  358. */
  359. protected void uninstallDefaults() {
  360. if(splitPane.getLayout() == layoutManager) {
  361. splitPane.setLayout(null);
  362. }
  363. if(nonContinuousLayoutDivider != null) {
  364. splitPane.remove(nonContinuousLayoutDivider);
  365. }
  366. LookAndFeel.uninstallBorder(splitPane);
  367. Border b = divider.getBorder();
  368. if (b instanceof UIResource) {
  369. divider.setBorder(null);
  370. }
  371. splitPane.remove(divider);
  372. divider.setBasicSplitPaneUI(null);
  373. layoutManager = null;
  374. divider = null;
  375. nonContinuousLayoutDivider = null;
  376. setNonContinuousLayoutDivider(null);
  377. // sets the focus forward and backward traversal keys to null
  378. // to restore the defaults
  379. splitPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);
  380. splitPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);
  381. }
  382. /**
  383. * Uninstalls the event listeners for the UI.
  384. */
  385. protected void uninstallListeners() {
  386. if (propertyChangeListener != null) {
  387. splitPane.removePropertyChangeListener(propertyChangeListener);
  388. propertyChangeListener = null;
  389. }
  390. if (focusListener != null) {
  391. splitPane.removeFocusListener(focusListener);
  392. focusListener = null;
  393. }
  394. keyboardUpLeftListener = null;
  395. keyboardDownRightListener = null;
  396. keyboardHomeListener = null;
  397. keyboardEndListener = null;
  398. keyboardResizeToggleListener = null;
  399. handler = null;
  400. }
  401. /**
  402. * Uninstalls the keyboard actions for the UI.
  403. */
  404. protected void uninstallKeyboardActions() {
  405. SwingUtilities.replaceUIActionMap(splitPane, null);
  406. SwingUtilities.replaceUIInputMap(splitPane, JComponent.
  407. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  408. null);
  409. }
  410. /**
  411. * Creates a PropertyChangeListener for the JSplitPane UI.
  412. */
  413. protected PropertyChangeListener createPropertyChangeListener() {
  414. return getHandler();
  415. }
  416. private Handler getHandler() {
  417. if (handler == null) {
  418. handler = new Handler();
  419. }
  420. return handler;
  421. }
  422. /**
  423. * Creates a FocusListener for the JSplitPane UI.
  424. */
  425. protected FocusListener createFocusListener() {
  426. return getHandler();
  427. }
  428. /**
  429. * As of Java 2 platform v1.3 this method is no
  430. * longer used. Subclassers previously using this method should
  431. * instead create an Action wrapping the ActionListener, and register
  432. * that Action by overriding <code>installKeyboardActions</code> and
  433. * placing the Action in the SplitPane's ActionMap. Please refer to
  434. * the key bindings specification for further details.
  435. * <p>
  436. * Creates a ActionListener for the JSplitPane UI that listens for
  437. * specific key presses.
  438. *
  439. * @deprecated As of Java 2 platform v1.3.
  440. */
  441. @Deprecated
  442. protected ActionListener createKeyboardUpLeftListener() {
  443. return new KeyboardUpLeftHandler();
  444. }
  445. /**
  446. * As of Java 2 platform v1.3 this method is no
  447. * longer used. Subclassers previously using this method should
  448. * instead create an Action wrapping the ActionListener, and register
  449. * that Action by overriding <code>installKeyboardActions</code> and
  450. * placing the Action in the SplitPane's ActionMap. Please refer to
  451. * the key bindings specification for further details.
  452. * <p>
  453. * Creates a ActionListener for the JSplitPane UI that listens for
  454. * specific key presses.
  455. *
  456. * @deprecated As of Java 2 platform v1.3.
  457. */
  458. @Deprecated
  459. protected ActionListener createKeyboardDownRightListener() {
  460. return new KeyboardDownRightHandler();
  461. }
  462. /**
  463. * As of Java 2 platform v1.3 this method is no
  464. * longer used. Subclassers previously using this method should
  465. * instead create an Action wrapping the ActionListener, and register
  466. * that Action by overriding <code>installKeyboardActions</code> and
  467. * placing the Action in the SplitPane's ActionMap. Please refer to
  468. * the key bindings specification for further details.
  469. * <p>
  470. * Creates a ActionListener for the JSplitPane UI that listens for
  471. * specific key presses.
  472. *
  473. * @deprecated As of Java 2 platform v1.3.
  474. */
  475. @Deprecated
  476. protected ActionListener createKeyboardHomeListener() {
  477. return new KeyboardHomeHandler();
  478. }
  479. /**
  480. * As of Java 2 platform v1.3 this method is no
  481. * longer used. Subclassers previously using this method should
  482. * instead create an Action wrapping the ActionListener, and register
  483. * that Action by overriding <code>installKeyboardActions</code> and
  484. * placing the Action in the SplitPane's ActionMap. Please refer to
  485. * the key bindings specification for further details.
  486. * <p>
  487. * Creates a ActionListener for the JSplitPane UI that listens for
  488. * specific key presses.
  489. *
  490. * @deprecated As of Java 2 platform v1.3.
  491. */
  492. @Deprecated
  493. protected ActionListener createKeyboardEndListener() {
  494. return new KeyboardEndHandler();
  495. }
  496. /**
  497. * As of Java 2 platform v1.3 this method is no
  498. * longer used. Subclassers previously using this method should
  499. * instead create an Action wrapping the ActionListener, and register
  500. * that Action by overriding <code>installKeyboardActions</code> and
  501. * placing the Action in the SplitPane's ActionMap. Please refer to
  502. * the key bindings specification for further details.
  503. * <p>
  504. * Creates a ActionListener for the JSplitPane UI that listens for
  505. * specific key presses.
  506. *
  507. * @deprecated As of Java 2 platform v1.3.
  508. */
  509. @Deprecated
  510. protected ActionListener createKeyboardResizeToggleListener() {
  511. return new KeyboardResizeToggleHandler();
  512. }
  513. /**
  514. * Returns the orientation for the JSplitPane.
  515. */
  516. public int getOrientation() {
  517. return orientation;
  518. }
  519. /**
  520. * Set the orientation for the JSplitPane.
  521. */
  522. public void setOrientation(int orientation) {
  523. this.orientation = orientation;
  524. }
  525. /**
  526. * Determines wether the JSplitPane is set to use a continuous layout.
  527. */
  528. public boolean isContinuousLayout() {
  529. return continuousLayout;
  530. }
  531. /**
  532. * Turn continuous layout on/off.
  533. */
  534. public void setContinuousLayout(boolean b) {
  535. continuousLayout = b;
  536. }
  537. /**
  538. * Returns the last drag location of the JSplitPane.
  539. */
  540. public int getLastDragLocation() {
  541. return lastDragLocation;
  542. }
  543. /**
  544. * Set the last drag location of the JSplitPane.
  545. */
  546. public void setLastDragLocation(int l) {
  547. lastDragLocation = l;
  548. }
  549. /**
  550. * @return increment via keyboard methods.
  551. */
  552. int getKeyboardMoveIncrement() {
  553. return KEYBOARD_DIVIDER_MOVE_OFFSET;
  554. }
  555. /**
  556. * Implementation of the PropertyChangeListener
  557. * that the JSplitPane UI uses.
  558. * <p>
  559. * This class should be treated as a "protected" inner class.
  560. * Instantiate it only within subclasses of BasicSplitPaneUI.
  561. */
  562. public class PropertyHandler implements PropertyChangeListener
  563. {
  564. // NOTE: This class exists only for backward compatability. All
  565. // its functionality has been moved into Handler. If you need to add
  566. // new functionality add it to the Handler, but make sure this
  567. // class calls into the Handler.
  568. /**
  569. * Messaged from the <code>JSplitPane</code> the receiver is
  570. * contained in. May potentially reset the layout manager and cause a
  571. * <code>validate</code> to be sent.
  572. */
  573. public void propertyChange(PropertyChangeEvent e) {
  574. getHandler().propertyChange(e);
  575. }
  576. }
  577. /**
  578. * Implementation of the FocusListener that the JSplitPane UI uses.
  579. * <p>
  580. * This class should be treated as a "protected" inner class.
  581. * Instantiate it only within subclasses of BasicSplitPaneUI.
  582. */
  583. public class FocusHandler extends FocusAdapter
  584. {
  585. // NOTE: This class exists only for backward compatability. All
  586. // its functionality has been moved into Handler. If you need to add
  587. // new functionality add it to the Handler, but make sure this
  588. // class calls into the Handler.
  589. public void focusGained(FocusEvent ev) {
  590. getHandler().focusGained(ev);
  591. }
  592. public void focusLost(FocusEvent ev) {
  593. getHandler().focusLost(ev);
  594. }
  595. }
  596. /**
  597. * Implementation of an ActionListener that the JSplitPane UI uses for
  598. * handling specific key presses.
  599. * <p>
  600. * This class should be treated as a "protected" inner class.
  601. * Instantiate it only within subclasses of BasicSplitPaneUI.
  602. */
  603. public class KeyboardUpLeftHandler implements ActionListener
  604. {
  605. public void actionPerformed(ActionEvent ev) {
  606. if (dividerKeyboardResize) {
  607. splitPane.setDividerLocation(Math.max(0,getDividerLocation
  608. (splitPane) - getKeyboardMoveIncrement()));
  609. }
  610. }
  611. }
  612. /**
  613. * Implementation of an ActionListener that the JSplitPane UI uses for
  614. * handling specific key presses.
  615. * <p>
  616. * This class should be treated as a "protected" inner class.
  617. * Instantiate it only within subclasses of BasicSplitPaneUI.
  618. */
  619. public class KeyboardDownRightHandler implements ActionListener
  620. {
  621. public void actionPerformed(ActionEvent ev) {
  622. if (dividerKeyboardResize) {
  623. splitPane.setDividerLocation(getDividerLocation(splitPane) +
  624. getKeyboardMoveIncrement());
  625. }
  626. }
  627. }
  628. /**
  629. * Implementation of an ActionListener that the JSplitPane UI uses for
  630. * handling specific key presses.
  631. * <p>
  632. * This class should be treated as a "protected" inner class.
  633. * Instantiate it only within subclasses of BasicSplitPaneUI.
  634. */
  635. public class KeyboardHomeHandler implements ActionListener
  636. {
  637. public void actionPerformed(ActionEvent ev) {
  638. if (dividerKeyboardResize) {
  639. splitPane.setDividerLocation(0);
  640. }
  641. }
  642. }
  643. /**
  644. * Implementation of an ActionListener that the JSplitPane UI uses for
  645. * handling specific key presses.
  646. * <p>
  647. * This class should be treated as a "protected" inner class.
  648. * Instantiate it only within subclasses of BasicSplitPaneUI.
  649. */
  650. public class KeyboardEndHandler implements ActionListener
  651. {
  652. public void actionPerformed(ActionEvent ev) {
  653. if (dividerKeyboardResize) {
  654. Insets insets = splitPane.getInsets();
  655. int bottomI = (insets != null) ? insets.bottom : 0;
  656. int rightI = (insets != null) ? insets.right : 0;
  657. if (orientation == JSplitPane.VERTICAL_SPLIT) {
  658. splitPane.setDividerLocation(splitPane.getHeight() -
  659. bottomI);
  660. }
  661. else {
  662. splitPane.setDividerLocation(splitPane.getWidth() -
  663. rightI);
  664. }
  665. }
  666. }
  667. }
  668. /**
  669. * Implementation of an ActionListener that the JSplitPane UI uses for
  670. * handling specific key presses.
  671. * <p>
  672. * This class should be treated as a "protected" inner class.
  673. * Instantiate it only within subclasses of BasicSplitPaneUI.
  674. */
  675. public class KeyboardResizeToggleHandler implements ActionListener
  676. {
  677. public void actionPerformed(ActionEvent ev) {
  678. if (!dividerKeyboardResize) {
  679. splitPane.requestFocus();
  680. }
  681. }
  682. }
  683. /**
  684. * Returns the divider between the top Components.
  685. */
  686. public BasicSplitPaneDivider getDivider() {
  687. return divider;
  688. }
  689. /**
  690. * Returns the default non continuous layout divider, which is an
  691. * instanceof Canvas that fills the background in dark gray.
  692. */
  693. protected Component createDefaultNonContinuousLayoutDivider() {
  694. return new Canvas() {
  695. public void paint(Graphics g) {
  696. if(!isContinuousLayout() && getLastDragLocation() != -1) {
  697. Dimension size = splitPane.getSize();
  698. g.setColor(dividerDraggingColor);
  699. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  700. g.fillRect(0, 0, dividerSize - 1, size.height - 1);
  701. } else {
  702. g.fillRect(0, 0, size.width - 1, dividerSize - 1);
  703. }
  704. }
  705. }
  706. };
  707. }
  708. /**
  709. * Sets the divider to use when the splitPane is configured to
  710. * not continuously layout. This divider will only be used during a
  711. * dragging session. It is recommended that the passed in component
  712. * be a heavy weight.
  713. */
  714. protected void setNonContinuousLayoutDivider(Component newDivider) {
  715. setNonContinuousLayoutDivider(newDivider, true);
  716. }
  717. /**
  718. * Sets the divider to use.
  719. */
  720. protected void setNonContinuousLayoutDivider(Component newDivider,
  721. boolean rememberSizes) {
  722. rememberPaneSizes = rememberSizes;
  723. if(nonContinuousLayoutDivider != null && splitPane != null) {
  724. splitPane.remove(nonContinuousLayoutDivider);
  725. }
  726. nonContinuousLayoutDivider = newDivider;
  727. }
  728. private void addHeavyweightDivider() {
  729. if(nonContinuousLayoutDivider != null && splitPane != null) {
  730. /* Needs to remove all the components and re-add them! YECK! */
  731. // This is all done so that the nonContinuousLayoutDivider will
  732. // be drawn on top of the other components, without this, one
  733. // of the heavyweights will draw over the divider!
  734. Component leftC = splitPane.getLeftComponent();
  735. Component rightC = splitPane.getRightComponent();
  736. int lastLocation = splitPane.
  737. getDividerLocation();
  738. if(leftC != null)
  739. splitPane.setLeftComponent(null);
  740. if(rightC != null)
  741. splitPane.setRightComponent(null);
  742. splitPane.remove(divider);
  743. splitPane.add(nonContinuousLayoutDivider, BasicSplitPaneUI.
  744. NON_CONTINUOUS_DIVIDER,
  745. splitPane.getComponentCount());
  746. splitPane.setLeftComponent(leftC);
  747. splitPane.setRightComponent(rightC);
  748. splitPane.add(divider, JSplitPane.DIVIDER);
  749. if(rememberPaneSizes) {
  750. splitPane.setDividerLocation(lastLocation);
  751. }
  752. }
  753. }
  754. /**
  755. * Returns the divider to use when the splitPane is configured to
  756. * not continuously layout. This divider will only be used during a
  757. * dragging session.
  758. */
  759. public Component getNonContinuousLayoutDivider() {
  760. return nonContinuousLayoutDivider;
  761. }
  762. /**
  763. * Returns the splitpane this instance is currently contained
  764. * in.
  765. */
  766. public JSplitPane getSplitPane() {
  767. return splitPane;
  768. }
  769. /**
  770. * Creates the default divider.
  771. */
  772. public BasicSplitPaneDivider createDefaultDivider() {
  773. return new BasicSplitPaneDivider(this);
  774. }
  775. /**
  776. * Messaged to reset the preferred sizes.
  777. */
  778. public void resetToPreferredSizes(JSplitPane jc) {
  779. if(splitPane != null) {
  780. layoutManager.resetToPreferredSizes();
  781. splitPane.revalidate();
  782. splitPane.repaint();
  783. }
  784. }
  785. /**
  786. * Sets the location of the divider to location.
  787. */
  788. public void setDividerLocation(JSplitPane jc, int location) {
  789. if (!ignoreDividerLocationChange) {
  790. dividerLocationIsSet = true;
  791. splitPane.revalidate();
  792. splitPane.repaint();
  793. if (keepHidden) {
  794. Insets insets = splitPane.getInsets();
  795. int orientation = splitPane.getOrientation();
  796. if ((orientation == JSplitPane.VERTICAL_SPLIT &&
  797. location != insets.top &&
  798. location != splitPane.getHeight()-divider.getHeight()-insets.top) ||
  799. (orientation == JSplitPane.HORIZONTAL_SPLIT &&
  800. location != insets.left &&
  801. location != splitPane.getWidth()-divider.getWidth()-insets.left)) {
  802. setKeepHidden(false);
  803. }
  804. }
  805. }
  806. else {
  807. ignoreDividerLocationChange = false;
  808. }
  809. }
  810. /**
  811. * Returns the location of the divider, which may differ from what
  812. * the splitpane thinks the location of the divider is.
  813. */
  814. public int getDividerLocation(JSplitPane jc) {
  815. if(orientation == JSplitPane.HORIZONTAL_SPLIT)
  816. return divider.getLocation().x;
  817. return divider.getLocation().y;
  818. }
  819. /**
  820. * Gets the minimum location of the divider.
  821. */
  822. public int getMinimumDividerLocation(JSplitPane jc) {
  823. int minLoc = 0;
  824. Component leftC = splitPane.getLeftComponent();
  825. if ((leftC != null) && (leftC.isVisible())) {
  826. Insets insets = splitPane.getInsets();
  827. Dimension minSize = leftC.getMinimumSize();
  828. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  829. minLoc = minSize.width;
  830. } else {
  831. minLoc = minSize.height;
  832. }
  833. if(insets != null) {
  834. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  835. minLoc += insets.left;
  836. } else {
  837. minLoc += insets.top;
  838. }
  839. }
  840. }
  841. return minLoc;
  842. }
  843. /**
  844. * Gets the maximum location of the divider.
  845. */
  846. public int getMaximumDividerLocation(JSplitPane jc) {
  847. Dimension splitPaneSize = splitPane.getSize();
  848. int maxLoc = 0;
  849. Component rightC = splitPane.getRightComponent();
  850. if (rightC != null) {
  851. Insets insets = splitPane.getInsets();
  852. Dimension minSize = new Dimension(0, 0);
  853. if (rightC.isVisible()) {
  854. minSize = rightC.getMinimumSize();
  855. }
  856. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  857. maxLoc = splitPaneSize.width - minSize.width;
  858. } else {
  859. maxLoc = splitPaneSize.height - minSize.height;
  860. }
  861. maxLoc -= dividerSize;
  862. if(insets != null) {
  863. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  864. maxLoc -= insets.right;
  865. } else {
  866. maxLoc -= insets.top;
  867. }
  868. }
  869. }
  870. return Math.max(getMinimumDividerLocation(splitPane), maxLoc);
  871. }
  872. /**
  873. * Messaged after the JSplitPane the receiver is providing the look
  874. * and feel for paints its children.
  875. */
  876. public void finishedPaintingChildren(JSplitPane jc, Graphics g) {
  877. if(jc == splitPane && getLastDragLocation() != -1 &&
  878. !isContinuousLayout() && !draggingHW) {
  879. Dimension size = splitPane.getSize();
  880. g.setColor(dividerDraggingColor);
  881. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  882. g.fillRect(getLastDragLocation(), 0, dividerSize - 1,
  883. size.height - 1);
  884. } else {
  885. g.fillRect(0, lastDragLocation, size.width - 1,
  886. dividerSize - 1);
  887. }
  888. }
  889. }
  890. /**
  891. * Messaged to paint the look and feel.
  892. */
  893. public void paint(Graphics g, JComponent jc) {
  894. if (!painted && splitPane.getDividerLocation()<0) {
  895. ignoreDividerLocationChange = true;
  896. splitPane.setDividerLocation(getDividerLocation(splitPane));
  897. }
  898. painted = true;
  899. }
  900. /**
  901. * Returns the preferred size for the passed in component,
  902. * This is passed off to the current layoutmanager.
  903. */
  904. public Dimension getPreferredSize(JComponent jc) {
  905. if(splitPane != null)
  906. return layoutManager.preferredLayoutSize(splitPane);
  907. return new Dimension(0, 0);
  908. }
  909. /**
  910. * Returns the minimum size for the passed in component,
  911. * This is passed off to the current layoutmanager.
  912. */
  913. public Dimension getMinimumSize(JComponent jc) {
  914. if(splitPane != null)
  915. return layoutManager.minimumLayoutSize(splitPane);
  916. return new Dimension(0, 0);
  917. }
  918. /**
  919. * Returns the maximum size for the passed in component,
  920. * This is passed off to the current layoutmanager.
  921. */
  922. public Dimension getMaximumSize(JComponent jc) {
  923. if(splitPane != null)
  924. return layoutManager.maximumLayoutSize(splitPane);
  925. return new Dimension(0, 0);
  926. }
  927. /**
  928. * Returns the insets. The insets are returned from the border insets
  929. * of the current border.
  930. */
  931. public Insets getInsets(JComponent jc) {
  932. return null;
  933. }
  934. /**
  935. * Resets the layout manager based on orientation and messages it
  936. * with invalidateLayout to pull in appropriate Components.
  937. */
  938. protected void resetLayoutManager() {
  939. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  940. layoutManager = new BasicHorizontalLayoutManager(0);
  941. } else {
  942. layoutManager = new BasicHorizontalLayoutManager(1);
  943. }
  944. splitPane.setLayout(layoutManager);
  945. layoutManager.updateComponents();
  946. splitPane.revalidate();
  947. splitPane.repaint();
  948. }
  949. /**
  950. * Set the value to indicate if one of the splitpane sides is expanded.
  951. */
  952. void setKeepHidden(boolean keepHidden) {
  953. this.keepHidden = keepHidden;
  954. }
  955. /**
  956. * The value returned indicates if one of the splitpane sides is expanded.
  957. * @return true if one of the splitpane sides is expanded, false otherwise.
  958. */
  959. private boolean getKeepHidden() {
  960. return keepHidden;
  961. }
  962. /**
  963. * Should be messaged before the dragging session starts, resets
  964. * lastDragLocation and dividerSize.
  965. */
  966. protected void startDragging() {
  967. Component leftC = splitPane.getLeftComponent();
  968. Component rightC = splitPane.getRightComponent();
  969. ComponentPeer cPeer;
  970. beginDragDividerLocation = getDividerLocation(splitPane);
  971. draggingHW = false;
  972. if(leftC != null && (cPeer = leftC.getPeer()) != null &&
  973. !(cPeer instanceof LightweightPeer)) {
  974. draggingHW = true;
  975. } else if(rightC != null && (cPeer = rightC.getPeer()) != null
  976. && !(cPeer instanceof LightweightPeer)) {
  977. draggingHW = true;
  978. }
  979. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  980. setLastDragLocation(divider.getBounds().x);
  981. dividerSize = divider.getSize().width;
  982. if(!isContinuousLayout() && draggingHW) {
  983. nonContinuousLayoutDivider.setBounds
  984. (getLastDragLocation(), 0, dividerSize,
  985. splitPane.getHeight());
  986. addHeavyweightDivider();
  987. }
  988. } else {
  989. setLastDragLocation(divider.getBounds().y);
  990. dividerSize = divider.getSize().height;
  991. if(!isContinuousLayout() && draggingHW) {
  992. nonContinuousLayoutDivider.setBounds
  993. (0, getLastDragLocation(), splitPane.getWidth(),
  994. dividerSize);
  995. addHeavyweightDivider();
  996. }
  997. }
  998. }
  999. /**
  1000. * Messaged during a dragging session to move the divider to the
  1001. * passed in location. If continuousLayout is true the location is
  1002. * reset and the splitPane validated.
  1003. */
  1004. protected void dragDividerTo(int location) {
  1005. if(getLastDragLocation() != location) {
  1006. if(isContinuousLayout()) {
  1007. splitPane.setDividerLocation(location);
  1008. setLastDragLocation(location);
  1009. } else {
  1010. int lastLoc = getLastDragLocation();
  1011. setLastDragLocation(location);
  1012. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  1013. if(draggingHW) {
  1014. nonContinuousLayoutDivider.setLocation(
  1015. getLastDragLocation(), 0);
  1016. } else {
  1017. int splitHeight = splitPane.getHeight();
  1018. splitPane.repaint(lastLoc, 0, dividerSize,
  1019. splitHeight);
  1020. splitPane.repaint(location, 0, dividerSize,
  1021. splitHeight);
  1022. }
  1023. } else {
  1024. if(draggingHW) {
  1025. nonContinuousLayoutDivider.setLocation(0,
  1026. getLastDragLocation());
  1027. } else {
  1028. int splitWidth = splitPane.getWidth();
  1029. splitPane.repaint(0, lastLoc, splitWidth,
  1030. dividerSize);
  1031. splitPane.repaint(0, location, splitWidth,
  1032. dividerSize);
  1033. }
  1034. }
  1035. }
  1036. }
  1037. }
  1038. /**
  1039. * Messaged to finish the dragging session. If not continuous display
  1040. * the dividers location will be reset.
  1041. */
  1042. protected void finishDraggingTo(int location) {
  1043. dragDividerTo(location);
  1044. setLastDragLocation(-1);
  1045. if(!isContinuousLayout()) {
  1046. Component leftC = splitPane.getLeftComponent();
  1047. Rectangle leftBounds = leftC.getBounds();
  1048. if (draggingHW) {
  1049. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  1050. nonContinuousLayoutDivider.setLocation(-dividerSize, 0);
  1051. }
  1052. else {
  1053. nonContinuousLayoutDivider.setLocation(0, -dividerSize);
  1054. }
  1055. splitPane.remove(nonContinuousLayoutDivider);
  1056. }
  1057. splitPane.setDividerLocation(location);
  1058. }
  1059. }
  1060. /**
  1061. * As of Java 2 platform v1.3 this method is no longer used. Instead
  1062. * you should set the border on the divider.
  1063. * <p>
  1064. * Returns the width of one side of the divider border.
  1065. *
  1066. * @deprecated As of Java 2 platform v1.3, instead set the border on the
  1067. * divider.
  1068. */
  1069. @Deprecated
  1070. protected int getDividerBorderSize() {
  1071. return 1;
  1072. }
  1073. /**
  1074. * LayoutManager for JSplitPanes that have an orientation of
  1075. * HORIZONTAL_SPLIT.
  1076. */
  1077. public class BasicHorizontalLayoutManager implements LayoutManager2
  1078. {
  1079. /* left, right, divider. (in this exact order) */
  1080. protected int[] sizes;
  1081. protected Component[] components;
  1082. /** Size of the splitpane the last time laid out. */
  1083. private int lastSplitPaneSize;
  1084. /** True if resetToPreferredSizes has been invoked. */
  1085. private boolean doReset;
  1086. /** Axis, 0 for horizontal, or 1 for veritcal. */
  1087. private int axis;
  1088. BasicHorizontalLayoutManager() {
  1089. this(0);
  1090. }
  1091. BasicHorizontalLayoutManager(int axis) {
  1092. this.axis = axis;
  1093. components = new Component[3];
  1094. components[0] = components[1] = components[2] = null;
  1095. sizes = new int[3];
  1096. }
  1097. //
  1098. // LayoutManager
  1099. //
  1100. /**
  1101. * Does the actual layout.
  1102. */
  1103. public void layoutContainer(Container container) {
  1104. Dimension containerSize = container.getSize();
  1105. // If the splitpane has a zero size then no op out of here.
  1106. // If we execute this function now, we're going to cause ourselves
  1107. // much grief.
  1108. if (containerSize.height <= 0 || containerSize.width <= 0 ) {
  1109. lastSplitPaneSize = 0;
  1110. return;
  1111. }
  1112. int spDividerLocation = splitPane.getDividerLocation();
  1113. Insets insets = splitPane.getInsets();
  1114. int availableSize = getAvailableSize(containerSize,
  1115. insets);
  1116. int newSize = getSizeForPrimaryAxis(containerSize);
  1117. int beginLocation = getDividerLocation(splitPane);
  1118. int dOffset = getSizeForPrimaryAxis(insets, true);
  1119. Dimension dSize = (components[2] == null) ? null :
  1120. components[2].getPreferredSize();
  1121. if ((doReset && !dividerLocationIsSet) || spDividerLocation < 0) {
  1122. resetToPreferredSizes(availableSize);
  1123. }
  1124. else if (lastSplitPaneSize <= 0 ||
  1125. availableSize == lastSplitPaneSize || !painted ||
  1126. (dSize != null &&
  1127. getSizeForPrimaryAxis(dSize) != sizes[2])) {
  1128. if (dSize != null) {
  1129. sizes[2] = getSizeForPrimaryAxis(dSize);
  1130. }
  1131. else {
  1132. sizes[2] = 0;
  1133. }
  1134. setDividerLocation(spDividerLocation - dOffset, availableSize);
  1135. dividerLocationIsSet = false;
  1136. }
  1137. else if (availableSize != lastSplitPaneSize) {
  1138. distributeSpace(availableSize - lastSplitPaneSize,
  1139. getKeepHidden());
  1140. }
  1141. doReset = false;
  1142. dividerLocationIsSet = false;
  1143. lastSplitPaneSize = availableSize;
  1144. // Reset the bounds of each component
  1145. int nextLocation = getInitialLocation(insets);
  1146. int counter = 0;
  1147. while (counter < 3) {
  1148. if (components[counter] != null &&
  1149. components[counter].isVisible()) {
  1150. setComponentToSize(components[counter], sizes[counter],
  1151. nextLocation, insets, containerSize);
  1152. nextLocation += sizes[counter];
  1153. }
  1154. switch (counter) {
  1155. case 0:
  1156. counter = 2;
  1157. break;
  1158. case 2:
  1159. counter = 1;
  1160. break;
  1161. case 1:
  1162. counter = 3;
  1163. break;
  1164. }
  1165. }
  1166. if (painted) {
  1167. // This is tricky, there is never a good time for us
  1168. // to push the value to the splitpane, painted appears to
  1169. // the best time to do it. What is really needed is
  1170. // notification that layout has completed.
  1171. int newLocation = getDividerLocation(splitPane);
  1172. if (newLocation != (spDividerLocation - dOffset)) {
  1173. int lastLocation = splitPane.getLastDividerLocation();
  1174. ignoreDividerLocationChange = true;
  1175. try {
  1176. splitPane.setDividerLocation(newLocation);
  1177. // This is not always needed, but is rather tricky
  1178. // to determine when... The case this is needed for
  1179. // is if the user sets the divider location to some
  1180. // bogus value, say 0, and the actual value is 1, the
  1181. // call to setDividerLocation(1) will preserve the
  1182. // old value of 0, when we really want the divider
  1183. // location value before the call. This is needed for
  1184. // the one touch buttons.
  1185. splitPane.setLastDividerLocation(lastLocation);
  1186. } finally {
  1187. ignoreDividerLocationChange = false;
  1188. }
  1189. }
  1190. }
  1191. }
  1192. /**
  1193. * Adds the component at place. Place must be one of
  1194. * JSplitPane.LEFT, RIGHT, TOP, BOTTOM, or null (for the
  1195. * divider).
  1196. */
  1197. public void addLayoutComponent(String place, Component component) {
  1198. boolean isValid = true;
  1199. if(place != null) {
  1200. if(place.equals(JSplitPane.DIVIDER)) {
  1201. /* Divider. */
  1202. components[2] = component;
  1203. sizes[2] = getSizeForPrimaryAxis(component.
  1204. getPreferredSize());
  1205. } else if(place.equals(JSplitPane.LEFT) ||
  1206. place.equals(JSplitPane.TOP)) {
  1207. components[0] = component;
  1208. sizes[0] = 0;
  1209. } else if(place.equals(JSplitPane.RIGHT) ||
  1210. place.equals(JSplitPane.BOTTOM)) {
  1211. components[1] = component;
  1212. sizes[1] = 0;
  1213. } else if(!place.equals(
  1214. BasicSplitPaneUI.NON_CONTINUOUS_DIVIDER))
  1215. isValid = false;
  1216. } else {
  1217. isValid = false;
  1218. }
  1219. if(!isValid)
  1220. throw new IllegalArgumentException("cannot add to layout: " +
  1221. "unknown constraint: " +
  1222. place);
  1223. doReset = true;
  1224. }
  1225. /**
  1226. * Returns the minimum size needed to contain the children.
  1227. * The width is the sum of all the childrens min widths and
  1228. * the height is the largest of the childrens minimum heights.
  1229. */
  1230. public Dimension minimumLayoutSize(Container container) {
  1231. int minPrimary = 0;
  1232. int minSecondary = 0;
  1233. Insets insets = splitPane.getInsets();
  1234. for (int counter=0; counter<3; counter++) {
  1235. if(components[counter] != null) {
  1236. Dimension minSize = components[counter].getMinimumSize();
  1237. int secSize = getSizeForSecondaryAxis(minSize);
  1238. minPrimary += getSizeForPrimaryAxis(minSize);
  1239. if(secSize > minSecondary)
  1240. minSecondary = secSize;
  1241. }
  1242. }
  1243. if(insets != null) {
  1244. minPrimary += getSizeForPrimaryAxis(insets, true) +
  1245. getSizeForPrimaryAxis(insets, false);
  1246. minSecondary += getSizeForSecondaryAxis(insets, true) +
  1247. getSizeForSecondaryAxis(insets, false);
  1248. }
  1249. if (axis == 0) {
  1250. return new Dimension(minPrimary, minSecondary);
  1251. }
  1252. return new Dimension(minSecondary, minPrimary);
  1253. }
  1254. /**
  1255. * Returns the preferred size needed to contain the children.
  1256. * The width is the sum of all the childrens preferred widths and
  1257. * the height is the largest of the childrens preferred heights.
  1258. */
  1259. public Dimension preferredLayoutSize(Container container) {
  1260. int prePrimary = 0;
  1261. int preSecondary = 0;
  1262. Insets insets = splitPane.getInsets();
  1263. for(int counter = 0; counter < 3; counter++) {
  1264. if(components[counter] != null) {
  1265. Dimension preSize = components[counter].
  1266. getPreferredSize();
  1267. int secSize = getSizeForSecondaryAxis(preSize);
  1268. prePrimary += getSizeForPrimaryAxis(preSize);
  1269. if(secSize > preSecondary)
  1270. preSecondary = secSize;
  1271. }
  1272. }
  1273. if(insets != null) {
  1274. prePrimary += getSizeForPrimaryAxis(insets, true) +
  1275. getSizeForPrimaryAxis(insets, false);
  1276. preSecondary += getSizeForSecondaryAxis(insets, true) +
  1277. getSizeForSecondaryAxis(insets, false);
  1278. }
  1279. if (axis == 0) {
  1280. return new Dimension(prePrimary, preSecondary);
  1281. }
  1282. return new Dimension(preSecondary, prePrimary);
  1283. }
  1284. /**
  1285. * Removes the specified component from our knowledge.
  1286. */
  1287. public void removeLayoutComponent(Component component) {
  1288. for(int counter = 0; counter < 3; counter++) {
  1289. if(components[counter] == component) {
  1290. components[counter] = null;
  1291. sizes[counter] = 0;
  1292. doReset = true;
  1293. }
  1294. }
  1295. }
  1296. //
  1297. // LayoutManager2
  1298. //
  1299. /**
  1300. * Adds the specified component to the layout, using the specified
  1301. * constraint object.
  1302. * @param comp the component to be added
  1303. * @param constraints where/how the component is added to the layout.
  1304. */
  1305. public void addLayoutComponent(Component comp, Object constraints) {
  1306. if ((constraints == null) || (constraints instanceof String)) {
  1307. addLayoutComponent((String)constraints, comp);
  1308. } else {
  1309. throw new IllegalArgumentException("cannot add to layout: " +
  1310. "constraint must be a " +
  1311. "string (or null)");
  1312. }
  1313. }
  1314. /**
  1315. * Returns the alignment along the x axis. This specifies how
  1316. * the component would like to be aligned relative to other
  1317. * components. The value should be a number between 0 and 1
  1318. * where 0 represents alignment along the origin, 1 is aligned
  1319. * the furthest away from the origin, 0.5 is centered, etc.
  1320. */
  1321. public float getLayoutAlignmentX(Container target) {
  1322. return 0.0f;
  1323. }
  1324. /**
  1325. * Returns the alignment along the y axis. This specifies how
  1326. * the component would like to be aligned relative to other
  1327. * components. The value should be a number between 0 and 1
  1328. * where 0 represents alignment along the origin, 1 is aligned
  1329. * the furthest away from the origin, 0.5 is centered, etc.
  1330. */
  1331. public float getLayoutAlignmentY(Container target) {
  1332. return 0.0f;
  1333. }
  1334. /**
  1335. * Does nothing. If the developer really wants to change the
  1336. * size of one of the views JSplitPane.resetToPreferredSizes should
  1337. * be messaged.
  1338. */
  1339. public void invalidateLayout(Container c) {
  1340. }
  1341. /**
  1342. * Returns the maximum layout size, which is Integer.MAX_VALUE
  1343. * in both directions.
  1344. */
  1345. public Dimension maximumLayoutSize(Container target) {
  1346. return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  1347. }
  1348. //
  1349. // New methods.
  1350. //
  1351. /**
  1352. * Marks the receiver so that the next time this instance is
  1353. * laid out it'll ask for the preferred sizes.
  1354. */
  1355. public void resetToPreferredSizes() {
  1356. doReset = true;
  1357. }
  1358. /**
  1359. * Resets the size of the Component at the passed in location.
  1360. */
  1361. protected void resetSizeAt(int index) {
  1362. sizes[index] = 0;
  1363. doReset = true;
  1364. }
  1365. /**
  1366. * Sets the sizes to <code>newSizes</code>.
  1367. */
  1368. protected void setSizes(int[] newSizes) {
  1369. System.arraycopy(newSizes, 0, sizes, 0, 3);
  1370. }
  1371. /**
  1372. * Returns the sizes of the components.
  1373. */
  1374. protected int[] getSizes() {
  1375. int[] retSizes = new int[3];
  1376. System.arraycopy(sizes, 0, retSizes, 0, 3);
  1377. return retSizes;
  1378. }
  1379. /**
  1380. * Returns the width of the passed in Components preferred size.
  1381. */
  1382. protected int getPreferredSizeOfComponent(Component c) {
  1383. return getSizeForPrimaryAxis(c.getPreferredSize());
  1384. }
  1385. /**
  1386. * Returns the width of the passed in Components minimum size.
  1387. */
  1388. int getMinimumSizeOfComponent(Component c) {
  1389. return getSizeForPrimaryAxis(c.getMinimumSize());
  1390. }
  1391. /**
  1392. * Returns the width of the passed in component.
  1393. */
  1394. protected int getSizeOfComponent(Component c) {
  1395. return getSizeForPrimaryAxis(c.getSize());
  1396. }
  1397. /**
  1398. * Returns the available width based on the container size and
  1399. * Insets.
  1400. */
  1401. protected int getAvailableSize(Dimension containerSize,
  1402. Insets insets) {
  1403. if(insets == null)
  1404. return getSizeForPrimaryAxis(containerSize);
  1405. return (getSizeForPrimaryAxis(containerSize) -
  1406. (getSizeForPrimaryAxis(insets, true) +
  1407. getSizeForPrimaryAxis(insets, false)));
  1408. }
  1409. /**
  1410. * Returns the left inset, unless the Insets are null in which case
  1411. * 0 is returned.
  1412. */
  1413. protected int getInitialLocation(Insets insets) {
  1414. if(insets != null)
  1415. return getSizeForPrimaryAxis(insets, true);
  1416. return 0;
  1417. }
  1418. /**
  1419. * Sets the width of the component c to be size, placing its
  1420. * x location at location, y to the insets.top and height
  1421. * to the containersize.height less the top and bottom insets.
  1422. */
  1423. protected void setComponentToSize(Component c, int size,
  1424. int location, Insets insets,
  1425. Dimension containerSize) {
  1426. if(insets != null) {
  1427. if (axis == 0) {
  1428. c.setBounds(location, insets.top, size,
  1429. containerSize.height -
  1430. (insets.top + insets.bottom));
  1431. }
  1432. else {
  1433. c.setBounds(insets.left, location, containerSize.width -
  1434. (insets.left + insets.right), size);
  1435. }
  1436. }
  1437. else {
  1438. if (axis == 0) {
  1439. c.setBounds(location, 0, size, containerSize.height);
  1440. }
  1441. else {
  1442. c.setBounds(0, location, containerSize.width, size);
  1443. }
  1444. }
  1445. }
  1446. /**
  1447. * If the axis == 0, the width is returned, otherwise the height.
  1448. */
  1449. int getSizeForPrimaryAxis(Dimension size) {
  1450. if (axis == 0) {
  1451. return size.width;
  1452. }
  1453. return size.height;
  1454. }
  1455. /**
  1456. * If the axis == 0, the width is returned, otherwise the height.
  1457. */
  1458. int getSizeForSecondaryAxis(Dimension size) {
  1459. if (axis == 0) {
  1460. return size.height;
  1461. }
  1462. return size.width;
  1463. }
  1464. /**
  1465. * Returns a particular value of the inset identified by the
  1466. * axis and <code>isTop</code><p>
  1467. * axis isTop
  1468. * 0 true - left
  1469. * 0 false - right
  1470. * 1 true - top
  1471. * 1 false - bottom
  1472. */
  1473. int getSizeForPrimaryAxis(Insets insets, boolean isTop) {
  1474. if (axis == 0) {
  1475. if (isTop) {
  1476. return insets.left;
  1477. }
  1478. return insets.right;
  1479. }
  1480. if (isTop) {
  1481. return insets.top;
  1482. }
  1483. return insets.bottom;
  1484. }
  1485. /**
  1486. * Returns a particular value of the inset identified by the
  1487. * axis and <code>isTop</code><p>
  1488. * axis isTop
  1489. * 0 true - left
  1490. * 0 false - right
  1491. * 1 true - top
  1492. * 1 false - bottom
  1493. */
  1494. int getSizeForSecondaryAxis(Insets insets, boolean isTop) {
  1495. if (axis == 0) {
  1496. if (isTop) {
  1497. return insets.top;
  1498. }
  1499. return insets.bottom;
  1500. }
  1501. if (isTop) {
  1502. return insets.left;
  1503. }
  1504. return insets.right;
  1505. }
  1506. /**
  1507. * Determines the components. This should be called whenever
  1508. * a new instance of this is installed into an existing
  1509. * SplitPane.
  1510. */
  1511. protected void updateComponents() {
  1512. Component comp;
  1513. comp = splitPane.getLeftComponent();
  1514. if(components[0] != comp) {
  1515. components[0] = comp;
  1516. if(comp == null) {
  1517. sizes[0] = 0;
  1518. } else {
  1519. sizes[0] = -1;
  1520. }
  1521. }
  1522. comp = splitPane.getRightComponent();
  1523. if(components[1] != comp) {
  1524. components[1] = comp;
  1525. if(comp == null) {
  1526. sizes[1] = 0;
  1527. } else {
  1528. sizes[1] = -1;
  1529. }
  1530. }
  1531. /* Find the divider. */
  1532. Component[] children = splitPane.getComponents();
  1533. Component oldDivider = components[2];
  1534. components[2] = null;
  1535. for(int counter = children.length - 1; counter >= 0; counter--) {
  1536. if(children[counter] != components[0] &&
  1537. children[counter] != components[1] &&
  1538. children[counter] != nonContinuousLayoutDivider) {
  1539. if(oldDivider != children[counter]) {
  1540. components[2] = children[counter];
  1541. } else {
  1542. components[2] = oldDivider;
  1543. }
  1544. break;
  1545. }
  1546. }
  1547. if(components[2] == null) {
  1548. sizes[2] = 0;
  1549. }
  1550. else {
  1551. sizes[2] = getSizeForPrimaryAxis(components[2].getPreferredSize());
  1552. }
  1553. }
  1554. /**
  1555. * Resets the size of the first component to <code>leftSize</code>,
  1556. * and the right component to the remainder of the space.
  1557. */
  1558. void setDividerLocation(int leftSize, int availableSize) {
  1559. boolean lValid = (components[0] != null &&
  1560. components[0].isVisible());
  1561. boolean rValid = (components[1] != null &&
  1562. components[1].isVisible());
  1563. boolean dValid = (components[2] != null &&
  1564. components[2].isVisible());
  1565. int max = availableSize;
  1566. if (dValid) {
  1567. max -= sizes[2];
  1568. }
  1569. leftSize = Math.max(0, Math.min(leftSize, max));
  1570. if (lValid) {
  1571. if (rValid) {
  1572. sizes[0] = leftSize;
  1573. sizes[1] = max - leftSize;
  1574. }
  1575. else {
  1576. sizes[0] = max;
  1577. sizes[1] = 0;
  1578. }
  1579. }
  1580. else if (rValid) {
  1581. sizes[1] = max;
  1582. sizes[0] = 0;
  1583. }
  1584. }
  1585. /**
  1586. * Returns an array of the minimum sizes of the components.
  1587. */
  1588. int[] getPreferredSizes() {
  1589. int[] retValue = new int[3];
  1590. for (int counter = 0; counter < 3; counter++) {
  1591. if (components[counter] != null &&
  1592. components[counter].isVisible()) {
  1593. retValue[counter] = getPreferredSizeOfComponent
  1594. (components[counter]);
  1595. }
  1596. else {
  1597. retValue[counter] = -1;
  1598. }
  1599. }
  1600. return retValue;
  1601. }
  1602. /**
  1603. * Returns an array of the minimum sizes of the components.
  1604. */
  1605. int[] getMinimumSizes() {
  1606. int[] retValue = new int[3];
  1607. for (int counter = 0; counter < 2; counter++) {
  1608. if (components[counter] != null &&
  1609. components[counter].isVisible()) {
  1610. retValue[counter] = getMinimumSizeOfComponent
  1611. (components[counter]);
  1612. }
  1613. else {
  1614. retValue[counter] = -1;
  1615. }
  1616. }
  1617. retValue[2] = (components[2] != null) ?
  1618. getMinimumSizeOfComponent(components[2]) : -1;
  1619. return retValue;
  1620. }
  1621. /**
  1622. * Resets the components to their preferred sizes.
  1623. */
  1624. void resetToPreferredSizes(int availableSize) {
  1625. // Set the sizes to the preferred sizes (if fits), otherwise
  1626. // set to min sizes and distribute any extra space.
  1627. int[] testSizes = getPreferredSizes();
  1628. int totalSize = 0;
  1629. for (int counter = 0; counter < 3; counter++) {
  1630. if (testSizes[counter] != -1) {
  1631. totalSize += testSizes[counter];
  1632. }
  1633. }
  1634. if (totalSize > availableSize) {
  1635. testSizes = getMinimumSizes();
  1636. totalSize = 0;
  1637. for (int counter = 0; counter < 3; counter++) {
  1638. if (testSizes[counter] != -1) {
  1639. totalSize += testSizes[counter];
  1640. }
  1641. }
  1642. }
  1643. setSizes(testSizes);
  1644. distributeSpace(availableSize - totalSize, false);
  1645. }
  1646. /**
  1647. * Distributes <code>space</code> between the two components
  1648. * (divider won't get any extra space) based on the weighting. This
  1649. * attempts to honor the min size of the components.
  1650. *
  1651. * @param keepHidden if true and one of the components is 0x0
  1652. * it gets none of the extra space
  1653. */
  1654. void distributeSpace(int space, boolean keepHidden) {
  1655. boolean lValid = (components[0] != null &&
  1656. components[0].isVisible());
  1657. boolean rValid = (components[1] != null &&
  1658. components[1].isVisible());
  1659. if (keepHidden) {
  1660. if (lValid && getSizeForPrimaryAxis(
  1661. components[0].getSize()) == 0) {
  1662. lValid = false;
  1663. if (rValid && getSizeForPrimaryAxis(
  1664. components[1].getSize()) == 0) {
  1665. // Both aren't valid, force them both to be valid
  1666. lValid = true;
  1667. }
  1668. }
  1669. else if (rValid && getSizeForPrimaryAxis(
  1670. components[1].getSize()) == 0) {
  1671. rValid = false;
  1672. }
  1673. }
  1674. if (lValid && rValid) {
  1675. double weight = splitPane.getResizeWeight();
  1676. int lExtra = (int)(weight * (double)space);
  1677. int rExtra = (space - lExtra);
  1678. sizes[0] += lExtra;
  1679. sizes[1] += rExtra;
  1680. int lMin = getMinimumSizeOfComponent(components[0]);
  1681. int rMin = getMinimumSizeOfComponent(components[1]);
  1682. boolean lMinValid = (sizes[0] >= lMin);
  1683. boolean rMinValid = (sizes[1] >= rMin);
  1684. if (!lMinValid && !rMinValid) {
  1685. if (sizes[0] < 0) {
  1686. sizes[1] += sizes[0];
  1687. sizes[0] = 0;
  1688. }
  1689. else if (sizes[1] < 0) {
  1690. sizes[0] += sizes[1];
  1691. sizes[1] = 0;
  1692. }
  1693. }
  1694. else if (!lMinValid) {
  1695. if (sizes[1] - (lMin - sizes[0]) < rMin) {
  1696. // both below min, just make sure > 0
  1697. if (sizes[0] < 0) {
  1698. sizes[1] += sizes[0];
  1699. sizes[0] = 0;
  1700. }
  1701. }
  1702. else {
  1703. sizes[1] -= (lMin - sizes[0]);
  1704. sizes[0] = lMin;
  1705. }
  1706. }
  1707. else if (!rMinValid) {
  1708. if (sizes[0] - (rMin - sizes[1]) < lMin) {
  1709. // both below min, just make sure > 0
  1710. if (sizes[1] < 0) {
  1711. sizes[0] += sizes[1];
  1712. sizes[1] = 0;
  1713. }
  1714. }
  1715. else {
  1716. sizes[0] -= (rMin - sizes[1]);
  1717. sizes[1] = rMin;
  1718. }
  1719. }
  1720. if (sizes[0] < 0) {
  1721. sizes[0] = 0;
  1722. }
  1723. if (sizes[1] < 0) {
  1724. sizes[1] = 0;
  1725. }
  1726. }
  1727. else if (lValid) {
  1728. sizes[0] = Math.max(0, sizes[0] + space);
  1729. }
  1730. else if (rValid) {
  1731. sizes[1] = Math.max(0, sizes[1] + space);
  1732. }
  1733. }
  1734. }
  1735. /**
  1736. * LayoutManager used for JSplitPanes with an orientation of
  1737. * VERTICAL_SPLIT.
  1738. * <p>
  1739. */
  1740. public class BasicVerticalLayoutManager extends
  1741. BasicHorizontalLayoutManager
  1742. {
  1743. public BasicVerticalLayoutManager() {
  1744. super(1);
  1745. }
  1746. }
  1747. private class Handler implements FocusListener, PropertyChangeListener {
  1748. //
  1749. // PropertyChangeListener
  1750. //
  1751. /**
  1752. * Messaged from the <code>JSplitPane</code> the receiver is
  1753. * contained in. May potentially reset the layout manager and cause a
  1754. * <code>validate</code> to be sent.
  1755. */
  1756. public void propertyChange(PropertyChangeEvent e) {
  1757. if(e.getSource() == splitPane) {
  1758. String changeName = e.getPropertyName();
  1759. if(changeName == JSplitPane.ORIENTATION_PROPERTY) {
  1760. orientation = splitPane.getOrientation();
  1761. resetLayoutManager();
  1762. } else if(changeName == JSplitPane.CONTINUOUS_LAYOUT_PROPERTY){
  1763. setContinuousLayout(splitPane.isContinuousLayout());
  1764. if(!isContinuousLayout()) {
  1765. if(nonContinuousLayoutDivider == null) {
  1766. setNonContinuousLayoutDivider(
  1767. createDefaultNonContinuousLayoutDivider(),
  1768. true);
  1769. } else if(nonContinuousLayoutDivider.getParent() ==
  1770. null) {
  1771. setNonContinuousLayoutDivider(
  1772. nonContinuousLayoutDivider,
  1773. true);
  1774. }
  1775. }
  1776. } else if(changeName == JSplitPane.DIVIDER_SIZE_PROPERTY){
  1777. divider.setDividerSize(splitPane.getDividerSize());
  1778. dividerSize = divider.getDividerSize();
  1779. splitPane.revalidate();
  1780. splitPane.repaint();
  1781. }
  1782. }
  1783. }
  1784. //
  1785. // FocusListener
  1786. //
  1787. public void focusGained(FocusEvent ev) {
  1788. dividerKeyboardResize = true;
  1789. splitPane.repaint();
  1790. }
  1791. public void focusLost(FocusEvent ev) {
  1792. dividerKeyboardResize = false;
  1793. splitPane.repaint();
  1794. }
  1795. }
  1796. private static class Actions extends UIAction {
  1797. private static final String NEGATIVE_INCREMENT = "negativeIncrement";
  1798. private static final String POSITIVE_INCREMENT = "positiveIncrement";
  1799. private static final String SELECT_MIN = "selectMin";
  1800. private static final String SELECT_MAX = "selectMax";
  1801. private static final String START_RESIZE = "startResize";
  1802. private static final String TOGGLE_FOCUS = "toggleFocus";
  1803. private static final String FOCUS_OUT_FORWARD = "focusOutForward";
  1804. private static final String FOCUS_OUT_BACKWARD = "focusOutBackward";
  1805. Actions(String key) {
  1806. super(key);
  1807. }
  1808. public void actionPerformed(ActionEvent ev) {
  1809. JSplitPane splitPane = (JSplitPane)ev.getSource();
  1810. BasicSplitPaneUI ui = (BasicSplitPaneUI)BasicLookAndFeel.
  1811. getUIOfType(splitPane.getUI(), BasicSplitPaneUI.class);
  1812. if (ui == null) {
  1813. return;
  1814. }
  1815. String key = getName();
  1816. if (key == NEGATIVE_INCREMENT) {
  1817. if (ui.dividerKeyboardResize) {
  1818. splitPane.setDividerLocation(Math.max(
  1819. 0, ui.getDividerLocation
  1820. (splitPane) - ui.getKeyboardMoveIncrement()));
  1821. }
  1822. }
  1823. else if (key == POSITIVE_INCREMENT) {
  1824. if (ui.dividerKeyboardResize) {
  1825. splitPane.setDividerLocation(
  1826. ui.getDividerLocation(splitPane) +
  1827. ui.getKeyboardMoveIncrement());
  1828. }
  1829. }
  1830. else if (key == SELECT_MIN) {
  1831. if (ui.dividerKeyboardResize) {
  1832. splitPane.setDividerLocation(0);
  1833. }
  1834. }
  1835. else if (key == SELECT_MAX) {
  1836. if (ui.dividerKeyboardResize) {
  1837. Insets insets = splitPane.getInsets();
  1838. int bottomI = (insets != null) ? insets.bottom : 0;
  1839. int rightI = (insets != null) ? insets.right : 0;
  1840. if (ui.orientation == JSplitPane.VERTICAL_SPLIT) {
  1841. splitPane.setDividerLocation(splitPane.getHeight() -
  1842. bottomI);
  1843. }
  1844. else {
  1845. splitPane.setDividerLocation(splitPane.getWidth() -
  1846. rightI);
  1847. }
  1848. }
  1849. }
  1850. else if (key == START_RESIZE) {
  1851. if (!ui.dividerKeyboardResize) {
  1852. splitPane.requestFocus();
  1853. } else {
  1854. JSplitPane parentSplitPane =
  1855. (JSplitPane)SwingUtilities.getAncestorOfClass(
  1856. JSplitPane.class, splitPane);
  1857. if (parentSplitPane!=null) {
  1858. parentSplitPane.requestFocus();
  1859. }
  1860. }
  1861. }
  1862. else if (key == TOGGLE_FOCUS) {
  1863. toggleFocus(splitPane);
  1864. }
  1865. else if (key == FOCUS_OUT_FORWARD) {
  1866. moveFocus(splitPane, 1);
  1867. }
  1868. else if (key == FOCUS_OUT_BACKWARD) {
  1869. moveFocus(splitPane, -1);
  1870. }
  1871. }
  1872. private void moveFocus(JSplitPane splitPane, int direction) {
  1873. Container rootAncestor = splitPane.getFocusCycleRootAncestor();
  1874. FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy();
  1875. Component focusOn = (direction > 0) ?
  1876. policy.getComponentAfter(rootAncestor, splitPane) :
  1877. policy.getComponentBefore(rootAncestor, splitPane);
  1878. HashSet focusFrom = new HashSet();
  1879. if (splitPane.isAncestorOf(focusOn)) {
  1880. do {
  1881. focusFrom.add(focusOn);
  1882. rootAncestor = focusOn.getFocusCycleRootAncestor();
  1883. policy = rootAncestor.getFocusTraversalPolicy();
  1884. focusOn = (direction > 0) ?
  1885. policy.getComponentAfter(rootAncestor, focusOn) :
  1886. policy.getComponentBefore(rootAncestor, focusOn);
  1887. } while (splitPane.isAncestorOf(focusOn) &&
  1888. !focusFrom.contains(focusOn));
  1889. }
  1890. if ( focusOn!=null && !splitPane.isAncestorOf(focusOn) ) {
  1891. focusOn.requestFocus();
  1892. }
  1893. }
  1894. private void toggleFocus(JSplitPane splitPane) {
  1895. Component left = splitPane.getLeftComponent();
  1896. Component right = splitPane.getRightComponent();
  1897. KeyboardFocusManager manager =
  1898. KeyboardFocusManager.getCurrentKeyboardFocusManager();
  1899. Component focus = manager.getFocusOwner();
  1900. Component focusOn = getNextSide(splitPane, focus);
  1901. if (focusOn != null) {
  1902. // don't change the focus if the new focused component belongs
  1903. // to the same splitpane and the same side
  1904. if ( focus!=null &&
  1905. ( (SwingUtilities.isDescendingFrom(focus, left) &&
  1906. SwingUtilities.isDescendingFrom(focusOn, left)) ||
  1907. (SwingUtilities.isDescendingFrom(focus, right) &&
  1908. SwingUtilities.isDescendingFrom(focusOn, right)) ) ) {
  1909. return;
  1910. }
  1911. BasicLookAndFeel.compositeRequestFocus(focusOn);
  1912. }
  1913. }
  1914. private Component getNextSide(JSplitPane splitPane, Component focus) {
  1915. Component left = splitPane.getLeftComponent();
  1916. Component right = splitPane.getRightComponent();
  1917. Component next = null;
  1918. if (focus!=null && SwingUtilities.isDescendingFrom(focus, left) &&
  1919. right!=null) {
  1920. next = getFirstAvailableComponent(right);
  1921. if (next != null) {
  1922. return next;
  1923. }
  1924. }
  1925. JSplitPane parentSplitPane = (JSplitPane)SwingUtilities.getAncestorOfClass(JSplitPane.class, splitPane);
  1926. if (parentSplitPane!=null) {
  1927. // focus next side of the parent split pane
  1928. next = getNextSide(parentSplitPane, focus);
  1929. } else {
  1930. next = getFirstAvailableComponent(left);
  1931. if (next == null) {
  1932. next = getFirstAvailableComponent(right);
  1933. }
  1934. }
  1935. return next;
  1936. }
  1937. private Component getFirstAvailableComponent(Component c) {
  1938. if (c!=null && c instanceof JSplitPane) {
  1939. JSplitPane sp = (JSplitPane)c;
  1940. Component left = getFirstAvailableComponent(sp.getLeftComponent());
  1941. if (left != null) {
  1942. c = left;
  1943. } else {
  1944. c = getFirstAvailableComponent(sp.getRightComponent());
  1945. }
  1946. }
  1947. return c;
  1948. }
  1949. }
  1950. }