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