History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/plaf/basic/BasicSliderUI.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/plaf/basic/BasicSliderUI.java+2025−0
@@ -0,0 +1,2025 @@
1/*
2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing.plaf.basic;
27
28import java.awt.event.*;
29import java.awt.*;
30import java.beans.*;
31import java.util.Dictionary;
32import java.util.Enumeration;
33
34import javax.swing.*;
35import javax.swing.event.*;
36import javax.swing.plaf.*;
37import sun.swing.DefaultLookup;
38import sun.swing.UIAction;
39
40
41/**
42 * A Basic L&F implementation of SliderUI.
43 *
44 * @author Tom Santos
45 */
46public class BasicSliderUI extends SliderUI{
47 // Old actions forward to an instance of this.
48 private static final Actions SHARED_ACTION = new Actions();
49
50 public static final int POSITIVE_SCROLL = +1;
51 public static final int NEGATIVE_SCROLL = -1;
52 public static final int MIN_SCROLL = -2;
53 public static final int MAX_SCROLL = +2;
54
55 protected Timer scrollTimer;
56 protected JSlider slider;
57
58 protected Insets focusInsets = null;
59 protected Insets insetCache = null;
60 protected boolean leftToRightCache = true;
61 protected Rectangle focusRect = null;
62 protected Rectangle contentRect = null;
63 protected Rectangle labelRect = null;
64 protected Rectangle tickRect = null;
65 protected Rectangle trackRect = null;
66 protected Rectangle thumbRect = null;
67
68 protected int trackBuffer = 0; // The distance that the track is from the side of the control
69
70 private transient boolean isDragging;
71
72 protected TrackListener trackListener;
73 protected ChangeListener changeListener;
74 protected ComponentListener componentListener;
75 protected FocusListener focusListener;
76 protected ScrollListener scrollListener;
77 protected PropertyChangeListener propertyChangeListener;
78 private Handler handler;
79 private int lastValue;
80
81 // Colors
82 private Color shadowColor;
83 private Color highlightColor;
84 private Color focusColor;
85
86 /**
87 * Whther or not sameLabelBaselines is up to date.
88 */
89 private boolean checkedLabelBaselines;
90 /**
91 * Whether or not all the entries in the labeltable have the same
92 * baseline.
93 */
94 private boolean sameLabelBaselines;
95
96
97 protected Color getShadowColor() {
98 return shadowColor;
99 }
100
101 protected Color getHighlightColor() {
102 return highlightColor;
103 }
104
105 protected Color getFocusColor() {
106 return focusColor;
107 }
108
109 /**
110 * Returns true if the user is dragging the slider.
111 *
112 * @return true if the user is dragging the slider
113 * @since 1.5
114 */
115 protected boolean isDragging() {
116 return isDragging;
117 }
118
119 /////////////////////////////////////////////////////////////////////////////
120 // ComponentUI Interface Implementation methods
121 /////////////////////////////////////////////////////////////////////////////
122 public static ComponentUI createUI(JComponent b) {
123 return new BasicSliderUI((JSlider)b);
124 }
125
126 public BasicSliderUI(JSlider b) {
127 }
128
129 public void installUI(JComponent c) {
130 slider = (JSlider) c;
131
132 checkedLabelBaselines = false;
133
134 slider.setEnabled(slider.isEnabled());
135 LookAndFeel.installProperty(slider, "opaque", Boolean.TRUE);
136
137 isDragging = false;
138 trackListener = createTrackListener( slider );
139 changeListener = createChangeListener( slider );
140 componentListener = createComponentListener( slider );
141 focusListener = createFocusListener( slider );
142 scrollListener = createScrollListener( slider );
143 propertyChangeListener = createPropertyChangeListener( slider );
144
145 installDefaults( slider );
146 installListeners( slider );
147 installKeyboardActions( slider );
148
149 scrollTimer = new Timer( 100, scrollListener );
150 scrollTimer.setInitialDelay( 300 );
151
152 insetCache = slider.getInsets();
153 leftToRightCache = BasicGraphicsUtils.isLeftToRight(slider);
154 focusRect = new Rectangle();
155 contentRect = new Rectangle();
156 labelRect = new Rectangle();
157 tickRect = new Rectangle();
158 trackRect = new Rectangle();
159 thumbRect = new Rectangle();
160 lastValue = slider.getValue();
161
162 calculateGeometry(); // This figures out where the labels, ticks, track, and thumb are.
163 }
164
165 public void uninstallUI(JComponent c) {
166 if ( c != slider )
167 throw new IllegalComponentStateException(
168 this + " was asked to deinstall() "
169 + c + " when it only knows about "
170 + slider + ".");
171
172 scrollTimer.stop();
173 scrollTimer = null;
174
175 uninstallDefaults(slider);
176 uninstallListeners( slider );
177 uninstallKeyboardActions(slider);
178
179 insetCache = null;
180 leftToRightCache = true;
181 focusRect = null;
182 contentRect = null;
183 labelRect = null;
184 tickRect = null;
185 trackRect = null;
186 thumbRect = null;
187 trackListener = null;
188 changeListener = null;
189 componentListener = null;
190 focusListener = null;
191 scrollListener = null;
192 propertyChangeListener = null;
193 slider = null;
194 }
195
196 protected void installDefaults( JSlider slider ) {
197 LookAndFeel.installBorder(slider, "Slider.border");
198 LookAndFeel.installColorsAndFont(slider, "Slider.background",
199 "Slider.foreground", "Slider.font");
200 highlightColor = UIManager.getColor("Slider.highlight");
201
202 shadowColor = UIManager.getColor("Slider.shadow");
203 focusColor = UIManager.getColor("Slider.focus");
204
205 focusInsets = (Insets)UIManager.get( "Slider.focusInsets" );
206 // use default if missing so that BasicSliderUI can be used in other
207 // LAFs like Nimbus
208 if (focusInsets == null) focusInsets = new InsetsUIResource(2,2,2,2);
209 }
210
211 protected void uninstallDefaults(JSlider slider) {
212 LookAndFeel.uninstallBorder(slider);
213
214 focusInsets = null;
215 }
216
217 protected TrackListener createTrackListener(JSlider slider) {
218 return new TrackListener();
219 }
220
221 protected ChangeListener createChangeListener(JSlider slider) {
222 return getHandler();
223 }
224
225 protected ComponentListener createComponentListener(JSlider slider) {
226 return getHandler();
227 }
228
229 protected FocusListener createFocusListener(JSlider slider) {
230 return getHandler();
231 }
232
233 protected ScrollListener createScrollListener( JSlider slider ) {
234 return new ScrollListener();
235 }
236
237 protected PropertyChangeListener createPropertyChangeListener(
238 JSlider slider) {
239 return getHandler();
240 }
241
242 private Handler getHandler() {
243 if (handler == null) {
244 handler = new Handler();
245 }
246 return handler;
247 }
248
249 protected void installListeners( JSlider slider ) {
250 slider.addMouseListener(trackListener);
251 slider.addMouseMotionListener(trackListener);
252 slider.addFocusListener(focusListener);
253 slider.addComponentListener(componentListener);
254 slider.addPropertyChangeListener( propertyChangeListener );
255 slider.getModel().addChangeListener(changeListener);
256 }
257
258 protected void uninstallListeners( JSlider slider ) {
259 slider.removeMouseListener(trackListener);
260 slider.removeMouseMotionListener(trackListener);
261 slider.removeFocusListener(focusListener);
262 slider.removeComponentListener(componentListener);
263 slider.removePropertyChangeListener( propertyChangeListener );
264 slider.getModel().removeChangeListener(changeListener);
265 handler = null;
266 }
267
268 protected void installKeyboardActions( JSlider slider ) {
269 InputMap km = getInputMap(JComponent.WHEN_FOCUSED, slider);
270 SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, km);
271 LazyActionMap.installLazyActionMap(slider, BasicSliderUI.class,
272 "Slider.actionMap");
273 }
274
275 InputMap getInputMap(int condition, JSlider slider) {
276 if (condition == JComponent.WHEN_FOCUSED) {
277 InputMap keyMap = (InputMap)DefaultLookup.get(slider, this,
278 "Slider.focusInputMap");
279 InputMap rtlKeyMap;
280
281 if (slider.getComponentOrientation().isLeftToRight() ||
282 ((rtlKeyMap = (InputMap)DefaultLookup.get(slider, this,
283 "Slider.focusInputMap.RightToLeft")) == null)) {
284 return keyMap;
285 } else {
286 rtlKeyMap.setParent(keyMap);
287 return rtlKeyMap;
288 }
289 }
290 return null;
291 }
292
293 /**
294 * Populates ComboBox's actions.
295 */
296 static void loadActionMap(LazyActionMap map) {
297 map.put(new Actions(Actions.POSITIVE_UNIT_INCREMENT));
298 map.put(new Actions(Actions.POSITIVE_BLOCK_INCREMENT));
299 map.put(new Actions(Actions.NEGATIVE_UNIT_INCREMENT));
300 map.put(new Actions(Actions.NEGATIVE_BLOCK_INCREMENT));
301 map.put(new Actions(Actions.MIN_SCROLL_INCREMENT));
302 map.put(new Actions(Actions.MAX_SCROLL_INCREMENT));
303 }
304
305 protected void uninstallKeyboardActions( JSlider slider ) {
306 SwingUtilities.replaceUIActionMap(slider, null);
307 SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED,
308 null);
309 }
310
311
312 /**
313 * Returns the baseline.
314 *
315 * @throws NullPointerException {@inheritDoc}
316 * @throws IllegalArgumentException {@inheritDoc}
317 * @see javax.swing.JComponent#getBaseline(int, int)
318 * @since 1.6
319 */
320 public int getBaseline(JComponent c, int width, int height) {
321 super.getBaseline(c, width, height);
322 if (slider.getPaintLabels() && labelsHaveSameBaselines()) {
323 FontMetrics metrics = slider.getFontMetrics(slider.getFont());
324 Insets insets = slider.getInsets();
325 Dimension thumbSize = getThumbSize();
326 if (slider.getOrientation() == JSlider.HORIZONTAL) {
327 int tickLength = getTickLength();
328 int contentHeight = height - insets.top - insets.bottom -
329 focusInsets.top - focusInsets.bottom;
330 int thumbHeight = thumbSize.height;
331 int centerSpacing = thumbHeight;
332 if (slider.getPaintTicks()) {
333 centerSpacing += tickLength;
334 }
335 // Assume uniform labels.
336 centerSpacing += getHeightOfTallestLabel();
337 int trackY = insets.top + focusInsets.top +
338 (contentHeight - centerSpacing - 1) / 2;
339 int trackHeight = thumbHeight;
340 int tickY = trackY + trackHeight;
341 int tickHeight = tickLength;
342 if (!slider.getPaintTicks()) {
343 tickHeight = 0;
344 }
345 int labelY = tickY + tickHeight;
346 return labelY + metrics.getAscent();
347 }
348 else { // vertical
349 boolean inverted = slider.getInverted();
350 Integer value = inverted ? getLowestValue() :
351 getHighestValue();
352 if (value != null) {
353 int thumbHeight = thumbSize.height;
354 int trackBuffer = Math.max(metrics.getHeight() / 2,
355 thumbHeight / 2);
356 int contentY = focusInsets.top + insets.top;
357 int trackY = contentY + trackBuffer;
358 int trackHeight = height - focusInsets.top -
359 focusInsets.bottom - insets.top - insets.bottom -
360 trackBuffer - trackBuffer;
361 int yPosition = yPositionForValue(value, trackY,
362 trackHeight);
363 return yPosition - metrics.getHeight() / 2 +
364 metrics.getAscent();
365 }
366 }
367 }
368 return 0;
369 }
370
371 /**
372 * Returns an enum indicating how the baseline of the component
373 * changes as the size changes.
374 *
375 * @throws NullPointerException {@inheritDoc}
376 * @see javax.swing.JComponent#getBaseline(int, int)
377 * @since 1.6
378 */
379 public Component.BaselineResizeBehavior getBaselineResizeBehavior(
380 JComponent c) {
381 super.getBaselineResizeBehavior(c);
382 // NOTE: BasicSpinner really provides for CENTER_OFFSET, but
383 // the default min/pref size is smaller than it should be
384 // so that getBaseline() doesn't implement the contract
385 // for CENTER_OFFSET as defined in Component.
386 return Component.BaselineResizeBehavior.OTHER;
387 }
388
389 /**
390 * Returns true if all the labels from the label table have the same
391 * baseline.
392 *
393 * @return true if all the labels from the label table have the
394 * same baseline
395 * @since 1.6
396 */
397 protected boolean labelsHaveSameBaselines() {
398 if (!checkedLabelBaselines) {
399 checkedLabelBaselines = true;
400 Dictionary dictionary = slider.getLabelTable();
401 if (dictionary != null) {
402 sameLabelBaselines = true;
403 Enumeration elements = dictionary.elements();
404 int baseline = -1;
405 while (elements.hasMoreElements()) {
406 JComponent label = (JComponent) elements.nextElement();
407 Dimension pref = label.getPreferredSize();
408 int labelBaseline = label.getBaseline(pref.width,
409 pref.height);
410 if (labelBaseline >= 0) {
411 if (baseline == -1) {
412 baseline = labelBaseline;
413 }
414 else if (baseline != labelBaseline) {
415 sameLabelBaselines = false;
416 break;
417 }
418 }
419 else {
420 sameLabelBaselines = false;
421 break;
422 }
423 }
424 }
425 else {
426 sameLabelBaselines = false;
427 }
428 }
429 return sameLabelBaselines;
430 }
431
432 public Dimension getPreferredHorizontalSize() {
433 Dimension horizDim = (Dimension)DefaultLookup.get(slider,
434 this, "Slider.horizontalSize");
435 if (horizDim == null) {
436 horizDim = new Dimension(200, 21);
437 }
438 return horizDim;
439 }
440
441 public Dimension getPreferredVerticalSize() {
442 Dimension vertDim = (Dimension)DefaultLookup.get(slider,
443 this, "Slider.verticalSize");
444 if (vertDim == null) {
445 vertDim = new Dimension(21, 200);
446 }
447 return vertDim;
448 }
449
450 public Dimension getMinimumHorizontalSize() {
451 Dimension minHorizDim = (Dimension)DefaultLookup.get(slider,
452 this, "Slider.minimumHorizontalSize");
453 if (minHorizDim == null) {
454 minHorizDim = new Dimension(36, 21);
455 }
456 return minHorizDim;
457 }
458
459 public Dimension getMinimumVerticalSize() {
460 Dimension minVertDim = (Dimension)DefaultLookup.get(slider,
461 this, "Slider.minimumVerticalSize");
462 if (minVertDim == null) {
463 minVertDim = new Dimension(21, 36);
464 }
465 return minVertDim;
466 }
467
468 public Dimension getPreferredSize(JComponent c) {
469 recalculateIfInsetsChanged();
470 Dimension d;
471 if ( slider.getOrientation() == JSlider.VERTICAL ) {
472 d = new Dimension(getPreferredVerticalSize());
473 d.width = insetCache.left + insetCache.right;
474 d.width += focusInsets.left + focusInsets.right;
475 d.width += trackRect.width + tickRect.width + labelRect.width;
476 }
477 else {
478 d = new Dimension(getPreferredHorizontalSize());
479 d.height = insetCache.top + insetCache.bottom;
480 d.height += focusInsets.top + focusInsets.bottom;
481 d.height += trackRect.height + tickRect.height + labelRect.height;
482 }
483
484 return d;
485 }
486
487 public Dimension getMinimumSize(JComponent c) {
488 recalculateIfInsetsChanged();
489 Dimension d;
490
491 if ( slider.getOrientation() == JSlider.VERTICAL ) {
492 d = new Dimension(getMinimumVerticalSize());
493 d.width = insetCache.left + insetCache.right;
494 d.width += focusInsets.left + focusInsets.right;
495 d.width += trackRect.width + tickRect.width + labelRect.width;
496 }
497 else {
498 d = new Dimension(getMinimumHorizontalSize());
499 d.height = insetCache.top + insetCache.bottom;
500 d.height += focusInsets.top + focusInsets.bottom;
501 d.height += trackRect.height + tickRect.height + labelRect.height;
502 }
503
504 return d;
505 }
506
507 public Dimension getMaximumSize(JComponent c) {
508 Dimension d = getPreferredSize(c);
509 if ( slider.getOrientation() == JSlider.VERTICAL ) {
510 d.height = Short.MAX_VALUE;
511 }
512 else {
513 d.width = Short.MAX_VALUE;
514 }
515
516 return d;
517 }
518
519 protected void calculateGeometry() {
520 calculateFocusRect();
521 calculateContentRect();
522 calculateThumbSize();
523 calculateTrackBuffer();
524 calculateTrackRect();
525 calculateTickRect();
526 calculateLabelRect();
527 calculateThumbLocation();
528 }
529
530 protected void calculateFocusRect() {
531 focusRect.x = insetCache.left;
532 focusRect.y = insetCache.top;
533 focusRect.width = slider.getWidth() - (insetCache.left + insetCache.right);
534 focusRect.height = slider.getHeight() - (insetCache.top + insetCache.bottom);
535 }
536
537 protected void calculateThumbSize() {
538 Dimension size = getThumbSize();
539 thumbRect.setSize( size.width, size.height );
540 }
541
542 protected void calculateContentRect() {
543 contentRect.x = focusRect.x + focusInsets.left;
544 contentRect.y = focusRect.y + focusInsets.top;
545 contentRect.width = focusRect.width - (focusInsets.left + focusInsets.right);
546 contentRect.height = focusRect.height - (focusInsets.top + focusInsets.bottom);
547 }
548
549 private int getTickSpacing() {
550 int majorTickSpacing = slider.getMajorTickSpacing();
551 int minorTickSpacing = slider.getMinorTickSpacing();
552
553 int result;
554
555 if (minorTickSpacing > 0) {
556 result = minorTickSpacing;
557 } else if (majorTickSpacing > 0) {
558 result = majorTickSpacing;
559 } else {
560 result = 0;
561 }
562
563 return result;
564 }
565
566 protected void calculateThumbLocation() {
567 if ( slider.getSnapToTicks() ) {
568 int sliderValue = slider.getValue();
569 int snappedValue = sliderValue;
570 int tickSpacing = getTickSpacing();
571
572 if ( tickSpacing != 0 ) {
573 // If it's not on a tick, change the value
574 if ( (sliderValue - slider.getMinimum()) % tickSpacing != 0 ) {
575 float temp = (float)(sliderValue - slider.getMinimum()) / (float)tickSpacing;
576 int whichTick = Math.round( temp );
577
578 // This is the fix for the bug #6401380
579 if (temp - (int)temp == .5 && sliderValue < lastValue) {
580 whichTick --;
581 }
582 snappedValue = slider.getMinimum() + (whichTick * tickSpacing);
583 }
584
585 if( snappedValue != sliderValue ) {
586 slider.setValue( snappedValue );
587 }
588 }
589 }
590
591 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
592 int valuePosition = xPositionForValue(slider.getValue());
593
594 thumbRect.x = valuePosition - (thumbRect.width / 2);
595 thumbRect.y = trackRect.y;
596 }
597 else {
598 int valuePosition = yPositionForValue(slider.getValue());
599
600 thumbRect.x = trackRect.x;
601 thumbRect.y = valuePosition - (thumbRect.height / 2);
602 }
603 }
604
605 protected void calculateTrackBuffer() {
606 if ( slider.getPaintLabels() && slider.getLabelTable() != null ) {
607 Component highLabel = getHighestValueLabel();
608 Component lowLabel = getLowestValueLabel();
609
610 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
611 trackBuffer = Math.max( highLabel.getBounds().width, lowLabel.getBounds().width ) / 2;
612 trackBuffer = Math.max( trackBuffer, thumbRect.width / 2 );
613 }
614 else {
615 trackBuffer = Math.max( highLabel.getBounds().height, lowLabel.getBounds().height ) / 2;
616 trackBuffer = Math.max( trackBuffer, thumbRect.height / 2 );
617 }
618 }
619 else {
620 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
621 trackBuffer = thumbRect.width / 2;
622 }
623 else {
624 trackBuffer = thumbRect.height / 2;
625 }
626 }
627 }
628
629
630 protected void calculateTrackRect() {
631 int centerSpacing; // used to center sliders added using BorderLayout.CENTER (bug 4275631)
632 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
633 centerSpacing = thumbRect.height;
634 if ( slider.getPaintTicks() ) centerSpacing += getTickLength();
635 if ( slider.getPaintLabels() ) centerSpacing += getHeightOfTallestLabel();
636 trackRect.x = contentRect.x + trackBuffer;
637 trackRect.y = contentRect.y + (contentRect.height - centerSpacing - 1)/2;
638 trackRect.width = contentRect.width - (trackBuffer * 2);
639 trackRect.height = thumbRect.height;
640 }
641 else {
642 centerSpacing = thumbRect.width;
643 if (BasicGraphicsUtils.isLeftToRight(slider)) {
644 if ( slider.getPaintTicks() ) centerSpacing += getTickLength();
645 if ( slider.getPaintLabels() ) centerSpacing += getWidthOfWidestLabel();
646 } else {
647 if ( slider.getPaintTicks() ) centerSpacing -= getTickLength();
648 if ( slider.getPaintLabels() ) centerSpacing -= getWidthOfWidestLabel();
649 }
650 trackRect.x = contentRect.x + (contentRect.width - centerSpacing - 1)/2;
651 trackRect.y = contentRect.y + trackBuffer;
652 trackRect.width = thumbRect.width;
653 trackRect.height = contentRect.height - (trackBuffer * 2);
654 }
655
656 }
657
658 /**
659 * Gets the height of the tick area for horizontal sliders and the width of the
660 * tick area for vertical sliders. BasicSliderUI uses the returned value to
661 * determine the tick area rectangle. If you want to give your ticks some room,
662 * make this larger than you need and paint your ticks away from the sides in paintTicks().
663 */
664 protected int getTickLength() {
665 return 8;
666 }
667
668 protected void calculateTickRect() {
669 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
670 tickRect.x = trackRect.x;
671 tickRect.y = trackRect.y + trackRect.height;
672 tickRect.width = trackRect.width;
673 tickRect.height = (slider.getPaintTicks()) ? getTickLength() : 0;
674 }
675 else {
676 tickRect.width = (slider.getPaintTicks()) ? getTickLength() : 0;
677 if(BasicGraphicsUtils.isLeftToRight(slider)) {
678 tickRect.x = trackRect.x + trackRect.width;
679 }
680 else {
681 tickRect.x = trackRect.x - tickRect.width;
682 }
683 tickRect.y = trackRect.y;
684 tickRect.height = trackRect.height;
685 }
686 }
687
688 protected void calculateLabelRect() {
689 if ( slider.getPaintLabels() ) {
690 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
691 labelRect.x = tickRect.x - trackBuffer;
692 labelRect.y = tickRect.y + tickRect.height;
693 labelRect.width = tickRect.width + (trackBuffer * 2);
694 labelRect.height = getHeightOfTallestLabel();
695 }
696 else {
697 if(BasicGraphicsUtils.isLeftToRight(slider)) {
698 labelRect.x = tickRect.x + tickRect.width;
699 labelRect.width = getWidthOfWidestLabel();
700 }
701 else {
702 labelRect.width = getWidthOfWidestLabel();
703 labelRect.x = tickRect.x - labelRect.width;
704 }
705 labelRect.y = tickRect.y - trackBuffer;
706 labelRect.height = tickRect.height + (trackBuffer * 2);
707 }
708 }
709 else {
710 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
711 labelRect.x = tickRect.x;
712 labelRect.y = tickRect.y + tickRect.height;
713 labelRect.width = tickRect.width;
714 labelRect.height = 0;
715 }
716 else {
717 if(BasicGraphicsUtils.isLeftToRight(slider)) {
718 labelRect.x = tickRect.x + tickRect.width;
719 }
720 else {
721 labelRect.x = tickRect.x;
722 }
723 labelRect.y = tickRect.y;
724 labelRect.width = 0;
725 labelRect.height = tickRect.height;
726 }
727 }
728 }
729
730 protected Dimension getThumbSize() {
731 Dimension size = new Dimension();
732
733 if ( slider.getOrientation() == JSlider.VERTICAL ) {
734 size.width = 20;
735 size.height = 11;
736 }
737 else {
738 size.width = 11;
739 size.height = 20;
740 }
741
742 return size;
743 }
744
745 public class PropertyChangeHandler implements PropertyChangeListener {
746 // NOTE: This class exists only for backward compatability. All
747 // its functionality has been moved into Handler. If you need to add
748 // new functionality add it to the Handler, but make sure this
749 // class calls into the Handler.
750 public void propertyChange( PropertyChangeEvent e ) {
751 getHandler().propertyChange(e);
752 }
753 }
754
755 protected int getWidthOfWidestLabel() {
756 Dictionary dictionary = slider.getLabelTable();
757 int widest = 0;
758 if ( dictionary != null ) {
759 Enumeration keys = dictionary.keys();
760 while ( keys.hasMoreElements() ) {
761 JComponent label = (JComponent) dictionary.get(keys.nextElement());
762 widest = Math.max( label.getPreferredSize().width, widest );
763 }
764 }
765 return widest;
766 }
767
768 protected int getHeightOfTallestLabel() {
769 Dictionary dictionary = slider.getLabelTable();
770 int tallest = 0;
771 if ( dictionary != null ) {
772 Enumeration keys = dictionary.keys();
773 while ( keys.hasMoreElements() ) {
774 JComponent label = (JComponent) dictionary.get(keys.nextElement());
775 tallest = Math.max( label.getPreferredSize().height, tallest );
776 }
777 }
778 return tallest;
779 }
780
781 protected int getWidthOfHighValueLabel() {
782 Component label = getHighestValueLabel();
783 int width = 0;
784
785 if ( label != null ) {
786 width = label.getPreferredSize().width;
787 }
788
789 return width;
790 }
791
792 protected int getWidthOfLowValueLabel() {
793 Component label = getLowestValueLabel();
794 int width = 0;
795
796 if ( label != null ) {
797 width = label.getPreferredSize().width;
798 }
799
800 return width;
801 }
802
803 protected int getHeightOfHighValueLabel() {
804 Component label = getHighestValueLabel();
805 int height = 0;
806
807 if ( label != null ) {
808 height = label.getPreferredSize().height;
809 }
810
811 return height;
812 }
813
814 protected int getHeightOfLowValueLabel() {
815 Component label = getLowestValueLabel();
816 int height = 0;
817
818 if ( label != null ) {
819 height = label.getPreferredSize().height;
820 }
821
822 return height;
823 }
824
825 protected boolean drawInverted() {
826 if (slider.getOrientation()==JSlider.HORIZONTAL) {
827 if(BasicGraphicsUtils.isLeftToRight(slider)) {
828 return slider.getInverted();
829 } else {
830 return !slider.getInverted();
831 }
832 } else {
833 return slider.getInverted();
834 }
835 }
836
837 /**
838 * Returns the biggest value that has an entry in the label table.
839 *
840 * @return biggest value that has an entry in the label table, or
841 * null.
842 * @since 1.6
843 */
844 protected Integer getHighestValue() {
845 Dictionary dictionary = slider.getLabelTable();
846
847 if (dictionary == null) {
848 return null;
849 }
850
851 Enumeration keys = dictionary.keys();
852
853 Integer max = null;
854
855 while (keys.hasMoreElements()) {
856 Integer i = (Integer) keys.nextElement();
857
858 if (max == null || i > max) {
859 max = i;
860 }
861 }
862
863 return max;
864 }
865
866 /**
867 * Returns the smallest value that has an entry in the label table.
868 *
869 * @return smallest value that has an entry in the label table, or
870 * null.
871 * @since 1.6
872 */
873 protected Integer getLowestValue() {
874 Dictionary dictionary = slider.getLabelTable();
875
876 if (dictionary == null) {
877 return null;
878 }
879
880 Enumeration keys = dictionary.keys();
881
882 Integer min = null;
883
884 while (keys.hasMoreElements()) {
885 Integer i = (Integer) keys.nextElement();
886
887 if (min == null || i < min) {
888 min = i;
889 }
890 }
891
892 return min;
893 }
894
895
896 /**
897 * Returns the label that corresponds to the highest slider value in the label table.
898 * @see JSlider#setLabelTable
899 */
900 protected Component getLowestValueLabel() {
901 Integer min = getLowestValue();
902 if (min != null) {
903 return (Component)slider.getLabelTable().get(min);
904 }
905 return null;
906 }
907
908 /**
909 * Returns the label that corresponds to the lowest slider value in the label table.
910 * @see JSlider#setLabelTable
911 */
912 protected Component getHighestValueLabel() {
913 Integer max = getHighestValue();
914 if (max != null) {
915 return (Component)slider.getLabelTable().get(max);
916 }
917 return null;
918 }
919
920 public void paint( Graphics g, JComponent c ) {
921 recalculateIfInsetsChanged();
922 recalculateIfOrientationChanged();
923 Rectangle clip = g.getClipBounds();
924
925 if ( !clip.intersects(trackRect) && slider.getPaintTrack())
926 calculateGeometry();
927
928 if ( slider.getPaintTrack() && clip.intersects( trackRect ) ) {
929 paintTrack( g );
930 }
931 if ( slider.getPaintTicks() && clip.intersects( tickRect ) ) {
932 paintTicks( g );
933 }
934 if ( slider.getPaintLabels() && clip.intersects( labelRect ) ) {
935 paintLabels( g );
936 }
937 if ( slider.hasFocus() && clip.intersects( focusRect ) ) {
938 paintFocus( g );
939 }
940 if ( clip.intersects( thumbRect ) ) {
941 paintThumb( g );
942 }
943 }
944
945 protected void recalculateIfInsetsChanged() {
946 Insets newInsets = slider.getInsets();
947 if ( !newInsets.equals( insetCache ) ) {
948 insetCache = newInsets;
949 calculateGeometry();
950 }
951 }
952
953 protected void recalculateIfOrientationChanged() {
954 boolean ltr = BasicGraphicsUtils.isLeftToRight(slider);
955 if ( ltr!=leftToRightCache ) {
956 leftToRightCache = ltr;
957 calculateGeometry();
958 }
959 }
960
961 public void paintFocus(Graphics g) {
962 g.setColor( getFocusColor() );
963
964 BasicGraphicsUtils.drawDashedRect( g, focusRect.x, focusRect.y,
965 focusRect.width, focusRect.height );
966 }
967
968 public void paintTrack(Graphics g) {
969
970 Rectangle trackBounds = trackRect;
971
972 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
973 int cy = (trackBounds.height / 2) - 2;
974 int cw = trackBounds.width;
975
976 g.translate(trackBounds.x, trackBounds.y + cy);
977
978 g.setColor(getShadowColor());
979 g.drawLine(0, 0, cw - 1, 0);
980 g.drawLine(0, 1, 0, 2);
981 g.setColor(getHighlightColor());
982 g.drawLine(0, 3, cw, 3);
983 g.drawLine(cw, 0, cw, 3);
984 g.setColor(Color.black);
985 g.drawLine(1, 1, cw-2, 1);
986
987 g.translate(-trackBounds.x, -(trackBounds.y + cy));
988 }
989 else {
990 int cx = (trackBounds.width / 2) - 2;
991 int ch = trackBounds.height;
992
993 g.translate(trackBounds.x + cx, trackBounds.y);
994
995 g.setColor(getShadowColor());
996 g.drawLine(0, 0, 0, ch - 1);
997 g.drawLine(1, 0, 2, 0);
998 g.setColor(getHighlightColor());
999 g.drawLine(3, 0, 3, ch);
1000 g.drawLine(0, ch, 3, ch);
1001 g.setColor(Color.black);
1002 g.drawLine(1, 1, 1, ch-2);
1003
1004 g.translate(-(trackBounds.x + cx), -trackBounds.y);
1005 }
1006 }
1007
1008 public void paintTicks(Graphics g) {
1009 Rectangle tickBounds = tickRect;
1010
1011 g.setColor(DefaultLookup.getColor(slider, this, "Slider.tickColor", Color.black));
1012
1013 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
1014 g.translate(0, tickBounds.y);
1015
1016 if (slider.getMinorTickSpacing() > 0) {
1017 int value = slider.getMinimum();
1018
1019 while ( value <= slider.getMaximum() ) {
1020 int xPos = xPositionForValue(value);
1021 paintMinorTickForHorizSlider( g, tickBounds, xPos );
1022
1023 // Overflow checking
1024 if (Integer.MAX_VALUE - slider.getMinorTickSpacing() < value) {
1025 break;
1026 }
1027
1028 value += slider.getMinorTickSpacing();
1029 }
1030 }
1031
1032 if (slider.getMajorTickSpacing() > 0) {
1033 int value = slider.getMinimum();
1034
1035 while ( value <= slider.getMaximum() ) {
1036 int xPos = xPositionForValue(value);
1037 paintMajorTickForHorizSlider( g, tickBounds, xPos );
1038
1039 // Overflow checking
1040 if (Integer.MAX_VALUE - slider.getMajorTickSpacing() < value) {
1041 break;
1042 }
1043
1044 value += slider.getMajorTickSpacing();
1045 }
1046 }
1047
1048 g.translate( 0, -tickBounds.y);
1049 } else {
1050 g.translate(tickBounds.x, 0);
1051
1052 if (slider.getMinorTickSpacing() > 0) {
1053 int offset = 0;
1054 if(!BasicGraphicsUtils.isLeftToRight(slider)) {
1055 offset = tickBounds.width - tickBounds.width / 2;
1056 g.translate(offset, 0);
1057 }
1058
1059 int value = slider.getMinimum();
1060
1061 while (value <= slider.getMaximum()) {
1062 int yPos = yPositionForValue(value);
1063 paintMinorTickForVertSlider( g, tickBounds, yPos );
1064
1065 // Overflow checking
1066 if (Integer.MAX_VALUE - slider.getMinorTickSpacing() < value) {
1067 break;
1068 }
1069
1070 value += slider.getMinorTickSpacing();
1071 }
1072
1073 if(!BasicGraphicsUtils.isLeftToRight(slider)) {
1074 g.translate(-offset, 0);
1075 }
1076 }
1077
1078 if (slider.getMajorTickSpacing() > 0) {
1079 if(!BasicGraphicsUtils.isLeftToRight(slider)) {
1080 g.translate(2, 0);
1081 }
1082
1083 int value = slider.getMinimum();
1084
1085 while (value <= slider.getMaximum()) {
1086 int yPos = yPositionForValue(value);
1087 paintMajorTickForVertSlider( g, tickBounds, yPos );
1088
1089 // Overflow checking
1090 if (Integer.MAX_VALUE - slider.getMajorTickSpacing() < value) {
1091 break;
1092 }
1093
1094 value += slider.getMajorTickSpacing();
1095 }
1096
1097 if(!BasicGraphicsUtils.isLeftToRight(slider)) {
1098 g.translate(-2, 0);
1099 }
1100 }
1101 g.translate(-tickBounds.x, 0);
1102 }
1103 }
1104
1105 protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
1106 g.drawLine( x, 0, x, tickBounds.height / 2 - 1 );
1107 }
1108
1109 protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
1110 g.drawLine( x, 0, x, tickBounds.height - 2 );
1111 }
1112
1113 protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
1114 g.drawLine( 0, y, tickBounds.width / 2 - 1, y );
1115 }
1116
1117 protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
1118 g.drawLine( 0, y, tickBounds.width - 2, y );
1119 }
1120
1121 public void paintLabels( Graphics g ) {
1122 Rectangle labelBounds = labelRect;
1123
1124 Dictionary dictionary = slider.getLabelTable();
1125 if ( dictionary != null ) {
1126 Enumeration keys = dictionary.keys();
1127 int minValue = slider.getMinimum();
1128 int maxValue = slider.getMaximum();
1129 boolean enabled = slider.isEnabled();
1130 while ( keys.hasMoreElements() ) {
1131 Integer key = (Integer)keys.nextElement();
1132 int value = key.intValue();
1133 if (value >= minValue && value <= maxValue) {
1134 JComponent label = (JComponent) dictionary.get(key);
1135 label.setEnabled(enabled);
1136
1137 if (label instanceof JLabel) {
1138 Icon icon = label.isEnabled() ? ((JLabel) label).getIcon() : ((JLabel) label).getDisabledIcon();
1139
1140 if (icon instanceof ImageIcon) {
1141 // Register Slider as an image observer. It allows to catch notifications about
1142 // image changes (e.g. gif animation)
1143 Toolkit.getDefaultToolkit().checkImage(((ImageIcon) icon).getImage(), -1, -1, slider);
1144 }
1145 }
1146
1147 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
1148 g.translate( 0, labelBounds.y );
1149 paintHorizontalLabel( g, value, label );
1150 g.translate( 0, -labelBounds.y );
1151 }
1152 else {
1153 int offset = 0;
1154 if (!BasicGraphicsUtils.isLeftToRight(slider)) {
1155 offset = labelBounds.width -
1156 label.getPreferredSize().width;
1157 }
1158 g.translate( labelBounds.x + offset, 0 );
1159 paintVerticalLabel( g, value, label );
1160 g.translate( -labelBounds.x - offset, 0 );
1161 }
1162 }
1163 }
1164 }
1165
1166 }
1167
1168 /**
1169 * Called for every label in the label table. Used to draw the labels for horizontal sliders.
1170 * The graphics have been translated to labelRect.y already.
1171 * @see JSlider#setLabelTable
1172 */
1173 protected void paintHorizontalLabel( Graphics g, int value, Component label ) {
1174 int labelCenter = xPositionForValue( value );
1175 int labelLeft = labelCenter - (label.getPreferredSize().width / 2);
1176 g.translate( labelLeft, 0 );
1177 label.paint( g );
1178 g.translate( -labelLeft, 0 );
1179 }
1180
1181 /**
1182 * Called for every label in the label table. Used to draw the labels for vertical sliders.
1183 * The graphics have been translated to labelRect.x already.
1184 * @see JSlider#setLabelTable
1185 */
1186 protected void paintVerticalLabel( Graphics g, int value, Component label ) {
1187 int labelCenter = yPositionForValue( value );
1188 int labelTop = labelCenter - (label.getPreferredSize().height / 2);
1189 g.translate( 0, labelTop );
1190 label.paint( g );
1191 g.translate( 0, -labelTop );
1192 }
1193
1194 public void paintThumb(Graphics g) {
1195 Rectangle knobBounds = thumbRect;
1196 int w = knobBounds.width;
1197 int h = knobBounds.height;
1198
1199 g.translate(knobBounds.x, knobBounds.y);
1200
1201 if ( slider.isEnabled() ) {
1202 g.setColor(slider.getBackground());
1203 }
1204 else {
1205 g.setColor(slider.getBackground().darker());
1206 }
1207
1208 Boolean paintThumbArrowShape =
1209 (Boolean)slider.getClientProperty("Slider.paintThumbArrowShape");
1210
1211 if ((!slider.getPaintTicks() && paintThumbArrowShape == null) ||
1212 paintThumbArrowShape == Boolean.FALSE) {
1213
1214 // "plain" version
1215 g.fillRect(0, 0, w, h);
1216
1217 g.setColor(Color.black);
1218 g.drawLine(0, h-1, w-1, h-1);
1219 g.drawLine(w-1, 0, w-1, h-1);
1220
1221 g.setColor(highlightColor);
1222 g.drawLine(0, 0, 0, h-2);
1223 g.drawLine(1, 0, w-2, 0);
1224
1225 g.setColor(shadowColor);
1226 g.drawLine(1, h-2, w-2, h-2);
1227 g.drawLine(w-2, 1, w-2, h-3);
1228 }
1229 else if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
1230 int cw = w / 2;
1231 g.fillRect(1, 1, w-3, h-1-cw);
1232 Polygon p = new Polygon();
1233 p.addPoint(1, h-cw);
1234 p.addPoint(cw-1, h-1);
1235 p.addPoint(w-2, h-1-cw);
1236 g.fillPolygon(p);
1237
1238 g.setColor(highlightColor);
1239 g.drawLine(0, 0, w-2, 0);
1240 g.drawLine(0, 1, 0, h-1-cw);
1241 g.drawLine(0, h-cw, cw-1, h-1);
1242
1243 g.setColor(Color.black);
1244 g.drawLine(w-1, 0, w-1, h-2-cw);
1245 g.drawLine(w-1, h-1-cw, w-1-cw, h-1);
1246
1247 g.setColor(shadowColor);
1248 g.drawLine(w-2, 1, w-2, h-2-cw);
1249 g.drawLine(w-2, h-1-cw, w-1-cw, h-2);
1250 }
1251 else { // vertical
1252 int cw = h / 2;
1253 if(BasicGraphicsUtils.isLeftToRight(slider)) {
1254 g.fillRect(1, 1, w-1-cw, h-3);
1255 Polygon p = new Polygon();
1256 p.addPoint(w-cw-1, 0);
1257 p.addPoint(w-1, cw);
1258 p.addPoint(w-1-cw, h-2);
1259 g.fillPolygon(p);
1260
1261 g.setColor(highlightColor);
1262 g.drawLine(0, 0, 0, h - 2); // left
1263 g.drawLine(1, 0, w-1-cw, 0); // top
1264 g.drawLine(w-cw-1, 0, w-1, cw); // top slant
1265
1266 g.setColor(Color.black);
1267 g.drawLine(0, h-1, w-2-cw, h-1); // bottom
1268 g.drawLine(w-1-cw, h-1, w-1, h-1-cw); // bottom slant
1269
1270 g.setColor(shadowColor);
1271 g.drawLine(1, h-2, w-2-cw, h-2 ); // bottom
1272 g.drawLine(w-1-cw, h-2, w-2, h-cw-1 ); // bottom slant
1273 }
1274 else {
1275 g.fillRect(5, 1, w-1-cw, h-3);
1276 Polygon p = new Polygon();
1277 p.addPoint(cw, 0);
1278 p.addPoint(0, cw);
1279 p.addPoint(cw, h-2);
1280 g.fillPolygon(p);
1281
1282 g.setColor(highlightColor);
1283 g.drawLine(cw-1, 0, w-2, 0); // top
1284 g.drawLine(0, cw, cw, 0); // top slant
1285
1286 g.setColor(Color.black);
1287 g.drawLine(0, h-1-cw, cw, h-1 ); // bottom slant
1288 g.drawLine(cw, h-1, w-1, h-1); // bottom
1289
1290 g.setColor(shadowColor);
1291 g.drawLine(cw, h-2, w-2, h-2 ); // bottom
1292 g.drawLine(w-1, 1, w-1, h-2 ); // right
1293 }
1294 }
1295
1296 g.translate(-knobBounds.x, -knobBounds.y);
1297 }
1298
1299 // Used exclusively by setThumbLocation()
1300 private static Rectangle unionRect = new Rectangle();
1301
1302 public void setThumbLocation(int x, int y) {
1303 unionRect.setBounds( thumbRect );
1304
1305 thumbRect.setLocation( x, y );
1306
1307 SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, unionRect );
1308 slider.repaint( unionRect.x, unionRect.y, unionRect.width, unionRect.height );
1309 }
1310
1311 public void scrollByBlock(int direction) {
1312 synchronized(slider) {
1313 int blockIncrement =
1314 (slider.getMaximum() - slider.getMinimum()) / 10;
1315 if (blockIncrement == 0) {
1316 blockIncrement = 1;
1317 }
1318
1319 if (slider.getSnapToTicks()) {
1320 int tickSpacing = getTickSpacing();
1321
1322 if (blockIncrement < tickSpacing) {
1323 blockIncrement = tickSpacing;
1324 }
1325 }
1326
1327 int delta = blockIncrement * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
1328 slider.setValue(slider.getValue() + delta);
1329 }
1330 }
1331
1332 public void scrollByUnit(int direction) {
1333 synchronized(slider) {
1334 int delta = ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
1335
1336 if (slider.getSnapToTicks()) {
1337 delta *= getTickSpacing();
1338 }
1339
1340 slider.setValue(slider.getValue() + delta);
1341 }
1342 }
1343
1344 /**
1345 * This function is called when a mousePressed was detected in the track, not
1346 * in the thumb. The default behavior is to scroll by block. You can
1347 * override this method to stop it from scrolling or to add additional behavior.
1348 */
1349 protected void scrollDueToClickInTrack( int dir ) {
1350 scrollByBlock( dir );
1351 }
1352
1353 protected int xPositionForValue( int value ) {
1354 int min = slider.getMinimum();
1355 int max = slider.getMaximum();
1356 int trackLength = trackRect.width;
1357 double valueRange = (double)max - (double)min;
1358 double pixelsPerValue = (double)trackLength / valueRange;
1359 int trackLeft = trackRect.x;
1360 int trackRight = trackRect.x + (trackRect.width - 1);
1361 int xPosition;
1362
1363 if ( !drawInverted() ) {
1364 xPosition = trackLeft;
1365 xPosition += Math.round( pixelsPerValue * ((double)value - min) );
1366 }
1367 else {
1368 xPosition = trackRight;
1369 xPosition -= Math.round( pixelsPerValue * ((double)value - min) );
1370 }
1371
1372 xPosition = Math.max( trackLeft, xPosition );
1373 xPosition = Math.min( trackRight, xPosition );
1374
1375 return xPosition;
1376 }
1377
1378 protected int yPositionForValue( int value ) {
1379 return yPositionForValue(value, trackRect.y, trackRect.height);
1380 }
1381
1382 /**
1383 * Returns the y location for the specified value. No checking is
1384 * done on the arguments. In particular if <code>trackHeight</code> is
1385 * negative undefined results may occur.
1386 *
1387 * @param value the slider value to get the location for
1388 * @param trackY y-origin of the track
1389 * @param trackHeight the height of the track
1390 * @since 1.6
1391 */
1392 protected int yPositionForValue(int value, int trackY, int trackHeight) {
1393 int min = slider.getMinimum();
1394 int max = slider.getMaximum();
1395 double valueRange = (double)max - (double)min;
1396 double pixelsPerValue = (double)trackHeight / valueRange;
1397 int trackBottom = trackY + (trackHeight - 1);
1398 int yPosition;
1399
1400 if ( !drawInverted() ) {
1401 yPosition = trackY;
1402 yPosition += Math.round( pixelsPerValue * ((double)max - value ) );
1403 }
1404 else {
1405 yPosition = trackY;
1406 yPosition += Math.round( pixelsPerValue * ((double)value - min) );
1407 }
1408
1409 yPosition = Math.max( trackY, yPosition );
1410 yPosition = Math.min( trackBottom, yPosition );
1411
1412 return yPosition;
1413 }
1414
1415 /**
1416 * Returns the value at the y position. If {@code yPos} is beyond the
1417 * track at the the bottom or the top, this method sets the value to either
1418 * the minimum or maximum value of the slider, depending on if the slider
1419 * is inverted or not.
1420 */
1421 public int valueForYPosition( int yPos ) {
1422 int value;
1423 final int minValue = slider.getMinimum();
1424 final int maxValue = slider.getMaximum();
1425 final int trackLength = trackRect.height;
1426 final int trackTop = trackRect.y;
1427 final int trackBottom = trackRect.y + (trackRect.height - 1);
1428
1429 if ( yPos <= trackTop ) {
1430 value = drawInverted() ? minValue : maxValue;
1431 }
1432 else if ( yPos >= trackBottom ) {
1433 value = drawInverted() ? maxValue : minValue;
1434 }
1435 else {
1436 int distanceFromTrackTop = yPos - trackTop;
1437 double valueRange = (double)maxValue - (double)minValue;
1438 double valuePerPixel = valueRange / (double)trackLength;
1439 int valueFromTrackTop = (int)Math.round( distanceFromTrackTop * valuePerPixel );
1440
1441 value = drawInverted() ? minValue + valueFromTrackTop : maxValue - valueFromTrackTop;
1442 }
1443
1444 return value;
1445 }
1446
1447 /**
1448 * Returns the value at the x position. If {@code xPos} is beyond the
1449 * track at the left or the right, this method sets the value to either the
1450 * minimum or maximum value of the slider, depending on if the slider is
1451 * inverted or not.
1452 */
1453 public int valueForXPosition( int xPos ) {
1454 int value;
1455 final int minValue = slider.getMinimum();
1456 final int maxValue = slider.getMaximum();
1457 final int trackLength = trackRect.width;
1458 final int trackLeft = trackRect.x;
1459 final int trackRight = trackRect.x + (trackRect.width - 1);
1460
1461 if ( xPos <= trackLeft ) {
1462 value = drawInverted() ? maxValue : minValue;
1463 }
1464 else if ( xPos >= trackRight ) {
1465 value = drawInverted() ? minValue : maxValue;
1466 }
1467 else {
1468 int distanceFromTrackLeft = xPos - trackLeft;
1469 double valueRange = (double)maxValue - (double)minValue;
1470 double valuePerPixel = valueRange / (double)trackLength;
1471 int valueFromTrackLeft = (int)Math.round( distanceFromTrackLeft * valuePerPixel );
1472
1473 value = drawInverted() ? maxValue - valueFromTrackLeft :
1474 minValue + valueFromTrackLeft;
1475 }
1476
1477 return value;
1478 }
1479
1480
1481 private class Handler implements ChangeListener,
1482 ComponentListener, FocusListener, PropertyChangeListener {
1483 // Change Handler
1484 public void stateChanged(ChangeEvent e) {
1485 if (!isDragging) {
1486 calculateThumbLocation();
1487 slider.repaint();
1488 }
1489 lastValue = slider.getValue();
1490 }
1491
1492 // Component Handler
1493 public void componentHidden(ComponentEvent e) { }
1494 public void componentMoved(ComponentEvent e) { }
1495 public void componentResized(ComponentEvent e) {
1496 calculateGeometry();
1497 slider.repaint();
1498 }
1499 public void componentShown(ComponentEvent e) { }
1500
1501 // Focus Handler
1502 public void focusGained(FocusEvent e) { slider.repaint(); }
1503 public void focusLost(FocusEvent e) { slider.repaint(); }
1504
1505 // Property Change Handler
1506 public void propertyChange(PropertyChangeEvent e) {
1507 String propertyName = e.getPropertyName();
1508 if (propertyName == "orientation" ||
1509 propertyName == "inverted" ||
1510 propertyName == "labelTable" ||
1511 propertyName == "majorTickSpacing" ||
1512 propertyName == "minorTickSpacing" ||
1513 propertyName == "paintTicks" ||
1514 propertyName == "paintTrack" ||
1515 propertyName == "font" ||
1516 propertyName == "paintLabels" ||
1517 propertyName == "Slider.paintThumbArrowShape") {
1518 checkedLabelBaselines = false;
1519 calculateGeometry();
1520 slider.repaint();
1521 } else if (propertyName == "componentOrientation") {
1522 calculateGeometry();
1523 slider.repaint();
1524 InputMap km = getInputMap(JComponent.WHEN_FOCUSED, slider);
1525 SwingUtilities.replaceUIInputMap(slider,
1526 JComponent.WHEN_FOCUSED, km);
1527 } else if (propertyName == "model") {
1528 ((BoundedRangeModel)e.getOldValue()).removeChangeListener(
1529 changeListener);
1530 ((BoundedRangeModel)e.getNewValue()).addChangeListener(
1531 changeListener);
1532 calculateThumbLocation();
1533 slider.repaint();
1534 }
1535 }
1536 }
1537
1538 /////////////////////////////////////////////////////////////////////////
1539 /// Model Listener Class
1540 /////////////////////////////////////////////////////////////////////////
1541 /**
1542 * Data model listener.
1543 *
1544 * This class should be treated as a &quot;protected&quot; inner class.
1545 * Instantiate it only within subclasses of <Foo>.
1546 */
1547 public class ChangeHandler implements ChangeListener {
1548 // NOTE: This class exists only for backward compatability. All
1549 // its functionality has been moved into Handler. If you need to add
1550 // new functionality add it to the Handler, but make sure this
1551 // class calls into the Handler.
1552 public void stateChanged(ChangeEvent e) {
1553 getHandler().stateChanged(e);
1554 }
1555 }
1556
1557 /////////////////////////////////////////////////////////////////////////
1558 /// Track Listener Class
1559 /////////////////////////////////////////////////////////////////////////
1560 /**
1561 * Track mouse movements.
1562 *
1563 * This class should be treated as a &quot;protected&quot; inner class.
1564 * Instantiate it only within subclasses of <Foo>.
1565 */
1566 public class TrackListener extends MouseInputAdapter {
1567 protected transient int offset;
1568 protected transient int currentMouseX, currentMouseY;
1569
1570 public void mouseReleased(MouseEvent e) {
1571 if (!slider.isEnabled()) {
1572 return;
1573 }
1574
1575 offset = 0;
1576 scrollTimer.stop();
1577
1578 isDragging = false;
1579 slider.setValueIsAdjusting(false);
1580 slider.repaint();
1581 }
1582
1583 /**
1584 * If the mouse is pressed above the "thumb" component
1585 * then reduce the scrollbars value by one page ("page up"),
1586 * otherwise increase it by one page. If there is no
1587 * thumb then page up if the mouse is in the upper half
1588 * of the track.
1589 */
1590 public void mousePressed(MouseEvent e) {
1591 if (!slider.isEnabled()) {
1592 return;
1593 }
1594
1595 // We should recalculate geometry just before
1596 // calculation of the thumb movement direction.
1597 // It is important for the case, when JSlider
1598 // is a cell editor in JTable. See 6348946.
1599 calculateGeometry();
1600
1601 currentMouseX = e.getX();
1602 currentMouseY = e.getY();
1603
1604 if (slider.isRequestFocusEnabled()) {
1605 slider.requestFocus();
1606 }
1607
1608 // Clicked in the Thumb area?
1609 if (thumbRect.contains(currentMouseX, currentMouseY)) {
1610 if (UIManager.getBoolean("Slider.onlyLeftMouseButtonDrag")
1611 && !SwingUtilities.isLeftMouseButton(e)) {
1612 return;
1613 }
1614
1615 switch (slider.getOrientation()) {
1616 case JSlider.VERTICAL:
1617 offset = currentMouseY - thumbRect.y;
1618 break;
1619 case JSlider.HORIZONTAL:
1620 offset = currentMouseX - thumbRect.x;
1621 break;
1622 }
1623 isDragging = true;
1624 return;
1625 }
1626
1627 if (!SwingUtilities.isLeftMouseButton(e)) {
1628 return;
1629 }
1630
1631 isDragging = false;
1632 slider.setValueIsAdjusting(true);
1633
1634 Dimension sbSize = slider.getSize();
1635 int direction = POSITIVE_SCROLL;
1636
1637 switch (slider.getOrientation()) {
1638 case JSlider.VERTICAL:
1639 if ( thumbRect.isEmpty() ) {
1640 int scrollbarCenter = sbSize.height / 2;
1641 if ( !drawInverted() ) {
1642 direction = (currentMouseY < scrollbarCenter) ?
1643 POSITIVE_SCROLL : NEGATIVE_SCROLL;
1644 }
1645 else {
1646 direction = (currentMouseY < scrollbarCenter) ?
1647 NEGATIVE_SCROLL : POSITIVE_SCROLL;
1648 }
1649 }
1650 else {
1651 int thumbY = thumbRect.y;
1652 if ( !drawInverted() ) {
1653 direction = (currentMouseY < thumbY) ?
1654 POSITIVE_SCROLL : NEGATIVE_SCROLL;
1655 }
1656 else {
1657 direction = (currentMouseY < thumbY) ?
1658 NEGATIVE_SCROLL : POSITIVE_SCROLL;
1659 }
1660 }
1661 break;
1662 case JSlider.HORIZONTAL:
1663 if ( thumbRect.isEmpty() ) {
1664 int scrollbarCenter = sbSize.width / 2;
1665 if ( !drawInverted() ) {
1666 direction = (currentMouseX < scrollbarCenter) ?
1667 NEGATIVE_SCROLL : POSITIVE_SCROLL;
1668 }
1669 else {
1670 direction = (currentMouseX < scrollbarCenter) ?
1671 POSITIVE_SCROLL : NEGATIVE_SCROLL;
1672 }
1673 }
1674 else {
1675 int thumbX = thumbRect.x;
1676 if ( !drawInverted() ) {
1677 direction = (currentMouseX < thumbX) ?
1678 NEGATIVE_SCROLL : POSITIVE_SCROLL;
1679 }
1680 else {
1681 direction = (currentMouseX < thumbX) ?
1682 POSITIVE_SCROLL : NEGATIVE_SCROLL;
1683 }
1684 }
1685 break;
1686 }
1687
1688 if (shouldScroll(direction)) {
1689 scrollDueToClickInTrack(direction);
1690 }
1691 if (shouldScroll(direction)) {
1692 scrollTimer.stop();
1693 scrollListener.setDirection(direction);
1694 scrollTimer.start();
1695 }
1696 }
1697
1698 public boolean shouldScroll(int direction) {
1699 Rectangle r = thumbRect;
1700 if (slider.getOrientation() == JSlider.VERTICAL) {
1701 if (drawInverted() ? direction < 0 : direction > 0) {
1702 if (r.y <= currentMouseY) {
1703 return false;
1704 }
1705 }
1706 else if (r.y + r.height >= currentMouseY) {
1707 return false;
1708 }
1709 }
1710 else {
1711 if (drawInverted() ? direction < 0 : direction > 0) {
1712 if (r.x + r.width >= currentMouseX) {
1713 return false;
1714 }
1715 }
1716 else if (r.x <= currentMouseX) {
1717 return false;
1718 }
1719 }
1720
1721 if (direction > 0 && slider.getValue() + slider.getExtent() >=
1722 slider.getMaximum()) {
1723 return false;
1724 }
1725 else if (direction < 0 && slider.getValue() <=
1726 slider.getMinimum()) {
1727 return false;
1728 }
1729
1730 return true;
1731 }
1732
1733 /**
1734 * Set the models value to the position of the top/left
1735 * of the thumb relative to the origin of the track.
1736 */
1737 public void mouseDragged(MouseEvent e) {
1738 int thumbMiddle;
1739
1740 if (!slider.isEnabled()) {
1741 return;
1742 }
1743
1744 currentMouseX = e.getX();
1745 currentMouseY = e.getY();
1746
1747 if (!isDragging) {
1748 return;
1749 }
1750
1751 slider.setValueIsAdjusting(true);
1752
1753 switch (slider.getOrientation()) {
1754 case JSlider.VERTICAL:
1755 int halfThumbHeight = thumbRect.height / 2;
1756 int thumbTop = e.getY() - offset;
1757 int trackTop = trackRect.y;
1758 int trackBottom = trackRect.y + (trackRect.height - 1);
1759 int vMax = yPositionForValue(slider.getMaximum() -
1760 slider.getExtent());
1761
1762 if (drawInverted()) {
1763 trackBottom = vMax;
1764 }
1765 else {
1766 trackTop = vMax;
1767 }
1768 thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight);
1769 thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight);
1770
1771 setThumbLocation(thumbRect.x, thumbTop);
1772
1773 thumbMiddle = thumbTop + halfThumbHeight;
1774 slider.setValue( valueForYPosition( thumbMiddle ) );
1775 break;
1776 case JSlider.HORIZONTAL:
1777 int halfThumbWidth = thumbRect.width / 2;
1778 int thumbLeft = e.getX() - offset;
1779 int trackLeft = trackRect.x;
1780 int trackRight = trackRect.x + (trackRect.width - 1);
1781 int hMax = xPositionForValue(slider.getMaximum() -
1782 slider.getExtent());
1783
1784 if (drawInverted()) {
1785 trackLeft = hMax;
1786 }
1787 else {
1788 trackRight = hMax;
1789 }
1790 thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
1791 thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);
1792
1793 setThumbLocation(thumbLeft, thumbRect.y);
1794
1795 thumbMiddle = thumbLeft + halfThumbWidth;
1796 slider.setValue(valueForXPosition(thumbMiddle));
1797 break;
1798 }
1799 }
1800
1801 public void mouseMoved(MouseEvent e) { }
1802 }
1803
1804 /**
1805 * Scroll-event listener.
1806 *
1807 * This class should be treated as a &quot;protected&quot; inner class.
1808 * Instantiate it only within subclasses of <Foo>.
1809 */
1810 public class ScrollListener implements ActionListener {
1811 // changed this class to public to avoid bogus IllegalAccessException
1812 // bug in InternetExplorer browser. It was protected. Work around
1813 // for 4109432
1814 int direction = POSITIVE_SCROLL;
1815 boolean useBlockIncrement;
1816
1817 public ScrollListener() {
1818 direction = POSITIVE_SCROLL;
1819 useBlockIncrement = true;
1820 }
1821
1822 public ScrollListener(int dir, boolean block) {
1823 direction = dir;
1824 useBlockIncrement = block;
1825 }
1826
1827 public void setDirection(int direction) {
1828 this.direction = direction;
1829 }
1830
1831 public void setScrollByBlock(boolean block) {
1832 this.useBlockIncrement = block;
1833 }
1834
1835 public void actionPerformed(ActionEvent e) {
1836 if (useBlockIncrement) {
1837 scrollByBlock(direction);
1838 }
1839 else {
1840 scrollByUnit(direction);
1841 }
1842 if (!trackListener.shouldScroll(direction)) {
1843 ((Timer)e.getSource()).stop();
1844 }
1845 }
1846 }
1847
1848 /**
1849 * Listener for resizing events.
1850 * <p>
1851 * This class should be treated as a &quot;protected&quot; inner class.
1852 * Instantiate it only within subclasses of <Foo>.
1853 */
1854 public class ComponentHandler extends ComponentAdapter {
1855 // NOTE: This class exists only for backward compatability. All
1856 // its functionality has been moved into Handler. If you need to add
1857 // new functionality add it to the Handler, but make sure this
1858 // class calls into the Handler.
1859 public void componentResized(ComponentEvent e) {
1860 getHandler().componentResized(e);
1861 }
1862 }
1863
1864 /**
1865 * Focus-change listener.
1866 * <p>
1867 * This class should be treated as a &quot;protected&quot; inner class.
1868 * Instantiate it only within subclasses of <Foo>.
1869 */
1870 public class FocusHandler implements FocusListener {
1871 // NOTE: This class exists only for backward compatability. All
1872 // its functionality has been moved into Handler. If you need to add
1873 // new functionality add it to the Handler, but make sure this
1874 // class calls into the Handler.
1875 public void focusGained(FocusEvent e) {
1876 getHandler().focusGained(e);
1877 }
1878
1879 public void focusLost(FocusEvent e) {
1880 getHandler().focusLost(e);
1881 }
1882 }
1883
1884 /**
1885 * As of Java 2 platform v1.3 this undocumented class is no longer used.
1886 * The recommended approach to creating bindings is to use a
1887 * combination of an <code>ActionMap</code>, to contain the action,
1888 * and an <code>InputMap</code> to contain the mapping from KeyStroke
1889 * to action description. The InputMap is is usually described in the
1890 * LookAndFeel tables.
1891 * <p>
1892 * Please refer to the key bindings specification for further details.
1893 * <p>
1894 * This class should be treated as a &quot;protected&quot; inner class.
1895 * Instantiate it only within subclasses of <Foo>.
1896 */
1897 public class ActionScroller extends AbstractAction {
1898 // NOTE: This class exists only for backward compatability. All
1899 // its functionality has been moved into Actions. If you need to add
1900 // new functionality add it to the Actions, but make sure this
1901 // class calls into the Actions.
1902 int dir;
1903 boolean block;
1904 JSlider slider;
1905
1906 public ActionScroller( JSlider slider, int dir, boolean block) {
1907 this.dir = dir;
1908 this.block = block;
1909 this.slider = slider;
1910 }
1911
1912 public void actionPerformed(ActionEvent e) {
1913 SHARED_ACTION.scroll(slider, BasicSliderUI.this, dir, block);
1914 }
1915
1916 public boolean isEnabled() {
1917 boolean b = true;
1918 if (slider != null) {
1919 b = slider.isEnabled();
1920 }
1921 return b;
1922 }
1923
1924 }
1925
1926
1927 /**
1928 * A static version of the above.
1929 */
1930 static class SharedActionScroller extends AbstractAction {
1931 // NOTE: This class exists only for backward compatability. All
1932 // its functionality has been moved into Actions. If you need to add
1933 // new functionality add it to the Actions, but make sure this
1934 // class calls into the Actions.
1935 int dir;
1936 boolean block;
1937
1938 public SharedActionScroller(int dir, boolean block) {
1939 this.dir = dir;
1940 this.block = block;
1941 }
1942
1943 public void actionPerformed(ActionEvent evt) {
1944 JSlider slider = (JSlider)evt.getSource();
1945 BasicSliderUI ui = (BasicSliderUI)BasicLookAndFeel.getUIOfType(
1946 slider.getUI(), BasicSliderUI.class);
1947 if (ui == null) {
1948 return;
1949 }
1950 SHARED_ACTION.scroll(slider, ui, dir, block);
1951 }
1952 }
1953
1954 private static class Actions extends UIAction {
1955 public static final String POSITIVE_UNIT_INCREMENT =
1956 "positiveUnitIncrement";
1957 public static final String POSITIVE_BLOCK_INCREMENT =
1958 "positiveBlockIncrement";
1959 public static final String NEGATIVE_UNIT_INCREMENT =
1960 "negativeUnitIncrement";
1961 public static final String NEGATIVE_BLOCK_INCREMENT =
1962 "negativeBlockIncrement";
1963 public static final String MIN_SCROLL_INCREMENT = "minScroll";
1964 public static final String MAX_SCROLL_INCREMENT = "maxScroll";
1965
1966
1967 Actions() {
1968 super(null);
1969 }
1970
1971 public Actions(String name) {
1972 super(name);
1973 }
1974
1975 public void actionPerformed(ActionEvent evt) {
1976 JSlider slider = (JSlider)evt.getSource();
1977 BasicSliderUI ui = (BasicSliderUI)BasicLookAndFeel.getUIOfType(
1978 slider.getUI(), BasicSliderUI.class);
1979 String name = getName();
1980
1981 if (ui == null) {
1982 return;
1983 }
1984 if (POSITIVE_UNIT_INCREMENT == name) {
1985 scroll(slider, ui, POSITIVE_SCROLL, false);
1986 } else if (NEGATIVE_UNIT_INCREMENT == name) {
1987 scroll(slider, ui, NEGATIVE_SCROLL, false);
1988 } else if (POSITIVE_BLOCK_INCREMENT == name) {
1989 scroll(slider, ui, POSITIVE_SCROLL, true);
1990 } else if (NEGATIVE_BLOCK_INCREMENT == name) {
1991 scroll(slider, ui, NEGATIVE_SCROLL, true);
1992 } else if (MIN_SCROLL_INCREMENT == name) {
1993 scroll(slider, ui, MIN_SCROLL, false);
1994 } else if (MAX_SCROLL_INCREMENT == name) {
1995 scroll(slider, ui, MAX_SCROLL, false);
1996 }
1997 }
1998
1999 private void scroll(JSlider slider, BasicSliderUI ui, int direction,
2000 boolean isBlock) {
2001 boolean invert = slider.getInverted();
2002
2003 if (direction == NEGATIVE_SCROLL || direction == POSITIVE_SCROLL) {
2004 if (invert) {
2005 direction = (direction == POSITIVE_SCROLL) ?
2006 NEGATIVE_SCROLL : POSITIVE_SCROLL;
2007 }
2008
2009 if (isBlock) {
2010 ui.scrollByBlock(direction);
2011 } else {
2012 ui.scrollByUnit(direction);
2013 }
2014 } else { // MIN or MAX
2015 if (invert) {
2016 direction = (direction == MIN_SCROLL) ?
2017 MAX_SCROLL : MIN_SCROLL;
2018 }
2019
2020 slider.setValue((direction == MIN_SCROLL) ?
2021 slider.getMinimum() : slider.getMaximum());
2022 }
2023 }
2024 }
2025}