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:
Post a Comment