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.Color;
23  import java.awt.Font;
24  import java.awt.Graphics;
25  import java.awt.Graphics2D;
26  import java.awt.Point;
27  import java.awt.Rectangle;
28  import java.awt.event.ActionEvent;
29  import java.awt.event.ComponentAdapter;
30  import java.awt.event.ComponentEvent;
31  import java.awt.event.WindowAdapter;
32  import java.awt.event.WindowEvent;
33  import java.awt.image.BufferedImage;
34  
35  import javax.swing.AbstractAction;
36  import javax.swing.JPanel;
37  
38  import com.cosylab.application.state.State;
39  import com.cosylab.application.state.StateFactory;
40  import com.cosylab.application.state.StateOriginator;
41  import com.cosylab.gui.components.customizer.AbstractCustomizerPanel;
42  import com.cosylab.gui.components.gauger.Needle;
43  import com.cosylab.gui.components.gauger.ScaleTransform;
44  import com.cosylab.gui.components.gauger.ScaleTransformFactory;
45  import com.cosylab.gui.components.gauger.ScaleTransformRenderer;
46  import com.cosylab.gui.components.gauger.ValueLabel;
47  import com.cosylab.gui.components.range2.LinearRange;
48  import com.cosylab.gui.components.range2.LogarithmicRange;
49  import com.cosylab.gui.components.range2.ManualTickCalculator;
50  import com.cosylab.gui.components.range2.RangedValueController;
51  import com.cosylab.gui.components.range2.RangedValueEvent;
52  import com.cosylab.gui.components.range2.RangedValueListener;
53  import com.cosylab.gui.components.range2.RangedValuePolicy;
54  import com.cosylab.gui.components.range2.RescalingValuePolicy;
55  import com.cosylab.gui.components.range2.ShiftValuePolicy;
56  import com.cosylab.gui.components.range2.Tick;
57  import com.cosylab.gui.components.range2.TickParameters;
58  import com.cosylab.gui.components.range2.TrimValuePolicy;
59  import com.cosylab.gui.components.util.ColorHelper;
60  import com.cosylab.gui.components.util.PaintHelper;
61  import com.cosylab.gui.components.util.PopupManageable;
62  import com.cosylab.gui.components.util.PopupManager;
63  import com.cosylab.gui.components.util.ScreenCapturer;
64  import com.cosylab.util.PrintfFormat;
65  
66  
67  /**
68   * Gauger is a visual component that displays a value on a scale between
69   * minimum and maximum. Shape and visual layout of the component will change
70   * to best fit into the available space. Behaviour of the scale can be
71   * adjusted to fixed (lower and upper bound cannot change), constant span
72   * (scale will always span fixed ammount) or rescaling (lower and upper bound
73   * will adjust to accomodate current value). Supported scales are linear and
74   * logarithmic.
75   * 
76   * <p>
77   * For accurate readouts, the currently set value is also displayed in custom
78   * formatted label.
79   * </p>
80   * 
81   * <p>
82   * Gauger is aimed to provide fastest possible response to value changes.
83   * </p>
84   *
85   * @author Ales Pucelj
86   * @version $id$
87   */
88  public class Gauger extends JPanel implements TickParameters, PopupManageable, /* implements TickCallback - edit apikus */
89  	StateOriginator
90  {
91  	private static final long serialVersionUID = 1L;
92  	
93  	/**
94  	 * Fixed limits scale.
95  	 *
96  	 * @see #setScaleMode(int)
97  	 * @deprecated replaced by {@link TrimValuePolicy}
98  	 */
99  	public static final int FIXED_SCALE = 0;
100 
101 	/**
102 	 * Constant span scale.
103 	 *
104 	 * @see #setScaleMode(int)
105 	 * @deprecated replaced by {@link ShiftValuePolicy}
106 	 */
107 	public static final int CONSTANT_SPAN = 1;
108 
109 	/**
110 	 * Resizable scale.
111 	 *
112 	 * @see #setScaleMode(int)
113 	 * @deprecated replaced by {@link RescalingValuePolicy}
114 	 */
115 	public static final int STRETCH_SCALE = 2;
116 	
117 	private AbstractCustomizerPanel customizer;
118 	
119 	private PopupManager popupManager;
120 	
121 	/**
122 	 * Gauger preferences
123 	 */
124 	private Color scaleColor = new Color(239, 239, 239);
125 	private Color needleColor = new Color(0, 192, 0);
126 	private Color alarmColor = new Color(239,0,0);
127 	private Color warningColor = new Color(0xEEEC300);
128 	private Color outOfBoundsColor = new Color(239,0,0);
129 
130 	private Font tickTextFont = new Font("Serif", Font.PLAIN, 12);
131 	private Font valueLabelFont = new Font("dialog", Font.PLAIN, 12);
132 	
133 	/* the size of range values for scale limit */
134 
135 	private double lowAlarmLimit = 0.05;
136 	private double lowWarningLimit= 0.2;
137 	private double highWarningLimit= 0.8;
138 	private double highAlarmLimit= 0.95;
139 	private PrintfFormat formatter = new PrintfFormat("%3.2f");	
140 	protected RangedValueController userRangedValue; /* edit apikus */
141 	private BufferedImage buffer = null;
142 	private ScaleTransformRenderer scaleRenderer = null;
143 	private ScaleTransform transform = null;
144 	protected Needle needle = null;
145 	private ValueLabel valueLabel = null;
146 	private double labelValue;
147 	private Tick[] ticks = null;
148 	private String title = "";
149 	private boolean titleVisible = false;
150 	private int titleMinimumFontSize = 2;
151 	private int titleMaximumFontSize = 20;
152 	private boolean resizable = true;
153 	private boolean popupEnabled;
154 	
155 	private class ComponentListener extends ComponentAdapter
156 	{
157 		/**
158 		 * Clears background and tick buffer whenever the component is resized.
159 		 * Buffer will be regenerated on next call to repaint.
160 		 *
161 		 * @param e ComponentEvent
162 		 *
163 		 * @see java.awt.event.ComponentListener#componentResized(ComponentEvent)
164 		 */
165 		public void componentResized(ComponentEvent e)
166 		{
167 			if (e.getSource() == Gauger.this) {
168 				synchronized (Gauger.this) {
169 					transform = null;
170 					clearBuffer();
171 					repaint();
172 				}
173 			}
174 		}
175 
176 		/**
177 		 * Disposes the buffer whenever the component is hidded to minimize
178 		 * memory use whenever the component is not visible.
179 		 *
180 		 * @param e ComponentEvent
181 		 *
182 		 * @see ComponentListener#componentHidden(ComponentEvent)
183 		 */
184 		public void componentHidden(ComponentEvent e)
185 		{
186 			super.componentHidden(e);
187 			clearBuffer();
188 		}
189 	}
190 
191 	private class ValueListener implements RangedValueListener
192 	{
193 		/**
194 		 * Responds to value changes by either repainint the background or only
195 		 * updating the needle position.
196 		 *
197 		 * @see com.cosylab.gui.components.range.RangedValueListener#valueChanged(RangedValueEvent)
198 		 */
199 		public void valueChanged(RangedValueEvent event)
200 		{
201 			
202 			synchronized (Gauger.this) {
203 				if (event.isMinimumChanged() || event.isMaximumChanged()) {
204 					clearBuffer();
205 					repaint();
206 				}
207 
208 			
209 				Needle needle = getNeedle();
210 				Rectangle oldArea = needle.getBounds();
211 
212 
213 				
214 				if(getValue()>getMaximum()) {
215 					needle.setPosition(userRangedValue.getRelativeValue()+0.05); /* edit apikus */
216 				} else if (getValue()<getMinimum()){
217 					needle.setPosition(userRangedValue.getRelativeValue()-0.05);
218 				} else {
219 					needle.setPosition(userRangedValue.getRelativeValue());
220 				}
221 			
222 				if (needle.isChangeSignificant()) {
223 					repaint(oldArea);
224 					repaint(needle.getBounds());
225 				}
226 
227 				ValueLabel vl = getValueLabel();
228 				oldArea = vl.getBounds();
229 
230 				if (vl.isChangeSignificant()) {
231 					repaint(oldArea);
232 					repaint(vl.getBounds());
233 				}
234 			}
235 		}
236 	}
237 
238 	/**
239 	 * Creates a new gauger and sets the default parameters.
240 	 */
241 	public Gauger()
242 	{
243 		super();
244 
245 		userRangedValue = new RangedValueController(); /* edit apikus */
246 		userRangedValue.setRange(new LinearRange());
247 		userRangedValue.addPolicy(new RescalingValuePolicy());
248 		userRangedValue.addRangedValueListener(new ValueListener());
249 		
250 		//default is fixed_scale
251 		setScaleMode(Gauger.FIXED_SCALE);
252 		setBackground(ColorHelper.getCosyControl());
253 		scaleRenderer = new ScaleTransformRenderer(this);
254 
255 		setDoubleBuffered(false);
256 		setOpaque(true);
257 		setDropTarget(null);
258 
259 		addComponentListener(new ComponentListener());
260 		setPopupEnabled(true);
261 	}
262 
263 	/**
264 	 * Creates a new buffer on which the scale background is rendered.  Buffer
265 	 * should always be accessed via this method, since the buffer can  be
266 	 * disposed if the component is not visible.
267 	 *
268 	 * @return BufferedImage
269 	 */
270 	private synchronized BufferedImage getBuffer()
271 	{
272 		if (buffer == null) {
273 			int w = Math.max(getWidth(), 1);
274 			int h = Math.max(getHeight(), 1);
275 
276 			buffer = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
277 
278 			Graphics2D g2 = buffer.createGraphics();
279 			g2.setRenderingHints(PaintHelper.getAntialiasingHints());
280 
281 			g2.setColor(getBackground());
282 			g2.fillRect(0, 0, w, h);
283 			scaleRenderer.render(getTransform(), g2);
284 		}
285 
286 		return buffer;
287 	}
288 
289 	/**
290 	 * Sets behaviour of the scale. This can take one of 3 possible modes.<br>
291 	 * 
292 	 * <ul>
293 	 * <li>
294 	 * Gauger.FIXED_SCALE
295 	 * </li>
296 	 * <li>
297 	 * Gauger.CONSTANT_SPAN
298 	 * </li>
299 	 * <li>
300 	 * Gauger.STRETCH_SCALE
301 	 * </li>
302 	 * </ul>
303 	 * 
304 	 * <p>
305 	 * FIXED_SCALE will fix the limits to any value regardless of the actual
306 	 * value displayed by needle. If value displayed is outside limits, it
307 	 * will be limited to either bound.
308 	 * </p>
309 	 * 
310 	 * <p>
311 	 * CONSTANT_SPAN will automatically move the scale based on needle
312 	 * movement. If needle moves outside current limits, they will move in the
313 	 * same  direction, but span (maximum-minimum) of the scale will remain
314 	 * unchanged.
315 	 * </p>
316 	 * 
317 	 * <p>
318 	 * STRETCH_SCALE will change only one limit at the time whenever needle
319 	 * moves outside current limits. Scale will not shrink back when needle is
320 	 * within old bounds.
321 	 * </p>
322 	 *
323 	 * @param mode New mode to set.
324 	 * @deprecated use {@link #setValuePolicy(RangedValuePolicy)}
325 	 */
326 	public void setScaleMode(int mode)
327 	{
328 		int oldValue = getScaleMode();
329 		switch (mode) {
330 		case FIXED_SCALE:
331 			setValuePolicy(new TrimValuePolicy());
332 			break;
333 
334 		case CONSTANT_SPAN:
335 			setValuePolicy(new ShiftValuePolicy());
336 
337 			break;
338 
339 		case STRETCH_SCALE:default:
340 			setValuePolicy(new RescalingValuePolicy());
341 
342 			break;
343 		}
344 		
345 		firePropertyChange("scaleMode", oldValue, mode);
346 	}
347 	
348 	/**
349 	 * Sets the RangedValuePolicy.
350 	 * 
351 	 * @param policy
352 	 * @see #setScaleMode(int)
353 	 */
354 	public void setValuePolicy(RangedValuePolicy policy) {
355 		RangedValuePolicy oldValue = getValuePolicy();
356 		getRangedValue().setPolicy(policy);
357 		firePropertyChange("valuePolicy", oldValue, policy);
358 	}
359 	
360 	/**
361 	 * Returns the RangedValuePolicy.
362 	 * 
363 	 * @return
364 	 * @see #getScaleMode()
365 	 */
366 	public RangedValuePolicy getValuePolicy() {
367 		return getRangedValue().getPolicy();
368 	}
369 
370 	/**
371 	 * Returns current scale mode. Return value will be one of <code>
372 	 * Gauger.FIXED_SCALE</code>, <code>Gauger.CONSTANT_SPAN</code> or <code>
373 	 * Gauger.STRETCH_SCALE</code>.
374 	 *
375 	 * @return One of scale mode constants.
376 	 *
377 	 * @see #setScaleMode(int)
378 	 * @deprecated use {@link #getValuePolicy()}
379 	 */
380 	public int getScaleMode()
381 	{
382 		RangedValuePolicy p = getValuePolicy();
383 
384 		if (p instanceof TrimValuePolicy) {
385 			return FIXED_SCALE;
386 		}
387 
388 		if (p instanceof ShiftValuePolicy) {
389 			return CONSTANT_SPAN;
390 		}
391 
392 		return STRETCH_SCALE;
393 	}
394 
395 	/**
396 	 * Clears the background buffer and precalculated ticks.
397 	 */
398 	private synchronized void clearBuffer()
399 	{
400 		ticks = null;
401 		buffer = null;
402 		transform = null;
403 	}
404 
405 	/**
406 	 * Returns the current maximum value of the scale.
407 	 *
408 	 * @return double
409 	 */
410 	public double getMaximum()
411 	{
412 		return userRangedValue.getMaximum(); 
413 	}
414 
415 	/**
416 	 * Returns the current minimum value of the scale.
417 	 *
418 	 * @return double
419 	 */
420 	public double getMinimum()
421 	{
422 		return userRangedValue.getMinimum(); 
423 	}
424 
425 	/**
426 	 * Returns needle renderer. If needle does not exist, it will be created.
427 	 * This method uses lazy initialization, so setting the needle field to
428 	 * null will cause it to be recreated on next call.
429 	 *
430 	 * @return Needle needle renderer implementation.
431 	 */
432 	protected synchronized Needle getNeedle()
433 	{
434 		if (needle == null) {
435 			needle = new Needle(this);
436 			needle.setPosition(0.5);
437 		}
438 
439 		return needle;
440 	}
441 
442 	/**
443 	 * Returns <code>RangedValueController</code>. This method can be used
444 	 * to gain access to more advanced aspects of <code>RangedValueController</code>
445 	 * Instance returned is not replacable.
446 	 *
447 	 * @return Instance of <code>RangedValue</code>
448 	 */
449 	public RangedValueController getRangedValue()
450 	{
451 		return userRangedValue;
452 	}
453 
454 	/**
455 	 * Returns the current value of the scale. This is the value displayed by
456 	 * the needle. Regardles of the scale, this value is never log based.
457 	 *
458 	 * @return double
459 	 */
460 	public double getValue()
461 	{
462 		return labelValue;
463 	}
464 
465 	/**
466 	 * Gets the instance of the value label used to display current value.
467 	 *
468 	 * @return ValueLabel
469 	 */
470 	protected ValueLabel getValueLabel()
471 	{
472 		if (valueLabel == null) {
473 			valueLabel = new ValueLabel(this);
474 		}
475 
476 		return valueLabel;
477 	}
478 
479 	/**
480 	 * Sets format string of value label.
481 	 *
482 	 * @param format C-Style format of value label.
483 	 */
484 	public void setFormat(String format)
485 	{
486 		String old = getValueLabel().getFormat();
487 
488 		try {
489 			getValueLabel().setFormat(format);
490 		} catch (Exception e) {
491 			e.printStackTrace();
492 		}
493 		formatter = new PrintfFormat(format);
494 		repaint();
495 
496 		firePropertyChange("format", old, format);
497 	}
498 
499 	/**
500 	 * Returns format of value label.
501 	 *
502 	 * @return C-Style format of value label.
503 	 */
504 	public String getFormat()
505 	{
506 		return getValueLabel().getFormat();
507 	}
508 
509 	/**
510 	 * Sets units of value label.
511 	 *
512 	 * @param units String to be appended to value label. Can be null.
513 	 */
514 	public void setUnits(String units)
515 	{
516 		String oldValue = getUnits();
517 		getValueLabel().setUnits(units);
518 		repaint();
519 		firePropertyChange("units", oldValue, units);
520 	}
521 
522 	/**
523 	 * Returns units string of value label.
524 	 *
525 	 * @return String representing the units. Can be null.
526 	 */
527 	public String getUnits()
528 	{
529 		return getValueLabel().getUnits();
530 	}
531 
532 	/**
533 	 * Sets units visible of value label.
534 	 *
535 	 * @param b whether units should be displayed.
536 	 */
537 	public void setUnitsVisible(boolean b)
538 	{
539 		if (this.isUnitsVisible() == b) return;
540 		getValueLabel().setUnitsVisible(b);
541 		repaint();
542 		firePropertyChange("unitsVisible", !b, b);
543 	}
544 
545 	/**
546 	 * Returns unitsVisible of value label.
547 	 *
548 	 * @return true if units are visible
549 	 */
550 	public boolean isUnitsVisible()
551 	{
552 		return getValueLabel().isUnitsVisible();
553 	}
554 
555 
556 	/**
557 	 * Sets the new value to be displayed on this scale. In this implementation
558 	 * the value is shown by the needle and value label. The value is adjusted
559 	 * according to the set value policy.
560 	 *
561 	 * @param newValue double
562 	 */
563 	public void setValue(double newValue)
564 	{
565 		double old = labelValue;
566 		labelValue = newValue;
567 		getValueLabel().setValue(labelValue);
568 
569 		try {
570 			userRangedValue.setValue(newValue);
571 		} catch (Exception e) {
572 			e.printStackTrace();
573 		}
574 		
575 		repaint();
576 
577 		firePropertyChange("value", new Double(old), new Double(newValue));
578 	}
579 
580 	/**
581 	 * Returns the information about the scale ticks. This method is used
582 	 * internaly by renderers
583 	 *
584 	 * @return Tick[]
585 	 */
586 	public Tick[] getTicks()
587 	{
588 		if (ticks == null) {
589 			//ticks = rangedValue.getRange().getTicks((int)getTransform().scaleWidth(0.7)).toTicks(this);
590 			ticks = userRangedValue.calculateTicks((int)getTransform().scaleWidth(0.7), this); /* edit apikus */
591 		}
592 		return ticks;
593 	}
594 
595 	/**
596 	 * Returns current transform used to render the scale. This transform is
597 	 * selected based on width and height of the component and is used when
598 	 * rendering Gauger.
599 	 *
600 	 * @return ScaleTransform
601 	 */
602 	public ScaleTransform getTransform()
603 	{
604 		if (transform == null) {
605 			transform = ScaleTransformFactory.createTransform(getWidth(),
606 				    getHeight());
607 
608 			transform.setParameters(getWidth(), getHeight(), 0,
609 			    getValueLabel().getBounds().height, Tick.TICKS_OFFSET_PIXELS);
610 
611 			getNeedle().setTransform(transform);
612 			setValueLabelPosition(getTransform().getLabelPosition());
613 		}
614 
615 		return transform;
616 	}
617 
618 	/**
619 	 * Sets the lower limit for the display. This value cannot be set above
620 	 * current maximum, it can be however set above current value.
621 	 *
622 	 * @param newMin
623 	 */
624 	public void setMinimum(double newMin)
625 	{
626 		double oldValue = getMinimum();
627 		userRangedValue.setValue(newMin, getMaximum(), getValue());
628 		repaint();
629 		firePropertyChange("minimum", oldValue, newMin);
630 	}
631 
632 	/**
633 	 * Sets the upper limit for the display. This value cannot be set below
634 	 * current minimum, it can be however set below current value.
635 	 *
636 	 * @param newMax
637 	 */
638 	public void setMaximum(double newMax)
639 	{
640 		double oldValue = getMaximum();
641 		userRangedValue.setValue(getMinimum(), newMax, getValue());
642 		repaint();
643 		firePropertyChange("maximum", oldValue, newMax);
644 	}
645 
646 	/**
647 	 * Sets the value and limits for the displayer.
648 	 *
649 	 * @param newMin New minimum.
650 	 * @param newMax New maximum.
651 	 * @param newVal New value.
652 	 */
653 	public void setValue(double newMin, double newMax, double newVal)
654 	{
655 		double old = labelValue;
656 		labelValue = newVal;
657 		getValueLabel().setValue(newVal);
658 		userRangedValue.setValue(newMin, newMax, newVal);
659 		firePropertyChange("value", new Double(old), new Double(newVal));
660 	}
661 
662 	/**
663 	 * Sets position of value label.
664 	 *
665 	 * @param p Center point of value label.
666 	 */
667 	public void setValueLabelPosition(Point p)
668 	{
669 		getValueLabel().setPosition(p.x, p.y);
670 	}
671 
672 	/**
673 	 * Paints the component. This method only paints the gauger from background
674 	 * buffer, which is initialized elsewhere. If neccessary, active elements
675 	 * such as needle and value label are updated as well.
676 	 *
677 	 * @param g Context to paint to.
678 	 */
679 	protected void paintComponent(Graphics g)
680 	{
681 		clearBuffer();
682 		//System.out.println(" SIZE "+getSize());
683 		//System.out.println("GSIZE "+g.getClipBounds());
684 		try {
685 			//if (getTransform() != null) {
686 				Graphics2D g2 = (Graphics2D)g;
687 				
688 				// Rectangle clip = g2.getClipBounds();
689 				// Image image = getBuffer().getSubimage(clip.x, clip.y, clip.width, clip.height);
690 				g.drawImage(getBuffer(), 0, 0, null);
691 				g2.addRenderingHints(PaintHelper.getAntialiasingHints());
692 				getNeedle().setTransform(getTransform());
693 				getNeedle().render(g2);
694 				g2.setColor(getForeground());
695 				getValueLabel().draw(g2);
696 				
697 			//}
698 		} catch (Exception e) {
699 			// Weird synhronization error has been noticed to occur in this
700 			// method.
701 		}
702 	}
703 
704 	/**
705 	 * Sets scale to logarithmic.
706 	 */
707 	public void setLogarithmicScale()
708 	{	
709 		userRangedValue.setRange(new LogarithmicRange()); 
710 		repaint();
711 	}
712 
713 	/**
714 	 * Sets scale to linear.
715 	 */
716 	public void setLinearScale()
717 	{
718 		userRangedValue.setRange(new LinearRange()); 
719 		repaint();
720 	}
721 
722 	/**
723 	 * Returns whether the scale is logarithmic.
724 	 *
725 	 * @return True if scale is logarithmic.
726 	 */
727 	public boolean isLogarithmicScale()
728 	{
729 		return userRangedValue.getRange() instanceof LogarithmicRange;
730 	}
731 
732 	/**
733 	 * Returns whether the scale is linear.
734 	 *
735 	 * @return True if scale is linear.
736 	 */
737 	public boolean isLinearScale()
738 	{
739 		return userRangedValue.getRange() instanceof LinearRange;
740 	}
741 
742 	/**
743 	 * Returns size of tick label when renderer. Return value is ammount in
744 	 * pixels. This is the size of label in the direction along the scale.
745 	 *
746 	 * @see com.cosylab.gui.components.range.TickCallback#measureTick(double,
747 	 *      String)
748 	 */
749 	public int measureTick(double position, String text)
750 	{
751 		return getTransform().measureTick(getBuffer().getGraphics(), position,
752 		    text);
753 
754 		//		int w = getBuffer().getGraphics().getFontMetrics().stringWidth(text);
755 		//		int x = (int)(getTransform().scaleWidth(0.7) * position);
756 		//		return new TickSize(x - w / 2, w);
757 	}
758 
759 	/**
760 	 * Returns popup manager for adding popup actions.
761 	 *
762 	 * @see com.cosylab.gui.components.util.PopupManageable#getPopupManager()
763 	 */
764 	public PopupManager getPopupManager()
765 	{
766 		if (popupManager == null) {
767 			popupManager = new PopupManager(this, false);
768 			popupManager.addAction(new AbstractAction("Preferences...") {
769 				private static final long serialVersionUID = 1L;
770 					public void actionPerformed(ActionEvent e)
771 					{
772 						getCustomizer().showDialog();
773 					}
774 				});
775 			popupManager.addAction(new AbstractAction("Capture screen...") {
776 				private static final long serialVersionUID = 1L;
777 				public void actionPerformed(ActionEvent e)
778 				{
779 					ScreenCapturer sc = new ScreenCapturer(Gauger.this);
780 					sc.showScreenDialog();
781 				
782 				}
783 			});
784 		}
785 
786 		return popupManager;
787 	}
788 	
789 	/**
790 	 * Return true if the popup menu is enabled or false otherwise.
791 	 * 
792 	 * @return true if popup is enabled
793 	 */
794 	public boolean isPopupEnabled() {
795 		return popupEnabled;
796 	}
797 	
798 	/**
799 	 * Enables or disables the popup menu.
800 	 * 
801 	 * @param enabled true if enable or false if disableds
802 	 */
803 	public void setPopupEnabled(boolean enabled) {
804 		if (popupEnabled == enabled) return;
805 		popupEnabled = enabled;
806 		if (enabled) {
807 			addMouseListener(getPopupManager().getMouseHook());
808 		} else {
809 			removeMouseListener(getPopupManager().getMouseHook());
810 		}
811 		firePropertyChange("popupEnabled",!popupEnabled,popupEnabled);
812 	}
813 
814 	/**
815 	 * Returns the Customizer intance for this displayer.
816 	 *
817 	 * @return the Customizer intance for this displayer
818 	 */
819 	public AbstractCustomizerPanel getCustomizer()
820 	{
821 		if (customizer == null) {
822 			customizer = AbstractCustomizerPanel.findCustomizer(this);
823 		}
824 		return customizer;
825 	}
826 
827 	/* (non-Javadoc)
828 	 * @see com.cosylab.application.state.StateOriginator#getState()
829 	 */
830 	public State getState()
831 	{
832 		State s = StateFactory.createState();
833 		s.putDouble("Min", getMinimum());
834 		s.putDouble("Max", getMaximum());
835 		s.putString("Format", getFormat());
836 		s.putString("Units", getUnits());
837 		s.putBoolean("LogarithmicScale", isLogarithmicScale());
838 		s.putBoolean("LinearScale", isLinearScale());
839 		s.putInt("ScaleMode", getScaleMode());
840 		s.putFont("Font", getValueLabelFont());
841 
842 		return s;
843 	}
844 
845 	/* (non-Javadoc)
846 	 * @see com.cosylab.application.state.StateOriginator#setState(com.cosylab.application.state.State)
847 	 */
848 	public void setState(State state)
849 	{
850 		setMinimum(state.getDouble("Min", getMinimum()));
851 		setMaximum(state.getDouble("Max", getMaximum()));
852 		setFormat(state.getString("Format", getFormat()));
853 		setUnits(state.getString("Units", getUnits()));
854 
855 		if (state.getBoolean("LogarithmicScale", false)) {
856 			setLogarithmicScale();
857 		}
858 
859 		if (state.getBoolean("LinearScale", false)) {
860 			setLinearScale();
861 		}
862 
863 		setScaleMode(state.getInt("ScaleMode", getScaleMode()));
864 		setValueLabelFont(state.getFont("Font"));
865 	}
866 	
867 	/**
868 	 * Sets the font used to display tick labels.
869 	 *
870 	 * @param tickText Font for tick labels.
871 	 */
872 	public void setTickTextFont(Font tickText)
873 	{
874 		Font oldValue = getTickTextFont();
875 		this.tickTextFont = tickText;
876 		firePropertyChange("tickTextFont", oldValue, tickText);
877 	}
878 
879 	/**
880 	 * Returns font used to display tick labels.
881 	 *
882 	 * @return Current font for ticks.
883 	 */
884 	public Font getTickTextFont()
885 	{
886 		return tickTextFont;
887 	}
888 
889 	/**
890 	 * Returns color of scale background.
891 	 *
892 	 * @return Color of scale background.
893 	 */
894 	public Color getScaleColor()
895 	{
896 		return scaleColor;
897 	}
898 
899 	/**
900 	 * Sets color of scale background.
901 	 *
902 	 * @param newScaleColor Color of scale background.
903 	 */
904 	public void setScaleColor(Color newScaleColor)
905 	{
906 		Color oldValue = getScaleColor();
907 		scaleColor = newScaleColor;
908 		firePropertyChange("scaleColor", oldValue, newScaleColor);
909 	}
910 
911 	/**
912 	 * Returns the color of the needle.
913 	 *
914 	 * @return Color of needle.
915 	 */
916 	public Color getNeedleColor()
917 	{
918 		return needleColor;
919 	}
920 	
921 
922 	/**
923 	 * Sets the color of the needle.
924 	 *
925 	 * @param newNeedleColor New color of needle.
926 	 */
927 	public void setNeedleColor(Color newNeedleColor)
928 	{
929 		Color oldValue = getNeedleColor();
930 		needleColor = newNeedleColor;
931 		firePropertyChange("needleColor", oldValue, newNeedleColor);
932 	}
933 
934 	/**
935 	 * Returns font for value label.
936 	 *
937 	 * @return Font for value label.
938 	 */
939 	public Font getValueLabelFont()
940 	{
941 		return valueLabelFont;
942 	}
943 
944 	/**
945 	 * Sets font for value label.
946 	 *
947 	 * @param value New font for value label.
948 	 */
949 	public void setValueLabelFont(Font value)
950 	{
951 		Font oldValue = getValueLabelFont();
952 		valueLabelFont = value;
953 		firePropertyChange("valueLabelFont", oldValue, value);
954 	}
955 	
956 	/**
957 	 * Sets the low warning limit. The limit should be given in percentage of
958 	 * the current span of the gauger.
959 	 * 
960 	 * @param v new low warning limit
961 	 */
962 	public void setLowWarningLimit(double v){
963 		double oldValue = getLowWarningLimit();
964 		lowWarningLimit = v;
965 		firePropertyChange("lowWarningLimit", oldValue, v);
966 	}
967 	
968 	/**
969 	 * Returns the low warning limit.
970 	 * 
971 	 * @return low warning limit
972 	 */
973 	public double getLowWarningLimit(){
974 		return lowWarningLimit;
975 	}
976 	
977 	/**
978 	 * Sets the low alarm limit. The limit should be given in percentage of
979 	 * the current span of the gauger.
980 	 * 
981 	 * @param v new low alarm limit.
982 	 */
983 	public void setLowAlarmLimit(double v){
984 		double oldValue = getLowAlarmLimit();
985 		lowAlarmLimit = v;
986 		firePropertyChange("lowAlarmLimit", oldValue, v);
987 	}
988 	
989 	/**
990 	 * Returns the low alarm limit.
991 	 * 
992 	 * @return low alarm limit
993 	 */
994 	public double getLowAlarmLimit(){
995 		return lowAlarmLimit;
996 	}
997 	
998 	/**
999 	 * Sets the high warning limit. The limit should be given in percentage of
1000 	 * the current span of the gauger.
1001 	 * 
1002 	 * @param v new high warning limit
1003 	 */
1004 	public void setHighWarningLimit(double v){
1005 		double oldValue = getHighWarningLimit();
1006 		highWarningLimit = v;
1007 		firePropertyChange("highWarningLimit", oldValue, v);
1008 	}
1009 	
1010 	/**
1011 	 * Returns the high warning limit.
1012 	 *  
1013 	 * @return high warning limit
1014 	 */
1015 	public double getHighWarningLimit(){
1016 		return highWarningLimit;
1017 	}
1018 	
1019 	/**
1020 	 * Sets the high alarm limit. The limit should be given in percentage of
1021 	 * the current span of the gauger.
1022 	 * 
1023 	 * @param v new high alarm limit
1024 	 */
1025 	public void setHighAlarmLimit(double v){
1026 		double oldValue = getHighAlarmLimit();
1027 		highAlarmLimit = v;
1028 		firePropertyChange("highAlarmLimit", oldValue, v);
1029 	}
1030 	
1031 	/**
1032 	 * Returns the high alarm limit.
1033 	 * 
1034 	 * @return high alarm limit
1035 	 */
1036 	public double getHighAlarmLimit(){
1037 		return highAlarmLimit;
1038 	}
1039 	
1040 	/**
1041 	 * Returns the color which indicates the alarm status.
1042 	 * 
1043 	 * @return the alarmColor.
1044 	 */
1045 	public Color getAlarmColor() {
1046 		return alarmColor;
1047 	}
1048 
1049 	/**
1050 	 * Sets the color which indicates the alarm status.
1051 	 * 
1052 	 * @param alarmColor new alarm color
1053 	 */
1054 	public void setAlarmColor(Color alarmColor) {
1055 		Color oldValue = getAlarmColor();
1056 		this.alarmColor = alarmColor;
1057 		firePropertyChange("alarmColor", oldValue, alarmColor);
1058 	}
1059 
1060 	/**
1061 	 * Returns the color which indicates out of bounds status.
1062 	 * 
1063 	 * @return the outOfBoundsColor.
1064 	 */
1065 	public Color getOutOfBoundsColor() {
1066 		return outOfBoundsColor;
1067 	}
1068 
1069 	/**
1070 	 * Sets the color which indicates out of bounds status.
1071 	 * 
1072 	 * @param outOfBoundsColor new out of bounds color
1073 	 */
1074 	public void setOutOfBoundsColor(Color outOfBoundsColor) {
1075 		Color oldValue = getOutOfBoundsColor();
1076 		this.outOfBoundsColor = outOfBoundsColor;
1077 		firePropertyChange("outOfBoundsColor", oldValue, outOfBoundsColor);
1078 	}
1079 
1080 	/**
1081 	 * Returns the color which indicates warning status.
1082 	 * 
1083 	 * @return the warningColor.
1084 	 */
1085 	public Color getWarningColor() {
1086 		return warningColor;
1087 	}
1088 
1089 	/**
1090 	 * Sets the color which indicates the warning status.
1091 	 * 
1092 	 * @param warningColor new warning color
1093 	 */
1094 	public void setWarningColor(Color warningColor) {
1095 		Color oldValue = getWarningColor();
1096 		this.warningColor = warningColor;
1097 		firePropertyChange("warningColor", oldValue, warningColor);
1098 	}
1099 
1100 	// --------- methods below do not have any effect on the behaviour ------
1101 	/**
1102 	 * Returns the title of this widget.
1103 	 * 
1104 	 * @return
1105 	 */
1106 	protected String getTitle() {
1107 		return title;
1108 	}
1109 
1110 	/**
1111 	 * Sets the title of this widget.
1112 	 * 
1113 	 * @param title
1114 	 */
1115 	protected void setTitle(String title) {
1116 		String oldValue = getTitle();
1117 		this.title = title;
1118 		firePropertyChange("title", oldValue, title);
1119 	}
1120 
1121 	/**
1122 	 * Returns true is title is visible.
1123 	 * 
1124 	 * @return
1125 	 */
1126 	protected boolean isTitleVisible() {
1127 		return titleVisible;
1128 	}
1129 
1130 	/**
1131 	 * Sets the title visible/invisible.
1132 	 * 
1133 	 * @param titleVisible
1134 	 */
1135 	protected void setTitleVisible(boolean titleVisible) {
1136 		if (this.titleVisible == titleVisible) return;
1137 		this.titleVisible = titleVisible;
1138 		firePropertyChange("titleVisible", !titleVisible, titleVisible);
1139 	}
1140 
1141 	protected boolean isResizable() {
1142 		return resizable;
1143 	}
1144 
1145 	protected void setResizable(boolean resizable) {
1146 		if (this.resizable == resizable) return;
1147 		this.resizable = resizable;
1148 		firePropertyChange("resizable", !resizable, resizable);
1149 	}
1150 
1151 	/**
1152 	 * Returns the maximum allowed font size of the title.
1153 	 * 
1154 	 * @return title maximum font size
1155 	 */
1156 	protected int getTitleMaximumFontSize() {
1157 		return titleMaximumFontSize;
1158 	}
1159 
1160 	/**
1161 	 * Sets the maximum allowed font size of the title.
1162 	 * 
1163 	 * @param titleMaximumFontSize new maximum font size
1164 	 */
1165 	protected void setTitleMaximumFontSize(int titleMaximumFontSize) {
1166 		int oldValue = getTitleMaximumFontSize();
1167 		this.titleMaximumFontSize = titleMaximumFontSize;
1168 		firePropertyChange("titleMaximumFontSize", oldValue, titleMaximumFontSize);
1169 	}
1170 
1171 	/**
1172 	 * Returns the minimum allowed font size of the title.
1173 	 * 
1174 	 * @return title minimum font size
1175 	 */
1176 	protected int getTitleMinimumFontSize() {
1177 		return titleMinimumFontSize;
1178 	}
1179 
1180 	/**
1181 	 * Sets the minimum allowed font size of the title.
1182 	 * 
1183 	 * @param titleMinimumFontSize new minimum font size
1184 	 */
1185 	protected void setTitleMinimumFontSize(int titleMinimumFontSize) {
1186 		int oldValue = getTitleMinimumFontSize();
1187 		this.titleMinimumFontSize = titleMinimumFontSize;
1188 		firePropertyChange("titleMinimumFontSize", oldValue, titleMinimumFontSize);
1189 	}
1190 	
1191 	/*
1192 	 * (non-Javadoc)
1193 	 * @see com.cosylab.gui.components.range2.TickParameters#formatNumber(double)
1194 	 */
1195 	public String formatNumber(double x) {
1196 		return formatter.sprintf(x);
1197 	}
1198 	
1199 	/**
1200 	 * Simple test method for gauger.
1201 	 *
1202 	 * @param args String[]
1203 	 */
1204 	public static void main(String[] args)
1205 	{
1206 		Thread t = new Thread() {
1207 				public void run()
1208 				{
1209 					javax.swing.JFrame frame = new javax.swing.JFrame();
1210 					frame.addWindowListener(new WindowAdapter() {
1211 							public void windowClosing(WindowEvent e)
1212 							{
1213 								System.exit(0);
1214 							}
1215 						});
1216 
1217 					frame.setSize(new java.awt.Dimension(150, 120));
1218 
1219 					Gauger sp = new Gauger();
1220 					sp.setLinearScale();
1221 					sp.getRangedValue().setTickCalculator(new ManualTickCalculator(new double[]{5,50,100,500}, new double[]{5,50,100,500}, 100));
1222 					sp.setScaleMode(Gauger.FIXED_SCALE);
1223 					sp.setValue(-100, 500.0, 0);
1224 					sp.setUnits("A");
1225 					sp.setFormat("%d");
1226 					frame.setContentPane(sp);
1227 					//frame.show();
1228 					frame.setVisible(true);
1229 
1230 					//double x = 5;
1231 					int i = 1;
1232 
1233 					//long time1 = System.currentTimeMillis();
1234 					//long time2 = 0;
1235 					//boolean b= false;
1236 					while (true) {
1237 						//time2 = System.currentTimeMillis() - time1;
1238 						//x = 2.0
1239 						//	+ NoiseMaker.perlinNoise((double)time2 / 100.0 * Math.PI / 180);
1240 						sp.setValue(Math.random() * 80 -40 );
1241 					
1242 						try {
1243 							i++;
1244 
1245 							if (i > 50) {
1246 								//	time2 = System.currentTimeMillis();
1247 								//	delta = time2 - time1;
1248 								//	time1 = time2;
1249 								//	System.out.println(i * 1000 / delta + " FPS");
1250 								i = 0;
1251 							}
1252 
1253 							sleep(500);
1254 						} catch (Exception e) {
1255 							e.printStackTrace();
1256 						}
1257 					}
1258 				}
1259 			};
1260 
1261 		t.run();
1262 	}
1263 }
1264 
1265 /* __oOo__ */