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