/*
* Copyright (c) 2005-2009 Substance Kirill Grouchnikov. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of Substance Kirill Grouchnikov nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jvnet.substance.utils;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import org.jvnet.lafwidget.animation.*;
import org.jvnet.lafwidget.layout.TransitionLayout;
import org.jvnet.substance.SubstanceLookAndFeel;
import org.jvnet.substance.SubstanceSplitPaneUI;
import org.jvnet.substance.api.*;
import org.jvnet.substance.painter.utils.BackgroundPaintingUtils;
import org.jvnet.substance.utils.icon.TransitionAwareIcon;
/**
* Split pane divider in <code>Substance</code> look and feel.
*
* @author Kirill Grouchnikov
*/
public class SubstanceSplitPaneDivider extends BasicSplitPaneDivider implements
Trackable {
/**
* Listener for fade animations.
*/
private RolloverControlListener substanceRolloverListener;
/**
* Listener for fade animations.
*/
protected FadeStateListener substanceFadeStateListener;
/**
* Listener on property change events.
*/
private PropertyChangeListener substancePropertyChangeListener;
/**
* Surrogate button model for tracking the thumb transitions.
*/
private ButtonModel gripModel;
/**
* Simple constructor.
*
* @param ui
* Associated UI.
*/
public SubstanceSplitPaneDivider(SubstanceSplitPaneUI ui) {
super(ui);
this.setLayout(new SubstanceDividerLayout());
}
@Override
public void setBasicSplitPaneUI(BasicSplitPaneUI newUI) {
if (this.splitPane != null) {
// fix for defect 358 - multiple listeners were installed
// on the same split pane
this.uninstall();
}
if (newUI != null) {
// installing
this.splitPane = newUI.getSplitPane();
this.gripModel = new DefaultButtonModel();
this.gripModel.setArmed(false);
this.gripModel.setSelected(false);
this.gripModel.setPressed(false);
this.gripModel.setRollover(false);
this.gripModel.setEnabled(this.splitPane.isEnabled());
// fix for defect 109 - memory leak on changing skin
this.substanceRolloverListener = new RolloverControlListener(this,
this.gripModel);
this.addMouseListener(this.substanceRolloverListener);
this.addMouseMotionListener(this.substanceRolloverListener);
this.substancePropertyChangeListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if ("enabled".equals(evt.getPropertyName())) {
boolean isEnabled = splitPane.isEnabled();
gripModel.setEnabled(isEnabled);
if (leftButton != null)
leftButton.setEnabled(isEnabled);
if (rightButton != null)
rightButton.setEnabled(isEnabled);
setEnabled(isEnabled);
}
}
};
// System.out.println("Registering " + this.hashCode() + ":"
// + this.substancePropertyChangeListener.hashCode() + " on "
// + this.splitPane.hashCode());
this.splitPane
.addPropertyChangeListener(this.substancePropertyChangeListener);
this.substanceFadeStateListener = new FadeStateListener(this,
this.gripModel, SubstanceCoreUtilities.getFadeCallback(
this.splitPane, this.gripModel, false, false, this));
this.substanceFadeStateListener.registerListeners(false);
} else {
uninstall();
}
super.setBasicSplitPaneUI(newUI);
}
/**
* Uninstalls this divider.
*/
private void uninstall() {
// uninstalling
// fix for defect 109 - memory leak on changing skin
this.removeMouseListener(this.substanceRolloverListener);
this.removeMouseMotionListener(this.substanceRolloverListener);
this.substanceRolloverListener = null;
if (this.substancePropertyChangeListener != null) {
// System.out.println("Unregistering " + this.hashCode() + ":"
// + this.substancePropertyChangeListener.hashCode()
// + " from " + this.splitPane.hashCode());
this.splitPane
.removePropertyChangeListener(this.substancePropertyChangeListener);
this.substancePropertyChangeListener = null;
}
if (this.substanceFadeStateListener != null) {
this.substanceFadeStateListener.unregisterListeners();
this.substanceFadeStateListener = null;
}
}
/*
* (non-Javadoc)
*
* @see java.awt.Component#paint(java.awt.Graphics)
*/
@Override
public void paint(Graphics g) {
if (SubstanceCoreUtilities.hasFlatAppearance(this.splitPane, true)) {
BackgroundPaintingUtils.updateIfOpaque(g, this.splitPane);
}
Graphics2D graphics = (Graphics2D) g.create();
ComponentState currState = ComponentState.getState(this.gripModel,
this.splitPane);
ComponentState prevState = SubstanceCoreUtilities
.getPrevComponentState(this.splitPane);
float alpha = SubstanceColorSchemeUtilities.getAlpha(this.splitPane,
currState);
if (alpha < 1.0f) {
graphics.setComposite(TransitionLayout.getAlphaComposite(
this.splitPane, alpha, g));
}
FadeState fadeState = SubstanceFadeUtilities.getFadeState(this,
FadeKind.ROLLOVER, FadeKind.SELECTION, FadeKind.PRESS);
float cyclePos = 1.0f;
if (fadeState != null) {
cyclePos = fadeState.getFadePosition();
if (fadeState.isFadingIn())
cyclePos = 1.0f - cyclePos;
}
// compute the grip handle dimension
int minSizeForGripPresence = SubstanceSizeUtils.getAdjustedSize(
SubstanceSizeUtils.getComponentFontSize(this), 30, 1, 2, false);
int maxGripSize = SubstanceSizeUtils.getAdjustedSize(SubstanceSizeUtils
.getComponentFontSize(this), 40, 1, 3, false);
if (this.splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
int thumbHeight = this.getHeight();
if (thumbHeight >= minSizeForGripPresence) {
int gripHeight = thumbHeight / 4;
if (gripHeight > maxGripSize)
gripHeight = maxGripSize;
int thumbWidth = this.getWidth();
int gripX = 0;
int gripY = (thumbHeight - gripHeight) / 2;
// draw the grip bumps
SubstanceImageCreator.paintSplitDividerBumpImage(graphics,
this, gripX, gripY, thumbWidth, gripHeight, false,
currState, SubstanceColorSchemeUtilities
.getColorScheme(this,
ColorSchemeAssociationKind.MARK,
currState),
SubstanceColorSchemeUtilities.getColorScheme(this,
ColorSchemeAssociationKind.MARK, prevState),
cyclePos);
}
} else {
int thumbWidth = this.getWidth();
if (thumbWidth >= minSizeForGripPresence) {
int gripWidth = thumbWidth / 4;
if (gripWidth > maxGripSize)
gripWidth = maxGripSize;
int thumbHeight = this.getHeight();
// int gripHeight = thumbHeight * 2 / 3;
int gripX = (thumbWidth - gripWidth) / 2;
int gripY = 1;
// draw the grip bumps
SubstanceImageCreator.paintSplitDividerBumpImage(graphics,
this, gripX, gripY, gripWidth, thumbHeight, true,
currState, SubstanceColorSchemeUtilities
.getColorScheme(this,
ColorSchemeAssociationKind.MARK,
currState),
SubstanceColorSchemeUtilities.getColorScheme(this,
ColorSchemeAssociationKind.MARK, prevState),
cyclePos);
}
}
graphics.dispose();
super.paint(g);
}
/*
* (non-Javadoc)
*
* @see
* javax.swing.plaf.basic.BasicSplitPaneDivider#createLeftOneTouchButton()
*/
@Override
protected JButton createLeftOneTouchButton() {
JButton oneTouchButton = new JButton() {
// Don't want the button to participate in focus traversable.
@Override
public boolean isFocusable() {
return false;
}
};
Icon verticalSplit = new TransitionAwareIcon(oneTouchButton,
new TransitionAwareIcon.Delegate() {
public Icon getColorSchemeIcon(SubstanceColorScheme scheme) {
int fontSize = SubstanceSizeUtils
.getComponentFontSize(splitPane);
return SubstanceImageCreator.getArrowIcon(
SubstanceSizeUtils
.getSplitPaneArrowIconWidth(fontSize),
SubstanceSizeUtils
.getSplitPaneArrowIconHeight(fontSize),
SubstanceSizeUtils
.getArrowStrokeWidth(fontSize),
SwingConstants.NORTH, scheme);
}
}, "substance.splitPane.left.vertical");
Icon horizontalSplit = new TransitionAwareIcon(oneTouchButton,
new TransitionAwareIcon.Delegate() {
public Icon getColorSchemeIcon(SubstanceColorScheme scheme) {
int fontSize = SubstanceSizeUtils
.getComponentFontSize(splitPane);
return SubstanceImageCreator.getArrowIcon(
SubstanceSizeUtils
.getSplitPaneArrowIconWidth(fontSize),
SubstanceSizeUtils
.getSplitPaneArrowIconHeight(fontSize),
SubstanceSizeUtils
.getArrowStrokeWidth(fontSize),
SwingConstants.WEST, scheme);
}
}, "substance.splitPane.left.horizontal");
oneTouchButton
.setIcon(this.splitPane.getOrientation() == JSplitPane.VERTICAL_SPLIT ? verticalSplit
: horizontalSplit);
oneTouchButton.putClientProperty(
SubstanceLookAndFeel.BUTTON_PAINT_NEVER_PROPERTY, Boolean.TRUE);
// fix for issue 281 - set empty border so that the arrow
// icon is not cropped
oneTouchButton.setBorder(new EmptyBorder(0, 0, 0, 0));
oneTouchButton.setRequestFocusEnabled(false);
oneTouchButton
.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
oneTouchButton.setFocusPainted(false);
oneTouchButton.setBorderPainted(false);
return oneTouchButton;
}
/*
* (non-Javadoc)
*
* @see
* javax.swing.plaf.basic.BasicSplitPaneDivider#createRightOneTouchButton()
*/
@Override
protected JButton createRightOneTouchButton() {
JButton oneTouchButton = new JButton() {
// Don't want the button to participate in focus traversable.
@Override
public boolean isFocusable() {
return false;
}
};
Icon verticalSplit = new TransitionAwareIcon(oneTouchButton,
new TransitionAwareIcon.Delegate() {
public Icon getColorSchemeIcon(SubstanceColorScheme scheme) {
int fontSize = SubstanceSizeUtils
.getComponentFontSize(splitPane);
return SubstanceImageCreator.getArrowIcon(
SubstanceSizeUtils
.getSplitPaneArrowIconWidth(fontSize),
SubstanceSizeUtils
.getSplitPaneArrowIconHeight(fontSize),
SubstanceSizeUtils
.getArrowStrokeWidth(fontSize),
SwingConstants.SOUTH, scheme);
}
}, "substance.splitPane.right.vertical");
Icon horizontalSplit = new TransitionAwareIcon(oneTouchButton,
new TransitionAwareIcon.Delegate() {
public Icon getColorSchemeIcon(SubstanceColorScheme scheme) {
int fontSize = SubstanceSizeUtils
.getComponentFontSize(splitPane);
return SubstanceImageCreator.getArrowIcon(
SubstanceSizeUtils
.getSplitPaneArrowIconWidth(fontSize),
SubstanceSizeUtils
.getSplitPaneArrowIconHeight(fontSize),
SubstanceSizeUtils
.getArrowStrokeWidth(fontSize),
SwingConstants.EAST, scheme);
}
}, "substance.splitPane.right.horizontal");
oneTouchButton
.setIcon(this.splitPane.getOrientation() == JSplitPane.VERTICAL_SPLIT ? verticalSplit
: horizontalSplit);
oneTouchButton.putClientProperty(
SubstanceLookAndFeel.BUTTON_PAINT_NEVER_PROPERTY, Boolean.TRUE);
// fix for issue 281 - set empty border so that the arrow
// icon is not cropped
oneTouchButton.setBorder(new EmptyBorder(0, 0, 0, 0));
oneTouchButton
.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
oneTouchButton.setFocusPainted(false);
oneTouchButton.setBorderPainted(false);
oneTouchButton.setRequestFocusEnabled(false);
// b.setOpaque(false);
return oneTouchButton;
}
/**
* Updates the one-touch buttons.
*
* @param orientation
* Split pane orientation.
*/
public void updateOneTouchButtons(int orientation) {
if (orientation == JSplitPane.VERTICAL_SPLIT) {
if (this.leftButton != null) {
this.leftButton.setIcon(new TransitionAwareIcon(
this.leftButton, new TransitionAwareIcon.Delegate() {
public Icon getColorSchemeIcon(
SubstanceColorScheme scheme) {
int fontSize = SubstanceSizeUtils
.getComponentFontSize(splitPane);
return SubstanceImageCreator
.getArrowIcon(
SubstanceSizeUtils
.getSplitPaneArrowIconWidth(fontSize),
SubstanceSizeUtils
.getSplitPaneArrowIconHeight(fontSize),
SubstanceSizeUtils
.getArrowStrokeWidth(fontSize),
SwingConstants.NORTH, scheme);
}
}, "substance.splitPane.left.vertical"));
}
if (this.rightButton != null) {
this.rightButton.setIcon(new TransitionAwareIcon(
this.rightButton, new TransitionAwareIcon.Delegate() {
public Icon getColorSchemeIcon(
SubstanceColorScheme scheme) {
int fontSize = SubstanceSizeUtils
.getComponentFontSize(splitPane);
return SubstanceImageCreator
.getArrowIcon(
SubstanceSizeUtils
.getSplitPaneArrowIconWidth(fontSize),
SubstanceSizeUtils
.getSplitPaneArrowIconHeight(fontSize),
SubstanceSizeUtils
.getArrowStrokeWidth(fontSize),
SwingConstants.SOUTH, scheme);
}
}, "substance.splitPane.right.vertical"));
}
} else {
if (this.leftButton != null) {
this.leftButton.setIcon(new TransitionAwareIcon(
this.leftButton, new TransitionAwareIcon.Delegate() {
public Icon getColorSchemeIcon(
SubstanceColorScheme scheme) {
int fontSize = SubstanceSizeUtils
.getComponentFontSize(splitPane);
return SubstanceImageCreator
.getArrowIcon(
SubstanceSizeUtils
.getSplitPaneArrowIconWidth(fontSize),
SubstanceSizeUtils
.getSplitPaneArrowIconHeight(fontSize),
SubstanceSizeUtils
.getArrowStrokeWidth(fontSize),
SwingConstants.WEST, scheme);
}
}, "substance.splitPane.left.horizontal"));
}
if (this.rightButton != null) {
this.rightButton.setIcon(new TransitionAwareIcon(
this.rightButton, new TransitionAwareIcon.Delegate() {
public Icon getColorSchemeIcon(
SubstanceColorScheme scheme) {
int fontSize = SubstanceSizeUtils
.getComponentFontSize(splitPane);
return SubstanceImageCreator
.getArrowIcon(
SubstanceSizeUtils
.getSplitPaneArrowIconWidth(fontSize),
SubstanceSizeUtils
.getSplitPaneArrowIconHeight(fontSize),
SubstanceSizeUtils
.getArrowStrokeWidth(fontSize),
SwingConstants.EAST, scheme);
}
}, "substance.splitPane.right.horizontal"));
}
}
}
/*
* (non-Javadoc)
*
* @see
* org.jvnet.substance.utils.Trackable#isInside(java.awt.event.MouseEvent)
*/
public boolean isInside(MouseEvent me) {
// entire area is sensitive
return true;
}
/**
* Layout manager for the split pane divider.
*
* @author Kirill Grouchnikov
*/
protected class SubstanceDividerLayout extends DividerLayout {
@Override
public void layoutContainer(Container c) {
if (leftButton != null && rightButton != null
&& c == SubstanceSplitPaneDivider.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);
}
int y = (c.getSize().height - blockSize) / 2;
int offset = SubstanceSizeUtils
.getSplitPaneButtonOffset(SubstanceSizeUtils
.getComponentFontSize(splitPane));
leftButton.setBounds(extraX + offset, y, leftButton
.getPreferredSize().width * 2 / 3, blockSize);
rightButton.setBounds(leftButton.getX()
+ leftButton.getWidth(), y, rightButton
.getPreferredSize().width * 2 / 3, 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);
}
int x = (c.getSize().width - blockSize) / 2;
int offset = SubstanceSizeUtils
.getSplitPaneButtonOffset(SubstanceSizeUtils
.getComponentFontSize(splitPane));
leftButton.setBounds(x, extraY + offset, blockSize,
leftButton.getPreferredSize().height * 2 / 3);
rightButton.setBounds(x, leftButton.getY()
+ leftButton.getHeight(), blockSize, leftButton
.getPreferredSize().height * 2 / 3);
}
} else {
leftButton.setBounds(-5, -5, 1, 1);
rightButton.setBounds(-5, -5, 1, 1);
}
}
}
}
}