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