You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1091 lines
36 KiB
1091 lines
36 KiB
/*
|
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
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™
|
|
* 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 revalidateSplitPane() {
|
|
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));
|
|
revalidateSplitPane();
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
revalidateSplitPane();
|
|
}
|
|
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
*/
|
|
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™
|
|
* 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.
|
|
*
|
|
*/
|
|
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
|
|
}
|