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