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 package javax.swing.plaf.basic; 28 29 30 import sun.swing.DefaultLookup; 31 import sun.swing.UIAction; 32 import javax.swing.*; 33 import javax.swing.border.Border; 34 import java.awt.*; 35 import java.awt.event.*; 36 import java.awt.peer.ComponentPeer; 37 import java.awt.peer.LightweightPeer; 38 import java.beans.*; 39 import java.util.*; 40 import javax.swing.plaf.SplitPaneUI; 41 import javax.swing.plaf.ComponentUI; 42 import javax.swing.plaf.UIResource; 43 import sun.swing.SwingUtilities2; 44 45 46 /** 47 * A Basic L&F implementation of the SplitPaneUI. 48 * 49 * @author Scott Violet 50 * @author Steve Wilson 51 * @author Ralph Kar 52 */ 53 public class BasicSplitPaneUI extends SplitPaneUI 54 { 55 /** 56 * The divider used for non-continuous layout is added to the split pane 57 * with this object. 58 */ 59 protected static final String NON_CONTINUOUS_DIVIDER = 60 "nonContinuousDivider"; 61 62 63 /** 64 * How far (relative) the divider does move when it is moved around by 65 * the cursor keys on the keyboard. 66 */ 67 protected static int KEYBOARD_DIVIDER_MOVE_OFFSET = 3; 68 69 70 /** 71 * JSplitPane instance this instance is providing 72 * the look and feel for. 73 */ 74 protected JSplitPane splitPane; 75 76 77 /** 78 * LayoutManager that is created and placed into the split pane. 79 */ 80 protected BasicHorizontalLayoutManager layoutManager; 81 82 83 /** 84 * Instance of the divider for this JSplitPane. 85 */ 86 protected BasicSplitPaneDivider divider; 87 88 89 /** 90 * Instance of the PropertyChangeListener for this JSplitPane. 91 */ 92 protected PropertyChangeListener propertyChangeListener; 93 94 95 /** 96 * Instance of the FocusListener for this JSplitPane. 97 */ 98 protected FocusListener focusListener; 99 100 private Handler handler; 101 102 103 /** 104 * Keys to use for forward focus traversal when the JComponent is 105 * managing focus. 106 */ 107 private Set<KeyStroke> managingFocusForwardTraversalKeys; 108 109 /** 110 * Keys to use for backward focus traversal when the JComponent is 111 * managing focus. 112 */ 113 private Set<KeyStroke> managingFocusBackwardTraversalKeys; 114 115 116 /** 117 * The size of the divider while the dragging session is valid. 118 */ 119 protected int dividerSize; 120 121 122 /** 123 * Instance for the shadow of the divider when non continuous layout 124 * is being used. 125 */ 126 protected Component nonContinuousLayoutDivider; 127 128 129 /** 130 * Set to true in startDragging if any of the children 131 * (not including the nonContinuousLayoutDivider) are heavy weights. 132 */ 133 protected boolean draggingHW; 134 135 136 /** 137 * Location of the divider when the dragging session began. 138 */ 139 protected int beginDragDividerLocation; 140 141 142 /** 143 * As of Java 2 platform v1.3 this previously undocumented field is no 144 * longer used. 145 * Key bindings are now defined by the LookAndFeel, please refer to 146 * the key bindings specification for further details. 147 * 148 * @deprecated As of Java 2 platform v1.3. 149 */ 150 @Deprecated 151 protected KeyStroke upKey; 152 /** 153 * As of Java 2 platform v1.3 this previously undocumented field is no 154 * longer used. 155 * Key bindings are now defined by the LookAndFeel, please refer to 156 * the key bindings specification for further details. 157 * 158 * @deprecated As of Java 2 platform v1.3. 159 */ 160 @Deprecated 161 protected KeyStroke downKey; 162 /** 163 * As of Java 2 platform v1.3 this previously undocumented field is no 164 * longer used. 165 * Key bindings are now defined by the LookAndFeel, please refer to 166 * the key bindings specification for further details. 167 * 168 * @deprecated As of Java 2 platform v1.3. 169 */ 170 @Deprecated 171 protected KeyStroke leftKey; 172 /** 173 * As of Java 2 platform v1.3 this previously undocumented field is no 174 * longer used. 175 * Key bindings are now defined by the LookAndFeel, please refer to 176 * the key bindings specification for further details. 177 * 178 * @deprecated As of Java 2 platform v1.3. 179 */ 180 @Deprecated 181 protected KeyStroke rightKey; 182 /** 183 * As of Java 2 platform v1.3 this previously undocumented field is no 184 * longer used. 185 * Key bindings are now defined by the LookAndFeel, please refer to 186 * the key bindings specification for further details. 187 * 188 * @deprecated As of Java 2 platform v1.3. 189 */ 190 @Deprecated 191 protected KeyStroke homeKey; 192 /** 193 * As of Java 2 platform v1.3 this previously undocumented field is no 194 * longer used. 195 * Key bindings are now defined by the LookAndFeel, please refer to 196 * the key bindings specification for further details. 197 * 198 * @deprecated As of Java 2 platform v1.3. 199 */ 200 @Deprecated 201 protected KeyStroke endKey; 202 /** 203 * As of Java 2 platform v1.3 this previously undocumented field is no 204 * longer used. 205 * Key bindings are now defined by the LookAndFeel, please refer to 206 * the key bindings specification for further details. 207 * 208 * @deprecated As of Java 2 platform v1.3. 209 */ 210 @Deprecated 211 protected KeyStroke dividerResizeToggleKey; 212 213 /** 214 * As of Java 2 platform v1.3 this previously undocumented field is no 215 * longer used. 216 * Key bindings are now defined by the LookAndFeel, please refer to 217 * the key bindings specification for further details. 218 * 219 * @deprecated As of Java 2 platform v1.3. 220 */ 221 @Deprecated 222 protected ActionListener keyboardUpLeftListener; 223 /** 224 * As of Java 2 platform v1.3 this previously undocumented field is no 225 * longer used. 226 * Key bindings are now defined by the LookAndFeel, please refer to 227 * the key bindings specification for further details. 228 * 229 * @deprecated As of Java 2 platform v1.3. 230 */ 231 @Deprecated 232 protected ActionListener keyboardDownRightListener; 233 /** 234 * As of Java 2 platform v1.3 this previously undocumented field is no 235 * longer used. 236 * Key bindings are now defined by the LookAndFeel, please refer to 237 * the key bindings specification for further details. 238 * 239 * @deprecated As of Java 2 platform v1.3. 240 */ 241 @Deprecated 242 protected ActionListener keyboardHomeListener; 243 /** 244 * As of Java 2 platform v1.3 this previously undocumented field is no 245 * longer used. 246 * Key bindings are now defined by the LookAndFeel, please refer to 247 * the key bindings specification for further details. 248 * 249 * @deprecated As of Java 2 platform v1.3. 250 */ 251 @Deprecated 252 protected ActionListener keyboardEndListener; 253 /** 254 * As of Java 2 platform v1.3 this previously undocumented field is no 255 * longer used. 256 * Key bindings are now defined by the LookAndFeel, please refer to 257 * the key bindings specification for further details. 258 * 259 * @deprecated As of Java 2 platform v1.3. 260 */ 261 @Deprecated 262 protected ActionListener keyboardResizeToggleListener; 263 264 265 // Private data of the instance 266 private int orientation; 267 private int lastDragLocation; 268 private boolean continuousLayout; 269 private boolean dividerKeyboardResize; 270 private boolean dividerLocationIsSet; // needed for tracking 271 // the first occurrence of 272 // setDividerLocation() 273 private Color dividerDraggingColor; 274 private boolean rememberPaneSizes; 275 276 // Indicates whether the one of splitpane sides is expanded 277 private boolean keepHidden = false; 278 279 /** Indicates that we have painted once. */ 280 // This is used by the LayoutManager to determine when it should use 281 // the divider location provided by the JSplitPane. This is used as there 282 // is no way to determine when the layout process has completed. 283 boolean painted; 284 /** If true, setDividerLocation does nothing. */ 285 boolean ignoreDividerLocationChange; 286 287 288 /** 289 * Creates a new BasicSplitPaneUI instance 290 */ 291 public static ComponentUI createUI(JComponent x) { 292 return new BasicSplitPaneUI(); 293 } 294 295 static void loadActionMap(LazyActionMap map) { 296 map.put(new Actions(Actions.NEGATIVE_INCREMENT)); 297 map.put(new Actions(Actions.POSITIVE_INCREMENT)); 298 map.put(new Actions(Actions.SELECT_MIN)); 299 map.put(new Actions(Actions.SELECT_MAX)); 300 map.put(new Actions(Actions.START_RESIZE)); 301 map.put(new Actions(Actions.TOGGLE_FOCUS)); 302 map.put(new Actions(Actions.FOCUS_OUT_FORWARD)); 303 map.put(new Actions(Actions.FOCUS_OUT_BACKWARD)); 304 } 305 306 307 308 /** 309 * Installs the UI. 310 */ 311 public void installUI(JComponent c) { 312 splitPane = (JSplitPane) c; 313 dividerLocationIsSet = false; 314 dividerKeyboardResize = false; 315 keepHidden = false; 316 installDefaults(); 317 installListeners(); 318 installKeyboardActions(); 319 setLastDragLocation(-1); 320 } 321 322 323 /** 324 * Installs the UI defaults. 325 */ 326 protected void installDefaults(){ 327 LookAndFeel.installBorder(splitPane, "SplitPane.border"); 328 LookAndFeel.installColors(splitPane, "SplitPane.background", 329 "SplitPane.foreground"); 330 LookAndFeel.installProperty(splitPane, "opaque", Boolean.TRUE); 331 332 if (divider == null) divider = createDefaultDivider(); 333 divider.setBasicSplitPaneUI(this); 334 335 Border b = divider.getBorder(); 336 337 if (b == null || !(b instanceof UIResource)) { 338 divider.setBorder(UIManager.getBorder("SplitPaneDivider.border")); 339 } 340 341 dividerDraggingColor = UIManager.getColor("SplitPaneDivider.draggingColor"); 342 343 setOrientation(splitPane.getOrientation()); 344 345 // note: don't rename this temp variable to dividerSize 346 // since it will conflict with "this.dividerSize" field 347 Integer temp = (Integer)UIManager.get("SplitPane.dividerSize"); 348 LookAndFeel.installProperty(splitPane, "dividerSize", temp == null? 10: temp); 349 350 divider.setDividerSize(splitPane.getDividerSize()); 351 dividerSize = divider.getDividerSize(); 352 splitPane.add(divider, JSplitPane.DIVIDER); 353 354 setContinuousLayout(splitPane.isContinuousLayout()); 355 356 resetLayoutManager(); 357 358 /* Install the nonContinuousLayoutDivider here to avoid having to 359 add/remove everything later. */ 360 if(nonContinuousLayoutDivider == null) { 361 setNonContinuousLayoutDivider( 362 createDefaultNonContinuousLayoutDivider(), 363 true); 364 } else { 365 setNonContinuousLayoutDivider(nonContinuousLayoutDivider, true); 366 } 367 368 // focus forward traversal key 369 if (managingFocusForwardTraversalKeys==null) { 370 managingFocusForwardTraversalKeys = new HashSet<KeyStroke>(); 371 managingFocusForwardTraversalKeys.add( 372 KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0)); 373 } 374 splitPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, 375 managingFocusForwardTraversalKeys); 376 // focus backward traversal key 377 if (managingFocusBackwardTraversalKeys==null) { 378 managingFocusBackwardTraversalKeys = new HashSet<KeyStroke>(); 379 managingFocusBackwardTraversalKeys.add( 380 KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK)); 381 } 382 splitPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, 383 managingFocusBackwardTraversalKeys); 384 } 385 386 387 /** 388 * Installs the event listeners for the UI. 389 */ 390 protected void installListeners() { 391 if ((propertyChangeListener = createPropertyChangeListener()) != 392 null) { 393 splitPane.addPropertyChangeListener(propertyChangeListener); 394 } 395 396 if ((focusListener = createFocusListener()) != null) { 397 splitPane.addFocusListener(focusListener); 398 } 399 } 400 401 402 /** 403 * Installs the keyboard actions for the UI. 404 */ 405 protected void installKeyboardActions() { 406 InputMap km = getInputMap(JComponent. 407 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 408 409 SwingUtilities.replaceUIInputMap(splitPane, JComponent. 410 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, 411 km); 412 LazyActionMap.installLazyActionMap(splitPane, BasicSplitPaneUI.class, 413 "SplitPane.actionMap"); 414 } 415 416 InputMap getInputMap(int condition) { 417 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) { 418 return (InputMap)DefaultLookup.get(splitPane, this, 419 "SplitPane.ancestorInputMap"); 420 } 421 return null; 422 } 423 424 /** 425 * Uninstalls the UI. 426 */ 427 public void uninstallUI(JComponent c) { 428 uninstallKeyboardActions(); 429 uninstallListeners(); 430 uninstallDefaults(); 431 dividerLocationIsSet = false; 432 dividerKeyboardResize = false; 433 splitPane = null; 434 } 435 436 437 /** 438 * Uninstalls the UI defaults. 439 */ 440 protected void uninstallDefaults() { 441 if(splitPane.getLayout() == layoutManager) { 442 splitPane.setLayout(null); 443 } 444 445 if(nonContinuousLayoutDivider != null) { 446 splitPane.remove(nonContinuousLayoutDivider); 447 } 448 449 LookAndFeel.uninstallBorder(splitPane); 450 451 Border b = divider.getBorder(); 452 453 if (b instanceof UIResource) { 454 divider.setBorder(null); 455 } 456 457 splitPane.remove(divider); 458 divider.setBasicSplitPaneUI(null); 459 layoutManager = null; 460 divider = null; 461 nonContinuousLayoutDivider = null; 462 463 setNonContinuousLayoutDivider(null); 464 465 // sets the focus forward and backward traversal keys to null 466 // to restore the defaults 467 splitPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null); 468 splitPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null); 469 } 470 471 472 /** 473 * Uninstalls the event listeners for the UI. 474 */ 475 protected void uninstallListeners() { 476 if (propertyChangeListener != null) { 477 splitPane.removePropertyChangeListener(propertyChangeListener); 478 propertyChangeListener = null; 479 } 480 if (focusListener != null) { 481 splitPane.removeFocusListener(focusListener); 482 focusListener = null; 483 } 484 485 keyboardUpLeftListener = null; 486 keyboardDownRightListener = null; 487 keyboardHomeListener = null; 488 keyboardEndListener = null; 489 keyboardResizeToggleListener = null; 490 handler = null; 491 } 492 493 494 /** 495 * Uninstalls the keyboard actions for the UI. 496 */ 497 protected void uninstallKeyboardActions() { 498 SwingUtilities.replaceUIActionMap(splitPane, null); 499 SwingUtilities.replaceUIInputMap(splitPane, JComponent. 500 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, 501 null); 502 } 503 504 505 /** 506 * Creates a PropertyChangeListener for the JSplitPane UI. 507 */ 508 protected PropertyChangeListener createPropertyChangeListener() { 509 return getHandler(); 510 } 511 512 private Handler getHandler() { 513 if (handler == null) { 514 handler = new Handler(); 515 } 516 return handler; 517 } 518 519 520 /** 521 * Creates a FocusListener for the JSplitPane UI. 522 */ 523 protected FocusListener createFocusListener() { 524 return getHandler(); 525 } 526 527 528 /** 529 * As of Java 2 platform v1.3 this method is no 530 * longer used. Subclassers previously using this method should 531 * instead create an Action wrapping the ActionListener, and register 532 * that Action by overriding <code>installKeyboardActions</code> and 533 * placing the Action in the SplitPane's ActionMap. Please refer to 534 * the key bindings specification for further details. 535 * <p> 536 * Creates a ActionListener for the JSplitPane UI that listens for 537 * specific key presses. 538 * 539 * @deprecated As of Java 2 platform v1.3. 540 */ 541 @Deprecated 542 protected ActionListener createKeyboardUpLeftListener() { 543 return new KeyboardUpLeftHandler(); 544 } 545 546 547 /** 548 * As of Java 2 platform v1.3 this method is no 549 * longer used. Subclassers previously using this method should 550 * instead create an Action wrapping the ActionListener, and register 551 * that Action by overriding <code>installKeyboardActions</code> and 552 * placing the Action in the SplitPane's ActionMap. Please refer to 553 * the key bindings specification for further details. 554 * <p> 555 * Creates a ActionListener for the JSplitPane UI that listens for 556 * specific key presses. 557 * 558 * @deprecated As of Java 2 platform v1.3. 559 */ 560 @Deprecated 561 protected ActionListener createKeyboardDownRightListener() { 562 return new KeyboardDownRightHandler(); 563 } 564 565 566 /** 567 * As of Java 2 platform v1.3 this method is no 568 * longer used. Subclassers previously using this method should 569 * instead create an Action wrapping the ActionListener, and register 570 * that Action by overriding <code>installKeyboardActions</code> and 571 * placing the Action in the SplitPane's ActionMap. Please refer to 572 * the key bindings specification for further details. 573 * <p> 574 * Creates a ActionListener for the JSplitPane UI that listens for 575 * specific key presses. 576 * 577 * @deprecated As of Java 2 platform v1.3. 578 */ 579 @Deprecated 580 protected ActionListener createKeyboardHomeListener() { 581 return new KeyboardHomeHandler(); 582 } 583 584 585 /** 586 * As of Java 2 platform v1.3 this method is no 587 * longer used. Subclassers previously using this method should 588 * instead create an Action wrapping the ActionListener, and register 589 * that Action by overriding <code>installKeyboardActions</code> and 590 * placing the Action in the SplitPane's ActionMap. Please refer to 591 * the key bindings specification for further details. 592 * <p> 593 * Creates a ActionListener for the JSplitPane UI that listens for 594 * specific key presses. 595 * 596 * @deprecated As of Java 2 platform v1.3. 597 */ 598 @Deprecated 599 protected ActionListener createKeyboardEndListener() { 600 return new KeyboardEndHandler(); 601 } 602 603 604 /** 605 * As of Java 2 platform v1.3 this method is no 606 * longer used. Subclassers previously using this method should 607 * instead create an Action wrapping the ActionListener, and register 608 * that Action by overriding <code>installKeyboardActions</code> and 609 * placing the Action in the SplitPane's ActionMap. Please refer to 610 * the key bindings specification for further details. 611 * <p> 612 * Creates a ActionListener for the JSplitPane UI that listens for 613 * specific key presses. 614 * 615 * @deprecated As of Java 2 platform v1.3. 616 */ 617 @Deprecated 618 protected ActionListener createKeyboardResizeToggleListener() { 619 return new KeyboardResizeToggleHandler(); 620 } 621 622 623 /** 624 * Returns the orientation for the JSplitPane. 625 */ 626 public int getOrientation() { 627 return orientation; 628 } 629 630 631 /** 632 * Set the orientation for the JSplitPane. 633 */ 634 public void setOrientation(int orientation) { 635 this.orientation = orientation; 636 } 637 638 639 /** 640 * Determines whether the JSplitPane is set to use a continuous layout. 641 */ 642 public boolean isContinuousLayout() { 643 return continuousLayout; 644 } 645 646 647 /** 648 * Turn continuous layout on/off. 649 */ 650 public void setContinuousLayout(boolean b) { 651 continuousLayout = b; 652 } 653 654 655 /** 656 * Returns the last drag location of the JSplitPane. 657 */ 658 public int getLastDragLocation() { 659 return lastDragLocation; 660 } 661 662 663 /** 664 * Set the last drag location of the JSplitPane. 665 */ 666 public void setLastDragLocation(int l) { 667 lastDragLocation = l; 668 } 669 670 /** 671 * @return increment via keyboard methods. 672 */ 673 int getKeyboardMoveIncrement() { 674 return 3; 675 } 676 677 /** 678 * Implementation of the PropertyChangeListener 679 * that the JSplitPane UI uses. 680 * <p> 681 * This class should be treated as a "protected" inner class. 682 * Instantiate it only within subclasses of BasicSplitPaneUI. 683 */ 684 public class PropertyHandler implements PropertyChangeListener 685 { 686 // NOTE: This class exists only for backward compatibility. All 687 // its functionality has been moved into Handler. If you need to add 688 // new functionality add it to the Handler, but make sure this 689 // class calls into the Handler. 690 691 /** 692 * Messaged from the <code>JSplitPane</code> the receiver is 693 * contained in. May potentially reset the layout manager and cause a 694 * <code>validate</code> to be sent. 695 */ 696 public void propertyChange(PropertyChangeEvent e) { 697 getHandler().propertyChange(e); 698 } 699 } 700 701 702 /** 703 * Implementation of the FocusListener that the JSplitPane UI uses. 704 * <p> 705 * This class should be treated as a "protected" inner class. 706 * Instantiate it only within subclasses of BasicSplitPaneUI. 707 */ 708 public class FocusHandler extends FocusAdapter 709 { 710 // NOTE: This class exists only for backward compatibility. All 711 // its functionality has been moved into Handler. If you need to add 712 // new functionality add it to the Handler, but make sure this 713 // class calls into the Handler. 714 public void focusGained(FocusEvent ev) { 715 getHandler().focusGained(ev); 716 } 717 718 public void focusLost(FocusEvent ev) { 719 getHandler().focusLost(ev); 720 } 721 } 722 723 724 /** 725 * Implementation of an ActionListener that the JSplitPane UI uses for 726 * handling specific key presses. 727 * <p> 728 * This class should be treated as a "protected" inner class. 729 * Instantiate it only within subclasses of BasicSplitPaneUI. 730 */ 731 public class KeyboardUpLeftHandler implements ActionListener 732 { 733 public void actionPerformed(ActionEvent ev) { 734 if (dividerKeyboardResize) { 735 splitPane.setDividerLocation(Math.max(0,getDividerLocation 736 (splitPane) - getKeyboardMoveIncrement())); 737 } 738 } 739 } 740 741 /** 742 * Implementation of an ActionListener that the JSplitPane UI uses for 743 * handling specific key presses. 744 * <p> 745 * This class should be treated as a "protected" inner class. 746 * Instantiate it only within subclasses of BasicSplitPaneUI. 747 */ 748 public class KeyboardDownRightHandler implements ActionListener 749 { 750 public void actionPerformed(ActionEvent ev) { 751 if (dividerKeyboardResize) { 752 splitPane.setDividerLocation(getDividerLocation(splitPane) + 753 getKeyboardMoveIncrement()); 754 } 755 } 756 } 757 758 759 /** 760 * Implementation of an ActionListener that the JSplitPane UI uses for 761 * handling specific key presses. 762 * <p> 763 * This class should be treated as a "protected" inner class. 764 * Instantiate it only within subclasses of BasicSplitPaneUI. 765 */ 766 public class KeyboardHomeHandler implements ActionListener 767 { 768 public void actionPerformed(ActionEvent ev) { 769 if (dividerKeyboardResize) { 770 splitPane.setDividerLocation(0); 771 } 772 } 773 } 774 775 776 /** 777 * Implementation of an ActionListener that the JSplitPane UI uses for 778 * handling specific key presses. 779 * <p> 780 * This class should be treated as a "protected" inner class. 781 * Instantiate it only within subclasses of BasicSplitPaneUI. 782 */ 783 public class KeyboardEndHandler implements ActionListener 784 { 785 public void actionPerformed(ActionEvent ev) { 786 if (dividerKeyboardResize) { 787 Insets insets = splitPane.getInsets(); 788 int bottomI = (insets != null) ? insets.bottom : 0; 789 int rightI = (insets != null) ? insets.right : 0; 790 791 if (orientation == JSplitPane.VERTICAL_SPLIT) { 792 splitPane.setDividerLocation(splitPane.getHeight() - 793 bottomI); 794 } 795 else { 796 splitPane.setDividerLocation(splitPane.getWidth() - 797 rightI); 798 } 799 } 800 } 801 } 802 803 804 /** 805 * Implementation of an ActionListener that the JSplitPane UI uses for 806 * handling specific key presses. 807 * <p> 808 * This class should be treated as a "protected" inner class. 809 * Instantiate it only within subclasses of BasicSplitPaneUI. 810 */ 811 public class KeyboardResizeToggleHandler implements ActionListener 812 { 813 public void actionPerformed(ActionEvent ev) { 814 if (!dividerKeyboardResize) { 815 splitPane.requestFocus(); 816 } 817 } 818 } 819 820 /** 821 * Returns the divider between the top Components. 822 */ 823 public BasicSplitPaneDivider getDivider() { 824 return divider; 825 } 826 827 828 /** 829 * Returns the default non continuous layout divider, which is an 830 * instance of {@code Canvas} that fills in the background with dark gray. 831 */ 832 @SuppressWarnings("serial") // anonymous class 833 protected Component createDefaultNonContinuousLayoutDivider() { 834 return new Canvas() { 835 public void paint(Graphics g) { 836 if(!isContinuousLayout() && getLastDragLocation() != -1) { 837 Dimension size = splitPane.getSize(); 838 839 g.setColor(dividerDraggingColor); 840 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 841 g.fillRect(0, 0, dividerSize - 1, size.height - 1); 842 } else { 843 g.fillRect(0, 0, size.width - 1, dividerSize - 1); 844 } 845 } 846 } 847 }; 848 } 849 850 851 /** 852 * Sets the divider to use when the splitPane is configured to 853 * not continuously layout. This divider will only be used during a 854 * dragging session. It is recommended that the passed in component 855 * be a heavy weight. 856 */ 857 protected void setNonContinuousLayoutDivider(Component newDivider) { 858 setNonContinuousLayoutDivider(newDivider, true); 859 } 860 861 862 /** 863 * Sets the divider to use. 864 */ 865 protected void setNonContinuousLayoutDivider(Component newDivider, 866 boolean rememberSizes) { 867 rememberPaneSizes = rememberSizes; 868 if(nonContinuousLayoutDivider != null && splitPane != null) { 869 splitPane.remove(nonContinuousLayoutDivider); 870 } 871 nonContinuousLayoutDivider = newDivider; 872 } 873 874 private void addHeavyweightDivider() { 875 if(nonContinuousLayoutDivider != null && splitPane != null) { 876 877 /* Needs to remove all the components and re-add them! YECK! */ 878 // This is all done so that the nonContinuousLayoutDivider will 879 // be drawn on top of the other components, without this, one 880 // of the heavyweights will draw over the divider! 881 Component leftC = splitPane.getLeftComponent(); 882 Component rightC = splitPane.getRightComponent(); 883 int lastLocation = splitPane. 884 getDividerLocation(); 885 886 if(leftC != null) 887 splitPane.setLeftComponent(null); 888 if(rightC != null) 889 splitPane.setRightComponent(null); 890 splitPane.remove(divider); 891 splitPane.add(nonContinuousLayoutDivider, BasicSplitPaneUI. 892 NON_CONTINUOUS_DIVIDER, 893 splitPane.getComponentCount()); 894 splitPane.setLeftComponent(leftC); 895 splitPane.setRightComponent(rightC); 896 splitPane.add(divider, JSplitPane.DIVIDER); 897 if(rememberPaneSizes) { 898 splitPane.setDividerLocation(lastLocation); 899 } 900 } 901 902 } 903 904 905 /** 906 * Returns the divider to use when the splitPane is configured to 907 * not continuously layout. This divider will only be used during a 908 * dragging session. 909 */ 910 public Component getNonContinuousLayoutDivider() { 911 return nonContinuousLayoutDivider; 912 } 913 914 915 /** 916 * Returns the splitpane this instance is currently contained 917 * in. 918 */ 919 public JSplitPane getSplitPane() { 920 return splitPane; 921 } 922 923 924 /** 925 * Creates the default divider. 926 */ 927 public BasicSplitPaneDivider createDefaultDivider() { 928 return new BasicSplitPaneDivider(this); 929 } 930 931 932 /** 933 * Messaged to reset the preferred sizes. 934 */ 935 public void resetToPreferredSizes(JSplitPane jc) { 936 if(splitPane != null) { 937 layoutManager.resetToPreferredSizes(); 938 splitPane.revalidate(); 939 splitPane.repaint(); 940 } 941 } 942 943 944 /** 945 * Sets the location of the divider to location. 946 */ 947 public void setDividerLocation(JSplitPane jc, int location) { 948 if (!ignoreDividerLocationChange) { 949 dividerLocationIsSet = true; 950 splitPane.revalidate(); 951 splitPane.repaint(); 952 953 if (keepHidden) { 954 Insets insets = splitPane.getInsets(); 955 int orientation = splitPane.getOrientation(); 956 if ((orientation == JSplitPane.VERTICAL_SPLIT && 957 location != insets.top && 958 location != splitPane.getHeight()-divider.getHeight()-insets.top) || 959 (orientation == JSplitPane.HORIZONTAL_SPLIT && 960 location != insets.left && 961 location != splitPane.getWidth()-divider.getWidth()-insets.left)) { 962 setKeepHidden(false); 963 } 964 } 965 } 966 else { 967 ignoreDividerLocationChange = false; 968 } 969 } 970 971 972 /** 973 * Returns the location of the divider, which may differ from what 974 * the splitpane thinks the location of the divider is. 975 */ 976 public int getDividerLocation(JSplitPane jc) { 977 if(orientation == JSplitPane.HORIZONTAL_SPLIT) 978 return divider.getLocation().x; 979 return divider.getLocation().y; 980 } 981 982 983 /** 984 * Gets the minimum location of the divider. 985 */ 986 public int getMinimumDividerLocation(JSplitPane jc) { 987 int minLoc = 0; 988 Component leftC = splitPane.getLeftComponent(); 989 990 if ((leftC != null) && (leftC.isVisible())) { 991 Insets insets = splitPane.getInsets(); 992 Dimension minSize = leftC.getMinimumSize(); 993 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 994 minLoc = minSize.width; 995 } else { 996 minLoc = minSize.height; 997 } 998 if(insets != null) { 999 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1000 minLoc += insets.left; 1001 } else { 1002 minLoc += insets.top; 1003 } 1004 } 1005 } 1006 return minLoc; 1007 } 1008 1009 1010 /** 1011 * Gets the maximum location of the divider. 1012 */ 1013 public int getMaximumDividerLocation(JSplitPane jc) { 1014 Dimension splitPaneSize = splitPane.getSize(); 1015 int maxLoc = 0; 1016 Component rightC = splitPane.getRightComponent(); 1017 1018 if (rightC != null) { 1019 Insets insets = splitPane.getInsets(); 1020 Dimension minSize = new Dimension(0, 0); 1021 if (rightC.isVisible()) { 1022 minSize = rightC.getMinimumSize(); 1023 } 1024 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1025 maxLoc = splitPaneSize.width - minSize.width; 1026 } else { 1027 maxLoc = splitPaneSize.height - minSize.height; 1028 } 1029 maxLoc -= dividerSize; 1030 if(insets != null) { 1031 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1032 maxLoc -= insets.right; 1033 } else { 1034 maxLoc -= insets.top; 1035 } 1036 } 1037 } 1038 return Math.max(getMinimumDividerLocation(splitPane), maxLoc); 1039 } 1040 1041 1042 /** 1043 * Called when the specified split pane has finished painting 1044 * its children. 1045 */ 1046 public void finishedPaintingChildren(JSplitPane sp, Graphics g) { 1047 if(sp == splitPane && getLastDragLocation() != -1 && 1048 !isContinuousLayout() && !draggingHW) { 1049 Dimension size = splitPane.getSize(); 1050 1051 g.setColor(dividerDraggingColor); 1052 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1053 g.fillRect(getLastDragLocation(), 0, dividerSize - 1, 1054 size.height - 1); 1055 } else { 1056 g.fillRect(0, lastDragLocation, size.width - 1, 1057 dividerSize - 1); 1058 } 1059 } 1060 } 1061 1062 1063 /** 1064 * {@inheritDoc} 1065 */ 1066 public void paint(Graphics g, JComponent jc) { 1067 if (!painted && splitPane.getDividerLocation()<0) { 1068 ignoreDividerLocationChange = true; 1069 splitPane.setDividerLocation(getDividerLocation(splitPane)); 1070 } 1071 painted = true; 1072 } 1073 1074 1075 /** 1076 * Returns the preferred size for the passed in component, 1077 * This is passed off to the current layout manager. 1078 */ 1079 public Dimension getPreferredSize(JComponent jc) { 1080 if(splitPane != null) 1081 return layoutManager.preferredLayoutSize(splitPane); 1082 return new Dimension(0, 0); 1083 } 1084 1085 1086 /** 1087 * Returns the minimum size for the passed in component, 1088 * This is passed off to the current layout manager. 1089 */ 1090 public Dimension getMinimumSize(JComponent jc) { 1091 if(splitPane != null) 1092 return layoutManager.minimumLayoutSize(splitPane); 1093 return new Dimension(0, 0); 1094 } 1095 1096 1097 /** 1098 * Returns the maximum size for the passed in component, 1099 * This is passed off to the current layout manager. 1100 */ 1101 public Dimension getMaximumSize(JComponent jc) { 1102 if(splitPane != null) 1103 return layoutManager.maximumLayoutSize(splitPane); 1104 return new Dimension(0, 0); 1105 } 1106 1107 1108 /** 1109 * Returns the insets. The insets are returned from the border insets 1110 * of the current border. 1111 */ 1112 public Insets getInsets(JComponent jc) { 1113 return null; 1114 } 1115 1116 1117 /** 1118 * Resets the layout manager based on orientation and messages it 1119 * with invalidateLayout to pull in appropriate Components. 1120 */ 1121 protected void resetLayoutManager() { 1122 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1123 layoutManager = new BasicHorizontalLayoutManager(0); 1124 } else { 1125 layoutManager = new BasicHorizontalLayoutManager(1); 1126 } 1127 splitPane.setLayout(layoutManager); 1128 layoutManager.updateComponents(); 1129 splitPane.revalidate(); 1130 splitPane.repaint(); 1131 } 1132 1133 /** 1134 * Set the value to indicate if one of the splitpane sides is expanded. 1135 */ 1136 void setKeepHidden(boolean keepHidden) { 1137 this.keepHidden = keepHidden; 1138 } 1139 1140 /** 1141 * The value returned indicates if one of the splitpane sides is expanded. 1142 * @return true if one of the splitpane sides is expanded, false otherwise. 1143 */ 1144 private boolean getKeepHidden() { 1145 return keepHidden; 1146 } 1147 1148 /** 1149 * Should be messaged before the dragging session starts, resets 1150 * lastDragLocation and dividerSize. 1151 */ 1152 protected void startDragging() { 1153 Component leftC = splitPane.getLeftComponent(); 1154 Component rightC = splitPane.getRightComponent(); 1155 ComponentPeer cPeer; 1156 1157 beginDragDividerLocation = getDividerLocation(splitPane); 1158 draggingHW = false; 1159 if(leftC != null && (cPeer = leftC.getPeer()) != null && 1160 !(cPeer instanceof LightweightPeer)) { 1161 draggingHW = true; 1162 } else if(rightC != null && (cPeer = rightC.getPeer()) != null 1163 && !(cPeer instanceof LightweightPeer)) { 1164 draggingHW = true; 1165 } 1166 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1167 setLastDragLocation(divider.getBounds().x); 1168 dividerSize = divider.getSize().width; 1169 if(!isContinuousLayout() && draggingHW) { 1170 nonContinuousLayoutDivider.setBounds 1171 (getLastDragLocation(), 0, dividerSize, 1172 splitPane.getHeight()); 1173 addHeavyweightDivider(); 1174 } 1175 } else { 1176 setLastDragLocation(divider.getBounds().y); 1177 dividerSize = divider.getSize().height; 1178 if(!isContinuousLayout() && draggingHW) { 1179 nonContinuousLayoutDivider.setBounds 1180 (0, getLastDragLocation(), splitPane.getWidth(), 1181 dividerSize); 1182 addHeavyweightDivider(); 1183 } 1184 } 1185 } 1186 1187 1188 /** 1189 * Messaged during a dragging session to move the divider to the 1190 * passed in location. If continuousLayout is true the location is 1191 * reset and the splitPane validated. 1192 */ 1193 protected void dragDividerTo(int location) { 1194 if(getLastDragLocation() != location) { 1195 if(isContinuousLayout()) { 1196 splitPane.setDividerLocation(location); 1197 setLastDragLocation(location); 1198 } else { 1199 int lastLoc = getLastDragLocation(); 1200 1201 setLastDragLocation(location); 1202 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1203 if(draggingHW) { 1204 nonContinuousLayoutDivider.setLocation( 1205 getLastDragLocation(), 0); 1206 } else { 1207 int splitHeight = splitPane.getHeight(); 1208 splitPane.repaint(lastLoc, 0, dividerSize, 1209 splitHeight); 1210 splitPane.repaint(location, 0, dividerSize, 1211 splitHeight); 1212 } 1213 } else { 1214 if(draggingHW) { 1215 nonContinuousLayoutDivider.setLocation(0, 1216 getLastDragLocation()); 1217 } else { 1218 int splitWidth = splitPane.getWidth(); 1219 1220 splitPane.repaint(0, lastLoc, splitWidth, 1221 dividerSize); 1222 splitPane.repaint(0, location, splitWidth, 1223 dividerSize); 1224 } 1225 } 1226 } 1227 } 1228 } 1229 1230 1231 /** 1232 * Messaged to finish the dragging session. If not continuous display 1233 * the dividers location will be reset. 1234 */ 1235 protected void finishDraggingTo(int location) { 1236 dragDividerTo(location); 1237 setLastDragLocation(-1); 1238 if(!isContinuousLayout()) { 1239 Component leftC = splitPane.getLeftComponent(); 1240 Rectangle leftBounds = leftC.getBounds(); 1241 1242 if (draggingHW) { 1243 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1244 nonContinuousLayoutDivider.setLocation(-dividerSize, 0); 1245 } 1246 else { 1247 nonContinuousLayoutDivider.setLocation(0, -dividerSize); 1248 } 1249 splitPane.remove(nonContinuousLayoutDivider); 1250 } 1251 splitPane.setDividerLocation(location); 1252 } 1253 } 1254 1255 1256 /** 1257 * As of Java 2 platform v1.3 this method is no longer used. Instead 1258 * you should set the border on the divider. 1259 * <p> 1260 * Returns the width of one side of the divider border. 1261 * 1262 * @deprecated As of Java 2 platform v1.3, instead set the border on the 1263 * divider. 1264 */ 1265 @Deprecated 1266 protected int getDividerBorderSize() { 1267 return 1; 1268 } 1269 1270 1271 /** 1272 * LayoutManager for JSplitPanes that have an orientation of 1273 * HORIZONTAL_SPLIT. 1274 */ 1275 public class BasicHorizontalLayoutManager implements LayoutManager2 1276 { 1277 /* left, right, divider. (in this exact order) */ 1278 protected int[] sizes; 1279 protected Component[] components; 1280 /** Size of the splitpane the last time laid out. */ 1281 private int lastSplitPaneSize; 1282 /** True if resetToPreferredSizes has been invoked. */ 1283 private boolean doReset; 1284 /** Axis, 0 for horizontal, or 1 for veritcal. */ 1285 private int axis; 1286 1287 1288 BasicHorizontalLayoutManager() { 1289 this(0); 1290 } 1291 1292 BasicHorizontalLayoutManager(int axis) { 1293 this.axis = axis; 1294 components = new Component[3]; 1295 components[0] = components[1] = components[2] = null; 1296 sizes = new int[3]; 1297 } 1298 1299 // 1300 // LayoutManager 1301 // 1302 1303 /** 1304 * Does the actual layout. 1305 */ 1306 public void layoutContainer(Container container) { 1307 Dimension containerSize = container.getSize(); 1308 1309 // If the splitpane has a zero size then no op out of here. 1310 // If we execute this function now, we're going to cause ourselves 1311 // much grief. 1312 if (containerSize.height <= 0 || containerSize.width <= 0 ) { 1313 lastSplitPaneSize = 0; 1314 return; 1315 } 1316 1317 int spDividerLocation = splitPane.getDividerLocation(); 1318 Insets insets = splitPane.getInsets(); 1319 int availableSize = getAvailableSize(containerSize, 1320 insets); 1321 int newSize = getSizeForPrimaryAxis(containerSize); 1322 int beginLocation = getDividerLocation(splitPane); 1323 int dOffset = getSizeForPrimaryAxis(insets, true); 1324 Dimension dSize = (components[2] == null) ? null : 1325 components[2].getPreferredSize(); 1326 1327 if ((doReset && !dividerLocationIsSet) || spDividerLocation < 0) { 1328 resetToPreferredSizes(availableSize); 1329 } 1330 else if (lastSplitPaneSize <= 0 || 1331 availableSize == lastSplitPaneSize || !painted || 1332 (dSize != null && 1333 getSizeForPrimaryAxis(dSize) != sizes[2])) { 1334 if (dSize != null) { 1335 sizes[2] = getSizeForPrimaryAxis(dSize); 1336 } 1337 else { 1338 sizes[2] = 0; 1339 } 1340 setDividerLocation(spDividerLocation - dOffset, availableSize); 1341 dividerLocationIsSet = false; 1342 } 1343 else if (availableSize != lastSplitPaneSize) { 1344 distributeSpace(availableSize - lastSplitPaneSize, 1345 getKeepHidden()); 1346 } 1347 doReset = false; 1348 dividerLocationIsSet = false; 1349 lastSplitPaneSize = availableSize; 1350 1351 // Reset the bounds of each component 1352 int nextLocation = getInitialLocation(insets); 1353 int counter = 0; 1354 1355 while (counter < 3) { 1356 if (components[counter] != null && 1357 components[counter].isVisible()) { 1358 setComponentToSize(components[counter], sizes[counter], 1359 nextLocation, insets, containerSize); 1360 nextLocation += sizes[counter]; 1361 } 1362 switch (counter) { 1363 case 0: 1364 counter = 2; 1365 break; 1366 case 2: 1367 counter = 1; 1368 break; 1369 case 1: 1370 counter = 3; 1371 break; 1372 } 1373 } 1374 if (painted) { 1375 // This is tricky, there is never a good time for us 1376 // to push the value to the splitpane, painted appears to 1377 // the best time to do it. What is really needed is 1378 // notification that layout has completed. 1379 int newLocation = getDividerLocation(splitPane); 1380 1381 if (newLocation != (spDividerLocation - dOffset)) { 1382 int lastLocation = splitPane.getLastDividerLocation(); 1383 1384 ignoreDividerLocationChange = true; 1385 try { 1386 splitPane.setDividerLocation(newLocation); 1387 // This is not always needed, but is rather tricky 1388 // to determine when... The case this is needed for 1389 // is if the user sets the divider location to some 1390 // bogus value, say 0, and the actual value is 1, the 1391 // call to setDividerLocation(1) will preserve the 1392 // old value of 0, when we really want the divider 1393 // location value before the call. This is needed for 1394 // the one touch buttons. 1395 splitPane.setLastDividerLocation(lastLocation); 1396 } finally { 1397 ignoreDividerLocationChange = false; 1398 } 1399 } 1400 } 1401 } 1402 1403 1404 /** 1405 * Adds the component at place. Place must be one of 1406 * JSplitPane.LEFT, RIGHT, TOP, BOTTOM, or null (for the 1407 * divider). 1408 */ 1409 public void addLayoutComponent(String place, Component component) { 1410 boolean isValid = true; 1411 1412 if(place != null) { 1413 if(place.equals(JSplitPane.DIVIDER)) { 1414 /* Divider. */ 1415 components[2] = component; 1416 sizes[2] = getSizeForPrimaryAxis(component. 1417 getPreferredSize()); 1418 } else if(place.equals(JSplitPane.LEFT) || 1419 place.equals(JSplitPane.TOP)) { 1420 components[0] = component; 1421 sizes[0] = 0; 1422 } else if(place.equals(JSplitPane.RIGHT) || 1423 place.equals(JSplitPane.BOTTOM)) { 1424 components[1] = component; 1425 sizes[1] = 0; 1426 } else if(!place.equals( 1427 BasicSplitPaneUI.NON_CONTINUOUS_DIVIDER)) 1428 isValid = false; 1429 } else { 1430 isValid = false; 1431 } 1432 if(!isValid) 1433 throw new IllegalArgumentException("cannot add to layout: " + 1434 "unknown constraint: " + 1435 place); 1436 doReset = true; 1437 } 1438 1439 1440 /** 1441 * Returns the minimum size needed to contain the children. 1442 * The width is the sum of all the children's min widths and 1443 * the height is the largest of the children's minimum heights. 1444 */ 1445 public Dimension minimumLayoutSize(Container container) { 1446 int minPrimary = 0; 1447 int minSecondary = 0; 1448 Insets insets = splitPane.getInsets(); 1449 1450 for (int counter=0; counter<3; counter++) { 1451 if(components[counter] != null) { 1452 Dimension minSize = components[counter].getMinimumSize(); 1453 int secSize = getSizeForSecondaryAxis(minSize); 1454 1455 minPrimary += getSizeForPrimaryAxis(minSize); 1456 if(secSize > minSecondary) 1457 minSecondary = secSize; 1458 } 1459 } 1460 if(insets != null) { 1461 minPrimary += getSizeForPrimaryAxis(insets, true) + 1462 getSizeForPrimaryAxis(insets, false); 1463 minSecondary += getSizeForSecondaryAxis(insets, true) + 1464 getSizeForSecondaryAxis(insets, false); 1465 } 1466 if (axis == 0) { 1467 return new Dimension(minPrimary, minSecondary); 1468 } 1469 return new Dimension(minSecondary, minPrimary); 1470 } 1471 1472 1473 /** 1474 * Returns the preferred size needed to contain the children. 1475 * The width is the sum of all the preferred widths of the children and 1476 * the height is the largest preferred height of the children. 1477 */ 1478 public Dimension preferredLayoutSize(Container container) { 1479 int prePrimary = 0; 1480 int preSecondary = 0; 1481 Insets insets = splitPane.getInsets(); 1482 1483 for(int counter = 0; counter < 3; counter++) { 1484 if(components[counter] != null) { 1485 Dimension preSize = components[counter]. 1486 getPreferredSize(); 1487 int secSize = getSizeForSecondaryAxis(preSize); 1488 1489 prePrimary += getSizeForPrimaryAxis(preSize); 1490 if(secSize > preSecondary) 1491 preSecondary = secSize; 1492 } 1493 } 1494 if(insets != null) { 1495 prePrimary += getSizeForPrimaryAxis(insets, true) + 1496 getSizeForPrimaryAxis(insets, false); 1497 preSecondary += getSizeForSecondaryAxis(insets, true) + 1498 getSizeForSecondaryAxis(insets, false); 1499 } 1500 if (axis == 0) { 1501 return new Dimension(prePrimary, preSecondary); 1502 } 1503 return new Dimension(preSecondary, prePrimary); 1504 } 1505 1506 1507 /** 1508 * Removes the specified component from our knowledge. 1509 */ 1510 public void removeLayoutComponent(Component component) { 1511 for(int counter = 0; counter < 3; counter++) { 1512 if(components[counter] == component) { 1513 components[counter] = null; 1514 sizes[counter] = 0; 1515 doReset = true; 1516 } 1517 } 1518 } 1519 1520 1521 // 1522 // LayoutManager2 1523 // 1524 1525 1526 /** 1527 * Adds the specified component to the layout, using the specified 1528 * constraint object. 1529 * @param comp the component to be added 1530 * @param constraints where/how the component is added to the layout. 1531 */ 1532 public void addLayoutComponent(Component comp, Object constraints) { 1533 if ((constraints == null) || (constraints instanceof String)) { 1534 addLayoutComponent((String)constraints, comp); 1535 } else { 1536 throw new IllegalArgumentException("cannot add to layout: " + 1537 "constraint must be a " + 1538 "string (or null)"); 1539 } 1540 } 1541 1542 1543 /** 1544 * Returns the alignment along the x axis. This specifies how 1545 * the component would like to be aligned relative to other 1546 * components. The value should be a number between 0 and 1 1547 * where 0 represents alignment along the origin, 1 is aligned 1548 * the furthest away from the origin, 0.5 is centered, etc. 1549 */ 1550 public float getLayoutAlignmentX(Container target) { 1551 return 0.0f; 1552 } 1553 1554 1555 /** 1556 * Returns the alignment along the y axis. This specifies how 1557 * the component would like to be aligned relative to other 1558 * components. The value should be a number between 0 and 1 1559 * where 0 represents alignment along the origin, 1 is aligned 1560 * the furthest away from the origin, 0.5 is centered, etc. 1561 */ 1562 public float getLayoutAlignmentY(Container target) { 1563 return 0.0f; 1564 } 1565 1566 1567 /** 1568 * Does nothing. If the developer really wants to change the 1569 * size of one of the views JSplitPane.resetToPreferredSizes should 1570 * be messaged. 1571 */ 1572 public void invalidateLayout(Container c) { 1573 } 1574 1575 1576 /** 1577 * Returns the maximum layout size, which is Integer.MAX_VALUE 1578 * in both directions. 1579 */ 1580 public Dimension maximumLayoutSize(Container target) { 1581 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 1582 } 1583 1584 1585 // 1586 // New methods. 1587 // 1588 1589 /** 1590 * Marks the receiver so that the next time this instance is 1591 * laid out it'll ask for the preferred sizes. 1592 */ 1593 public void resetToPreferredSizes() { 1594 doReset = true; 1595 } 1596 1597 /** 1598 * Resets the size of the Component at the passed in location. 1599 */ 1600 protected void resetSizeAt(int index) { 1601 sizes[index] = 0; 1602 doReset = true; 1603 } 1604 1605 1606 /** 1607 * Sets the sizes to <code>newSizes</code>. 1608 */ 1609 protected void setSizes(int[] newSizes) { 1610 System.arraycopy(newSizes, 0, sizes, 0, 3); 1611 } 1612 1613 1614 /** 1615 * Returns the sizes of the components. 1616 */ 1617 protected int[] getSizes() { 1618 int[] retSizes = new int[3]; 1619 1620 System.arraycopy(sizes, 0, retSizes, 0, 3); 1621 return retSizes; 1622 } 1623 1624 1625 /** 1626 * Returns the width of the passed in Components preferred size. 1627 */ 1628 protected int getPreferredSizeOfComponent(Component c) { 1629 return getSizeForPrimaryAxis(c.getPreferredSize()); 1630 } 1631 1632 1633 /** 1634 * Returns the width of the passed in Components minimum size. 1635 */ 1636 int getMinimumSizeOfComponent(Component c) { 1637 return getSizeForPrimaryAxis(c.getMinimumSize()); 1638 } 1639 1640 1641 /** 1642 * Returns the width of the passed in component. 1643 */ 1644 protected int getSizeOfComponent(Component c) { 1645 return getSizeForPrimaryAxis(c.getSize()); 1646 } 1647 1648 1649 /** 1650 * Returns the available width based on the container size and 1651 * Insets. 1652 */ 1653 protected int getAvailableSize(Dimension containerSize, 1654 Insets insets) { 1655 if(insets == null) 1656 return getSizeForPrimaryAxis(containerSize); 1657 return (getSizeForPrimaryAxis(containerSize) - 1658 (getSizeForPrimaryAxis(insets, true) + 1659 getSizeForPrimaryAxis(insets, false))); 1660 } 1661 1662 1663 /** 1664 * Returns the left inset, unless the Insets are null in which case 1665 * 0 is returned. 1666 */ 1667 protected int getInitialLocation(Insets insets) { 1668 if(insets != null) 1669 return getSizeForPrimaryAxis(insets, true); 1670 return 0; 1671 } 1672 1673 1674 /** 1675 * Sets the width of the component c to be size, placing its 1676 * x location at location, y to the insets.top and height 1677 * to the containersize.height less the top and bottom insets. 1678 */ 1679 protected void setComponentToSize(Component c, int size, 1680 int location, Insets insets, 1681 Dimension containerSize) { 1682 if(insets != null) { 1683 if (axis == 0) { 1684 c.setBounds(location, insets.top, size, 1685 containerSize.height - 1686 (insets.top + insets.bottom)); 1687 } 1688 else { 1689 c.setBounds(insets.left, location, containerSize.width - 1690 (insets.left + insets.right), size); 1691 } 1692 } 1693 else { 1694 if (axis == 0) { 1695 c.setBounds(location, 0, size, containerSize.height); 1696 } 1697 else { 1698 c.setBounds(0, location, containerSize.width, size); 1699 } 1700 } 1701 } 1702 1703 /** 1704 * If the axis == 0, the width is returned, otherwise the height. 1705 */ 1706 int getSizeForPrimaryAxis(Dimension size) { 1707 if (axis == 0) { 1708 return size.width; 1709 } 1710 return size.height; 1711 } 1712 1713 /** 1714 * If the axis == 0, the width is returned, otherwise the height. 1715 */ 1716 int getSizeForSecondaryAxis(Dimension size) { 1717 if (axis == 0) { 1718 return size.height; 1719 } 1720 return size.width; 1721 } 1722 1723 /** 1724 * Returns a particular value of the inset identified by the 1725 * axis and <code>isTop</code><p> 1726 * axis isTop 1727 * 0 true - left 1728 * 0 false - right 1729 * 1 true - top 1730 * 1 false - bottom 1731 */ 1732 int getSizeForPrimaryAxis(Insets insets, boolean isTop) { 1733 if (axis == 0) { 1734 if (isTop) { 1735 return insets.left; 1736 } 1737 return insets.right; 1738 } 1739 if (isTop) { 1740 return insets.top; 1741 } 1742 return insets.bottom; 1743 } 1744 1745 /** 1746 * Returns a particular value of the inset identified by the 1747 * axis and <code>isTop</code><p> 1748 * axis isTop 1749 * 0 true - left 1750 * 0 false - right 1751 * 1 true - top 1752 * 1 false - bottom 1753 */ 1754 int getSizeForSecondaryAxis(Insets insets, boolean isTop) { 1755 if (axis == 0) { 1756 if (isTop) { 1757 return insets.top; 1758 } 1759 return insets.bottom; 1760 } 1761 if (isTop) { 1762 return insets.left; 1763 } 1764 return insets.right; 1765 } 1766 1767 /** 1768 * Determines the components. This should be called whenever 1769 * a new instance of this is installed into an existing 1770 * SplitPane. 1771 */ 1772 protected void updateComponents() { 1773 Component comp; 1774 1775 comp = splitPane.getLeftComponent(); 1776 if(components[0] != comp) { 1777 components[0] = comp; 1778 if(comp == null) { 1779 sizes[0] = 0; 1780 } else { 1781 sizes[0] = -1; 1782 } 1783 } 1784 1785 comp = splitPane.getRightComponent(); 1786 if(components[1] != comp) { 1787 components[1] = comp; 1788 if(comp == null) { 1789 sizes[1] = 0; 1790 } else { 1791 sizes[1] = -1; 1792 } 1793 } 1794 1795 /* Find the divider. */ 1796 Component[] children = splitPane.getComponents(); 1797 Component oldDivider = components[2]; 1798 1799 components[2] = null; 1800 for(int counter = children.length - 1; counter >= 0; counter--) { 1801 if(children[counter] != components[0] && 1802 children[counter] != components[1] && 1803 children[counter] != nonContinuousLayoutDivider) { 1804 if(oldDivider != children[counter]) { 1805 components[2] = children[counter]; 1806 } else { 1807 components[2] = oldDivider; 1808 } 1809 break; 1810 } 1811 } 1812 if(components[2] == null) { 1813 sizes[2] = 0; 1814 } 1815 else { 1816 sizes[2] = getSizeForPrimaryAxis(components[2].getPreferredSize()); 1817 } 1818 } 1819 1820 /** 1821 * Resets the size of the first component to <code>leftSize</code>, 1822 * and the right component to the remainder of the space. 1823 */ 1824 void setDividerLocation(int leftSize, int availableSize) { 1825 boolean lValid = (components[0] != null && 1826 components[0].isVisible()); 1827 boolean rValid = (components[1] != null && 1828 components[1].isVisible()); 1829 boolean dValid = (components[2] != null && 1830 components[2].isVisible()); 1831 int max = availableSize; 1832 1833 if (dValid) { 1834 max -= sizes[2]; 1835 } 1836 leftSize = Math.max(0, Math.min(leftSize, max)); 1837 if (lValid) { 1838 if (rValid) { 1839 sizes[0] = leftSize; 1840 sizes[1] = max - leftSize; 1841 } 1842 else { 1843 sizes[0] = max; 1844 sizes[1] = 0; 1845 } 1846 } 1847 else if (rValid) { 1848 sizes[1] = max; 1849 sizes[0] = 0; 1850 } 1851 } 1852 1853 /** 1854 * Returns an array of the minimum sizes of the components. 1855 */ 1856 int[] getPreferredSizes() { 1857 int[] retValue = new int[3]; 1858 1859 for (int counter = 0; counter < 3; counter++) { 1860 if (components[counter] != null && 1861 components[counter].isVisible()) { 1862 retValue[counter] = getPreferredSizeOfComponent 1863 (components[counter]); 1864 } 1865 else { 1866 retValue[counter] = -1; 1867 } 1868 } 1869 return retValue; 1870 } 1871 1872 /** 1873 * Returns an array of the minimum sizes of the components. 1874 */ 1875 int[] getMinimumSizes() { 1876 int[] retValue = new int[3]; 1877 1878 for (int counter = 0; counter < 2; counter++) { 1879 if (components[counter] != null && 1880 components[counter].isVisible()) { 1881 retValue[counter] = getMinimumSizeOfComponent 1882 (components[counter]); 1883 } 1884 else { 1885 retValue[counter] = -1; 1886 } 1887 } 1888 retValue[2] = (components[2] != null) ? 1889 getMinimumSizeOfComponent(components[2]) : -1; 1890 return retValue; 1891 } 1892 1893 /** 1894 * Resets the components to their preferred sizes. 1895 */ 1896 void resetToPreferredSizes(int availableSize) { 1897 // Set the sizes to the preferred sizes (if fits), otherwise 1898 // set to min sizes and distribute any extra space. 1899 int[] testSizes = getPreferredSizes(); 1900 int totalSize = 0; 1901 1902 for (int counter = 0; counter < 3; counter++) { 1903 if (testSizes[counter] != -1) { 1904 totalSize += testSizes[counter]; 1905 } 1906 } 1907 if (totalSize > availableSize) { 1908 testSizes = getMinimumSizes(); 1909 1910 totalSize = 0; 1911 for (int counter = 0; counter < 3; counter++) { 1912 if (testSizes[counter] != -1) { 1913 totalSize += testSizes[counter]; 1914 } 1915 } 1916 } 1917 setSizes(testSizes); 1918 distributeSpace(availableSize - totalSize, false); 1919 } 1920 1921 /** 1922 * Distributes <code>space</code> between the two components 1923 * (divider won't get any extra space) based on the weighting. This 1924 * attempts to honor the min size of the components. 1925 * 1926 * @param keepHidden if true and one of the components is 0x0 1927 * it gets none of the extra space 1928 */ 1929 void distributeSpace(int space, boolean keepHidden) { 1930 boolean lValid = (components[0] != null && 1931 components[0].isVisible()); 1932 boolean rValid = (components[1] != null && 1933 components[1].isVisible()); 1934 1935 if (keepHidden) { 1936 if (lValid && getSizeForPrimaryAxis( 1937 components[0].getSize()) == 0) { 1938 lValid = false; 1939 if (rValid && getSizeForPrimaryAxis( 1940 components[1].getSize()) == 0) { 1941 // Both aren't valid, force them both to be valid 1942 lValid = true; 1943 } 1944 } 1945 else if (rValid && getSizeForPrimaryAxis( 1946 components[1].getSize()) == 0) { 1947 rValid = false; 1948 } 1949 } 1950 if (lValid && rValid) { 1951 double weight = splitPane.getResizeWeight(); 1952 int lExtra = (int)(weight * (double)space); 1953 int rExtra = (space - lExtra); 1954 1955 sizes[0] += lExtra; 1956 sizes[1] += rExtra; 1957 1958 int lMin = getMinimumSizeOfComponent(components[0]); 1959 int rMin = getMinimumSizeOfComponent(components[1]); 1960 boolean lMinValid = (sizes[0] >= lMin); 1961 boolean rMinValid = (sizes[1] >= rMin); 1962 1963 if (!lMinValid && !rMinValid) { 1964 if (sizes[0] < 0) { 1965 sizes[1] += sizes[0]; 1966 sizes[0] = 0; 1967 } 1968 else if (sizes[1] < 0) { 1969 sizes[0] += sizes[1]; 1970 sizes[1] = 0; 1971 } 1972 } 1973 else if (!lMinValid) { 1974 if (sizes[1] - (lMin - sizes[0]) < rMin) { 1975 // both below min, just make sure > 0 1976 if (sizes[0] < 0) { 1977 sizes[1] += sizes[0]; 1978 sizes[0] = 0; 1979 } 1980 } 1981 else { 1982 sizes[1] -= (lMin - sizes[0]); 1983 sizes[0] = lMin; 1984 } 1985 } 1986 else if (!rMinValid) { 1987 if (sizes[0] - (rMin - sizes[1]) < lMin) { 1988 // both below min, just make sure > 0 1989 if (sizes[1] < 0) { 1990 sizes[0] += sizes[1]; 1991 sizes[1] = 0; 1992 } 1993 } 1994 else { 1995 sizes[0] -= (rMin - sizes[1]); 1996 sizes[1] = rMin; 1997 } 1998 } 1999 if (sizes[0] < 0) { 2000 sizes[0] = 0; 2001 } 2002 if (sizes[1] < 0) { 2003 sizes[1] = 0; 2004 } 2005 } 2006 else if (lValid) { 2007 sizes[0] = Math.max(0, sizes[0] + space); 2008 } 2009 else if (rValid) { 2010 sizes[1] = Math.max(0, sizes[1] + space); 2011 } 2012 } 2013 } 2014 2015 2016 /** 2017 * LayoutManager used for JSplitPanes with an orientation of 2018 * VERTICAL_SPLIT. 2019 * 2020 */ 2021 public class BasicVerticalLayoutManager extends 2022 BasicHorizontalLayoutManager 2023 { 2024 public BasicVerticalLayoutManager() { 2025 super(1); 2026 } 2027 } 2028 2029 2030 private class Handler implements FocusListener, PropertyChangeListener { 2031 // 2032 // PropertyChangeListener 2033 // 2034 /** 2035 * Messaged from the <code>JSplitPane</code> the receiver is 2036 * contained in. May potentially reset the layout manager and cause a 2037 * <code>validate</code> to be sent. 2038 */ 2039 public void propertyChange(PropertyChangeEvent e) { 2040 if(e.getSource() == splitPane) { 2041 String changeName = e.getPropertyName(); 2042 2043 if(changeName == JSplitPane.ORIENTATION_PROPERTY) { 2044 orientation = splitPane.getOrientation(); 2045 resetLayoutManager(); 2046 } else if(changeName == JSplitPane.CONTINUOUS_LAYOUT_PROPERTY){ 2047 setContinuousLayout(splitPane.isContinuousLayout()); 2048 if(!isContinuousLayout()) { 2049 if(nonContinuousLayoutDivider == null) { 2050 setNonContinuousLayoutDivider( 2051 createDefaultNonContinuousLayoutDivider(), 2052 true); 2053 } else if(nonContinuousLayoutDivider.getParent() == 2054 null) { 2055 setNonContinuousLayoutDivider( 2056 nonContinuousLayoutDivider, 2057 true); 2058 } 2059 } 2060 } else if(changeName == JSplitPane.DIVIDER_SIZE_PROPERTY){ 2061 divider.setDividerSize(splitPane.getDividerSize()); 2062 dividerSize = divider.getDividerSize(); 2063 splitPane.revalidate(); 2064 splitPane.repaint(); 2065 } 2066 } 2067 } 2068 2069 // 2070 // FocusListener 2071 // 2072 public void focusGained(FocusEvent ev) { 2073 dividerKeyboardResize = true; 2074 splitPane.repaint(); 2075 } 2076 2077 public void focusLost(FocusEvent ev) { 2078 dividerKeyboardResize = false; 2079 splitPane.repaint(); 2080 } 2081 } 2082 2083 2084 private static class Actions extends UIAction { 2085 private static final String NEGATIVE_INCREMENT = "negativeIncrement"; 2086 private static final String POSITIVE_INCREMENT = "positiveIncrement"; 2087 private static final String SELECT_MIN = "selectMin"; 2088 private static final String SELECT_MAX = "selectMax"; 2089 private static final String START_RESIZE = "startResize"; 2090 private static final String TOGGLE_FOCUS = "toggleFocus"; 2091 private static final String FOCUS_OUT_FORWARD = "focusOutForward"; 2092 private static final String FOCUS_OUT_BACKWARD = "focusOutBackward"; 2093 2094 Actions(String key) { 2095 super(key); 2096 } 2097 2098 public void actionPerformed(ActionEvent ev) { 2099 JSplitPane splitPane = (JSplitPane)ev.getSource(); 2100 BasicSplitPaneUI ui = (BasicSplitPaneUI)BasicLookAndFeel. 2101 getUIOfType(splitPane.getUI(), BasicSplitPaneUI.class); 2102 2103 if (ui == null) { 2104 return; 2105 } 2106 String key = getName(); 2107 if (key == NEGATIVE_INCREMENT) { 2108 if (ui.dividerKeyboardResize) { 2109 splitPane.setDividerLocation(Math.max( 2110 0, ui.getDividerLocation 2111 (splitPane) - ui.getKeyboardMoveIncrement())); 2112 } 2113 } 2114 else if (key == POSITIVE_INCREMENT) { 2115 if (ui.dividerKeyboardResize) { 2116 splitPane.setDividerLocation( 2117 ui.getDividerLocation(splitPane) + 2118 ui.getKeyboardMoveIncrement()); 2119 } 2120 } 2121 else if (key == SELECT_MIN) { 2122 if (ui.dividerKeyboardResize) { 2123 splitPane.setDividerLocation(0); 2124 } 2125 } 2126 else if (key == SELECT_MAX) { 2127 if (ui.dividerKeyboardResize) { 2128 Insets insets = splitPane.getInsets(); 2129 int bottomI = (insets != null) ? insets.bottom : 0; 2130 int rightI = (insets != null) ? insets.right : 0; 2131 2132 if (ui.orientation == JSplitPane.VERTICAL_SPLIT) { 2133 splitPane.setDividerLocation(splitPane.getHeight() - 2134 bottomI); 2135 } 2136 else { 2137 splitPane.setDividerLocation(splitPane.getWidth() - 2138 rightI); 2139 } 2140 } 2141 } 2142 else if (key == START_RESIZE) { 2143 if (!ui.dividerKeyboardResize) { 2144 splitPane.requestFocus(); 2145 } else { 2146 JSplitPane parentSplitPane = 2147 (JSplitPane)SwingUtilities.getAncestorOfClass( 2148 JSplitPane.class, splitPane); 2149 if (parentSplitPane!=null) { 2150 parentSplitPane.requestFocus(); 2151 } 2152 } 2153 } 2154 else if (key == TOGGLE_FOCUS) { 2155 toggleFocus(splitPane); 2156 } 2157 else if (key == FOCUS_OUT_FORWARD) { 2158 moveFocus(splitPane, 1); 2159 } 2160 else if (key == FOCUS_OUT_BACKWARD) { 2161 moveFocus(splitPane, -1); 2162 } 2163 } 2164 2165 private void moveFocus(JSplitPane splitPane, int direction) { 2166 Container rootAncestor = splitPane.getFocusCycleRootAncestor(); 2167 FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy(); 2168 Component focusOn = (direction > 0) ? 2169 policy.getComponentAfter(rootAncestor, splitPane) : 2170 policy.getComponentBefore(rootAncestor, splitPane); 2171 HashSet<Component> focusFrom = new HashSet<Component>(); 2172 if (splitPane.isAncestorOf(focusOn)) { 2173 do { 2174 focusFrom.add(focusOn); 2175 rootAncestor = focusOn.getFocusCycleRootAncestor(); 2176 policy = rootAncestor.getFocusTraversalPolicy(); 2177 focusOn = (direction > 0) ? 2178 policy.getComponentAfter(rootAncestor, focusOn) : 2179 policy.getComponentBefore(rootAncestor, focusOn); 2180 } while (splitPane.isAncestorOf(focusOn) && 2181 !focusFrom.contains(focusOn)); 2182 } 2183 if ( focusOn!=null && !splitPane.isAncestorOf(focusOn) ) { 2184 focusOn.requestFocus(); 2185 } 2186 } 2187 2188 private void toggleFocus(JSplitPane splitPane) { 2189 Component left = splitPane.getLeftComponent(); 2190 Component right = splitPane.getRightComponent(); 2191 2192 KeyboardFocusManager manager = 2193 KeyboardFocusManager.getCurrentKeyboardFocusManager(); 2194 Component focus = manager.getFocusOwner(); 2195 Component focusOn = getNextSide(splitPane, focus); 2196 if (focusOn != null) { 2197 // don't change the focus if the new focused component belongs 2198 // to the same splitpane and the same side 2199 if ( focus!=null && 2200 ( (SwingUtilities.isDescendingFrom(focus, left) && 2201 SwingUtilities.isDescendingFrom(focusOn, left)) || 2202 (SwingUtilities.isDescendingFrom(focus, right) && 2203 SwingUtilities.isDescendingFrom(focusOn, right)) ) ) { 2204 return; 2205 } 2206 SwingUtilities2.compositeRequestFocus(focusOn); 2207 } 2208 } 2209 2210 private Component getNextSide(JSplitPane splitPane, Component focus) { 2211 Component left = splitPane.getLeftComponent(); 2212 Component right = splitPane.getRightComponent(); 2213 Component next; 2214 if (focus!=null && SwingUtilities.isDescendingFrom(focus, left) && 2215 right!=null) { 2216 next = getFirstAvailableComponent(right); 2217 if (next != null) { 2218 return next; 2219 } 2220 } 2221 JSplitPane parentSplitPane = (JSplitPane)SwingUtilities.getAncestorOfClass(JSplitPane.class, splitPane); 2222 if (parentSplitPane!=null) { 2223 // focus next side of the parent split pane 2224 next = getNextSide(parentSplitPane, focus); 2225 } else { 2226 next = getFirstAvailableComponent(left); 2227 if (next == null) { 2228 next = getFirstAvailableComponent(right); 2229 } 2230 } 2231 return next; 2232 } 2233 2234 private Component getFirstAvailableComponent(Component c) { 2235 if (c!=null && c instanceof JSplitPane) { 2236 JSplitPane sp = (JSplitPane)c; 2237 Component left = getFirstAvailableComponent(sp.getLeftComponent()); 2238 if (left != null) { 2239 c = left; 2240 } else { 2241 c = getFirstAvailableComponent(sp.getRightComponent()); 2242 } 2243 } 2244 return c; 2245 } 2246 } 2247 }