/*
* org.openmicroscopy.shoola.util.ui.slider.OneKnobSliderUI
*
*------------------------------------------------------------------------------
* Copyright (C) 2006 University of Dundee. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.util.ui.slider;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JSlider;
import javax.swing.plaf.basic.BasicSliderUI;
import org.openmicroscopy.shoola.util.ui.IconManager;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
/**
* The UI of the <code>OnknobSlider</code>.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author Donald MacDonald
* <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
* @version 3.0
* @since OME2.2
*/
public class OneKnobSliderUI
extends BasicSliderUI
{
/** Spacing between the arrow and the end of the slider track. */
private static final int ARROW_SPACE = 2;
/** Spacing between the arrow and the end label. */
private static final int TEXT_SPACE = 2;
/** Offset to the left of the mouse used for placing tooltip. */
//private static final int TOOLTIP_OFFSET = 25;
/** Image used for the thumb. */
private Image thumbImage;
/** Image used for the thumb when the component is disabled. */
private Image disabledThumbImage;
/** Image used for the up arrow. */
private Image upArrowImage;
/** Image used for the up arrow when the component is disabled. */
private Image upArrowDisabledImage;
/** Image used for the down arrow. */
private Image downArrowImage;
/** Image used for the down arrow when the component is disabled. */
private Image downArrowDisabledImage;
/** Image used for the left arrow. */
private Image leftArrowImage;
/** Image used for the left arrow when the component is disabled. */
private Image leftArrowDisabledImage;
/** Image used for the right arrow. */
private Image rightArrowImage;
/** Image used for the right arrow when the component is disabled. */
private Image rightArrowDisabledImage;
/** Show arrow at the side of the slider track. */
private boolean showArrows;
/** Area in which the min arrow will reside. */
private Rectangle minArrowRect;
/** Area in which the max arrow will reside. */
private Rectangle maxArrowRect;
/** Dialog used to display tooltip containing the position of the slider. */
//private TipDialog tipDialog;
/** The end label displayed at top, or left of slider. */
private String endLabel;
/** Show the end label if set. */
private boolean showEndLabel;
/** Show the tip label ovet the thumb when slider moved. */
//private boolean showTipLabel;
/** The rect holding the location of the end label. */
private Rectangle endLabelRect;
/** The height of the end label. */
private int labelHeight;
/** The width of the end label. */
private int labelWidth;
/** This variable is set to <code>true</code> if user dragging thumb. */
protected boolean isDragging;
/** The height of the arrow. */
private int arrowHeight;
/** The width of the arrow. */
private int arrowWidth;
/** The width of the bottom/left arrow. */
private int minArrowWidth;
/** The height of the bottom/left arrow. */
private int minArrowHeight;
/** The width of the thumb. */
private int thumbWidth;
/** The height of the thumb. */
private int thumbHeight;
/** Load the thumb and arrow images. */
private void loadThumbArrowImage()
{
IconManager icons = IconManager.getInstance();
ImageIcon img = icons.getImageIcon(IconManager.THUMB);
thumbWidth = img.getIconWidth();
thumbHeight = img.getIconHeight();
thumbImage = img.getImage();
img = icons.getImageIcon(IconManager.THUMB_DISABLED);
disabledThumbImage = img.getImage();
img = icons.getImageIcon(IconManager.UP_ARROW_DISABLED_10);
arrowWidth = img.getIconWidth();
arrowHeight = img.getIconHeight();
minArrowHeight = arrowHeight;
minArrowWidth = arrowWidth;
upArrowDisabledImage = img.getImage();
img = icons.getImageIcon(IconManager.DOWN_ARROW_DISABLED_10);
downArrowDisabledImage = img.getImage();
img = icons.getImageIcon(IconManager.LEFT_ARROW_DISABLED_10);
leftArrowDisabledImage = img.getImage();
img = icons.getImageIcon(IconManager.RIGHT_ARROW_DISABLED_10);
rightArrowDisabledImage = img.getImage();
img = icons.getImageIcon(IconManager.UP_ARROW_10);
upArrowImage = img.getImage();
img = icons.getImageIcon(IconManager.DOWN_ARROW_10);
downArrowImage = img.getImage();
img = icons.getImageIcon(IconManager.LEFT_ARROW_10);
leftArrowImage = img.getImage();
img = icons.getImageIcon(IconManager.RIGHT_ARROW_10);
rightArrowImage = img.getImage();
}
/**
* This method calculates the size and position of the arrows used displayed
* in the trackRect.
*/
private void calculateArrowRect()
{
if (slider.getOrientation() == JSlider.HORIZONTAL) {
int offsetY = (trackRect.height-minArrowHeight)/2-1;
minArrowRect = new Rectangle(trackRect.x-
(minArrowWidth+thumbWidth/2+ARROW_SPACE),
trackRect.y+offsetY, minArrowWidth,
minArrowHeight);
offsetY = (trackRect.height-arrowHeight)/2-1;
maxArrowRect = new Rectangle(trackRect.x+trackRect.width+
ARROW_SPACE+thumbWidth/2, trackRect.y+offsetY, arrowWidth,
arrowHeight);
} else {
int offsetX = (trackRect.width-arrowWidth)/2;
if (arrowWidth != minArrowWidth) offsetX +=1;
maxArrowRect = new Rectangle(trackRect.x+offsetX, trackRect.y-
(arrowHeight+thumbHeight/2+ARROW_SPACE), arrowWidth,
arrowHeight);
offsetX = (trackRect.width-minArrowWidth)/2;
if (arrowWidth != minArrowWidth) offsetX +=1;
minArrowRect = new Rectangle(trackRect.x+offsetX, trackRect.y+
trackRect.height+ARROW_SPACE+thumbHeight/2, minArrowWidth,
minArrowHeight);
}
}
/**
* This method calculates the size and position of the end label displayed
* in the trackRect.
*/
private void calculateEndLabelRect()
{
if (slider.getOrientation() == JSlider.HORIZONTAL) {
int offsetY = trackRect.height+labelHeight/2+1;
endLabelRect = new Rectangle(0, offsetY, labelWidth, labelHeight);
} else {
int offsetX = trackRect.width/2-labelWidth/2+1;
endLabelRect = new Rectangle(offsetX, trackRect.y-
(minArrowRect.height+labelHeight+TEXT_SPACE),
labelWidth, labelHeight);
}
}
/**
* Paints the vertical track, and arrows if selected, this method is called
* from the {@link #paintTrack(Graphics)} method.
*
* @param g Graphics context.
*/
private void paintVerticalTrack(Graphics2D g)
{
Paint paint = new GradientPaint(trackRect.x+trackRect.width/2-2,
trackRect.y, UIUtilities.TRACK_GRADIENT_START,
trackRect.x+trackRect.width/2+2,
trackRect.y, UIUtilities.TRACK_GRADIENT_END, false);
g.setPaint(paint);
g.fillRoundRect(trackRect.x+trackRect.width/2-2, trackRect.y, 4,
trackRect.height, 4, 4);
g.setPaint(UIUtilities.LINE_COLOR);
if (showArrows) {
if (slider.isEnabled()) {
g.drawImage(downArrowImage, minArrowRect.x, minArrowRect.y,
minArrowRect.width, minArrowRect.height, null);
g.drawImage(upArrowImage, maxArrowRect.x, maxArrowRect.y,
maxArrowRect.width, maxArrowRect.height, null);
} else {
g.drawImage(downArrowDisabledImage, minArrowRect.x,
minArrowRect.y, minArrowRect.width, minArrowRect.height,
null);
g.drawImage(upArrowDisabledImage, maxArrowRect.x,
maxArrowRect.y, maxArrowRect.width, maxArrowRect.height,
null);
}
}
if (showEndLabel && endLabel != null)
g.drawString(endLabel, endLabelRect.x, endLabelRect.y);
}
/**
* Paints the Horizontal track, and arrows if selected, this method is
* called from the {@link #paintTrack(Graphics)} method.
*
* @param g Graphics context.
*/
private void paintHorizontalTrack(Graphics2D g)
{
Paint paint = new GradientPaint(0, trackRect.y+thumbRect.height/2-3,
UIUtilities.TRACK_GRADIENT_START, 0,
trackRect.y+thumbRect.height/2+2,
UIUtilities.TRACK_GRADIENT_END, false);
g.setPaint(paint);
g.fillRoundRect(trackRect.x, trackRect.y+thumbRect.height/2-3,
trackRect.width, 4, 4, 4);
g.setPaint(UIUtilities.LINE_COLOR);
if (showArrows) {
if (slider.isEnabled()) {
g.drawImage(leftArrowImage, minArrowRect.x, minArrowRect.y,
minArrowRect.width, minArrowRect.height, null);
g.drawImage(rightArrowImage, maxArrowRect.x, maxArrowRect.y,
maxArrowRect.width, maxArrowRect.height, null);
} else {
g.drawImage(leftArrowDisabledImage, minArrowRect.x,
minArrowRect.y, minArrowRect.width, minArrowRect.height,
null);
g.drawImage(rightArrowDisabledImage, maxArrowRect.x,
maxArrowRect.y, maxArrowRect.width, maxArrowRect.height,
null);
}
}
if (showEndLabel && endLabel != null)
g.drawString(endLabel, endLabelRect.x, endLabelRect.height);
}
/**
* Creates a new instance.
*
* @param slider parent slider component.
*/
OneKnobSliderUI(OneKnobSlider slider)
{
super(slider);
showArrows = true;
loadThumbArrowImage();
showEndLabel = false;
endLabelRect = new Rectangle();
}
/**
* Sets the end label.
*
* @param endLabel
*/
void setEndLabel(String endLabel)
{
this.endLabel = endLabel;
labelWidth = 6;
labelHeight = 12;
}
/**
* Will show the end tip label which hovers over the thumb if set.
*
* @param show see above.
*/
void setShowTipLabel(boolean show)
{
//showTipLabel = show;
//if (showTipLabel) tipDialog = new TipDialog(endLabel);
}
/**
* Shows the end label if set.
*
* @param show see above.
*/
void setShowEndLabel(boolean show) { showEndLabel = show; }
/**
* Shows or hides the arrows on the track.
*
* @param isShow See above.
*/
void setShowArrows(boolean isShow)
{
showArrows = isShow;
this.calculateGeometry();
}
/**
* Replaces the arrows icons by the specified one.
*
* @param up The icon displayed at the top of the slider if
* vertical, at the right of the slider if horizontal.
* @param down The icon displayed at the bottom of the slider if
* vertical, at the left of the slider if horizontal.
*/
void setArrowsImageIcon(ImageIcon up, ImageIcon down)
{
setArrowsImageIcon(up, down, null, null);
}
/**
* Replaces the arrows icons by the specified one.
*
* @param up The icon displayed at the top of the slider if
* vertical, at the right of the slider if horizontal.
* @param down The icon displayed at the bottom of the slider if
* vertical, at the left of the slider if horizontal.
* @param disabledUp The disabled icon displayed at the top of the slider if
* vertical, at the right of the slider if horizontal.
* @param disabledDown The disabled icon displayed at the bottom of the
* slider if vertical, at the left of the slider if horizontal.
*/
void setArrowsImageIcon(ImageIcon up, ImageIcon down,
ImageIcon disabledUp, ImageIcon disabledDown)
{
if (slider.getOrientation() == JSlider.HORIZONTAL) {
rightArrowImage = up.getImage();
if (disabledUp != null)
rightArrowDisabledImage = disabledUp.getImage();
else rightArrowDisabledImage = up.getImage();
leftArrowImage = down.getImage();
if (disabledDown != null)
leftArrowDisabledImage = disabledDown.getImage();
else leftArrowDisabledImage = down.getImage();
} else {
upArrowImage = up.getImage();
if (disabledUp != null)
upArrowDisabledImage = disabledUp.getImage();
else upArrowDisabledImage = up.getImage();
downArrowImage = down.getImage();
if (disabledDown != null)
downArrowDisabledImage = disabledDown.getImage();
else downArrowDisabledImage = down.getImage();
}
arrowWidth = up.getIconWidth();
arrowHeight = up.getIconHeight();
minArrowWidth = down.getIconWidth();
minArrowHeight = down.getIconHeight();
this.calculateGeometry();
}
/**
* Returns <code>true</code> if the arrows on the track,
* <code>false</code> otherwise.
*
* @return See above.
*/
boolean isShowArrows() { return showArrows; }
/**
* Extends the {@link #calculateTrackBuffer()} to allow the extra space
* required to display the arrows on the track.
* @see BasicSliderUI#calculateTrackBuffer()
*/
protected void calculateTrackBuffer()
{
super.calculateTrackBuffer();
if (showArrows)
if (slider.getOrientation() == JSlider.HORIZONTAL) {
if (arrowWidth > minArrowWidth)
trackBuffer += arrowWidth+ARROW_SPACE;
else
trackBuffer += minArrowWidth+ARROW_SPACE;
} else {
if (arrowHeight > minArrowHeight)
trackBuffer += arrowHeight+ARROW_SPACE;
else
trackBuffer += minArrowHeight+ARROW_SPACE;
}
if (showEndLabel)
if (slider.getOrientation() == JSlider.HORIZONTAL)
trackBuffer += labelWidth+TEXT_SPACE;
else
trackBuffer += labelHeight+TEXT_SPACE;
}
/**
* Overridden to get the size of the thumb.
* @see BasicSliderUI#getThumbSize()
*/
protected Dimension getThumbSize()
{
return new Dimension(thumbWidth, thumbHeight);
}
/**
* Overridden to calculate the size of the thumb rectangle.
* @see BasicSliderUI#calculateThumbSize()
*/
public void calculateThumbSize()
{
this.thumbRect = new Rectangle(0, 0, thumbWidth, thumbHeight);
}
/**
* Overridden to calculate the size of the thumb rectangle.
* @see BasicSliderUI#paintFocus(Graphics g)
*/
public void paintFocus(Graphics g) {}
/**
* Overridden this method will paint the gradient on the slider track
* @see BasicSliderUI#paintTrack(Graphics)
*/
public void paintTrack(Graphics og)
{
if (slider.getOrientation() == JSlider.HORIZONTAL)
paintHorizontalTrack((Graphics2D) og);
else
paintVerticalTrack((Graphics2D) og);
}
/**
* Overridden to paint thumb on slider.
* @see BasicSliderUI#paintThumb(Graphics)
*/
public void paintThumb(Graphics og)
{
Graphics2D g = (Graphics2D) og;
if (slider.isEnabled())
g.drawImage(thumbImage, thumbRect.x, thumbRect.y, thumbRect.width,
thumbRect.height, null);
else
g.drawImage(disabledThumbImage, thumbRect.x, thumbRect.y,
thumbRect.width, thumbRect.height, null);
}
/**
* Overridden to calculate the geometry of the slider, this calls the
* {@link BasicSliderUI#calculateGeometry} and to add extra calculations to
* calculate the <code>ArrowRect</code> if showArrows is <code>true</code>.
* @see BasicSliderUI#calculateGeometry()
*/
public void calculateGeometry()
{
super.calculateGeometry();
if (showArrows) calculateArrowRect();
if (showEndLabel) calculateEndLabelRect();
}
/**
* Overridden to avoid flicking.
* @see BasicSliderUI#paint(Graphics, JComponent)
*/
public void paint(Graphics g, JComponent c)
{
recalculateIfInsetsChanged();
recalculateIfOrientationChanged();
Rectangle clip = g.getClipBounds();
if (!clip.intersects(trackRect) && slider.getPaintTrack())
calculateGeometry();
if (slider.getPaintTrack() && (clip.intersects(trackRect) ||
clip.intersects(minArrowRect) || clip.intersects(maxArrowRect)
|| clip.intersects(endLabelRect)))
paintTrack(g);
if (slider.getPaintTicks() && clip.intersects(tickRect))
paintTicks(g);
if (slider.getPaintLabels() && clip.intersects(labelRect))
paintLabels(g);
if (slider.hasFocus() && clip.intersects(focusRect))
paintFocus(g);
if (clip.intersects(thumbRect))
paintThumb(g);
}
/**
* Assign the new overloaded trackListener to the slider.
*
* @param slider Parent slider.
* @return TrackListner New listener.
* @see BasicSliderUI#createTrackListener(JSlider)
*/
protected TrackListener createTrackListener(JSlider slider)
{
return new TrackListener2();
}
/**
* Overridden TrackListener class, as we wish to extend the functionality
* of the on track click events.
*/
public class TrackListener2
extends TrackListener
{
/**
* Overridden to determine when a drag event ends.
* This method will also determine when the tool tip Dialog should
* stop showing.
*/
public void mouseReleased(MouseEvent event)
{
super.mouseReleased(event);
if (isDragging && slider instanceof OneKnobSlider) {
isDragging = false;
((OneKnobSlider) slider).onMouseReleased();
}
/*
if (showTipLabel && tipDialog != null)
{
if (tipDialog.isVisible()) tipDialog.setVisible(false);
}
slider.repaint();
*/
}
/**
* This method will detect a click on the track or min/max arrows and
* behave accordingly.
* If the user clicks in the track then the thumb is moved to that
* position and the value updated. If the user clicks the arrows then
* the value is incremented or decremented by one depending on which
* slider was clicked.
*
* @param event mouseEvent.
*/
public void mouseClicked(MouseEvent event)
{
// Check to see that the slider is enabled before proceeeding.
if (!slider.isEnabled())
return;
isDragging = true;
// Get mouse x, y positions.
currentMouseX = event.getX();
currentMouseY = event.getY();
int value;
scrollTimer.stop();
if (showArrows)
{
if (minArrowRect.contains(currentMouseX, currentMouseY))
{
value = slider.getValue();
isDragging = false;
if (value > slider.getMinimum())
{
slider.setValue(value-1);
slider.repaint();
}
return;
}
if (maxArrowRect.contains(currentMouseX, currentMouseY))
{
value = slider.getValue();
isDragging = false;
if (value < slider.getMaximum())
{
slider.setValue(value+1);
slider.repaint();
}
return;
}
}
if (trackRect.contains(currentMouseX, currentMouseY))
{
// Depending on the slider orientation lets move the thumb to the
// position clicked by the user.
switch (slider.getOrientation()) {
case JSlider.HORIZONTAL:
value = valueForXPosition(currentMouseX);
slider.setValue(value);
break;
case JSlider.VERTICAL:
value = valueForYPosition(currentMouseY);
slider.setValue(value);
}
}
}
/**
* Overloaded the <code>mousePressed</code> event in the TrackListener.
*/
public void mousePressed(MouseEvent event)
{
// Check to see that the slider is enabled before proceeeding.
if (!slider.isEnabled())
return;
isDragging = true;
// Get mouse x, y positions.
currentMouseX = event.getX();
currentMouseY = event.getY();
// If the slider has {@link #setFocusEnabled} true then
// request focus.
if (slider.isRequestFocusEnabled())
slider.requestFocus();
// Check to see if the thumb was clicked.
if (thumbRect.contains(currentMouseX, currentMouseY))
super.mousePressed(event);
if (showArrows)
{
if (minArrowRect.contains(currentMouseX, currentMouseY))
{
int value = slider.getValue();
isDragging = false;
if (value > slider.getMinimum())
{
//scrollTimer.stop();
scrollListener.setScrollByBlock(false);
scrollListener.setDirection(
OneKnobSliderUI.NEGATIVE_SCROLL);
//scrollTimer.start();
slider.repaint();
}
return;
}
if (maxArrowRect.contains(currentMouseX, currentMouseY))
{
int value = slider.getValue();
isDragging = false;
if (value < slider.getMaximum())
{
//scrollTimer.stop();
scrollListener.setScrollByBlock(false);
scrollListener.setDirection(
OneKnobSliderUI.POSITIVE_SCROLL);
//scrollTimer.start();
slider.repaint();
}
return;
}
}
slider.repaint();
}
/**
* Overridden function of the slider track listener method mouseDragged
* as this method relied on the private member of isDragged in sliderUI.
* Has to override this as we could not set the isDragging variable in
* the basicSliderUI. :-( *Why private??!*
*/
public void mouseDragged(MouseEvent event)
{
super.mouseDragged(event);
isDragging = true;
/*
if (showTipLabel && tipDialog != null && endLabel != null &&
slider.isVisible())
{
Point location = slider.getLocationOnScreen();
location.x += thumbRect.x+TOOLTIP_OFFSET;
location.y += thumbRect.y;
tipDialog.setTipString(
endLabel+" : " + slider.getValue());
tipDialog.setLocation(location);
tipDialog.setVisible(true);
}
*/
}
}
}