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