001: /*
002: * $Id: JXMultiThumbSlider.java,v 1.10 2006/09/24 14:29:22 l2fprod Exp $
003: *
004: * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005: * Santa Clara, California 95054, U.S.A. All rights reserved.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020: */
021:
022: package org.jdesktop.swingx;
023:
024: import java.awt.Color;
025: import java.awt.Dimension;
026: import java.awt.Graphics;
027: import java.awt.Graphics2D;
028: import java.awt.Point;
029: import java.awt.event.ActionEvent;
030: import java.awt.event.ActionListener;
031: import java.awt.event.MouseEvent;
032: import java.util.ArrayList;
033: import java.util.Comparator;
034: import java.util.List;
035: import javax.swing.JComponent;
036: import javax.swing.UIManager;
037: import javax.swing.event.MouseInputAdapter;
038: import org.jdesktop.swingx.multislider.*;
039: import org.jdesktop.swingx.multislider.ThumbListener;
040: import org.jdesktop.swingx.multislider.ThumbRenderer;
041: import org.jdesktop.swingx.multislider.TrackRenderer;
042: import org.jdesktop.swingx.plaf.JXMultiThumbSliderAddon;
043: import org.jdesktop.swingx.plaf.LookAndFeelAddons;
044: import org.jdesktop.swingx.plaf.MultiThumbSliderUI;
045:
046: /**
047: * <p>A slider which can have multiple control points or <i>Thumbs</i></p>
048: * <p>The thumbs each represent a value between the minimum and maximum values
049: * of the slider. Thumbs can pass each other when being dragged. Thumbs have
050: * no default visual representation. To customize the look of the thumbs and the
051: * track behind the thumbs you must provide a ThumbRenderer and a TrackRenderer
052: * implementation. To listen for changes to the thumbs you must provide an
053: * implemention of ThumbDataListener.
054: *
055: * TODOs:
056: * move public inner classes (interfaces, etc) to subpackage
057: * add min/maxvalue convenience methods to jxmultithumbslider
058: * add plafs for windows, mac, and basic (if necessary)
059: * make way to properly control the height.
060: * hide the inner thumb component
061: *
062: * @author joshy
063: */
064: public class JXMultiThumbSlider<E> extends JComponent {
065: static {
066: LookAndFeelAddons.contribute(new JXMultiThumbSliderAddon());
067: }
068:
069: public static final String uiClassID = "MultiThumbSliderUI";
070:
071: /** Creates a new instance of JMultiThumbSlider */
072: public JXMultiThumbSlider() {
073: thumbs = new ArrayList();
074: setLayout(null);
075:
076: tdl = new ThumbHandler();
077:
078: setModel(new DefaultMultiThumbModel<E>());
079: MultiThumbMouseListener mia = new MultiThumbMouseListener(this );
080: addMouseListener(mia);
081: addMouseMotionListener(mia);
082:
083: Dimension dim = new Dimension(60, 16);
084: setPreferredSize(dim);
085: setSize(dim);
086: setMinimumSize(new Dimension(30, 16));
087: updateUI();
088: }
089:
090: public MultiThumbSliderUI getUI() {
091: return (MultiThumbSliderUI) ui;
092: }
093:
094: public void setUI(MultiThumbSliderUI ui) {
095: super .setUI(ui);
096: }
097:
098: public void updateUI() {
099: setUI((MultiThumbSliderUI) LookAndFeelAddons.getUI(this ,
100: MultiThumbSliderUI.class));
101: invalidate();
102: }
103:
104: /**
105: * @inheritDoc
106: */
107: @Override
108: public String getUIClassID() {
109: return uiClassID;
110: }
111:
112: private ThumbDataListener tdl;
113:
114: private List<ThumbComp> thumbs;
115:
116: private ThumbRenderer thumbRenderer;
117:
118: private TrackRenderer trackRenderer;
119:
120: private MultiThumbModel<E> model;
121:
122: private List<ThumbListener> listeners = new ArrayList();
123:
124: private ThumbComp selected;
125:
126: protected void paintComponent(Graphics g) {
127: if (isVisible()) {
128: if (trackRenderer != null) {
129: JComponent comp = trackRenderer
130: .getRendererComponent(this );
131: add(comp);
132: comp.paint(g);
133: remove(comp);
134: } else {
135: paintRange((Graphics2D) g);
136: }
137: }
138: }
139:
140: private void paintRange(Graphics2D g) {
141: g.setColor(Color.blue);
142: g.fillRect(0, 0, getWidth(), getHeight());
143: }
144:
145: private float getThumbValue(int thumbIndex) {
146: return getModel().getThumbAt(thumbIndex).getPosition();
147: }
148:
149: private float getThumbValue(ThumbComp thumb) {
150: return getThumbValue(thumbs.indexOf(thumb));
151: }
152:
153: private int getThumbIndex(ThumbComp thumb) {
154: return thumbs.indexOf(thumb);
155: }
156:
157: private void clipThumbPosition(ThumbComp thumb) {
158: if (getThumbValue(thumb) < getModel().getMinimumValue()) {
159: getModel().getThumbAt(getThumbIndex(thumb)).setPosition(
160: getModel().getMinimumValue());
161: }
162: if (getThumbValue(thumb) > getModel().getMaximumValue()) {
163: getModel().getThumbAt(getThumbIndex(thumb)).setPosition(
164: getModel().getMaximumValue());
165: }
166: }
167:
168: public ThumbRenderer getThumbRenderer() {
169: return thumbRenderer;
170: }
171:
172: public void setThumbRenderer(ThumbRenderer thumbRenderer) {
173: this .thumbRenderer = thumbRenderer;
174: }
175:
176: public TrackRenderer getTrackRenderer() {
177: return trackRenderer;
178: }
179:
180: public void setTrackRenderer(TrackRenderer trackRenderer) {
181: this .trackRenderer = trackRenderer;
182: }
183:
184: public float getMinimumValue() {
185: return getModel().getMinimumValue();
186: }
187:
188: public void setMinimumValue(float minimumValue) {
189: getModel().setMinimumValue(minimumValue);
190: }
191:
192: public float getMaximumValue() {
193: return getModel().getMaximumValue();
194: }
195:
196: public void setMaximumValue(float maximumValue) {
197: getModel().setMaximumValue(maximumValue);
198: }
199:
200: private void setThumbPositionByX(ThumbComp selected) {
201: float range = getModel().getMaximumValue()
202: - getModel().getMinimumValue();
203: int x = selected.getX();
204: // adjust to the center of the thumb
205: x += selected.getWidth() / 2;
206: // adjust for the leading space on the slider
207: x -= selected.getWidth() / 2;
208:
209: int w = getWidth();
210: // adjust for the leading and trailing space on the slider
211: w -= selected.getWidth();
212: float delta = ((float) x) / ((float) w);
213: int thumb_index = getThumbIndex(selected);
214: float value = delta * range;
215: getModel().getThumbAt(thumb_index).setPosition(value);
216: //getModel().setPositionAt(thumb_index,value);
217: clipThumbPosition(selected);
218: }
219:
220: private void setThumbXByPosition(ThumbComp thumb, float pos) {
221: float tu = pos;
222: float lp = getWidth() - thumb.getWidth();
223: float lu = getModel().getMaximumValue()
224: - getModel().getMinimumValue();
225: float tp = (tu * lp) / lu;
226: thumb.setLocation((int) tp - thumb.getWidth() / 2
227: + thumb.getWidth() / 2, thumb.getY());
228: }
229:
230: private void recalc() {
231: for (ThumbComp th : thumbs) {
232: setThumbXByPosition(th, getModel().getThumbAt(
233: getThumbIndex(th)).getPosition());
234: //getPositionAt(getThumbIndex(th)));
235: }
236: }
237:
238: public void setBounds(int x, int y, int w, int h) {
239: super .setBounds(x, y, w, h);
240: recalc();
241: }
242:
243: public JComponent getSelectedThumb() {
244: return selected;
245: }
246:
247: public int getSelectedIndex() {
248: return getThumbIndex(selected);
249: }
250:
251: public MultiThumbModel<E> getModel() {
252: return model;
253: }
254:
255: public void setModel(MultiThumbModel<E> model) {
256: if (this .model != null) {
257: this .model.removeThumbDataListener(tdl);
258: }
259: this .model = model;
260: this .model.addThumbDataListener(tdl);
261: }
262:
263: public void addMultiThumbListener(ThumbListener listener) {
264: listeners.add(listener);
265: }
266:
267: private class MultiThumbMouseListener extends MouseInputAdapter {
268: private JXMultiThumbSlider slider;
269:
270: public MultiThumbMouseListener(JXMultiThumbSlider slider) {
271: this .slider = slider;
272: }
273:
274: public void mousePressed(MouseEvent evt) {
275: ThumbComp handle = findHandle(evt);
276: if (handle != null) {
277: selected = handle;
278: selected.setSelected(true);
279: int thumb_index = getThumbIndex(selected);
280: for (ThumbListener tl : listeners) {
281: tl.thumbSelected(thumb_index);
282: }
283: repaint();
284: } else {
285: selected = null;
286: for (ThumbListener tl : listeners) {
287: tl.thumbSelected(-1);
288: }
289: repaint();
290: }
291: for (ThumbListener tl : listeners) {
292: tl.mousePressed(evt);
293: }
294: }
295:
296: public void mouseReleased(MouseEvent evt) {
297: if (selected != null) {
298: selected.setSelected(false);
299: }
300: }
301:
302: public void mouseDragged(MouseEvent evt) {
303: if (selected != null) {
304: int nx = (int) evt.getPoint().getX()
305: - selected.getWidth() / 2;
306: if (nx < 0) {
307: nx = 0;
308: }
309: if (nx > getWidth() - selected.getWidth()) {
310: nx = getWidth() - selected.getWidth();
311: }
312: selected.setLocation(nx, (int) selected.getLocation()
313: .getY());
314: setThumbPositionByX(selected);
315: int thumb_index = getThumbIndex(selected);
316: //System.out.println("still dragging: " + thumb_index);
317: for (ThumbListener mtl : listeners) {
318: mtl.thumbMoved(thumb_index, getModel().getThumbAt(
319: thumb_index).getPosition());
320: //getPositionAt(thumb_index));
321: }
322: repaint();
323: }
324: }
325:
326: private ThumbComp findHandle(MouseEvent evt) {
327: for (ThumbComp hand : thumbs) {
328: Point p2 = new Point();
329: p2.setLocation(evt.getPoint().getX() - hand.getX(), evt
330: .getPoint().getY()
331: - hand.getY());
332: if (hand.contains(p2)) {
333: return hand;
334: }
335: }
336: return null;
337: }
338: }
339:
340: private class ThumbComp extends JComponent {
341:
342: private JXMultiThumbSlider<E> slider;
343:
344: public ThumbComp(JXMultiThumbSlider slider) {
345: this .slider = slider;
346: Dimension dim = new Dimension(10, 10);//slider.getHeight());
347: /*if(slider.getThumbRenderer() != null) {
348: JComponent comp = getRenderer();
349: dim = comp.getPreferredSize();
350: }*/
351: setSize(dim);
352: setMinimumSize(dim);
353: setPreferredSize(dim);
354: setMaximumSize(dim);
355: setBackground(Color.white);
356: }
357:
358: public void paintComponent(Graphics g) {
359: if (slider.getThumbRenderer() != null) {
360: JComponent comp = getRenderer();
361: comp.setSize(this .getSize());
362: comp.paint(g);
363: } else {
364: g.setColor(getBackground());
365: g.fillRect(0, 0, getWidth(), getHeight());
366: if (isSelected()) {
367: g.setColor(Color.black);
368: g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
369: }
370: }
371: }
372:
373: private JComponent getRenderer() {
374: JComponent comp = slider.getThumbRenderer()
375: .getThumbRendererComponent(slider,
376: slider.getThumbIndex(this ), isSelected());
377: return comp;
378: }
379:
380: private boolean selected;
381:
382: public boolean isSelected() {
383: return selected;
384: }
385:
386: public void setSelected(boolean selected) {
387: this .selected = selected;
388: }
389: }
390:
391: private class ThumbHandler implements ThumbDataListener {
392:
393: public void positionChanged(ThumbDataEvent e) {
394: System.out.println("position changed");
395: }
396:
397: public void thumbAdded(ThumbDataEvent evt) {
398: ThumbComp thumb = new ThumbComp(JXMultiThumbSlider.this );
399: thumb.setLocation(0, 0);
400: add(thumb);
401: thumbs.add(evt.getIndex(), thumb);
402: clipThumbPosition(thumb);
403: setThumbXByPosition(thumb, evt.getThumb().getPosition());
404: repaint();
405: }
406:
407: public void thumbRemoved(ThumbDataEvent evt) {
408: ThumbComp thumb = thumbs.get(evt.getIndex());
409: remove(thumb);
410: thumbs.remove(thumb);
411: repaint();
412: }
413:
414: public void valueChanged(ThumbDataEvent e) {
415: System.out.println("value changed");
416: }
417: }
418:
419: }
|