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