1 /* 2 * Copyright (c) 1997, 2011, 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 protected Component createDefaultNonContinuousLayoutDivider() { 833 return new Canvas() { 834 public void paint(Graphics g) { 835 if(!isContinuousLayout() && getLastDragLocation() != -1) { 836 Dimension size = splitPane.getSize(); 837 838 g.setColor(dividerDraggingColor); 839 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 840 g.fillRect(0, 0, dividerSize - 1, size.height - 1); 841 } else { 842 g.fillRect(0, 0, size.width - 1, dividerSize - 1); 843 } 844 } 845 } 846 }; 847 } 848 849 850 /** 851 * Sets the divider to use when the splitPane is configured to 852 * not continuously layout. This divider will only be used during a 853 * dragging session. It is recommended that the passed in component 854 * be a heavy weight. 855 */ 856 protected void setNonContinuousLayoutDivider(Component newDivider) { 857 setNonContinuousLayoutDivider(newDivider, true); 858 } 859 860 861 /** 862 * Sets the divider to use. 863 */ 864 protected void setNonContinuousLayoutDivider(Component newDivider, 865 boolean rememberSizes) { 866 rememberPaneSizes = rememberSizes; 867 if(nonContinuousLayoutDivider != null && splitPane != null) { 868 splitPane.remove(nonContinuousLayoutDivider); 869 } 870 nonContinuousLayoutDivider = newDivider; 871 } 872 873 private void addHeavyweightDivider() { 874 if(nonContinuousLayoutDivider != null && splitPane != null) { 875 876 /* Needs to remove all the components and re-add them! YECK! */ 877 // This is all done so that the nonContinuousLayoutDivider will 878 // be drawn on top of the other components, without this, one 879 // of the heavyweights will draw over the divider! 880 Component leftC = splitPane.getLeftComponent(); 881 Component rightC = splitPane.getRightComponent(); 882 int lastLocation = splitPane. 883 getDividerLocation(); 884 885 if(leftC != null) 886 splitPane.setLeftComponent(null); 887 if(rightC != null) 888 splitPane.setRightComponent(null); 889 splitPane.remove(divider); 890 splitPane.add(nonContinuousLayoutDivider, BasicSplitPaneUI. 891 NON_CONTINUOUS_DIVIDER, 892 splitPane.getComponentCount()); 893 splitPane.setLeftComponent(leftC); 894 splitPane.setRightComponent(rightC); 895 splitPane.add(divider, JSplitPane.DIVIDER); 896 if(rememberPaneSizes) { 897 splitPane.setDividerLocation(lastLocation); 898 } 899 } 900 901 } 902 903 904 /** 905 * Returns the divider to use when the splitPane is configured to 906 * not continuously layout. This divider will only be used during a 907 * dragging session. 908 */ 909 public Component getNonContinuousLayoutDivider() { 910 return nonContinuousLayoutDivider; 911 } 912 913 914 /** 915 * Returns the splitpane this instance is currently contained 916 * in. 917 */ 918 public JSplitPane getSplitPane() { 919 return splitPane; 920 } 921 922 923 /** 924 * Creates the default divider. 925 */ 926 public BasicSplitPaneDivider createDefaultDivider() { 927 return new BasicSplitPaneDivider(this); 928 } 929 930 931 /** 932 * Messaged to reset the preferred sizes. 933 */ 934 public void resetToPreferredSizes(JSplitPane jc) { 935 if(splitPane != null) { 936 layoutManager.resetToPreferredSizes(); 937 splitPane.revalidate(); 938 splitPane.repaint(); 939 } 940 } 941 942 943 /** 944 * Sets the location of the divider to location. 945 */ 946 public void setDividerLocation(JSplitPane jc, int location) { 947 if (!ignoreDividerLocationChange) { 948 dividerLocationIsSet = true; 949 splitPane.revalidate(); 950 splitPane.repaint(); 951 952 if (keepHidden) { 953 Insets insets = splitPane.getInsets(); 954 int orientation = splitPane.getOrientation(); 955 if ((orientation == JSplitPane.VERTICAL_SPLIT && 956 location != insets.top && 957 location != splitPane.getHeight()-divider.getHeight()-insets.top) || 958 (orientation == JSplitPane.HORIZONTAL_SPLIT && 959 location != insets.left && 960 location != splitPane.getWidth()-divider.getWidth()-insets.left)) { 961 setKeepHidden(false); 962 } 963 } 964 } 965 else { 966 ignoreDividerLocationChange = false; 967 } 968 } 969 970 971 /** 972 * Returns the location of the divider, which may differ from what 973 * the splitpane thinks the location of the divider is. 974 */ 975 public int getDividerLocation(JSplitPane jc) { 976 if(orientation == JSplitPane.HORIZONTAL_SPLIT) 977 return divider.getLocation().x; 978 return divider.getLocation().y; 979 } 980 981 982 /** 983 * Gets the minimum location of the divider. 984 */ 985 public int getMinimumDividerLocation(JSplitPane jc) { 986 int minLoc = 0; 987 Component leftC = splitPane.getLeftComponent(); 988 989 if ((leftC != null) && (leftC.isVisible())) { 990 Insets insets = splitPane.getInsets(); 991 Dimension minSize = leftC.getMinimumSize(); 992 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 993 minLoc = minSize.width; 994 } else { 995 minLoc = minSize.height; 996 } 997 if(insets != null) { 998 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 999 minLoc += insets.left; 1000 } else { 1001 minLoc += insets.top; 1002 } 1003 } 1004 } 1005 return minLoc; 1006 } 1007 1008 1009 /** 1010 * Gets the maximum location of the divider. 1011 */ 1012 public int getMaximumDividerLocation(JSplitPane jc) { 1013 Dimension splitPaneSize = splitPane.getSize(); 1014 int maxLoc = 0; 1015 Component rightC = splitPane.getRightComponent(); 1016 1017 if (rightC != null) { 1018 Insets insets = splitPane.getInsets(); 1019 Dimension minSize = new Dimension(0, 0); 1020 if (rightC.isVisible()) { 1021 minSize = rightC.getMinimumSize(); 1022 } 1023 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1024 maxLoc = splitPaneSize.width - minSize.width; 1025 } else { 1026 maxLoc = splitPaneSize.height - minSize.height; 1027 } 1028 maxLoc -= dividerSize; 1029 if(insets != null) { 1030 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1031 maxLoc -= insets.right; 1032 } else { 1033 maxLoc -= insets.top; 1034 } 1035 } 1036 } 1037 return Math.max(getMinimumDividerLocation(splitPane), maxLoc); 1038 } 1039 1040 1041 /** 1042 * Called when the specified split pane has finished painting 1043 * its children. 1044 */ 1045 public void finishedPaintingChildren(JSplitPane sp, Graphics g) { 1046 if(sp == splitPane && getLastDragLocation() != -1 && 1047 !isContinuousLayout() && !draggingHW) { 1048 Dimension size = splitPane.getSize(); 1049 1050 g.setColor(dividerDraggingColor); 1051 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1052 g.fillRect(getLastDragLocation(), 0, dividerSize - 1, 1053 size.height - 1); 1054 } else { 1055 g.fillRect(0, lastDragLocation, size.width - 1, 1056 dividerSize - 1); 1057 } 1058 } 1059 } 1060 1061 1062 /** 1063 * {@inheritDoc} 1064 */ 1065 public void paint(Graphics g, JComponent jc) { 1066 if (!painted && splitPane.getDividerLocation()<0) { 1067 ignoreDividerLocationChange = true; 1068 splitPane.setDividerLocation(getDividerLocation(splitPane)); 1069 } 1070 painted = true; 1071 } 1072 1073 1074 /** 1075 * Returns the preferred size for the passed in component, 1076 * This is passed off to the current layout manager. 1077 */ 1078 public Dimension getPreferredSize(JComponent jc) { 1079 if(splitPane != null) 1080 return layoutManager.preferredLayoutSize(splitPane); 1081 return new Dimension(0, 0); 1082 } 1083 1084 1085 /** 1086 * Returns the minimum size for the passed in component, 1087 * This is passed off to the current layout manager. 1088 */ 1089 public Dimension getMinimumSize(JComponent jc) { 1090 if(splitPane != null) 1091 return layoutManager.minimumLayoutSize(splitPane); 1092 return new Dimension(0, 0); 1093 } 1094 1095 1096 /** 1097 * Returns the maximum size for the passed in component, 1098 * This is passed off to the current layout manager. 1099 */ 1100 public Dimension getMaximumSize(JComponent jc) { 1101 if(splitPane != null) 1102 return layoutManager.maximumLayoutSize(splitPane); 1103 return new Dimension(0, 0); 1104 } 1105 1106 1107 /** 1108 * Returns the insets. The insets are returned from the border insets 1109 * of the current border. 1110 */ 1111 public Insets getInsets(JComponent jc) { 1112 return null; 1113 } 1114 1115 1116 /** 1117 * Resets the layout manager based on orientation and messages it 1118 * with invalidateLayout to pull in appropriate Components. 1119 */ 1120 protected void resetLayoutManager() { 1121 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1122 layoutManager = new BasicHorizontalLayoutManager(0); 1123 } else { 1124 layoutManager = new BasicHorizontalLayoutManager(1); 1125 } 1126 splitPane.setLayout(layoutManager); 1127 layoutManager.updateComponents(); 1128 splitPane.revalidate(); 1129 splitPane.repaint(); 1130 } 1131 1132 /** 1133 * Set the value to indicate if one of the splitpane sides is expanded. 1134 */ 1135 void setKeepHidden(boolean keepHidden) { 1136 this.keepHidden = keepHidden; 1137 } 1138 1139 /** 1140 * The value returned indicates if one of the splitpane sides is expanded. 1141 * @return true if one of the splitpane sides is expanded, false otherwise. 1142 */ 1143 private boolean getKeepHidden() { 1144 return keepHidden; 1145 } 1146 1147 /** 1148 * Should be messaged before the dragging session starts, resets 1149 * lastDragLocation and dividerSize. 1150 */ 1151 protected void startDragging() { 1152 Component leftC = splitPane.getLeftComponent(); 1153 Component rightC = splitPane.getRightComponent(); 1154 ComponentPeer cPeer; 1155 1156 beginDragDividerLocation = getDividerLocation(splitPane); 1157 draggingHW = false; 1158 if(leftC != null && (cPeer = leftC.getPeer()) != null && 1159 !(cPeer instanceof LightweightPeer)) { 1160 draggingHW = true; 1161 } else if(rightC != null && (cPeer = rightC.getPeer()) != null 1162 && !(cPeer instanceof LightweightPeer)) { 1163 draggingHW = true; 1164 } 1165 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1166 setLastDragLocation(divider.getBounds().x); 1167 dividerSize = divider.getSize().width; 1168 if(!isContinuousLayout() && draggingHW) { 1169 nonContinuousLayoutDivider.setBounds 1170 (getLastDragLocation(), 0, dividerSize, 1171 splitPane.getHeight()); 1172 addHeavyweightDivider(); 1173 } 1174 } else { 1175 setLastDragLocation(divider.getBounds().y); 1176 dividerSize = divider.getSize().height; 1177 if(!isContinuousLayout() && draggingHW) { 1178 nonContinuousLayoutDivider.setBounds 1179 (0, getLastDragLocation(), splitPane.getWidth(), 1180 dividerSize); 1181 addHeavyweightDivider(); 1182 } 1183 } 1184 } 1185 1186 1187 /** 1188 * Messaged during a dragging session to move the divider to the 1189 * passed in location. If continuousLayout is true the location is 1190 * reset and the splitPane validated. 1191 */ 1192 protected void dragDividerTo(int location) { 1193 if(getLastDragLocation() != location) { 1194 if(isContinuousLayout()) { 1195 splitPane.setDividerLocation(location); 1196 setLastDragLocation(location); 1197 } else { 1198 int lastLoc = getLastDragLocation(); 1199 1200 setLastDragLocation(location); 1201 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1202 if(draggingHW) { 1203 nonContinuousLayoutDivider.setLocation( 1204 getLastDragLocation(), 0); 1205 } else { 1206 int splitHeight = splitPane.getHeight(); 1207 splitPane.repaint(lastLoc, 0, dividerSize, 1208 splitHeight); 1209 splitPane.repaint(location, 0, dividerSize, 1210 splitHeight); 1211 } 1212 } else { 1213 if(draggingHW) { 1214 nonContinuousLayoutDivider.setLocation(0, 1215 getLastDragLocation()); 1216 } else { 1217 int splitWidth = splitPane.getWidth(); 1218 1219 splitPane.repaint(0, lastLoc, splitWidth, 1220 dividerSize); 1221 splitPane.repaint(0, location, splitWidth, 1222 dividerSize); 1223 } 1224 } 1225 } 1226 } 1227 } 1228 1229 1230 /** 1231 * Messaged to finish the dragging session. If not continuous display 1232 * the dividers location will be reset. 1233 */ 1234 protected void finishDraggingTo(int location) { 1235 dragDividerTo(location); 1236 setLastDragLocation(-1); 1237 if(!isContinuousLayout()) { 1238 Component leftC = splitPane.getLeftComponent(); 1239 Rectangle leftBounds = leftC.getBounds(); 1240 1241 if (draggingHW) { 1242 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1243 nonContinuousLayoutDivider.setLocation(-dividerSize, 0); 1244 } 1245 else { 1246 nonContinuousLayoutDivider.setLocation(0, -dividerSize); 1247 } 1248 splitPane.remove(nonContinuousLayoutDivider); 1249 } 1250 splitPane.setDividerLocation(location); 1251 } 1252 } 1253 1254 1255 /** 1256 * As of Java 2 platform v1.3 this method is no longer used. Instead 1257 * you should set the border on the divider. 1258 * <p> 1259 * Returns the width of one side of the divider border. 1260 * 1261 * @deprecated As of Java 2 platform v1.3, instead set the border on the 1262 * divider. 1263 */ 1264 @Deprecated 1265 protected int getDividerBorderSize() { 1266 return 1; 1267 } 1268 1269 1270 /** 1271 * LayoutManager for JSplitPanes that have an orientation of 1272 * HORIZONTAL_SPLIT. 1273 */ 1274 public class BasicHorizontalLayoutManager implements LayoutManager2 1275 { 1276 /* left, right, divider. (in this exact order) */ 1277 protected int[] sizes; 1278 protected Component[] components; 1279 /** Size of the splitpane the last time laid out. */ 1280 private int lastSplitPaneSize; 1281 /** True if resetToPreferredSizes has been invoked. */ 1282 private boolean doReset; 1283 /** Axis, 0 for horizontal, or 1 for veritcal. */ 1284 private int axis; 1285 1286 1287 BasicHorizontalLayoutManager() { 1288 this(0); 1289 } 1290 1291 BasicHorizontalLayoutManager(int axis) { 1292 this.axis = axis; 1293 components = new Component[3]; 1294 components[0] = components[1] = components[2] = null; 1295 sizes = new int[3]; 1296 } 1297 1298 // 1299 // LayoutManager 1300 // 1301 1302 /** 1303 * Does the actual layout. 1304 */ 1305 public void layoutContainer(Container container) { 1306 Dimension containerSize = container.getSize(); 1307 1308 // If the splitpane has a zero size then no op out of here. 1309 // If we execute this function now, we're going to cause ourselves 1310 // much grief. 1311 if (containerSize.height <= 0 || containerSize.width <= 0 ) { 1312 lastSplitPaneSize = 0; 1313 return; 1314 } 1315 1316 int spDividerLocation = splitPane.getDividerLocation(); 1317 Insets insets = splitPane.getInsets(); 1318 int availableSize = getAvailableSize(containerSize, 1319 insets); 1320 int newSize = getSizeForPrimaryAxis(containerSize); 1321 int beginLocation = getDividerLocation(splitPane); 1322 int dOffset = getSizeForPrimaryAxis(insets, true); 1323 Dimension dSize = (components[2] == null) ? null : 1324 components[2].getPreferredSize(); 1325 1326 if ((doReset && !dividerLocationIsSet) || spDividerLocation < 0) { 1327 resetToPreferredSizes(availableSize); 1328 } 1329 else if (lastSplitPaneSize <= 0 || 1330 availableSize == lastSplitPaneSize || !painted || 1331 (dSize != null && 1332 getSizeForPrimaryAxis(dSize) != sizes[2])) { 1333 if (dSize != null) { 1334 sizes[2] = getSizeForPrimaryAxis(dSize); 1335 } 1336 else { 1337 sizes[2] = 0; 1338 } 1339 setDividerLocation(spDividerLocation - dOffset, availableSize); 1340 dividerLocationIsSet = false; 1341 } 1342 else if (availableSize != lastSplitPaneSize) { 1343 distributeSpace(availableSize - lastSplitPaneSize, 1344 getKeepHidden()); 1345 } 1346 doReset = false; 1347 dividerLocationIsSet = false; 1348 lastSplitPaneSize = availableSize; 1349 1350 // Reset the bounds of each component 1351 int nextLocation = getInitialLocation(insets); 1352 int counter = 0; 1353 1354 while (counter < 3) { 1355 if (components[counter] != null && 1356 components[counter].isVisible()) { 1357 setComponentToSize(components[counter], sizes[counter], 1358 nextLocation, insets, containerSize); 1359 nextLocation += sizes[counter]; 1360 } 1361 switch (counter) { 1362 case 0: 1363 counter = 2; 1364 break; 1365 case 2: 1366 counter = 1; 1367 break; 1368 case 1: 1369 counter = 3; 1370 break; 1371 } 1372 } 1373 if (painted) { 1374 // This is tricky, there is never a good time for us 1375 // to push the value to the splitpane, painted appears to 1376 // the best time to do it. What is really needed is 1377 // notification that layout has completed. 1378 int newLocation = getDividerLocation(splitPane); 1379 1380 if (newLocation != (spDividerLocation - dOffset)) { 1381 int lastLocation = splitPane.getLastDividerLocation(); 1382 1383 ignoreDividerLocationChange = true; 1384 try { 1385 splitPane.setDividerLocation(newLocation); 1386 // This is not always needed, but is rather tricky 1387 // to determine when... The case this is needed for 1388 // is if the user sets the divider location to some 1389 // bogus value, say 0, and the actual value is 1, the 1390 // call to setDividerLocation(1) will preserve the 1391 // old value of 0, when we really want the divider 1392 // location value before the call. This is needed for 1393 // the one touch buttons. 1394 splitPane.setLastDividerLocation(lastLocation); 1395 } finally { 1396 ignoreDividerLocationChange = false; 1397 } 1398 } 1399 } 1400 } 1401 1402 1403 /** 1404 * Adds the component at place. Place must be one of 1405 * JSplitPane.LEFT, RIGHT, TOP, BOTTOM, or null (for the 1406 * divider). 1407 */ 1408 public void addLayoutComponent(String place, Component component) { 1409 boolean isValid = true; 1410 1411 if(place != null) { 1412 if(place.equals(JSplitPane.DIVIDER)) { 1413 /* Divider. */ 1414 components[2] = component; 1415 sizes[2] = getSizeForPrimaryAxis(component. 1416 getPreferredSize()); 1417 } else if(place.equals(JSplitPane.LEFT) || 1418 place.equals(JSplitPane.TOP)) { 1419 components[0] = component; 1420 sizes[0] = 0; 1421 } else if(place.equals(JSplitPane.RIGHT) || 1422 place.equals(JSplitPane.BOTTOM)) { 1423 components[1] = component; 1424 sizes[1] = 0; 1425 } else if(!place.equals( 1426 BasicSplitPaneUI.NON_CONTINUOUS_DIVIDER)) 1427 isValid = false; 1428 } else { 1429 isValid = false; 1430 } 1431 if(!isValid) 1432 throw new IllegalArgumentException("cannot add to layout: " + 1433 "unknown constraint: " + 1434 place); 1435 doReset = true; 1436 } 1437 1438 1439 /** 1440 * Returns the minimum size needed to contain the children. 1441 * The width is the sum of all the children's min widths and 1442 * the height is the largest of the children's minimum heights. 1443 */ 1444 public Dimension minimumLayoutSize(Container container) { 1445 int minPrimary = 0; 1446 int minSecondary = 0; 1447 Insets insets = splitPane.getInsets(); 1448 1449 for (int counter=0; counter<3; counter++) { 1450 if(components[counter] != null) { 1451 Dimension minSize = components[counter].getMinimumSize(); 1452 int secSize = getSizeForSecondaryAxis(minSize); 1453 1454 minPrimary += getSizeForPrimaryAxis(minSize); 1455 if(secSize > minSecondary) 1456 minSecondary = secSize; 1457 } 1458 } 1459 if(insets != null) { 1460 minPrimary += getSizeForPrimaryAxis(insets, true) + 1461 getSizeForPrimaryAxis(insets, false); 1462 minSecondary += getSizeForSecondaryAxis(insets, true) + 1463 getSizeForSecondaryAxis(insets, false); 1464 } 1465 if (axis == 0) { 1466 return new Dimension(minPrimary, minSecondary); 1467 } 1468 return new Dimension(minSecondary, minPrimary); 1469 } 1470 1471 1472 /** 1473 * Returns the preferred size needed to contain the children. 1474 * The width is the sum of all the preferred widths of the children and 1475 * the height is the largest preferred height of the children. 1476 */ 1477 public Dimension preferredLayoutSize(Container container) { 1478 int prePrimary = 0; 1479 int preSecondary = 0; 1480 Insets insets = splitPane.getInsets(); 1481 1482 for(int counter = 0; counter < 3; counter++) { 1483 if(components[counter] != null) { 1484 Dimension preSize = components[counter]. 1485 getPreferredSize(); 1486 int secSize = getSizeForSecondaryAxis(preSize); 1487 1488 prePrimary += getSizeForPrimaryAxis(preSize); 1489 if(secSize > preSecondary) 1490 preSecondary = secSize; 1491 } 1492 } 1493 if(insets != null) { 1494 prePrimary += getSizeForPrimaryAxis(insets, true) + 1495 getSizeForPrimaryAxis(insets, false); 1496 preSecondary += getSizeForSecondaryAxis(insets, true) + 1497 getSizeForSecondaryAxis(insets, false); 1498 } 1499 if (axis == 0) { 1500 return new Dimension(prePrimary, preSecondary); 1501 } 1502 return new Dimension(preSecondary, prePrimary); 1503 } 1504 1505 1506 /** 1507 * Removes the specified component from our knowledge. 1508 */ 1509 public void removeLayoutComponent(Component component) { 1510 for(int counter = 0; counter < 3; counter++) { 1511 if(components[counter] == component) { 1512 components[counter] = null; 1513 sizes[counter] = 0; 1514 doReset = true; 1515 } 1516 } 1517 } 1518 1519 1520 // 1521 // LayoutManager2 1522 // 1523 1524 1525 /** 1526 * Adds the specified component to the layout, using the specified 1527 * constraint object. 1528 * @param comp the component to be added 1529 * @param constraints where/how the component is added to the layout. 1530 */ 1531 public void addLayoutComponent(Component comp, Object constraints) { 1532 if ((constraints == null) || (constraints instanceof String)) { 1533 addLayoutComponent((String)constraints, comp); 1534 } else { 1535 throw new IllegalArgumentException("cannot add to layout: " + 1536 "constraint must be a " + 1537 "string (or null)"); 1538 } 1539 } 1540 1541 1542 /** 1543 * Returns the alignment along the x axis. This specifies how 1544 * the component would like to be aligned relative to other 1545 * components. The value should be a number between 0 and 1 1546 * where 0 represents alignment along the origin, 1 is aligned 1547 * the furthest away from the origin, 0.5 is centered, etc. 1548 */ 1549 public float getLayoutAlignmentX(Container target) { 1550 return 0.0f; 1551 } 1552 1553 1554 /** 1555 * Returns the alignment along the y axis. This specifies how 1556 * the component would like to be aligned relative to other 1557 * components. The value should be a number between 0 and 1 1558 * where 0 represents alignment along the origin, 1 is aligned 1559 * the furthest away from the origin, 0.5 is centered, etc. 1560 */ 1561 public float getLayoutAlignmentY(Container target) { 1562 return 0.0f; 1563 } 1564 1565 1566 /** 1567 * Does nothing. If the developer really wants to change the 1568 * size of one of the views JSplitPane.resetToPreferredSizes should 1569 * be messaged. 1570 */ 1571 public void invalidateLayout(Container c) { 1572 } 1573 1574 1575 /** 1576 * Returns the maximum layout size, which is Integer.MAX_VALUE 1577 * in both directions. 1578 */ 1579 public Dimension maximumLayoutSize(Container target) { 1580 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 1581 } 1582 1583 1584 // 1585 // New methods. 1586 // 1587 1588 /** 1589 * Marks the receiver so that the next time this instance is 1590 * laid out it'll ask for the preferred sizes. 1591 */ 1592 public void resetToPreferredSizes() { 1593 doReset = true; 1594 } 1595 1596 /** 1597 * Resets the size of the Component at the passed in location. 1598 */ 1599 protected void resetSizeAt(int index) { 1600 sizes[index] = 0; 1601 doReset = true; 1602 } 1603 1604 1605 /** 1606 * Sets the sizes to <code>newSizes</code>. 1607 */ 1608 protected void setSizes(int[] newSizes) { 1609 System.arraycopy(newSizes, 0, sizes, 0, 3); 1610 } 1611 1612 1613 /** 1614 * Returns the sizes of the components. 1615 */ 1616 protected int[] getSizes() { 1617 int[] retSizes = new int[3]; 1618 1619 System.arraycopy(sizes, 0, retSizes, 0, 3); 1620 return retSizes; 1621 } 1622 1623 1624 /** 1625 * Returns the width of the passed in Components preferred size. 1626 */ 1627 protected int getPreferredSizeOfComponent(Component c) { 1628 return getSizeForPrimaryAxis(c.getPreferredSize()); 1629 } 1630 1631 1632 /** 1633 * Returns the width of the passed in Components minimum size. 1634 */ 1635 int getMinimumSizeOfComponent(Component c) { 1636 return getSizeForPrimaryAxis(c.getMinimumSize()); 1637 } 1638 1639 1640 /** 1641 * Returns the width of the passed in component. 1642 */ 1643 protected int getSizeOfComponent(Component c) { 1644 return getSizeForPrimaryAxis(c.getSize()); 1645 } 1646 1647 1648 /** 1649 * Returns the available width based on the container size and 1650 * Insets. 1651 */ 1652 protected int getAvailableSize(Dimension containerSize, 1653 Insets insets) { 1654 if(insets == null) 1655 return getSizeForPrimaryAxis(containerSize); 1656 return (getSizeForPrimaryAxis(containerSize) - 1657 (getSizeForPrimaryAxis(insets, true) + 1658 getSizeForPrimaryAxis(insets, false))); 1659 } 1660 1661 1662 /** 1663 * Returns the left inset, unless the Insets are null in which case 1664 * 0 is returned. 1665 */ 1666 protected int getInitialLocation(Insets insets) { 1667 if(insets != null) 1668 return getSizeForPrimaryAxis(insets, true); 1669 return 0; 1670 } 1671 1672 1673 /** 1674 * Sets the width of the component c to be size, placing its 1675 * x location at location, y to the insets.top and height 1676 * to the containersize.height less the top and bottom insets. 1677 */ 1678 protected void setComponentToSize(Component c, int size, 1679 int location, Insets insets, 1680 Dimension containerSize) { 1681 if(insets != null) { 1682 if (axis == 0) { 1683 c.setBounds(location, insets.top, size, 1684 containerSize.height - 1685 (insets.top + insets.bottom)); 1686 } 1687 else { 1688 c.setBounds(insets.left, location, containerSize.width - 1689 (insets.left + insets.right), size); 1690 } 1691 } 1692 else { 1693 if (axis == 0) { 1694 c.setBounds(location, 0, size, containerSize.height); 1695 } 1696 else { 1697 c.setBounds(0, location, containerSize.width, size); 1698 } 1699 } 1700 } 1701 1702 /** 1703 * If the axis == 0, the width is returned, otherwise the height. 1704 */ 1705 int getSizeForPrimaryAxis(Dimension size) { 1706 if (axis == 0) { 1707 return size.width; 1708 } 1709 return size.height; 1710 } 1711 1712 /** 1713 * If the axis == 0, the width is returned, otherwise the height. 1714 */ 1715 int getSizeForSecondaryAxis(Dimension size) { 1716 if (axis == 0) { 1717 return size.height; 1718 } 1719 return size.width; 1720 } 1721 1722 /** 1723 * Returns a particular value of the inset identified by the 1724 * axis and <code>isTop</code><p> 1725 * axis isTop 1726 * 0 true - left 1727 * 0 false - right 1728 * 1 true - top 1729 * 1 false - bottom 1730 */ 1731 int getSizeForPrimaryAxis(Insets insets, boolean isTop) { 1732 if (axis == 0) { 1733 if (isTop) { 1734 return insets.left; 1735 } 1736 return insets.right; 1737 } 1738 if (isTop) { 1739 return insets.top; 1740 } 1741 return insets.bottom; 1742 } 1743 1744 /** 1745 * Returns a particular value of the inset identified by the 1746 * axis and <code>isTop</code><p> 1747 * axis isTop 1748 * 0 true - left 1749 * 0 false - right 1750 * 1 true - top 1751 * 1 false - bottom 1752 */ 1753 int getSizeForSecondaryAxis(Insets insets, boolean isTop) { 1754 if (axis == 0) { 1755 if (isTop) { 1756 return insets.top; 1757 } 1758 return insets.bottom; 1759 } 1760 if (isTop) { 1761 return insets.left; 1762 } 1763 return insets.right; 1764 } 1765 1766 /** 1767 * Determines the components. This should be called whenever 1768 * a new instance of this is installed into an existing 1769 * SplitPane. 1770 */ 1771 protected void updateComponents() { 1772 Component comp; 1773 1774 comp = splitPane.getLeftComponent(); 1775 if(components[0] != comp) { 1776 components[0] = comp; 1777 if(comp == null) { 1778 sizes[0] = 0; 1779 } else { 1780 sizes[0] = -1; 1781 } 1782 } 1783 1784 comp = splitPane.getRightComponent(); 1785 if(components[1] != comp) { 1786 components[1] = comp; 1787 if(comp == null) { 1788 sizes[1] = 0; 1789 } else { 1790 sizes[1] = -1; 1791 } 1792 } 1793 1794 /* Find the divider. */ 1795 Component[] children = splitPane.getComponents(); 1796 Component oldDivider = components[2]; 1797 1798 components[2] = null; 1799 for(int counter = children.length - 1; counter >= 0; counter--) { 1800 if(children[counter] != components[0] && 1801 children[counter] != components[1] && 1802 children[counter] != nonContinuousLayoutDivider) { 1803 if(oldDivider != children[counter]) { 1804 components[2] = children[counter]; 1805 } else { 1806 components[2] = oldDivider; 1807 } 1808 break; 1809 } 1810 } 1811 if(components[2] == null) { 1812 sizes[2] = 0; 1813 } 1814 else { 1815 sizes[2] = getSizeForPrimaryAxis(components[2].getPreferredSize()); 1816 } 1817 } 1818 1819 /** 1820 * Resets the size of the first component to <code>leftSize</code>, 1821 * and the right component to the remainder of the space. 1822 */ 1823 void setDividerLocation(int leftSize, int availableSize) { 1824 boolean lValid = (components[0] != null && 1825 components[0].isVisible()); 1826 boolean rValid = (components[1] != null && 1827 components[1].isVisible()); 1828 boolean dValid = (components[2] != null && 1829 components[2].isVisible()); 1830 int max = availableSize; 1831 1832 if (dValid) { 1833 max -= sizes[2]; 1834 } 1835 leftSize = Math.max(0, Math.min(leftSize, max)); 1836 if (lValid) { 1837 if (rValid) { 1838 sizes[0] = leftSize; 1839 sizes[1] = max - leftSize; 1840 } 1841 else { 1842 sizes[0] = max; 1843 sizes[1] = 0; 1844 } 1845 } 1846 else if (rValid) { 1847 sizes[1] = max; 1848 sizes[0] = 0; 1849 } 1850 } 1851 1852 /** 1853 * Returns an array of the minimum sizes of the components. 1854 */ 1855 int[] getPreferredSizes() { 1856 int[] retValue = new int[3]; 1857 1858 for (int counter = 0; counter < 3; counter++) { 1859 if (components[counter] != null && 1860 components[counter].isVisible()) { 1861 retValue[counter] = getPreferredSizeOfComponent 1862 (components[counter]); 1863 } 1864 else { 1865 retValue[counter] = -1; 1866 } 1867 } 1868 return retValue; 1869 } 1870 1871 /** 1872 * Returns an array of the minimum sizes of the components. 1873 */ 1874 int[] getMinimumSizes() { 1875 int[] retValue = new int[3]; 1876 1877 for (int counter = 0; counter < 2; counter++) { 1878 if (components[counter] != null && 1879 components[counter].isVisible()) { 1880 retValue[counter] = getMinimumSizeOfComponent 1881 (components[counter]); 1882 } 1883 else { 1884 retValue[counter] = -1; 1885 } 1886 } 1887 retValue[2] = (components[2] != null) ? 1888 getMinimumSizeOfComponent(components[2]) : -1; 1889 return retValue; 1890 } 1891 1892 /** 1893 * Resets the components to their preferred sizes. 1894 */ 1895 void resetToPreferredSizes(int availableSize) { 1896 // Set the sizes to the preferred sizes (if fits), otherwise 1897 // set to min sizes and distribute any extra space. 1898 int[] testSizes = getPreferredSizes(); 1899 int totalSize = 0; 1900 1901 for (int counter = 0; counter < 3; counter++) { 1902 if (testSizes[counter] != -1) { 1903 totalSize += testSizes[counter]; 1904 } 1905 } 1906 if (totalSize > availableSize) { 1907 testSizes = getMinimumSizes(); 1908 1909 totalSize = 0; 1910 for (int counter = 0; counter < 3; counter++) { 1911 if (testSizes[counter] != -1) { 1912 totalSize += testSizes[counter]; 1913 } 1914 } 1915 } 1916 setSizes(testSizes); 1917 distributeSpace(availableSize - totalSize, false); 1918 } 1919 1920 /** 1921 * Distributes <code>space</code> between the two components 1922 * (divider won't get any extra space) based on the weighting. This 1923 * attempts to honor the min size of the components. 1924 * 1925 * @param keepHidden if true and one of the components is 0x0 1926 * it gets none of the extra space 1927 */ 1928 void distributeSpace(int space, boolean keepHidden) { 1929 boolean lValid = (components[0] != null && 1930 components[0].isVisible()); 1931 boolean rValid = (components[1] != null && 1932 components[1].isVisible()); 1933 1934 if (keepHidden) { 1935 if (lValid && getSizeForPrimaryAxis( 1936 components[0].getSize()) == 0) { 1937 lValid = false; 1938 if (rValid && getSizeForPrimaryAxis( 1939 components[1].getSize()) == 0) { 1940 // Both aren't valid, force them both to be valid 1941 lValid = true; 1942 } 1943 } 1944 else if (rValid && getSizeForPrimaryAxis( 1945 components[1].getSize()) == 0) { 1946 rValid = false; 1947 } 1948 } 1949 if (lValid && rValid) { 1950 double weight = splitPane.getResizeWeight(); 1951 int lExtra = (int)(weight * (double)space); 1952 int rExtra = (space - lExtra); 1953 1954 sizes[0] += lExtra; 1955 sizes[1] += rExtra; 1956 1957 int lMin = getMinimumSizeOfComponent(components[0]); 1958 int rMin = getMinimumSizeOfComponent(components[1]); 1959 boolean lMinValid = (sizes[0] >= lMin); 1960 boolean rMinValid = (sizes[1] >= rMin); 1961 1962 if (!lMinValid && !rMinValid) { 1963 if (sizes[0] < 0) { 1964 sizes[1] += sizes[0]; 1965 sizes[0] = 0; 1966 } 1967 else if (sizes[1] < 0) { 1968 sizes[0] += sizes[1]; 1969 sizes[1] = 0; 1970 } 1971 } 1972 else if (!lMinValid) { 1973 if (sizes[1] - (lMin - sizes[0]) < rMin) { 1974 // both below min, just make sure > 0 1975 if (sizes[0] < 0) { 1976 sizes[1] += sizes[0]; 1977 sizes[0] = 0; 1978 } 1979 } 1980 else { 1981 sizes[1] -= (lMin - sizes[0]); 1982 sizes[0] = lMin; 1983 } 1984 } 1985 else if (!rMinValid) { 1986 if (sizes[0] - (rMin - sizes[1]) < lMin) { 1987 // both below min, just make sure > 0 1988 if (sizes[1] < 0) { 1989 sizes[0] += sizes[1]; 1990 sizes[1] = 0; 1991 } 1992 } 1993 else { 1994 sizes[0] -= (rMin - sizes[1]); 1995 sizes[1] = rMin; 1996 } 1997 } 1998 if (sizes[0] < 0) { 1999 sizes[0] = 0; 2000 } 2001 if (sizes[1] < 0) { 2002 sizes[1] = 0; 2003 } 2004 } 2005 else if (lValid) { 2006 sizes[0] = Math.max(0, sizes[0] + space); 2007 } 2008 else if (rValid) { 2009 sizes[1] = Math.max(0, sizes[1] + space); 2010 } 2011 } 2012 } 2013 2014 2015 /** 2016 * LayoutManager used for JSplitPanes with an orientation of 2017 * VERTICAL_SPLIT. 2018 * 2019 */ 2020 public class BasicVerticalLayoutManager extends 2021 BasicHorizontalLayoutManager 2022 { 2023 public BasicVerticalLayoutManager() { 2024 super(1); 2025 } 2026 } 2027 2028 2029 private class Handler implements FocusListener, PropertyChangeListener { 2030 // 2031 // PropertyChangeListener 2032 // 2033 /** 2034 * Messaged from the <code>JSplitPane</code> the receiver is 2035 * contained in. May potentially reset the layout manager and cause a 2036 * <code>validate</code> to be sent. 2037 */ 2038 public void propertyChange(PropertyChangeEvent e) { 2039 if(e.getSource() == splitPane) { 2040 String changeName = e.getPropertyName(); 2041 2042 if(changeName == JSplitPane.ORIENTATION_PROPERTY) { 2043 orientation = splitPane.getOrientation(); 2044 resetLayoutManager(); 2045 } else if(changeName == JSplitPane.CONTINUOUS_LAYOUT_PROPERTY){ 2046 setContinuousLayout(splitPane.isContinuousLayout()); 2047 if(!isContinuousLayout()) { 2048 if(nonContinuousLayoutDivider == null) { 2049 setNonContinuousLayoutDivider( 2050 createDefaultNonContinuousLayoutDivider(), 2051 true); 2052 } else if(nonContinuousLayoutDivider.getParent() == 2053 null) { 2054 setNonContinuousLayoutDivider( 2055 nonContinuousLayoutDivider, 2056 true); 2057 } 2058 } 2059 } else if(changeName == JSplitPane.DIVIDER_SIZE_PROPERTY){ 2060 divider.setDividerSize(splitPane.getDividerSize()); 2061 dividerSize = divider.getDividerSize(); 2062 splitPane.revalidate(); 2063 splitPane.repaint(); 2064 } 2065 } 2066 } 2067 2068 // 2069 // FocusListener 2070 // 2071 public void focusGained(FocusEvent ev) { 2072 dividerKeyboardResize = true; 2073 splitPane.repaint(); 2074 } 2075 2076 public void focusLost(FocusEvent ev) { 2077 dividerKeyboardResize = false; 2078 splitPane.repaint(); 2079 } 2080 } 2081 2082 2083 private static class Actions extends UIAction { 2084 private static final String NEGATIVE_INCREMENT = "negativeIncrement"; 2085 private static final String POSITIVE_INCREMENT = "positiveIncrement"; 2086 private static final String SELECT_MIN = "selectMin"; 2087 private static final String SELECT_MAX = "selectMax"; 2088 private static final String START_RESIZE = "startResize"; 2089 private static final String TOGGLE_FOCUS = "toggleFocus"; 2090 private static final String FOCUS_OUT_FORWARD = "focusOutForward"; 2091 private static final String FOCUS_OUT_BACKWARD = "focusOutBackward"; 2092 2093 Actions(String key) { 2094 super(key); 2095 } 2096 2097 public void actionPerformed(ActionEvent ev) { 2098 JSplitPane splitPane = (JSplitPane)ev.getSource(); 2099 BasicSplitPaneUI ui = (BasicSplitPaneUI)BasicLookAndFeel. 2100 getUIOfType(splitPane.getUI(), BasicSplitPaneUI.class); 2101 2102 if (ui == null) { 2103 return; 2104 } 2105 String key = getName(); 2106 if (key == NEGATIVE_INCREMENT) { 2107 if (ui.dividerKeyboardResize) { 2108 splitPane.setDividerLocation(Math.max( 2109 0, ui.getDividerLocation 2110 (splitPane) - ui.getKeyboardMoveIncrement())); 2111 } 2112 } 2113 else if (key == POSITIVE_INCREMENT) { 2114 if (ui.dividerKeyboardResize) { 2115 splitPane.setDividerLocation( 2116 ui.getDividerLocation(splitPane) + 2117 ui.getKeyboardMoveIncrement()); 2118 } 2119 } 2120 else if (key == SELECT_MIN) { 2121 if (ui.dividerKeyboardResize) { 2122 splitPane.setDividerLocation(0); 2123 } 2124 } 2125 else if (key == SELECT_MAX) { 2126 if (ui.dividerKeyboardResize) { 2127 Insets insets = splitPane.getInsets(); 2128 int bottomI = (insets != null) ? insets.bottom : 0; 2129 int rightI = (insets != null) ? insets.right : 0; 2130 2131 if (ui.orientation == JSplitPane.VERTICAL_SPLIT) { 2132 splitPane.setDividerLocation(splitPane.getHeight() - 2133 bottomI); 2134 } 2135 else { 2136 splitPane.setDividerLocation(splitPane.getWidth() - 2137 rightI); 2138 } 2139 } 2140 } 2141 else if (key == START_RESIZE) { 2142 if (!ui.dividerKeyboardResize) { 2143 splitPane.requestFocus(); 2144 } else { 2145 JSplitPane parentSplitPane = 2146 (JSplitPane)SwingUtilities.getAncestorOfClass( 2147 JSplitPane.class, splitPane); 2148 if (parentSplitPane!=null) { 2149 parentSplitPane.requestFocus(); 2150 } 2151 } 2152 } 2153 else if (key == TOGGLE_FOCUS) { 2154 toggleFocus(splitPane); 2155 } 2156 else if (key == FOCUS_OUT_FORWARD) { 2157 moveFocus(splitPane, 1); 2158 } 2159 else if (key == FOCUS_OUT_BACKWARD) { 2160 moveFocus(splitPane, -1); 2161 } 2162 } 2163 2164 private void moveFocus(JSplitPane splitPane, int direction) { 2165 Container rootAncestor = splitPane.getFocusCycleRootAncestor(); 2166 FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy(); 2167 Component focusOn = (direction > 0) ? 2168 policy.getComponentAfter(rootAncestor, splitPane) : 2169 policy.getComponentBefore(rootAncestor, splitPane); 2170 HashSet<Component> focusFrom = new HashSet<Component>(); 2171 if (splitPane.isAncestorOf(focusOn)) { 2172 do { 2173 focusFrom.add(focusOn); 2174 rootAncestor = focusOn.getFocusCycleRootAncestor(); 2175 policy = rootAncestor.getFocusTraversalPolicy(); 2176 focusOn = (direction > 0) ? 2177 policy.getComponentAfter(rootAncestor, focusOn) : 2178 policy.getComponentBefore(rootAncestor, focusOn); 2179 } while (splitPane.isAncestorOf(focusOn) && 2180 !focusFrom.contains(focusOn)); 2181 } 2182 if ( focusOn!=null && !splitPane.isAncestorOf(focusOn) ) { 2183 focusOn.requestFocus(); 2184 } 2185 } 2186 2187 private void toggleFocus(JSplitPane splitPane) { 2188 Component left = splitPane.getLeftComponent(); 2189 Component right = splitPane.getRightComponent(); 2190 2191 KeyboardFocusManager manager = 2192 KeyboardFocusManager.getCurrentKeyboardFocusManager(); 2193 Component focus = manager.getFocusOwner(); 2194 Component focusOn = getNextSide(splitPane, focus); 2195 if (focusOn != null) { 2196 // don't change the focus if the new focused component belongs 2197 // to the same splitpane and the same side 2198 if ( focus!=null && 2199 ( (SwingUtilities.isDescendingFrom(focus, left) && 2200 SwingUtilities.isDescendingFrom(focusOn, left)) || 2201 (SwingUtilities.isDescendingFrom(focus, right) && 2202 SwingUtilities.isDescendingFrom(focusOn, right)) ) ) { 2203 return; 2204 } 2205 SwingUtilities2.compositeRequestFocus(focusOn); 2206 } 2207 } 2208 2209 private Component getNextSide(JSplitPane splitPane, Component focus) { 2210 Component left = splitPane.getLeftComponent(); 2211 Component right = splitPane.getRightComponent(); 2212 Component next; 2213 if (focus!=null && SwingUtilities.isDescendingFrom(focus, left) && 2214 right!=null) { 2215 next = getFirstAvailableComponent(right); 2216 if (next != null) { 2217 return next; 2218 } 2219 } 2220 JSplitPane parentSplitPane = (JSplitPane)SwingUtilities.getAncestorOfClass(JSplitPane.class, splitPane); 2221 if (parentSplitPane!=null) { 2222 // focus next side of the parent split pane 2223 next = getNextSide(parentSplitPane, focus); 2224 } else { 2225 next = getFirstAvailableComponent(left); 2226 if (next == null) { 2227 next = getFirstAvailableComponent(right); 2228 } 2229 } 2230 return next; 2231 } 2232 2233 private Component getFirstAvailableComponent(Component c) { 2234 if (c!=null && c instanceof JSplitPane) { 2235 JSplitPane sp = (JSplitPane)c; 2236 Component left = getFirstAvailableComponent(sp.getLeftComponent()); 2237 if (left != null) { 2238 c = left; 2239 } else { 2240 c = getFirstAvailableComponent(sp.getRightComponent()); 2241 } 2242 } 2243 return c; 2244 } 2245 } 2246 }