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