org.jdesktop.swingx.JXMultiThumbSlider Maven / Gradle / Ivy
/*
* $Id: JXMultiThumbSlider.java 4147 2012-02-01 17:13:24Z kschaefe $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jdesktop.swingx;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.event.MouseInputAdapter;
import org.jdesktop.beans.JavaBean;
import org.jdesktop.swingx.multislider.DefaultMultiThumbModel;
import org.jdesktop.swingx.multislider.MultiThumbModel;
import org.jdesktop.swingx.multislider.ThumbDataEvent;
import org.jdesktop.swingx.multislider.ThumbDataListener;
import org.jdesktop.swingx.multislider.ThumbListener;
import org.jdesktop.swingx.multislider.ThumbRenderer;
import org.jdesktop.swingx.multislider.TrackRenderer;
import org.jdesktop.swingx.plaf.LookAndFeelAddons;
import org.jdesktop.swingx.plaf.MultiThumbSliderAddon;
import org.jdesktop.swingx.plaf.MultiThumbSliderUI;
/**
* A slider which can have multiple control points or Thumbs
* The thumbs each represent a value between the minimum and maximum values
* of the slider. Thumbs can pass each other when being dragged. Thumbs have
* no default visual representation. To customize the look of the thumbs and the
* track behind the thumbs you must provide a ThumbRenderer and a TrackRenderer
* implementation. To listen for changes to the thumbs you must provide an
* implementation of ThumbDataListener.
*
* TODOs:
* add min/maxvalue convenience methods to jxmultithumbslider
* add plafs for windows, mac, and basic (if necessary)
* make way to properly control the height.
* hide the inner thumb component
*
* @author joshy
*/
@JavaBean
public class JXMultiThumbSlider extends JComponent {
public static final String uiClassID = "MultiThumbSliderUI";
private ThumbDataListener tdl;
private List thumbs;
private ThumbRenderer thumbRenderer;
private TrackRenderer trackRenderer;
private MultiThumbModel model;
private List listeners = new ArrayList();
private ThumbComp selected;
static {
LookAndFeelAddons.contribute(new MultiThumbSliderAddon());
}
/** Creates a new instance of JMultiThumbSlider */
public JXMultiThumbSlider() {
thumbs = new ArrayList();
setLayout(null);
tdl = new ThumbHandler();
setModel(new DefaultMultiThumbModel());
MultiThumbMouseListener mia = new MultiThumbMouseListener();
addMouseListener(mia);
addMouseMotionListener(mia);
Dimension dim = new Dimension(60,16);
setPreferredSize(dim);
setSize(dim);
setMinimumSize(new Dimension(30,16));
updateUI();
}
/**
* {@inheritDoc}
*/
@Override
public String getUIClassID() {
return uiClassID;
}
public MultiThumbSliderUI getUI() {
return (MultiThumbSliderUI)ui;
}
public void setUI(MultiThumbSliderUI ui) {
super.setUI(ui);
}
@Override
public void updateUI() {
setUI((MultiThumbSliderUI)LookAndFeelAddons.getUI(this, MultiThumbSliderUI.class));
invalidate();
}
@Override
protected void paintComponent(Graphics g) {
if(isVisible()) {
if(trackRenderer != null) {
JComponent comp = trackRenderer.getRendererComponent(this);
add(comp);
comp.paint(g);
remove(comp);
} else {
paintRange((Graphics2D)g);
}
}
}
private void paintRange(Graphics2D g) {
g.setColor(Color.blue);
g.fillRect(0,0,getWidth(),getHeight());
}
private float getThumbValue(int thumbIndex) {
return getModel().getThumbAt(thumbIndex).getPosition();
}
private float getThumbValue(ThumbComp thumb) {
return getThumbValue(thumbs.indexOf(thumb));
}
private int getThumbIndex(ThumbComp thumb) {
return thumbs.indexOf(thumb);
}
private void clipThumbPosition(ThumbComp thumb) {
if(getThumbValue(thumb) < getModel().getMinimumValue()) {
getModel().getThumbAt(getThumbIndex(thumb)).setPosition(
getModel().getMinimumValue());
}
if(getThumbValue(thumb) > getModel().getMaximumValue()) {
getModel().getThumbAt(getThumbIndex(thumb)).setPosition(
getModel().getMaximumValue());
}
}
public ThumbRenderer getThumbRenderer() {
return thumbRenderer;
}
public void setThumbRenderer(ThumbRenderer thumbRenderer) {
this.thumbRenderer = thumbRenderer;
}
public TrackRenderer getTrackRenderer() {
return trackRenderer;
}
public void setTrackRenderer(TrackRenderer trackRenderer) {
this.trackRenderer = trackRenderer;
}
public float getMinimumValue() {
return getModel().getMinimumValue();
}
public void setMinimumValue(float minimumValue) {
getModel().setMinimumValue(minimumValue);
}
public float getMaximumValue() {
return getModel().getMaximumValue();
}
public void setMaximumValue(float maximumValue) {
getModel().setMaximumValue(maximumValue);
}
private void setThumbPositionByX(ThumbComp selected) {
float range = getModel().getMaximumValue()-getModel().getMinimumValue();
int x = selected.getX();
// adjust to the center of the thumb
x += selected.getWidth()/2;
// adjust for the leading space on the slider
x -= selected.getWidth()/2;
int w = getWidth();
// adjust for the leading and trailing space on the slider
w -= selected.getWidth();
float delta = ((float)x)/((float)w);
int thumb_index = getThumbIndex(selected);
float value = delta*range;
getModel().getThumbAt(thumb_index).setPosition(value);
//getModel().setPositionAt(thumb_index,value);
clipThumbPosition(selected);
}
private void setThumbXByPosition(ThumbComp thumb, float pos) {
float lp = getWidth()-thumb.getWidth();
float lu = getModel().getMaximumValue()-getModel().getMinimumValue();
float tp = (pos*lp)/lu;
thumb.setLocation((int)tp-thumb.getWidth()/2 + thumb.getWidth()/2, thumb.getY());
}
private void recalc() {
for(ThumbComp th : thumbs) {
setThumbXByPosition(th,getModel().getThumbAt(getThumbIndex(th)).getPosition());
//getPositionAt(getThumbIndex(th)));
}
}
@Override
public void setBounds(int x, int y, int w, int h) {
super.setBounds(x,y,w,h);
recalc();
}
public JComponent getSelectedThumb() {
return selected;
}
public int getSelectedIndex() {
return getThumbIndex(selected);
}
public MultiThumbModel getModel() {
return model;
}
public void setModel(MultiThumbModel model) {
if(this.model != null) {
this.model.removeThumbDataListener(tdl);
}
this.model = model;
this.model.addThumbDataListener(tdl);
}
public void addMultiThumbListener(ThumbListener listener) {
listeners.add(listener);
}
private class MultiThumbMouseListener extends MouseInputAdapter {
@Override
public void mousePressed(MouseEvent evt) {
ThumbComp handle = findHandle(evt);
if(handle != null) {
selected = handle;
selected.setSelected(true);
int thumb_index = getThumbIndex(selected);
for(ThumbListener tl : listeners) {
tl.thumbSelected(thumb_index);
}
repaint();
} else {
selected = null;
for(ThumbListener tl : listeners) {
tl.thumbSelected(-1);
}
repaint();
}
for(ThumbListener tl : listeners) {
tl.mousePressed(evt);
}
}
@Override
public void mouseReleased(MouseEvent evt) {
if(selected != null) {
selected.setSelected(false);
}
}
@Override
public void mouseDragged(MouseEvent evt) {
if(selected != null) {
int nx = (int)evt.getPoint().getX()- selected.getWidth()/2;
if(nx < 0) {
nx = 0;
}
if(nx > getWidth()-selected.getWidth()) {
nx = getWidth()-selected.getWidth();
}
selected.setLocation(nx,(int)selected.getLocation().getY());
setThumbPositionByX(selected);
int thumb_index = getThumbIndex(selected);
//log.fine("still dragging: " + thumb_index);
for(ThumbListener mtl : listeners) {
mtl.thumbMoved(thumb_index,getModel().getThumbAt(thumb_index).getPosition());
//getPositionAt(thumb_index));
}
repaint();
}
}
private ThumbComp findHandle(MouseEvent evt) {
for(ThumbComp hand : thumbs) {
Point p2 = new Point();
p2.setLocation(evt.getPoint().getX() - hand.getX(),
evt.getPoint().getY() - hand.getY());
if(hand.contains(p2)) {
return hand;
}
}
return null;
}
}
private static class ThumbComp extends JComponent {
private JXMultiThumbSlider> slider;
public ThumbComp(JXMultiThumbSlider> slider) {
this.slider = slider;
Dimension dim = new Dimension(10,10);//slider.getHeight());
/*if(slider.getThumbRenderer() != null) {
JComponent comp = getRenderer();
dim = comp.getPreferredSize();
}*/
setSize(dim);
setMinimumSize(dim);
setPreferredSize(dim);
setMaximumSize(dim);
setBackground(Color.white);
}
@Override
public void paintComponent(Graphics g) {
if(slider.getThumbRenderer() != null) {
JComponent comp = getRenderer();
comp.setSize(this.getSize());
comp.paint(g);
} else {
g.setColor(getBackground());
g.fillRect(0,0,getWidth(),getHeight());
if(isSelected()) {
g.setColor(Color.black);
g.drawRect(0,0,getWidth()-1,getHeight()-1);
}
}
}
private JComponent getRenderer() {
return slider.getThumbRenderer().
getThumbRendererComponent(slider,slider.getThumbIndex(this),isSelected());
}
private boolean selected;
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
private class ThumbHandler implements ThumbDataListener {
@Override
public void positionChanged(ThumbDataEvent e) {
ThumbComp comp = thumbs.get(e.getIndex());
clipThumbPosition(comp);
setThumbXByPosition(comp, e.getThumb().getPosition());
repaint();
}
@Override
public void thumbAdded(ThumbDataEvent evt) {
ThumbComp thumb = new ThumbComp(JXMultiThumbSlider.this);
thumb.setLocation(0, 0);
add(thumb);
thumbs.add(evt.getIndex(), thumb);
clipThumbPosition(thumb);
setThumbXByPosition(thumb, evt.getThumb().getPosition());
repaint();
}
@Override
public void thumbRemoved(ThumbDataEvent evt) {
ThumbComp thumb = thumbs.get(evt.getIndex());
remove(thumb);
thumbs.remove(thumb);
repaint();
}
@Override
public void valueChanged(ThumbDataEvent e) {
repaint();
}
}
}