blob: 13563b80ecca638cafc0ab6e9ec017dd7369a319 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2005 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.metal;
27
28import javax.swing.plaf.basic.BasicSliderUI;
29
30import java.awt.Component;
31import java.awt.Container;
32import java.awt.Graphics;
33import java.awt.Dimension;
34import java.awt.Rectangle;
35import java.awt.Point;
36import java.awt.Insets;
37import java.awt.Color;
38import java.io.Serializable;
39import java.awt.IllegalComponentStateException;
40import java.awt.Polygon;
41import java.beans.*;
42
43import javax.swing.border.AbstractBorder;
44
45import javax.swing.*;
46import javax.swing.event.*;
47import javax.swing.plaf.*;
48
49/**
50 * A Java L&F implementation of SliderUI.
51 * <p>
52 * <strong>Warning:</strong>
53 * Serialized objects of this class will not be compatible with
54 * future Swing releases. The current serialization support is
55 * appropriate for short term storage or RMI between applications running
56 * the same version of Swing. As of 1.4, support for long term storage
57 * of all JavaBeans<sup><font size="-2">TM</font></sup>
58 * has been added to the <code>java.beans</code> package.
59 * Please see {@link java.beans.XMLEncoder}.
60 *
61 * @author Tom Santos
62 */
63public class MetalSliderUI extends BasicSliderUI {
64
65 protected final int TICK_BUFFER = 4;
66 protected boolean filledSlider = false;
67 // NOTE: these next three variables are currently unused.
68 protected static Color thumbColor;
69 protected static Color highlightColor;
70 protected static Color darkShadowColor;
71 protected static int trackWidth;
72 protected static int tickLength;
73
74 /**
75 * A default horizontal thumb <code>Icon</code>. This field might not be
76 * used. To change the <code>Icon</code> used by this delgate directly set it
77 * using the <code>Slider.horizontalThumbIcon</code> UIManager property.
78 */
79 protected static Icon horizThumbIcon;
80
81 /**
82 * A default vertical thumb <code>Icon</code>. This field might not be
83 * used. To change the <code>Icon</code> used by this delgate directly set it
84 * using the <code>Slider.verticalThumbIcon</code> UIManager property.
85 */
86 protected static Icon vertThumbIcon;
87
88 private static Icon SAFE_HORIZ_THUMB_ICON;
89 private static Icon SAFE_VERT_THUMB_ICON;
90
91
92 protected final String SLIDER_FILL = "JSlider.isFilled";
93
94 public static ComponentUI createUI(JComponent c) {
95 return new MetalSliderUI();
96 }
97
98 public MetalSliderUI() {
99 super( null );
100 }
101
102 private static Icon getHorizThumbIcon() {
103 if (System.getSecurityManager() != null) {
104 return SAFE_HORIZ_THUMB_ICON;
105 } else {
106 return horizThumbIcon;
107 }
108 }
109
110 private static Icon getVertThumbIcon() {
111 if (System.getSecurityManager() != null) {
112 return SAFE_VERT_THUMB_ICON;
113 } else {
114 return vertThumbIcon;
115 }
116 }
117
118 public void installUI( JComponent c ) {
119 trackWidth = ((Integer)UIManager.get( "Slider.trackWidth" )).intValue();
120 tickLength = ((Integer)UIManager.get( "Slider.majorTickLength" )).intValue();
121 horizThumbIcon = SAFE_HORIZ_THUMB_ICON =
122 UIManager.getIcon( "Slider.horizontalThumbIcon" );
123 vertThumbIcon = SAFE_VERT_THUMB_ICON =
124 UIManager.getIcon( "Slider.verticalThumbIcon" );
125
126 super.installUI( c );
127
128 thumbColor = UIManager.getColor("Slider.thumb");
129 highlightColor = UIManager.getColor("Slider.highlight");
130 darkShadowColor = UIManager.getColor("Slider.darkShadow");
131
132 scrollListener.setScrollByBlock( false );
133
134 Object sliderFillProp = c.getClientProperty( SLIDER_FILL );
135 if ( sliderFillProp != null ) {
136 filledSlider = ((Boolean)sliderFillProp).booleanValue();
137 }
138 }
139
140 protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
141 return new MetalPropertyListener();
142 }
143
144 protected class MetalPropertyListener extends BasicSliderUI.PropertyChangeHandler {
145 public void propertyChange( PropertyChangeEvent e ) { // listen for slider fill
146 super.propertyChange( e );
147
148 String name = e.getPropertyName();
149 if ( name.equals( SLIDER_FILL ) ) {
150 if ( e.getNewValue() != null ) {
151 filledSlider = ((Boolean)e.getNewValue()).booleanValue();
152 }
153 else {
154 filledSlider = false;
155 }
156 }
157 }
158 }
159
160 public void paintThumb(Graphics g) {
161 Rectangle knobBounds = thumbRect;
162
163 g.translate( knobBounds.x, knobBounds.y );
164
165 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
166 getHorizThumbIcon().paintIcon( slider, g, 0, 0 );
167 }
168 else {
169 getVertThumbIcon().paintIcon( slider, g, 0, 0 );
170 }
171
172 g.translate( -knobBounds.x, -knobBounds.y );
173 }
174
175 /**
176 * If <code>chooseFirst</code>is true, <code>c1</code> is returned,
177 * otherwise <code>c2</code>.
178 */
179 private Color chooseColor(boolean chooseFirst, Color c1, Color c2) {
180 if (chooseFirst) {
181 return c2;
182 }
183 return c1;
184 }
185
186 /**
187 * Returns a rectangle enclosing the track that will be painted.
188 */
189 private Rectangle getPaintTrackRect() {
190 int trackLeft = 0, trackRight = 0, trackTop = 0, trackBottom = 0;
191 if (slider.getOrientation() == JSlider.HORIZONTAL) {
192 trackBottom = (trackRect.height - 1) - getThumbOverhang();
193 trackTop = trackBottom - (getTrackWidth() - 1);
194 trackRight = trackRect.width - 1;
195 }
196 else {
197 if (MetalUtils.isLeftToRight(slider)) {
198 trackLeft = (trackRect.width - getThumbOverhang()) -
199 getTrackWidth();
200 trackRight = (trackRect.width - getThumbOverhang()) - 1;
201 }
202 else {
203 trackLeft = getThumbOverhang();
204 trackRight = getThumbOverhang() + getTrackWidth() - 1;
205 }
206 trackBottom = trackRect.height - 1;
207 }
208 return new Rectangle(trackRect.x + trackLeft, trackRect.y + trackTop,
209 trackRight - trackLeft, trackBottom - trackTop);
210 }
211
212 public void paintTrack(Graphics g) {
213 if (MetalLookAndFeel.usingOcean()) {
214 oceanPaintTrack(g);
215 return;
216 }
217 Color trackColor = !slider.isEnabled() ? MetalLookAndFeel.getControlShadow() :
218 slider.getForeground();
219
220 boolean leftToRight = MetalUtils.isLeftToRight(slider);
221
222 g.translate( trackRect.x, trackRect.y );
223
224 int trackLeft = 0;
225 int trackTop = 0;
226 int trackRight = 0;
227 int trackBottom = 0;
228
229 // Draw the track
230 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
231 trackBottom = (trackRect.height - 1) - getThumbOverhang();
232 trackTop = trackBottom - (getTrackWidth() - 1);
233 trackRight = trackRect.width - 1;
234 }
235 else {
236 if (leftToRight) {
237 trackLeft = (trackRect.width - getThumbOverhang()) -
238 getTrackWidth();
239 trackRight = (trackRect.width - getThumbOverhang()) - 1;
240 }
241 else {
242 trackLeft = getThumbOverhang();
243 trackRight = getThumbOverhang() + getTrackWidth() - 1;
244 }
245 trackBottom = trackRect.height - 1;
246 }
247
248 if ( slider.isEnabled() ) {
249 g.setColor( MetalLookAndFeel.getControlDarkShadow() );
250 g.drawRect( trackLeft, trackTop,
251 (trackRight - trackLeft) - 1, (trackBottom - trackTop) - 1 );
252
253 g.setColor( MetalLookAndFeel.getControlHighlight() );
254 g.drawLine( trackLeft + 1, trackBottom, trackRight, trackBottom );
255 g.drawLine( trackRight, trackTop + 1, trackRight, trackBottom );
256
257 g.setColor( MetalLookAndFeel.getControlShadow() );
258 g.drawLine( trackLeft + 1, trackTop + 1, trackRight - 2, trackTop + 1 );
259 g.drawLine( trackLeft + 1, trackTop + 1, trackLeft + 1, trackBottom - 2 );
260 }
261 else {
262 g.setColor( MetalLookAndFeel.getControlShadow() );
263 g.drawRect( trackLeft, trackTop,
264 (trackRight - trackLeft) - 1, (trackBottom - trackTop) - 1 );
265 }
266
267 // Draw the fill
268 if ( filledSlider ) {
269 int middleOfThumb = 0;
270 int fillTop = 0;
271 int fillLeft = 0;
272 int fillBottom = 0;
273 int fillRight = 0;
274
275 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
276 middleOfThumb = thumbRect.x + (thumbRect.width / 2);
277 middleOfThumb -= trackRect.x; // To compensate for the g.translate()
278 fillTop = !slider.isEnabled() ? trackTop : trackTop + 1;
279 fillBottom = !slider.isEnabled() ? trackBottom - 1 : trackBottom - 2;
280
281 if ( !drawInverted() ) {
282 fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1;
283 fillRight = middleOfThumb;
284 }
285 else {
286 fillLeft = middleOfThumb;
287 fillRight = !slider.isEnabled() ? trackRight - 1 : trackRight - 2;
288 }
289 }
290 else {
291 middleOfThumb = thumbRect.y + (thumbRect.height / 2);
292 middleOfThumb -= trackRect.y; // To compensate for the g.translate()
293 fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1;
294 fillRight = !slider.isEnabled() ? trackRight - 1 : trackRight - 2;
295
296 if ( !drawInverted() ) {
297 fillTop = middleOfThumb;
298 fillBottom = !slider.isEnabled() ? trackBottom - 1 : trackBottom - 2;
299 }
300 else {
301 fillTop = !slider.isEnabled() ? trackTop : trackTop + 1;
302 fillBottom = middleOfThumb;
303 }
304 }
305
306 if ( slider.isEnabled() ) {
307 g.setColor( slider.getBackground() );
308 g.drawLine( fillLeft, fillTop, fillRight, fillTop );
309 g.drawLine( fillLeft, fillTop, fillLeft, fillBottom );
310
311 g.setColor( MetalLookAndFeel.getControlShadow() );
312 g.fillRect( fillLeft + 1, fillTop + 1,
313 fillRight - fillLeft, fillBottom - fillTop );
314 }
315 else {
316 g.setColor( MetalLookAndFeel.getControlShadow() );
317 g.fillRect( fillLeft, fillTop,
318 fillRight - fillLeft, trackBottom - trackTop );
319 }
320 }
321
322 g.translate( -trackRect.x, -trackRect.y );
323 }
324
325 private void oceanPaintTrack(Graphics g) {
326 boolean leftToRight = MetalUtils.isLeftToRight(slider);
327 boolean drawInverted = drawInverted();
328 Color sliderAltTrackColor = (Color)UIManager.get(
329 "Slider.altTrackColor");
330
331 // Translate to the origin of the painting rectangle
332 Rectangle paintRect = getPaintTrackRect();
333 g.translate(paintRect.x, paintRect.y);
334
335 // Width and height of the painting rectangle.
336 int w = paintRect.width;
337 int h = paintRect.height;
338
339 if (!slider.isEnabled()) {
340 g.setColor(MetalLookAndFeel.getControlShadow());
341 g.drawRect(0, 0, w - 1, h - 1);
342 }
343 else if (slider.getOrientation() == JSlider.HORIZONTAL) {
344 int middleOfThumb = thumbRect.x + (thumbRect.width / 2) -
345 paintRect.x;
346 int fillMinX;
347 int fillMaxX;
348
349 if (middleOfThumb > 0) {
350 g.setColor(chooseColor(drawInverted,
351 MetalLookAndFeel.getPrimaryControlDarkShadow(),
352 MetalLookAndFeel.getControlDarkShadow()));
353 g.drawRect(0, 0, middleOfThumb - 1, h - 1);
354 }
355 if (middleOfThumb < w) {
356 g.setColor(chooseColor(drawInverted,
357 MetalLookAndFeel.getControlDarkShadow(),
358 MetalLookAndFeel.getPrimaryControlDarkShadow()));
359 g.drawRect(middleOfThumb, 0, w - middleOfThumb - 1, h - 1);
360 }
361 g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
362 if (drawInverted) {
363 fillMinX = middleOfThumb;
364 fillMaxX = w - 2;
365 g.drawLine(1, 1, middleOfThumb, 1);
366 }
367 else {
368 fillMinX = 1;
369 fillMaxX = middleOfThumb;
370 g.drawLine(middleOfThumb, 1, w - 1, 1);
371 }
372 if (h == 6) {
373 g.setColor(MetalLookAndFeel.getWhite());
374 g.drawLine(fillMinX, 1, fillMaxX, 1);
375 g.setColor(sliderAltTrackColor);
376 g.drawLine(fillMinX, 2, fillMaxX, 2);
377 g.setColor(MetalLookAndFeel.getControlShadow());
378 g.drawLine(fillMinX, 3, fillMaxX, 3);
379 g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
380 g.drawLine(fillMinX, 4, fillMaxX, 4);
381 }
382 }
383 else {
384 int middleOfThumb = thumbRect.y + (thumbRect.height / 2) -
385 paintRect.y;
386 int fillMinY;
387 int fillMaxY;
388
389 if (middleOfThumb > 0) {
390 g.setColor(chooseColor(drawInverted,
391 MetalLookAndFeel.getControlDarkShadow(),
392 MetalLookAndFeel.getPrimaryControlDarkShadow()));
393 g.drawRect(0, 0, w - 1, middleOfThumb - 1);
394 }
395 if (middleOfThumb < h) {
396 g.setColor(chooseColor(drawInverted,
397 MetalLookAndFeel.getPrimaryControlDarkShadow(),
398 MetalLookAndFeel.getControlDarkShadow()));
399 g.drawRect(0, middleOfThumb, w - 1, h - middleOfThumb - 1);
400 }
401 g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
402 if (drawInverted()) {
403 fillMinY = 1;
404 fillMaxY = middleOfThumb;
405 if (leftToRight) {
406 g.drawLine(1, middleOfThumb, 1, h - 1);
407 }
408 else {
409 g.drawLine(w - 2, middleOfThumb, w - 2, h - 1);
410 }
411 }
412 else {
413 fillMinY = middleOfThumb;
414 fillMaxY = h - 2;
415 if (leftToRight) {
416 g.drawLine(1, 1, 1, middleOfThumb);
417 }
418 else {
419 g.drawLine(w - 2, 1, w - 2, middleOfThumb);
420 }
421 }
422 if (w == 6) {
423 g.setColor(chooseColor(!leftToRight,
424 MetalLookAndFeel.getWhite(),
425 MetalLookAndFeel.getPrimaryControlShadow()));
426 g.drawLine(1, fillMinY, 1, fillMaxY);
427 g.setColor(chooseColor(!leftToRight, sliderAltTrackColor,
428 MetalLookAndFeel.getControlShadow()));
429 g.drawLine(2, fillMinY, 2, fillMaxY);
430 g.setColor(chooseColor(!leftToRight,
431 MetalLookAndFeel.getControlShadow(),
432 sliderAltTrackColor));
433 g.drawLine(3, fillMinY, 3, fillMaxY);
434 g.setColor(chooseColor(!leftToRight,
435 MetalLookAndFeel.getPrimaryControlShadow(),
436 MetalLookAndFeel.getWhite()));
437 g.drawLine(4, fillMinY, 4, fillMaxY);
438 }
439 }
440
441 g.translate(-paintRect.x, -paintRect.y);
442 }
443
444 public void paintFocus(Graphics g) {
445 }
446
447 protected Dimension getThumbSize() {
448 Dimension size = new Dimension();
449
450 if ( slider.getOrientation() == JSlider.VERTICAL ) {
451 size.width = getVertThumbIcon().getIconWidth();
452 size.height = getVertThumbIcon().getIconHeight();
453 }
454 else {
455 size.width = getHorizThumbIcon().getIconWidth();
456 size.height = getHorizThumbIcon().getIconHeight();
457 }
458
459 return size;
460 }
461
462 /**
463 * Gets the height of the tick area for horizontal sliders and the width of the
464 * tick area for vertical sliders. BasicSliderUI uses the returned value to
465 * determine the tick area rectangle.
466 */
467 public int getTickLength() {
468 return slider.getOrientation() == JSlider.HORIZONTAL ? tickLength + TICK_BUFFER + 1 :
469 tickLength + TICK_BUFFER + 3;
470 }
471
472 /**
473 * Returns the shorter dimension of the track.
474 */
475 protected int getTrackWidth() {
476 // This strange calculation is here to keep the
477 // track in proportion to the thumb.
478 final double kIdealTrackWidth = 7.0;
479 final double kIdealThumbHeight = 16.0;
480 final double kWidthScalar = kIdealTrackWidth / kIdealThumbHeight;
481
482 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
483 return (int)(kWidthScalar * thumbRect.height);
484 }
485 else {
486 return (int)(kWidthScalar * thumbRect.width);
487 }
488 }
489
490 /**
491 * Returns the longer dimension of the slide bar. (The slide bar is only the
492 * part that runs directly under the thumb)
493 */
494 protected int getTrackLength() {
495 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
496 return trackRect.width;
497 }
498 return trackRect.height;
499 }
500
501 /**
502 * Returns the amount that the thumb goes past the slide bar.
503 */
504 protected int getThumbOverhang() {
505 return (int)(getThumbSize().getHeight()-getTrackWidth())/2;
506 }
507
508 protected void scrollDueToClickInTrack( int dir ) {
509 scrollByUnit( dir );
510 }
511
512 protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
513 g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
514 g.drawLine( x, TICK_BUFFER, x, TICK_BUFFER + (tickLength / 2) );
515 }
516
517 protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
518 g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
519 g.drawLine( x, TICK_BUFFER , x, TICK_BUFFER + (tickLength - 1) );
520 }
521
522 protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
523 g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
524
525 if (MetalUtils.isLeftToRight(slider)) {
526 g.drawLine( TICK_BUFFER, y, TICK_BUFFER + (tickLength / 2), y );
527 }
528 else {
529 g.drawLine( 0, y, tickLength/2, y );
530 }
531 }
532
533 protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
534 g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
535
536 if (MetalUtils.isLeftToRight(slider)) {
537 g.drawLine( TICK_BUFFER, y, TICK_BUFFER + tickLength, y );
538 }
539 else {
540 g.drawLine( 0, y, tickLength, y );
541 }
542 }
543}