View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of CosyBeans-Common.
5    *
6    * CosyBeans-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * CosyBeans-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with CosyBeans-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.gui.components.slider;
21  
22  import java.awt.AlphaComposite;
23  import java.awt.Color;
24  import java.awt.FontMetrics;
25  import java.awt.Graphics;
26  import java.awt.Graphics2D;
27  import java.awt.Rectangle;
28  import java.awt.image.BufferedImage;
29  
30  import javax.swing.Icon;
31  import javax.swing.JComponent;
32  import javax.swing.JSlider;
33  import javax.swing.plaf.metal.MetalSliderUI;
34  
35  import com.cosylab.gui.components.range2.RangedValueController;
36  import com.cosylab.gui.components.range2.Tick;
37  import com.cosylab.gui.components.range2.TickParameters;
38  import com.cosylab.util.PrintfFormat;
39  
40  
41  /**
42   * This is customized Swing l&f for JSlider. 
43   *
44   * @author <a href="mailto:ales.pucelj@cosylab.com">Ales Pucelj</a>
45   * @version $id$
46   */
47  public class CosySliderUI extends MetalSliderUI
48  {
49  	private boolean checkValue;
50  	protected Icon vertTrailerIcon = null;
51  	protected BufferedImage horizTrailerBuffer = null;
52  	private AlphaComposite alphaComposite;
53  	protected Rectangle trailerRect = new Rectangle(0, 0, 16, 16);
54  	protected Rectangle trailerCache = new Rectangle();
55  	protected double trailerPosition = 0;
56  	private boolean showIntegerOnly = false;
57  	private String format = "%f";
58  	private double tolerance = 0.0001;
59  	private boolean paintTrailer = true;
60  	
61  	protected static final Color[] GREEN_LED = {
62  		new Color(0, 160, 0), new Color(0, 220, 0), new Color(0, 250, 0)
63  	};
64  	protected static final Color[] RED_LED = {
65  		new Color(160, 0, 0), new Color(220, 0, 0), new Color(250, 0, 0)
66  	};
67  	protected static final Color[] YELLOW_LED = {
68  		new Color(150, 150, 0), new Color(200, 200, 0), new Color(250, 250, 0)
69  	};
70  	protected static final Color[] GREY_LED = {
71  		new Color(64, 64, 64), new Color(128, 128, 128),
72  		new Color(160, 160, 160)
73  	};
74  
75  	private class TickMeasurer implements TickParameters
76  	{
77  		private FontMetrics fm;
78  		private PrintfFormat formatter;
79  
80  		/**
81  		 * Creates a new TickMeasurer object.
82  		 *
83  		 * @param g 
84  		 */
85  		public TickMeasurer(Graphics g, String format)
86  		{
87  			fm = g.getFontMetrics();
88  			if (format != null)
89  				formatter = new PrintfFormat(format);
90  		}
91  		/*
92  		 * (non-Javadoc)
93  		 * @see com.cosylab.gui.components.range2.TickParameters#measureTick(double, java.lang.String)
94  		 */
95  		public int measureTick(double position, String text)
96  		{
97  			//			System.out.println(fm.stringWidth(text));
98  			if(text==null) {
99  				return 0;
100 			}
101 			return fm.stringWidth(text);
102 		}
103 
104 		/*
105 		 * (non-Javadoc)
106 		 * @see com.cosylab.gui.components.range2.TickParameters#formatNumber(double)
107 		 */
108 		public String formatNumber(double value) {
109 			if (formatter != null)
110 				return formatter.sprintf(value);
111 			else
112 				return null;
113 		}
114 	}
115 
116 	private RangedValueController rangedValue;
117 
118 	/**
119 	 * Creates a new CosySliderUI object.
120 	 *
121 	 * @param range 
122 	 */
123 	public CosySliderUI(RangedValueController range)
124 	{
125 		super();
126 		rangedValue = range;
127 		alphaComposite = AlphaComposite.getInstance(AlphaComposite.DST_OVER,
128 			    (float)0.35);
129 	}
130 
131 	private void renderHorizontalTrailer(Graphics g)
132 	{
133 		int[] xPoints = { 1, 14, 15, 15, 8, 0, 0 };
134 		int[] yPoints = { 0, 0, 1, 9, 16, 9, 1 };
135 				
136 		g.setColor((checkValue ? Color.black : YELLOW_LED[0]));
137 		g.fillPolygon(xPoints, yPoints, xPoints.length);
138 	}
139 
140 	/**
141 	 * Returns the image used to display the trailer. This method uses lazy
142 	 * initialization and loads the image using the IconHelper class.
143 	 *
144 	 * @return BufferedImage
145 	 */
146 	protected BufferedImage getTrailer()
147 	{
148 		if(horizTrailerBuffer == null) {
149 			//Icon horizTrailerIcon = null;
150 
151 			//try {
152 			//	horizTrailerIcon = IconHelper.createIcon(
153 			//		    "/icons/specific/SliderTrailer16.gif");
154 			//	trailerRect.width = horizTrailerIcon.getIconWidth();
155 			//	trailerRect.height = horizTrailerIcon.getIconHeight();
156 
157 				trailerCache.width = trailerRect.width;
158 				trailerCache.height = trailerRect.height;
159 			//} catch(Exception e) {
160 			//}
161 
162 			horizTrailerBuffer = new BufferedImage(16, 16,
163 				    BufferedImage.TYPE_INT_ARGB);
164 
165 			Graphics2D gbi = horizTrailerBuffer.createGraphics();
166 			gbi.setComposite(alphaComposite);
167 
168 			renderHorizontalTrailer(gbi);
169 		}
170 
171 		return horizTrailerBuffer;
172 	}
173 
174 	protected void calculateTrailerLocation()
175 	{
176 		int t = (int)(rangedValue.toRelative(getTrailerPosition()));
177 
178 		if(slider.getOrientation() == JSlider.HORIZONTAL) {
179 			int valuePosition = xPositionForValue(t);
180 
181 			trailerRect.x = valuePosition - (trailerRect.width / 2);
182 			trailerRect.y = thumbRect.y;
183 		} else {
184 			int valuePosition = yPositionForValue(t);
185 
186 			trailerRect.x = thumbRect.x;
187 			trailerRect.y = valuePosition - (trailerRect.height / 2);
188 		}
189 	}
190 
191 	/**
192 	 * Returns the position of the slider trailer.
193 	 *
194 	 * @return double
195 	 */
196 	public double getTrailerPosition()
197 	{
198 		return trailerPosition;
199 	}
200 
201 	/**
202 	 * Sets the slider trailer position.
203 	 *
204 	 * @param value new position
205 	 */
206 	public void setTrailerPosition(double value)
207 	{
208 		trailerCache.x = trailerRect.x;
209 		trailerCache.y = trailerRect.y;
210 				
211 		trailerPosition = value;
212 		calculateTrailerLocation();
213 
214 		//System.out.println(">>>> Ranged Value: " + rangedValue.getValue());
215 		//System.out.println(">>>> Trailer Position: " + trailerPosition);
216 		
217 		boolean oldCheck = checkValue;
218 
219 		checkValue = (trailerPosition<=rangedValue.getMaximum() && trailerPosition>=rangedValue.getMinimum());
220 
221 		if (checkValue!=oldCheck) horizTrailerBuffer = null;
222 
223 		slider.repaint(trailerCache);
224 		slider.repaint(trailerRect);
225 		slider.repaint(new Rectangle(0, 0, slider.getWidth(), slider.getHeight()));
226 	}
227 
228 	/**
229 	 * Paints the trailer.
230 	 *
231 	 * @param g destination graphics
232 	 */
233 	public void paintTrailer(Graphics g)
234 	{	
235 		if (!paintTrailer) return;
236 		g.translate(trailerRect.x, thumbRect.y);
237 
238 		if(slider.getOrientation() == JSlider.HORIZONTAL) {
239 			((Graphics2D)g).drawImage(getTrailer(), null, 0, 0);
240 		} else {
241 		}
242 
243 		g.translate(-trailerRect.x, -thumbRect.y);
244 
245 		if(thumbRect.x == trailerRect.x) {
246 			paintThumb(g);
247 		}
248 	}
249 
250 	protected Color[] getIndicatorColor()
251 	{
252 		if (slider.isEnabled()) {
253 			if (paintTrailer) {
254 				if (!checkValue) {
255 					return RED_LED;
256 				} else {
257 					//retVal = (Math.abs(thumbRect.x - trailerRect.x) < 2) ? GREEN_LED : RED_LED;
258 					return (Math.abs(rangedValue.getValue() - trailerPosition) < tolerance) ? GREEN_LED : RED_LED;
259 				}
260 			} else {
261 				return GREEN_LED;
262 			}
263 		} else {
264 			return GREY_LED;
265 		}
266 	}
267 
268 	/*
269 	 * (non-Javadoc)
270 	 * @see javax.swing.plaf.metal.MetalSliderUI#paintThumb(java.awt.Graphics)
271 	 */
272 	public void paintThumb(Graphics g)
273 	{
274 		super.paintThumb(g);
275 		
276 		if (!paintTrailer) {
277 			return;
278 		}
279 
280 		Rectangle knobBounds = thumbRect;
281 		g.translate(knobBounds.x, knobBounds.y);
282 
283 		if(slider.getOrientation() == JSlider.HORIZONTAL) {
284 			Color[] colors = getIndicatorColor();
285 
286 			g.setColor(colors[0]);
287 			g.drawLine(1, 3, 13, 3);
288 			g.drawLine(13, 2, 13, 1);
289 
290 			g.setColor(colors[1]);
291 			g.drawLine(1, 1, 13, 1);
292 			g.drawLine(1, 1, 1, 3);
293 			g.setColor(colors[2]);
294 			g.drawLine(2, 2, 12, 2);
295 			g.setColor(Color.black);
296 			g.drawLine(1, 4, 13, 4);
297 		} else {
298 		}
299 
300 		g.translate(-knobBounds.x, -knobBounds.y);
301 	}
302 
303 	/*
304 	 * (non-Javadoc)
305 	 * @see javax.swing.plaf.basic.BasicSliderUI#paint(java.awt.Graphics, javax.swing.JComponent)
306 	 */
307 	public void paint(Graphics g, JComponent c)
308 	{
309 		super.paint(g, c);
310 		calculateTrailerLocation();
311 
312 		Rectangle clip = g.getClipBounds();
313 
314 		if(clip.intersects(trailerRect)) {
315 			paintTrailer(g);
316 		}
317 
318 		if(trailerRect.intersects(thumbRect)) {
319 			paintThumb(g);
320 		}
321 	}
322 
323 	/*
324 	 * (non-Javadoc)
325 	 * @see javax.swing.plaf.basic.BasicSliderUI#paintTicks(java.awt.Graphics)
326 	 */
327 	public void paintTicks(Graphics g)
328 	{
329 		final Rectangle tr = tickRect;
330 		final Rectangle lr = labelRect;
331 		int w = tr.width - 1;
332 
333 		g.setColor(slider.getBackground());
334 		g.fillRect(tr.x, tr.y, tr.width, tr.height);
335 
336 		final Tick[] ticks = rangedValue.calculateTicks(w,new TickMeasurer(g, format));
337 		
338 		if(ticks == null) {
339 			return;
340 		}
341 
342 		g.translate(tr.x, tr.y-2);
343 
344 		final FontMetrics fm = g.getFontMetrics();
345 		String text;
346 		int left;
347 		int x;
348 		double tickValue;
349 		for(int i = 0; i < ticks.length; i++) {
350 			x = (int)(ticks[i].proportional * w);
351 			if (showIntegerOnly) {
352 				tickValue = ticks[i].absolute;
353 				if(((long)(tickValue*1000L+0.5))/1000. !=  (long)(tickValue+0.5)) 
354 					continue;
355 //				else if (ticks[i].text != null && ticks[i].text.length() > 3){
356 //					//ignore everything behind decimal separator
357 //					ticks[i].text = ticks[i].text.substring(0, ticks[i].text.length()-2);
358 //				}
359 			}
360 			
361 			if(ticks[i].major) {
362 				paintMajorTickForHorizSlider(g, tr, x);
363 
364 				text = ticks[i].text;
365 				if(text==null) {
366 					left = x;
367 				} else {
368 				    left = x - (fm.stringWidth(text) / 2);
369 				}
370 
371 //				g.setColor(Color.BLACK);
372 				g.setColor(slider.getForeground());
373 				if(text!=null) {
374 					g.drawString(text, left, lr.y - 8);
375 				}
376 			} else {
377 				paintMinorTickForHorizSlider(g, tr, x);
378 			}
379 		}
380 
381 		g.translate(-tr.x, -tr.y+2);
382 	}
383 
384 	/*
385 	 * (non-Javadoc)
386 	 * @see javax.swing.plaf.basic.BasicSliderUI#paintLabels(java.awt.Graphics)
387 	 */
388 	public void paintLabels(Graphics g)
389 	{
390 		paintTicks(g);
391 	}
392 
393 	/*
394 	 * (non-Javadoc)
395 	 * @see javax.swing.plaf.metal.MetalSliderUI#scrollDueToClickInTrack(int)
396 	 */
397 	protected void scrollDueToClickInTrack(int dir)
398 	{
399 		// This has been disabled to prevent accidental value changes.
400 		// super.scrollDueToClickInTrack(dir);
401 	}
402 	
403 	/**
404 	 * Sets the value display format.
405 	 * 
406 	 * @param format new format
407 	 */
408 	public void setFormat(String format) {
409 		this.format = format;
410 		if (format.contains("%d")) {
411 			showIntegerOnly = true;
412 		} else {
413 			showIntegerOnly = false;
414 		}
415 	}
416 	
417 	/**
418 	 * Sets the tolerance. This is the tolerance at which the thumb is considered
419 	 * to be synchronized with the trailer.
420 	 * 
421 	 * @param tol new tolerance
422 	 */
423 	public void setTolerance(double tol) {
424 		this.tolerance=tol;
425 	}
426 	
427 	/**
428 	 * Returns the tolerance.
429 	 * 
430 	 * @return
431 	 */
432 	public double getTolerance() {
433 		return this.tolerance;
434 	}
435 	
436 	/**
437 	 * Sets the flag whether the trailer should be painted or not.
438 	 * @param paint
439 	 */
440 	public void setPaintTrailer(boolean paint) {
441 		this.paintTrailer = paint;
442 		slider.repaint();
443 	}
444 	
445 	/**
446 	 * Returns a flag indicating whether the trailer is painted or not.
447 	 * @return
448 	 */
449 	public boolean isPaintTrailer() {
450 		return this.paintTrailer;
451 	}
452 
453 	/**
454 	 * Returns the format used by this ui to format the ticks.
455 	 * @return
456 	 */
457 	public String getFormat() {
458 		return format;
459 	}
460 }
461 /* __oOo__ */