1. /*
  2. * @(#)JSplitPane.java 1.76 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import 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. * <strong>Warning:</strong>
  54. * Serialized objects of this class will not be compatible with
  55. * future Swing releases. The current serialization support is
  56. * appropriate for short term storage or RMI between applications running
  57. * the same version of Swing. As of 1.4, support for long term storage
  58. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  59. * has been added to the <code>java.beans</code> package.
  60. * Please see {@link java.beans.XMLEncoder}.
  61. *
  62. * @see #setDividerLocation
  63. * @see #resetToPreferredSizes
  64. *
  65. * @version 1.76 12/19/03
  66. * @author Scott Violet
  67. */
  68. public class JSplitPane extends JComponent implements Accessible
  69. {
  70. /**
  71. * @see #getUIClassID
  72. * @see #readObject
  73. */
  74. private static final String uiClassID = "SplitPaneUI";
  75. /**
  76. * Vertical split indicates the <code>Component</code>s are
  77. * split along the y axis. For example the two
  78. * <code>Component</code>s will be split one on top of the other.
  79. */
  80. public final static int VERTICAL_SPLIT = 0;
  81. /**
  82. * Horizontal split indicates the <code>Component</code>s are
  83. * split along the x axis. For example the two
  84. * <code>Component</code>s will be split one to the left of the
  85. * other.
  86. */
  87. public final static int HORIZONTAL_SPLIT = 1;
  88. /**
  89. * Used to add a <code>Component</code> to the left of the other
  90. * <code>Component</code>.
  91. */
  92. public final static String LEFT = "left";
  93. /**
  94. * Used to add a <code>Component</code> to the right of the other
  95. * <code>Component</code>.
  96. */
  97. public final static String RIGHT = "right";
  98. /**
  99. * Used to add a <code>Component</code> above the other
  100. * <code>Component</code>.
  101. */
  102. public final static String TOP = "top";
  103. /**
  104. * Used to add a <code>Component</code> below the other
  105. * <code>Component</code>.
  106. */
  107. public final static String BOTTOM = "bottom";
  108. /**
  109. * Used to add a <code>Component</code> that will represent the divider.
  110. */
  111. public final static String DIVIDER = "divider";
  112. /**
  113. * Bound property name for orientation (horizontal or vertical).
  114. */
  115. public final static String ORIENTATION_PROPERTY = "orientation";
  116. /**
  117. * Bound property name for continuousLayout.
  118. */
  119. public final static String CONTINUOUS_LAYOUT_PROPERTY = "continuousLayout";
  120. /**
  121. * Bound property name for border.
  122. */
  123. public final static String DIVIDER_SIZE_PROPERTY = "dividerSize";
  124. /**
  125. * Bound property for oneTouchExpandable.
  126. */
  127. public final static String ONE_TOUCH_EXPANDABLE_PROPERTY =
  128. "oneTouchExpandable";
  129. /**
  130. * Bound property for lastLocation.
  131. */
  132. public final static String LAST_DIVIDER_LOCATION_PROPERTY =
  133. "lastDividerLocation";
  134. /**
  135. * Bound property for the dividerLocation.
  136. * @since 1.3
  137. */
  138. public final static String DIVIDER_LOCATION_PROPERTY = "dividerLocation";
  139. /**
  140. * Bound property for weight.
  141. * @since 1.3.
  142. */
  143. public final static String RESIZE_WEIGHT_PROPERTY = "resizeWeight";
  144. /**
  145. * How the views are split.
  146. */
  147. protected int orientation;
  148. /**
  149. * Whether or not the views are continuously redisplayed while
  150. * resizing.
  151. */
  152. protected boolean continuousLayout;
  153. /**
  154. * The left or top component.
  155. */
  156. protected Component leftComponent;
  157. /**
  158. * The right or bottom component.
  159. */
  160. protected Component rightComponent;
  161. /**
  162. * Size of the divider.
  163. */
  164. protected int dividerSize;
  165. private boolean dividerSizeSet = false;
  166. /**
  167. * Is a little widget provided to quickly expand/collapse the
  168. * split pane?
  169. */
  170. protected boolean oneTouchExpandable;
  171. private boolean oneTouchExpandableSet;
  172. /**
  173. * Previous location of the split pane.
  174. */
  175. protected int lastDividerLocation;
  176. /**
  177. * How to distribute extra space.
  178. */
  179. private double resizeWeight;
  180. /**
  181. * Location of the divider, at least the value that was set, the UI may
  182. * have a different value.
  183. */
  184. private int dividerLocation;
  185. /**
  186. * Creates a new <code>JSplitPane</code> configured to arrange the child
  187. * components side-by-side horizontally with no continuous
  188. * layout, using two buttons for the components.
  189. */
  190. public JSplitPane() {
  191. this(JSplitPane.HORIZONTAL_SPLIT, false,
  192. new JButton(UIManager.getString("SplitPane.leftButtonText")),
  193. new JButton(UIManager.getString("SplitPane.rightButtonText")));
  194. }
  195. /**
  196. * Creates a new <code>JSplitPane</code> configured with the
  197. * specified orientation and no continuous layout.
  198. *
  199. * @param newOrientation <code>JSplitPane.HORIZONTAL_SPLIT</code> or
  200. * <code>JSplitPane.VERTICAL_SPLIT</code>
  201. * @exception IllegalArgumentException if <code>orientation</code>
  202. * is not one of HORIZONTAL_SPLIT or VERTICAL_SPLIT.
  203. */
  204. public JSplitPane(int newOrientation) {
  205. this(newOrientation, false);
  206. }
  207. /**
  208. * Creates a new <code>JSplitPane</code> with the specified
  209. * orientation and redrawing style.
  210. *
  211. * @param newOrientation <code>JSplitPane.HORIZONTAL_SPLIT</code> or
  212. * <code>JSplitPane.VERTICAL_SPLIT</code>
  213. * @param newContinuousLayout a boolean, true for the components to
  214. * redraw continuously as the divider changes position, false
  215. * to wait until the divider position stops changing to redraw
  216. * @exception IllegalArgumentException if <code>orientation</code>
  217. * is not one of HORIZONTAL_SPLIT or VERTICAL_SPLIT
  218. */
  219. public JSplitPane(int newOrientation,
  220. boolean newContinuousLayout) {
  221. this(newOrientation, newContinuousLayout, null, null);
  222. }
  223. /**
  224. * Creates a new <code>JSplitPane</code> with the specified
  225. * orientation and
  226. * with the specified components that do not do continuous
  227. * redrawing.
  228. *
  229. * @param newOrientation <code>JSplitPane.HORIZONTAL_SPLIT</code> or
  230. * <code>JSplitPane.VERTICAL_SPLIT</code>
  231. * @param newLeftComponent the <code>Component</code> that will
  232. * appear on the left
  233. * of a horizontally-split pane, or at the top of a
  234. * vertically-split pane
  235. * @param newRightComponent the <code>Component</code> that will
  236. * appear on the right
  237. * of a horizontally-split pane, or at the bottom of a
  238. * vertically-split pane
  239. * @exception IllegalArgumentException if <code>orientation</code>
  240. * is not one of: HORIZONTAL_SPLIT or VERTICAL_SPLIT
  241. */
  242. public JSplitPane(int newOrientation,
  243. Component newLeftComponent,
  244. Component newRightComponent){
  245. this(newOrientation, false, newLeftComponent, newRightComponent);
  246. }
  247. /**
  248. * Creates a new <code>JSplitPane</code> with the specified
  249. * orientation and
  250. * redrawing style, and with the specified components.
  251. *
  252. * @param newOrientation <code>JSplitPane.HORIZONTAL_SPLIT</code> or
  253. * <code>JSplitPane.VERTICAL_SPLIT</code>
  254. * @param newContinuousLayout a boolean, true for the components to
  255. * redraw continuously as the divider changes position, false
  256. * to wait until the divider position stops changing to redraw
  257. * @param newLeftComponent the <code>Component</code> that will
  258. * appear on the left
  259. * of a horizontally-split pane, or at the top of a
  260. * vertically-split pane
  261. * @param newRightComponent the <code>Component</code> that will
  262. * appear on the right
  263. * of a horizontally-split pane, or at the bottom of a
  264. * vertically-split pane
  265. * @exception IllegalArgumentException if <code>orientation</code>
  266. * is not one of HORIZONTAL_SPLIT or VERTICAL_SPLIT
  267. */
  268. public JSplitPane(int newOrientation,
  269. boolean newContinuousLayout,
  270. Component newLeftComponent,
  271. Component newRightComponent){
  272. super();
  273. dividerLocation = -1;
  274. setLayout(null);
  275. setUIProperty("opaque", Boolean.TRUE);
  276. orientation = newOrientation;
  277. if (orientation != HORIZONTAL_SPLIT && orientation != VERTICAL_SPLIT)
  278. throw new IllegalArgumentException("cannot create JSplitPane, " +
  279. "orientation must be one of " +
  280. "JSplitPane.HORIZONTAL_SPLIT " +
  281. "or JSplitPane.VERTICAL_SPLIT");
  282. continuousLayout = newContinuousLayout;
  283. if (newLeftComponent != null)
  284. setLeftComponent(newLeftComponent);
  285. if (newRightComponent != null)
  286. setRightComponent(newRightComponent);
  287. updateUI();
  288. }
  289. /**
  290. * Sets the L&F object that renders this component.
  291. *
  292. * @param ui the <code>SplitPaneUI</code> L&F object
  293. * @see UIDefaults#getUI
  294. * @beaninfo
  295. * bound: true
  296. * hidden: true
  297. * attribute: visualUpdate true
  298. * description: The UI object that implements the Component's LookAndFeel.
  299. */
  300. public void setUI(SplitPaneUI ui) {
  301. if ((SplitPaneUI)this.ui != ui) {
  302. super.setUI(ui);
  303. revalidate();
  304. }
  305. }
  306. /**
  307. * Returns the <code>SplitPaneUI</code> that is providing the
  308. * current look and feel.
  309. *
  310. * @return the <code>SplitPaneUI</code> object that renders this component
  311. * @beaninfo
  312. * expert: true
  313. * description: The L&F object that renders this component.
  314. */
  315. public SplitPaneUI getUI() {
  316. return (SplitPaneUI)ui;
  317. }
  318. /**
  319. * Notification from the <code>UIManager</code> that the L&F has changed.
  320. * Replaces the current UI object with the latest version from the
  321. * <code>UIManager</code>.
  322. *
  323. * @see JComponent#updateUI
  324. */
  325. public void updateUI() {
  326. setUI((SplitPaneUI)UIManager.getUI(this));
  327. revalidate();
  328. }
  329. /**
  330. * Returns the name of the L&F class that renders this component.
  331. *
  332. * @return the string "SplitPaneUI"
  333. * @see JComponent#getUIClassID
  334. * @see UIDefaults#getUI
  335. * @beaninfo
  336. * expert: true
  337. * description: A string that specifies the name of the L&F class.
  338. */
  339. public String getUIClassID() {
  340. return uiClassID;
  341. }
  342. /**
  343. * Sets the size of the divider.
  344. *
  345. * @param newSize an integer giving the size of the divider in pixels
  346. * @beaninfo
  347. * bound: true
  348. * description: The size of the divider.
  349. */
  350. public void setDividerSize(int newSize) {
  351. int oldSize = dividerSize;
  352. dividerSizeSet = true;
  353. if (oldSize != newSize) {
  354. dividerSize = newSize;
  355. firePropertyChange(DIVIDER_SIZE_PROPERTY, oldSize, newSize);
  356. }
  357. }
  358. /**
  359. * Returns the size of the divider.
  360. *
  361. * @return an integer giving the size of the divider in pixels
  362. */
  363. public int getDividerSize() {
  364. return dividerSize;
  365. }
  366. /**
  367. * Sets the component to the left (or above) the divider.
  368. *
  369. * @param comp the <code>Component</code> to display in that position
  370. */
  371. public void setLeftComponent(Component comp) {
  372. if (comp == null) {
  373. if (leftComponent != null) {
  374. remove(leftComponent);
  375. leftComponent = null;
  376. }
  377. } else {
  378. add(comp, JSplitPane.LEFT);
  379. }
  380. }
  381. /**
  382. * Returns the component to the left (or above) the divider.
  383. *
  384. * @return the <code>Component</code> displayed in that position
  385. * @beaninfo
  386. * preferred: true
  387. * description: The component to the left (or above) the divider.
  388. */
  389. public Component getLeftComponent() {
  390. return leftComponent;
  391. }
  392. /**
  393. * Sets the component above, or to the left of the divider.
  394. *
  395. * @param comp the <code>Component</code> to display in that position
  396. * @beaninfo
  397. * description: The component above, or to the left of the divider.
  398. */
  399. public void setTopComponent(Component comp) {
  400. setLeftComponent(comp);
  401. }
  402. /**
  403. * Returns the component above, or to the left of the divider.
  404. *
  405. * @return the <code>Component</code> displayed in that position
  406. */
  407. public Component getTopComponent() {
  408. return leftComponent;
  409. }
  410. /**
  411. * Sets the component to the right (or below) the divider.
  412. *
  413. * @param comp the <code>Component</code> to display in that position
  414. * @beaninfo
  415. * preferred: true
  416. * description: The component to the right (or below) the divider.
  417. */
  418. public void setRightComponent(Component comp) {
  419. if (comp == null) {
  420. if (rightComponent != null) {
  421. remove(rightComponent);
  422. rightComponent = null;
  423. }
  424. } else {
  425. add(comp, JSplitPane.RIGHT);
  426. }
  427. }
  428. /**
  429. * Returns the component to the right (or below) the divider.
  430. *
  431. * @return the <code>Component</code> displayed in that position
  432. */
  433. public Component getRightComponent() {
  434. return rightComponent;
  435. }
  436. /**
  437. * Sets the component below, or to the right of the divider.
  438. *
  439. * @param comp the <code>Component</code> to display in that position
  440. * @beaninfo
  441. * description: The component below, or to the right of the divider.
  442. */
  443. public void setBottomComponent(Component comp) {
  444. setRightComponent(comp);
  445. }
  446. /**
  447. * Returns the component below, or to the right of the divider.
  448. *
  449. * @return the <code>Component</code> displayed in that position
  450. */
  451. public Component getBottomComponent() {
  452. return rightComponent;
  453. }
  454. /**
  455. * Sets the value of the <code>oneTouchExpandable</code> property,
  456. * which must be <code>true</code> for the
  457. * <code>JSplitPane</code> to provide a UI widget
  458. * on the divider to quickly expand/collapse the divider.
  459. * The default value of this property is <code>false</code>.
  460. * Some look and feels might not support one-touch expanding;
  461. * they will ignore this property.
  462. *
  463. * @param newValue <code>true</code> to specify that the split pane should provide a
  464. * collapse/expand widget
  465. * @beaninfo
  466. * bound: true
  467. * description: UI widget on the divider to quickly
  468. * expand/collapse the divider.
  469. *
  470. * @see #isOneTouchExpandable
  471. */
  472. public void setOneTouchExpandable(boolean newValue) {
  473. boolean oldValue = oneTouchExpandable;
  474. oneTouchExpandable = newValue;
  475. oneTouchExpandableSet = true;
  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 = g.create();
  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. void setUIProperty(String propertyName, Object value) {
  906. if (propertyName == "dividerSize") {
  907. if (!dividerSizeSet) {
  908. setDividerSize(((Number)value).intValue());
  909. dividerSizeSet = false;
  910. }
  911. } else if (propertyName == "oneTouchExpandable") {
  912. if (!oneTouchExpandableSet) {
  913. setOneTouchExpandable(((Boolean)value).booleanValue());
  914. oneTouchExpandableSet = false;
  915. }
  916. } else {
  917. super.setUIProperty(propertyName, value);
  918. }
  919. }
  920. /**
  921. * Returns a string representation of this <code>JSplitPane</code>.
  922. * This method
  923. * is intended to be used only for debugging purposes, and the
  924. * content and format of the returned string may vary between
  925. * implementations. The returned string may be empty but may not
  926. * be <code>null</code>.
  927. *
  928. * @return a string representation of this <code>JSplitPane</code>.
  929. */
  930. protected String paramString() {
  931. String orientationString = (orientation == HORIZONTAL_SPLIT ?
  932. "HORIZONTAL_SPLIT" : "VERTICAL_SPLIT");
  933. String continuousLayoutString = (continuousLayout ?
  934. "true" : "false");
  935. String oneTouchExpandableString = (oneTouchExpandable ?
  936. "true" : "false");
  937. return super.paramString() +
  938. ",continuousLayout=" + continuousLayoutString +
  939. ",dividerSize=" + dividerSize +
  940. ",lastDividerLocation=" + lastDividerLocation +
  941. ",oneTouchExpandable=" + oneTouchExpandableString +
  942. ",orientation=" + orientationString;
  943. }
  944. ///////////////////////////
  945. // Accessibility support //
  946. ///////////////////////////
  947. /**
  948. * Gets the AccessibleContext associated with this JSplitPane.
  949. * For split panes, the AccessibleContext takes the form of an
  950. * AccessibleJSplitPane.
  951. * A new AccessibleJSplitPane instance is created if necessary.
  952. *
  953. * @return an AccessibleJSplitPane that serves as the
  954. * AccessibleContext of this JSplitPane
  955. * @beaninfo
  956. * expert: true
  957. * description: The AccessibleContext associated with this SplitPane.
  958. */
  959. public AccessibleContext getAccessibleContext() {
  960. if (accessibleContext == null) {
  961. accessibleContext = new AccessibleJSplitPane();
  962. }
  963. return accessibleContext;
  964. }
  965. /**
  966. * This class implements accessibility support for the
  967. * <code>JSplitPane</code> class. It provides an implementation of the
  968. * Java Accessibility API appropriate to split pane user-interface elements.
  969. * <p>
  970. * <strong>Warning:</strong>
  971. * Serialized objects of this class will not be compatible with
  972. * future Swing releases. The current serialization support is
  973. * appropriate for short term storage or RMI between applications running
  974. * the same version of Swing. As of 1.4, support for long term storage
  975. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  976. * has been added to the <code>java.beans</code> package.
  977. * Please see {@link java.beans.XMLEncoder}.
  978. */
  979. protected class AccessibleJSplitPane extends AccessibleJComponent
  980. implements AccessibleValue {
  981. /**
  982. * Gets the state set of this object.
  983. *
  984. * @return an instance of AccessibleState containing the current state
  985. * of the object
  986. * @see AccessibleState
  987. */
  988. public AccessibleStateSet getAccessibleStateSet() {
  989. AccessibleStateSet states = super.getAccessibleStateSet();
  990. // FIXME: [[[WDW - Should also add BUSY if this implements
  991. // Adjustable at some point. If this happens, we probably
  992. // should also add actions.]]]
  993. if (getOrientation() == VERTICAL_SPLIT) {
  994. states.add(AccessibleState.VERTICAL);
  995. } else {
  996. states.add(AccessibleState.HORIZONTAL);
  997. }
  998. return states;
  999. }
  1000. /**
  1001. * Get the AccessibleValue associated with this object. In the
  1002. * implementation of the Java Accessibility API for this class,
  1003. * return this object, which is responsible for implementing the
  1004. * AccessibleValue interface on behalf of itself.
  1005. *
  1006. * @return this object
  1007. */
  1008. public AccessibleValue getAccessibleValue() {
  1009. return this;
  1010. }
  1011. /**
  1012. * Gets the accessible value of this object.
  1013. *
  1014. * @return a localized String describing the value of this object
  1015. */
  1016. public Number getCurrentAccessibleValue() {
  1017. return new Integer(getDividerLocation());
  1018. }
  1019. /**
  1020. * Sets the value of this object as a Number.
  1021. *
  1022. * @return True if the value was set.
  1023. */
  1024. public boolean setCurrentAccessibleValue(Number n) {
  1025. // TIGER - 4422535
  1026. if (n == null) {
  1027. return false;
  1028. }
  1029. setDividerLocation(n.intValue());
  1030. return true;
  1031. }
  1032. /**
  1033. * Gets the minimum accessible value of this object.
  1034. *
  1035. * @return The minimum value of this object.
  1036. */
  1037. public Number getMinimumAccessibleValue() {
  1038. return new Integer(getUI().getMinimumDividerLocation(
  1039. JSplitPane.this));
  1040. }
  1041. /**
  1042. * Gets the maximum accessible value of this object.
  1043. *
  1044. * @return The maximum value of this object.
  1045. */
  1046. public Number getMaximumAccessibleValue() {
  1047. return new Integer(getUI().getMaximumDividerLocation(
  1048. JSplitPane.this));
  1049. }
  1050. /**
  1051. * Gets the role of this object.
  1052. *
  1053. * @return an instance of AccessibleRole describing the role of
  1054. * the object
  1055. * @see AccessibleRole
  1056. */
  1057. public AccessibleRole getAccessibleRole() {
  1058. return AccessibleRole.SPLIT_PANE;
  1059. }
  1060. } // inner class AccessibleJSplitPane
  1061. }