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