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