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