--- /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
+}