001: package prefuse.util.ui;
002:
003: import java.awt.BasicStroke;
004: import java.awt.Color;
005: import java.awt.Cursor;
006: import java.awt.Dimension;
007: import java.awt.Graphics;
008: import java.awt.Graphics2D;
009: import java.awt.Rectangle;
010: import java.awt.event.KeyEvent;
011: import java.awt.event.KeyListener;
012: import java.awt.event.MouseEvent;
013: import java.awt.event.MouseListener;
014: import java.awt.event.MouseMotionListener;
015: import java.util.ArrayList;
016: import java.util.Iterator;
017:
018: import javax.swing.BoundedRangeModel;
019: import javax.swing.DefaultBoundedRangeModel;
020: import javax.swing.JComponent;
021: import javax.swing.event.ChangeEvent;
022: import javax.swing.event.ChangeListener;
023:
024: /**
025: * <p>Implements a Swing-based Range slider, which allows the user to enter a
026: * range (minimum and maximum) value.</p>
027: *
028: * @author Ben Bederson
029: * @author Jesse Grosjean
030: * @author Jon Meyer
031: * @author Lance Good
032: * @author jeffrey heer
033: * @author Colin Combe
034: */
035: public class JRangeSlider extends JComponent implements MouseListener,
036: MouseMotionListener, KeyListener {
037: /*
038: * NOTE: This is a modified version of the original class distributed by
039: * Ben Bederson, Jesse Grosjean, and Jon Meyer as part of an HCIL Tech
040: * Report. It is modified to allow both vertical and horitonal modes.
041: * It also fixes a bug with offset on the buttons. Also fixed a bug with
042: * rendering using (x,y) instead of (0,0) as origin. Also modified to
043: * render arrows as a series of lines rather than as a GeneralPath.
044: * Also modified to fix rounding errors on toLocal and toScreen.
045: *
046: * With inclusion in prefuse, this class has been further modified to use a
047: * bounded range model, support keyboard commands and more extensize
048: * parameterization of rendering/appearance options. Furthermore, a stub
049: * method has been introduced to allow subclasses to perform custom
050: * rendering within the slider through.
051: */
052:
053: final public static int VERTICAL = 0;
054: final public static int HORIZONTAL = 1;
055: final public static int LEFTRIGHT_TOPBOTTOM = 0;
056: final public static int RIGHTLEFT_BOTTOMTOP = 1;
057:
058: final public static int PREFERRED_BREADTH = 16;
059: final public static int PREFERRED_LENGTH = 300;
060: final protected static int ARROW_SZ = 16;
061: final protected static int ARROW_WIDTH = 8;
062: final protected static int ARROW_HEIGHT = 4;
063:
064: protected BoundedRangeModel model;
065: protected int orientation;
066: protected int direction;
067: protected boolean empty;
068: protected int increment = 1;
069: protected int minExtent = 0; // min extent, in pixels
070:
071: protected ArrayList listeners = new ArrayList();
072: protected ChangeEvent changeEvent = null;
073: protected ChangeListener lstnr;
074:
075: protected Color thumbColor = new Color(150, 180, 220);
076:
077: // ------------------------------------------------------------------------
078:
079: /**
080: * Create a new range slider.
081: *
082: * @param minimum - the minimum value of the range.
083: * @param maximum - the maximum value of the range.
084: * @param lowValue - the current low value shown by the range slider's bar.
085: * @param highValue - the current high value shown by the range slider's bar.
086: * @param orientation - construct a horizontal or vertical slider?
087: */
088: public JRangeSlider(int minimum, int maximum, int lowValue,
089: int highValue, int orientation) {
090: this (new DefaultBoundedRangeModel(lowValue, highValue
091: - lowValue, minimum, maximum), orientation,
092: LEFTRIGHT_TOPBOTTOM);
093: }
094:
095: /**
096: * Create a new range slider.
097: *
098: * @param minimum - the minimum value of the range.
099: * @param maximum - the maximum value of the range.
100: * @param lowValue - the current low value shown by the range slider's bar.
101: * @param highValue - the current high value shown by the range slider's bar.
102: * @param orientation - construct a horizontal or vertical slider?
103: * @param direction - Is the slider left-to-right/top-to-bottom or right-to-left/bottom-to-top
104: */
105: public JRangeSlider(int minimum, int maximum, int lowValue,
106: int highValue, int orientation, int direction) {
107: this (new DefaultBoundedRangeModel(lowValue, highValue
108: - lowValue, minimum, maximum), orientation, direction);
109: }
110:
111: /**
112: * Create a new range slider.
113: *
114: * @param model - a BoundedRangeModel specifying the slider's range
115: * @param orientation - construct a horizontal or vertical slider?
116: * @param direction - Is the slider left-to-right/top-to-bottom or right-to-left/bottom-to-top
117: */
118: public JRangeSlider(BoundedRangeModel model, int orientation,
119: int direction) {
120: super .setFocusable(true);
121: this .model = model;
122: this .orientation = orientation;
123: this .direction = direction;
124:
125: setForeground(Color.LIGHT_GRAY);
126:
127: this .lstnr = createListener();
128: model.addChangeListener(lstnr);
129:
130: addMouseListener(this );
131: addMouseMotionListener(this );
132: addKeyListener(this );
133: }
134:
135: /**
136: * Create a listener to relay change events from the bounded range model.
137: * @return a ChangeListener to relay events from the range model
138: */
139: protected ChangeListener createListener() {
140: return new RangeSliderChangeListener();
141: }
142:
143: /**
144: * Listener that fires a change event when it receives change event from
145: * the slider list model.
146: */
147: protected class RangeSliderChangeListener implements ChangeListener {
148: public void stateChanged(ChangeEvent e) {
149: fireChangeEvent();
150: }
151: }
152:
153: /**
154: * Returns the current "low" value shown by the range slider's bar. The low
155: * value meets the constraint minimum <= lowValue <= highValue <= maximum.
156: */
157: public int getLowValue() {
158: return model.getValue();
159: }
160:
161: /**
162: * Sets the low value shown by this range slider. This causes the range slider to be
163: * repainted and a ChangeEvent to be fired.
164: * @param lowValue the low value to use
165: */
166: public void setLowValue(int lowValue) {
167: int e = (model.getValue() - lowValue) + model.getExtent();
168: model.setRangeProperties(lowValue, e, model.getMinimum(), model
169: .getMaximum(), false);
170: model.setValue(lowValue);
171: }
172:
173: /**
174: * Returns the current "high" value shown by the range slider's bar. The high
175: * value meets the constraint minimum <= lowValue <= highValue <= maximum.
176: */
177: public int getHighValue() {
178: return model.getValue() + model.getExtent();
179: }
180:
181: /**
182: * Sets the high value shown by this range slider. This causes the range slider to be
183: * repainted and a ChangeEvent to be fired.
184: * @param highValue the high value to use
185: */
186: public void setHighValue(int highValue) {
187: model.setExtent(highValue - model.getValue());
188: }
189:
190: /**
191: * Set the slider range span.
192: * @param lowValue the low value of the slider range
193: * @param highValue the high value of the slider range
194: */
195: public void setRange(int lowValue, int highValue) {
196: model.setRangeProperties(lowValue, highValue - lowValue, model
197: .getMinimum(), model.getMaximum(), false);
198: }
199:
200: /**
201: * Gets the minimum possible value for either the low value or the high value.
202: * @return the minimum possible range value
203: */
204: public int getMinimum() {
205: return model.getMinimum();
206: }
207:
208: /**
209: * Sets the minimum possible value for either the low value or the high value.
210: * @param minimum the minimum possible range value
211: */
212: public void setMinimum(int minimum) {
213: model.setMinimum(minimum);
214: }
215:
216: /**
217: * Gets the maximum possible value for either the low value or the high value.
218: * @return the maximum possible range value
219: */
220: public int getMaximum() {
221: return model.getMaximum();
222: }
223:
224: /**
225: * Sets the maximum possible value for either the low value or the high value.
226: * @param maximum the maximum possible range value
227: */
228: public void setMaximum(int maximum) {
229: model.setMaximum(maximum);
230: }
231:
232: /**
233: * Sets the minimum extent (difference between low and high values).
234: * This method <strong>does not</strong> change the current state of the
235: * model, but can affect all subsequent interaction.
236: * @param minExtent the minimum extent allowed in subsequent interaction
237: */
238: public void setMinExtent(int minExtent) {
239: this .minExtent = minExtent;
240: }
241:
242: /**
243: * Sets whether this slider is empty.
244: * @param empty true if set to empty, false otherwise
245: */
246: public void setEmpty(boolean empty) {
247: this .empty = empty;
248: repaint();
249: }
250:
251: /**
252: * Get the slider thumb color. This is the part of the slider between
253: * the range resize buttons.
254: * @return the slider thumb color
255: */
256: public Color getThumbColor() {
257: return thumbColor;
258: }
259:
260: /**
261: * Set the slider thumb color. This is the part of the slider between
262: * the range resize buttons.
263: * @param thumbColor the slider thumb color
264: */
265: public void setThumbColor(Color thumbColor) {
266: this .thumbColor = thumbColor;
267: }
268:
269: /**
270: * Get the BoundedRangeModel backing this slider.
271: * @return the slider's range model
272: */
273: public BoundedRangeModel getModel() {
274: return model;
275: }
276:
277: /**
278: * Set the BoundedRangeModel backing this slider.
279: * @param brm the slider range model to use
280: */
281: public void setModel(BoundedRangeModel brm) {
282: model.removeChangeListener(lstnr);
283: model = brm;
284: model.addChangeListener(lstnr);
285: repaint();
286: }
287:
288: /**
289: * Registers a listener for ChangeEvents.
290: * @param cl the ChangeListener to add
291: */
292: public void addChangeListener(ChangeListener cl) {
293: if (!listeners.contains(cl))
294: listeners.add(cl);
295: }
296:
297: /**
298: * Removes a listener for ChangeEvents.
299: * @param cl the ChangeListener to remove
300: */
301: public void removeChangeListener(ChangeListener cl) {
302: listeners.remove(cl);
303: }
304:
305: /**
306: * Fire a change event to all listeners.
307: */
308: protected void fireChangeEvent() {
309: repaint();
310: if (changeEvent == null)
311: changeEvent = new ChangeEvent(this );
312: Iterator iter = listeners.iterator();
313: while (iter.hasNext())
314: ((ChangeListener) iter.next()).stateChanged(changeEvent);
315: }
316:
317: /**
318: * @see java.awt.Component#getPreferredSize()
319: */
320: public Dimension getPreferredSize() {
321: if (orientation == VERTICAL) {
322: return new Dimension(PREFERRED_BREADTH, PREFERRED_LENGTH);
323: } else {
324: return new Dimension(PREFERRED_LENGTH, PREFERRED_BREADTH);
325: }
326: }
327:
328: // ------------------------------------------------------------------------
329: // Rendering
330:
331: /**
332: * Override this method to perform custom painting of the slider trough.
333: * @param g a Graphics2D context for rendering
334: * @param width the width of the slider trough
335: * @param height the height of the slider trough
336: */
337: protected void customPaint(Graphics2D g, int width, int height) {
338: // does nothing in this class
339: // subclasses can override to perform custom painting
340: }
341:
342: /**
343: * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
344: */
345: public void paintComponent(Graphics g) {
346: Rectangle bounds = getBounds();
347: int width = (int) bounds.getWidth() - 1;
348: int height = (int) bounds.getHeight() - 1;
349:
350: int min = toScreen(getLowValue());
351: int max = toScreen(getHighValue());
352:
353: // Paint the full slider if the slider is marked as empty
354: if (empty) {
355: if (direction == LEFTRIGHT_TOPBOTTOM) {
356: min = ARROW_SZ;
357: max = (orientation == VERTICAL) ? height - ARROW_SZ
358: : width - ARROW_SZ;
359: } else {
360: min = (orientation == VERTICAL) ? height - ARROW_SZ
361: : width - ARROW_SZ;
362: max = ARROW_SZ;
363: }
364: }
365:
366: Graphics2D g2 = (Graphics2D) g;
367: g2.setColor(getBackground());
368: g2.fillRect(0, 0, width, height);
369: g2.setColor(getForeground());
370: g2.drawRect(0, 0, width, height);
371:
372: customPaint(g2, width, height);
373:
374: // Draw arrow and thumb backgrounds
375: g2.setStroke(new BasicStroke(1));
376: if (orientation == VERTICAL) {
377: if (direction == LEFTRIGHT_TOPBOTTOM) {
378: g2.setColor(getForeground());
379: g2.fillRect(0, min - ARROW_SZ, width, ARROW_SZ - 1);
380: paint3DRectLighting(g2, 0, min - ARROW_SZ, width,
381: ARROW_SZ - 1);
382:
383: if (thumbColor != null) {
384: g2.setColor(thumbColor);
385: g2.fillRect(0, min, width, max - min - 1);
386: paint3DRectLighting(g2, 0, min, width, max - min
387: - 1);
388: }
389:
390: g2.setColor(getForeground());
391: g2.fillRect(0, max, width, ARROW_SZ - 1);
392: paint3DRectLighting(g2, 0, max, width, ARROW_SZ - 1);
393:
394: // Draw arrows
395: g2.setColor(Color.black);
396: paintArrow(g2, (width - ARROW_WIDTH) / 2.0, min
397: - ARROW_SZ + (ARROW_SZ - ARROW_HEIGHT) / 2.0,
398: ARROW_WIDTH, ARROW_HEIGHT, true);
399: paintArrow(g2, (width - ARROW_WIDTH) / 2.0, max
400: + (ARROW_SZ - ARROW_HEIGHT) / 2.0, ARROW_WIDTH,
401: ARROW_HEIGHT, false);
402: } else {
403: g2.setColor(getForeground());
404: g2.fillRect(0, min, width, ARROW_SZ - 1);
405: paint3DRectLighting(g2, 0, min, width, ARROW_SZ - 1);
406:
407: if (thumbColor != null) {
408: g2.setColor(thumbColor);
409: g2.fillRect(0, max, width, min - max - 1);
410: paint3DRectLighting(g2, 0, max, width, min - max
411: - 1);
412: }
413:
414: g2.setColor(getForeground());
415: g2.fillRect(0, max - ARROW_SZ, width, ARROW_SZ - 1);
416: paint3DRectLighting(g2, 0, max - ARROW_SZ, width,
417: ARROW_SZ - 1);
418:
419: // Draw arrows
420: g2.setColor(Color.black);
421: paintArrow(g2, (width - ARROW_WIDTH) / 2.0, min
422: + (ARROW_SZ - ARROW_HEIGHT) / 2.0, ARROW_WIDTH,
423: ARROW_HEIGHT, false);
424: paintArrow(g2, (width - ARROW_WIDTH) / 2.0, max
425: - ARROW_SZ + (ARROW_SZ - ARROW_HEIGHT) / 2.0,
426: ARROW_WIDTH, ARROW_HEIGHT, true);
427: }
428: } else {
429: if (direction == LEFTRIGHT_TOPBOTTOM) {
430: g2.setColor(getForeground());
431: g2.fillRect(min - ARROW_SZ, 0, ARROW_SZ - 1, height);
432: paint3DRectLighting(g2, min - ARROW_SZ, 0,
433: ARROW_SZ - 1, height);
434:
435: if (thumbColor != null) {
436: g2.setColor(thumbColor);
437: g2.fillRect(min, 0, max - min - 1, height);
438: paint3DRectLighting(g2, min, 0, max - min - 1,
439: height);
440: }
441:
442: g2.setColor(getForeground());
443: g2.fillRect(max, 0, ARROW_SZ - 1, height);
444: paint3DRectLighting(g2, max, 0, ARROW_SZ - 1, height);
445:
446: // Draw arrows
447: g2.setColor(Color.black);
448: paintArrow(g2, min - ARROW_SZ
449: + (ARROW_SZ - ARROW_HEIGHT) / 2.0,
450: (height - ARROW_WIDTH) / 2.0, ARROW_HEIGHT,
451: ARROW_WIDTH, true);
452: paintArrow(g2, max + (ARROW_SZ - ARROW_HEIGHT) / 2.0,
453: (height - ARROW_WIDTH) / 2.0, ARROW_HEIGHT,
454: ARROW_WIDTH, false);
455: } else {
456: g2.setColor(getForeground());
457: g2.fillRect(min, 0, ARROW_SZ - 1, height);
458: paint3DRectLighting(g2, min, 0, ARROW_SZ - 1, height);
459:
460: if (thumbColor != null) {
461: g2.setColor(thumbColor);
462: g2.fillRect(max, 0, min - max - 1, height);
463: paint3DRectLighting(g2, max, 0, min - max - 1,
464: height);
465: }
466:
467: g2.setColor(getForeground());
468: g2.fillRect(max - ARROW_SZ, 0, ARROW_SZ - 1, height);
469: paint3DRectLighting(g2, max - ARROW_SZ, 0,
470: ARROW_SZ - 1, height);
471:
472: // Draw arrows
473: g2.setColor(Color.black);
474: paintArrow(g2, min + (ARROW_SZ - ARROW_HEIGHT) / 2.0,
475: (height - ARROW_WIDTH) / 2.0, ARROW_HEIGHT,
476: ARROW_WIDTH, true);
477: paintArrow(g2, max - ARROW_SZ
478: + (ARROW_SZ - ARROW_HEIGHT) / 2.0,
479: (height - ARROW_WIDTH) / 2.0, ARROW_HEIGHT,
480: ARROW_WIDTH, false);
481: }
482: }
483: }
484:
485: /**
486: * This draws an arrow as a series of lines within the specified box.
487: * The last boolean specifies whether the point should be at the
488: * right/bottom or left/top.
489: */
490: protected void paintArrow(Graphics2D g2, double x, double y, int w,
491: int h, boolean topDown) {
492: int intX = (int) (x + 0.5);
493: int intY = (int) (y + 0.5);
494:
495: if (orientation == VERTICAL) {
496: if (w % 2 == 0) {
497: w = w - 1;
498: }
499:
500: if (topDown) {
501: for (int i = 0; i < (w / 2 + 1); i++) {
502: g2.drawLine(intX + i, intY + i, intX + w - i - 1,
503: intY + i);
504: }
505: } else {
506: for (int i = 0; i < (w / 2 + 1); i++) {
507: g2.drawLine(intX + w / 2 - i, intY + i, intX + w
508: - w / 2 + i - 1, intY + i);
509: }
510: }
511: } else {
512: if (h % 2 == 0) {
513: h = h - 1;
514: }
515:
516: if (topDown) {
517: for (int i = 0; i < (h / 2 + 1); i++) {
518: g2.drawLine(intX + i, intY + i, intX + i, intY + h
519: - i - 1);
520: }
521: } else {
522: for (int i = 0; i < (h / 2 + 1); i++) {
523: g2.drawLine(intX + i, intY + h / 2 - i, intX + i,
524: intY + h - h / 2 + i - 1);
525: }
526: }
527: }
528: }
529:
530: /**
531: * Adds Windows2K type 3D lighting effects
532: */
533: protected void paint3DRectLighting(Graphics2D g2, int x, int y,
534: int width, int height) {
535: g2.setColor(Color.white);
536: g2.drawLine(x + 1, y + 1, x + 1, y + height - 1);
537: g2.drawLine(x + 1, y + 1, x + width - 1, y + 1);
538: g2.setColor(Color.gray);
539: g2.drawLine(x + 1, y + height - 1, x + width - 1, y + height
540: - 1);
541: g2
542: .drawLine(x + width - 1, y + 1, x + width - 1, y
543: + height - 1);
544: g2.setColor(Color.darkGray);
545: g2.drawLine(x, y + height, x + width, y + height);
546: g2.drawLine(x + width, y, x + width, y + height);
547: }
548:
549: /**
550: * Converts from screen coordinates to a range value.
551: */
552: protected int toLocal(int xOrY) {
553: Dimension sz = getSize();
554: int min = getMinimum();
555: double scale;
556: if (orientation == VERTICAL) {
557: scale = (sz.height - (2 * ARROW_SZ))
558: / (double) (getMaximum() - min);
559: } else {
560: scale = (sz.width - (2 * ARROW_SZ))
561: / (double) (getMaximum() - min);
562: }
563:
564: if (direction == LEFTRIGHT_TOPBOTTOM) {
565: return (int) (((xOrY - ARROW_SZ) / scale) + min + 0.5);
566: } else {
567: if (orientation == VERTICAL) {
568: return (int) ((sz.height - xOrY - ARROW_SZ) / scale
569: + min + 0.5);
570: } else {
571: return (int) ((sz.width - xOrY - ARROW_SZ) / scale
572: + min + 0.5);
573: }
574: }
575: }
576:
577: /**
578: * Converts from a range value to screen coordinates.
579: */
580: protected int toScreen(int xOrY) {
581: Dimension sz = getSize();
582: int min = getMinimum();
583: double scale;
584: if (orientation == VERTICAL) {
585: scale = (sz.height - (2 * ARROW_SZ))
586: / (double) (getMaximum() - min);
587: } else {
588: scale = (sz.width - (2 * ARROW_SZ))
589: / (double) (getMaximum() - min);
590: }
591:
592: // If the direction is left/right_top/bottom then we subtract the min and multiply times scale
593: // Otherwise, we have to invert the number by subtracting the value from the height
594: if (direction == LEFTRIGHT_TOPBOTTOM) {
595: return (int) (ARROW_SZ + ((xOrY - min) * scale) + 0.5);
596: } else {
597: if (orientation == VERTICAL) {
598: return (int) (sz.height - (xOrY - min) * scale
599: - ARROW_SZ + 0.5);
600: } else {
601: return (int) (sz.width - (xOrY - min) * scale
602: - ARROW_SZ + 0.5);
603: }
604: }
605: }
606:
607: /**
608: * Converts from a range value to screen coordinates.
609: */
610: protected double toScreenDouble(int xOrY) {
611: Dimension sz = getSize();
612: int min = getMinimum();
613: double scale;
614: if (orientation == VERTICAL) {
615: scale = (sz.height - (2 * ARROW_SZ))
616: / (double) (getMaximum() + 1 - min);
617: } else {
618: scale = (sz.width - (2 * ARROW_SZ))
619: / (double) (getMaximum() + 1 - min);
620: }
621:
622: // If the direction is left/right_top/bottom then we subtract the min and multiply times scale
623: // Otherwise, we have to invert the number by subtracting the value from the height
624: if (direction == LEFTRIGHT_TOPBOTTOM) {
625: return ARROW_SZ + ((xOrY - min) * scale);
626: } else {
627: if (orientation == VERTICAL) {
628: return sz.height - (xOrY - min) * scale - ARROW_SZ;
629: } else {
630: return sz.width - (xOrY - min) * scale - ARROW_SZ;
631: }
632: }
633: }
634:
635: // ------------------------------------------------------------------------
636: // Event Handling
637:
638: static final int PICK_NONE = 0;
639: static final int PICK_LEFT_OR_TOP = 1;
640: static final int PICK_THUMB = 2;
641: static final int PICK_RIGHT_OR_BOTTOM = 3;
642: int pick;
643: int pickOffsetLow;
644: int pickOffsetHigh;
645: int mouse;
646:
647: private int pickHandle(int xOrY) {
648: int min = toScreen(getLowValue());
649: int max = toScreen(getHighValue());
650: int pick = PICK_NONE;
651:
652: if (direction == LEFTRIGHT_TOPBOTTOM) {
653: if ((xOrY > (min - ARROW_SZ)) && (xOrY < min)) {
654: pick = PICK_LEFT_OR_TOP;
655: } else if ((xOrY >= min) && (xOrY <= max)) {
656: pick = PICK_THUMB;
657: } else if ((xOrY > max) && (xOrY < (max + ARROW_SZ))) {
658: pick = PICK_RIGHT_OR_BOTTOM;
659: }
660: } else {
661: if ((xOrY > min) && (xOrY < (min + ARROW_SZ))) {
662: pick = PICK_LEFT_OR_TOP;
663: } else if ((xOrY <= min) && (xOrY >= max)) {
664: pick = PICK_THUMB;
665: } else if ((xOrY > (max - ARROW_SZ) && (xOrY < max))) {
666: pick = PICK_RIGHT_OR_BOTTOM;
667: }
668: }
669:
670: return pick;
671: }
672:
673: private void offset(int dxOrDy) {
674: model.setValue(model.getValue() + dxOrDy);
675: }
676:
677: /**
678: * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
679: */
680: public void mousePressed(MouseEvent e) {
681: if (orientation == VERTICAL) {
682: pick = pickHandle(e.getY());
683: pickOffsetLow = e.getY() - toScreen(getLowValue());
684: pickOffsetHigh = e.getY() - toScreen(getHighValue());
685: mouse = e.getY();
686: } else {
687: pick = pickHandle(e.getX());
688: pickOffsetLow = e.getX() - toScreen(getLowValue());
689: pickOffsetHigh = e.getX() - toScreen(getHighValue());
690: mouse = e.getX();
691: }
692: repaint();
693: }
694:
695: /**
696: * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
697: */
698: public void mouseDragged(MouseEvent e) {
699: requestFocus();
700: int value = (orientation == VERTICAL) ? e.getY() : e.getX();
701:
702: int minimum = getMinimum();
703: int maximum = getMaximum();
704: int lowValue = getLowValue();
705: int highValue = getHighValue();
706:
707: switch (pick) {
708: case PICK_LEFT_OR_TOP:
709: int low = toLocal(value - pickOffsetLow);
710:
711: if (low < minimum) {
712: low = minimum;
713: }
714: if (low > maximum - minExtent) {
715: low = maximum - minExtent;
716: }
717: if (low > highValue - minExtent) {
718: setRange(low, low + minExtent);
719: } else
720: setLowValue(low);
721: break;
722:
723: case PICK_RIGHT_OR_BOTTOM:
724: int high = toLocal(value - pickOffsetHigh);
725:
726: if (high < minimum + minExtent) {
727: high = minimum + minExtent;
728: }
729: if (high > maximum) {
730: high = maximum;
731: }
732: if (high < lowValue + minExtent) {
733: setRange(high - minExtent, high);
734: } else
735: setHighValue(high);
736: break;
737:
738: case PICK_THUMB:
739: int dxOrDy = toLocal(value - pickOffsetLow) - lowValue;
740: if ((dxOrDy < 0) && ((lowValue + dxOrDy) < minimum)) {
741: dxOrDy = minimum - lowValue;
742: }
743: if ((dxOrDy > 0) && ((highValue + dxOrDy) > maximum)) {
744: dxOrDy = maximum - highValue;
745: }
746: if (dxOrDy != 0) {
747: offset(dxOrDy);
748: }
749: break;
750: }
751: }
752:
753: /**
754: * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
755: */
756: public void mouseReleased(MouseEvent e) {
757: pick = PICK_NONE;
758: repaint();
759: }
760:
761: /**
762: * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
763: */
764: public void mouseMoved(MouseEvent e) {
765: if (orientation == VERTICAL) {
766: switch (pickHandle(e.getY())) {
767: case PICK_LEFT_OR_TOP:
768: setCursor(Cursor
769: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
770: break;
771: case PICK_RIGHT_OR_BOTTOM:
772: setCursor(Cursor
773: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
774: break;
775: case PICK_THUMB:
776: setCursor(Cursor
777: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
778: break;
779: case PICK_NONE:
780: setCursor(Cursor
781: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
782: break;
783: }
784: } else {
785: switch (pickHandle(e.getX())) {
786: case PICK_LEFT_OR_TOP:
787: setCursor(Cursor
788: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
789: break;
790: case PICK_RIGHT_OR_BOTTOM:
791: setCursor(Cursor
792: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
793: break;
794: case PICK_THUMB:
795: setCursor(Cursor
796: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
797: break;
798: case PICK_NONE:
799: setCursor(Cursor
800: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
801: break;
802: }
803: }
804: }
805:
806: /**
807: * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
808: */
809: public void mouseClicked(MouseEvent e) {
810: }
811:
812: /**
813: * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
814: */
815: public void mouseEntered(MouseEvent e) {
816: }
817:
818: /**
819: * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
820: */
821: public void mouseExited(MouseEvent e) {
822: }
823:
824: private void grow(int increment) {
825: model.setRangeProperties(model.getValue() - increment, model
826: .getExtent()
827: + 2 * increment, model.getMinimum(),
828: model.getMaximum(), false);
829: }
830:
831: /**
832: * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
833: */
834: public void keyPressed(KeyEvent e) {
835: int kc = e.getKeyCode();
836: boolean v = (orientation == VERTICAL);
837: boolean d = (kc == KeyEvent.VK_DOWN);
838: boolean u = (kc == KeyEvent.VK_UP);
839: boolean l = (kc == KeyEvent.VK_LEFT);
840: boolean r = (kc == KeyEvent.VK_RIGHT);
841:
842: int minimum = getMinimum();
843: int maximum = getMaximum();
844: int lowValue = getLowValue();
845: int highValue = getHighValue();
846:
847: if (v && r || !v && u) {
848: if (lowValue - increment >= minimum
849: && highValue + increment <= maximum) {
850: grow(increment);
851: }
852: } else if (v && l || !v && d) {
853: if (highValue - lowValue >= 2 * increment) {
854: grow(-1 * increment);
855: }
856: } else if (v && d || !v && l) {
857: if (lowValue - increment >= minimum) {
858: offset(-increment);
859: }
860: } else if (v && u || !v && r) {
861: if (highValue + increment <= maximum) {
862: offset(increment);
863: }
864: }
865: }
866:
867: /**
868: * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
869: */
870: public void keyReleased(KeyEvent e) {
871: }
872:
873: /**
874: * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
875: */
876: public void keyTyped(KeyEvent e) {
877: }
878:
879: } // end of class JRangeSlider
|