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