42 |
import java.awt.Dimension; |
import java.awt.Dimension; |
43 |
import java.awt.Graphics; |
import java.awt.Graphics; |
44 |
import java.awt.Rectangle; |
import java.awt.Rectangle; |
45 |
|
import java.beans.PropertyChangeEvent; |
46 |
import java.beans.PropertyChangeListener; |
import java.beans.PropertyChangeListener; |
|
import java.util.HashMap; |
|
47 |
|
|
48 |
import javax.swing.Icon; |
import javax.swing.Icon; |
49 |
import javax.swing.JComponent; |
import javax.swing.JComponent; |
59 |
public class MetalSliderUI |
public class MetalSliderUI |
60 |
extends BasicSliderUI |
extends BasicSliderUI |
61 |
{ |
{ |
62 |
// TODO: find a use for this |
/** |
63 |
|
* A property change handler that updates the rendered component in response |
64 |
|
* to specific property change events. This custom handler is used to |
65 |
|
* intercept the "JSlider.isFilled" property, which is only recognised by |
66 |
|
* the {@link MetalLookAndFeel}. |
67 |
|
*/ |
68 |
|
protected class MetalPropertyListener |
69 |
|
extends BasicSliderUI.PropertyChangeHandler |
70 |
|
{ |
71 |
|
/** |
72 |
|
* Creates a new listener. |
73 |
|
*/ |
74 |
|
protected MetalPropertyListener() |
75 |
|
{ |
76 |
|
} |
77 |
|
|
78 |
|
/** |
79 |
|
* Handles property change events. Events with the name "JSlider.isFilled" |
80 |
|
* are handled here, and other events are passed to the superclass. |
81 |
|
* |
82 |
|
* @param e the property change event. |
83 |
|
*/ |
84 |
|
public void propertyChange(PropertyChangeEvent e) |
85 |
|
{ |
86 |
|
if (e.getPropertyName().equals(SLIDER_FILL)) |
87 |
|
{ |
88 |
|
Boolean b = (Boolean) e.getNewValue(); |
89 |
|
if (b == null) |
90 |
|
filledSlider = false; |
91 |
|
else |
92 |
|
filledSlider = b.booleanValue(); |
93 |
|
} |
94 |
|
else |
95 |
|
super.propertyChange(e); |
96 |
|
} |
97 |
|
} |
98 |
|
|
99 |
|
/** The thumb color (unused, because an icon is used to draw the thumb). */ |
100 |
protected static Color thumbColor; |
protected static Color thumbColor; |
101 |
|
|
102 |
// TODO: find a use for this |
/** |
103 |
|
* The highlight color used for drawing the track rect when the slider is |
104 |
|
* enabled. |
105 |
|
*/ |
106 |
protected static Color highlightColor; |
protected static Color highlightColor; |
107 |
|
|
108 |
// TODO: find a use for this |
/** |
109 |
|
* The shadow color used for drawing the track rect when the slider is |
110 |
|
* enabled. |
111 |
|
*/ |
112 |
protected static Color darkShadowColor; |
protected static Color darkShadowColor; |
113 |
|
|
114 |
/** The track width. */ |
/** The track width. */ |
128 |
/** The gap between the track and the tick marks. */ |
/** The gap between the track and the tick marks. */ |
129 |
protected final int TICK_BUFFER = 4; |
protected final int TICK_BUFFER = 4; |
130 |
|
|
131 |
|
/** A key to look up the filledSlider setting in the {@link UIManager}. */ |
132 |
|
protected final String SLIDER_FILL = "JSlider.isFilled"; |
133 |
|
|
134 |
/** |
/** |
135 |
* A flag that controls whether or not the track is filled up to the value |
* A flag that controls whether or not the track is filled up to the value |
136 |
* of the slider. |
* of the slider. |
137 |
*/ |
*/ |
138 |
protected boolean filledSlider; |
protected boolean filledSlider; |
|
|
|
|
/** A key to look up the filledSlider setting in the {@link UIManager}. */ |
|
|
protected final String SLIDER_FILL = "JSlider.isFilled"; |
|
|
|
|
|
/** The UI instances for MetalSliderUIs */ |
|
|
private static HashMap instances; |
|
139 |
|
|
140 |
/** |
/** |
141 |
* Constructs a new instance. |
* Constructs a new instance. |
144 |
{ |
{ |
145 |
super(null); |
super(null); |
146 |
filledSlider = UIManager.getBoolean(SLIDER_FILL); |
filledSlider = UIManager.getBoolean(SLIDER_FILL); |
147 |
|
darkShadowColor = MetalLookAndFeel.getControlDarkShadow(); |
148 |
|
highlightColor = MetalLookAndFeel.getControlHighlight(); |
149 |
} |
} |
150 |
|
|
151 |
/** |
/** |
152 |
* Returns an instance of MetalSliderUI. |
* Returns an instance of MetalSliderUI. |
153 |
* |
* |
154 |
* @param component the component for which we return an UI instance |
* @param component the component (ignored). |
155 |
* |
* |
156 |
* @return an instance of MetalSliderUI |
* @return an instance of MetalSliderUI |
157 |
*/ |
*/ |
158 |
public static ComponentUI createUI(JComponent component) |
public static ComponentUI createUI(JComponent component) |
159 |
{ |
{ |
160 |
if (instances == null) |
return new MetalSliderUI(); |
|
instances = new HashMap(); |
|
|
|
|
|
Object o = instances.get(component); |
|
|
MetalSliderUI instance; |
|
|
if (o == null) |
|
|
{ |
|
|
instance = new MetalSliderUI(); |
|
|
instances.put(component, instance); |
|
|
} |
|
|
else |
|
|
instance = (MetalSliderUI) o; |
|
|
|
|
|
return instance; |
|
161 |
} |
} |
162 |
|
|
163 |
|
/** |
164 |
|
* Installs the default for this UI delegate in the supplied component. |
165 |
|
* |
166 |
|
* @param c the component. |
167 |
|
*/ |
168 |
public void installUI(JComponent c) |
public void installUI(JComponent c) |
169 |
{ |
{ |
170 |
super.installUI(c); |
super.installUI(c); |
174 |
} |
} |
175 |
|
|
176 |
/** |
/** |
177 |
|
* Creates a property change listener for the slider. |
178 |
|
* |
179 |
|
* @param slider the slider. |
180 |
|
* |
181 |
|
* @return A new instance of {@link MetalPropertyListener}. |
182 |
|
*/ |
183 |
|
protected PropertyChangeListener createPropertyChangeListener(JSlider slider) |
184 |
|
{ |
185 |
|
return new MetalPropertyListener(); |
186 |
|
} |
187 |
|
|
188 |
|
/** |
189 |
* Paints the thumb icon for the slider. |
* Paints the thumb icon for the slider. |
190 |
* |
* |
191 |
* @param g the graphics device. |
* @param g the graphics device. |
199 |
} |
} |
200 |
|
|
201 |
/** |
/** |
|
* Creates a property change listener for the slider. |
|
|
* |
|
|
* @param slider the slider. |
|
|
*/ |
|
|
protected PropertyChangeListener createPropertyChangeListener(JSlider slider) |
|
|
{ |
|
|
// TODO: try to figure out why it might be necessary to override this |
|
|
// method as is done in Sun's implementation |
|
|
return super.createPropertyChangeListener(slider); |
|
|
} |
|
|
|
|
|
/** |
|
202 |
* Paints the track along which the thumb control moves. |
* Paints the track along which the thumb control moves. |
203 |
* |
* |
204 |
* @param g the graphics device. |
* @param g the graphics device. |
205 |
*/ |
*/ |
206 |
public void paintTrack(Graphics g) |
public void paintTrack(Graphics g) |
207 |
{ |
{ |
208 |
|
Color shadowColor = MetalLookAndFeel.getControlShadow(); |
209 |
if (slider.getOrientation() == JSlider.HORIZONTAL) |
if (slider.getOrientation() == JSlider.HORIZONTAL) |
|
{ |
|
|
if (filledSlider) |
|
210 |
{ |
{ |
211 |
// TODO: fill the track |
int trackX = trackRect.x; |
212 |
|
int trackY = trackRect.y + (trackRect.height - getTrackWidth()) / 2; |
213 |
|
int trackW = trackRect.width - 1; |
214 |
|
int trackH = getTrackWidth(); |
215 |
|
|
216 |
|
// draw border |
217 |
|
if (slider.isEnabled()) |
218 |
|
BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH, |
219 |
|
darkShadowColor, shadowColor, darkShadowColor, highlightColor); |
220 |
|
else |
221 |
|
{ |
222 |
|
g.setColor(MetalLookAndFeel.getControlShadow()); |
223 |
|
g.drawRect(trackX, trackY, trackW - 2, trackH - 2); |
224 |
|
} |
225 |
|
|
226 |
|
// fill track (if required) |
227 |
|
if (filledSlider) |
228 |
|
{ |
229 |
|
int xPos = xPositionForValue(slider.getValue()); |
230 |
|
int x = (slider.getInverted() ? xPos : trackRect.x); |
231 |
|
int w = (slider.getInverted() ? trackX + trackW - xPos |
232 |
|
: xPos - trackRect.x); |
233 |
|
g.setColor(MetalLookAndFeel.getControlShadow()); |
234 |
|
g.fillRect(x + 1, trackY + 1, w - 3, getTrackWidth() - 3); |
235 |
|
if (slider.isEnabled()) |
236 |
|
{ |
237 |
|
g.setColor(MetalLookAndFeel.getControl()); |
238 |
|
g.drawLine(x + 1, trackY + 1, x + w - 3, trackY + 1); |
239 |
|
g.drawLine(x + 1, trackY + 1, x + 1, |
240 |
|
trackY + getTrackWidth() - 3); |
241 |
|
} |
242 |
|
} |
243 |
} |
} |
|
BasicGraphicsUtils.drawEtchedRect(g, trackRect.x, trackRect.y |
|
|
+ (trackRect.height - getTrackWidth()) / 2, trackRect.width - 1, |
|
|
getTrackWidth(), Color.darkGray, Color.gray, Color.darkGray, |
|
|
Color.white); |
|
|
} |
|
244 |
else |
else |
|
{ |
|
|
if (filledSlider) |
|
245 |
{ |
{ |
246 |
// TODO: fill the track |
int trackX = trackRect.x + (trackRect.width - getTrackWidth()) / 2; |
247 |
|
int trackY = trackRect.y; |
248 |
|
int trackW = getTrackWidth(); |
249 |
|
int trackH = trackRect.height - 1; |
250 |
|
if (slider.isEnabled()) |
251 |
|
BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH, |
252 |
|
darkShadowColor, shadowColor, darkShadowColor, highlightColor); |
253 |
|
else |
254 |
|
{ |
255 |
|
g.setColor(MetalLookAndFeel.getControlShadow()); |
256 |
|
g.drawRect(trackX, trackY, trackW - 2, trackH - 2); |
257 |
|
} |
258 |
|
|
259 |
|
if (filledSlider) |
260 |
|
{ |
261 |
|
int yPos = yPositionForValue(slider.getValue()); |
262 |
|
int y = (slider.getInverted() ? trackY : yPos); |
263 |
|
int h = (slider.getInverted() ? yPos - trackY |
264 |
|
: trackY + trackH - yPos); |
265 |
|
g.setColor(MetalLookAndFeel.getControlShadow()); |
266 |
|
g.fillRect(trackX + 1, y + 1, getTrackWidth() - 3, h - 3); |
267 |
|
if (slider.isEnabled()) |
268 |
|
{ |
269 |
|
g.setColor(MetalLookAndFeel.getControl()); |
270 |
|
g.drawLine(trackX + 1, y + 1, trackX + trackW - 3, y + 1); |
271 |
|
g.drawLine(trackX + 1, y + 1, trackX + 1, y + h - 3); |
272 |
|
} |
273 |
|
} |
274 |
} |
} |
|
BasicGraphicsUtils.drawEtchedRect(g, trackRect.x + (trackRect.width |
|
|
- getTrackWidth()) / 2, trackRect.y, getTrackWidth(), |
|
|
trackRect.height - 1, Color.darkGray, Color.gray, Color.darkGray, |
|
|
Color.white); |
|
|
} |
|
275 |
} |
} |
276 |
|
|
277 |
/** |
/** |
341 |
*/ |
*/ |
342 |
protected int getThumbOverhang() |
protected int getThumbOverhang() |
343 |
{ |
{ |
344 |
// TODO: figure out what this is used for |
// FIXME: for what might this method be used? |
345 |
return 0; |
return 0; |
346 |
} |
} |
347 |
|
|
348 |
protected void scrollDueToClickInTrack(int dir) |
protected void scrollDueToClickInTrack(int dir) |
349 |
{ |
{ |
350 |
|
// FIXME: for what might this method be overridden? |
351 |
super.scrollDueToClickInTrack(dir); |
super.scrollDueToClickInTrack(dir); |
352 |
} |
} |
353 |
|
|
363 |
{ |
{ |
364 |
// Note the incoming 'g' has a translation in place to get us to the |
// Note the incoming 'g' has a translation in place to get us to the |
365 |
// start of the tick rect already... |
// start of the tick rect already... |
366 |
// TODO: get color from UIManager... |
if (slider.isEnabled()) |
367 |
g.setColor(new Color(153, 153, 204)); |
g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); |
368 |
|
else |
369 |
|
g.setColor(MetalLookAndFeel.getControlDisabled()); |
370 |
g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength / 2); |
g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength / 2); |
371 |
} |
} |
372 |
|
|
382 |
{ |
{ |
383 |
// Note the incoming 'g' has a translation in place to get us to the |
// Note the incoming 'g' has a translation in place to get us to the |
384 |
// start of the tick rect already... |
// start of the tick rect already... |
385 |
// TODO: get color from UIManager... |
if (slider.isEnabled()) |
386 |
g.setColor(new Color(153, 153, 204)); |
g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); |
387 |
|
else |
388 |
|
g.setColor(MetalLookAndFeel.getControlDisabled()); |
389 |
g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength); |
g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength); |
390 |
} |
} |
391 |
|
|
401 |
{ |
{ |
402 |
// Note the incoming 'g' has a translation in place to get us to the |
// Note the incoming 'g' has a translation in place to get us to the |
403 |
// start of the tick rect already... |
// start of the tick rect already... |
404 |
// TODO: get color from UIManager... |
if (slider.isEnabled()) |
405 |
g.setColor(new Color(153, 153, 204)); |
g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); |
406 |
|
else |
407 |
|
g.setColor(MetalLookAndFeel.getControlDisabled()); |
408 |
g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength / 2, y); |
g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength / 2, y); |
409 |
} |
} |
410 |
|
|
420 |
{ |
{ |
421 |
// Note the incoming 'g' has a translation in place to get us to the |
// Note the incoming 'g' has a translation in place to get us to the |
422 |
// start of the tick rect already... |
// start of the tick rect already... |
423 |
// TODO: get color from UIManager... |
if (slider.isEnabled()) |
424 |
g.setColor(new Color(153, 153, 204)); |
g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); |
425 |
|
else |
426 |
|
g.setColor(MetalLookAndFeel.getControlDisabled()); |
427 |
g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength, y); |
g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength, y); |
428 |
} |
} |
429 |
|
|