1. /*
  2. * @(#)JSplitPane.java 1.70 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import javax.swing.border.Border;
  9. import javax.swing.plaf.*;
  10. import javax.accessibility.*;
  11. import java.awt.*;
  12. import java.io.ObjectOutputStream;
  13. import java.io.ObjectInputStream;
  14. import java.io.IOException;
  15. /**
  16. * <code>JSplitPane</code> is used to divide two (and only two)
  17. * <code>Component</code>s. The two <code>Component</code>s
  18. * are graphically divided based on the look and feel
  19. * implementation, and the two <code>Component</code>s can then be
  20. * interactively resized by the user.
  21. * Information on using <code>JSplitPane</code> is in
  22. * <a
  23. href="http://java.sun.com/docs/books/tutorial/uiswing/components/splitpane.html">How to Use Split Panes</a> in
  24. * <em>The Java Tutorial</em>.
  25. * <p>
  26. * The two <code>Component</code>s in a split pane can be aligned
  27. * left to right using
  28. * <code>JSplitPane.HORIZONTAL_SPLIT</code>, or top to bottom using
  29. * <code>JSplitPane.VERTICAL_SPLIT</code>.
  30. * The preferred way to change the size of the <code>Component</code>s
  31. * is to invoke
  32. * <code>setDividerLocation</code> where <code>location</code> is either
  33. * the new x or y position, depending on the orientation of the
  34. * <code>JSplitPane</code>.
  35. * <p>
  36. * To resize the <code>Component</code>s to their preferred sizes invoke
  37. * <code>resetToPreferredSizes</code>.
  38. * <p>
  39. * When the user is resizing the <code>Component</code>s the minimum
  40. * size of the <code>Components</code> is used to determine the
  41. * maximum/minimum position the <code>Component</code>s
  42. * can be set to. If the minimum size of the two
  43. * components is greater than the size of the split pane the divider
  44. * will not allow you to resize it. To alter the minimum size of a
  45. * <code>JComponent</code>, see {@link JComponent#setMinimumSize}.
  46. * <p>
  47. * When the user resizes the split pane the new space is distributed between
  48. * the two components based on the <code>resizeWeight</code> property.
  49. * A value of 0,
  50. * the default, indicates the right/bottom component gets all the space,
  51. * where as a value of 1 indicates the left/top component gets all the space.
  52. * <p>
  53. * For the keyboard keys used by this component in the standard Look and
  54. * Feel (L&F) renditions, see the
  55. * <a href="doc-files/Key-Index.html#JSplitPane"><code>JSplitPane</code> key assignments</a>.
  56. * <p>
  57. * <strong>Warning:</strong>
  58. * Serialized objects of this class will not be compatible with
  59. * future Swing releases. The current serialization support is
  60. * appropriate for short term storage or RMI between applications running
  61. * the same version of Swing. As of 1.4, support for long term storage
  62. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  63. * has been added to the <code>java.beans</code> package.
  64. * Please see {@link java.beans.XMLEncoder}.
  65. *
  66. * @see #setDividerLocation
  67. * @see #resetToPreferredSizes
  68. *
  69. * @version 1.70 01/23/03
  70. * @author Scott Violet
  71. */
  72. public class JSplitPane extends JComponent implements Accessible
  73. {
  74. /**
  75. * @see #getUIClassID
  76. * @see #readObject
  77. */
  78. private static final String uiClassID = "SplitPaneUI";
  79. /**
  80. * Vertical split indicates the <code>Component</code>s are
  81. * split along the y axis. For example the two
  82. * <code>Component</code>s will be split one on top of the other.
  83. */
  84. public final static int VERTICAL_SPLIT = 0;
  85. /**
  86. * Horizontal split indicates the <code>Component</code>s are
  87. * split along the x axis. For example the two
  88. * <code>Component</code>s will be split one to the left of the
  89. * other.
  90. */
  91. public final static int HORIZONTAL_SPLIT = 1;
  92. /**
  93. * Used to add a <code>Component</code> to the left of the other
  94. * <code>Component</code>.
  95. */
  96. public final static String LEFT = "left";
  97. /**
  98. * Used to add a <code>Component</code> to the right of the other
  99. * <code>Component</code>.
  100. */
  101. public final static String RIGHT = "right";
  102. /**
  103. * Used to add a <code>Component</code> above the other
  104. * <code>Component</code>.
  105. */
  106. public final static String TOP = "top";
  107. /**
  108. * Used to add a <code>Component</code> below the other
  109. * <code>Component</code>.
  110. */
  111. public final static String BOTTOM = "bottom";
  112. /**
  113. * Used to add a <code>Component</code> that will represent the divider.
  114. */
  115. public final static String DIVIDER = "divider";
  116. /**
  117. * Bound property name for orientation (horizontal or vertical).
  118. */
  119. public final static String ORIENTATION_PROPERTY = "orientation";
  120. /**
  121. * Bound property name for continuousLayout.
  122. */
  123. public final static String CONTINUOUS_LAYOUT_PROPERTY = "continuousLayout";
  124. /**
  125. * Bound property name for border.
  126. */
  127. public final static String DIVIDER_SIZE_PROPERTY = "dividerSize";
  128. /**
  129. * Bound property for oneTouchExpandable.
  130. */
  131. public final static String ONE_TOUCH_EXPANDABLE_PROPERTY =
  132. "oneTouchExpandable";
  133. /**
  134. * Bound property for lastLocation.
  135. */
  136. public final static String LAST_DIVIDER_LOCATION_PROPERTY =
  137. "lastDividerLocation";
  138. /**
  139. * Bound property for the dividerLocation.
  140. * @since 1.3
  141. */
  142. public final static String DIVIDER_LOCATION_PROPERTY = "dividerLocation";
  143. /**
  144. * Bound property for weight.
  145. * @since 1.3.
  146. */
  147. public final static String RESIZE_WEIGHT_PROPERTY = "resizeWeight";
  148. /**
  149. * How the views are split.
  150. */
  151. protected int orientation;
  152. /**
  153. * Whether or not the views are continuously redisplayed while
  154. * resizing.
  155. */
  156. protected boolean continuousLayout;
  157. /**
  158. * The left or top component.
  159. */
  160. protected Component leftComponent;
  161. /**
  162. * The right or bottom component.
  163. */
  164. protected Component rightComponent;
  165. /**
  166. * Size of the divider.
  167. */
  168. protected int dividerSize;
  169. /**
  170. * Is a little widget provided to quickly expand/collapse the
  171. * split pane?
  172. */
  173. protected boolean oneTouchExpandable;
  174. /**
  175. * Previous location of the split pane.
  176. */
  177. protected int lastDividerLocation;
  178. /**
  179. * How to distribute extra space.
  180. */
  181. private double resizeWeight;
  182. /**
  183. * Location of the divider, at least the value that was set, the UI may
  184. * have a different value.
  185. */
  186. private int dividerLocation;
  187. /**
  188. * Creates a new <code>JSplitPane</code> configured to arrange the child
  189. * components side-by-side horizontally with no continuous
  190. * layout, using two buttons for the components.
  191. */
  192. public JSplitPane() {
  193. this(JSplitPane.HORIZONTAL_SPLIT, false,
  194. new JButton(UIManager.getString("SplitPane.leftButtonText")),
  195. new JButton(UIManager.getString("SplitPane.rightButtonText")));
  196. }
  197. /**
  198. * Creates a new <code>JSplitPane</code> configured with the
  199. * specified orientation and no continuous layout.
  200. *
  201. * @param newOrientation <code>JSplitPane.HORIZONTAL_SPLIT</code> or
  202. * <code>JSplitPane.VERTICAL_SPLIT</code>
  203. * @exception IllegalArgumentException if <code>orientation</code>
  204. * is not one of HORIZONTAL_SPLIT or VERTICAL_SPLIT.
  205. */
  206. public JSplitPane(int newOrientation) {
  207. this(newOrientation, false);
  208. }
  209. /**
  210. * Creates a new <code>JSplitPane</code> with the specified
  211. * orientation and redrawing style.
  212. *
  213. * @param newOrientation <code>JSplitPane.HORIZONTAL_SPLIT</code> or
  214. * <code>JSplitPane.VERTICAL_SPLIT</code>
  215. * @param newContinuousLayout a boolean, true for the components to
  216. * redraw continuously as the divider changes position, false
  217. * to wait until the divider position stops changing to redraw
  218. * @exception IllegalArgumentException if <code>orientation</code>
  219. * is not one of HORIZONTAL_SPLIT or VERTICAL_SPLIT
  220. */
  221. public JSplitPane(int newOrientation,
  222. boolean newContinuousLayout) {
  223. this(newOrientation, newContinuousLayout, null, null);
  224. }
  225. /**
  226. * Creates a new <code>JSplitPane</code> with the specified
  227. * orientation and
  228. * with the specified components that do not do continuous
  229. * redrawing.
  230. *
  231. * @param newOrientation <code>JSplitPane.HORIZONTAL_SPLIT</code> or
  232. * <code>JSplitPane.VERTICAL_SPLIT</code>
  233. * @param newLeftComponent the <code>Component</code> that will
  234. * appear on the left
  235. * of a horizontally-split pane, or at the top of a
  236. * vertically-split pane
  237. * @param newRightComponent the <code>Component</code> that will
  238. * appear on the right
  239. * of a horizontally-split pane, or at the bottom of a
  240. * vertically-split pane
  241. * @exception IllegalArgumentException if <code>orientation</code>
  242. * is not one of: HORIZONTAL_SPLIT or VERTICAL_SPLIT
  243. */
  244. public JSplitPane(int newOrientation,
  245. Component newLeftComponent,
  246. Component newRightComponent){
  247. this(newOrientation, false, newLeftComponent, newRightComponent);
  248. }
  249. /**
  250. * Creates a new <code>JSplitPane</code> with the specified
  251. * orientation and
  252. * redrawing style, and with the specified components.
  253. *
  254. * @param newOrientation <code>JSplitPane.HORIZONTAL_SPLIT</code> or
  255. * <code>JSplitPane.VERTICAL_SPLIT</code>
  256. * @param newContinuousLayout a boolean, true for the components to
  257. * redraw continuously as the divider changes position, false
  258. * to wait until the divider position stops changing to redraw
  259. * @param newLeftComponent the <code>Component</code> that will
  260. * appear on the left
  261. * of a horizontally-split pane, or at the top of a
  262. * vertically-split pane
  263. * @param newRightComponent the <code>Component</code> that will
  264. * appear on the right
  265. * of a horizontally-split pane, or at the bottom of a
  266. * vertically-split pane
  267. * @exception IllegalArgumentException if <code>orientation</code>
  268. * is not one of HORIZONTAL_SPLIT or VERTICAL_SPLIT
  269. */
  270. public JSplitPane(int newOrientation,
  271. boolean newContinuousLayout,
  272. Component newLeftComponent,
  273. Component newRightComponent){
  274. super();
  275. dividerLocation = -1;
  276. setLayout(null);
  277. setOpaque(true);
  278. orientation = newOrientation;
  279. if (orientation != HORIZONTAL_SPLIT && orientation != VERTICAL_SPLIT)
  280. throw new IllegalArgumentException("cannot create JSplitPane, " +
  281. "orientation must be one of " +
  282. "JSplitPane.HORIZONTAL_SPLIT " +
  283. "or JSplitPane.VERTICAL_SPLIT");
  284. continuousLayout = newContinuousLayout;
  285. if (newLeftComponent != null)
  286. setLeftComponent(newLeftComponent);
  287. if (newRightComponent != null)
  288. setRightComponent(newRightComponent);
  289. updateUI();
  290. }
  291. /**
  292. * Sets the L&F object that renders this component.
  293. *
  294. * @param ui the <code>SplitPaneUI</code> L&F object
  295. * @see UIDefaults#getUI
  296. * @beaninfo
  297. * bound: true
  298. * hidden: true
  299. * attribute: visualUpdate true
  300. * description: The UI object that implements the Component's LookAndFeel.
  301. */
  302. public void setUI(SplitPaneUI ui) {
  303. if ((SplitPaneUI)this.ui != ui) {
  304. super.setUI(ui);
  305. revalidate();
  306. }
  307. }
  308. /**
  309. * Returns the <code>SplitPaneUI</code> that is providing the
  310. * current look and feel.
  311. *
  312. * @return the <code>SplitPaneUI</code> object that renders this component
  313. * @beaninfo
  314. * expert: true
  315. * description: The L&F object that renders this component.
  316. */
  317. public SplitPaneUI getUI() {
  318. return (SplitPaneUI)ui;
  319. }
  320. /**
  321. * Notification from the <code>UIManager</code> that the L&F has changed.
  322. * Replaces the current UI object with the latest version from the
  323. * <code>UIManager</code>.
  324. *
  325. * @see JComponent#updateUI
  326. */
  327. public void updateUI() {
  328. setUI((SplitPaneUI)UIManager.getUI(this));
  329. revalidate();
  330. }
  331. /**
  332. * Returns the name of the L&F class that renders this component.
  333. *
  334. * @return the string "SplitPaneUI"
  335. * @see JComponent#getUIClassID
  336. * @see UIDefaults#getUI
  337. * @beaninfo
  338. * expert: true
  339. * description: A string that specifies the name of the L&F class.
  340. */
  341. public String getUIClassID() {
  342. return uiClassID;
  343. }
  344. /**
  345. * Sets the size of the divider.
  346. *
  347. * @param newSize an integer giving the size of the divider in pixels
  348. * @beaninfo
  349. * bound: true
  350. * description: The size of the divider.
  351. */
  352. public void setDividerSize(int newSize) {
  353. int oldSize = dividerSize;
  354. if (oldSize != newSize) {
  355. dividerSize = newSize;
  356. firePropertyChange(DIVIDER_SIZE_PROPERTY, oldSize, newSize);
  357. }
  358. }
  359. /**
  360. * Returns the size of the divider.
  361. *
  362. * @return an integer giving the size of the divider in pixels
  363. */
  364. public int getDividerSize() {
  365. return dividerSize;
  366. }
  367. /**
  368. * Sets the component to the left (or above) the divider.
  369. *
  370. * @param comp the <code>Component</code> to display in that position
  371. */
  372. public void setLeftComponent(Component comp) {
  373. if (comp == null) {
  374. if (leftComponent != null) {
  375. remove(leftComponent);
  376. leftComponent = null;
  377. }
  378. } else {
  379. add(comp, JSplitPane.LEFT);
  380. }
  381. }
  382. /**
  383. * Returns the component to the left (or above) the divider.
  384. *
  385. * @return the <code>Component</code> displayed in that position
  386. * @beaninfo
  387. * preferred: true
  388. * description: The component to the left (or above) the divider.
  389. */
  390. public Component getLeftComponent() {
  391. return leftComponent;
  392. }
  393. /**
  394. * Sets the component above, or to the left of the divider.
  395. *
  396. * @param comp the <code>Component</code> to display in that position
  397. * @beaninfo
  398. * description: The component above, or to the left of the divider.
  399. */
  400. public void setTopComponent(Component comp) {
  401. setLeftComponent(comp);
  402. }
  403. /**
  404. * Returns the component above, or to the left of the divider.
  405. *
  406. * @return the <code>Component</code> displayed in that position
  407. */
  408. public Component getTopComponent() {
  409. return leftComponent;
  410. }
  411. /**
  412. * Sets the component to the right (or below) the divider.
  413. *
  414. * @param comp the <code>Component</code> to display in that position
  415. * @beaninfo
  416. * preferred: true
  417. * description: The component to the right (or below) the divider.
  418. */
  419. public void setRightComponent(Component comp) {
  420. if (comp == null) {
  421. if (rightComponent != null) {
  422. remove(rightComponent);
  423. rightComponent = null;
  424. }
  425. } else {
  426. add(comp, JSplitPane.RIGHT);
  427. }
  428. }
  429. /**
  430. * Returns the component to the right (or below) the divider.
  431. *
  432. * @return the <code>Component</code> displayed in that position
  433. */
  434. public Component getRightComponent() {
  435. return rightComponent;
  436. }
  437. /**
  438. * Sets the component below, or to the right of the divider.
  439. *
  440. * @param comp the <code>Component</code> to display in that position
  441. * @beaninfo
  442. * description: The component below, or to the right of the divider.
  443. */
  444. public void setBottomComponent(Component comp) {
  445. setRightComponent(comp);
  446. }
  447. /**
  448. * Returns the component below, or to the right of the divider.
  449. *
  450. * @return the <code>Component</code> displayed in that position
  451. */
  452. public Component getBottomComponent() {
  453. return rightComponent;
  454. }
  455. /**
  456. * Sets the value of the <code>oneTouchExpandable</code> property,
  457. * which must be <code>true</code> for the
  458. * <code>JSplitPane</code> to provide a UI widget
  459. * on the divider to quickly expand/collapse the divider.
  460. * The default value of this property is <code>false</code>.
  461. * Some look and feels might not support one-touch expanding;
  462. * they will ignore this property.
  463. *
  464. * @param newValue <code>true</code> to specify that the split pane should provide a
  465. * collapse/expand widget
  466. * @beaninfo
  467. * bound: true
  468. * description: UI widget on the divider to quickly
  469. * expand/collapse the divider.
  470. *
  471. * @see #isOneTouchExpandable
  472. */
  473. public void setOneTouchExpandable(boolean newValue) {
  474. boolean oldValue = oneTouchExpandable;
  475. oneTouchExpandable = newValue;
  476. firePropertyChange(ONE_TOUCH_EXPANDABLE_PROPERTY, oldValue, newValue);
  477. repaint();
  478. }
  479. /**
  480. * Gets the <code>oneTouchExpandable</code> property.
  481. *
  482. * @return the value of the <code>oneTouchExpandable</code> property
  483. * @see #setOneTouchExpandable
  484. */
  485. public boolean isOneTouchExpandable() {
  486. return oneTouchExpandable;
  487. }
  488. /**
  489. * Sets the last location the divider was at to
  490. * <code>newLastLocation</code>.
  491. *
  492. * @param newLastLocation an integer specifying the last divider location
  493. * in pixels, from the left (or upper) edge of the pane to the
  494. * left (or upper) edge of the divider
  495. * @beaninfo
  496. * bound: true
  497. * description: The last location the divider was at.
  498. */
  499. public void setLastDividerLocation(int newLastLocation) {
  500. int oldLocation = lastDividerLocation;
  501. lastDividerLocation = newLastLocation;
  502. firePropertyChange(LAST_DIVIDER_LOCATION_PROPERTY, oldLocation,
  503. newLastLocation);
  504. }
  505. /**
  506. * Returns the last location the divider was at.
  507. *
  508. * @return an integer specifying the last divider location as a count
  509. * of pixels from the left (or upper) edge of the pane to the
  510. * left (or upper) edge of the divider
  511. */
  512. public int getLastDividerLocation() {
  513. return lastDividerLocation;
  514. }
  515. /**
  516. * Sets the orientation, or how the splitter is divided. The options
  517. * are:<ul>
  518. * <li>JSplitPane.VERTICAL_SPLIT (above/below orientation of components)
  519. * <li>JSplitPane.HORIZONTAL_SPLIT (left/right orientation of components)
  520. * </ul>
  521. *
  522. * @param orientation an integer specifying the orientation
  523. * @exception IllegalArgumentException if orientation is not one of:
  524. * HORIZONTAL_SPLIT or VERTICAL_SPLIT.
  525. * @beaninfo
  526. * bound: true
  527. * description: The orientation, or how the splitter is divided.
  528. * enum: HORIZONTAL_SPLIT JSplitPane.HORIZONTAL_SPLIT
  529. * VERTICAL_SPLIT JSplitPane.VERTICAL_SPLIT
  530. */
  531. public void setOrientation(int orientation) {
  532. if ((orientation != VERTICAL_SPLIT) &&
  533. (orientation != HORIZONTAL_SPLIT)) {
  534. throw new IllegalArgumentException("JSplitPane: orientation must " +
  535. "be one of " +
  536. "JSplitPane.VERTICAL_SPLIT or " +
  537. "JSplitPane.HORIZONTAL_SPLIT");
  538. }
  539. int oldOrientation = this.orientation;
  540. this.orientation = orientation;
  541. firePropertyChange(ORIENTATION_PROPERTY, oldOrientation, orientation);
  542. }
  543. /**
  544. * Returns the orientation.
  545. *
  546. * @return an integer giving the orientation
  547. * @see #setOrientation
  548. */
  549. public int getOrientation() {
  550. return orientation;
  551. }
  552. /**
  553. * Sets the value of the <code>continuousLayout</code> property,
  554. * which must be <code>true</code> for the child components
  555. * to be continuously
  556. * redisplayed and laid out during user intervention.
  557. * The default value of this property is <code>false</code>.
  558. * Some look and feels might not support continuous layout;
  559. * they will ignore this property.
  560. *
  561. * @param newContinuousLayout <code>true</code> if the components
  562. * should continuously be redrawn as the divider changes position
  563. * @beaninfo
  564. * bound: true
  565. * description: Whether the child components are
  566. * continuously redisplayed and laid out during
  567. * user intervention.
  568. * @see #isContinuousLayout
  569. */
  570. public void setContinuousLayout(boolean newContinuousLayout) {
  571. boolean oldCD = continuousLayout;
  572. continuousLayout = newContinuousLayout;
  573. firePropertyChange(CONTINUOUS_LAYOUT_PROPERTY, oldCD,
  574. newContinuousLayout);
  575. }
  576. /**
  577. * Gets the <code>continuousLayout</code> property.
  578. *
  579. * @return the value of the <code>continuousLayout</code> property
  580. * @see #setContinuousLayout
  581. */
  582. public boolean isContinuousLayout() {
  583. return continuousLayout;
  584. }
  585. /**
  586. * Specifies how to distribute extra space when the size of the split pane
  587. * changes. A value of 0, the default,
  588. * indicates the right/bottom component gets all the extra space (the
  589. * left/top component acts fixed), where as a value of 1 specifies the
  590. * left/top component gets all the extra space (the right/bottom component
  591. * acts fixed). Specifically, the left/top component gets (weight * diff)
  592. * extra space and the right/bottom component gets (1 - weight) * diff
  593. * extra space.
  594. *
  595. * @param value as described above
  596. * @exception IllegalArgumentException if <code>value</code> is < 0 or > 1
  597. * @since 1.3
  598. * @beaninfo
  599. * bound: true
  600. * description: Specifies how to distribute extra space when the split pane
  601. * resizes.
  602. */
  603. public void setResizeWeight(double value) {
  604. if (value < 0 || value > 1) {
  605. throw new IllegalArgumentException("JSplitPane weight must be between 0 and 1");
  606. }
  607. double oldWeight = resizeWeight;
  608. resizeWeight = value;
  609. firePropertyChange(RESIZE_WEIGHT_PROPERTY, oldWeight, value);
  610. }
  611. /**
  612. * Returns the number that determines how extra space is distributed.
  613. * @return how extra space is to be distributed on a resize of the
  614. * split pane
  615. * @since 1.3
  616. */
  617. public double getResizeWeight() {
  618. return resizeWeight;
  619. }
  620. /**
  621. * Lays out the <code>JSplitPane</code> layout based on the preferred size
  622. * of the children components. This will likely result in changing
  623. * the divider location.
  624. */
  625. public void resetToPreferredSizes() {
  626. SplitPaneUI ui = getUI();
  627. if (ui != null) {
  628. ui.resetToPreferredSizes(this);
  629. }
  630. }
  631. /**
  632. * Sets the divider location as a percentage of the
  633. * <code>JSplitPane</code>'s size.
  634. * <p>
  635. * This method is implemented in terms of
  636. * <code>setDividerLocation(int)</code>.
  637. * This method immediately changes the size of the split pane based on
  638. * its current size. If the split pane is not correctly realized and on
  639. * screen, this method will have no effect (new divider location will
  640. * become (current size * proportionalLocation) which is 0).
  641. *
  642. * @param proportionalLocation a double-precision floating point value
  643. * that specifies a percentage, from zero (top/left) to 1.0
  644. * (bottom/right)
  645. * @exception IllegalArgumentException if the specified location is < 0
  646. * or > 1.0
  647. * @beaninfo
  648. * description: The location of the divider.
  649. */
  650. public void setDividerLocation(double proportionalLocation) {
  651. if (proportionalLocation < 0.0 ||
  652. proportionalLocation > 1.0) {
  653. throw new IllegalArgumentException("proportional location must " +
  654. "be between 0.0 and 1.0.");
  655. }
  656. if (getOrientation() == VERTICAL_SPLIT) {
  657. setDividerLocation((int)((double)(getHeight() - getDividerSize()) *
  658. proportionalLocation));
  659. } else {
  660. setDividerLocation((int)((double)(getWidth() - getDividerSize()) *
  661. proportionalLocation));
  662. }
  663. }
  664. /**
  665. * Sets the location of the divider. This is passed off to the
  666. * look and feel implementation, and then listeners are notified. A value
  667. * less than 0 implies the divider should be reset to a value that
  668. * attempts to honor the preferred size of the left/top component.
  669. * After notifying the listeners, the last divider location is updated,
  670. * via <code>setLastDividerLocation</code>.
  671. *
  672. * @param location an int specifying a UI-specific value (typically a
  673. * pixel count)
  674. * @beaninfo
  675. * bound: true
  676. * description: The location of the divider.
  677. */
  678. public void setDividerLocation(int location) {
  679. int oldValue = dividerLocation;
  680. dividerLocation = location;
  681. // Notify UI.
  682. SplitPaneUI ui = getUI();
  683. if (ui != null) {
  684. ui.setDividerLocation(this, location);
  685. }
  686. // Then listeners
  687. firePropertyChange(DIVIDER_LOCATION_PROPERTY, oldValue, location);
  688. // And update the last divider location.
  689. setLastDividerLocation(oldValue);
  690. }
  691. /**
  692. * Returns the last value passed to <code>setDividerLocation</code>.
  693. * The value returned from this method may differ from the actual
  694. * divider location (if <code>setDividerLocation</code> was passed a
  695. * value bigger than the curent size).
  696. *
  697. * @return an integer specifying the location of the divider
  698. */
  699. public int getDividerLocation() {
  700. return dividerLocation;
  701. }
  702. /**
  703. * Returns the minimum location of the divider from the look and feel
  704. * implementation.
  705. *
  706. * @return an integer specifying a UI-specific value for the minimum
  707. * location (typically a pixel count); or -1 if the UI is
  708. * <code>null</code>
  709. * @beaninfo
  710. * description: The minimum location of the divider from the L&F.
  711. */
  712. public int getMinimumDividerLocation() {
  713. SplitPaneUI ui = getUI();
  714. if (ui != null) {
  715. return ui.getMinimumDividerLocation(this);
  716. }
  717. return -1;
  718. }
  719. /**
  720. * Returns the maximum location of the divider from the look and feel
  721. * implementation.
  722. *
  723. * @return an integer specifying a UI-specific value for the maximum
  724. * location (typically a pixel count); or -1 if the UI is
  725. * <code>null</code>
  726. */
  727. public int getMaximumDividerLocation() {
  728. SplitPaneUI ui = getUI();
  729. if (ui != null) {
  730. return ui.getMaximumDividerLocation(this);
  731. }
  732. return -1;
  733. }
  734. /**
  735. * Removes the child component, <code>component</code> from the
  736. * pane. Resets the <code>leftComponent</code> or
  737. * <code>rightComponent</code> instance variable, as necessary.
  738. *
  739. * @param component the <code>Component</code> to remove
  740. */
  741. public void remove(Component component) {
  742. if (component == leftComponent) {
  743. leftComponent = null;
  744. } else if (component == rightComponent) {
  745. rightComponent = null;
  746. }
  747. super.remove(component);
  748. // Update the JSplitPane on the screen
  749. revalidate();
  750. repaint();
  751. }
  752. /**
  753. * Removes the <code>Component</code> at the specified index.
  754. * Updates the <code>leftComponent</code> and <code>rightComponent</code>
  755. * instance variables as necessary, and then messages super.
  756. *
  757. * @param index an integer specifying the component to remove, where
  758. * 1 specifies the left/top component and 2 specifies the
  759. * bottom/right component
  760. */
  761. public void remove(int index) {
  762. Component comp = getComponent(index);
  763. if (comp == leftComponent) {
  764. leftComponent = null;
  765. } else if (comp == rightComponent) {
  766. rightComponent = null;
  767. }
  768. super.remove(index);
  769. // Update the JSplitPane on the screen
  770. revalidate();
  771. repaint();
  772. }
  773. /**
  774. * Removes all the child components from the split pane. Resets the
  775. * <code>leftComonent</code> and <code>rightComponent</code>
  776. * instance variables.
  777. */
  778. public void removeAll() {
  779. leftComponent = rightComponent = null;
  780. super.removeAll();
  781. // Update the JSplitPane on the screen
  782. revalidate();
  783. repaint();
  784. }
  785. /**
  786. * Returns true, so that calls to <code>revalidate</code>
  787. * on any descendant of this <code>JSplitPane</code>
  788. * will cause a request to be queued that
  789. * will validate the <code>JSplitPane</code> and all its descendants.
  790. *
  791. * @return true
  792. * @see JComponent#revalidate
  793. *
  794. * @beaninfo
  795. * hidden: true
  796. */
  797. public boolean isValidateRoot() {
  798. return true;
  799. }
  800. /**
  801. * Adds the specified component to this split pane.
  802. * If <code>constraints</code> identifies the left/top or
  803. * right/bottom child component, and a component with that identifier
  804. * was previously added, it will be removed and then <code>comp</code>
  805. * will be added in its place. If <code>constraints</code> is not
  806. * one of the known identifiers the layout manager may throw an
  807. * <code>IllegalArgumentException</code>.
  808. * <p>
  809. * The possible constraints objects (Strings) are:
  810. * <ul>
  811. * <li>JSplitPane.TOP
  812. * <li>JSplitPane.LEFT
  813. * <li>JSplitPane.BOTTOM
  814. * <li>JSplitPane.RIGHT
  815. * </ul>
  816. * If the <code>constraints</code> object is <code>null</code>,
  817. * the component is added in the
  818. * first available position (left/top if open, else right/bottom).
  819. *
  820. * @param comp the component to add
  821. * @param constraints an <code>Object</code> specifying the
  822. * layout constraints
  823. * (position) for this component
  824. * @param index an integer specifying the index in the container's
  825. * list.
  826. * @exception IllegalArgumentException if the <code>constraints</code>
  827. * object does not match an existing component
  828. * @see java.awt.Container#addImpl(Component, Object, int)
  829. */
  830. protected void addImpl(Component comp, Object constraints, int index)
  831. {
  832. Component toRemove;
  833. if (constraints != null && !(constraints instanceof String)) {
  834. throw new IllegalArgumentException("cannot add to layout: " +
  835. "constraint must be a string " +
  836. "(or null)");
  837. }
  838. /* If the constraints are null and the left/right component is
  839. invalid, add it at the left/right component. */
  840. if (constraints == null) {
  841. if (getLeftComponent() == null) {
  842. constraints = JSplitPane.LEFT;
  843. } else if (getRightComponent() == null) {
  844. constraints = JSplitPane.RIGHT;
  845. }
  846. }
  847. /* Find the Component that already exists and remove it. */
  848. if (constraints != null && (constraints.equals(JSplitPane.LEFT) ||
  849. constraints.equals(JSplitPane.TOP))) {
  850. toRemove = getLeftComponent();
  851. if (toRemove != null) {
  852. remove(toRemove);
  853. }
  854. leftComponent = comp;
  855. index = -1;
  856. } else if (constraints != null &&
  857. (constraints.equals(JSplitPane.RIGHT) ||
  858. constraints.equals(JSplitPane.BOTTOM))) {
  859. toRemove = getRightComponent();
  860. if (toRemove != null) {
  861. remove(toRemove);
  862. }
  863. rightComponent = comp;
  864. index = -1;
  865. } else if (constraints != null &&
  866. constraints.equals(JSplitPane.DIVIDER)) {
  867. index = -1;
  868. }
  869. /* LayoutManager should raise for else condition here. */
  870. super.addImpl(comp, constraints, index);
  871. // Update the JSplitPane on the screen
  872. revalidate();
  873. repaint();
  874. }
  875. /**
  876. * Subclassed to message the UI with <code>finishedPaintingChildren</code>
  877. * after super has been messaged, as well as painting the border.
  878. *
  879. * @param g the <code>Graphics</code> context within which to paint
  880. */
  881. protected void paintChildren(Graphics g) {
  882. super.paintChildren(g);
  883. SplitPaneUI ui = getUI();
  884. if (ui != null) {
  885. Graphics tempG = SwingGraphics.createSwingGraphics(g);
  886. ui.finishedPaintingChildren(this, tempG);
  887. tempG.dispose();
  888. }
  889. }
  890. /**
  891. * See <code>readObject</code> and <code>writeObject</code> in
  892. * <code>JComponent</code> for more
  893. * information about serialization in Swing.
  894. */
  895. private void writeObject(ObjectOutputStream s) throws IOException {
  896. s.defaultWriteObject();
  897. if (getUIClassID().equals(uiClassID)) {
  898. byte count = JComponent.getWriteObjCounter(this);
  899. JComponent.setWriteObjCounter(this, --count);
  900. if (count == 0 && ui != null) {
  901. ui.installUI(this);
  902. }
  903. }
  904. }
  905. /**
  906. * Returns a string representation of this <code>JSplitPane</code>.
  907. * This method
  908. * is intended to be used only for debugging purposes, and the
  909. * content and format of the returned string may vary between
  910. * implementations. The returned string may be empty but may not
  911. * be <code>null</code>.
  912. *
  913. * @return a string representation of this <code>JSplitPane</code>.
  914. */
  915. protected String paramString() {
  916. String orientationString = (orientation == HORIZONTAL_SPLIT ?
  917. "HORIZONTAL_SPLIT" : "VERTICAL_SPLIT");
  918. String continuousLayoutString = (continuousLayout ?
  919. "true" : "false");
  920. String oneTouchExpandableString = (oneTouchExpandable ?
  921. "true" : "false");
  922. return super.paramString() +
  923. ",continuousLayout=" + continuousLayoutString +
  924. ",dividerSize=" + dividerSize +
  925. ",lastDividerLocation=" + lastDividerLocation +
  926. ",oneTouchExpandable=" + oneTouchExpandableString +
  927. ",orientation=" + orientationString;
  928. }
  929. ///////////////////////////
  930. // Accessibility support //
  931. ///////////////////////////
  932. /**
  933. * Gets the AccessibleContext associated with this JSplitPane.
  934. * For split panes, the AccessibleContext takes the form of an
  935. * AccessibleJSplitPane.
  936. * A new AccessibleJSplitPane instance is created if necessary.
  937. *
  938. * @return an AccessibleJSplitPane that serves as the
  939. * AccessibleContext of this JSplitPane
  940. * @beaninfo
  941. * expert: true
  942. * description: The AccessibleContext associated with this SplitPane.
  943. */
  944. public AccessibleContext getAccessibleContext() {
  945. if (accessibleContext == null) {
  946. accessibleContext = new AccessibleJSplitPane();
  947. }
  948. return accessibleContext;
  949. }
  950. /**
  951. * This class implements accessibility support for the
  952. * <code>JSplitPane</code> class. It provides an implementation of the
  953. * Java Accessibility API appropriate to split pane user-interface elements.
  954. * <p>
  955. * <strong>Warning:</strong>
  956. * Serialized objects of this class will not be compatible with
  957. * future Swing releases. The current serialization support is
  958. * appropriate for short term storage or RMI between applications running
  959. * the same version of Swing. As of 1.4, support for long term storage
  960. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  961. * has been added to the <code>java.beans</code> package.
  962. * Please see {@link java.beans.XMLEncoder}.
  963. */
  964. protected class AccessibleJSplitPane extends AccessibleJComponent
  965. implements AccessibleValue {
  966. /**
  967. * Gets the state set of this object.
  968. *
  969. * @return an instance of AccessibleState containing the current state
  970. * of the object
  971. * @see AccessibleState
  972. */
  973. public AccessibleStateSet getAccessibleStateSet() {
  974. AccessibleStateSet states = super.getAccessibleStateSet();
  975. // FIXME: [[[WDW - Should also add BUSY if this implements
  976. // Adjustable at some point. If this happens, we probably
  977. // should also add actions.]]]
  978. if (getOrientation() == VERTICAL_SPLIT) {
  979. states.add(AccessibleState.VERTICAL);
  980. } else {
  981. states.add(AccessibleState.HORIZONTAL);
  982. }
  983. return states;
  984. }
  985. /**
  986. * Get the AccessibleValue associated with this object. In the
  987. * implementation of the Java Accessibility API for this class,
  988. * return this object, which is responsible for implementing the
  989. * AccessibleValue interface on behalf of itself.
  990. *
  991. * @return this object
  992. */
  993. public AccessibleValue getAccessibleValue() {
  994. return this;
  995. }
  996. /**
  997. * Gets the accessible value of this object.
  998. *
  999. * @return a localized String describing the value of this object
  1000. */
  1001. public Number getCurrentAccessibleValue() {
  1002. return new Integer(getDividerLocation());
  1003. }
  1004. /**
  1005. * Sets the value of this object as a Number.
  1006. *
  1007. * @return True if the value was set.
  1008. */
  1009. public boolean setCurrentAccessibleValue(Number n) {
  1010. if (n instanceof Integer) {
  1011. setDividerLocation(n.intValue());
  1012. return true;
  1013. } else {
  1014. return false;
  1015. }
  1016. }
  1017. /**
  1018. * Gets the minimum accessible value of this object.
  1019. *
  1020. * @return The minimum value of this object.
  1021. */
  1022. public Number getMinimumAccessibleValue() {
  1023. return new Integer(getUI().getMinimumDividerLocation(
  1024. JSplitPane.this));
  1025. }
  1026. /**
  1027. * Gets the maximum accessible value of this object.
  1028. *
  1029. * @return The maximum value of this object.
  1030. */
  1031. public Number getMaximumAccessibleValue() {
  1032. return new Integer(getUI().getMaximumDividerLocation(
  1033. JSplitPane.this));
  1034. }
  1035. /**
  1036. * Gets the role of this object.
  1037. *
  1038. * @return an instance of AccessibleRole describing the role of
  1039. * the object
  1040. * @see AccessibleRole
  1041. */
  1042. public AccessibleRole getAccessibleRole() {
  1043. return AccessibleRole.SPLIT_PANE;
  1044. }
  1045. } // inner class AccessibleJSplitPane
  1046. }