jdk/src/share/classes/javax/swing/plaf/basic/BasicSplitPaneDivider.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicSplitPaneDivider.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1090 @@
+/*
+ * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+
+
+package javax.swing.plaf.basic;
+
+
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.plaf.*;
+import javax.swing.border.Border;
+import java.beans.*;
+import sun.swing.DefaultLookup;
+
+
+
+/**
+ * Divider used by BasicSplitPaneUI. Subclassers may wish to override
+ * paint to do something more interesting.
+ * The border effect is drawn in BasicSplitPaneUI, so if you don't like
+ * that border, reset it there.
+ * To conditionally drag from certain areas subclass mousePressed and
+ * call super when you wish the dragging to begin.
+ * <p>
+ * <strong>Warning:</strong>
+ * Serialized objects of this class will not be compatible with
+ * future Swing releases. The current serialization support is
+ * appropriate for short term storage or RMI between applications running
+ * the same version of Swing.  As of 1.4, support for long term storage
+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
+ * has been added to the <code>java.beans</code> package.
+ * Please see {@link java.beans.XMLEncoder}.
+ *
+ * @author Scott Violet
+ */
+public class BasicSplitPaneDivider extends Container
+    implements PropertyChangeListener
+{
+    /**
+     * Width or height of the divider based on orientation
+     * BasicSplitPaneUI adds two to this.
+     */
+    protected static final int ONE_TOUCH_SIZE = 6;
+    protected static final int ONE_TOUCH_OFFSET = 2;
+
+    /**
+     * Handles mouse dragging message to do the actual dragging.
+     */
+    protected DragController dragger;
+
+    /**
+     * UI this instance was created from.
+     */
+    protected BasicSplitPaneUI splitPaneUI;
+
+    /**
+     * Size of the divider.
+     */
+    protected int dividerSize = 0; // default - SET TO 0???
+
+    /**
+     * Divider that is used for noncontinuous layout mode.
+     */
+    protected Component hiddenDivider;
+
+    /**
+     * JSplitPane the receiver is contained in.
+     */
+    protected JSplitPane splitPane;
+
+    /**
+     * Handles mouse events from both this class, and the split pane.
+     * Mouse events are handled for the splitpane since you want to be able
+     * to drag when clicking on the border of the divider, which is not
+     * drawn by the divider.
+     */
+    protected MouseHandler mouseHandler;
+
+    /**
+     * Orientation of the JSplitPane.
+     */
+    protected int orientation;
+
+    /**
+     * Button for quickly toggling the left component.
+     */
+    protected JButton leftButton;
+
+    /**
+     * Button for quickly toggling the right component.
+     */
+    protected JButton rightButton;
+
+    /** Border. */
+    private Border border;
+
+    /**
+     * Is the mouse over the divider?
+     */
+    private boolean mouseOver;
+
+    private int oneTouchSize;
+    private int oneTouchOffset;
+
+    /**
+     * If true the one touch buttons are centered on the divider.
+     */
+    private boolean centerOneTouchButtons;
+
+
+    /**
+     * Creates an instance of BasicSplitPaneDivider. Registers this
+     * instance for mouse events and mouse dragged events.
+     */
+    public BasicSplitPaneDivider(BasicSplitPaneUI ui) {
+        oneTouchSize = DefaultLookup.getInt(ui.getSplitPane(), ui,
+                "SplitPane.oneTouchButtonSize", ONE_TOUCH_SIZE);
+        oneTouchOffset = DefaultLookup.getInt(ui.getSplitPane(), ui,
+                "SplitPane.oneTouchButtonOffset", ONE_TOUCH_OFFSET);
+        centerOneTouchButtons = DefaultLookup.getBoolean(ui.getSplitPane(),
+                 ui, "SplitPane.centerOneTouchButtons", true);
+        setLayout(new DividerLayout());
+        setBasicSplitPaneUI(ui);
+        orientation = splitPane.getOrientation();
+        setCursor((orientation == JSplitPane.HORIZONTAL_SPLIT) ?
+                  Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR) :
+                  Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
+        setBackground(UIManager.getColor("SplitPane.background"));
+    }
+
+    private void revalidate() {
+        invalidate();
+        if (splitPane != null) {
+            splitPane.revalidate();
+        }
+    }
+
+    /**
+     * Sets the SplitPaneUI that is using the receiver.
+     */
+    public void setBasicSplitPaneUI(BasicSplitPaneUI newUI) {
+        if (splitPane != null) {
+            splitPane.removePropertyChangeListener(this);
+           if (mouseHandler != null) {
+               splitPane.removeMouseListener(mouseHandler);
+               splitPane.removeMouseMotionListener(mouseHandler);
+               removeMouseListener(mouseHandler);
+               removeMouseMotionListener(mouseHandler);
+               mouseHandler = null;
+           }
+        }
+        splitPaneUI = newUI;
+        if (newUI != null) {
+            splitPane = newUI.getSplitPane();
+            if (splitPane != null) {
+                if (mouseHandler == null) mouseHandler = new MouseHandler();
+                splitPane.addMouseListener(mouseHandler);
+                splitPane.addMouseMotionListener(mouseHandler);
+                addMouseListener(mouseHandler);
+                addMouseMotionListener(mouseHandler);
+                splitPane.addPropertyChangeListener(this);
+                if (splitPane.isOneTouchExpandable()) {
+                    oneTouchExpandableChanged();
+                }
+            }
+        }
+        else {
+            splitPane = null;
+        }
+    }
+
+
+    /**
+     * Returns the <code>SplitPaneUI</code> the receiver is currently
+     * in.
+     */
+    public BasicSplitPaneUI getBasicSplitPaneUI() {
+        return splitPaneUI;
+    }
+
+
+    /**
+     * Sets the size of the divider to <code>newSize</code>. That is
+     * the width if the splitpane is <code>HORIZONTAL_SPLIT</code>, or
+     * the height of <code>VERTICAL_SPLIT</code>.
+     */
+    public void setDividerSize(int newSize) {
+        dividerSize = newSize;
+    }
+
+
+    /**
+     * Returns the size of the divider, that is the width if the splitpane
+     * is HORIZONTAL_SPLIT, or the height of VERTICAL_SPLIT.
+     */
+    public int getDividerSize() {
+        return dividerSize;
+    }
+
+
+    /**
+     * Sets the border of this component.
+     * @since 1.3
+     */
+    public void setBorder(Border border) {
+        Border         oldBorder = this.border;
+
+        this.border = border;
+    }
+
+    /**
+     * Returns the border of this component or null if no border is
+     * currently set.
+     *
+     * @return the border object for this component
+     * @see #setBorder
+     * @since 1.3
+     */
+    public Border getBorder() {
+        return border;
+    }
+
+    /**
+     * If a border has been set on this component, returns the
+     * border's insets, else calls super.getInsets.
+     *
+     * @return the value of the insets property.
+     * @see #setBorder
+     */
+    public Insets getInsets() {
+        Border    border = getBorder();
+
+        if (border != null) {
+            return border.getBorderInsets(this);
+        }
+        return super.getInsets();
+    }
+
+    /**
+     * Sets whether or not the mouse is currently over the divider.
+     *
+     * @param mouseOver whether or not the mouse is currently over the divider
+     * @since 1.5
+     */
+    protected void setMouseOver(boolean mouseOver) {
+        this.mouseOver = mouseOver;
+    }
+
+    /**
+     * Returns whether or not the mouse is currently over the divider
+     *
+     * @return whether or not the mouse is currently over the divider
+     * @since 1.5
+     */
+    public boolean isMouseOver() {
+        return mouseOver;
+    }
+
+    /**
+     * Returns dividerSize x dividerSize
+     */
+    public Dimension getPreferredSize() {
+        // Ideally this would return the size from the layout manager,
+        // but that could result in the layed out size being different from
+        // the dividerSize, which may break developers as well as
+        // BasicSplitPaneUI.
+        if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
+            return new Dimension(getDividerSize(), 1);
+        }
+        return new Dimension(1, getDividerSize());
+    }
+
+    /**
+     * Returns dividerSize x dividerSize
+     */
+    public Dimension getMinimumSize() {
+        return getPreferredSize();
+    }
+
+
+    /**
+     * Property change event, presumably from the JSplitPane, will message
+     * updateOrientation if necessary.
+     */
+    public void propertyChange(PropertyChangeEvent e) {
+        if (e.getSource() == splitPane) {
+            if (e.getPropertyName() == JSplitPane.ORIENTATION_PROPERTY) {
+                orientation = splitPane.getOrientation();
+                setCursor((orientation == JSplitPane.HORIZONTAL_SPLIT) ?
+                          Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR) :
+                          Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
+                revalidate();
+            }
+            else if (e.getPropertyName() == JSplitPane.
+                      ONE_TOUCH_EXPANDABLE_PROPERTY) {
+                oneTouchExpandableChanged();
+            }
+        }
+    }
+
+
+    /**
+     * Paints the divider.
+     */
+    public void paint(Graphics g) {
+      super.paint(g);
+
+      // Paint the border.
+      Border   border = getBorder();
+
+      if (border != null) {
+          Dimension     size = getSize();
+
+          border.paintBorder(this, g, 0, 0, size.width, size.height);
+      }
+    }
+
+
+    /**
+     * Messaged when the oneTouchExpandable value of the JSplitPane the
+     * receiver is contained in changes. Will create the
+     * <code>leftButton</code> and <code>rightButton</code> if they
+     * are null. invalidates the receiver as well.
+     */
+    protected void oneTouchExpandableChanged() {
+        if (!DefaultLookup.getBoolean(splitPane, splitPaneUI,
+                           "SplitPane.supportsOneTouchButtons", true)) {
+            // Look and feel doesn't want to support one touch buttons, bail.
+            return;
+        }
+        if (splitPane.isOneTouchExpandable() &&
+            leftButton == null &&
+            rightButton == null) {
+            /* Create the left button and add an action listener to
+               expand/collapse it. */
+            leftButton = createLeftOneTouchButton();
+            if (leftButton != null)
+                leftButton.addActionListener(new OneTouchActionHandler(true));
+
+
+            /* Create the right button and add an action listener to
+               expand/collapse it. */
+            rightButton = createRightOneTouchButton();
+            if (rightButton != null)
+                rightButton.addActionListener(new OneTouchActionHandler
+                    (false));
+
+            if (leftButton != null && rightButton != null) {
+                add(leftButton);
+                add(rightButton);
+            }
+        }
+        revalidate();
+    }
+
+
+    /**
+     * Creates and return an instance of JButton that can be used to
+     * collapse the left component in the split pane.
+     */
+    protected JButton createLeftOneTouchButton() {
+        JButton b = new JButton() {
+            public void setBorder(Border b) {
+            }
+            public void paint(Graphics g) {
+                if (splitPane != null) {
+                    int[]   xs = new int[3];
+                    int[]   ys = new int[3];
+                    int     blockSize;
+
+                    // Fill the background first ...
+                    g.setColor(this.getBackground());
+                    g.fillRect(0, 0, this.getWidth(),
+                               this.getHeight());
+
+                    // ... then draw the arrow.
+                    g.setColor(Color.black);
+                    if (orientation == JSplitPane.VERTICAL_SPLIT) {
+                        blockSize = Math.min(getHeight(), oneTouchSize);
+                        xs[0] = blockSize;
+                        xs[1] = 0;
+                        xs[2] = blockSize << 1;
+                        ys[0] = 0;
+                        ys[1] = ys[2] = blockSize;
+                        g.drawPolygon(xs, ys, 3); // Little trick to make the
+                                                  // arrows of equal size
+                    }
+                    else {
+                        blockSize = Math.min(getWidth(), oneTouchSize);
+                        xs[0] = xs[2] = blockSize;
+                        xs[1] = 0;
+                        ys[0] = 0;
+                        ys[1] = blockSize;
+                        ys[2] = blockSize << 1;
+                    }
+                    g.fillPolygon(xs, ys, 3);
+                }
+            }
+            // Don't want the button to participate in focus traversable.
+            public boolean isFocusTraversable() {
+                return false;
+            }
+        };
+        b.setMinimumSize(new Dimension(oneTouchSize, oneTouchSize));
+        b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+        b.setFocusPainted(false);
+        b.setBorderPainted(false);
+        b.setRequestFocusEnabled(false);
+        return b;
+    }
+
+
+    /**
+     * Creates and return an instance of JButton that can be used to
+     * collapse the right component in the split pane.
+     */
+    protected JButton createRightOneTouchButton() {
+        JButton b = new JButton() {
+            public void setBorder(Border border) {
+            }
+            public void paint(Graphics g) {
+                if (splitPane != null) {
+                    int[]          xs = new int[3];
+                    int[]          ys = new int[3];
+                    int            blockSize;
+
+                    // Fill the background first ...
+                    g.setColor(this.getBackground());
+                    g.fillRect(0, 0, this.getWidth(),
+                               this.getHeight());
+
+                    // ... then draw the arrow.
+                    if (orientation == JSplitPane.VERTICAL_SPLIT) {
+                        blockSize = Math.min(getHeight(), oneTouchSize);
+                        xs[0] = blockSize;
+                        xs[1] = blockSize << 1;
+                        xs[2] = 0;
+                        ys[0] = blockSize;
+                        ys[1] = ys[2] = 0;
+                    }
+                    else {
+                        blockSize = Math.min(getWidth(), oneTouchSize);
+                        xs[0] = xs[2] = 0;
+                        xs[1] = blockSize;
+                        ys[0] = 0;
+                        ys[1] = blockSize;
+                        ys[2] = blockSize << 1;
+                    }
+                    g.setColor(Color.black);
+                    g.fillPolygon(xs, ys, 3);
+                }
+            }
+            // Don't want the button to participate in focus traversable.
+            public boolean isFocusTraversable() {
+                return false;
+            }
+        };
+        b.setMinimumSize(new Dimension(oneTouchSize, oneTouchSize));
+        b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+        b.setFocusPainted(false);
+        b.setBorderPainted(false);
+        b.setRequestFocusEnabled(false);
+        return b;
+    }
+
+
+    /**
+     * Message to prepare for dragging. This messages the BasicSplitPaneUI
+     * with startDragging.
+     */
+    protected void prepareForDragging() {
+        splitPaneUI.startDragging();
+    }
+
+
+    /**
+     * Messages the BasicSplitPaneUI with dragDividerTo that this instance
+     * is contained in.
+     */
+    protected void dragDividerTo(int location) {
+        splitPaneUI.dragDividerTo(location);
+    }
+
+
+    /**
+     * Messages the BasicSplitPaneUI with finishDraggingTo that this instance
+     * is contained in.
+     */
+    protected void finishDraggingTo(int location) {
+        splitPaneUI.finishDraggingTo(location);
+    }
+
+
+    /**
+     * MouseHandler is responsible for converting mouse events
+     * (released, dragged...) into the appropriate DragController
+     * methods.
+     * <p>
+     */
+    protected class MouseHandler extends MouseAdapter
+            implements MouseMotionListener
+    {
+        /**
+         * Starts the dragging session by creating the appropriate instance
+         * of DragController.
+         */
+        public void mousePressed(MouseEvent e) {
+            if ((e.getSource() == BasicSplitPaneDivider.this ||
+                 e.getSource() == splitPane) &&
+                dragger == null &&splitPane.isEnabled()) {
+                Component            newHiddenDivider = splitPaneUI.
+                                     getNonContinuousLayoutDivider();
+
+                if (hiddenDivider != newHiddenDivider) {
+                    if (hiddenDivider != null) {
+                        hiddenDivider.removeMouseListener(this);
+                        hiddenDivider.removeMouseMotionListener(this);
+                    }
+                    hiddenDivider = newHiddenDivider;
+                    if (hiddenDivider != null) {
+                        hiddenDivider.addMouseMotionListener(this);
+                        hiddenDivider.addMouseListener(this);
+                    }
+                }
+                if (splitPane.getLeftComponent() != null &&
+                    splitPane.getRightComponent() != null) {
+                    if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
+                        dragger = new DragController(e);
+                    }
+                    else {
+                        dragger = new VerticalDragController(e);
+                    }
+                    if (!dragger.isValid()) {
+                        dragger = null;
+                    }
+                    else {
+                        prepareForDragging();
+                        dragger.continueDrag(e);
+                    }
+                }
+                e.consume();
+            }
+        }
+
+
+        /**
+         * If dragger is not null it is messaged with completeDrag.
+         */
+        public void mouseReleased(MouseEvent e) {
+            if (dragger != null) {
+                if (e.getSource() == splitPane) {
+                    dragger.completeDrag(e.getX(), e.getY());
+                }
+                else if (e.getSource() == BasicSplitPaneDivider.this) {
+                    Point   ourLoc = getLocation();
+
+                    dragger.completeDrag(e.getX() + ourLoc.x,
+                                         e.getY() + ourLoc.y);
+                }
+                else if (e.getSource() == hiddenDivider) {
+                    Point   hDividerLoc = hiddenDivider.getLocation();
+                    int     ourX = e.getX() + hDividerLoc.x;
+                    int     ourY = e.getY() + hDividerLoc.y;
+
+                    dragger.completeDrag(ourX, ourY);
+                }
+                dragger = null;
+                e.consume();
+            }
+        }
+
+
+        //
+        // MouseMotionListener
+        //
+
+        /**
+         * If dragger is not null it is messaged with continueDrag.
+         */
+        public void mouseDragged(MouseEvent e) {
+            if (dragger != null) {
+                if (e.getSource() == splitPane) {
+                    dragger.continueDrag(e.getX(), e.getY());
+                }
+                else if (e.getSource() == BasicSplitPaneDivider.this) {
+                    Point   ourLoc = getLocation();
+
+                    dragger.continueDrag(e.getX() + ourLoc.x,
+                                         e.getY() + ourLoc.y);
+                }
+                else if (e.getSource() == hiddenDivider) {
+                    Point   hDividerLoc = hiddenDivider.getLocation();
+                    int     ourX = e.getX() + hDividerLoc.x;
+                    int     ourY = e.getY() + hDividerLoc.y;
+
+                    dragger.continueDrag(ourX, ourY);
+                }
+                e.consume();
+            }
+        }
+
+
+        /**
+         *  Resets the cursor based on the orientation.
+         */
+        public void mouseMoved(MouseEvent e) {
+        }
+
+        /**
+         * Invoked when the mouse enters a component.
+         *
+         * @param e MouseEvent describing the details of the enter event.
+         * @since 1.5
+         */
+        public void mouseEntered(MouseEvent e) {
+            if (e.getSource() == BasicSplitPaneDivider.this) {
+                setMouseOver(true);
+            }
+        }
+
+        /**
+         * Invoked when the mouse exits a component.
+         *
+         * @param e MouseEvent describing the details of the exit event.
+         * @since 1.5
+         */
+        public void mouseExited(MouseEvent e) {
+            if (e.getSource() == BasicSplitPaneDivider.this) {
+                setMouseOver(false);
+            }
+        }
+    }
+
+
+    /**
+     * Handles the events during a dragging session for a
+     * HORIZONTAL_SPLIT oriented split pane. This continually
+     * messages <code>dragDividerTo</code> and then when done messages
+     * <code>finishDraggingTo</code>. When an instance is created it should be
+     * messaged with <code>isValid</code> to insure that dragging can happen
+     * (dragging won't be allowed if the two views can not be resized).
+     * <p>
+     * <strong>Warning:</strong>
+     * Serialized objects of this class will not be compatible with
+     * future Swing releases. The current serialization support is
+     * appropriate for short term storage or RMI between applications running
+     * the same version of Swing.  As of 1.4, support for long term storage
+     * of all JavaBeans<sup><font size="-2">TM</font></sup>
+     * has been added to the <code>java.beans</code> package.
+     * Please see {@link java.beans.XMLEncoder}.
+     */
+    protected class DragController
+    {
+        /**
+         * Initial location of the divider.
+         */
+        int initialX;
+
+        /**
+         * Maximum and minimum positions to drag to.
+         */
+        int maxX, minX;
+
+        /**
+         * Initial location the mouse down happened at.
+         */
+        int offset;
+
+
+        protected DragController(MouseEvent e) {
+            JSplitPane  splitPane = splitPaneUI.getSplitPane();
+            Component   leftC = splitPane.getLeftComponent();
+            Component   rightC = splitPane.getRightComponent();
+
+            initialX = getLocation().x;
+            if (e.getSource() == BasicSplitPaneDivider.this) {
+                offset = e.getX();
+            }
+            else { // splitPane
+                offset = e.getX() - initialX;
+            }
+            if (leftC == null || rightC == null || offset < -1 ||
+                offset >= getSize().width) {
+                // Don't allow dragging.
+                maxX = -1;
+            }
+            else {
+                Insets      insets = splitPane.getInsets();
+
+                if (leftC.isVisible()) {
+                    minX = leftC.getMinimumSize().width;
+                    if (insets != null) {
+                        minX += insets.left;
+                    }
+                }
+                else {
+                    minX = 0;
+                }
+                if (rightC.isVisible()) {
+                    int right = (insets != null) ? insets.right : 0;
+                    maxX = Math.max(0, splitPane.getSize().width -
+                                    (getSize().width + right) -
+                                    rightC.getMinimumSize().width);
+                }
+                else {
+                    int right = (insets != null) ? insets.right : 0;
+                    maxX = Math.max(0, splitPane.getSize().width -
+                                    (getSize().width + right));
+                }
+                if (maxX < minX) minX = maxX = 0;
+            }
+        }
+
+
+        /**
+         * Returns true if the dragging session is valid.
+         */
+        protected boolean isValid() {
+            return (maxX > 0);
+        }
+
+
+        /**
+         * Returns the new position to put the divider at based on
+         * the passed in MouseEvent.
+         */
+        protected int positionForMouseEvent(MouseEvent e) {
+            int newX = (e.getSource() == BasicSplitPaneDivider.this) ?
+                        (e.getX() + getLocation().x) : e.getX();
+
+            newX = Math.min(maxX, Math.max(minX, newX - offset));
+            return newX;
+        }
+
+
+        /**
+         * Returns the x argument, since this is used for horizontal
+         * splits.
+         */
+        protected int getNeededLocation(int x, int y) {
+            int newX;
+
+            newX = Math.min(maxX, Math.max(minX, x - offset));
+            return newX;
+        }
+
+
+        protected void continueDrag(int newX, int newY) {
+            dragDividerTo(getNeededLocation(newX, newY));
+        }
+
+
+        /**
+         * Messages dragDividerTo with the new location for the mouse
+         * event.
+         */
+        protected void continueDrag(MouseEvent e) {
+            dragDividerTo(positionForMouseEvent(e));
+        }
+
+
+        protected void completeDrag(int x, int y) {
+            finishDraggingTo(getNeededLocation(x, y));
+        }
+
+
+        /**
+         * Messages finishDraggingTo with the new location for the mouse
+         * event.
+         */
+        protected void completeDrag(MouseEvent e) {
+            finishDraggingTo(positionForMouseEvent(e));
+        }
+    } // End of BasicSplitPaneDivider.DragController
+
+
+    /**
+     * Handles the events during a dragging session for a
+     * VERTICAL_SPLIT oriented split pane. This continually
+     * messages <code>dragDividerTo</code> and then when done messages
+     * <code>finishDraggingTo</code>. When an instance is created it should be
+     * messaged with <code>isValid</code> to insure that dragging can happen
+     * (dragging won't be allowed if the two views can not be resized).
+     */
+    protected class VerticalDragController extends DragController
+    {
+        /* DragControllers ivars are now in terms of y, not x. */
+        protected VerticalDragController(MouseEvent e) {
+            super(e);
+            JSplitPane splitPane = splitPaneUI.getSplitPane();
+            Component  leftC = splitPane.getLeftComponent();
+            Component  rightC = splitPane.getRightComponent();
+
+            initialX = getLocation().y;
+            if (e.getSource() == BasicSplitPaneDivider.this) {
+                offset = e.getY();
+            }
+            else {
+                offset = e.getY() - initialX;
+            }
+            if (leftC == null || rightC == null || offset < -1 ||
+                offset > getSize().height) {
+                // Don't allow dragging.
+                maxX = -1;
+            }
+            else {
+                Insets     insets = splitPane.getInsets();
+
+                if (leftC.isVisible()) {
+                    minX = leftC.getMinimumSize().height;
+                    if (insets != null) {
+                        minX += insets.top;
+                    }
+                }
+                else {
+                    minX = 0;
+                }
+                if (rightC.isVisible()) {
+                    int    bottom = (insets != null) ? insets.bottom : 0;
+
+                    maxX = Math.max(0, splitPane.getSize().height -
+                                    (getSize().height + bottom) -
+                                    rightC.getMinimumSize().height);
+                }
+                else {
+                    int    bottom = (insets != null) ? insets.bottom : 0;
+
+                    maxX = Math.max(0, splitPane.getSize().height -
+                                    (getSize().height + bottom));
+                }
+                if (maxX < minX) minX = maxX = 0;
+            }
+        }
+
+
+        /**
+         * Returns the y argument, since this is used for vertical
+         * splits.
+         */
+        protected int getNeededLocation(int x, int y) {
+            int newY;
+
+            newY = Math.min(maxX, Math.max(minX, y - offset));
+            return newY;
+        }
+
+
+        /**
+         * Returns the new position to put the divider at based on
+         * the passed in MouseEvent.
+         */
+        protected int positionForMouseEvent(MouseEvent e) {
+            int newY = (e.getSource() == BasicSplitPaneDivider.this) ?
+                        (e.getY() + getLocation().y) : e.getY();
+
+
+            newY = Math.min(maxX, Math.max(minX, newY - offset));
+            return newY;
+        }
+    } // End of BasicSplitPaneDividier.VerticalDragController
+
+
+    /**
+     * Used to layout a <code>BasicSplitPaneDivider</code>.
+     * Layout for the divider
+     * involves appropriately moving the left/right buttons around.
+     * <p>
+     */
+    protected class DividerLayout implements LayoutManager
+    {
+        public void layoutContainer(Container c) {
+            if (leftButton != null && rightButton != null &&
+                c == BasicSplitPaneDivider.this) {
+                if (splitPane.isOneTouchExpandable()) {
+                    Insets insets = getInsets();
+
+                    if (orientation == JSplitPane.VERTICAL_SPLIT) {
+                        int extraX = (insets != null) ? insets.left : 0;
+                        int blockSize = getHeight();
+
+                        if (insets != null) {
+                            blockSize -= (insets.top + insets.bottom);
+                            blockSize = Math.max(blockSize, 0);
+                        }
+                        blockSize = Math.min(blockSize, oneTouchSize);
+
+                        int y = (c.getSize().height - blockSize) / 2;
+
+                        if (!centerOneTouchButtons) {
+                            y = (insets != null) ? insets.top : 0;
+                            extraX = 0;
+                        }
+                        leftButton.setBounds(extraX + oneTouchOffset, y,
+                                             blockSize * 2, blockSize);
+                        rightButton.setBounds(extraX + oneTouchOffset +
+                                              oneTouchSize * 2, y,
+                                              blockSize * 2, blockSize);
+                    }
+                    else {
+                        int extraY = (insets != null) ? insets.top : 0;
+                        int blockSize = getWidth();
+
+                        if (insets != null) {
+                            blockSize -= (insets.left + insets.right);
+                            blockSize = Math.max(blockSize, 0);
+                        }
+                        blockSize = Math.min(blockSize, oneTouchSize);
+
+                        int x = (c.getSize().width - blockSize) / 2;
+
+                        if (!centerOneTouchButtons) {
+                            x = (insets != null) ? insets.left : 0;
+                            extraY = 0;
+                        }
+
+                        leftButton.setBounds(x, extraY + oneTouchOffset,
+                                             blockSize, blockSize * 2);
+                        rightButton.setBounds(x, extraY + oneTouchOffset +
+                                              oneTouchSize * 2, blockSize,
+                                              blockSize * 2);
+                    }
+                }
+                else {
+                    leftButton.setBounds(-5, -5, 1, 1);
+                    rightButton.setBounds(-5, -5, 1, 1);
+                }
+            }
+        }
+
+
+        public Dimension minimumLayoutSize(Container c) {
+            // NOTE: This isn't really used, refer to
+            // BasicSplitPaneDivider.getPreferredSize for the reason.
+            // I leave it in hopes of having this used at some point.
+            if (c != BasicSplitPaneDivider.this || splitPane == null) {
+                return new Dimension(0,0);
+            }
+            Dimension buttonMinSize = null;
+
+            if (splitPane.isOneTouchExpandable() && leftButton != null) {
+                buttonMinSize = leftButton.getMinimumSize();
+            }
+
+            Insets insets = getInsets();
+            int width = getDividerSize();
+            int height = width;
+
+            if (orientation == JSplitPane.VERTICAL_SPLIT) {
+                if (buttonMinSize != null) {
+                    int size = buttonMinSize.height;
+                    if (insets != null) {
+                        size += insets.top + insets.bottom;
+                    }
+                    height = Math.max(height, size);
+                }
+                width = 1;
+            }
+            else {
+                if (buttonMinSize != null) {
+                    int size = buttonMinSize.width;
+                    if (insets != null) {
+                        size += insets.left + insets.right;
+                    }
+                    width = Math.max(width, size);
+                }
+                height = 1;
+            }
+            return new Dimension(width, height);
+        }
+
+
+        public Dimension preferredLayoutSize(Container c) {
+            return minimumLayoutSize(c);
+        }
+
+
+        public void removeLayoutComponent(Component c) {}
+
+        public void addLayoutComponent(String string, Component c) {}
+    } // End of class BasicSplitPaneDivider.DividerLayout
+
+
+    /**
+     * Listeners installed on the one touch expandable buttons.
+     */
+    private class OneTouchActionHandler implements ActionListener {
+        /** True indicates the resize should go the minimum (top or left)
+         * vs false which indicates the resize should go to the maximum.
+         */
+        private boolean toMinimum;
+
+        OneTouchActionHandler(boolean toMinimum) {
+            this.toMinimum = toMinimum;
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            Insets  insets = splitPane.getInsets();
+            int     lastLoc = splitPane.getLastDividerLocation();
+            int     currentLoc = splitPaneUI.getDividerLocation(splitPane);
+            int     newLoc;
+
+            // We use the location from the UI directly, as the location the
+            // JSplitPane itself maintains is not necessarly correct.
+            if (toMinimum) {
+                if (orientation == JSplitPane.VERTICAL_SPLIT) {
+                    if (currentLoc >= (splitPane.getHeight() -
+                                       insets.bottom - getHeight())) {
+                        int maxLoc = splitPane.getMaximumDividerLocation();
+                        newLoc = Math.min(lastLoc, maxLoc);
+                        splitPaneUI.setKeepHidden(false);
+                    }
+                    else {
+                        newLoc = insets.top;
+                        splitPaneUI.setKeepHidden(true);
+                    }
+                }
+                else {
+                    if (currentLoc >= (splitPane.getWidth() -
+                                       insets.right - getWidth())) {
+                        int maxLoc = splitPane.getMaximumDividerLocation();
+                        newLoc = Math.min(lastLoc, maxLoc);
+                        splitPaneUI.setKeepHidden(false);
+                    }
+                    else {
+                        newLoc = insets.left;
+                        splitPaneUI.setKeepHidden(true);
+                    }
+                }
+            }
+            else {
+                if (orientation == JSplitPane.VERTICAL_SPLIT) {
+                    if (currentLoc == insets.top) {
+                        int maxLoc = splitPane.getMaximumDividerLocation();
+                        newLoc = Math.min(lastLoc, maxLoc);
+                        splitPaneUI.setKeepHidden(false);
+                    }
+                    else {
+                        newLoc = splitPane.getHeight() - getHeight() -
+                                 insets.top;
+                        splitPaneUI.setKeepHidden(true);
+                    }
+                }
+                else {
+                    if (currentLoc == insets.left) {
+                        int maxLoc = splitPane.getMaximumDividerLocation();
+                        newLoc = Math.min(lastLoc, maxLoc);
+                        splitPaneUI.setKeepHidden(false);
+                    }
+                    else {
+                        newLoc = splitPane.getWidth() - getWidth() -
+                                 insets.left;
+                        splitPaneUI.setKeepHidden(true);
+                    }
+                }
+            }
+            if (currentLoc != newLoc) {
+                splitPane.setDividerLocation(newLoc);
+                // We do this in case the dividers notion of the location
+                // differs from the real location.
+                splitPane.setLastDividerLocation(currentLoc);
+            }
+        }
+    } // End of class BasicSplitPaneDivider.LeftActionListener
+}