Friday, May 28, 2010

Multi knob (thumb) JSlider in Swing


This is multi thumb slider using swing.

As the jSlider provides only the single slider,
This slider contains multi thumb with different color.



/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package CUSTOMCOMPONENT.jslider;

import javax.swing.JFrame;

/**
*
* @author Chetan Menge
* Copyright : USIT
*/
public class MultiSliderDemo {

public static void main(String[] args) {
JFrame frame = new JFrame("Multislider Demo");

MultiSlider multiSlider = new MultiSlider(0, 150000);

multiSlider.setPaintLabels(true);
multiSlider.setPaintTicks(true);
multiSlider.setMajorTickSpacing(15000);


multiSlider.setValueAt(0, 30000);
multiSlider.setValueAt(1, 90000);

frame.add(multiSlider);

frame.setSize(500, 500);
frame.setVisible(true);


}
}


-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package multithumbslider;

/**
*
* @author Chetan Menge
* Copyright : USIT
*/
/* -------------------------------------------------------------------
* GeoVISTA Center (Penn State, Dept. of Geography)
*
* Java source file for the class MultiSlider
*
* Copyright (c), 1999 - 2002, Masahiro Takatsuka and GeoVISTA Center
* All Rights Researved.
*
* Original Author: Masahiro Takatsuka
* $Author: eytanadar $
*
* $Date: 2005/10/05 20:19:52 $
*
*
* Reference: Document no:
* ___ ___
*
* To Do:
* ___
*
------------------------------------------------------------------- */

/* --------------------------- Package ---------------------------- */
import java.awt.*;
import javax.accessibility.*;
import javax.swing.*;
import javax.swing.plaf.*;


/*====================================================================
Implementation of class MultiSlider
====================================================================*/
/***
* A component that lets the user graphically select values by slding
* multiple thumbs within a bounded interval. MultiSlider inherits all
* fields and methods from javax.swing.JSlider.
*

*
* @version $Revision: 1.1 $
* @author Masahiro Takatsuka (masa@jbeans.net)
* @see JSlider
*/
public class MultiSlider extends JSlider {

/***
* @see #getUIClassID
* @see #readObject
*/
private static final String uiClassID = "MultiSliderUI";
/***
* An array of data models that handle the numeric maximum values,
* minimum values, and current-position values for the multi slider.
*/
private BoundedRangeModel[] sliderModels;
/***
* If it is true, a thumb is bounded by adjacent thumbs.
*/
private boolean bounded = false;
/***
* This is a color to paint the current thumb
*/
private Color currentThumbColor = Color.red;
/***
* this flag is used to create default anchors.
*/
transient private boolean useEndPoints = false;
transient private int valueBeforeStateChange;

/***
* Creates a slider with the specified orientation and the
* specified mimimum, maximum, and initial values.
*
* @exception IllegalArgumentException if orientation is not one of VERTICAL, HORIZONTAL
*
* @see #setOrientation
* @see #setMinimum
* @see #setMaximum
* @see #setValue
*/
public MultiSlider(int orientation, int min, int max,
int val1, int val2) {
checkOrientation(orientation);
this.orientation = orientation;
setNumberOfThumbs(min, max, new int[]{val1, val2});
}

/***
* Creates a slider with the specified orientation and the
* specified mimimum, maximum, and the number of thumbs.
*
* @exception IllegalArgumentException if orientation is not one of VERTICAL, HORIZONTAL
*
* @see #setOrientation
* @see #setMinimum
* @see #setMaximum
* @see #setValue
*/
public MultiSlider(int orientation, int min, int max) {
checkOrientation(orientation);
this.orientation = orientation;
setNumberOfThumbs(min, max, 2);
}

/***
* Creates a horizontal slider with the range 0 to 100 and
* an intitial value of 50.
*/
public MultiSlider() {
this(HORIZONTAL, 0, 100);

}

/***
* Creates a slider using the specified orientation with the
* range 0 to 100 and an intitial value of 50.
*/
public MultiSlider(int orientation) {
this(orientation, 0, 100);
}

/***
* Creates a horizontal slider using the specified min and max
* with an intitial value of 50.
*/
public MultiSlider(int min, int max) {
this(HORIZONTAL, min, max);
}

public void setCurrentThumbColor(Color c) {
this.currentThumbColor = c;
}

public Color getCurrentThumbColor() {
return this.currentThumbColor;
}

public int getTrackBuffer() {
return ((MultiSliderUI) this.ui).getTrackBuffer();
}

/***
* Validates the orientation parameter.
*/
private void checkOrientation(int orientation) {
switch (orientation) {
case VERTICAL:
case HORIZONTAL:
break;
default:
throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
}
}

/***
* Notification from the UIFactory that the L&F has changed.
* Called to replace the UI with the latest version from the
* default UIFactory.
*
* @see JComponent#updateUI
*/
public void updateUI() {
updateLabelUIs();
MultiSliderUI ui = new MultiSliderUI();
if (this.sliderModels != null) {
ui.setThumbCount(this.sliderModels.length);
}
setUI((SliderUI) ui);
}

/***
* Returns the number of thumbs in the slider.
*/
public int getNumberOfThumbs() {
return this.sliderModels.length;
}

/***
* Sets the number of thumbs with the specified parameters.
*/
private void setNumberOfThumbs(int min, int max, int num, boolean useEndPoints) {
int[] values = createDefaultValues(min, max, num, useEndPoints);
setNumberOfThumbs(min, max, values);
}

/***
* Sets the number of thumbs with the specified parameters.
*/
private void setNumberOfThumbs(int min, int max, int num) {
setNumberOfThumbs(min, max, num, false);
}

/***
* Sets the number of thumbs with the specified parameters.
*/
private void setNumberOfThumbs(int min, int max, int[] values) {
if (values == null || values.length < 1) { values = new int[]{50}; } int num = values.length; this.sliderModels = new BoundedRangeModel[num]; for (int i = 0; i < num; i++) { this.sliderModels[i] = new DefaultBoundedRangeModel(values[i], 0, min, max); this.sliderModels[i].addChangeListener(changeListener); } updateUI(); } /*** * Sets the number of thumbs. */ private void setNumberOfThumbs(int num) { setNumberOfThumbs(num, false); } /*** * Sets the number of thumbs. */ private void setNumberOfThumbs(int num, boolean useEndPoints) { if (getNumberOfThumbs() != num) { setNumberOfThumbs(getMinimum(), getMaximum(), num, useEndPoints); } } /*** * Sets the number of thumbs by specifying the initial values. */ private void setNumberOfThumbs(int[] values) { setNumberOfThumbs(getMinimum(), getMaximum(), values); } /*** * creates evenly spaced values for thumbs. */ private int[] createDefaultValues(int min, int max, int num_of_values, boolean useEndPoints) { int[] values = new int[num_of_values]; int range = max - min; if (!useEndPoints) { int step = range / (num_of_values + 1); for (int i = 0; i < num_of_values; i++) { values[i] = min + (i + 1) * step; } } else { if (num_of_values < 1) { return new int[0]; } values[0] = getMinimum(); values[num_of_values - 1] = getMaximum(); int[] def = createDefaultValues(getMinimum(), getMaximum(), num_of_values - 2, false); for (int i = 0; i < def.length; i++) { values[i + 1] = def[i]; } } return values; } /*** * Returns the index number of currently operated thumb. */ public int getCurrentThumbIndex() { return ((MultiSliderUI) ui).getCurrentIndex(); } /*** * Returns data model that handles the sliders three * fundamental properties: minimum, maximum, value. * * @see #setModel */ public BoundedRangeModel getModel() { return getModelAt(getCurrentThumbIndex()); } /*** * Returns data model that handles the sliders three * fundamental properties: minimum, maximum, value. * * @see #setModel */ public BoundedRangeModel getModelAt(int index) { if (this.sliderModels == null || index >= this.sliderModels.length) {
return null;
}
return this.sliderModels[index];
}

/***
* Returns data model that handles the sliders three
* fundamental properties: minimum, maximum, value.
*
* @see #setModel
*/
public BoundedRangeModel[] getModels() {
return this.sliderModels;
}

/***
* Sets the model that handles the sliders three
* fundamental properties: minimum, maximum, value.
*
* @see #getModel
* @beaninfo
* bound: true
* description: The sliders BoundedRangeModel.
*/
public void setModel(BoundedRangeModel newModel) {
setModelAt(getCurrentThumbIndex(), newModel);
}

/***
* Sets the model that handles the sliders three
* fundamental properties: minimum, maximum, value.
*
* @see #getModel
* @beaninfo
* bound: true
* description: The sliders BoundedRangeModel.
*/
public void setModelAt(int index, BoundedRangeModel newModel) {
BoundedRangeModel oldModel = getModelAt(index);

if (oldModel != null) {
oldModel.removeChangeListener(changeListener);
}

this.sliderModels[index] = newModel;

if (newModel != null) {
newModel.addChangeListener(changeListener);

if (accessibleContext != null) {
accessibleContext.firePropertyChange(
AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
(oldModel == null
? null : new Integer(oldModel.getValue())),
(newModel == null
? null : new Integer(newModel.getValue())));
}
}

firePropertyChange("model", oldModel, this.sliderModels[index]);
}

/***
* Sets the models minimum property.
*
* @see #getMinimum
* @see BoundedRangeModel#setMinimum
* @beaninfo
* bound: true
* preferred: true
* description: The sliders minimum value.
*/
public void setMinimum(int minimum) {
int count = getNumberOfThumbs();
int oldMin = getModel().getMinimum();
for (int i = 0; i < count; i++) { getModelAt(i).setMinimum(minimum); } firePropertyChange("minimum", new Integer(oldMin), new Integer(minimum)); } /*** * Sets the models maximum property. * * @see #getMaximum * @see BoundedRangeModel#setMaximum * @beaninfo * bound: true * preferred: true * description: The sliders maximum value. */ public void setMaximum(int maximum) { int count = getNumberOfThumbs(); int oldMax = getModel().getMaximum(); for (int i = 0; i < count; i++) { getModelAt(i).setMaximum(maximum); } firePropertyChange("maximum", new Integer(oldMax), new Integer(maximum)); } /*** * Returns the sliders value. * @return the models value property * @see #setValue */ public int getValue() { return getValueAt(getCurrentThumbIndex()); } /*** * Returns the sliders value. * @return the models value property * @see #setValue */ public int getValueAt(int index) { return getModelAt(index).getValue(); } /*** * Sets the sliders current value. This method just forwards * the value to the model. * * @see #getValue * @beaninfo * preferred: true * description: The sliders current value. */ public void setValue(int n) { setValueAt(getCurrentThumbIndex(), n); } /*** * Sets the sliders current value. This method just forwards * the value to the model. * * @see #getValue * @beaninfo * preferred: true * description: The sliders current value. */ public void setValueAt(int index, int n) { BoundedRangeModel m = getModelAt(index); int oldValue = m.getValue(); m.setValue(n); if (accessibleContext != null) { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, new Integer(oldValue), new Integer(m.getValue())); } } /*** * True if the slider knob is being dragged. * * @return the value of the models valueIsAdjusting property * @see #setValueIsAdjusting */ public boolean getValueIsAdjusting() { boolean result = false; int count = getNumberOfThumbs(); for (int i = 0; i < count; i++) { result = (result || getValueIsAdjustingAt(i)); } return result; } /*** * True if the slider knob is being dragged. */ public boolean getValueIsAdjustingAt(int index) { return getModelAt(index).getValueIsAdjusting(); } /*** * Sets the models valueIsAdjusting property. Slider look and * feel implementations should set this property to true when * a knob drag begins, and to false when the drag ends. The * slider model will not generate ChangeEvents while * valueIsAdjusting is true. * * @see #getValueIsAdjusting * @see BoundedRangeModel#setValueIsAdjusting * @beaninfo * expert: true * description: True if the slider knob is being dragged. */ public void setValueIsAdjusting(boolean b) { setValueIsAdjustingAt(getCurrentThumbIndex(), b); } /*** * Sets the models valueIsAdjusting property. Slider look and * feel implementations should set this property to true when * a knob drag begins, and to false when the drag ends. The * slider model will not generate ChangeEvents while * valueIsAdjusting is true. */ public void setValueIsAdjustingAt(int index, boolean b) { BoundedRangeModel m = getModelAt(index); boolean oldValue = m.getValueIsAdjusting(); m.setValueIsAdjusting(b); if ((oldValue != b) && (accessibleContext != null)) { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, ((oldValue) ? AccessibleState.BUSY : null), ((b) ? AccessibleState.BUSY : null)); } } /*** * Sets the size of the range "covered" by the knob. Most look * and feel implementations will change the value by this amount * if the user clicks on either side of the knob. * * @see #getExtent * @see BoundedRangeModel#setExtent * @beaninfo * expert: true * description: Size of the range covered by the knob. */ public void setExtent(int extent) { int count = getNumberOfThumbs(); for (int i = 0; i < count; i++) { getModelAt(i).setExtent(extent); } } /*** * Sets a bounded attribute of a slider thumb. *


*
*
* @param b
* @return void
*/
public void setBounded(boolean b) {
this.bounded = b;
}

/***
* Returns a bounded attribute of a slider thumb.
*
     * 
*
* @return boolean
*/
public boolean isBounded() {
return this.bounded;
}

public int getValueBeforeStateChange() {
return this.valueBeforeStateChange;
}

void setValueBeforeStateChange(int v) {
this.valueBeforeStateChange = v;
}

public void drawLine(Graphics g, Point orgn, Point dest,Color c,int sub) {
g.setColor(c);
g.drawLine((int) 21, (int) orgn.getY()-sub, (int) dest.getX(), (int) dest.getY()-sub);
}


}

----------------------------------------------------------------------
----------------------------------------------------------------------
----------------------------------------------------------------------

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package multithumbslider;

/**
*
* @author Chetan Menge
* Copyright : USIT
*/
/* -------------------------------------------------------------------
* GeoVISTA Center (Penn State, Dept. of Geography)
*
* Java source file for the class MultiSliderUI
*
* Copyright (c), 1999 - 2002, Masahiro Takatsuka and GeoVISTA Center
* All Rights Researved.
*
* Original Author: Masahiro Takatsuka
* $Author: eytanadar $
*
* $Date: 2005/10/05 20:19:52 $
*
*
* Reference: Document no:
* ___ ___
*
* To Do:
* ___
*
------------------------------------------------------------------- */

/* --------------------------- Package ---------------------------- */
import java.awt.*;
import java.awt.event.*;
import java.net.URL;

import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.basic.BasicSliderUI.*;
import javax.swing.plaf.metal.*;

/*====================================================================
Implementation of class MultiSliderUI
====================================================================*/
/***
* A Basic L&F implementation of SliderUI.
*
* @version $Revision: 1.1 $
* @author Masahiro Takatsuka (masa@jbeans.net)
* @see MetalSliderUI
*/
class MultiSliderUI extends BasicSliderUI {

private Rectangle[] thumbRects = null;
private int thumbCount;
transient private int currentIndex = 0;
transient private boolean isDragging;
transient private int[] minmaxIndices = new int[2];
protected boolean filledSlider = true;
private Color firstThumbColor = new Color(0, 102, 0);
private Color secondThumbColor = new Color(223, 1, 1);

/***
* ComponentUI Interface Implementation methods
*/
public static ComponentUI createUI(JComponent b) {
return new MultiSliderUI();
}

/***
* Construct a new MultiSliderUI object.
*/
public MultiSliderUI() {
super(null);
filledSlider = true;
}

int getTrackBuffer() {
return this.trackBuffer;
}

public Rectangle[] getThumbRects() {
return thumbRects;
}

/***
* Sets the number of Thumbs.
*/
public void setThumbCount(int count) {
this.thumbCount = count;
}

/***
* Returns the index number of the thumb currently operated.
*/
protected int getCurrentIndex() {
return this.currentIndex;
}

@Override
public void installUI(JComponent c) {
this.thumbRects = new Rectangle[this.thumbCount];
for (int i = 0; i < this.thumbCount; i++) { this.thumbRects[i] = new Rectangle(); } this.currentIndex = 0; if (this.thumbCount > 0) {
thumbRect = this.thumbRects[this.currentIndex];
}
super.installUI(c);
}

public void uninstallUI(JComponent c) {
super.uninstallUI(c);
for (int i = 0; i < this.thumbCount; i++) { this.thumbRects[i] = null; } this.thumbRects = null; } protected void installListeners(JSlider slider) { // slider.addMouseListener(trackListener); // slider.addMouseMotionListener(trackListener); slider.addFocusListener(focusListener); slider.addComponentListener(componentListener); slider.addPropertyChangeListener(propertyChangeListener); for (int i = 0; i < this.thumbCount; i++) { ((MultiSlider) slider).getModelAt(i).addChangeListener(changeListener); } } protected void uninstallListeners(JSlider slider) { slider.removeMouseListener(trackListener); slider.removeMouseMotionListener(trackListener); slider.removeFocusListener(focusListener); slider.removeComponentListener(componentListener); slider.removePropertyChangeListener(propertyChangeListener); for (int i = 0; i < this.thumbCount; i++) { BoundedRangeModel model = ((MultiSlider) slider).getModelAt(i); if (model != null) { model.removeChangeListener(changeListener); } } } @Override protected void calculateThumbSize() { Dimension size = new Dimension(10, 15);//getThumbSize(); for (int i = 0; i < this.thumbCount; i++) { this.thumbRects[i].setSize(size.width, size.height); } thumbRect.setSize(size.width, size.height); } protected void calculateThumbLocation() { MultiSlider slider = (MultiSlider) this.slider; int majorTickSpacing = slider.getMajorTickSpacing(); int minorTickSpacing = slider.getMinorTickSpacing(); int tickSpacing = 0; if (minorTickSpacing > 0) {
tickSpacing = minorTickSpacing;
} else if (majorTickSpacing > 0) {
tickSpacing = majorTickSpacing;
}
for (int i = 0; i < this.thumbCount; i++) { if (slider.getSnapToTicks()) { int sliderValue = slider.getValueAt(i); int snappedValue = sliderValue; if (tickSpacing != 0) { // If it's not on a tick, change the value if ((sliderValue - slider.getMinimum()) % tickSpacing != 0) { float temp = (float) (sliderValue - slider.getMinimum()) / (float) tickSpacing; int whichTick = Math.round(temp); snappedValue = slider.getMinimum() + (whichTick * tickSpacing); } if (snappedValue != sliderValue) { slider.setValueAt(i, snappedValue); } } } if (slider.getOrientation() == JSlider.HORIZONTAL) { int valuePosition = xPositionForValue(slider.getValueAt(i)); this.thumbRects[i].x = valuePosition - (this.thumbRects[i].width / 2); this.thumbRects[i].y = trackRect.y; } else { int valuePosition = yPositionForValue(slider.getValueAt(i)); this.thumbRects[i].x = trackRect.x; this.thumbRects[i].y = valuePosition - (this.thumbRects[i].height / 2); } } } public void paint(Graphics g, JComponent c) { // super.paint(g, c); recalculateIfInsetsChanged(); recalculateIfOrientationChanged(); Rectangle clip = g.getClipBounds(); if (slider.getPaintTrack() && clip.intersects(trackRect)) { 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); } // first paint unfocused thumbs. for (int i = 0; i < this.thumbCount; i++) { if (i != this.currentIndex) { if (clip.intersects(this.thumbRects[i])) { thumbRect = this.thumbRects[i]; paintThumb(g,secondThumbColor); } } } // then paint currently focused thumb. if (clip.intersects(this.thumbRects[this.currentIndex])) { thumbRect = this.thumbRects[this.currentIndex]; // thumbRect.setSize(5, 5); paintThumb(g,firstThumbColor); } } public void paintThumb(Graphics g,Color thumbColor) { // super.paintThumb(g); Color highlightColor = UIManager.getColor("Slider.highlight"); Color shadowColor = UIManager.getColor("Slider.shadow"); Rectangle knobBounds = thumbRect; int w = knobBounds.width - 4; int h = knobBounds.height + 1; g.translate(knobBounds.x, knobBounds.y - 21); if (slider.isEnabled()) { g.setColor(slider.getBackground()); } else { g.setColor(slider.getBackground().darker()); } Boolean paintThumbArrowShape = (Boolean) slider.getClientProperty("Slider.paintThumbArrowShape"); if ((!slider.getPaintTicks() && paintThumbArrowShape == null) || paintThumbArrowShape == Boolean.FALSE) { // "plain" version g.fillRect(0, 0, w, h); g.setColor(Color.black); g.drawLine(0, h - 1, w - 1, h - 1); g.drawLine(w - 1, 0, w - 1, h - 1); g.setColor(highlightColor); g.drawLine(0, 0, 0, h - 2); g.drawLine(1, 0, w - 2, 0); g.setColor(shadowColor); g.drawLine(1, h - 2, w - 2, h - 2); g.drawLine(w - 2, 1, w - 2, h - 3); } else if (slider.getOrientation() == JSlider.HORIZONTAL) { int cw = w / 2; // g.setColor(Color.blue); /// thumb color g.setColor(thumbColor); /// thumb color g.fillRect(1, 1, w - 3, h - 1 - cw); Polygon p = new Polygon(); p.addPoint(1, h - cw); p.addPoint(cw - 1, h - 1); p.addPoint(w - 2, h - 1 - cw); g.fillPolygon(p); g.setColor(highlightColor); g.drawLine(0, 0, w - 2, 0); g.drawLine(0, 1, 0, h - 1 - cw); g.drawLine(0, h - cw, cw - 1, h - 1); g.setColor(Color.black); g.drawLine(w - 1, 0, w - 1, h - 2 - cw); g.drawLine(w - 1, h - 1 - cw, w - 1 - cw, h - 1); g.setColor(shadowColor); g.drawLine(w - 2, 1, w - 2, h - 2 - cw); g.drawLine(w - 2, h - 1 - cw, w - 1 - cw, h - 2); } g.translate(-knobBounds.x, -knobBounds.y + 21); /* Icon SAFE_HORIZ_THUMB_ICON = null; URL imgURL = getClass().getResource("sliderIconBlue.jpg"); // System.out.println("the img url --" + imgURL); if (imgURL != null) { SAFE_HORIZ_THUMB_ICON = new ImageIcon(imgURL, ""); } // SAFE_HORIZ_THUMB_ICON= UIManager.getIcon("Slider.horizontalThumbIcon"); Rectangle knobBounds = thumbRect; g.translate(knobBounds.x, knobBounds.y); if (slider.getOrientation() == JSlider.HORIZONTAL) { SAFE_HORIZ_THUMB_ICON.paintIcon(slider, g, 0, -25); //SAFE_HORIZ_THUMB_ICON. } else { SAFE_HORIZ_THUMB_ICON.paintIcon(slider, g, 0, 0); } g.translate(-knobBounds.x, -knobBounds.y);*/ } public void paintTrack(Graphics g) { //super.paintTrack(g); Color trackColor = !slider.isEnabled() ? MetalLookAndFeel.getControlShadow() : slider.getForeground(); boolean leftToRight = true;//MetalUtils.isLeftToRight(slider); g.translate(trackRect.x, trackRect.y + 10); int trackLeft = 0; int trackTop = 0; int trackRight = 0; int trackBottom = 0; // Draw the track if (slider.getOrientation() == JSlider.HORIZONTAL) { trackBottom = (trackRect.height - 1) - getThumbOverhang(); trackTop = trackBottom - (getTrackWidth() - 1); trackRight = trackRect.width - 1; } else { if (leftToRight) { trackLeft = (trackRect.width - getThumbOverhang()) - getTrackWidth(); trackRight = (trackRect.width - getThumbOverhang()) - 1; } else { trackLeft = getThumbOverhang(); trackRight = getThumbOverhang() + getTrackWidth() - 1; } trackBottom = trackRect.height - 1; } if (slider.isEnabled()) { g.setColor(MetalLookAndFeel.getControlDarkShadow()); g.drawRect(trackLeft, trackTop, (trackRight - trackLeft) - 1, (trackBottom - trackTop) - 1); g.setColor(MetalLookAndFeel.getControlHighlight()); g.drawLine(trackLeft + 1, trackBottom, trackRight, trackBottom); g.drawLine(trackRight, trackTop + 1, trackRight, trackBottom); g.setColor(MetalLookAndFeel.getControlShadow()); g.drawLine(trackLeft + 1, trackTop + 1, trackRight - 2, trackTop + 1); g.drawLine(trackLeft + 1, trackTop + 1, trackLeft + 1, trackBottom - 2); g.setColor(new Color(0, 128, 0)); g.drawLine(trackLeft, trackTop - 3, (int) this.thumbRects[0].getX() - 16, trackTop - 3); g.setColor(Color.red); g.drawLine(trackLeft, trackTop - 6, (int) this.thumbRects[1].getX() - 16, trackTop - 6); g.setColor(MetalLookAndFeel.getControlShadow()); } else { g.setColor(MetalLookAndFeel.getControlShadow()); g.drawRect(trackLeft, trackTop, (trackRight - trackLeft) - 1, (trackBottom - trackTop) - 1); } // Draw the fill if (filledSlider) { int middleOfThumb = 0; int fillTop = 0; int fillLeft = 0; int fillBottom = 0; int fillRight = 0; if (slider.getOrientation() == JSlider.HORIZONTAL) { middleOfThumb = thumbRect.x + (thumbRect.width / 2); middleOfThumb -= trackRect.x; // To compensate for the g.translate() fillTop = !slider.isEnabled() ? trackTop : trackTop + 1; fillBottom = !slider.isEnabled() ? trackBottom - 1 : trackBottom - 2; if (!drawInverted()) { fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1; fillRight = middleOfThumb; } else { fillLeft = middleOfThumb; fillRight = !slider.isEnabled() ? trackRight - 1 : trackRight - 2; } } else { middleOfThumb = thumbRect.y + (thumbRect.height / 2); middleOfThumb -= trackRect.y; // To compensate for the g.translate() fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1; fillRight = !slider.isEnabled() ? trackRight - 1 : trackRight - 2; if (!drawInverted()) { fillTop = middleOfThumb; fillBottom = !slider.isEnabled() ? trackBottom - 1 : trackBottom - 2; } else { fillTop = !slider.isEnabled() ? trackTop : trackTop + 1; fillBottom = middleOfThumb; } } if (slider.isEnabled()) { // g.setColor( Color.RED); g.drawLine(fillLeft, fillTop, fillRight, fillTop); g.drawLine(fillLeft, fillTop, fillLeft, fillBottom); //g.setColor( Color.green ); g.fillRect(fillLeft + 1, fillTop + 1, fillRight - fillLeft, fillBottom - fillTop); } else { g.setColor(MetalLookAndFeel.getControlShadow()); g.fillRect(fillLeft, fillTop, fillRight - fillLeft, trackBottom - trackTop); } } g.translate(-trackRect.x, -trackRect.y); } public void scrollByBlock(int direction) { synchronized (slider) { int oldValue = ((MultiSlider) slider).getValueAt(this.currentIndex); int blockIncrement = slider.getMaximum() / 10; int delta = blockIncrement * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
((MultiSlider) slider).setValueAt(this.currentIndex, oldValue + delta);
}
}

public void scrollByUnit(int direction) {
synchronized (slider) {
int oldValue = ((MultiSlider) slider).getValueAt(this.currentIndex);
int delta = 1 * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
((MultiSlider) slider).setValueAt(this.currentIndex, oldValue + delta);
}
}

protected TrackListener createTrackListener(JSlider slider) {
return new MultiTrackListener();
}

/***
* Track Listener Class tracks mouse movements.
*/
class MultiTrackListener extends BasicSliderUI.TrackListener {

int _trackTop;
int _trackBottom;
int _trackLeft;
int _trackRight;
transient private int[] firstXY = new int[2];

/***
* If the mouse is pressed above the "thumb" component
* then reduce the scrollbars value by one page ("page up"),
* otherwise increase it by one page. If there is no
* thumb then page up if the mouse is in the upper half
* of the track.
*/
public void mousePressed(MouseEvent e) {
int[] neighbours = new int[2];
boolean bounded = ((MultiSlider) slider).isBounded();
if (!slider.isEnabled()) {
return;
}

currentMouseX = e.getX();
currentMouseY = e.getY();
firstXY[0] = currentMouseX;
firstXY[1] = currentMouseY;

slider.requestFocus();
// Clicked in the Thumb area?
minmaxIndices[0] = -1;
minmaxIndices[1] = -1;
for (int i = 0; i < MultiSliderUI.this.thumbCount; i++) { if (MultiSliderUI.this.thumbRects[i].contains(currentMouseX, currentMouseY)) { if (minmaxIndices[0] == -1) { minmaxIndices[0] = i; MultiSliderUI.this.currentIndex = i; } if (minmaxIndices[1] < i) { minmaxIndices[1] = i; } switch (slider.getOrientation()) { case JSlider.VERTICAL: offset = currentMouseY - MultiSliderUI.this.thumbRects[i].y; break; case JSlider.HORIZONTAL: offset = currentMouseX - MultiSliderUI.this.thumbRects[i].x; break; } MultiSliderUI.this.isDragging = true; thumbRect = MultiSliderUI.this.thumbRects[i]; if (bounded) { neighbours[0] = ((i - 1) < 0) ? -1 : (i - 1); neighbours[1] = ((i + 1) >= MultiSliderUI.this.thumbCount) ? -1 : (i + 1);
//findClosest(currentMouseX, currentMouseY, neighbours, i);
} else {
MultiSliderUI.this.currentIndex = i;
((MultiSlider) slider).setValueIsAdjustingAt(i, true);
neighbours[0] = -1;
neighbours[1] = -1;
}
setThumbBounds(neighbours);
//return;
}
}
if (minmaxIndices[0] > -1) {
return;
}

MultiSliderUI.this.currentIndex = findClosest(currentMouseX, currentMouseY, neighbours, -1);
thumbRect = MultiSliderUI.this.thumbRects[MultiSliderUI.this.currentIndex];
MultiSliderUI.this.isDragging = false;
((MultiSlider) slider).setValueIsAdjustingAt(MultiSliderUI.this.currentIndex, true);

Dimension sbSize = slider.getSize();
int direction = POSITIVE_SCROLL;

switch (slider.getOrientation()) {
case JSlider.VERTICAL:
if (thumbRect.isEmpty()) {
int scrollbarCenter = sbSize.height / 2;
if (!drawInverted()) {
direction = (currentMouseY < scrollbarCenter) ? POSITIVE_SCROLL : NEGATIVE_SCROLL; } else { direction = (currentMouseY < scrollbarCenter) ? NEGATIVE_SCROLL : POSITIVE_SCROLL; } } else { int thumbY = thumbRect.y; if (!drawInverted()) { direction = (currentMouseY < thumbY) ? POSITIVE_SCROLL : NEGATIVE_SCROLL; } else { direction = (currentMouseY < thumbY) ? NEGATIVE_SCROLL : POSITIVE_SCROLL; } } break; case JSlider.HORIZONTAL: if (thumbRect.isEmpty()) { int scrollbarCenter = sbSize.width / 2; if (!drawInverted()) { direction = (currentMouseX < scrollbarCenter) ? NEGATIVE_SCROLL : POSITIVE_SCROLL; } else { direction = (currentMouseX < scrollbarCenter) ? POSITIVE_SCROLL : NEGATIVE_SCROLL; } } else { int thumbX = thumbRect.x; if (!drawInverted()) { direction = (currentMouseX < thumbX) ? NEGATIVE_SCROLL : POSITIVE_SCROLL; } else { direction = (currentMouseX < thumbX) ? POSITIVE_SCROLL : NEGATIVE_SCROLL; } } break; } scrollDueToClickInTrack(direction); Rectangle r = thumbRect; if (!r.contains(currentMouseX, currentMouseY)) { if (shouldScroll(direction)) { scrollTimer.stop(); scrollListener.setDirection(direction); scrollTimer.start(); } } } /*** * Sets a track bound for th thumb currently operated. */ private void setThumbBounds(int[] neighbours) { int halfThumbWidth = thumbRect.width / 2; int halfThumbHeight = thumbRect.height / 2; switch (slider.getOrientation()) { case JSlider.VERTICAL: _trackTop = (neighbours[1] == -1) ? trackRect.y : MultiSliderUI.this.thumbRects[neighbours[1]].y + halfThumbHeight; _trackBottom = (neighbours[0] == -1) ? trackRect.y + (trackRect.height - 1) : MultiSliderUI.this.thumbRects[neighbours[0]].y + halfThumbHeight; break; case JSlider.HORIZONTAL: _trackLeft = (neighbours[0] == -1) ? trackRect.x : MultiSliderUI.this.thumbRects[neighbours[0]].x + halfThumbWidth; _trackRight = (neighbours[1] == -1) ? trackRect.x + (trackRect.width - 1) : MultiSliderUI.this.thumbRects[neighbours[1]].x + halfThumbWidth; break; } } /* * this is a very lazy way to find the closest. One might want to * implement a much faster algorithm. */ private int findClosest(int x, int y, int[] neighbours, int excluded) { int orientation = slider.getOrientation(); int rightmin = Integer.MAX_VALUE; // for dxw, dy int leftmin = -Integer.MAX_VALUE; // for dx, dyh int dx = 0; int dxw = 0; int dy = 0; int dyh = 0; neighbours[0] = -1; // left neighbours[1] = -1; // right for (int i = 0; i < MultiSliderUI.this.thumbCount; i++) { if (i == excluded) { continue; } switch (orientation) { case JSlider.VERTICAL: dy = MultiSliderUI.this.thumbRects[i].y - y; dyh = (MultiSliderUI.this.thumbRects[i].y + MultiSliderUI.this.thumbRects[i].height) - y; if (dyh <= 0) { if (dyh > leftmin) { // has to be > and not >=
leftmin = dyh;
neighbours[0] = i;
}
}
if (dy >= 0) {
if (dy <= rightmin) { rightmin = dy; neighbours[1] = i; } } break; case JSlider.HORIZONTAL: dx = MultiSliderUI.this.thumbRects[i].x - x; dxw = (MultiSliderUI.this.thumbRects[i].x + MultiSliderUI.this.thumbRects[i].width) - x; if (dxw <= 0) { if (dxw >= leftmin) {
leftmin = dxw;
neighbours[0] = i;
}
}
if (dx >= 0) {
if (dx < rightmin) { // has to be < and not <= rightmin = dx; neighbours[1] = i; } } break; } } //System.out.println("neighbours = " + neighbours[0] + ", " + neighbours[1]); int closest = (Math.abs(leftmin) <= Math.abs(rightmin)) ? neighbours[0] : neighbours[1]; return (closest == -1) ? 0 : closest; } /*** * Set the models value to the position of the top/left * of the thumb relative to the origin of the track. */ public void mouseDragged(MouseEvent e) { ((MultiSlider) MultiSliderUI.this.slider).setValueBeforeStateChange(((MultiSlider) MultiSliderUI.this.slider).getValueAt(MultiSliderUI.this.currentIndex)); int thumbMiddle = 0; boolean bounded = ((MultiSlider) slider).isBounded(); if (!slider.isEnabled()) { return; } currentMouseX = e.getX(); currentMouseY = e.getY(); if (!MultiSliderUI.this.isDragging) { return; } switch (slider.getOrientation()) { case JSlider.VERTICAL: int halfThumbHeight = thumbRect.height / 2; int thumbTop = e.getY() - offset; if (bounded) { int[] neighbours = new int[2]; int idx = -1; int diff = e.getY() - firstXY[1]; //System.out.println("diff = " + diff); if (e.getY() - firstXY[1] > 0) {
idx = minmaxIndices[0];
} else {
idx = minmaxIndices[1];
}
minmaxIndices[0] = minmaxIndices[1] = idx;
//System.out.println("idx = " + idx);
if (idx == -1) {
break;
}

//System.out.println("thumbTop = " + thumbTop);
neighbours[0] = ((idx - 1) < 0) ? -1 : (idx - 1); neighbours[1] = ((idx + 1) >= MultiSliderUI.this.thumbCount) ? -1 : (idx + 1);
thumbRect = MultiSliderUI.this.thumbRects[idx];
MultiSliderUI.this.currentIndex = idx;
((MultiSlider) slider).setValueIsAdjustingAt(idx, true);
setThumbBounds(neighbours);
}

thumbTop = Math.max(thumbTop, _trackTop - halfThumbHeight);
thumbTop = Math.min(thumbTop, _trackBottom - halfThumbHeight);

setThumbLocation(thumbRect.x, thumbTop);

thumbMiddle = thumbTop + halfThumbHeight;
((MultiSlider) slider).setValueAt(MultiSliderUI.this.currentIndex, valueForYPosition(thumbMiddle));
break;
case JSlider.HORIZONTAL:
int halfThumbWidth = thumbRect.width / 2;
int thumbLeft = e.getX() - offset;
if (bounded) {
int[] neighbours = new int[2];
int idx = -1;
if (e.getX() - firstXY[0] <= 0) { idx = minmaxIndices[0]; } else { idx = minmaxIndices[1]; } minmaxIndices[0] = minmaxIndices[1] = idx; //System.out.println("idx = " + idx); if (idx == -1) { break; } //System.out.println("thumbLeft = " + thumbLeft); neighbours[0] = ((idx - 1) < 0) ? -1 : (idx - 1); neighbours[1] = ((idx + 1) >= MultiSliderUI.this.thumbCount) ? -1 : (idx + 1);
thumbRect = MultiSliderUI.this.thumbRects[idx];
MultiSliderUI.this.currentIndex = idx;
((MultiSlider) slider).setValueIsAdjustingAt(idx, true);
setThumbBounds(neighbours);
}

thumbLeft = Math.max(thumbLeft, _trackLeft - halfThumbWidth);
thumbLeft = Math.min(thumbLeft, _trackRight - halfThumbWidth);

setThumbLocation(thumbLeft, thumbRect.y);

thumbMiddle = thumbLeft + halfThumbWidth;

((MultiSlider) slider).setValueAt(MultiSliderUI.this.currentIndex, valueForXPosition(thumbMiddle));
break;
default:
return;
}
}

public void mouseReleased(MouseEvent e) {
if (!slider.isEnabled()) {
return;
}

offset = 0;
scrollTimer.stop();

if (slider.getSnapToTicks()) {
MultiSliderUI.this.isDragging = false;
((MultiSlider) slider).setValueIsAdjustingAt(MultiSliderUI.this.currentIndex, false);
} else {
((MultiSlider) slider).setValueIsAdjustingAt(MultiSliderUI.this.currentIndex, false);
MultiSliderUI.this.isDragging = false;
}

slider.repaint();
}
}

/***
* A static version of the above.
*/
static class SharedActionScroller extends AbstractAction {

int _dir;
boolean _block;

public SharedActionScroller(int dir, boolean block) {
_dir = dir;
_block = block;
}

public void actionPerformed(ActionEvent e) {
JSlider slider = (JSlider) e.getSource();
MultiSliderUI ui = (MultiSliderUI) slider.getUI();
if (_dir == NEGATIVE_SCROLL || _dir == POSITIVE_SCROLL) {
int realDir = _dir;
if (slider.getInverted()) {
realDir = _dir == NEGATIVE_SCROLL ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
}
if (_block) {
ui.scrollByBlock(realDir);
} else {
ui.scrollByUnit(realDir);
}
} else {
if (slider.getInverted()) {
if (_dir == MIN_SCROLL) {
((MultiSlider) slider).setValueAt(ui.currentIndex,
slider.getMaximum());
} else if (_dir == MAX_SCROLL) {
((MultiSlider) slider).setValueAt(ui.currentIndex,
slider.getMinimum());
}
} else {
if (_dir == MIN_SCROLL) {
((MultiSlider) slider).setValueAt(ui.currentIndex,
slider.getMinimum());
} else if (_dir == MAX_SCROLL) {
((MultiSlider) slider).setValueAt(ui.currentIndex,
slider.getMaximum());
}
}
}
}
}

/**
* Returns the shorter dimension of the track.
*/
protected int getTrackWidth() {
// This strange calculation is here to keep the
// track in proportion to the thumb.
final double kIdealTrackWidth = 7.0;
final double kIdealThumbHeight = 16.0;
final double kWidthScalar = kIdealTrackWidth / kIdealThumbHeight;

if (slider.getOrientation() == JSlider.HORIZONTAL) {
return (int) (kWidthScalar * thumbRect.height);
} else {
return (int) (kWidthScalar * thumbRect.width);
}
}

/**
* Returns the amount that the thumb goes past the slide bar.
*/
protected int getThumbOverhang() {
return (int) (getThumbSize().getHeight() - getTrackWidth()) / 2;
}
}

No comments: