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