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