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 @SuppressWarnings("deprecation") 1195 protected void startDragging() { 1196 Component leftC = splitPane.getLeftComponent(); 1197 Component rightC = splitPane.getRightComponent(); 1198 ComponentPeer cPeer; 1199 1200 beginDragDividerLocation = getDividerLocation(splitPane); 1201 draggingHW = false; 1202 if(leftC != null && (cPeer = leftC.getPeer()) != null && 1203 !(cPeer instanceof LightweightPeer)) { 1204 draggingHW = true; 1205 } else if(rightC != null && (cPeer = rightC.getPeer()) != null 1206 && !(cPeer instanceof LightweightPeer)) { 1207 draggingHW = true; 1208 } 1209 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1210 setLastDragLocation(divider.getBounds().x); 1211 dividerSize = divider.getSize().width; 1212 if(!isContinuousLayout() && draggingHW) { 1213 nonContinuousLayoutDivider.setBounds 1214 (getLastDragLocation(), 0, dividerSize, 1215 splitPane.getHeight()); 1216 addHeavyweightDivider(); 1217 } 1218 } else { 1219 setLastDragLocation(divider.getBounds().y); 1220 dividerSize = divider.getSize().height; 1221 if(!isContinuousLayout() && draggingHW) { 1222 nonContinuousLayoutDivider.setBounds 1223 (0, getLastDragLocation(), splitPane.getWidth(), 1224 dividerSize); 1225 addHeavyweightDivider(); 1226 } 1227 } 1228 } 1229 1230 1231 /** 1232 * Messaged during a dragging session to move the divider to the 1233 * passed in {@code location}. If {@code continuousLayout} is {@code true} 1234 * the location is reset and the splitPane validated. 1235 * 1236 * @param location the location of divider 1237 */ 1238 protected void dragDividerTo(int location) { 1239 if(getLastDragLocation() != location) { 1240 if(isContinuousLayout()) { 1241 splitPane.setDividerLocation(location); 1242 setLastDragLocation(location); 1243 } else { 1244 int lastLoc = getLastDragLocation(); 1245 1246 setLastDragLocation(location); 1247 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1248 if(draggingHW) { 1249 nonContinuousLayoutDivider.setLocation( 1250 getLastDragLocation(), 0); 1251 } else { 1252 int splitHeight = splitPane.getHeight(); 1253 splitPane.repaint(lastLoc, 0, dividerSize, 1254 splitHeight); 1255 splitPane.repaint(location, 0, dividerSize, 1256 splitHeight); 1257 } 1258 } else { 1259 if(draggingHW) { 1260 nonContinuousLayoutDivider.setLocation(0, 1261 getLastDragLocation()); 1262 } else { 1263 int splitWidth = splitPane.getWidth(); 1264 1265 splitPane.repaint(0, lastLoc, splitWidth, 1266 dividerSize); 1267 splitPane.repaint(0, location, splitWidth, 1268 dividerSize); 1269 } 1270 } 1271 } 1272 } 1273 } 1274 1275 1276 /** 1277 * Messaged to finish the dragging session. If not continuous display 1278 * the dividers {@code location} will be reset. 1279 * 1280 * @param location the location of divider 1281 */ 1282 protected void finishDraggingTo(int location) { 1283 dragDividerTo(location); 1284 setLastDragLocation(-1); 1285 if(!isContinuousLayout()) { 1286 Component leftC = splitPane.getLeftComponent(); 1287 Rectangle leftBounds = leftC.getBounds(); 1288 1289 if (draggingHW) { 1290 if(orientation == JSplitPane.HORIZONTAL_SPLIT) { 1291 nonContinuousLayoutDivider.setLocation(-dividerSize, 0); 1292 } 1293 else { 1294 nonContinuousLayoutDivider.setLocation(0, -dividerSize); 1295 } 1296 splitPane.remove(nonContinuousLayoutDivider); 1297 } 1298 splitPane.setDividerLocation(location); 1299 } 1300 } 1301 1302 1303 /** 1304 * As of Java 2 platform v1.3 this method is no longer used. Instead 1305 * you should set the border on the divider. 1306 * <p> 1307 * Returns the width of one side of the divider border. 1308 * 1309 * @return the width of one side of the divider border 1310 * @deprecated As of Java 2 platform v1.3, instead set the border on the 1311 * divider. 1312 */ 1313 @Deprecated 1314 protected int getDividerBorderSize() { 1315 return 1; 1316 } 1317 1318 1319 /** 1320 * LayoutManager for JSplitPanes that have an orientation of 1321 * HORIZONTAL_SPLIT. 1322 */ 1323 public class BasicHorizontalLayoutManager implements LayoutManager2 1324 { 1325 /* left, right, divider. (in this exact order) */ 1326 /** 1327 * The size of components. 1328 */ 1329 protected int[] sizes; 1330 /** 1331 * The components. 1332 */ 1333 protected Component[] components; 1334 /** Size of the splitpane the last time laid out. */ 1335 private int lastSplitPaneSize; 1336 /** True if resetToPreferredSizes has been invoked. */ 1337 private boolean doReset; 1338 /** Axis, 0 for horizontal, or 1 for veritcal. */ 1339 private int axis; 1340 1341 1342 BasicHorizontalLayoutManager() { 1343 this(0); 1344 } 1345 1346 BasicHorizontalLayoutManager(int axis) { 1347 this.axis = axis; 1348 components = new Component[3]; 1349 components[0] = components[1] = components[2] = null; 1350 sizes = new int[3]; 1351 } 1352 1353 // 1354 // LayoutManager 1355 // 1356 1357 /** 1358 * Does the actual layout. 1359 */ 1360 public void layoutContainer(Container container) { 1361 Dimension containerSize = container.getSize(); 1362 1363 // If the splitpane has a zero size then no op out of here. 1364 // If we execute this function now, we're going to cause ourselves 1365 // much grief. 1366 if (containerSize.height <= 0 || containerSize.width <= 0 ) { 1367 lastSplitPaneSize = 0; 1368 return; 1369 } 1370 1371 int spDividerLocation = splitPane.getDividerLocation(); 1372 Insets insets = splitPane.getInsets(); 1373 int availableSize = getAvailableSize(containerSize, 1374 insets); 1375 int newSize = getSizeForPrimaryAxis(containerSize); 1376 int beginLocation = getDividerLocation(splitPane); 1377 int dOffset = getSizeForPrimaryAxis(insets, true); 1378 Dimension dSize = (components[2] == null) ? null : 1379 components[2].getPreferredSize(); 1380 1381 if ((doReset && !dividerLocationIsSet) || spDividerLocation < 0) { 1382 resetToPreferredSizes(availableSize); 1383 } 1384 else if (lastSplitPaneSize <= 0 || 1385 availableSize == lastSplitPaneSize || !painted || 1386 (dSize != null && 1387 getSizeForPrimaryAxis(dSize) != sizes[2])) { 1388 if (dSize != null) { 1389 sizes[2] = getSizeForPrimaryAxis(dSize); 1390 } 1391 else { 1392 sizes[2] = 0; 1393 } 1394 setDividerLocation(spDividerLocation - dOffset, availableSize); 1395 dividerLocationIsSet = false; 1396 } 1397 else if (availableSize != lastSplitPaneSize) { 1398 distributeSpace(availableSize - lastSplitPaneSize, 1399 getKeepHidden()); 1400 } 1401 doReset = false; 1402 dividerLocationIsSet = false; 1403 lastSplitPaneSize = availableSize; 1404 1405 // Reset the bounds of each component 1406 int nextLocation = getInitialLocation(insets); 1407 int counter = 0; 1408 1409 while (counter < 3) { 1410 if (components[counter] != null && 1411 components[counter].isVisible()) { 1412 setComponentToSize(components[counter], sizes[counter], 1413 nextLocation, insets, containerSize); 1414 nextLocation += sizes[counter]; 1415 } 1416 switch (counter) { 1417 case 0: 1418 counter = 2; 1419 break; 1420 case 2: 1421 counter = 1; 1422 break; 1423 case 1: 1424 counter = 3; 1425 break; 1426 } 1427 } 1428 if (painted) { 1429 // This is tricky, there is never a good time for us 1430 // to push the value to the splitpane, painted appears to 1431 // the best time to do it. What is really needed is 1432 // notification that layout has completed. 1433 int newLocation = getDividerLocation(splitPane); 1434 1435 if (newLocation != (spDividerLocation - dOffset)) { 1436 int lastLocation = splitPane.getLastDividerLocation(); 1437 1438 ignoreDividerLocationChange = true; 1439 try { 1440 splitPane.setDividerLocation(newLocation); 1441 // This is not always needed, but is rather tricky 1442 // to determine when... The case this is needed for 1443 // is if the user sets the divider location to some 1444 // bogus value, say 0, and the actual value is 1, the 1445 // call to setDividerLocation(1) will preserve the 1446 // old value of 0, when we really want the divider 1447 // location value before the call. This is needed for 1448 // the one touch buttons. 1449 splitPane.setLastDividerLocation(lastLocation); 1450 } finally { 1451 ignoreDividerLocationChange = false; 1452 } 1453 } 1454 } 1455 } 1456 1457 1458 /** 1459 * Adds the component at place. Place must be one of 1460 * JSplitPane.LEFT, RIGHT, TOP, BOTTOM, or null (for the 1461 * divider). 1462 */ 1463 public void addLayoutComponent(String place, Component component) { 1464 boolean isValid = true; 1465 1466 if(place != null) { 1467 if(place.equals(JSplitPane.DIVIDER)) { 1468 /* Divider. */ 1469 components[2] = component; 1470 sizes[2] = getSizeForPrimaryAxis(component. 1471 getPreferredSize()); 1472 } else if(place.equals(JSplitPane.LEFT) || 1473 place.equals(JSplitPane.TOP)) { 1474 components[0] = component; 1475 sizes[0] = 0; 1476 } else if(place.equals(JSplitPane.RIGHT) || 1477 place.equals(JSplitPane.BOTTOM)) { 1478 components[1] = component; 1479 sizes[1] = 0; 1480 } else if(!place.equals( 1481 BasicSplitPaneUI.NON_CONTINUOUS_DIVIDER)) 1482 isValid = false; 1483 } else { 1484 isValid = false; 1485 } 1486 if(!isValid) 1487 throw new IllegalArgumentException("cannot add to layout: " + 1488 "unknown constraint: " + 1489 place); 1490 doReset = true; 1491 } 1492 1493 1494 /** 1495 * Returns the minimum size needed to contain the children. 1496 * The width is the sum of all the children's min widths and 1497 * the height is the largest of the children's minimum heights. 1498 */ 1499 public Dimension minimumLayoutSize(Container container) { 1500 int minPrimary = 0; 1501 int minSecondary = 0; 1502 Insets insets = splitPane.getInsets(); 1503 1504 for (int counter=0; counter<3; counter++) { 1505 if(components[counter] != null) { 1506 Dimension minSize = components[counter].getMinimumSize(); 1507 int secSize = getSizeForSecondaryAxis(minSize); 1508 1509 minPrimary += getSizeForPrimaryAxis(minSize); 1510 if(secSize > minSecondary) 1511 minSecondary = secSize; 1512 } 1513 } 1514 if(insets != null) { 1515 minPrimary += getSizeForPrimaryAxis(insets, true) + 1516 getSizeForPrimaryAxis(insets, false); 1517 minSecondary += getSizeForSecondaryAxis(insets, true) + 1518 getSizeForSecondaryAxis(insets, false); 1519 } 1520 if (axis == 0) { 1521 return new Dimension(minPrimary, minSecondary); 1522 } 1523 return new Dimension(minSecondary, minPrimary); 1524 } 1525 1526 1527 /** 1528 * Returns the preferred size needed to contain the children. 1529 * The width is the sum of all the preferred widths of the children and 1530 * the height is the largest preferred height of the children. 1531 */ 1532 public Dimension preferredLayoutSize(Container container) { 1533 int prePrimary = 0; 1534 int preSecondary = 0; 1535 Insets insets = splitPane.getInsets(); 1536 1537 for(int counter = 0; counter < 3; counter++) { 1538 if(components[counter] != null) { 1539 Dimension preSize = components[counter]. 1540 getPreferredSize(); 1541 int secSize = getSizeForSecondaryAxis(preSize); 1542 1543 prePrimary += getSizeForPrimaryAxis(preSize); 1544 if(secSize > preSecondary) 1545 preSecondary = secSize; 1546 } 1547 } 1548 if(insets != null) { 1549 prePrimary += getSizeForPrimaryAxis(insets, true) + 1550 getSizeForPrimaryAxis(insets, false); 1551 preSecondary += getSizeForSecondaryAxis(insets, true) + 1552 getSizeForSecondaryAxis(insets, false); 1553 } 1554 if (axis == 0) { 1555 return new Dimension(prePrimary, preSecondary); 1556 } 1557 return new Dimension(preSecondary, prePrimary); 1558 } 1559 1560 1561 /** 1562 * Removes the specified component from our knowledge. 1563 */ 1564 public void removeLayoutComponent(Component component) { 1565 for(int counter = 0; counter < 3; counter++) { 1566 if(components[counter] == component) { 1567 components[counter] = null; 1568 sizes[counter] = 0; 1569 doReset = true; 1570 } 1571 } 1572 } 1573 1574 1575 // 1576 // LayoutManager2 1577 // 1578 1579 1580 /** 1581 * Adds the specified component to the layout, using the specified 1582 * constraint object. 1583 * @param comp the component to be added 1584 * @param constraints where/how the component is added to the layout. 1585 */ 1586 public void addLayoutComponent(Component comp, Object constraints) { 1587 if ((constraints == null) || (constraints instanceof String)) { 1588 addLayoutComponent((String)constraints, comp); 1589 } else { 1590 throw new IllegalArgumentException("cannot add to layout: " + 1591 "constraint must be a " + 1592 "string (or null)"); 1593 } 1594 } 1595 1596 1597 /** 1598 * Returns the alignment along the x axis. This specifies how 1599 * the component would like to be aligned relative to other 1600 * components. The value should be a number between 0 and 1 1601 * where 0 represents alignment along the origin, 1 is aligned 1602 * the furthest away from the origin, 0.5 is centered, etc. 1603 */ 1604 public float getLayoutAlignmentX(Container target) { 1605 return 0.0f; 1606 } 1607 1608 1609 /** 1610 * Returns the alignment along the y axis. This specifies how 1611 * the component would like to be aligned relative to other 1612 * components. The value should be a number between 0 and 1 1613 * where 0 represents alignment along the origin, 1 is aligned 1614 * the furthest away from the origin, 0.5 is centered, etc. 1615 */ 1616 public float getLayoutAlignmentY(Container target) { 1617 return 0.0f; 1618 } 1619 1620 1621 /** 1622 * Does nothing. If the developer really wants to change the 1623 * size of one of the views JSplitPane.resetToPreferredSizes should 1624 * be messaged. 1625 */ 1626 public void invalidateLayout(Container c) { 1627 } 1628 1629 1630 /** 1631 * Returns the maximum layout size, which is Integer.MAX_VALUE 1632 * in both directions. 1633 */ 1634 public Dimension maximumLayoutSize(Container target) { 1635 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 1636 } 1637 1638 1639 // 1640 // New methods. 1641 // 1642 1643 /** 1644 * Marks the receiver so that the next time this instance is 1645 * laid out it'll ask for the preferred sizes. 1646 */ 1647 public void resetToPreferredSizes() { 1648 doReset = true; 1649 } 1650 1651 /** 1652 * Resets the size of the Component at the passed in location. 1653 * 1654 * @param index the index of a component 1655 */ 1656 protected void resetSizeAt(int index) { 1657 sizes[index] = 0; 1658 doReset = true; 1659 } 1660 1661 1662 /** 1663 * Sets the sizes to {@code newSizes}. 1664 * 1665 * @param newSizes the new sizes 1666 */ 1667 protected void setSizes(int[] newSizes) { 1668 System.arraycopy(newSizes, 0, sizes, 0, 3); 1669 } 1670 1671 1672 /** 1673 * Returns the sizes of the components. 1674 * 1675 * @return the sizes of the components 1676 */ 1677 protected int[] getSizes() { 1678 int[] retSizes = new int[3]; 1679 1680 System.arraycopy(sizes, 0, retSizes, 0, 3); 1681 return retSizes; 1682 } 1683 1684 1685 /** 1686 * Returns the width of the passed in Components preferred size. 1687 * 1688 * @param c a component 1689 * @return the preferred width of the component 1690 */ 1691 protected int getPreferredSizeOfComponent(Component c) { 1692 return getSizeForPrimaryAxis(c.getPreferredSize()); 1693 } 1694 1695 1696 /** 1697 * Returns the width of the passed in Components minimum size. 1698 * 1699 * @param c a component 1700 * @return the minimum width of the component 1701 */ 1702 int getMinimumSizeOfComponent(Component c) { 1703 return getSizeForPrimaryAxis(c.getMinimumSize()); 1704 } 1705 1706 1707 /** 1708 * Returns the width of the passed in component. 1709 * 1710 * @param c a component 1711 * @return the width of the component 1712 */ 1713 protected int getSizeOfComponent(Component c) { 1714 return getSizeForPrimaryAxis(c.getSize()); 1715 } 1716 1717 1718 /** 1719 * Returns the available width based on the container size and 1720 * {@code Insets}. 1721 * 1722 * @param containerSize a container size 1723 * @param insets an insets 1724 * @return the available width 1725 */ 1726 protected int getAvailableSize(Dimension containerSize, 1727 Insets insets) { 1728 if(insets == null) 1729 return getSizeForPrimaryAxis(containerSize); 1730 return (getSizeForPrimaryAxis(containerSize) - 1731 (getSizeForPrimaryAxis(insets, true) + 1732 getSizeForPrimaryAxis(insets, false))); 1733 } 1734 1735 1736 /** 1737 * Returns the left inset, unless the {@code Insets} are null in which case 1738 * 0 is returned. 1739 * 1740 * @param insets the insets 1741 * @return the left inset 1742 */ 1743 protected int getInitialLocation(Insets insets) { 1744 if(insets != null) 1745 return getSizeForPrimaryAxis(insets, true); 1746 return 0; 1747 } 1748 1749 1750 /** 1751 * Sets the width of the component {@code c} to be {@code size}, placing its 1752 * x location at {@code location}, y to the {@code insets.top} and height 1753 * to the {@code containerSize.height} less the top and bottom insets. 1754 * 1755 * @param c a component 1756 * @param size a new width 1757 * @param location a new X coordinate 1758 * @param insets an insets 1759 * @param containerSize a container size 1760 */ 1761 protected void setComponentToSize(Component c, int size, 1762 int location, Insets insets, 1763 Dimension containerSize) { 1764 if(insets != null) { 1765 if (axis == 0) { 1766 c.setBounds(location, insets.top, size, 1767 containerSize.height - 1768 (insets.top + insets.bottom)); 1769 } 1770 else { 1771 c.setBounds(insets.left, location, containerSize.width - 1772 (insets.left + insets.right), size); 1773 } 1774 } 1775 else { 1776 if (axis == 0) { 1777 c.setBounds(location, 0, size, containerSize.height); 1778 } 1779 else { 1780 c.setBounds(0, location, containerSize.width, size); 1781 } 1782 } 1783 } 1784 1785 /** 1786 * If the axis == 0, the width is returned, otherwise the height. 1787 */ 1788 int getSizeForPrimaryAxis(Dimension size) { 1789 if (axis == 0) { 1790 return size.width; 1791 } 1792 return size.height; 1793 } 1794 1795 /** 1796 * If the axis == 0, the width is returned, otherwise the height. 1797 */ 1798 int getSizeForSecondaryAxis(Dimension size) { 1799 if (axis == 0) { 1800 return size.height; 1801 } 1802 return size.width; 1803 } 1804 1805 /** 1806 * Returns a particular value of the inset identified by the 1807 * axis and <code>isTop</code><p> 1808 * axis isTop 1809 * 0 true - left 1810 * 0 false - right 1811 * 1 true - top 1812 * 1 false - bottom 1813 */ 1814 int getSizeForPrimaryAxis(Insets insets, boolean isTop) { 1815 if (axis == 0) { 1816 if (isTop) { 1817 return insets.left; 1818 } 1819 return insets.right; 1820 } 1821 if (isTop) { 1822 return insets.top; 1823 } 1824 return insets.bottom; 1825 } 1826 1827 /** 1828 * Returns a particular value of the inset identified by the 1829 * axis and <code>isTop</code><p> 1830 * axis isTop 1831 * 0 true - left 1832 * 0 false - right 1833 * 1 true - top 1834 * 1 false - bottom 1835 */ 1836 int getSizeForSecondaryAxis(Insets insets, boolean isTop) { 1837 if (axis == 0) { 1838 if (isTop) { 1839 return insets.top; 1840 } 1841 return insets.bottom; 1842 } 1843 if (isTop) { 1844 return insets.left; 1845 } 1846 return insets.right; 1847 } 1848 1849 /** 1850 * Determines the components. This should be called whenever 1851 * a new instance of this is installed into an existing 1852 * SplitPane. 1853 */ 1854 protected void updateComponents() { 1855 Component comp; 1856 1857 comp = splitPane.getLeftComponent(); 1858 if(components[0] != comp) { 1859 components[0] = comp; 1860 if(comp == null) { 1861 sizes[0] = 0; 1862 } else { 1863 sizes[0] = -1; 1864 } 1865 } 1866 1867 comp = splitPane.getRightComponent(); 1868 if(components[1] != comp) { 1869 components[1] = comp; 1870 if(comp == null) { 1871 sizes[1] = 0; 1872 } else { 1873 sizes[1] = -1; 1874 } 1875 } 1876 1877 /* Find the divider. */ 1878 Component[] children = splitPane.getComponents(); 1879 Component oldDivider = components[2]; 1880 1881 components[2] = null; 1882 for(int counter = children.length - 1; counter >= 0; counter--) { 1883 if(children[counter] != components[0] && 1884 children[counter] != components[1] && 1885 children[counter] != nonContinuousLayoutDivider) { 1886 if(oldDivider != children[counter]) { 1887 components[2] = children[counter]; 1888 } else { 1889 components[2] = oldDivider; 1890 } 1891 break; 1892 } 1893 } 1894 if(components[2] == null) { 1895 sizes[2] = 0; 1896 } 1897 else { 1898 sizes[2] = getSizeForPrimaryAxis(components[2].getPreferredSize()); 1899 } 1900 } 1901 1902 /** 1903 * Resets the size of the first component to <code>leftSize</code>, 1904 * and the right component to the remainder of the space. 1905 */ 1906 void setDividerLocation(int leftSize, int availableSize) { 1907 boolean lValid = (components[0] != null && 1908 components[0].isVisible()); 1909 boolean rValid = (components[1] != null && 1910 components[1].isVisible()); 1911 boolean dValid = (components[2] != null && 1912 components[2].isVisible()); 1913 int max = availableSize; 1914 1915 if (dValid) { 1916 max -= sizes[2]; 1917 } 1918 leftSize = Math.max(0, Math.min(leftSize, max)); 1919 if (lValid) { 1920 if (rValid) { 1921 sizes[0] = leftSize; 1922 sizes[1] = max - leftSize; 1923 } 1924 else { 1925 sizes[0] = max; 1926 sizes[1] = 0; 1927 } 1928 } 1929 else if (rValid) { 1930 sizes[1] = max; 1931 sizes[0] = 0; 1932 } 1933 } 1934 1935 /** 1936 * Returns an array of the minimum sizes of the components. 1937 */ 1938 int[] getPreferredSizes() { 1939 int[] retValue = new int[3]; 1940 1941 for (int counter = 0; counter < 3; counter++) { 1942 if (components[counter] != null && 1943 components[counter].isVisible()) { 1944 retValue[counter] = getPreferredSizeOfComponent 1945 (components[counter]); 1946 } 1947 else { 1948 retValue[counter] = -1; 1949 } 1950 } 1951 return retValue; 1952 } 1953 1954 /** 1955 * Returns an array of the minimum sizes of the components. 1956 */ 1957 int[] getMinimumSizes() { 1958 int[] retValue = new int[3]; 1959 1960 for (int counter = 0; counter < 2; counter++) { 1961 if (components[counter] != null && 1962 components[counter].isVisible()) { 1963 retValue[counter] = getMinimumSizeOfComponent 1964 (components[counter]); 1965 } 1966 else { 1967 retValue[counter] = -1; 1968 } 1969 } 1970 retValue[2] = (components[2] != null) ? 1971 getMinimumSizeOfComponent(components[2]) : -1; 1972 return retValue; 1973 } 1974 1975 /** 1976 * Resets the components to their preferred sizes. 1977 */ 1978 void resetToPreferredSizes(int availableSize) { 1979 // Set the sizes to the preferred sizes (if fits), otherwise 1980 // set to min sizes and distribute any extra space. 1981 int[] testSizes = getPreferredSizes(); 1982 int totalSize = 0; 1983 1984 for (int counter = 0; counter < 3; counter++) { 1985 if (testSizes[counter] != -1) { 1986 totalSize += testSizes[counter]; 1987 } 1988 } 1989 if (totalSize > availableSize) { 1990 testSizes = getMinimumSizes(); 1991 1992 totalSize = 0; 1993 for (int counter = 0; counter < 3; counter++) { 1994 if (testSizes[counter] != -1) { 1995 totalSize += testSizes[counter]; 1996 } 1997 } 1998 } 1999 setSizes(testSizes); 2000 distributeSpace(availableSize - totalSize, false); 2001 } 2002 2003 /** 2004 * Distributes <code>space</code> between the two components 2005 * (divider won't get any extra space) based on the weighting. This 2006 * attempts to honor the min size of the components. 2007 * 2008 * @param keepHidden if true and one of the components is 0x0 2009 * it gets none of the extra space 2010 */ 2011 void distributeSpace(int space, boolean keepHidden) { 2012 boolean lValid = (components[0] != null && 2013 components[0].isVisible()); 2014 boolean rValid = (components[1] != null && 2015 components[1].isVisible()); 2016 2017 if (keepHidden) { 2018 if (lValid && getSizeForPrimaryAxis( 2019 components[0].getSize()) == 0) { 2020 lValid = false; 2021 if (rValid && getSizeForPrimaryAxis( 2022 components[1].getSize()) == 0) { 2023 // Both aren't valid, force them both to be valid 2024 lValid = true; 2025 } 2026 } 2027 else if (rValid && getSizeForPrimaryAxis( 2028 components[1].getSize()) == 0) { 2029 rValid = false; 2030 } 2031 } 2032 if (lValid && rValid) { 2033 double weight = splitPane.getResizeWeight(); 2034 int lExtra = (int)(weight * (double)space); 2035 int rExtra = (space - lExtra); 2036 2037 sizes[0] += lExtra; 2038 sizes[1] += rExtra; 2039 2040 int lMin = getMinimumSizeOfComponent(components[0]); 2041 int rMin = getMinimumSizeOfComponent(components[1]); 2042 boolean lMinValid = (sizes[0] >= lMin); 2043 boolean rMinValid = (sizes[1] >= rMin); 2044 2045 if (!lMinValid && !rMinValid) { 2046 if (sizes[0] < 0) { 2047 sizes[1] += sizes[0]; 2048 sizes[0] = 0; 2049 } 2050 else if (sizes[1] < 0) { 2051 sizes[0] += sizes[1]; 2052 sizes[1] = 0; 2053 } 2054 } 2055 else if (!lMinValid) { 2056 if (sizes[1] - (lMin - sizes[0]) < rMin) { 2057 // both below min, just make sure > 0 2058 if (sizes[0] < 0) { 2059 sizes[1] += sizes[0]; 2060 sizes[0] = 0; 2061 } 2062 } 2063 else { 2064 sizes[1] -= (lMin - sizes[0]); 2065 sizes[0] = lMin; 2066 } 2067 } 2068 else if (!rMinValid) { 2069 if (sizes[0] - (rMin - sizes[1]) < lMin) { 2070 // both below min, just make sure > 0 2071 if (sizes[1] < 0) { 2072 sizes[0] += sizes[1]; 2073 sizes[1] = 0; 2074 } 2075 } 2076 else { 2077 sizes[0] -= (rMin - sizes[1]); 2078 sizes[1] = rMin; 2079 } 2080 } 2081 if (sizes[0] < 0) { 2082 sizes[0] = 0; 2083 } 2084 if (sizes[1] < 0) { 2085 sizes[1] = 0; 2086 } 2087 } 2088 else if (lValid) { 2089 sizes[0] = Math.max(0, sizes[0] + space); 2090 } 2091 else if (rValid) { 2092 sizes[1] = Math.max(0, sizes[1] + space); 2093 } 2094 } 2095 } 2096 2097 2098 /** 2099 * LayoutManager used for JSplitPanes with an orientation of 2100 * VERTICAL_SPLIT. 2101 * 2102 */ 2103 public class BasicVerticalLayoutManager extends 2104 BasicHorizontalLayoutManager 2105 { 2106 /** 2107 * Constructs a new instance of {@code BasicVerticalLayoutManager}. 2108 */ 2109 public BasicVerticalLayoutManager() { 2110 super(1); 2111 } 2112 } 2113 2114 2115 private class Handler implements FocusListener, PropertyChangeListener { 2116 // 2117 // PropertyChangeListener 2118 // 2119 /** 2120 * Messaged from the <code>JSplitPane</code> the receiver is 2121 * contained in. May potentially reset the layout manager and cause a 2122 * <code>validate</code> to be sent. 2123 */ 2124 public void propertyChange(PropertyChangeEvent e) { 2125 if(e.getSource() == splitPane) { 2126 String changeName = e.getPropertyName(); 2127 2128 if(changeName == JSplitPane.ORIENTATION_PROPERTY) { 2129 orientation = splitPane.getOrientation(); 2130 resetLayoutManager(); 2131 } else if(changeName == JSplitPane.CONTINUOUS_LAYOUT_PROPERTY){ 2132 setContinuousLayout(splitPane.isContinuousLayout()); 2133 if(!isContinuousLayout()) { 2134 if(nonContinuousLayoutDivider == null) { 2135 setNonContinuousLayoutDivider( 2136 createDefaultNonContinuousLayoutDivider(), 2137 true); 2138 } else if(nonContinuousLayoutDivider.getParent() == 2139 null) { 2140 setNonContinuousLayoutDivider( 2141 nonContinuousLayoutDivider, 2142 true); 2143 } 2144 } 2145 } else if(changeName == JSplitPane.DIVIDER_SIZE_PROPERTY){ 2146 divider.setDividerSize(splitPane.getDividerSize()); 2147 dividerSize = divider.getDividerSize(); 2148 splitPane.revalidate(); 2149 splitPane.repaint(); 2150 } 2151 } 2152 } 2153 2154 // 2155 // FocusListener 2156 // 2157 public void focusGained(FocusEvent ev) { 2158 dividerKeyboardResize = true; 2159 splitPane.repaint(); 2160 } 2161 2162 public void focusLost(FocusEvent ev) { 2163 dividerKeyboardResize = false; 2164 splitPane.repaint(); 2165 } 2166 } 2167 2168 2169 private static class Actions extends UIAction { 2170 private static final String NEGATIVE_INCREMENT = "negativeIncrement"; 2171 private static final String POSITIVE_INCREMENT = "positiveIncrement"; 2172 private static final String SELECT_MIN = "selectMin"; 2173 private static final String SELECT_MAX = "selectMax"; 2174 private static final String START_RESIZE = "startResize"; 2175 private static final String TOGGLE_FOCUS = "toggleFocus"; 2176 private static final String FOCUS_OUT_FORWARD = "focusOutForward"; 2177 private static final String FOCUS_OUT_BACKWARD = "focusOutBackward"; 2178 2179 Actions(String key) { 2180 super(key); 2181 } 2182 2183 public void actionPerformed(ActionEvent ev) { 2184 JSplitPane splitPane = (JSplitPane)ev.getSource(); 2185 BasicSplitPaneUI ui = (BasicSplitPaneUI)BasicLookAndFeel. 2186 getUIOfType(splitPane.getUI(), BasicSplitPaneUI.class); 2187 2188 if (ui == null) { 2189 return; 2190 } 2191 String key = getName(); 2192 if (key == NEGATIVE_INCREMENT) { 2193 if (ui.dividerKeyboardResize) { 2194 splitPane.setDividerLocation(Math.max( 2195 0, ui.getDividerLocation 2196 (splitPane) - ui.getKeyboardMoveIncrement())); 2197 } 2198 } 2199 else if (key == POSITIVE_INCREMENT) { 2200 if (ui.dividerKeyboardResize) { 2201 splitPane.setDividerLocation( 2202 ui.getDividerLocation(splitPane) + 2203 ui.getKeyboardMoveIncrement()); 2204 } 2205 } 2206 else if (key == SELECT_MIN) { 2207 if (ui.dividerKeyboardResize) { 2208 splitPane.setDividerLocation(0); 2209 } 2210 } 2211 else if (key == SELECT_MAX) { 2212 if (ui.dividerKeyboardResize) { 2213 Insets insets = splitPane.getInsets(); 2214 int bottomI = (insets != null) ? insets.bottom : 0; 2215 int rightI = (insets != null) ? insets.right : 0; 2216 2217 if (ui.orientation == JSplitPane.VERTICAL_SPLIT) { 2218 splitPane.setDividerLocation(splitPane.getHeight() - 2219 bottomI); 2220 } 2221 else { 2222 splitPane.setDividerLocation(splitPane.getWidth() - 2223 rightI); 2224 } 2225 } 2226 } 2227 else if (key == START_RESIZE) { 2228 if (!ui.dividerKeyboardResize) { 2229 splitPane.requestFocus(); 2230 } else { 2231 JSplitPane parentSplitPane = 2232 (JSplitPane)SwingUtilities.getAncestorOfClass( 2233 JSplitPane.class, splitPane); 2234 if (parentSplitPane!=null) { 2235 parentSplitPane.requestFocus(); 2236 } 2237 } 2238 } 2239 else if (key == TOGGLE_FOCUS) { 2240 toggleFocus(splitPane); 2241 } 2242 else if (key == FOCUS_OUT_FORWARD) { 2243 moveFocus(splitPane, 1); 2244 } 2245 else if (key == FOCUS_OUT_BACKWARD) { 2246 moveFocus(splitPane, -1); 2247 } 2248 } 2249 2250 private void moveFocus(JSplitPane splitPane, int direction) { 2251 Container rootAncestor = splitPane.getFocusCycleRootAncestor(); 2252 FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy(); 2253 Component focusOn = (direction > 0) ? 2254 policy.getComponentAfter(rootAncestor, splitPane) : 2255 policy.getComponentBefore(rootAncestor, splitPane); 2256 HashSet<Component> focusFrom = new HashSet<Component>(); 2257 if (splitPane.isAncestorOf(focusOn)) { 2258 do { 2259 focusFrom.add(focusOn); 2260 rootAncestor = focusOn.getFocusCycleRootAncestor(); 2261 policy = rootAncestor.getFocusTraversalPolicy(); 2262 focusOn = (direction > 0) ? 2263 policy.getComponentAfter(rootAncestor, focusOn) : 2264 policy.getComponentBefore(rootAncestor, focusOn); 2265 } while (splitPane.isAncestorOf(focusOn) && 2266 !focusFrom.contains(focusOn)); 2267 } 2268 if ( focusOn!=null && !splitPane.isAncestorOf(focusOn) ) { 2269 focusOn.requestFocus(); 2270 } 2271 } 2272 2273 private void toggleFocus(JSplitPane splitPane) { 2274 Component left = splitPane.getLeftComponent(); 2275 Component right = splitPane.getRightComponent(); 2276 2277 KeyboardFocusManager manager = 2278 KeyboardFocusManager.getCurrentKeyboardFocusManager(); 2279 Component focus = manager.getFocusOwner(); 2280 Component focusOn = getNextSide(splitPane, focus); 2281 if (focusOn != null) { 2282 // don't change the focus if the new focused component belongs 2283 // to the same splitpane and the same side 2284 if ( focus!=null && 2285 ( (SwingUtilities.isDescendingFrom(focus, left) && 2286 SwingUtilities.isDescendingFrom(focusOn, left)) || 2287 (SwingUtilities.isDescendingFrom(focus, right) && 2288 SwingUtilities.isDescendingFrom(focusOn, right)) ) ) { 2289 return; 2290 } 2291 SwingUtilities2.compositeRequestFocus(focusOn); 2292 } 2293 } 2294 2295 private Component getNextSide(JSplitPane splitPane, Component focus) { 2296 Component left = splitPane.getLeftComponent(); 2297 Component right = splitPane.getRightComponent(); 2298 Component next; 2299 if (focus!=null && SwingUtilities.isDescendingFrom(focus, left) && 2300 right!=null) { 2301 next = getFirstAvailableComponent(right); 2302 if (next != null) { 2303 return next; 2304 } 2305 } 2306 JSplitPane parentSplitPane = (JSplitPane)SwingUtilities.getAncestorOfClass(JSplitPane.class, splitPane); 2307 if (parentSplitPane!=null) { 2308 // focus next side of the parent split pane 2309 next = getNextSide(parentSplitPane, focus); 2310 } else { 2311 next = getFirstAvailableComponent(left); 2312 if (next == null) { 2313 next = getFirstAvailableComponent(right); 2314 } 2315 } 2316 return next; 2317 } 2318 2319 private Component getFirstAvailableComponent(Component c) { 2320 if (c!=null && c instanceof JSplitPane) { 2321 JSplitPane sp = (JSplitPane)c; 2322 Component left = getFirstAvailableComponent(sp.getLeftComponent()); 2323 if (left != null) { 2324 c = left; 2325 } else { 2326 c = getFirstAvailableComponent(sp.getRightComponent()); 2327 } 2328 } 2329 return c; 2330 } 2331 } 2332 }