blob: 0cf5ed22648fc170c3bd2e6709d168c9737f3445 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.swing.plaf.synth;
27
28import java.awt.Component;
29import java.awt.Container;
30import java.awt.Adjustable;
31import java.awt.event.*;
32import java.awt.Graphics;
33import java.awt.Dimension;
34import java.awt.Font;
35import java.awt.FontMetrics;
36import java.awt.Rectangle;
37import java.awt.Point;
38import java.awt.Insets;
39import java.awt.Color;
40import java.awt.IllegalComponentStateException;
41import java.awt.Polygon;
42import java.beans.*;
43import java.util.Dictionary;
44import java.util.Enumeration;
45import javax.swing.border.AbstractBorder;
46import javax.swing.*;
47import javax.swing.event.*;
48import javax.swing.plaf.*;
49import javax.swing.plaf.basic.BasicSliderUI;
50import sun.swing.plaf.synth.SynthUI;
51import sun.swing.SwingUtilities2;
52
53
54/**
55 * Synth's SliderUI.
56 *
57 * @author Joshua Outwater
58 */
59class SynthSliderUI extends BasicSliderUI implements PropertyChangeListener,
60 SynthUI {
61 protected Dimension contentDim = new Dimension();
62 protected Rectangle valueRect = new Rectangle();
63 protected boolean paintValue;
64
65 private int trackHeight;
66 private int trackBorder;
67 private int thumbWidth;
68 private int thumbHeight;
69
70 private SynthStyle style;
71 private SynthStyle sliderTrackStyle;
72 private SynthStyle sliderThumbStyle;
73
74 /** Used to determine the color to paint the thumb. */
75 private transient boolean thumbActive;
76
77 ///////////////////////////////////////////////////
78 // ComponentUI Interface Implementation methods
79 ///////////////////////////////////////////////////
80 public static ComponentUI createUI(JComponent c) {
81 return new SynthSliderUI((JSlider)c);
82 }
83
84 public SynthSliderUI(JSlider c) {
85 super(c);
86 }
87
88 protected void installDefaults(JSlider slider) {
89 updateStyle(slider);
90 }
91
92 protected void uninstallDefaults() {
93 SynthContext context = getContext(slider, ENABLED);
94 style.uninstallDefaults(context);
95 context.dispose();
96 style = null;
97
98 context = getContext(slider, Region.SLIDER_TRACK, ENABLED);
99 sliderTrackStyle.uninstallDefaults(context);
100 context.dispose();
101 sliderTrackStyle = null;
102
103 context = getContext(slider, Region.SLIDER_THUMB, ENABLED);
104 sliderThumbStyle.uninstallDefaults(context);
105 context.dispose();
106 sliderThumbStyle = null;
107 }
108
109 protected void installListeners(JSlider slider) {
110 super.installListeners(slider);
111 slider.addPropertyChangeListener(this);
112 }
113
114 protected void uninstallListeners(JSlider slider) {
115 slider.removePropertyChangeListener(this);
116 super.uninstallListeners(slider);
117 }
118
119 private void updateStyle(JSlider c) {
120 SynthContext context = getContext(c, ENABLED);
121 SynthStyle oldStyle = style;
122 style = SynthLookAndFeel.updateStyle(context, this);
123
124 if (style != oldStyle) {
125 thumbWidth =
126 style.getInt(context, "Slider.thumbWidth", 30);
127
128 thumbHeight =
129 style.getInt(context, "Slider.thumbHeight", 14);
130
131 trackBorder =
132 style.getInt(context, "Slider.trackBorder", 1);
133
134 trackHeight = thumbHeight + trackBorder * 2;
135
136 paintValue = style.getBoolean(context,
137 "Slider.paintValue", true);
138 if (oldStyle != null) {
139 uninstallKeyboardActions(c);
140 installKeyboardActions(c);
141 }
142 }
143 context.dispose();
144
145 context = getContext(c, Region.SLIDER_TRACK, ENABLED);
146 sliderTrackStyle =
147 SynthLookAndFeel.updateStyle(context, this);
148 context.dispose();
149
150 context = getContext(c, Region.SLIDER_THUMB, ENABLED);
151 sliderThumbStyle =
152 SynthLookAndFeel.updateStyle(context, this);
153 context.dispose();
154 }
155
156 protected TrackListener createTrackListener(JSlider s) {
157 return new SynthTrackListener();
158 }
159
160 private void updateThumbState(int x, int y) {
161 setThumbActive(thumbRect.contains(x, y));
162 }
163
164 private void setThumbActive(boolean active) {
165 if (thumbActive != active) {
166 thumbActive = active;
167 slider.repaint(thumbRect);
168 }
169 }
170
171 public int getBaseline(JComponent c, int width, int height) {
172 if (c == null) {
173 throw new NullPointerException("Component must be non-null");
174 }
175 if (width < 0 || height < 0) {
176 throw new IllegalArgumentException(
177 "Width and height must be >= 0");
178 }
179 if (slider.getPaintLabels() && labelsHaveSameBaselines()) {
180 // Get the insets for the track.
181 Insets trackInsets = new Insets(0, 0, 0, 0);
182 SynthContext trackContext = getContext(slider,
183 Region.SLIDER_TRACK);
184 style.getInsets(trackContext, trackInsets);
185 trackContext.dispose();
186 if (slider.getOrientation() == JSlider.HORIZONTAL) {
187 int valueHeight = 0;
188 if (paintValue) {
189 SynthContext context = getContext(slider);
190 valueHeight = context.getStyle().getGraphicsUtils(context).
191 getMaximumCharHeight(context);
192 context.dispose();
193 }
194 int tickHeight = 0;
195 if (slider.getPaintTicks()) {
196 tickHeight = getTickLength();
197 }
198 int labelHeight = getHeightOfTallestLabel();
199 int contentHeight = valueHeight + trackHeight +
200 trackInsets.top + trackInsets.bottom +
201 tickHeight + labelHeight + 4;
202 int centerY = height / 2 - contentHeight / 2;
203 centerY += valueHeight + 2;
204 centerY += trackHeight + trackInsets.top + trackInsets.bottom;
205 centerY += tickHeight + 2;
206 Component label = (Component)slider.getLabelTable().
207 elements().nextElement();
208 Dimension pref = label.getPreferredSize();
209 return centerY + label.getBaseline(pref.width, pref.height);
210 }
211 else { // VERTICAL
212 Integer value = slider.getInverted() ? getLowestValue() :
213 getHighestValue();
214 if (value != null) {
215 int valueY = insetCache.top;
216 int valueHeight = 0;
217 if (paintValue) {
218 SynthContext context = getContext(slider);
219 valueHeight = context.getStyle().getGraphicsUtils(
220 context).getMaximumCharHeight(context);
221 context.dispose();
222 }
223 int contentHeight = height - insetCache.top -
224 insetCache.bottom;
225 int trackY = valueY + valueHeight;
226 int trackHeight = contentHeight - valueHeight;
227 int yPosition = yPositionForValue(value.intValue(), trackY,
228 trackHeight);
229 Component label = (Component)slider.getLabelTable().
230 get(value);
231 Dimension pref = label.getPreferredSize();
232 return yPosition - pref.height / 2 +
233 label.getBaseline(pref.width, pref.height);
234 }
235 }
236 }
237 return -1;
238 }
239
240 public Dimension getPreferredSize(JComponent c) {
241 recalculateIfInsetsChanged();
242 Dimension d = new Dimension(contentDim);
243 if (slider.getOrientation() == JSlider.VERTICAL) {
244 d.height = 200;
245 } else {
246 d.width = 200;
247 }
248 Insets i = slider.getInsets();
249 d.width += i.left + i.right;
250 d.height += i.top + i.bottom;
251 return d;
252 }
253
254 public Dimension getMinimumSize(JComponent c) {
255 recalculateIfInsetsChanged();
256 Dimension d = new Dimension(contentDim);
257 if (slider.getOrientation() == JSlider.VERTICAL) {
258 d.height = thumbRect.height + insetCache.top + insetCache.bottom;
259 } else {
260 d.width = thumbRect.width + insetCache.left + insetCache.right;
261 }
262 return d;
263 }
264
265 protected void calculateGeometry() {
266 layout();
267 calculateThumbLocation();
268 }
269
270 protected void layout() {
271 SynthContext context = getContext(slider);
272 SynthGraphicsUtils synthGraphics = style.getGraphicsUtils(context);
273
274 // Set the thumb size.
275 Dimension size = getThumbSize();
276 thumbRect.setSize(size.width, size.height);
277
278 // Get the insets for the track.
279 Insets trackInsets = new Insets(0, 0, 0, 0);
280 SynthContext trackContext = getContext(slider, Region.SLIDER_TRACK);
281 style.getInsets(trackContext, trackInsets);
282 trackContext.dispose();
283
284 if (slider.getOrientation() == JSlider.HORIZONTAL) {
285 // Calculate the height of all the subcomponents so we can center
286 // them.
287 valueRect.height = 0;
288 if (paintValue) {
289 valueRect.height =
290 synthGraphics.getMaximumCharHeight(context);
291 }
292
293 trackRect.height = trackHeight;
294
295 tickRect.height = 0;
296 if (slider.getPaintTicks()) {
297 tickRect.height = getTickLength();
298 }
299
300 labelRect.height = 0;
301 if (slider.getPaintLabels()) {
302 labelRect.height = getHeightOfTallestLabel();
303 }
304
305 contentDim.height = valueRect.height + trackRect.height
306 + trackInsets.top + trackInsets.bottom
307 + tickRect.height + labelRect.height + 4;
308 contentDim.width = slider.getWidth() - insetCache.left
309 - insetCache.right;
310
311 // Check if any of the labels will paint out of bounds.
312 int pad = 0;
313 if (slider.getPaintLabels()) {
314 // Calculate the track rectangle. It is necessary for
315 // xPositionForValue to return correct values.
316 trackRect.x = insetCache.left;
317 trackRect.width = contentDim.width;
318
319 Dictionary dictionary = slider.getLabelTable();
320 if (dictionary != null) {
321 int minValue = slider.getMinimum();
322 int maxValue = slider.getMaximum();
323
324 // Iterate through the keys in the dictionary and find the
325 // first and last labels indices that fall within the
326 // slider range.
327 int firstLblIdx = Integer.MAX_VALUE;
328 int lastLblIdx = Integer.MIN_VALUE;
329 for (Enumeration keys = dictionary.keys();
330 keys.hasMoreElements(); ) {
331 int keyInt = ((Integer)keys.nextElement()).intValue();
332 if (keyInt >= minValue && keyInt < firstLblIdx) {
333 firstLblIdx = keyInt;
334 }
335 if (keyInt <= maxValue && keyInt > lastLblIdx) {
336 lastLblIdx = keyInt;
337 }
338 }
339 // Calculate the pad necessary for the labels at the first
340 // and last visible indices.
341 pad = getPadForLabel(firstLblIdx);
342 pad = Math.max(pad, getPadForLabel(lastLblIdx));
343 }
344 }
345 // Calculate the painting rectangles for each of the different
346 // slider areas.
347 valueRect.x = trackRect.x = tickRect.x = labelRect.x =
348 (insetCache.left + pad);
349 valueRect.width = trackRect.width = tickRect.width =
350 labelRect.width = (contentDim.width - (pad * 2));
351
352 int centerY = slider.getHeight() / 2 - contentDim.height / 2;
353
354 valueRect.y = centerY;
355 centerY += valueRect.height + 2;
356
357 trackRect.y = centerY + trackInsets.top;
358 centerY += trackRect.height + trackInsets.top + trackInsets.bottom;
359
360 tickRect.y = centerY;
361 centerY += tickRect.height + 2;
362
363 labelRect.y = centerY;
364 centerY += labelRect.height;
365 } else {
366 // Calculate the width of all the subcomponents so we can center
367 // them.
368 trackRect.width = trackHeight;
369
370 tickRect.width = 0;
371 if (slider.getPaintTicks()) {
372 tickRect.width = getTickLength();
373 }
374
375 labelRect.width = 0;
376 if (slider.getPaintLabels()) {
377 labelRect.width = getWidthOfWidestLabel();
378 }
379
380 valueRect.y = insetCache.top;
381 valueRect.height = 0;
382 if (paintValue) {
383 valueRect.height =
384 synthGraphics.getMaximumCharHeight(context);
385 }
386
387 // Get the max width of the min or max value of the slider.
388 FontMetrics fm = slider.getFontMetrics(slider.getFont());
389 valueRect.width = Math.max(
390 synthGraphics.computeStringWidth(context, slider.getFont(),
391 fm, "" + slider.getMaximum()),
392 synthGraphics.computeStringWidth(context, slider.getFont(),
393 fm, "" + slider.getMinimum()));
394
395 int l = valueRect.width / 2;
396 int w1 = trackInsets.left + trackRect.width / 2;
397 int w2 = trackRect.width / 2 + trackInsets.right +
398 tickRect.width + labelRect.width;
399 contentDim.width = Math.max(w1, l) + Math.max(w2, l) +
400 2 + insetCache.left + insetCache.right;
401 contentDim.height = slider.getHeight() -
402 insetCache.top - insetCache.bottom;
403
404 // Layout the components.
405 trackRect.y = tickRect.y = labelRect.y =
406 valueRect.y + valueRect.height;
407 trackRect.height = tickRect.height = labelRect.height =
408 contentDim.height - valueRect.height;
409
410 int startX = slider.getWidth() / 2 - contentDim.width / 2;
411 if (SynthLookAndFeel.isLeftToRight(slider)) {
412 if (l > w1) {
413 startX += (l - w1);
414 }
415 trackRect.x = startX + trackInsets.left;
416
417 startX += trackInsets.left + trackRect.width + trackInsets.right;
418 tickRect.x = startX;
419 labelRect.x = startX + tickRect.width + 2;
420 } else {
421 if (l > w2) {
422 startX += (l - w2);
423 }
424 labelRect.x = startX;
425
426 startX += labelRect.width + 2;
427 tickRect.x = startX;
428 trackRect.x = startX + tickRect.width + trackInsets.left;
429 }
430 }
431 context.dispose();
432 }
433
434 /**
435 * Calculates the pad for the label at the specified index.
436 *
437 * @param index index of the label to calculate pad for.
438 * @return padding required to keep label visible.
439 */
440 private int getPadForLabel(int i) {
441 Dictionary dictionary = slider.getLabelTable();
442 int pad = 0;
443
444 Object o = dictionary.get(i);
445 if (o != null) {
446 Component c = (Component)o;
447 int centerX = xPositionForValue(i);
448 int cHalfWidth = c.getPreferredSize().width / 2;
449 if (centerX - cHalfWidth < insetCache.left) {
450 pad = Math.max(pad, insetCache.left - (centerX - cHalfWidth));
451 }
452
453 if (centerX + cHalfWidth > slider.getWidth() - insetCache.right) {
454 pad = Math.max(pad, (centerX + cHalfWidth) -
455 (slider.getWidth() - insetCache.right));
456 }
457 }
458 return pad;
459 }
460
461 protected void calculateThumbLocation() {
462 super.calculateThumbLocation();
463 if (slider.getOrientation() == JSlider.HORIZONTAL) {
464 thumbRect.y += trackBorder;
465 } else {
466 thumbRect.x += trackBorder;
467 }
468 Point mousePosition = slider.getMousePosition();
469 if(mousePosition != null) {
470 updateThumbState(mousePosition.x, mousePosition.y);
471 }
472 }
473
474 protected void calculateTickRect() {
475 if (slider.getOrientation() == JSlider.HORIZONTAL) {
476 tickRect.x = trackRect.x;
477 tickRect.y = trackRect.y + trackRect.height + 2 + getTickLength();
478 tickRect.width = trackRect.width;
479 tickRect.height = getTickLength();
480
481 if (!slider.getPaintTicks()) {
482 --tickRect.y;
483 tickRect.height = 0;
484 }
485 } else {
486 if (SynthLookAndFeel.isLeftToRight(slider)) {
487 tickRect.x = trackRect.x + trackRect.width;
488 tickRect.width = getTickLength();
489 } else {
490 tickRect.width = getTickLength();
491 tickRect.x = trackRect.x - tickRect.width;
492 }
493 tickRect.y = trackRect.y;
494 tickRect.height = trackRect.height;
495
496 if (!slider.getPaintTicks()) {
497 --tickRect.x;
498 tickRect.width = 0;
499 }
500 }
501 }
502
503 private static Rectangle unionRect = new Rectangle();
504
505 public void setThumbLocation(int x, int y) {
506 super.setThumbLocation(x, y);
507 // Value rect is tied to the thumb location. We need to repaint when
508 // the thumb repaints.
509 slider.repaint(valueRect.x, valueRect.y,
510 valueRect.width, valueRect.height);
511 setThumbActive(false);
512 }
513
514 protected int xPositionForValue(int value) {
515 int min = slider.getMinimum();
516 int max = slider.getMaximum();
517 int trackLeft = trackRect.x + thumbRect.width / 2 + trackBorder;
518 int trackRight = trackRect.x + trackRect.width - thumbRect.width / 2
519 - trackBorder;
520 int trackLength = trackRight - trackLeft;
521 double valueRange = (double)max - (double)min;
522 double pixelsPerValue = (double)trackLength / valueRange;
523 int xPosition;
524
525 if (!drawInverted()) {
526 xPosition = trackLeft;
527 xPosition += Math.round( pixelsPerValue * ((double)value - min));
528 } else {
529 xPosition = trackRight;
530 xPosition -= Math.round( pixelsPerValue * ((double)value - min));
531 }
532
533 xPosition = Math.max(trackLeft, xPosition);
534 xPosition = Math.min(trackRight, xPosition);
535
536 return xPosition;
537 }
538
539 protected int yPositionForValue(int value, int trackY, int trackHeight) {
540 int min = slider.getMinimum();
541 int max = slider.getMaximum();
542 int trackTop = trackY + thumbRect.height / 2 + trackBorder;
543 int trackBottom = trackY + trackHeight - thumbRect.height / 2 -
544 trackBorder;
545 int trackLength = trackBottom - trackTop;
546 double valueRange = (double)max - (double)min;
547 double pixelsPerValue = (double)trackLength / (double)valueRange;
548 int yPosition;
549
550 if (!drawInverted()) {
551 yPosition = trackTop;
552 yPosition += Math.round(pixelsPerValue * ((double)max - value));
553 } else {
554 yPosition = trackTop;
555 yPosition += Math.round(pixelsPerValue * ((double)value - min));
556 }
557
558 yPosition = Math.max(trackTop, yPosition);
559 yPosition = Math.min(trackBottom, yPosition);
560
561 return yPosition;
562 }
563
564 /**
565 * Returns a value give a y position. If yPos is past the track at the
566 * top or the bottom it will set the value to the min or max of the
567 * slider, depending if the slider is inverted or not.
568 */
569 public int valueForYPosition(int yPos) {
570 int value;
571 int minValue = slider.getMinimum();
572 int maxValue = slider.getMaximum();
573 int trackTop = trackRect.y + thumbRect.height / 2 + trackBorder;
574 int trackBottom = trackRect.y + trackRect.height
575 - thumbRect.height / 2 - trackBorder;
576 int trackLength = trackBottom - trackTop;
577
578 if (yPos <= trackTop) {
579 value = drawInverted() ? minValue : maxValue;
580 } else if (yPos >= trackBottom) {
581 value = drawInverted() ? maxValue : minValue;
582 } else {
583 int distanceFromTrackTop = yPos - trackTop;
584 double valueRange = (double)maxValue - (double)minValue;
585 double valuePerPixel = valueRange / (double)trackLength;
586 int valueFromTrackTop =
587 (int)Math.round(distanceFromTrackTop * valuePerPixel);
588 value = drawInverted() ?
589 minValue + valueFromTrackTop : maxValue - valueFromTrackTop;
590 }
591 return value;
592 }
593
594 /**
595 * Returns a value give an x position. If xPos is past the track at the
596 * left or the right it will set the value to the min or max of the
597 * slider, depending if the slider is inverted or not.
598 */
599 public int valueForXPosition(int xPos) {
600 int value;
601 int minValue = slider.getMinimum();
602 int maxValue = slider.getMaximum();
603 int trackLeft = trackRect.x + thumbRect.width / 2 + trackBorder;
604 int trackRight = trackRect.x + trackRect.width
605 - thumbRect.width / 2 - trackBorder;
606 int trackLength = trackRight - trackLeft;
607
608 if (xPos <= trackLeft) {
609 value = drawInverted() ? maxValue : minValue;
610 } else if (xPos >= trackRight) {
611 value = drawInverted() ? minValue : maxValue;
612 } else {
613 int distanceFromTrackLeft = xPos - trackLeft;
614 double valueRange = (double)maxValue - (double)minValue;
615 double valuePerPixel = valueRange / (double)trackLength;
616 int valueFromTrackLeft =
617 (int)Math.round(distanceFromTrackLeft * valuePerPixel);
618 value = drawInverted() ?
619 maxValue - valueFromTrackLeft : minValue + valueFromTrackLeft;
620 }
621 return value;
622 }
623
624 protected Dimension getThumbSize() {
625 Dimension size = new Dimension();
626
627 if (slider.getOrientation() == JSlider.VERTICAL) {
628 size.width = thumbHeight;
629 size.height = thumbWidth;
630 } else {
631 size.width = thumbWidth;
632 size.height = thumbHeight;
633 }
634 return size;
635 }
636
637 protected void recalculateIfInsetsChanged() {
638 SynthContext context = getContext(slider);
639 Insets newInsets = style.getInsets(context, null);
640 Insets compInsets = slider.getInsets();
641 newInsets.left += compInsets.left; newInsets.right += compInsets.right;
642 newInsets.top += compInsets.top; newInsets.bottom += compInsets.bottom;
643 if (!newInsets.equals(insetCache)) {
644 insetCache = newInsets;
645 calculateGeometry();
646 }
647 context.dispose();
648 }
649
650 public Region getRegion(JComponent c) {
651 return SynthLookAndFeel.getRegion(c);
652 }
653
654 public SynthContext getContext(JComponent c) {
655 return getContext(c, getComponentState(c));
656 }
657
658 public SynthContext getContext(JComponent c, int state) {
659 return SynthContext.getContext(SynthContext.class, c,
660 SynthLookAndFeel.getRegion(c), style, state);
661 }
662
663 public SynthContext getContext(JComponent c, Region subregion) {
664 return getContext(c, subregion, getComponentState(c, subregion));
665 }
666
667 private SynthContext getContext(JComponent c, Region subregion, int state) {
668 SynthStyle style = null;
669 Class klass = SynthContext.class;
670
671 if (subregion == Region.SLIDER_TRACK) {
672 style = sliderTrackStyle;
673 } else if (subregion == Region.SLIDER_THUMB) {
674 style = sliderThumbStyle;
675 }
676 return SynthContext.getContext(klass, c, subregion, style, state);
677 }
678
679 public int getComponentState(JComponent c) {
680 return SynthLookAndFeel.getComponentState(c);
681 }
682
683 private int getComponentState(JComponent c, Region region) {
684 if (region == Region.SLIDER_THUMB && thumbActive &&c.isEnabled()) {
685 return MOUSE_OVER;
686 }
687 return SynthLookAndFeel.getComponentState(c);
688 }
689
690 public void update(Graphics g, JComponent c) {
691 SynthContext context = getContext(c);
692 SynthLookAndFeel.update(context, g);
693 context.getPainter().paintSliderBackground(context,
694 g, 0, 0, c.getWidth(), c.getHeight(),
695 slider.getOrientation());
696 paint(context, g);
697 context.dispose();
698 }
699
700 public void paint(Graphics g, JComponent c) {
701 SynthContext context = getContext(c);
702 paint(context, g);
703 context.dispose();
704 }
705
706 public void paint(SynthContext context, Graphics g) {
707 recalculateIfInsetsChanged();
708 recalculateIfOrientationChanged();
709 Rectangle clip = g.getClipBounds();
710
711 if (paintValue) {
712 FontMetrics fm = SwingUtilities2.getFontMetrics(slider, g);
713 int labelWidth = context.getStyle().getGraphicsUtils(context).
714 computeStringWidth(context, g.getFont(), fm,
715 "" + slider.getValue());
716 valueRect.x = thumbRect.x + (thumbRect.width - labelWidth) / 2;
717
718 // For horizontal sliders, make sure value is not painted
719 // outside slider bounds.
720 if (slider.getOrientation() == JSlider.HORIZONTAL) {
721 if (valueRect.x + labelWidth > insetCache.left + contentDim.width) {
722 valueRect.x = (insetCache.left + contentDim.width) - labelWidth;
723 }
724 valueRect.x = Math.max(valueRect.x, 0);
725 }
726
727 g.setColor(context.getStyle().getColor(
728 context, ColorType.TEXT_FOREGROUND));
729 context.getStyle().getGraphicsUtils(context).paintText(
730 context, g, "" + slider.getValue(), valueRect.x,
731 valueRect.y, -1);
732 }
733
734 SynthContext subcontext = getContext(slider, Region.SLIDER_TRACK);
735 paintTrack(subcontext, g, trackRect);
736 subcontext.dispose();
737
738 subcontext = getContext(slider, Region.SLIDER_THUMB);
739 paintThumb(subcontext, g, thumbRect);
740 subcontext.dispose();
741
742 if (slider.getPaintTicks() && clip.intersects(tickRect)) {
743 paintTicks(g);
744 }
745
746 if (slider.getPaintLabels() && clip.intersects(labelRect)) {
747 paintLabels(g);
748 }
749 }
750
751 public void paintBorder(SynthContext context, Graphics g, int x,
752 int y, int w, int h) {
753 context.getPainter().paintSliderBorder(context, g, x, y, w, h,
754 slider.getOrientation());
755 }
756
757 public void paintThumb(SynthContext context, Graphics g,
758 Rectangle thumbBounds) {
759 int orientation = slider.getOrientation();
760 SynthLookAndFeel.updateSubregion(context, g, thumbBounds);
761 context.getPainter().paintSliderThumbBackground(context, g,
762 thumbBounds.x, thumbBounds.y, thumbBounds.width,
763 thumbBounds.height, orientation);
764 context.getPainter().paintSliderThumbBorder(context, g,
765 thumbBounds.x, thumbBounds.y, thumbBounds.width,
766 thumbBounds.height, orientation);
767 }
768
769 public void paintTrack(SynthContext context, Graphics g,
770 Rectangle trackBounds) {
771 int orientation = slider.getOrientation();
772 SynthLookAndFeel.updateSubregion(context, g, trackBounds);
773 context.getPainter().paintSliderTrackBackground(context, g,
774 trackBounds.x, trackBounds.y, trackBounds.width,
775 trackBounds.height, orientation);
776 context.getPainter().paintSliderTrackBorder(context, g,
777 trackBounds.x, trackBounds.y, trackBounds.width,
778 trackBounds.height, orientation);
779 }
780
781 public void propertyChange(PropertyChangeEvent e) {
782 if (SynthLookAndFeel.shouldUpdateStyle(e)) {
783 updateStyle((JSlider)e.getSource());
784 }
785 }
786
787 //////////////////////////////////////////////////
788 /// Track Listener Class
789 //////////////////////////////////////////////////
790 /**
791 * Track mouse movements.
792 */
793 protected class SynthTrackListener extends TrackListener {
794
795 public void mouseExited(MouseEvent e) {
796 setThumbActive(false);
797 }
798
799 public void mouseReleased(MouseEvent e) {
800 super.mouseReleased(e);
801 updateThumbState(e.getX(), e.getY());
802 }
803
804 public void mouseDragged(MouseEvent e) {
805 SynthScrollBarUI ui;
806 int thumbMiddle = 0;
807
808 if (!slider.isEnabled()) {
809 return;
810 }
811
812 currentMouseX = e.getX();
813 currentMouseY = e.getY();
814
815 if (!isDragging()) {
816 return;
817 }
818
819 slider.setValueIsAdjusting(true);
820
821 switch (slider.getOrientation()) {
822 case JSlider.VERTICAL:
823 int halfThumbHeight = thumbRect.height / 2;
824 int thumbTop = e.getY() - offset;
825 int trackTop = trackRect.y;
826 int trackBottom = trackRect.y + trackRect.height
827 - halfThumbHeight - trackBorder;
828 int vMax = yPositionForValue(slider.getMaximum() -
829 slider.getExtent());
830
831 if (drawInverted()) {
832 trackBottom = vMax;
833 } else {
834 trackTop = vMax;
835 }
836 thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight);
837 thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight);
838
839 setThumbLocation(thumbRect.x, thumbTop);
840
841 thumbMiddle = thumbTop + halfThumbHeight;
842 slider.setValue(valueForYPosition(thumbMiddle));
843 break;
844 case JSlider.HORIZONTAL:
845 int halfThumbWidth = thumbRect.width / 2;
846 int thumbLeft = e.getX() - offset;
847 int trackLeft = trackRect.x + halfThumbWidth + trackBorder;
848 int trackRight = trackRect.x + trackRect.width
849 - halfThumbWidth - trackBorder;
850 int hMax = xPositionForValue(slider.getMaximum() -
851 slider.getExtent());
852
853 if (drawInverted()) {
854 trackLeft = hMax;
855 } else {
856 trackRight = hMax;
857 }
858 thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
859 thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);
860
861 setThumbLocation(thumbLeft, thumbRect.y);
862
863 thumbMiddle = thumbLeft + halfThumbWidth;
864 slider.setValue(valueForXPosition(thumbMiddle));
865 break;
866 default:
867 return;
868 }
869
870 if (slider.getValueIsAdjusting()) {
871 setThumbActive(true);
872 }
873 }
874
875 public void mouseMoved(MouseEvent e) {
876 updateThumbState(e.getX(), e.getY());
877 }
878 }
879}