ch.randelshofer.quaqua.QuaquaSplitPaneDivider Maven / Gradle / Ivy
Show all versions of Quaqua Show documentation
/*
* @(#)QuaquaSplitPaneDivider.java
*
* Copyright (c) 2005-2010 Werner Randelshofer, Immensee, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package ch.randelshofer.quaqua;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.basic.*;
/**
* QuaquaSplitPaneDivider.
*
* @author Werner Randelshofer
* @version $Id: QuaquaSplitPaneDivider.java 361 2010-11-21 11:19:20Z wrandelshofer $
*/
public class QuaquaSplitPaneDivider extends BasicSplitPaneDivider {
static final Cursor defaultCursor =
Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
/**
* Width or height of the divider based on orientation
* BasicSplitPaneUI adds two to this.
*/
protected static final int ONE_TOUCH_SIZE_ = 4;
protected static final int ONE_TOUCH_OFFSET_ = 2;
// private ActionListener doubleClickHandler = new DoubleClickActionHandler();
/**
* Creates a new instance.
*/
public QuaquaSplitPaneDivider(BasicSplitPaneUI ui) {
super(ui);
setLayout(new QuaquaDividerLayout());
setFocusable(UIManager.getBoolean("SplitPaneDivider.focusable"));
}
/**
* Sets the SplitPaneUI that is using the receiver.
*/
@Override
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 QuaquaMouseHandler();
}
splitPane.addMouseListener(mouseHandler);
splitPane.addMouseMotionListener(mouseHandler);
addMouseListener(mouseHandler);
addMouseMotionListener(mouseHandler);
splitPane.addPropertyChangeListener(this);
if (splitPane.isOneTouchExpandable()) {
oneTouchExpandableChanged();
}
}
} else {
splitPane = null;
}
}
/**
* Paints the divider.
*/
@Override
public void paint(Graphics g) {
Dimension size = getSize();
Insets insets = getInsets();
boolean isHorizontal = splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT;
// We support the following styles:
// "bar": The divider is a bar, that goes accross the split view.
// "thumb" (this is the default): The divider is a thumb (dimple) on
// the regular background pattern.
String style = (String) splitPane.getClientProperty("Quaqua.SplitPane.style");
if (style == null) {
style = "thumb";
}
if (style.equals("bar")) {
Border border = UIManager.getBorder(
(isHorizontal) ? "SplitPane.vBar" : "SplitPane.hBar");
if (border != null) {
border.paintBorder(
splitPane, g,
insets.left, insets.top,
size.width - insets.right - insets.left,
size.height - insets.top - insets.bottom);
}
}
Icon dimple;
boolean drawDimple;
if (style.equals("thumb")) {
dimple = UIManager.getIcon("SplitPane.thumbDimple");
drawDimple = true;
} else {
dimple = UIManager.getIcon("SplitPane.barDimple");
drawDimple = size.width >= dimple.getIconWidth()
&& size.height >= dimple.getIconHeight();
}
if (drawDimple) {
int x = (size.width - dimple.getIconWidth()) / 2;
int y = (size.height - dimple.getIconHeight()) / 2;
// Make sure, dimple does not intersect with the buttons
if (splitPane.isOneTouchExpandable()) {
if (isHorizontal) {
y = Math.min(y, leftButton.getY() - dimple.getIconHeight() - ONE_TOUCH_OFFSET_);
} else {
x = Math.min(x, leftButton.getX() - dimple.getIconWidth() - ONE_TOUCH_OFFSET_);
}
}
dimple.paintIcon(splitPane, g, x, y);
}
super.paint(g);
}
/**
* Creates and return an instance of JButton that can be used to
* collapse the left component in the split pane.
*/
@Override
protected JButton createLeftOneTouchButton() {
JButton b = new JButton() {
@Override
public Icon getIcon() {
return UIManager.getIcon(
(splitPane.getOrientation() == VERTICAL) ? "SplitPane.leftArrow" : "SplitPane.upArrow");
}
};
b.setBorder(null);
b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE_, ONE_TOUCH_SIZE_));
b.setCursor(defaultCursor);
b.setFocusPainted(false);
b.setBorderPainted(false);
b.setRequestFocusEnabled(false);
b.setFocusable(UIManager.getBoolean("SplitPaneDivider.focusable"));
return b;
}
/**
* Creates and return an instance of JButton that can be used to
* collapse the right component in the split pane.
*/
@Override
protected JButton createRightOneTouchButton() {
JButton b = new JButton() {
@Override
public Icon getIcon() {
return UIManager.getIcon(
(splitPane.getOrientation() == VERTICAL) ? "SplitPane.rightArrow" : "SplitPane.downArrow");
}
};
b.setBorder(null);
b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE_, ONE_TOUCH_SIZE_));
b.setCursor(defaultCursor);
b.setFocusPainted(false);
b.setBorderPainted(false);
b.setRequestFocusEnabled(false);
b.setFocusable(UIManager.getBoolean("SplitPaneDivider.focusable"));
return b;
}
/**
* Used to layout a BasicSplitPaneDivider
.
* Layout for the divider
* involves appropriately moving the left/right buttons around.
*
*/
protected class QuaquaDividerLayout implements LayoutManager {
public void layoutContainer(Container c) {
if (leftButton != null && rightButton != null
&& c == QuaquaSplitPaneDivider.this) {
if (splitPane.isOneTouchExpandable()) {
Insets insets = c.getInsets();
if (orientation == JSplitPane.VERTICAL_SPLIT) {
//int extraX = (insets != null) ? insets.left : 0;
int blockSize = getHeight();
blockSize -= (insets.top + insets.bottom);
blockSize = Math.max(blockSize, 0);
blockSize = Math.min(blockSize, ONE_TOUCH_SIZE_);
int extraX = c.getSize().width
- insets.right
- ONE_TOUCH_OFFSET_ - blockSize * 4;
int y = (c.getSize().height - blockSize) / 2;
leftButton.setBounds(extraX, y,
blockSize * 2, blockSize);
rightButton.setBounds(extraX + blockSize * 2, y,
blockSize * 2, blockSize);
} else {
//int extraY = (insets != null) ? insets.top : 0;
int blockSize = getWidth();
blockSize -= (insets.left + insets.right);
blockSize = Math.max(blockSize, 0);
blockSize = Math.min(blockSize, ONE_TOUCH_SIZE_);
int extraY = c.getSize().height
- insets.bottom
- ONE_TOUCH_OFFSET_ - blockSize * 4;
int x = (c.getSize().width - blockSize) / 2;
leftButton.setBounds(x, extraY,
blockSize, blockSize * 2);
rightButton.setBounds(x, extraY + blockSize * 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 != QuaquaSplitPaneDivider.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
/**
* MouseHandler is responsible for converting mouse events
* (released, dragged...) into the appropriate DragController
* methods.
*
*/
protected class QuaquaMouseHandler extends MouseHandler {
/**
* If dragger is not null it is messaged with completeDrag.
*/
@Override
public void mouseReleased(MouseEvent e) {
// The following code is needed, because the mouseReleased implementation
// in the superclass changes the divider location even when the
// user hasn't moved it.
int lastLoc = splitPane.getLastDividerLocation();
int currentLoc = splitPane.getDividerLocation();
super.mouseReleased(e);
if (splitPane.getDividerLocation() == currentLoc) {
splitPane.setLastDividerLocation(lastLoc);
}
}
/**
* Double click on the split bar moves it to the bottom or to the left.
* If it is already at the bottom most or leftmost position, it is moved
* to its last location.
*/
@Override
public void mouseClicked(MouseEvent evt) {
if (evt.getClickCount() == 2 && splitPane.isOneTouchExpandable()) {
boolean isHorizontal = splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT;
Component leftC = splitPane.getLeftComponent();
Component rightC = splitPane.getRightComponent();
if (leftC == null || rightC == null) {
return;
}
Insets insets = splitPane.getInsets();
int minLoc, maxLoc;
if (isHorizontal) {
if (leftC.isVisible()) {
minLoc = leftC.getMinimumSize().width
+ insets.left;
} else {
minLoc = insets.left;
}
if (rightC.isVisible()) {
maxLoc = splitPane.getWidth()
- rightC.getMinimumSize().width
- insets.right
- getSize().width;
} else {
maxLoc = splitPane.getWidth() - insets.right;
}
} else {
if (leftC.isVisible()) {
minLoc = leftC.getMinimumSize().height
+ insets.top;
} else {
minLoc = insets.top;
}
if (rightC.isVisible()) {
maxLoc = splitPane.getHeight()
- rightC.getMinimumSize().height
- insets.bottom
- getSize().height;
} else {
maxLoc = splitPane.getHeight() - insets.bottom;
}
}
maxLoc = Math.max(0, maxLoc);
minLoc = Math.max(0, Math.min(minLoc, maxLoc));
// FIXME We will provide a client property on the split
// bar which can be used to specify into which direction the
// split bar shall be moved on double click.
if (isHorizontal) {
int helper = maxLoc;
maxLoc = minLoc;
minLoc = helper;
}
int lastLoc = splitPane.getLastDividerLocation();
int currentLoc = splitPaneUI.getDividerLocation(splitPane);
int newLoc;
if (currentLoc == maxLoc) {
newLoc = lastLoc;
} else {
newLoc = maxLoc;
}
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);
}
}
}
}
}