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