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