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;
21  
22  import java.awt.AlphaComposite;
23  import java.awt.Color;
24  import java.awt.Dimension;
25  import java.awt.Font;
26  import java.awt.GradientPaint;
27  import java.awt.Graphics;
28  import java.awt.Graphics2D;
29  import java.awt.GridLayout;
30  import java.awt.Rectangle;
31  import java.awt.event.ActionEvent;
32  import java.awt.event.ComponentAdapter;
33  import java.awt.event.ComponentEvent;
34  import java.awt.event.WindowAdapter;
35  import java.awt.event.WindowEvent;
36  import java.awt.image.BufferedImage;
37  import java.beans.Beans;
38  import java.util.Timer;
39  import java.util.TimerTask;
40  import javax.swing.AbstractAction;
41  import javax.swing.JComponent;
42  import javax.swing.JPanel;
43  import com.cosylab.application.state.State;
44  import com.cosylab.application.state.StateFactory;
45  import com.cosylab.application.state.StateOriginator;
46  import com.cosylab.gui.components.customizer.AbstractCustomizerPanel;
47  import com.cosylab.gui.components.range2.RangedValueController;
48  import com.cosylab.gui.components.range2.LinearRange;
49  import com.cosylab.gui.components.range2.LogarithmicRange;
50  import com.cosylab.gui.components.range2.RangedValueEvent;
51  import com.cosylab.gui.components.range2.RangedValueListener;
52  import com.cosylab.gui.components.range2.RangedValuePolicy;
53  import com.cosylab.gui.components.range2.Tick;
54  import com.cosylab.gui.components.range2.TickParameters;
55  import com.cosylab.gui.components.range2.TrimValuePolicy;
56  import com.cosylab.gui.components.util.ActionList;
57  import com.cosylab.gui.components.util.ColorHelper;
58  import com.cosylab.gui.components.util.FontHelper;
59  import com.cosylab.gui.components.util.PaintHelper;
60  import com.cosylab.gui.components.util.PopupManageable;
61  import com.cosylab.gui.components.util.PopupManager;
62  import com.cosylab.gui.components.util.ScreenCapturer;
63  import com.cosylab.util.PrintfFormat;
64  
65  
66  /**
67   * A visual component for displaying numerical (<code>double</code>) values
68   * using a liquid partially filled vertical tube visualisation. A value label
69   * above the pipe displays the textural value together vith possible units.
70   *
71   * @author <a href="mailto:jernej.kamenik@cosylab.com">Jernej Kamenik</a>
72   * @version $id$
73   */
74  public class Piper extends JComponent implements PopupManageable,
75  	StateOriginator
76  {
77  	/**
78  	 * Defines the orietation of the piper. Orientation defines how the piper is
79  	 * drawn, on which side are max/min. 
80  	 * <ul>
81  	 * <li> BOTTOM_UP - lower bound is on the bottom, pipe is filled from bottom to top </li>
82  	 * <li> LEFT_RIGHT - lower bound is on the left, pipe is filled from left to right </li>
83  	 * <li> RIGHT_LEFT - lower bound is on the right, pipe is filled from right to left </li>
84  	 * <li> TOP_DOWN - lower bound is on the top, pipe is filled from the top to the bottom </li>
85  	 * </ul>
86  	 * @author <a href="mailto:jaka.bobnar@cosylab.com">Jaka Bobnar</a>
87  	 * @version $Id: Piper.java,v 1.30 2008-05-19 06:49:05 jbobnar Exp $
88  	 *
89  	 */
90  	public enum LayoutOrientation {
91  		BOTTOM_UP, LEFT_RIGHT, TOP_DOWN, RIGHT_LEFT
92  	}
93  		
94  	private static final long serialVersionUID = 1L;
95  	/*
96  	 * An extension of Timer used for periodic tilting of the Wheelswitch.
97  	 *
98  	 * @author <a href="mailto:jernej.kamenik@cosylab.com">Jernej Kamenik</a>
99  	 * @version $id$
100 	 *
101 	 */
102 	protected class TiltHandler extends Timer
103 	{
104 		private class TiltTask extends TimerTask
105 		{
106 			/**
107 			 * DOCUMENT ME!
108 			 */
109 			public void run()
110 			{
111 				if (numberOfTilts >= MAX_NUMBER_OF_TILTS) {
112 					cancel();
113 					tilting = false;
114 				} else {
115 					numberOfTilts++;
116 					tilting = !tilting;
117 				}
118 
119 				renderer.clearBuffer();
120 				repaint();
121 			}
122 		}
123 
124 		private final int MAX_NUMBER_OF_TILTS = 3;
125 		private final long TILT_RATE = 200;
126 		private int numberOfTilts = MAX_NUMBER_OF_TILTS;
127 
128 		/**
129 		 * Scedules a new tilting task if the user value equals any of the
130 		 * bounds.
131 		 */
132 		public void tilt()
133 		{
134 			if (numberOfTilts >= MAX_NUMBER_OF_TILTS) {
135 				numberOfTilts = 0;
136 				schedule(new TiltTask(), 0, TILT_RATE);
137 			} else {
138 				numberOfTilts = 0;
139 			}
140 		}
141 	}
142 
143 	private class PiperRenderer implements TickParameters /* implements TickCallback - edit apikus */
144 	{
145 		private Graphics2D g;
146 		private Rectangle pipeRect;
147 		private Rectangle pipeRectOrg;
148 		private int arcRadius;
149 		private BufferedImage pipeImage;
150 		private BufferedImage scaleImage;
151 		private BufferedImage tickImage;
152 		private final Color DARK_COLOR = new Color(0, 0, 10);
153 		private final Color LIGHT_COLOR = new Color(50, 150, 250);
154 		private final Color OUT_OF_BOUNDS_DARK_COLOR = new Color(250, 120, 10);
155 		private final Color OUT_OF_BOUNDS_LIGHT_COLOR = new Color(250, 250, 50);
156 		private Color darkColor;
157 		private Color ligtColor;
158 		private Color outOfBoundsDarkColor;
159 		private Color outOfBoundsLightColor;
160 		private Tick[] ticks;
161 		
162 		public PiperRenderer() {
163 			darkColor = DARK_COLOR;
164 			ligtColor = LIGHT_COLOR;
165 			outOfBoundsDarkColor = OUT_OF_BOUNDS_DARK_COLOR;
166 			outOfBoundsLightColor = OUT_OF_BOUNDS_LIGHT_COLOR;
167 		}
168 
169 		private int getPiperWidth() {
170 			if (orientation == LayoutOrientation.BOTTOM_UP || orientation == LayoutOrientation.TOP_DOWN) {
171 				return getWidth();
172 			} else if (orientation == LayoutOrientation.LEFT_RIGHT || orientation == LayoutOrientation.RIGHT_LEFT) {
173 				return getHeight();
174 			}
175 			return 0;
176 		}
177 		
178 		private int getPiperHeight() {
179 			if (orientation == LayoutOrientation.BOTTOM_UP || orientation == LayoutOrientation.TOP_DOWN) {
180 				return getHeight();
181 			} else if (orientation == LayoutOrientation.LEFT_RIGHT || orientation == LayoutOrientation.RIGHT_LEFT) {
182 				return getWidth();
183 			}
184 			return 0;
185 		}
186 		
187 		private int transformXToOrientation(int x, int piperWidth) {
188 			if (orientation == LayoutOrientation.TOP_DOWN) {
189 				return getWidth() - x - piperWidth;
190 			} else if (orientation == LayoutOrientation.RIGHT_LEFT) {
191 				return getHeight() - x - piperWidth;
192 			} else {
193 				return x;
194 			}
195 		}
196 		
197 		/**
198 		 * Clears image buffers.
199 		 */
200 		public void clearBuffer() {
201 			synchronized (renderer) {
202 				pipeImage = null;
203 				scaleImage = null;
204 				tickImage = null;
205 				pipeRect = null;
206 				pipeRectOrg = null;
207 				ticks = null;
208 				arcRadius = 0;
209 			}
210 		}
211 		
212 		/**
213 		 * Gets the size of the scale text.
214 		 *
215 		 * @param position double
216 		 * @param text String
217 		 *
218 		 * @return int
219 		 *
220 		 * @see com.cosylab.gui.components.range.TickCallback#measureTick(double,
221 		 *      java.lang.String)
222 		 */
223 		public int measureTick(double position, String text)
224 		{
225 			if (orientation == LayoutOrientation.BOTTOM_UP || orientation == LayoutOrientation.TOP_DOWN) {
226 				return g.getFontMetrics().getHeight();
227 			} else {
228 				return g.getFontMetrics().stringWidth(text);
229 			}
230 		}
231 
232 		/**
233 		 * Paints the liquid fill buffered image on the Graphics supplied. If
234 		 * no buffer exists, new one is created.
235 		 *
236 		 * @param g Graphics2D
237 		 */
238 		public void paintFill(Graphics2D g)
239 		{
240 			//setPipeRect(g);
241 
242 			//System.out.println("FILL " + Piper.this.getSize() + " " + pipeRect);
243 
244 			int fillHeight = Math.max(1,
245 				    (int)Math.round(
246 				        rangedValue.getRelativeValue() * (pipeRect.height
247 				        - 2 * arcRadius)));
248 			int fillWidth = pipeRect.width;
249 
250 			BufferedImage fillImage = new BufferedImage(fillWidth,
251 				    fillHeight + arcRadius, BufferedImage.TYPE_4BYTE_ABGR);
252 			Graphics2D g2D = (Graphics2D)fillImage.getGraphics();
253 			prepareGraphics(g2D);
254 
255 			boolean checkValue = (getValue() >= getMinimum()
256 				&& getValue() <= getMaximum());
257 
258 			Color lightColor = (checkValue ? this.ligtColor
259 				: outOfBoundsLightColor);
260 			Color darkColor = (checkValue ? this.darkColor : outOfBoundsDarkColor);
261 
262 			if (enhanced) {
263 				g2D.setPaint(new GradientPaint(0.f, 0.f, lightColor,
264 				        fillWidth, fillHeight, darkColor));
265 			} else {
266 				g2D.setColor(lightColor);
267 			}
268 
269 			g2D.fillRect(0, 0, fillWidth, fillHeight);
270 			g2D.fillArc(0, fillHeight - arcRadius, 2 * arcRadius,
271 			    2 * arcRadius, 180, 270);
272 			g2D.fillArc(fillWidth - 2 * arcRadius, fillHeight - arcRadius,
273 			    2 * arcRadius, 2 * arcRadius, 270, 360);
274 
275 			if (arcRadius < fillWidth) {
276 				g2D.fillRect(arcRadius, fillHeight, fillWidth - 2 * arcRadius,
277 				    arcRadius);
278 			}
279 
280 			if (enhanced) {
281 				g.setComposite(AlphaComposite.getInstance(
282 				        AlphaComposite.SRC_OVER, 0.5f));
283 			}
284 			
285 			g.drawImage(fillImage, null, pipeRect.x,
286 			    pipeRect.y + pipeRect.height - arcRadius - fillHeight);
287 
288 			//System.out.println("VALUE "+rangedValue.getRelativeValue()+" "+fillHeight+" "+pipeRect.y+" "+ pipeRect.height +" "+ arcRadius +" "+ fillHeight);
289 			if (enhanced) {
290 				g.setComposite(AlphaComposite.getInstance(
291 				        AlphaComposite.SRC_OVER, 1.f));
292 			}
293 		}
294 
295 		/**
296 		 * Paints the value label showing the actual value dipalyed on the
297 		 * Graphics supplied.
298 		 *
299 		 * @param g Graphics2D
300 		 */
301 		public void paintLabel(Graphics2D g)
302 		{
303 			//setPipeRect(g);
304 
305 			String unitText = units;
306 
307 			if (units == null && Beans.isDesignTime()) {
308 				unitText = "<u>";
309 			}
310 
311 			if (unitText == null || !unitsVisible) {
312 				unitText = "";
313 			}
314 
315 			String text;
316 
317 			try {
318 				//text = formatter.sprintf(labelValue); /* edit apikus */
319 				text = this.formatNumber(labelValue);
320 			} catch (Exception e) {
321 				text = Double.toString(labelValue);
322 			}
323 
324 			text += (units == null ? "" : " " + unitText);
325 
326 			int textWidth = g.getFontMetrics().stringWidth(text);
327 			int textHeight = g.getFontMetrics().getAscent();
328 
329 			// workaround - Java bug
330 			textHeight = (int)(textHeight * 0.7) + 1;
331 			int botLeftX;
332 			int botLeftY;
333 			
334 			if (orientation == LayoutOrientation.LEFT_RIGHT || orientation == LayoutOrientation.RIGHT_LEFT) {
335 				botLeftX = pipeRectOrg.y + (pipeRectOrg.height - textWidth) / 2;
336 				botLeftY = getHeight() - (getHeight() - (pipeRectOrg.x + pipeRectOrg.width))/2 + textHeight;
337 			} else {
338 				botLeftX = pipeRectOrg.x + (pipeRectOrg.width - textWidth) / 2;
339 				botLeftY = pipeRectOrg.y + pipeRectOrg.height + (textHeight + pipeRectOrg.y) / 2;
340 				if (orientation == LayoutOrientation.BOTTOM_UP) {
341 					if (!titleVisible) {
342 						botLeftY += g.getFontMetrics().getHeight()/2;
343 					}
344 				} else {
345 					if (!titleVisible) {
346 						botLeftY -= g.getFontMetrics().getHeight();
347 					}
348 				}
349 			}
350 			
351 			
352 
353 			g.setColor((isEnabled() ? Color.BLACK : Color.GRAY));
354 			g.drawString(text, botLeftX, botLeftY);
355 		}
356 
357 		/**
358 		 * Paints the pipe buffered image on the Graphics supplied. If no
359 		 * buffer exists, new one is created.
360 		 *
361 		 * @param g Graphics2D
362 		 */
363 		public void paintPipe(Graphics2D g)
364 		{
365 			//setPipeRect(g);
366 
367 			if (pipeImage == null) {
368 				//Debug.out("New pipe image created.");
369 				pipeImage = new BufferedImage(pipeRect.width, pipeRect.height,
370 					    BufferedImage.TYPE_4BYTE_ABGR);
371 
372 				Graphics2D g2D = (Graphics2D)pipeImage.getGraphics();
373 				prepareGraphics(g2D);
374 				g2D.setColor((tilting ? ColorHelper.getAlarm() : Color.WHITE));
375 				g2D.fillRoundRect(0, 0, pipeRect.width, pipeRect.height,
376 				    2 * arcRadius, 2 * arcRadius);
377 			}
378 			
379 			g.drawImage(pipeImage, null, pipeRect.x, pipeRect.y);
380 		}
381 
382 		private Dimension getTickDim(Graphics g, int pipeHeigth)
383 		{
384 			Tick[] tcks = getTicks(pipeHeigth);
385 			int tckW = 0;
386 			int maxLen = 0;
387 
388 			for (int i = 0; i < tcks.length; i++) {
389 				if (tcks[i] != null && tcks[i].text != null
390 				    && tcks[i].text.length() > maxLen) {
391 					int newW = g.getFontMetrics().stringWidth(tcks[i].text);
392 					tckW = Math.max(tckW, newW);
393 				}
394 			}
395 
396 			return new Dimension(tckW, g.getFontMetrics().getHeight());
397 		}
398 
399 		private void setPipeRect(Graphics2D g)
400 		{
401 			if (pipeRect == null) {
402 
403 				this.g=g;
404 
405 				int compW = getPiperWidth();
406 				int compH = getPiperHeight();
407 				int x;
408 				int y;
409 				int w;
410 				int h;
411 
412 				y = Math.max(1, (int)(6 * Math.atan(compH / 100)));
413 				if (orientation == LayoutOrientation.TOP_DOWN) {
414 					if (titleVisible) {
415 						y += g.getFontMetrics().getHeight();
416 						h = Math.max(1, compH - 2 * y);
417 					} else { 
418 						y += g.getFontMetrics().getHeight();
419 						h = Math.max(1, compH - 2 * y + g.getFontMetrics().getHeight());
420 					}
421 				} else if (orientation == LayoutOrientation.BOTTOM_UP) {
422 					if (titleVisible) {
423 						y += g.getFontMetrics().getHeight();
424 						h = Math.max(1, compH - 2 * y);
425 					} else { 
426 						h = Math.max(1, compH - 2 * y - g.getFontMetrics().getHeight());
427 					}
428 
429 				} else {
430 					h = Math.max(1, compH - 2 * y);
431 				}
432 
433 				Dimension tickDim = getTickDim(g, h);
434 
435 				if (stretch) {
436 					x = 10;
437 					w = Math.max(1, compW - tickDim.width * 3 / 2 - 2 * x);
438 				} else {
439 					x = compW * 1 / 5;
440 
441 					if (10 + tickDim.width > x) {
442 						x = (2 * x - 10 - tickDim.width) / 2 + 5;
443 						w = Math.max(1, compW - x - 10 - tickDim.width);
444 					} else {
445 						w = Math.max(1, compW - 2 * x);
446 					}
447 				}
448 				
449 				pipeRect = new Rectangle(transformXToOrientation(x,w), y, w, h);
450 				pipeRectOrg = new Rectangle(x, y, w, h);
451 				g=null;
452 
453 				setArcRadius(pipeRect);
454 			}
455 		}
456 
457 		private void setArcRadius(Rectangle pipeRect)
458 		{
459 			double ratio = pipeRect.height / (double)pipeRect.width;
460 
461 			if (ratio > 5) {
462 				arcRadius = pipeRect.width / 2;
463 			} else {
464 				arcRadius = (int)Math.max(ratio / 5 * pipeRect.width / 2, 2);
465 			}
466 		}
467 
468 		/**
469 		 * Paints the scale buffered image on the Graphics supplied. If no
470 		 * buffer exists, new one is created.
471 		 *
472 		 * @param g Graphics2D
473 		 */
474 		public void paintScale(Graphics2D g)
475 		{
476 			g.drawImage(getScaleImage(), null, 0, 0);
477 		}
478 		
479 		public void paintTickLabels(Graphics2D g) {
480 			
481 			g.drawImage(getTickImage(), null, 0, 0);
482 		}
483 
484 		/**
485 		 * Paints the title.
486 		 *
487 		 * @param g
488 		 */
489 		public void paintTitle(Graphics2D g)
490 		{
491 			//setPipeRect(g);
492 
493 			String text = title;
494 
495 			if (title == null && Beans.isDesignTime()) {
496 				text = "<title>";
497 			}
498 
499 			if (text == null || text.length() == 0) {
500 				return;
501 			}
502 
503 			int textWidth = g.getFontMetrics().stringWidth(text);
504 			int textHeight = g.getFontMetrics().getAscent();
505 
506 			// workaround - Java bug
507 			textHeight = (int)(textHeight * 0.7) + 1;
508 			int botLeftX;
509 			int botLeftY;
510 			
511 			if (orientation == LayoutOrientation.LEFT_RIGHT || orientation == LayoutOrientation.RIGHT_LEFT) {
512 				botLeftX = pipeRect.y + (pipeRect.height - textWidth) / 2;
513 				botLeftY = (textHeight + pipeRectOrg.x) / 2;
514 			} else {
515 				botLeftX = pipeRectOrg.x + (pipeRectOrg.width - textWidth) / 2;
516 				botLeftY = (textHeight + pipeRectOrg.y) / 2;
517 			}
518 			
519 
520 			g.setColor((isEnabled() ? Color.BLACK : Color.GRAY));
521 
522 			//g.setFont(FontHelper.getFontWithSize(g.getFont().getSize()*12/10,g.getFont()));
523 			g.drawString(text, botLeftX, botLeftY);
524 		}
525 
526 		protected void prepareGraphics(Graphics2D g)
527 		{
528 			Font f = FontHelper.getDefaultFont();
529 
530 			if (resizable) {
531 				int fontHeight = 5 + Math.min(getWidth(), getHeight() / 2) / 15;
532 				if (orientation == LayoutOrientation.LEFT_RIGHT || orientation == LayoutOrientation.RIGHT_LEFT) {
533 					f = FontHelper.getFontWithSize((int)(fontHeight*1.5), f);
534 				} else {
535 					f = FontHelper.getFontWithSize((int)(fontHeight*1.2), f);
536 				}
537 //				f = FontHelper.getFontWithSize(fontHeight, f);
538 			}
539 
540 			g.setFont(f);
541 
542 			if (enhanced) {
543 				g.addRenderingHints(PaintHelper.getAntialiasingHints());
544 			}
545 		}
546 
547 		private BufferedImage getScaleImage()
548 		{
549 			if (scaleImage == null) {
550 				//Debug.out("New scale image created.");
551 				int width = getPiperWidth();
552 				int height = getPiperHeight();
553 
554 				scaleImage = new BufferedImage(width, height,
555 					    BufferedImage.TYPE_4BYTE_ABGR);
556 
557 				Graphics2D g2D = (Graphics2D)scaleImage.getGraphics();
558 				prepareGraphics(g2D);
559 				g2D.setColor((isEnabled() ? Color.BLACK : Color.GRAY));
560 
561 				Font oldFont = g2D.getFont();
562 				g2D.setFont(oldFont.deriveFont(Font.PLAIN,
563 				        oldFont.getSize2D() * 0.8f));
564 
565 				//setPipeRect(g2D);
566 
567 				/*System.out.println("TICKS " + Piper.this.getSize() + " "
568 				    + pipeRect);*/
569 
570 				int spaceWidth = width - pipeRect.x - pipeRect.width;
571 
572 				int scaleHeight = pipeRect.height - 2 * arcRadius;
573 
574 				int tickLength = Math.max(pipeRect.width * 3 / 4, 5);
575 				int minorTickLength = tickLength / 2;
576 
577 				if (pipeRect.width > 200) {
578 					tickLength = 150 + (pipeRect.width - 200) * 1 / 3;
579 					minorTickLength = 75;
580 				}
581 
582 				// Offset from the right edge of the pipe
583 				Dimension tickDim = getTickDim(g2D, pipeRect.height);
584 				int offsetX = tickDim.height / 2;
585 
586 				if (2 * offsetX + tickDim.width > spaceWidth) {
587 					offsetX = Math.max(1, (spaceWidth - tickDim.width) / 2);
588 				}
589 
590 				Tick[] ticks = getTicks(scaleHeight);
591 
592 				if (ticks == null) {
593 					return scaleImage;
594 				}
595 
596 				for (int i = 0; i < ticks.length; i++) {
597 					if (ticks[i].major) {
598 						g2D.drawLine(pipeRect.x
599 						    + (pipeRect.width - tickLength) / 2,
600 						    pipeRect.y + arcRadius
601 						    + (int)(scaleHeight * (1. - ticks[i].proportional)),
602 						    pipeRect.x + (pipeRect.width + tickLength) / 2 - 1,
603 						    pipeRect.y + arcRadius
604 						    + (int)(scaleHeight * (1. - ticks[i].proportional)));
605 					} else {
606 						g2D.drawLine(pipeRect.x
607 						    + (pipeRect.width - minorTickLength) / 2,
608 						    pipeRect.y + arcRadius
609 						    + (int)(scaleHeight * (1. - ticks[i].proportional)),
610 						    pipeRect.x + (pipeRect.width + minorTickLength) / 2
611 						    - 1,
612 						    pipeRect.y + arcRadius
613 						    + (int)(scaleHeight * (1. - ticks[i].proportional)));
614 					}
615 				}
616 			}
617 
618 			return scaleImage;
619 		}
620 		
621 		private BufferedImage getTickImage()
622 		{
623 			if (tickImage == null) {
624 				int width;
625 				int height;
626 				if (orientation == LayoutOrientation.BOTTOM_UP || orientation == LayoutOrientation.TOP_DOWN) {
627 					width = getPiperWidth();
628 					height = getPiperHeight();	
629 				} else {
630 					width = getPiperHeight();
631 					height = getPiperWidth();
632 				}
633 				
634 				tickImage = new BufferedImage(width, height,
635 					    BufferedImage.TYPE_4BYTE_ABGR);
636 				
637 				Graphics2D g2D = (Graphics2D)tickImage.getGraphics();
638 				prepareGraphics(g2D);
639 				g2D.setColor((isEnabled() ? Color.BLACK : Color.GRAY));
640 
641 				Font oldFont = g2D.getFont();
642 				g2D.setFont(oldFont.deriveFont(Font.PLAIN,
643 				        oldFont.getSize2D() * 0.8f));
644 
645 				int spaceWidth = width - pipeRectOrg.x - pipeRectOrg.width;
646 				int scaleHeight = pipeRect.height - 2 * arcRadius;
647 
648 				Dimension tickDim = getTickDim(g2D, pipeRectOrg.height);
649 				int offsetX = tickDim.height / 2;
650 
651 				if (orientation == LayoutOrientation.TOP_DOWN || orientation == LayoutOrientation.BOTTOM_UP) {
652 					if (2 * offsetX + tickDim.width > spaceWidth) {
653 						offsetX = Math.max(1, (spaceWidth - tickDim.width) / 2);
654 					}
655 				}				
656 
657 				Tick[] ticks = getTicks(scaleHeight);
658 
659 				if (ticks == null) {
660 					return tickImage;
661 				}
662 
663 				int textPosX = pipeRectOrg.x + pipeRectOrg.width + offsetX;
664 								
665 				if (orientation == LayoutOrientation.TOP_DOWN) {
666 					int y0 = pipeRectOrg.y;
667 					if (!titleVisible) {
668 						y0 -= g.getFontMetrics().getHeight();
669 					}
670 					for (int i = 0; i < ticks.length; i++) {
671 						if (ticks[i].major) {
672 							g2D.drawString(ticks[i].text,
673 									textPosX,
674 									pipeRectOrg.y + arcRadius
675 								    + (int)(scaleHeight * (ticks[i].proportional))
676 								    + 5);	
677 						} 
678 					}
679 				} else if(orientation == LayoutOrientation.BOTTOM_UP) {
680 					for (int i = 0; i < ticks.length; i++) {
681 						if (ticks[i].major) {
682 							g2D.drawString(ticks[i].text,
683 									textPosX,
684 									pipeRectOrg.y + arcRadius
685 								    + (int)(scaleHeight * (1-ticks[i].proportional))
686 								    + 5);	
687 						} 
688 					}
689 				} else if (orientation == LayoutOrientation.LEFT_RIGHT){
690 					for (int i = 0; i < ticks.length; i++) {
691 						if (ticks[i].major && ticks[i].text != null && ticks[i].text.length() != 0) {
692 							g2D.drawString(ticks[i].text,
693 									pipeRectOrg.y + arcRadius
694 								    + (int)(scaleHeight * (ticks[i].proportional))
695 								    - tickDim.width/2, textPosX + 5);
696 						} 
697 					}
698 				} else if (orientation == LayoutOrientation.RIGHT_LEFT) {
699 					for (int i = 0; i < ticks.length; i++) {
700 						if (ticks[i].major) {
701 							g2D.drawString(ticks[i].text,
702 									pipeRectOrg.y + arcRadius
703 								    + (int)(scaleHeight * (1-ticks[i].proportional))
704 								    - tickDim.width/2, textPosX + 5);	
705 						} 
706 					}
707 				}
708 				
709 			}
710 
711 			return tickImage;
712 		}
713 
714 		private Tick[] getTicks(int height)
715 		{
716 			if (ticks == null) {
717 				//ticks = rangedValue.getRange().getTicks(height).toTicks(this); /* edit apikus */
718 				ticks = rangedValue.calculateTicks(height,this);
719 			}
720 
721 			return ticks;
722 		}
723 		
724 		public String formatNumber(double x) { /* Use this method - edit apikus */
725 			return formatter.sprintf(x);
726 		}
727 	}
728 
729 	private class ResizeListener extends ComponentAdapter
730 	{
731 		/**
732 		 * Listens for component events and resizes the component accordingly.
733 		 *
734 		 * @param e ComponentEvent
735 		 *
736 		 * @see java.awt.event.ComponentListener#componentResized(ComponentEvent)
737 		 */
738 		public void componentResized(ComponentEvent e)
739 		{
740 			Piper.this.setSize(getBounds().width, getBounds().height);
741 			renderer.clearBuffer(); /* edit apikus */
742 			repaint();
743 		}
744 	}
745 
746 	private class ValueListener implements RangedValueListener
747 	{
748 		/**
749 		 * Listens for value changes and redraws the component accordingly.
750 		 *
751 		 * @param event RangedValueEvent
752 		 *
753 		 * @see com.cosylab.gui.components.range.RangedValueListener#valueChanged(RangedValueEvent)
754 		 */
755 		public void valueChanged(RangedValueEvent event)
756 		{
757 			if (event.isMinimumChanged() || event.isMaximumChanged()) {
758 				renderer.clearBuffer();
759 
760 				if (!isEnabled()) {
761 					return;
762 				}
763 				repaint();
764 			}
765 
766 			if (event.isValueChanged()) {
767 				if (!isEnabled()) {
768 					return;
769 				}
770 				repaint();
771 			}
772 		}
773 	}
774 
775 	private RangedValuePolicy policy;
776 	private AbstractCustomizerPanel customizer;
777 	private PiperRenderer renderer;
778 	private PopupManager popupManager;
779 	private PrintfFormat formatter;
780 	//private RangedValue rangedValue; /* edit apikus */
781 	private RangedValueController rangedValue;
782 	private String format;
783 	private String title;
784 	private boolean titleVisible;
785 	private int titleMinimumFontSize;
786 	private int titleMaximumFontSize;
787 	private String units;
788 	private TiltHandler tiltHandler;
789 	private boolean enhanced = true;
790 	private boolean logarithmic;
791 	private boolean popupEnabled;
792 	private boolean resizable = true;
793 	private boolean stretch = false;
794 	private boolean tilting;
795 	private boolean tiltingEnabled;
796 	private double labelValue;
797 	private boolean unitsVisible = true;
798 	
799 	private LayoutOrientation orientation = LayoutOrientation.BOTTOM_UP;
800 
801 	/**
802 	 * Creates a new Piper displaying the specified value.
803 	 *
804 	 * @param newValue double
805 	 */
806 	public Piper(double newValue)
807 	{
808 		super();
809 
810 		renderer = new PiperRenderer();
811 
812 		format = "%3.2f";
813 		formatter = new PrintfFormat(format);
814 
815 		policy = new TrimValuePolicy();
816 
817 		labelValue = newValue;
818 
819 		rangedValue = new RangedValueController(); /* edit apikus */
820 		rangedValue.setValue(0, 1, newValue);
821 		rangedValue.setRange(new LinearRange());
822 		rangedValue.addPolicy(policy);
823 		rangedValue.addRangedValueListener(new ValueListener());
824 
825 		addComponentListener(new ResizeListener());
826 
827 		setBackground(ColorHelper.getCosyControl());
828 		setPreferredSize(new Dimension(150, 300));
829 		setMinimumSize(new Dimension(60, 100));
830 		setVisible(true);
831 		setOpaque(true);
832 		setPopupEnabled(true);
833 	}
834 
835 	/**
836 	 * Creates a new Piper with zero value.
837 	 */
838 	public Piper()
839 	{
840 		this(0);
841 	}
842 
843 	/**
844 	 * Returns the Customizer intance for this displayer.
845 	 *
846 	 * @return the Customizer intance for this displayer
847 	 */
848 	public AbstractCustomizerPanel getCustomizer()
849 	{
850 		if (customizer == null) {
851 			customizer = AbstractCustomizerPanel.findCustomizer(this);
852 		}
853 
854 		return customizer;
855 	}
856 
857 	/**
858 	 * Triggers on and off the visually enhanced mode of the Piper. When
859 	 * enhanced, the liquid filling is painted with a gradient color and
860 	 * anti-aliasing is used.
861 	 *
862 	 * @param enhanced
863 	 */
864 	public void setEnhanced(boolean enhanced)
865 	{
866 		if (this.enhanced == enhanced) {
867 			return;
868 		}
869 
870 		boolean oldEnhanced = this.enhanced;
871 		this.enhanced = enhanced;
872 		renderer.clearBuffer(); /* edit apikus */
873 		repaint();
874 		firePropertyChange("enhanced", oldEnhanced, enhanced);
875 	}
876 
877 	/**
878 	 * Returns wether the component visualisation is enhanced.
879 	 *
880 	 * @return boolean
881 	 */
882 	public boolean isEnhanced()
883 	{
884 		return enhanced;
885 	}
886 
887 	/**
888 	 * Sets the format of the value label text.
889 	 *
890 	 * @param format The format to set
891 	 */
892 	public void setFormat(String format)
893 	{
894 		String oldFormat = this.format;
895 
896 		if (format == null && oldFormat == null
897 		    || format != null && format.equals(oldFormat)) {
898 			return;
899 		}
900 
901 		this.format = format;
902 		formatter = new PrintfFormat(format);
903 		firePropertyChange("format", oldFormat, format);
904 		renderer.clearBuffer();
905 		if (!isEnabled()) {
906 			return;
907 		}
908 		
909 		repaint();
910 	}
911 
912 	/**
913 	 * Returns the format of the value label text.
914 	 *
915 	 * @return String
916 	 */
917 	public String getFormat()
918 	{
919 		return format;
920 	}
921 
922 	/**
923 	 * Sets the new maximum value displayed at the top of the scale of the
924 	 * Piper. This method delegates all logic to the RangedValue
925 	 * implementation.
926 	 *
927 	 * @param newMaximum double
928 	 */
929 	public void setMaximum(double newMaximum)
930 	{
931 		double oldMaximum = rangedValue.getMaximum();
932 
933 		//if (oldMaximum == newMaximum) {
934 		//	return;
935 		//}
936 		rangedValue.setValue(rangedValue.getMinimum(), newMaximum,
937 		    rangedValue.getValue());
938 		firePropertyChange("maximum", oldMaximum, newMaximum);
939 	}
940 
941 	/**
942 	 * Returns the maximum value displayed at the top of the scale of the
943 	 * Piper.
944 	 *
945 	 * @return double
946 	 */
947 	public double getMaximum()
948 	{
949 		return rangedValue.getMaximum();
950 	}
951 
952 	/**
953 	 * Sets the new minimum value displayed at the bottom of the scale of the
954 	 * Piper. This method delegates all logic to the RangedValue
955 	 * implementation.
956 	 *
957 	 * @param newMinimum double
958 	 */
959 	public void setMinimum(double newMinimum)
960 	{
961 		double oldMinimum = rangedValue.getMinimum();
962 
963 		//if (oldMinimum == newMinimum) {
964 		//	return;
965 		//}
966 		rangedValue.setValue(newMinimum, rangedValue.getMaximum(),
967 		    rangedValue.getValue());
968 		firePropertyChange("minimum", oldMinimum, newMinimum);
969 	}
970 
971 	/**
972 	 * Gets the minimum value displayed at the bottom of the scale of the
973 	 * Piper.
974 	 *
975 	 * @return double
976 	 */
977 	public double getMinimum()
978 	{
979 		return rangedValue.getMinimum();
980 	}
981 
982 	/**
983 	 * Switches between linear and logarithmic scales displayed. This method
984 	 * delegates all logic to the RangedValue implementation.
985 	 *
986 	 * @param logScale boolean
987 	 */
988 	public void setLogarithmic(boolean logScale)
989 	{
990 		if (logScale == logarithmic) {
991 			return;
992 		}
993 
994 		logarithmic = logScale;
995 
996 		if (logScale) {
997 			rangedValue.setRange(new LogarithmicRange());
998 		} else {
999 			rangedValue.setRange(new LinearRange());
1000 		}
1001 
1002 		firePropertyChange("logarithmic", !logScale, logScale);
1003 	}
1004 
1005 	/**
1006 	 * Returns <code>true</code> if the display is in logarithmic scale.
1007 	 *
1008 	 * @return boolean
1009 	 */
1010 	public boolean isLogarithmic()
1011 	{
1012 		return logarithmic;
1013 	}
1014 
1015 	/**
1016 	 * Sets both minimum and maximum displayed values displayed  at the bottom
1017 	 * and at the top of the Pipers's scale. This method delegates all logic
1018 	 * to the RangedValue implementation.
1019 	 *
1020 	 * @param newMax double
1021 	 * @param newMin double
1022 	 */
1023 	public void setMaxMin(double newMax, double newMin)
1024 	{
1025 		double oldMaximum = rangedValue.getMaximum();
1026 		double oldMinimum = rangedValue.getMinimum();
1027 
1028 		if (oldMaximum == newMax && oldMinimum == newMin) {
1029 			return;
1030 		}
1031 
1032 		rangedValue.setValue(newMin, newMax, rangedValue.getValue());
1033 		firePropertyChange("maximum", oldMaximum, newMax);
1034 		firePropertyChange("minimum", oldMinimum, newMin);
1035 	}
1036 
1037 	/**
1038 	 * Enables or disables the popup capabilities of the component.
1039 	 *
1040 	 * @param b the component should bring up popup dialog on mouse click.
1041 	 */
1042 	public void setPopupEnabled(boolean b)
1043 	{
1044 		if (popupEnabled == b) return;
1045 		popupEnabled = b;
1046 		if (b) {
1047 			addMouseListener(getPopupManager().getMouseHook());
1048 		} else {
1049 			removeMouseListener(getPopupManager().getMouseHook());
1050 		}
1051 		firePropertyChange("popupEnabled", !b, b);
1052 	}
1053 
1054 	/**
1055 	 * Returns whether the user can trigger the component's popup menu through
1056 	 * mouse click.
1057 	 *
1058 	 * @return true if popup is enabled
1059 	 */
1060 	public boolean isPopupEnabled()
1061 	{
1062 		return popupEnabled;
1063 	}
1064 
1065 	/**
1066 	 * Sets the new value to be displayed. This method delegates all logic to
1067 	 * the RangedValue implementation.
1068 	 *
1069 	 * @param newValue double
1070 	 */
1071 	public void setValue(double newValue)
1072 	{
1073 		double oldValue = rangedValue.getValue();
1074 
1075 		labelValue = newValue;
1076 
1077 		//if (oldValue == newValue) {
1078 		//	return;
1079 		//}
1080 		rangedValue.setValue(newValue);
1081 
1082 		if ((newValue > rangedValue.getMaximum()
1083 		    || newValue < rangedValue.getMinimum()) && tiltingEnabled) {
1084 			getTiltHandler().tilt();
1085 		}
1086 
1087 		firePropertyChange("value", oldValue, newValue);
1088 	}
1089 
1090 	/**
1091 	 * Returns the value currently displayed.
1092 	 *
1093 	 * @return double
1094 	 */
1095 	public double getValue()
1096 	{
1097 		return labelValue;
1098 	}
1099 
1100 	/**
1101 	 * For demonstration purposes.
1102 	 *
1103 	 * @param args String[]
1104 	 */
1105 	public static void main(String[] args)
1106 	{
1107 		Thread t = new Thread() {
1108 				public void run()
1109 				{
1110 					javax.swing.JFrame frame = new javax.swing.JFrame();
1111 					frame.addWindowListener(new WindowAdapter() {
1112 							public void windowClosing(WindowEvent e)
1113 							{
1114 								System.exit(0);
1115 							}
1116 						});
1117 
1118 					JPanel panel = new JPanel();
1119 					frame.setContentPane(panel);
1120 					frame.setSize(new java.awt.Dimension(100, 220));
1121 
1122 					Piper sp = new Piper();
1123 					sp.setEnhanced(true);
1124 					sp.setPopupEnabled(true);
1125 //					sp.setStretch(false);
1126 					sp.setMinimum(-10.0);
1127 					sp.setMaximum(20.0);
1128 					sp.setValue(10.0);
1129 					sp.setTitle("NASLOV");
1130 					sp.setTitleVisible(false);
1131 					sp.setLayoutOrientation(LayoutOrientation.LEFT_RIGHT);
1132 					panel.setLayout(new GridLayout());
1133 					panel.add(sp);
1134 					
1135 					frame.setVisible(true);
1136 
1137 					//double x = 5;
1138 					//int i = 1;
1139 					//long time1 = System.currentTimeMillis();
1140 					//long time2 = 0;
1141 
1142 					/*while (true) {
1143 					    time2 = System.currentTimeMillis() - time1;
1144 					    x = 0.5
1145 					        + NoiseMaker.perlinNoise((double)time2 / 100.0 * Math.PI / 180);
1146 
1147 					    //sp.setValue(x);
1148 					    //sp.setValue(0.5);
1149 					    sp.repaint();
1150 
1151 					    try {
1152 					        i++;
1153 
1154 					        if (i > 50) {
1155 					            i = 0;
1156 					        }
1157 
1158 					        sleep(10);
1159 					    } catch (Exception e) {
1160 					    }
1161 					}*/
1162 				}
1163 			};
1164 
1165 		t.run();
1166 	}
1167 
1168 	/**
1169 	 * Overriden to implement visual notification of enabled state.
1170 	 *
1171 	 * @see java.awt.Component#setEnabled(boolean)
1172 	 */
1173 	public void setEnabled(boolean enabled)
1174 	{
1175 		super.setEnabled(enabled);
1176 
1177 		renderer.clearBuffer();
1178 		repaint();
1179 	}
1180 
1181 	/**
1182 	 * Returns popup manager for adding popup actions.
1183 	 *
1184 	 * @see com.cosylab.gui.components.util.PopupManageable#getPopupManager()
1185 	 */
1186 	public PopupManager getPopupManager()
1187 	{
1188 		if (popupManager == null) {
1189 			popupManager = new PopupManager(this, false);
1190 			popupManager.addAction(new AbstractAction("Preferences...") {
1191 				private static final long serialVersionUID = 1L;
1192 
1193 					public void actionPerformed(ActionEvent e)
1194 					{
1195 						getCustomizer().showDialog();
1196 					}
1197 				});
1198 
1199 			//JMenu submenu = new JMenu("Capture screen");
1200 
1201 			ActionList al=new ActionList("Capture screen");
1202 
1203 			al.addAction(new AbstractAction("To file...") {
1204 				private static final long serialVersionUID = 1L;
1205 
1206 					public void actionPerformed(ActionEvent e)
1207 					{
1208 						ScreenCapturer sc = new ScreenCapturer(Piper.this);
1209 						sc.showScreenDialog();
1210 					}
1211 				});
1212 			/*al.addAction(new AbstractAction("To clipboard...") {
1213 				public void actionPerformed(ActionEvent e)
1214 				{
1215 					ScreenCapturer sc = new ScreenCapturer();
1216 					sc.saveImageToSysClipBoard(Piper.this);
1217 				}
1218 			});*/
1219 		   popupManager.addAction(al);
1220 
1221 		}
1222 
1223 		return popupManager;
1224 	}
1225 
1226 	/**
1227 	 * Sets the resizable property of the component. If set, all components
1228 	 * text is rendered in font size, adjusted to the size of the whole
1229 	 * component.
1230 	 *
1231 	 * @param b
1232 	 */
1233 	public void setResizable(boolean b)
1234 	{
1235 		if (b == resizable) {
1236 			return;
1237 		}
1238 		boolean oldResizable = resizable;
1239 
1240 		resizable = b;
1241 
1242 		firePropertyChange("resizable", oldResizable, resizable);
1243 		renderer.clearBuffer();
1244 
1245 		if (!isEnabled()) {
1246 			return;
1247 		}
1248 
1249 		repaint();
1250 	}
1251 
1252 	/**
1253 	 * Returns the resizable property of the component.
1254 	 *
1255 	 * @return resizable
1256 	 */
1257 	public boolean isResizable()
1258 	{
1259 		return resizable;
1260 	}
1261 
1262 	/**
1263 	 * Sets the state to the component.
1264 	 *
1265 	 * @param state to set.
1266 	 *
1267 	 * @see com.cosylab.application.state.StateOriginator#setState(com.cosylab.application.state.State)
1268 	 */
1269 	public void setState(State state)
1270 	{
1271 		setFormat(state.getString("format", getFormat()));
1272 		setMaximum(state.getDouble("maximum", getMaximum()));
1273 		setMinimum(state.getDouble("minimum", getMinimum()));
1274 		setTitle(state.getString("title", getTitle()));
1275 		setUnits(state.getString("units", getUnits()));
1276 		setLogarithmic(state.getBoolean("logarithmic", isLogarithmic()));
1277 		setStretch(state.getBoolean("stretch", isStretch()));
1278 		setEnhanced(state.getBoolean("enhanced", isEnhanced()));
1279 		setResizable(state.getBoolean("resizable", isResizable()));
1280 		setPopupEnabled(state.getBoolean("popupEnabled", isPopupEnabled()));
1281 		setTiltingEnabled(state.getBoolean("tiltingEnabled", isTiltingEnabled()));
1282 	}
1283 
1284 	/**
1285 	 * Returns the current state of the component.
1286 	 *
1287 	 * @return current state.
1288 	 *
1289 	 * @see com.cosylab.application.state.StateOriginator#getState()
1290 	 */
1291 	public State getState()
1292 	{
1293 		State state = StateFactory.createState();
1294 
1295 		state.putString("format", getFormat());
1296 		state.putDouble("graphMax", getMaximum());
1297 		state.putDouble("graphMin", getMinimum());
1298 		state.putString("title", getTitle());
1299 		state.putString("units", getUnits());
1300 		state.putBoolean("logarithmic", isLogarithmic());
1301 		state.putBoolean("stretch", isStretch());
1302 		state.putBoolean("enhanced", isEnhanced());
1303 		state.putBoolean("resizable", isResizable());
1304 		state.putBoolean("popupEnabled", isPopupEnabled());
1305 		state.putBoolean("tiltingEnabled", isTiltingEnabled());
1306 
1307 		return state;
1308 	}
1309 
1310 	/**
1311 	 * Sets if the pipe should strech all across the width of the component or
1312 	 * not.
1313 	 *
1314 	 * @param b
1315 	 */
1316 	public void setStretch(boolean b)
1317 	{
1318 		if (b == stretch) {
1319 			return;
1320 		}
1321 
1322 		boolean oldStretch = stretch;
1323 		stretch = b;
1324 		renderer.clearBuffer();
1325 		firePropertyChange("stretch", oldStretch, stretch);
1326 
1327 		if (!isEnabled()) {
1328 			return;
1329 		}
1330 		repaint();
1331 	}
1332 
1333 	/**
1334 	 * Returns wether the pipe should strech all across the width of the
1335 	 * component or not.
1336 	 *
1337 	 * @return strech
1338 	 */
1339 	public boolean isStretch()
1340 	{
1341 		return stretch;
1342 	}
1343 
1344 	/**
1345 	 * Sets the tilitng enabled property.
1346 	 *
1347 	 * @param b whether the component should tilt when value is out of bounds.
1348 	 */
1349 	public void setTiltingEnabled(boolean b)
1350 	{
1351 		if (tiltingEnabled == b) {
1352 			return;
1353 		}
1354 
1355 		tiltingEnabled = b;
1356 		firePropertyChange("tiltingEnabled", !b, b);
1357 	}
1358 
1359 	/**
1360 	 * Returns whether the component should indicate value out of bounds
1361 	 * condition by visually tilting its border.
1362 	 *
1363 	 * @return boolean
1364 	 */
1365 	public boolean isTiltingEnabled()
1366 	{
1367 		return tiltingEnabled;
1368 	}
1369 
1370 	/**
1371 	 * Sets the title to be rendered on top of the component when not
1372 	 * stretched. Can be null in which case nothing is rendered.
1373 	 *
1374 	 * @param string title
1375 	 */
1376 	public void setTitle(String string)
1377 	{
1378 		if (string == null && title == null
1379 		    || string != null && string.equals(title)) {
1380 			return;
1381 		}
1382 
1383 		String oldTitle = title;
1384 
1385 		title = string;
1386 		firePropertyChange("title", oldTitle, title);
1387 
1388 		if (!isEnabled()) {
1389 			return;
1390 		}
1391 
1392 		repaint();
1393 	}
1394 
1395 	/**
1396 	 * Returns the current title of the component or null is none is set.
1397 	 *
1398 	 * @return title
1399 	 */
1400 	public String getTitle()
1401 	{
1402 		return title;
1403 	}
1404 
1405 	/**
1406 	 * Sets the units to be displayed in the value label. If null, nothing is
1407 	 * rendered.
1408 	 *
1409 	 * @param units The units to set, can be null
1410 	 */
1411 	public void setUnits(String units)
1412 	{
1413 		if (this.units == null && units == null
1414 		    || units != null && units.equals(this.units)) {
1415 			return;
1416 		}
1417 
1418 		String oldUnits = this.units;
1419 		this.units = units;
1420 		firePropertyChange("units", oldUnits, units);
1421 		
1422 		if (!isEnabled()) {
1423 			return;
1424 		}
1425 		repaint();
1426 	}
1427 
1428 	/**
1429 	 * Returns the units to be displayed in the value label or null is none is
1430 	 * set.
1431 	 *
1432 	 * @return String
1433 	 */
1434 	public String getUnits()
1435 	{
1436 		return units;
1437 	}
1438 
1439 	/**
1440 	 * Returns the rangedValue object containing all the value logic.
1441 	 *
1442 	 * @return RangedValue
1443 	 */
1444 	protected RangedValueController getRangedValue()
1445 	{
1446 		return rangedValue;
1447 	}
1448 	
1449 	/**
1450 	 * Sets the orientation of the pipe. Pipe's bounds are position according to
1451 	 * the orientation (eg. TOP_DOWN means that lower values are printed at the
1452 	 * top of the piper and higher at the bottom; piper is filled from the top
1453 	 * down).
1454 	 * 
1455 	 * @param orientation
1456 	 */
1457 	public void setLayoutOrientation(LayoutOrientation orientation) {
1458 		if (this.orientation == orientation) return;
1459 		LayoutOrientation oldValue = this.orientation;
1460 		this.orientation = orientation;
1461 		firePropertyChange("layoutOrientation", oldValue, orientation);
1462 		repaint();
1463 	}
1464 	
1465 	/**
1466 	 * Returns the layout orientation.
1467 	 * 
1468 	 * @return
1469 	 */
1470 	public LayoutOrientation getLayoutOrientation() {
1471 		return orientation;
1472 	}
1473 	
1474 	private double getAngle() {
1475 		if (orientation == LayoutOrientation.BOTTOM_UP) {
1476 			return 0;
1477 		} else if (orientation == LayoutOrientation.LEFT_RIGHT) {
1478 			return Math.PI/2;
1479 		} else if (orientation == LayoutOrientation.TOP_DOWN) {
1480 			return Math.PI;
1481 		} else if (orientation == LayoutOrientation.RIGHT_LEFT) {
1482 			return 3*Math.PI/2;
1483 		}
1484 		return 0;
1485 	}
1486 	
1487 	private double getHorizontalShift() {
1488 		if (orientation == LayoutOrientation.BOTTOM_UP || orientation == LayoutOrientation.RIGHT_LEFT) {
1489 			return 0;
1490 		} else {
1491 			return getWidth();
1492 		} 
1493 	}
1494 	
1495 	private double getVerticalShift() {
1496 		if (orientation == LayoutOrientation.BOTTOM_UP || orientation == LayoutOrientation.LEFT_RIGHT) {
1497 			return 0;
1498 		} else {
1499 			return getHeight();
1500 		}
1501 	}
1502 		
1503 	/**
1504 	 * This method has been overriden to implement custom painting of the
1505 	 * Piper.
1506 	 *
1507 	 * @param g Graphics
1508 	 *
1509 	 * @see JComponent#paintComponent(java.awt.Graphics)
1510 	 */
1511 	protected void paintComponent(Graphics g)
1512 	{
1513 		if (isOpaque()) {
1514 			g.setColor(getBackground());
1515 			g.fillRect(0, 0, getWidth(), getHeight());
1516 		}
1517 		
1518 		((Graphics2D)g).translate(getHorizontalShift(),getVerticalShift());
1519 		((Graphics2D)g).rotate(getAngle());
1520 		
1521 		Graphics2D g2D = (Graphics2D)g;
1522 		g2D.setBackground(getBackground());
1523 		synchronized (renderer) {
1524 		
1525 			
1526 			renderer.prepareGraphics(g2D);
1527 			renderer.setPipeRect(g2D);
1528 			renderer.paintPipe(g2D);
1529 			renderer.paintFill(g2D);
1530 			renderer.paintScale(g2D);
1531 			
1532 			((Graphics2D)g).rotate(-getAngle());
1533 			((Graphics2D)g).translate(-getHorizontalShift(),-getVerticalShift());
1534 			renderer.paintTickLabels(g2D);
1535 			renderer.paintLabel(g2D);
1536 			if (titleVisible)
1537 				renderer.paintTitle(g2D);
1538 		}
1539 		
1540 
1541 //		g.dispose();
1542 	}
1543 
1544 	/**
1545 	 * DOCUMENT ME!
1546 	 *
1547 	 * @return DOCUMENT ME!
1548 	 */
1549 	private TiltHandler getTiltHandler()
1550 	{
1551 		if (tiltHandler == null) {
1552 			tiltHandler = new TiltHandler();
1553 		}
1554 
1555 		return tiltHandler;
1556 	}
1557 
1558 	/**
1559 	 * DOCUMENT ME!
1560 	 *
1561 	 * @param p DOCUMENT ME!
1562 	 */
1563 	public void setValuePolicy(RangedValuePolicy p)
1564 	{
1565 		rangedValue.removePolicy(policy);
1566 		this.policy = p;
1567 		rangedValue.addPolicy(p);
1568 	}
1569 
1570 	/**
1571 	 * DOCUMENT ME!
1572 	 *
1573 	 * @return
1574 	 */
1575 	public RangedValuePolicy getValuePolicy()
1576 	{
1577 		return policy;
1578 	}
1579 
1580 	/**
1581 	 * DOCUMENT ME!
1582 	 *
1583 	 * @return
1584 	 */
1585 	public boolean isUnitsVisible()
1586 	{
1587 		return unitsVisible;
1588 	}
1589 
1590 	/**
1591 	 * DOCUMENT ME!
1592 	 *
1593 	 * @param b
1594 	 */
1595 	public void setUnitsVisible(boolean b)
1596 	{
1597 		if (unitsVisible == b) {
1598 			return;
1599 		}
1600 
1601 		boolean oldB = unitsVisible;
1602 		unitsVisible = b;
1603 		firePropertyChange("unitsVisible", oldB, b);
1604 		renderer.clearBuffer(); /* edit apikus */
1605 		repaint();
1606 	}
1607 
1608 	public boolean isTitleVisible() {
1609 		return titleVisible;
1610 	}
1611 
1612 	public void setTitleVisible(boolean titleVisible) {
1613 		if (this.titleVisible == titleVisible) {
1614 			return;
1615 		}
1616 
1617 		this.titleVisible = titleVisible;
1618 		firePropertyChange("titleVisible", !titleVisible, titleVisible);
1619 		repaint();
1620 	}
1621 
1622 	public int getTitleMaximumFontSize() {
1623 		return titleMaximumFontSize;
1624 	}
1625 
1626 	public void setTitleMaximumFontSize(int titleMaximumFontSize) {
1627 
1628 		if (this.titleMaximumFontSize == titleMaximumFontSize) return;
1629 		int i = this.titleMaximumFontSize;
1630 		this.titleMaximumFontSize = titleMaximumFontSize;
1631 		firePropertyChange("titleMaximumFontSize", i, titleMaximumFontSize);
1632 	}
1633 
1634 	public int getTitleMinimumFontSize() {
1635 		return titleMinimumFontSize;
1636 	}
1637 
1638 	public void setTitleMinimumFontSize(int titleMinimumFontSize) {
1639 		if (this.titleMinimumFontSize == titleMinimumFontSize) return;
1640 		int i = this.titleMinimumFontSize;
1641 		this.titleMinimumFontSize = titleMinimumFontSize;
1642 		firePropertyChange("titleMinimumFontSize", i, titleMinimumFontSize);
1643 	}
1644 	
1645 	/**
1646 	 * Sets the light color of the water in the piper.
1647 	 * 
1648 	 * @param color
1649 	 */
1650 	public void setLightWaterColor(Color color) {
1651 		Color old = renderer.ligtColor;
1652 		renderer.ligtColor = color;
1653 		firePropertyChange("lightWaterColor", old, color);
1654 	}
1655 	
1656 	/**
1657 	 * Sets the dark color of the water in the piper.
1658 	 * 
1659 	 * @param color
1660 	 */
1661 	public void setDarkWaterColor(Color color) {
1662 		Color old = renderer.darkColor;
1663 		renderer.darkColor = color;
1664 		firePropertyChange("darkWaterColor", old, color);
1665 	}
1666 
1667 	/**
1668 	 * Sets the light color of the water in case value is out of bounds.
1669 	 * 
1670 	 * @param color
1671 	 */
1672 	public void setOutOfBoundsLightWaterColor(Color color) {
1673 		Color old = renderer.outOfBoundsLightColor;
1674 		renderer.outOfBoundsLightColor = color;
1675 		firePropertyChange("outOfBoundsLightWaterColor", old, color);
1676 	}
1677 
1678 	/**
1679 	 * Sets the dark color of the water in case value is out of bounds.
1680 	 * 
1681 	 * @param color
1682 	 */
1683 	public void setOutOfBoundsDarkWaterColor(Color color) {
1684 		Color old = renderer.outOfBoundsDarkColor;
1685 		renderer.outOfBoundsDarkColor = color;
1686 		firePropertyChange("outOfBoundsDarkWaterColor", old, color);
1687 	}
1688 	
1689 	/**
1690 	 * @return the light color of the water in the piper
1691 	 */
1692 	public Color getLightWaterColor() {
1693 			return renderer.ligtColor;
1694 	}
1695 	
1696 	/**
1697 	 * @return the dark color of the water in the piper
1698 	 */
1699 	public Color getDarkWaterColor() {
1700 			return renderer.darkColor;
1701 	}
1702 	
1703 	/**
1704 	 * @return the light color of the water in case value is out of bounds
1705 	 */
1706 	public Color getOutOfBoundsLightWaterColor() {
1707 			return renderer.outOfBoundsLightColor;
1708 	}
1709 	
1710 	/**
1711 	 * @return the dark color of the water in case value is out of bounds
1712 	 */
1713 	public Color getOutOfBoundsDarkWaterColor() {
1714 			return renderer.outOfBoundsDarkColor;		
1715 	}
1716 }
1717 
1718 /* __oOo__ */