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.Dimension;
24  import java.awt.Font;
25  import java.awt.GridBagConstraints;
26  import java.awt.GridBagLayout;
27  import java.awt.Insets;
28  import java.awt.event.ActionEvent;
29  import java.awt.event.ActionListener;
30  import java.awt.event.MouseAdapter;
31  import java.awt.event.MouseEvent;
32  import java.util.ArrayList;
33  
34  import javax.swing.AbstractAction;
35  import javax.swing.Icon;
36  import javax.swing.JPanel;
37  import javax.swing.JSlider;
38  import javax.swing.Timer;
39  import javax.swing.UIDefaults;
40  import javax.swing.UIManager;
41  import javax.swing.event.ChangeEvent;
42  import javax.swing.event.ChangeListener;
43  
44  import com.cosylab.application.state.State;
45  import com.cosylab.application.state.StateFactory;
46  import com.cosylab.application.state.StateOriginator;
47  import com.cosylab.events.SetEvent;
48  import com.cosylab.events.SetListener;
49  import com.cosylab.gui.components.customizer.AbstractCustomizerPanel;
50  import com.cosylab.gui.components.range2.IntegerValuePolicy;
51  import com.cosylab.gui.components.range2.LinearRange;
52  import com.cosylab.gui.components.range2.ManualTickCalculator;
53  import com.cosylab.gui.components.range2.RangedValueController;
54  import com.cosylab.gui.components.range2.RangedValueEvent;
55  import com.cosylab.gui.components.range2.RangedValueListener;
56  import com.cosylab.gui.components.range2.RangedValuePolicy;
57  import com.cosylab.gui.components.range2.TickParameters;
58  import com.cosylab.gui.components.slider.CosySliderUI;
59  import com.cosylab.gui.components.slider.InfoBar;
60  import com.cosylab.gui.components.slider.NavigationBar;
61  import com.cosylab.gui.components.util.ColorHelper;
62  import com.cosylab.gui.components.util.CosyTransferHandler;
63  import com.cosylab.gui.components.util.CosyUIElements;
64  import com.cosylab.gui.components.util.PopupManageable;
65  import com.cosylab.gui.components.util.PopupManager;
66  import com.cosylab.gui.components.util.RunnerHelper;
67  import com.cosylab.util.PrintfFormat;
68  
69  /**
70   * Slider is a GUI component for displaying double value. Slider defines limits within
71   * which the value may be set and fire update events when this value is changed. For
72   * precise control, textual entry and button controls are also available.
73   * <p>
74   * When displaying values slider can show two different values. The front thumb displays
75   * the value that was set by the user. According to the set mode currently set on the
76   * Slider ({@link #setSetMode(SliderSetMode)}) slider then notifies listeners of the value
77   * change. The second value is displayed by a grey shadow. This value can be controlled by
78   * the {@link #setReadback(double)} method and can only be changed manually (through this
79   * method call).
80   * 
81   * @author <a href="mailto:ales.pucelj@cosylab.com">Ales Pucelj </a>
82   * @version $id$
83   */
84  public class Slider extends JPanel implements TickParameters, PopupManageable, StateOriginator {
85  
86  	/**
87  	 * Button identifiers for any of the buttons used in the Slider.
88  	 * 
89  	 * @see Slider#setButtonTooltip(com.cosylab.gui.components.Slider.Button, String)
90  	 * @author <a href="mailto:jaka.bobnar@cosylab.com">Jaka Bobnar</a>
91  	 * @version $Id: Slider.java,v 1.84 2009-01-12 12:46:02 jbobnar Exp $
92  	 */
93  	public enum Button {
94  		FAST_INCREMENT, FAST_DECREMENT, BIT_INCREMENT, BIT_DECREMENT, SLOW_INCREMENT, SLOW_DECREMENT,
95  	}
96  
97  	private static final long serialVersionUID = 1L;
98  
99  	private boolean popupEnabled;
100 
101 	/**
102 	 * Set only when demanded
103 	 * 
104 	 * @deprecated use {@link SliderSetMode#SET_MANUAL} instead.
105 	 */
106 	@Deprecated
107 	public static final int SET_MANUAL = 0;
108 
109 	/**
110 	 * Set when dragging stops
111 	 * 
112 	 * @deprecated use {@link SliderSetMode#SET_ON_RELEASE} instead.
113 	 */
114 	@Deprecated
115 	public static final int SET_ON_RELEASE = 1;
116 
117 	/**
118 	 * Set when value changes
119 	 * 
120 	 * @deprecated use {@link SliderSetMode#SET_ON_CHANGE} instead.
121 	 */
122 	@Deprecated
123 	public static final int SET_ON_CHANGE = 2;
124 
125 	private AbstractCustomizerPanel customizer;
126 	private PopupManager popupManager;
127 
128 	private final ArrayList<SetListener> listeners = new ArrayList<SetListener>();
129 	
130 	private JSlider slider;
131 	private CosySliderUI sliderUI;
132 	private InfoBar infoBar;
133 	private NavigationBar navigationBar;
134 
135 	private RangedValueController rangedValue;
136 
137 	// suspended count
138 	private int suspended = 0;
139 	private boolean showIntegerOnly = false;
140 	private boolean supressChanges = false;
141 	private boolean supressSetEvent = false;
142 	private boolean supressPropChEvent = false;
143 	private boolean externalSet = false;
144 
145 	private static final double DEFAULT_BIT_STEP = 1;
146 	private static final double DEFAULT_SMALL_STEP = 10;
147 	private static final double DEFAULT_LARGE_STEP = 25;
148 	
149 	// BitStep value , to tell slider how big the step should be
150 	private double bitStep = DEFAULT_BIT_STEP;
151 	// If isBitSteepRelative=false then we change absulut value else relative
152 	private boolean isBitStepRelative = true;
153 	// SmallStep value , to tell slider how big the step should be
154 	private double smallStep = DEFAULT_SMALL_STEP;
155 	// If isSmallSteepRelative=false then we change absulute value else relative
156 	private boolean isSmallStepRelative = true;
157 	// LargeStep value , to tell slider how big the step should be
158 	private double largeStep = DEFAULT_LARGE_STEP;
159 	// If isLargeSteepRelative=false then we change absulut value else relative
160 	private boolean isLargeStepRelative = true;
161 
162 	
163 	// For autosynchronization see RT #22324
164 	private Timer autoSyncTimer;
165 	/*
166 	 * Flag indicating whether the slider is automatically synchronized each time the
167 	 * readback changes. Synchronization occurs after the autoSynchronizeDelay miliseconds
168 	 * pass since the last user intervention.
169 	 */
170 	private boolean autoSynchronize = false;
171 	/*
172 	 * The time in milliseconds that have to pass since last user intervention for the
173 	 * automatical synchronization to occur for the first time.
174 	 */
175 	private long autoSynchronizeDelay = 2000;
176 	private boolean allowAutoSync = true;
177 	private double storedValue = Double.NaN;
178 	private boolean continuousControlVisible = false;
179 	private boolean continuousModeEnabled = false;
180 	
181 	private PrintfFormat formatter;
182 	private String title = " ";
183 	private boolean settable = true;
184 	private SliderSetMode mode = SliderSetMode.SET_ON_CHANGE;
185 	private boolean snapToTicks = false;
186 
187 	private ManualTickCalculator manualTickProvider = new ManualTickCalculator();
188 
189 	private class ValueListener implements RangedValueListener {
190 		/**
191 		 * @see com.cosylab.gui.components.range.RangedValueListener#valueChanged(RangedValueEvent)
192 		 */
193 		public void valueChanged(RangedValueEvent event) {
194 			if (event.isMinOrMaxChanged()) {
195 
196 				getSlider().setValue((int)getRangedValue().getRelativeValue());
197 
198 				getSliderUI().setTrailerPosition(getSliderUI().getTrailerPosition());
199 				getNavigationBar().setMinimum(getRangedValue().getMinimum());
200 				getNavigationBar().setMaximum(getRangedValue().getMaximum());
201 				repaint();
202 
203 			}
204 		}
205 	}
206 
207 	/**
208 	 * Resets sliders steps to default values
209 	 */
210 	public void resetSteps() {
211 		setBitStep(DEFAULT_BIT_STEP);
212 		setBitStepRelative(true);
213 		setSmallStep(DEFAULT_SMALL_STEP);
214 		setSmallStepRelative(true);
215 		setLargeStep(DEFAULT_LARGE_STEP);
216 		setLargeStepRelative(true);
217 	}
218 	
219 	private PrintfFormat getFormatter() {
220 		if (formatter == null) {
221 			formatter = new PrintfFormat("%f");
222 		}
223 		return formatter;
224 	}
225 
226 	/**
227 	 * Listens for changes to draggable slider button.
228 	 * 
229 	 * @author <a href="mailto:ales.pucelj@cosylab.com">Ales Pucelj </a>
230 	 * @version $id$
231 	 */
232 	private class SliderChange implements ChangeListener {
233 		/**
234 		 * Notifies of value change.
235 		 * 
236 		 * @param event ChangeEvent
237 		 * @see javax.swing.event.ChangeListener#stateChanged(ChangeEvent)
238 		 */
239 		public void stateChanged(ChangeEvent event) {
240 			if (mode == SliderSetMode.SET_ON_CHANGE || mode == SliderSetMode.SET_MANUAL) {
241 				performInternalSliderSet();
242 			}
243 			if (!externalSet || mode == SliderSetMode.SET_ON_RELEASE) {
244 				getNavigationBar().setValue(getRangedValue().toAbsolute(getSlider().getValue()));
245 			}
246 		}
247 	}
248 
249 	/**
250 	 * Creates a new Slider object.
251 	 */
252 	public Slider() {
253 		super();
254 		initialize();
255 	}
256 
257 	/**
258 	 * Adds set listener. SetListener receives events when the thumb was dragged by the
259 	 * mouse.
260 	 * 
261 	 * @param listener SetListener
262 	 */
263 	public void addSetListener(SetListener listener) {
264 		synchronized (listeners) {
265 			listeners.add(listener);
266 		}
267 	}
268 
269 	/**
270 	 * Removes set listener.
271 	 * 
272 	 * @param listener SetListener
273 	 */
274 	public void removeSetListener(SetListener listener) {
275 		synchronized (listeners) {
276 			listeners.remove(listener);
277 		}
278 	}
279 
280 	/**
281 	 * Fires event that value has changed.
282 	 * 
283 	 * @param newValue double
284 	 */
285 	protected void fireSetPerformed(double newValue) {
286 		final SetEvent e = new SetEvent(this,newValue);
287 
288 		synchronized (listeners) {
289 			final int n = listeners.size();
290 
291 			for (int i = 0; i < n; i++) {
292 				listeners.get(i).setPerformed(e);
293 			}
294 		}
295 	}
296 
297 	private void initialize() {
298 		setLayout(new GridBagLayout());
299 		setBorder(CosyUIElements.getPlainBorder(true));
300 		setBackground(ColorHelper.getCosyControl());
301 
302 		add(getInfoBar(),new GridBagConstraints(0,0,1,1,1,0,GridBagConstraints.CENTER,GridBagConstraints.HORIZONTAL,new Insets(1,2,2,2),0,0));
303 		add(getNavigationBar(),new GridBagConstraints(0,1,1,1,1,0,GridBagConstraints.CENTER,GridBagConstraints.HORIZONTAL,new Insets(1,2,2,2),0,0));
304 
305 		GridBagConstraints gc = new GridBagConstraints();
306 
307 		gc.gridx = 0;
308 		gc.gridy = 2;
309 		gc.weightx = 1.0;
310 		gc.weighty = 0.0;
311 		gc.fill = GridBagConstraints.HORIZONTAL;
312 		gc.insets = new Insets(0,2,0,2);
313 
314 		getSliderUI().setTolerance(0.0000001);
315 		add(getSlider(),gc);
316 
317 		setValue(0);
318 		setForeground(Color.BLACK);
319 		setStorageButtonsVisible(false);
320 		setContinuousControlVisible(false);
321 		setPopupEnabled(true);
322 	}
323 
324 	protected InfoBar getInfoBar() {
325 		if (infoBar == null) {
326 			infoBar = new InfoBar(this);
327 		}
328 
329 		return infoBar;
330 	}
331 
332 	protected NavigationBar getNavigationBar() {
333 		if (navigationBar == null) {
334 			navigationBar = new NavigationBar(this);
335 			navigationBar.addSetListener(new SetListener() {
336 
337 				public void setPerformed(SetEvent e) {
338 					performInternalSet(e.getDoubleValue());
339 				}
340 
341 			});
342 		}
343 
344 		return navigationBar;
345 	}
346 
347 	protected JSlider getSlider() {
348 		if (slider == null) {
349 			slider = new JSlider();
350 			slider.setBackground(ColorHelper.getCosyControl());
351 			slider.setPaintLabels(true);
352 			slider.setPaintTicks(true);
353 			slider.setMinorTickSpacing(10000);
354 			slider.setMajorTickSpacing(10000);
355 
356 			slider.setMinimum(0);
357 			slider.setMaximum(10000);
358 
359 			// *** BUGFIX ***
360 			// This is bugfix for netbeans 3.6RC1 L&F problem
361 			// It appears there is a problem with how the NB L&F is implemented.
362 			// Done to resolve RT#5535
363 			// Setting these global variables may affect other sliders,
364 			// depending
365 			// on Metal or all L&F settings
366 			// ***
367 			UIManager.put("Slider.trackWidth",new Integer(7));
368 			UIManager.put("Slider.majorTickLength",new Integer(6));
369 			UIManager.put("Slider.horizontalThumbIcon",new UIDefaults.ProxyLazyValue("javax.swing.plaf.metal.MetalIconFactory",
370 					"getHorizontalSliderThumbIcon"));
371 			UIManager.put("Slider.verticalThumbIcon",new UIDefaults.ProxyLazyValue("javax.swing.plaf.metal.MetalIconFactory",
372 					"getVerticalSliderThumbIcon"));
373 
374 			// *** END BUGFIX ***
375 			slider.setUI(getSliderUI());
376 			slider.addChangeListener(new SliderChange());
377 			slider.addMouseListener(new MouseAdapter() {
378 
379 				@Override
380 				public void mousePressed(MouseEvent e) {
381 					allowAutoSync = false;
382 				}
383 
384 				@Override
385 				public void mouseReleased(MouseEvent e) {
386 					if (mode == SliderSetMode.SET_ON_RELEASE) {
387 						performInternalSliderSet();
388 					} else if (showIntegerOnly) {
389 						getSlider().setValue((int)(getRangedValue().getRelativeValue()));
390 					}
391 					allowAutoSync = true;
392 
393 				}
394 			});
395 			slider.setPreferredSize(new Dimension(150,50));
396 		}
397 		return slider;
398 	}
399 
400 	private int old = Integer.MIN_VALUE;
401 
402 	private void performInternalSliderSet() {
403 		synchronized (this) {
404 			if (!supressChanges) {
405 				int newV = getSlider().getValue();
406 				if (old != newV) {
407 					performInternalSetRelative(newV);
408 				}
409 				old = newV;
410 			}
411 		}
412 	}
413 
414 	private void performInternalSet(double value) {
415 
416 		value = getRangedValue().validate(value);
417 
418 		if (value == getValue()) {
419 			return;
420 		}
421 		
422 		getNavigationBar().setValue(value);
423 
424 		if (mode == SliderSetMode.SET_MANUAL) {
425 			synchronized (this) {
426 				if (!supressChanges) {
427 					supressChanges = true;
428 
429 					getRangedValue().setValue(value);
430 					// distribute change
431 					getSlider().setValue((int)getRangedValue().getRelativeValue());
432 					getNavigationBar().setValue(getValue());
433 
434 					supressChanges = false;
435 				}
436 			}
437 			return;
438 		}
439 
440 		synchronized (this) {
441 			if (!supressChanges) {
442 				supressChanges = true;
443 
444 				getAutoSyncTimer().stop();
445 
446 				double old = getValue();
447 				getRangedValue().setValue(value);
448 
449 				// distribute change
450 				getSlider().setValue((int)getRangedValue().getRelativeValue());
451 				getNavigationBar().setValue(getValue());
452 				
453 				if (!isEditable()) {
454 					setReadback(getValue());
455 				}
456 
457 				if (!supressSetEvent && !isSuspended()) {
458 					fireSetPerformed(getValue());
459 				}
460 				if (!supressPropChEvent && !isSuspended()) {
461 					firePropertyChange("value",old,getValue());
462 				}
463 
464 				if (autoSynchronize) {
465 					getAutoSyncTimer().restart();
466 				}
467 
468 				supressChanges = false;
469 			}
470 		}
471 
472 	}
473 
474 	private void performInternalSetRelative(double rVal) {
475 		performInternalSet(getRangedValue().toAbsolute(rVal));
476 	}
477 	
478 	private void performInternalFractionSet(double fraction) {
479 		performInternalSetRelative(getRangedValue().getRelativeValue() + fraction * getRangedValue().getScalingFactor());
480 
481 	}
482 
483 	/**
484 	 * Create this slider UI.
485 	 * 
486 	 * @return the ui
487 	 */
488 	protected CosySliderUI getSliderUI() {
489 		if (sliderUI == null) {
490 			sliderUI = new CosySliderUI(getRangedValue());
491 		}
492 
493 		return sliderUI;
494 	}
495 	
496 	/**
497 	 * Sets new minimum value.
498 	 * 
499 	 * @param minimum new minimum value.
500 	 */
501 	public void setMinimum(double minimum) {
502 		double oldValue = getMinimum();
503 		// double trailerValue = getSliderUI().getTrailerPosition();
504 		getRangedValue().setMinimum(minimum);
505 		/*
506 		 * If minimum has changed, navigationBar's minimum and slider's scale and position
507 		 * has to be modified.
508 		 */
509 		getNavigationBar().setMinimum(minimum);
510 		modifySliderPosition();
511 		firePropertyChange("minimum",oldValue,minimum);
512 	}
513 
514 	/**
515 	 * Sets new maximum value.
516 	 * 
517 	 * @param maximum new maximum value.
518 	 */
519 	public void setMaximum(double maximum) {
520 		double oldValue = getMaximum();
521 		// double trailerValue = getSliderUI().getTrailerPosition();
522 		getRangedValue().setMaximum(maximum);
523 		/*
524 		 * If maximum has changed, navigationBar's maximum and slider's scale and position
525 		 * has to be modified.
526 		 */
527 		getNavigationBar().setMaximum(maximum);
528 		modifySliderPosition();
529 		firePropertyChange("maximum",oldValue,maximum);
530 	}
531 
532 	/**
533 	 * Sets the new value that this slider will display. This method will set the front
534 	 * thumb value.
535 	 * 
536 	 * @param value new value.
537 	 */
538 	public void setValue(double value) {
539 		getAutoSyncTimer().stop();
540 
541 		double oldValue = getValue();
542 
543 		synchronized (this) {
544 			supressChanges = true;
545 			getRangedValue().setValue(value);
546 			// distribute change
547 			
548 			externalSet = true;
549 			getSlider().setValue((int)getRangedValue().getRelativeValue());
550 			getNavigationBar().setValue(getValue());
551 			
552 			if (!isEditable()) {
553 				setReadback(getValue());
554 			}
555 
556 			supressChanges = false;
557 		}
558 
559 		if (autoSynchronize) {
560 			getAutoSyncTimer().restart();
561 		}
562 
563 		firePropertyChange("value",oldValue,value);
564 	}
565 
566 	private Timer getAutoSyncTimer() {
567 		if (autoSyncTimer == null) {
568 			autoSyncTimer = new Timer((int)autoSynchronizeDelay,new ActionListener() {
569 				public void actionPerformed(ActionEvent e) {
570 					if (allowAutoSync) synchronize();
571 					autoSyncTimer.stop();
572 				}
573 			});
574 		}
575 		return autoSyncTimer;
576 	}
577 
578 	/**
579 	 * Returns currently set minimum value.
580 	 * 
581 	 * @return double minimum value.
582 	 */
583 	public double getMinimum() {
584 		return getRangedValue().getMinimum();
585 	}
586 
587 	/**
588 	 * Returns currently set maxium value.
589 	 * 
590 	 * @return double maximum value.
591 	 */
592 	public double getMaximum() {
593 		return getRangedValue().getMaximum();
594 	}
595 
596 	/**
597 	 * Returns current value the slider is displaying.
598 	 * 
599 	 * @return double Current value.
600 	 */
601 	public double getValue() {
602 		/*
603 		 * Fix for out of bounds behaviour of this component (RT #26250). Replaced: return
604 		 * getRangedValue().getValue();
605 		 */
606 		return getRangedValue().getRawValue();
607 	}
608 
609 	/**
610 	 * Sets string representing units displayed next to value entry field.
611 	 * 
612 	 * @param units new value for units, may be null.
613 	 */
614 	public void setUnits(String units) {
615 		String oldValue = getUnits();
616 		getNavigationBar().setUnits(units);
617 		firePropertyChange("units",oldValue,units);
618 	}
619 
620 	/**
621 	 * Returns string representing units that are displayed next to value entry field.
622 	 * 
623 	 * @return String units string.
624 	 */
625 	public String getUnits() {
626 		return getNavigationBar().getUnits();
627 	}
628 
629 	/**
630 	 * Sets whether units are displayed next to value entry field.
631 	 * 
632 	 * @param b wehter units are displayed.
633 	 */
634 	public void setUnitsVisible(boolean b) {
635 		if (isUnitsVisible() == b) return;
636 		getNavigationBar().setUnitsVisible(b);
637 		firePropertyChange("unitsVisible",!b,b);
638 	}
639 
640 	/**
641 	 * Returns true if units are displayed next to value entry field.
642 	 * 
643 	 * @return boolean unitsVisible.
644 	 */
645 	public boolean isUnitsVisible() {
646 		return getNavigationBar().isUnitsVisible();
647 	}
648 	
649 	//used only in the method below
650 	private double lastReadbackValue = Double.NaN;
651 
652 	/**
653 	 * Sets the readback indicator position. This determines the position of trailer
654 	 * shadow that is displayed to indicate last readback. <br>
655 	 * This method has no effect if slider is not settable.
656 	 * 
657 	 * @param value double new readback position.
658 	 */
659 	public void setReadback(double value) {
660 		double oldValue = getReadback();
661 		getSliderUI().setTrailerPosition(value);
662 		if (value < getMinimum() || value > getMaximum()) {
663 			getNavigationBar().setValue(value);
664 		}
665 		if (autoSynchronize) {
666 			if (lastReadbackValue != value) {
667 				getAutoSyncTimer().restart();
668 			} else {
669 				if (!getAutoSyncTimer().isRunning()) {
670 					getAutoSyncTimer().start();
671 				}
672 			}
673 
674 		}
675 
676 		lastReadbackValue = value;
677 		firePropertyChange("readBack",oldValue,value);
678 		if (!isEditable() || !isSyncButtonVisible()) {
679 			synchronize();
680 		}
681 	}
682 	
683 	/**
684 	 * Returns ranged value representing this slider's value.
685 	 * 
686 	 * @return RangedValue.
687 	 */
688 	private RangedValueController getRangedValue() {
689 		if (rangedValue == null) {
690 			rangedValue = new RangedValueController();
691 			rangedValue.setRange(new LinearRange());
692 			rangedValue.setScalingFactor(10000);
693 			rangedValue.addRangedValueListener(new ValueListener());
694 		}
695 
696 		return rangedValue;
697 	}
698 
699 	/**
700 	 * Returns current readback position. This is the position of the trailer shadow thad
701 	 * indicates last readback. <br>
702 	 * If slider is not settable the method will return the value of the front thumb.
703 	 * 
704 	 * @return double the readback value
705 	 */
706 	public double getReadback() {
707 		return getSliderUI().getTrailerPosition();
708 	}
709 
710 	/**
711 	 * Sets format for navigator and ticks.
712 	 * 
713 	 * @param s new C-Style format.
714 	 * @deprecated use {@link #setTicksFormat(String)} and {@link #setFieldFormat(String)}
715 	 */
716 	@Deprecated
717 	public void setFormat(String s) {
718 		String oldValue = getFormat();
719 		setFieldFormat(s);
720 		setTicksFormat(s);
721 		firePropertyChange("format",oldValue,s);
722 	}
723 	
724 	/**
725 	 * Returns the format used for number labels on the slider. This method
726 	 * in fact returns the format of the number field.
727 	 * 
728 	 * @return the C-style format
729 	 * @deprecated use {@link #getTicksFormat()} and {@link #getFieldFormat()} 
730 	 */
731 	@Deprecated
732 	public String getFormat() {
733 		return getFieldFormat();
734 	}
735 	
736 	
737 	/**
738 	 * Sets format for editable field.
739 	 * 
740 	 * @param s new C-Style format.
741 	 */
742 	public void setFieldFormat(String s) {
743 		String oldValue = getFieldFormat();
744 		getNavigationBar().setFormat(s);
745 		if (Double.isNaN(storedValue)) {
746 			getInfoBar().setStoredValue(null);
747 		} else {
748 			getInfoBar().setStoredValue(getNavigationBar().format(storedValue));
749 		}
750 		firePropertyChange("fieldFormat",oldValue,s);
751 	}
752 	
753 	/**
754 	 * Returns format of the editable field.
755 	 * 
756 	 * @return C-Style format.
757 	 */
758 	public String getFieldFormat() {
759 		return getNavigationBar().getFormat();
760 	}
761 	
762 	private RangedValuePolicy integerPolicy = new IntegerValuePolicy();
763 	
764 	/**
765 	 * Sets the string format for the ticks of this slider. This format
766 	 * will affect only the strings in the slider scale but not the ticks
767 	 * in the naviagtion bar.
768 	 * 
769 	 * @param ticksFormat the ticks format
770 	 */
771 	public void setTicksFormat(String ticksFormat) {
772 		String oldValue = getTicksFormat();
773 		if (ticksFormat.contains("%d")) {
774 			showIntegerOnly = true;
775 			getRangedValue().addPolicy(integerPolicy);
776 			// round to tick
777 			setValue(getValue());
778 		} else {
779 			getRangedValue().removePolicy(integerPolicy);
780 			showIntegerOnly = false;
781 		}
782 		getSliderUI().setFormat(ticksFormat);
783 		formatter = new PrintfFormat(ticksFormat);
784 		firePropertyChange("ticksFormat",oldValue,ticksFormat);
785 		// repaint ticks
786 		repaint();
787 	}
788 		
789 	/**
790 	 * Returns the formatter used for the slider ticks on the scale.
791 	 * 
792 	 * @return the ticks format
793 	 */
794 	public String getTicksFormat() {
795 		return getSliderUI().getFormat();
796 	}
797 
798 	/**
799 	 * Returns the title of the Slider.
800 	 * 
801 	 * @return title.
802 	 */
803 	public String getTitle() {
804 		return getInfoBar().getTitle();
805 	}
806 
807 	/**
808 	 * Sets the title of the slider.
809 	 * 
810 	 * @param title new title
811 	 */
812 	public void setTitle(String title) {
813 		String oldValue = getTitle();
814 		this.title = title;
815 		getInfoBar().setTitle(title);
816 		firePropertyChange("title",oldValue,title);
817 	}
818 
819 	/**
820 	 * Sets the visibility of the title.
821 	 * 
822 	 * @param titleVisible true if title should be visible
823 	 */
824 	public void setTitleVisible(boolean titleVisible) {
825 		if (getTitleVisible() == titleVisible) return;
826 		if (!titleVisible) {
827 			title = getInfoBar().getTitle();
828 			getInfoBar().setTitle(" ");
829 		} else {
830 			getInfoBar().setTitle(this.title);
831 		}
832 		firePropertyChange("titleVisible",!titleVisible,titleVisible);
833 	}
834 
835 	/**
836 	 * Returns true if title is visible.
837 	 * 
838 	 * @return true if title is visible
839 	 */
840 	public boolean getTitleVisible() {
841 		return !getInfoBar().getTitle().equals("");
842 	}
843 
844 	/**
845 	 * Performs a small value increment.
846 	 * 
847 	 * @see #setSmallStep(double)
848 	 */
849 	public void incrementSmall() {
850 		if (isSmallStepRelative()) {
851 			performInternalFractionSet(getSmallStep() / 100);
852 		} else {
853 			performInternalSet(getRangedValue().validate(getValue() + getSmallStep()));
854 		}
855 	}
856 
857 	/**
858 	 * Peforms a large value increment.
859 	 * 
860 	 * @see #setLargeStep(double)
861 	 */
862 	public void incrementLarge() {
863 		if (isLargeStepRelative() == true) {
864 			performInternalFractionSet(getLargeStep() / 100);
865 		} else {
866 			performInternalSet(getRangedValue().validate(getValue() + getLargeStep()));
867 		}
868 	}
869 
870 	/**
871 	 * Increments value for a single bit.
872 	 * 
873 	 * @see #setBitStep(double)
874 	 */
875 	public void incrementBit() {
876 		if (isBitStepRelative()) {
877 			performInternalFractionSet(getBitStep() / 100);
878 		} else {
879 			performInternalSet(getRangedValue().validate(getValue() + getBitStep()));
880 		}
881 	}
882 
883 	/**
884 	 * Decrements value for a single bit.
885 	 * 
886 	 * @see #setBitStep(double)
887 	 */
888 	public void decrementBit() {
889 		if (isBitStepRelative()) {
890 			performInternalFractionSet(-getBitStep() / 100);
891 		} else {
892 			performInternalSet(getRangedValue().validate(getValue() - getBitStep()));
893 		}
894 	}
895 
896 	/**
897 	 * Decrements value for the small step.
898 	 * 
899 	 * @see #setSmallStep(double)
900 	 */
901 	public void decrementSmall() {
902 		if (isSmallStepRelative()) {
903 			performInternalFractionSet(-getSmallStep() / 100);
904 		} else {
905 			performInternalSet(getRangedValue().validate(getValue() - getSmallStep()));
906 		}
907 	}
908 
909 	/**
910 	 * Decrements value for the large step.
911 	 * 
912 	 * @see #setLargeStep(double)
913 	 */
914 	public void decrementLarge() {
915 		if (isLargeStepRelative()) {
916 			performInternalFractionSet(-getLargeStep() / 100);
917 		} else {
918 			performInternalSet(getRangedValue().validate(getValue() - getLargeStep()));
919 		}
920 	}
921 
922 	/**
923 	 * Sets the value of a bit step.
924 	 * 
925 	 * @param value new bit step
926 	 */
927 	public void setBitStep(double value) {
928 		double oldValue = getBitStep();
929 		bitStep = value;
930 		firePropertyChange("bitStep",oldValue,value);
931 	}
932 
933 	/**
934 	 * Returns the value of bit step.
935 	 * 
936 	 * @return bitStep the bit step
937 	 */
938 	public double getBitStep() {
939 		return bitStep;
940 	}
941 
942 	/**
943 	 * Returns the value of the small step.
944 	 * 
945 	 * @return current small step.
946 	 */
947 	public double getSmallStep() {
948 		return smallStep;
949 	}
950 
951 	/**
952 	 * Sets the small step.
953 	 * 
954 	 * @param value new small step
955 	 */
956 	public void setSmallStep(double value) {
957 		double oldValue = getSmallStep();
958 		smallStep = value;
959 		firePropertyChange("smallStep",oldValue,value);
960 	}
961 
962 	/**
963 	 * Sets the value of large step
964 	 * 
965 	 * @param value new large step
966 	 */
967 	public void setLargeStep(double value) {
968 		double oldValue = getLargeStep();
969 		largeStep = value;
970 		firePropertyChange("largeStep",oldValue,value);
971 	}
972 
973 	/**
974 	 * Returns the value of largeStep.
975 	 * 
976 	 * @return current large step
977 	 */
978 	public double getLargeStep() {
979 		return largeStep;
980 	}
981 
982 	/**
983 	 * Returns true if bitStep is relative, else false.
984 	 * 
985 	 * @return
986 	 */
987 	public boolean isBitStepRelative() {
988 		return isBitStepRelative;
989 	}
990 
991 	/**
992 	 * Sets bitStep to be relative if true (example: bitStep=20 means that bitStep will
993 	 * change for 20% of sliders current range(min,max)).
994 	 * 
995 	 * @param value true is realtive
996 	 */
997 	public void setBitStepRelative(boolean value) {
998 		if (isBitStepRelative() == value) return;
999 		isBitStepRelative = value;
1000 		firePropertyChange("bitStepRelative",!value,value);
1001 	}
1002 
1003 	/**
1004 	 * Returns true if smallStep is relative, else false.
1005 	 * 
1006 	 * @return
1007 	 */
1008 	public boolean isSmallStepRelative() {
1009 		return isSmallStepRelative;
1010 	}
1011 
1012 	/**
1013 	 * Sets smallStep to be relative if true (example: smallStep=20 means that smallStep
1014 	 * will change for 20% of sliders current range(min,max)).
1015 	 * 
1016 	 * @param value true if relative
1017 	 */
1018 	public void setSmallStepRelative(boolean value) {
1019 		if (isSmallStepRelative() == value) return;
1020 		isSmallStepRelative = value;
1021 		firePropertyChange("smallStepRelative",!value,value);
1022 	}
1023 
1024 	/**
1025 	 * Returns true if largeStep is relative, else false.
1026 	 * 
1027 	 * @return
1028 	 */
1029 	public boolean isLargeStepRelative() {
1030 		return isLargeStepRelative;
1031 	}
1032 
1033 	/**
1034 	 * Sets largeStep to be relative if true (example: largeStep=20 means that largeStep
1035 	 * will change for 20% of sliders current range(min,max)).
1036 	 * 
1037 	 * @param value true if relative
1038 	 */
1039 	public void setLargeStepRelative(boolean value) {
1040 		if (isLargeStepRelative() == value) return;
1041 		isLargeStepRelative = value;
1042 		firePropertyChange("largeStepRelative",!value,value);
1043 	}
1044 
1045 	/**
1046 	 * Return set mode.
1047 	 * 
1048 	 * @return int
1049 	 * @deprecated use {@link Slider#getSetMode()}
1050 	 */
1051 	@Deprecated
1052 	public int getMode() {
1053 		return mode.ordinal();
1054 	}
1055 
1056 	/**
1057 	 * Sets the set mode.
1058 	 * 
1059 	 * @param newMode int
1060 	 * @deprecated use
1061 	 *             {@link Slider#setSetMode(com.cosylab.gui.components.Slider.SetMode)}
1062 	 */
1063 	@Deprecated
1064 	public void setMode(int newMode) {
1065 		setSetMode(SliderSetMode.valueOf(newMode));
1066 	}
1067 
1068 	/**
1069 	 * Sets the slider set mode.
1070 	 * 
1071 	 * @param mode
1072 	 * @see SliderSetMode
1073 	 */
1074 	public void setSetMode(SliderSetMode mode) {
1075 		SliderSetMode oldValue = getSetMode();
1076 		this.mode = mode;
1077 		getInfoBar().setSetButtonVisible(mode == SliderSetMode.SET_MANUAL);
1078 		firePropertyChange("setMode",oldValue,this.mode);
1079 	}
1080 
1081 	/**
1082 	 * Returns the set mode.
1083 	 * 
1084 	 * @return
1085 	 */
1086 	public SliderSetMode getSetMode() {
1087 		return mode;
1088 	}
1089 
1090 	/*
1091 	 * (non-Javadoc)
1092 	 * @see com.cosylab.gui.components.range2.TickParameters#measureTick(double,
1093 	 * java.lang.String)
1094 	 */
1095 	public int measureTick(double position, String text) {
1096 		if (text == null) {
1097 			return 0;
1098 		}
1099 
1100 		return getGraphics().getFontMetrics().stringWidth(text);
1101 	}
1102 
1103 	/**
1104 	 * Sets whether this slider is settable. If editable, user can change values,
1105 	 * otherwise UI is disabled.
1106 	 * 
1107 	 * @param settable true/false
1108 	 */
1109 	public void setEditable(boolean settable) {
1110 		this.settable = settable;
1111 		getNavigationBar().setEnabled(settable && isEnabled());
1112 		getInfoBar().setEnabled(settable && isEnabled());
1113 		slider.setEnabled(settable && isEnabled());
1114 	}
1115 
1116 	/**
1117 	 * Returns whether this slider is settable.
1118 	 * 
1119 	 * @return true/false
1120 	 */
1121 	public boolean isEditable() {
1122 		return settable;
1123 	}
1124 
1125 	/*
1126 	 * (non-Javadoc)
1127 	 * @see com.cosylab.gui.components.util.PopupManageable#getPopupManager()
1128 	 */
1129 	public PopupManager getPopupManager() {
1130 		if (popupManager == null) {
1131 			popupManager = new PopupManager(this,false);
1132 			popupManager.addAction(new AbstractAction("Synchronize") {
1133 				private static final long serialVersionUID = 1L;
1134 
1135 				public void actionPerformed(ActionEvent e) {
1136 					synchronize();
1137 				}
1138 			});
1139 			popupManager.addAction(null);
1140 			popupManager.addAction(new AbstractAction("Preferences...") {
1141 				private static final long serialVersionUID = 1L;
1142 
1143 				public void actionPerformed(ActionEvent e) {
1144 					getCustomizer().showDialog();
1145 				}
1146 			});
1147 		}
1148 
1149 		return popupManager;
1150 	}
1151 
1152 	/**
1153 	 * Returns the Customizer instance for this component.
1154 	 * 
1155 	 * @return the Customizer instance for this component.
1156 	 */
1157 	public AbstractCustomizerPanel getCustomizer() {
1158 		if (customizer == null) {
1159 			customizer = AbstractCustomizerPanel.findCustomizer(this);
1160 		}
1161 
1162 		return customizer;
1163 	}
1164 
1165 	/**
1166 	 * Synchronized front thumb value with the shadow. Value is synchronized to the
1167 	 * readback.
1168 	 */
1169 	public void synchronize() {
1170 		synchronized (this) {
1171 			supressPropChEvent = supressSetEvent = true;
1172 			performInternalSet(getReadback());
1173 			supressPropChEvent = supressSetEvent = false;
1174 		}
1175 	}
1176 
1177 	/**
1178 	 * If in {@link SliderSetMode#SET_MANUAL} method will notify listeners that set was
1179 	 * performed. If in any other setMode, the call will have no effect.
1180 	 */
1181 	public void setManual() {
1182 		if (mode == SliderSetMode.SET_MANUAL) {
1183 			double oldValue = getValue();
1184 			double value = getRangedValue().validate(getNavigationBar().getValue());
1185 
1186 			synchronized (this) {
1187 				if (!supressChanges) {
1188 					supressChanges = true;
1189 
1190 					getAutoSyncTimer().stop();
1191 
1192 					getRangedValue().setValue(value);
1193 
1194 					if (!isSuspended()) {
1195 						fireSetPerformed(getValue());
1196 						firePropertyChange("value",oldValue,getValue());
1197 					}
1198 
1199 					if (autoSynchronize) {
1200 						getAutoSyncTimer().restart();
1201 					}
1202 
1203 					supressChanges = false;
1204 				}
1205 			}
1206 		}
1207 	}
1208 
1209 	/*
1210 	 * (non-Javadoc)
1211 	 * @see com.cosylab.application.state.StateOriginator#getState()
1212 	 */
1213 	public State getState() {
1214 		State s = StateFactory.createState();
1215 		s.putDouble("Min",getMinimum());
1216 		s.putDouble("Max",getMaximum());
1217 		s.putString("Format",getFormat());
1218 		s.putString("Units",getUnits());
1219 
1220 		return s;
1221 	}
1222 
1223 	/*
1224 	 * (non-Javadoc)
1225 	 * @see
1226 	 * com.cosylab.application.state.StateOriginator#setState(com.cosylab.application.
1227 	 * state.State)
1228 	 */
1229 	public void setState(State state) {
1230 		setMinimum(state.getDouble("Min",getMinimum()));
1231 		setMaximum(state.getDouble("Max",getMaximum()));
1232 		setFormat(state.getString("Format",getFormat()));
1233 		setUnits(state.getString("Units",getUnits()));
1234 	}
1235 
1236 	/**
1237 	 * Returns whether the user can trigger the component's popup menu through mouse
1238 	 * click.
1239 	 * 
1240 	 * @return true if popup is enabled
1241 	 */
1242 	public boolean isPopupEnabled() {
1243 		return popupEnabled;
1244 	}
1245 
1246 	/**
1247 	 * Enables or disables the popup capabilities of the component.
1248 	 * 
1249 	 * @param b the component should bring up popup dialog on mouse click.
1250 	 */
1251 	public void setPopupEnabled(boolean b) {
1252 		if (b == popupEnabled) return;
1253 		popupEnabled = b;
1254 		if (b) {
1255 			getSlider().addMouseListener(getPopupManager().getMouseHook());
1256 			addMouseListener(getPopupManager().getMouseHook());
1257 		} else {
1258 			getSlider().removeMouseListener(getPopupManager().getMouseHook());
1259 			removeMouseListener(getPopupManager().getMouseHook());
1260 		}
1261 		firePropertyChange("popupEnabled",!b,b);
1262 	}
1263 
1264 	/*
1265 	 * (non-Javadoc)
1266 	 * @see javax.swing.JComponent#getToolTipText()
1267 	 */
1268 	@Override
1269 	public String getToolTipText() {
1270 		StringBuffer sb = new StringBuffer();
1271 		sb.append("setpoint= ");
1272 		sb.append(getNavigationBar().format(getValue()));
1273 		sb.append(" readback= ");
1274 		sb.append(getNavigationBar().format(getReadback()));
1275 
1276 		return sb.toString();
1277 	}
1278 
1279 	/**
1280 	 * Suspends displayer, minimal GUI action is taken, no data value events fired and
1281 	 * slider is disabled. Call <code>resume()</code> as many times as suspend was called.
1282 	 */
1283 	public void suspend() {
1284 		suspended++;
1285 		setEnabled(false);
1286 	}
1287 
1288 	/**
1289 	 * Resumes suspended slider. Call <code>resume()</code> as many times as suspend was
1290 	 * called.
1291 	 * 
1292 	 * @see Slider#suspend();
1293 	 */
1294 	public void resume() {
1295 		if (suspended > 0) {
1296 			suspended--;
1297 		} else {
1298 			setEnabled(true);
1299 		}
1300 	}
1301 
1302 	/**
1303 	 * Returns <code>true</code> if slider is suspended
1304 	 * 
1305 	 * @return <code>true</code> if suspended
1306 	 * @see Slider#suspend()
1307 	 */
1308 	public boolean isSuspended() {
1309 		return suspended > 0;
1310 	}
1311 
1312 	/*
1313 	 * (non-Javadoc)
1314 	 * @see javax.swing.JComponent#setEnabled(boolean)
1315 	 */
1316 	@Override
1317 	public void setEnabled(boolean enabled) {
1318 		super.setEnabled(enabled);
1319 		getSlider().setEnabled(enabled && settable);
1320 		getNavigationBar().setEnabled(enabled && settable);
1321 		getInfoBar().setEnabled(enabled && settable);
1322 	}
1323 
1324 	/**
1325 	 * Returns the state of autoSynchronization feature (enabled / disabled).
1326 	 * 
1327 	 * @return
1328 	 */
1329 	public boolean isAutoSynchronize() {
1330 		return autoSynchronize;
1331 	}
1332 
1333 	/**
1334 	 * Turns the autoSynchronization on/off. If autosynchronization is on slider will
1335 	 * automatically synchronize the value with readback after the specified delay if no
1336 	 * activity was present during that time.
1337 	 * 
1338 	 * @param autoSynchronize
1339 	 * @see #setAutoSynchronizeDelay(long)
1340 	 */
1341 	public void setAutoSynchronize(boolean autoSynchronize) {
1342 		if (isAutoSynchronize() == autoSynchronize) return;
1343 		this.autoSynchronize = autoSynchronize;
1344 		if (autoSynchronize) {
1345 			getAutoSyncTimer().restart();
1346 		} else {
1347 			getAutoSyncTimer().stop();
1348 		}
1349 		firePropertyChange("autoSynchronize",!autoSynchronize,autoSynchronize);
1350 	}
1351 
1352 	/**
1353 	 * Returns the time in milliseconds that specify the delay of the autoSynchronization.
1354 	 * 
1355 	 * @return
1356 	 */
1357 	public long getAutoSynchronizeDelay() {
1358 		return autoSynchronizeDelay;
1359 	}
1360 
1361 	/**
1362 	 * Sets the autoSynchronization in miliseconds.
1363 	 * 
1364 	 * @param autoSynchronizeDelay
1365 	 */
1366 	public void setAutoSynchronizeDelay(long autoSynchronizeDelay) {
1367 		long oldValue = getAutoSynchronizeDelay();
1368 		this.autoSynchronizeDelay = autoSynchronizeDelay;
1369 
1370 		getAutoSyncTimer().setDelay((int)autoSynchronizeDelay);
1371 		getAutoSyncTimer().setInitialDelay((int)autoSynchronizeDelay);
1372 		if (autoSynchronize) {
1373 			getAutoSyncTimer().restart();
1374 		} else {
1375 			getAutoSyncTimer().stop();
1376 		}
1377 		firePropertyChange("autoSynchronizeDelay",oldValue,this.autoSynchronizeDelay);
1378 
1379 	}
1380 
1381 	/**
1382 	 * Modifies slider position. Recalculates position on JSlider's integer scale and
1383 	 * repaints this component.
1384 	 */
1385 	private void modifySliderPosition() {
1386 		getSlider().setValue((int)getRangedValue().getRelativeValue());
1387 		repaint();
1388 	}
1389 
1390 	/**
1391 	 * Enables/disables mouse dragging. Dragging can only be enabled if this component
1392 	 * uses CosyTransferHandler.
1393 	 * 
1394 	 * @param enabled
1395 	 */
1396 	public void setDragEnabled(boolean enabled) {
1397 		if (isDragEnabled() == enabled) return;
1398 		if (getTransferHandler() instanceof CosyTransferHandler) {
1399 			if (((CosyTransferHandler)getTransferHandler()).isExportEnabled() == enabled) return;
1400 
1401 			((CosyTransferHandler)getTransferHandler()).setExportEnabled(enabled,this);
1402 		}
1403 		firePropertyChange("dragEnabled",!enabled,enabled);
1404 	}
1405 
1406 	/**
1407 	 * Returns true if drag is enabled.
1408 	 * 
1409 	 * @return
1410 	 */
1411 	public boolean isDragEnabled() {
1412 		if (getTransferHandler() instanceof CosyTransferHandler) {
1413 			return ((CosyTransferHandler)getTransferHandler()).isExportEnabled();
1414 		}
1415 		return getTransferHandler() != null;
1416 	}
1417 
1418 	/**
1419 	 * Enable/disable the mouse drop. Drop can only be enabled if this component uses
1420 	 * CosyTransferHandler.
1421 	 * 
1422 	 * @param enabled
1423 	 */
1424 	public void setDropEnabled(boolean enabled) {
1425 		if (isDropEnabled() == enabled) return;
1426 		if (getTransferHandler() instanceof CosyTransferHandler) {
1427 			if (((CosyTransferHandler)getTransferHandler()).isReceiveEnabled() == enabled) return;
1428 
1429 			((CosyTransferHandler)getTransferHandler()).setReceiveEnabled(enabled,this);
1430 		}
1431 		firePropertyChange("dropEnabled",!enabled,enabled);
1432 	}
1433 
1434 	/**
1435 	 * Returns true if drop is enabled.
1436 	 * 
1437 	 * @return
1438 	 */
1439 	public boolean isDropEnabled() {
1440 		if (getTransferHandler() instanceof CosyTransferHandler) {
1441 			return ((CosyTransferHandler)getTransferHandler()).isReceiveEnabled();
1442 		}
1443 		return getTransferHandler() != null;
1444 	}
1445 
1446 	/*
1447 	 * (non-Javadoc)
1448 	 * @see javax.swing.JComponent#setBackground(java.awt.Color)
1449 	 */
1450 	@Override
1451 	public void setBackground(Color bg) {
1452 		super.setBackground(bg);
1453 		getNavigationBar().setBackground(bg);
1454 		getSlider().setBackground(bg);
1455 		getInfoBar().setBackground(bg);
1456 	}
1457 
1458 	/*
1459 	 * (non-Javadoc)
1460 	 * @see javax.swing.JComponent#setForeground(java.awt.Color)
1461 	 */
1462 	@Override
1463 	public void setForeground(Color fg) {
1464 		super.setForeground(fg);
1465 		getNavigationBar().setForeground(fg);
1466 		getSlider().setForeground(fg);
1467 		getInfoBar().setForeground(fg);
1468 	}
1469 
1470 	/*
1471 	 * (non-Javadoc)
1472 	 * @see com.cosylab.gui.components.range2.TickParameters#formatNumber(double)
1473 	 */
1474 	public String formatNumber(double value) {
1475 		return this.getFormatter().sprintf(value);
1476 	}
1477 
1478 	/**
1479 	 * Sets a text to a desired button. Text can only be set for navigation buttons and
1480 	 * not for 'SYNC', 'STORE' and 'RESTORE'.
1481 	 * 
1482 	 * @param b <code>Button</code> to which a text we want to set.
1483 	 * @param text <code>String</code> containing a text for a <code>Button</code>.
1484 	 */
1485 	public void setButtonText(Button b, String text) {
1486 		if (b == Button.FAST_INCREMENT || b == Button.FAST_DECREMENT || b == Button.SLOW_INCREMENT || b == Button.SLOW_DECREMENT
1487 				|| b == Button.BIT_INCREMENT || b == Button.BIT_DECREMENT) {
1488 			getNavigationBar().setButtonText(b,text);
1489 		}
1490 	}
1491 
1492 	/**
1493 	 * Sets a Icon to a desired button. Icon can only be set for navigation buttons and
1494 	 * not for 'SYNC', 'STORE' and 'RESTORE'.
1495 	 * 
1496 	 * @param b <code>Button</code> to which a text we want to set.
1497 	 * @param icon <code>Icon</code> containing a text for a <code>Button</code>.
1498 	 */
1499 	public void setButtonIcon(Button b, Icon icon) {
1500 		if (b == Button.FAST_INCREMENT || b == Button.FAST_DECREMENT || b == Button.SLOW_INCREMENT || b == Button.SLOW_DECREMENT
1501 				|| b == Button.BIT_INCREMENT || b == Button.BIT_DECREMENT) {
1502 			getNavigationBar().setButtonIcon(b,icon);
1503 		}
1504 	}
1505 
1506 	/**
1507 	 * Returns the text of a desired button.
1508 	 * 
1509 	 * @param b <code>Button</code> of which a text we want to get.
1510 	 * @return <code>String</code> containing the text for a <code>Button</code> if the
1511 	 *         text is set.
1512 	 */
1513 	public String getButtonText(Button b) {
1514 		if (b == Button.FAST_INCREMENT || b == Button.FAST_DECREMENT || b == Button.SLOW_INCREMENT || b == Button.SLOW_DECREMENT
1515 				|| b == Button.BIT_INCREMENT || b == Button.BIT_DECREMENT) {
1516 			return getNavigationBar().getButtonText(b);
1517 		}
1518 		return null;
1519 	}
1520 
1521 	/**
1522 	 * Returns the Icon of a desired button.
1523 	 * 
1524 	 * @param b <code>Button</code> of which an Icon we want to get.
1525 	 * @return <code>String</code> containing the Icon for a <code>Button</code> if the
1526 	 *         Icon is set.
1527 	 */
1528 	public Icon getButtonIcon(Button b) {
1529 		if (b == Button.FAST_INCREMENT || b == Button.FAST_DECREMENT || b == Button.SLOW_INCREMENT || b == Button.SLOW_DECREMENT
1530 				|| b == Button.BIT_INCREMENT || b == Button.BIT_DECREMENT) {
1531 			return getNavigationBar().getButtonIcon(b);
1532 		}
1533 		return null;
1534 	}
1535 
1536 	/**
1537 	 * Sets a tooltip to a desired button. Tooltips can only be set for navigation buttons
1538 	 * and not for 'SYNC', 'STORE' and 'RESTORE'.
1539 	 * 
1540 	 * @param b <code>Button</code> to which a tooltip we want to set.
1541 	 * @param tooltip <code>String</code> containing a tooltip for a <code>Button</code>.
1542 	 */
1543 	public void setButtonTooltip(Button b, String tooltip) {
1544 		if (b == Button.FAST_INCREMENT || b == Button.FAST_DECREMENT || b == Button.SLOW_INCREMENT || b == Button.SLOW_DECREMENT
1545 				|| b == Button.BIT_INCREMENT || b == Button.BIT_DECREMENT) {
1546 			getNavigationBar().setButtonTooltip(b,tooltip);
1547 		}
1548 	}
1549 
1550 	/**
1551 	 * Returns the tooltip of a desired button.
1552 	 * 
1553 	 * @param b <code>Button</code> of which a tooltip we want to get.
1554 	 * @return <code>String</code> containing a tooltip for a <code>Button</code> if a
1555 	 *         tooltip is set.
1556 	 */
1557 	public String getButtonTooltip(Button b) {
1558 		if (b == Button.FAST_INCREMENT || b == Button.FAST_DECREMENT || b == Button.SLOW_INCREMENT || b == Button.SLOW_DECREMENT
1559 				|| b == Button.BIT_INCREMENT || b == Button.BIT_DECREMENT) {
1560 			return getNavigationBar().getButtonTooltip(b);
1561 		}
1562 		return null;
1563 	}
1564 
1565 	/**
1566 	 * Toggles the visibility of the specified button.
1567 	 * 
1568 	 * @param b button to be set visible/invisible
1569 	 * @param visible true if button should be visible
1570 	 */
1571 	public void setButtonVisible(Button b, boolean visible) {
1572 		if (b == Button.FAST_INCREMENT || b == Button.FAST_DECREMENT || b == Button.SLOW_INCREMENT || b == Button.SLOW_DECREMENT
1573 				|| b == Button.BIT_INCREMENT || b == Button.BIT_DECREMENT) {
1574 			getNavigationBar().setButtonVisible(b,visible);
1575 		}
1576 	}
1577 
1578 	/**
1579 	 * Returns the visibility of a button.
1580 	 * 
1581 	 * @param b button
1582 	 * @return true if button b is visible
1583 	 */
1584 	public boolean isButtonVisible(Button b) {
1585 		if (b == Button.FAST_INCREMENT || b == Button.FAST_DECREMENT || b == Button.SLOW_INCREMENT || b == Button.SLOW_DECREMENT
1586 				|| b == Button.BIT_INCREMENT || b == Button.BIT_DECREMENT) {
1587 			return getNavigationBar().isButtonVisible(b);
1588 		}
1589 		return false;
1590 	}
1591 
1592 	/**
1593 	 * Sets the store and restore buttons visibile/invisible.
1594 	 * 
1595 	 * @param visible visiblity of storage buttons
1596 	 */
1597 	public void setStorageButtonsVisible(boolean visible) {
1598 		if (isStorageButtonsVisible() == visible) return;
1599 		getInfoBar().setStorageButtonsVisible(visible);
1600 		firePropertyChange("storageButtonsVisible",!visible,visible);
1601 	}
1602 
1603 	/**
1604 	 * Returns true if the stored value label is set to be visible. Label can be actually
1605 	 * shown only when storage buttons are visible.
1606 	 * 
1607 	 * @param visible visiblity of stored value label
1608 	 * @see Slider#isStorageButtonsVisible()
1609 	 */
1610 	public boolean isStoredValueLabelVisible() {
1611 		return getInfoBar().isStoredValueLabelVisible();
1612 	}
1613 
1614 	/**
1615 	 * Sets the stored value label to be visible. Label can be only wisible, when storage
1616 	 * buttons are visible.
1617 	 * 
1618 	 * @param visible visiblity of storage buttons
1619 	 * @see Slider#setStorageButtonsVisible(boolean)
1620 	 */
1621 	public void setStoredValueLabelVisible(boolean visible) {
1622 		if (isStoredValueLabelVisible() == visible) return;
1623 		getInfoBar().setStoredValueLabelVisible(visible);
1624 		firePropertyChange("storedValueLabelVisible",!visible,visible);
1625 	}
1626 
1627 	/**
1628 	 * Returns the visibility flag of the store and restore buttons.
1629 	 * 
1630 	 * @return true if buttons are visible
1631 	 */
1632 	public boolean isStorageButtonsVisible() {
1633 		return getInfoBar().isStorageButtonsVisible();
1634 	}
1635 
1636 	/**
1637 	 * Sets the sync button visibile/invisible.
1638 	 * 
1639 	 * @param visible visiblity of storage buttons
1640 	 */
1641 	public void setSyncButtonVisible(boolean visible) {
1642 		if (isSyncButtonVisible() == visible) return;
1643 		getInfoBar().setSyncButtonVisible(visible);
1644 		getSliderUI().setPaintTrailer(visible);
1645 		firePropertyChange("syncButtonVisible",!visible,visible);
1646 	}
1647 
1648 	/**
1649 	 * Returns the visibility flag of the sync button.
1650 	 * 
1651 	 * @return true if button is visible
1652 	 */
1653 	public boolean isSyncButtonVisible() {
1654 		return getInfoBar().isSyncButtonVisible();
1655 	}
1656 
1657 	/**
1658 	 * Sets the tolerance at which the thumb is considered to be synchronized with the
1659 	 * trailer.
1660 	 * 
1661 	 * @param precision
1662 	 * @see CosySliderUI#setTolerance(double)
1663 	 */
1664 	public void setValuePrecision(double precision) {
1665 		getSliderUI().setTolerance(precision);
1666 	}
1667 
1668 	/**
1669 	 * Returns the tolerance for current value set via slider.
1670 	 * 
1671 	 * @return precision <code>Double</code> precision value
1672 	 * @see CosySliderUI#getTolerance()
1673 	 */
1674 	public double getValuePrecision() {
1675 		return getSliderUI().getTolerance();
1676 	}
1677 
1678 	/**
1679 	 * Sets the font used to display the title of the slider.
1680 	 * 
1681 	 * @param font new font
1682 	 */
1683 	public void setTitleFont(Font font) {
1684 		Font oldFont = getTitleFont();
1685 		getInfoBar().setFont(font);
1686 		firePropertyChange("titleFont",oldFont,font);
1687 	}
1688 
1689 	/**
1690 	 * Returns the font used to display the title.
1691 	 * 
1692 	 * @return
1693 	 */
1694 	public Font getTitleFont() {
1695 		return getInfoBar().getFont();
1696 	}
1697 
1698 	/**
1699 	 * Sets the font used to display the units.
1700 	 * 
1701 	 * @param font new font
1702 	 */
1703 	public void setUnitsFont(Font font) {
1704 		Font oldFont = getUnitsFont();
1705 		getNavigationBar().setUnitsFont(font);
1706 		firePropertyChange("unitsFont",oldFont,font);
1707 	}
1708 
1709 	/**
1710 	 * Returns the font used to display units.
1711 	 * 
1712 	 * @return
1713 	 */
1714 	public Font getUnitsFont() {
1715 		return getNavigationBar().getFont();
1716 	}
1717 
1718 	/**
1719 	 * Returns the values which are painted together with majot ticks.
1720 	 * 
1721 	 * @return
1722 	 */
1723 	public double[] getMajorTicks() {
1724 		return manualTickProvider.getMajorTicks();
1725 	}
1726 
1727 	/**
1728 	 * Returns the values which have labeles painted next to the tick. Only major ticks
1729 	 * can have labels.
1730 	 * 
1731 	 * @return
1732 	 */
1733 	public double[] getMajorTickLabels() {
1734 		return manualTickProvider.getLabels();
1735 	}
1736 
1737 	/**
1738 	 * Returns the distance between two minor ticks in same units as value.
1739 	 * 
1740 	 * @return
1741 	 */
1742 	public double getMinorTicksStep() {
1743 		return manualTickProvider.getMinor();
1744 	}
1745 
1746 	/**
1747 	 * Explicitely sets which values on the slider scale should be painted with major
1748 	 * ticks.
1749 	 * 
1750 	 * @param ticks
1751 	 */
1752 	public void setMajorTicks(double[] ticks) {
1753 		double[] oldValue = getMajorTicks();
1754 		manualTickProvider.setMajorTicks(ticks);
1755 		if (ticks != null) {
1756 			getRangedValue().setTickCalculator(manualTickProvider);
1757 		} else {
1758 			getRangedValue().setTickCalculator(null);
1759 		}
1760 		repaint();
1761 		firePropertyChange("majorTicks",oldValue,ticks);
1762 	}
1763 
1764 	/**
1765 	 * Sets the distance between two minor ticks in same units as value.
1766 	 * 
1767 	 * @param ticks
1768 	 */
1769 	public void setMinorTicksStep(double step) {
1770 		double oldValue = getMinorTicksStep();
1771 		if (oldValue == step) return;
1772 		manualTickProvider.setMinor(step);
1773 		firePropertyChange("minorTicksNumber",oldValue,step);
1774 		if (getMajorTicks() != null) repaint();
1775 	}
1776 
1777 	/**
1778 	 * Explicitely sets which major ticks will have value labels painted. If any of the
1779 	 * labels in this array is not a major tick, that value will be ignored.
1780 	 * 
1781 	 * @param labelledTicks
1782 	 */
1783 	public void setMajorTickLabels(double[] labelledTicks) {
1784 		double[] oldValue = getMajorTickLabels();
1785 		manualTickProvider.setLabels(labelledTicks);
1786 		firePropertyChange("majorTickLabels",oldValue,labelledTicks);
1787 		if (getMajorTicks() != null) repaint();
1788 	}
1789 
1790 	/**
1791 	 * Returns the stored value. If no value was stored Double.NaN is returned.
1792 	 * 
1793 	 * @return stored value
1794 	 */
1795 	public double getStoredValue() {
1796 		return storedValue;
1797 	}
1798 
1799 	/**
1800 	 * Stores new value. The stored value can be restored at any later time provided that
1801 	 * is has not been override by a different value.
1802 	 * 
1803 	 * @param storedValue new stored value
1804 	 * @see Slider#restoreValue();
1805 	 */
1806 	public void setStoredValue(double storedValue) {
1807 		if (this.storedValue == storedValue) return;
1808 		double oldValue = this.storedValue;
1809 		this.storedValue = storedValue;
1810 		if (Double.isNaN(storedValue)) {
1811 			getInfoBar().setStoredValue(null);
1812 		} else {
1813 			getInfoBar().setStoredValue(getNavigationBar().format(storedValue));
1814 		}
1815 		firePropertyChange("storedValue",oldValue,storedValue);
1816 	}
1817 
1818 	/**
1819 	 * Stores the current setpoint value.
1820 	 */
1821 	public void storeValue() {
1822 		setStoredValue(getValue());
1823 	}
1824 
1825 	/**
1826 	 * Restores the stored value. Value is set to the slider. This method has the same
1827 	 * effect as {@link #setValue(double)} where the parameter is
1828 	 * {@link #getStoredValue()}.
1829 	 */
1830 	public void restoreValue() {
1831 		if (Double.isNaN(getStoredValue())) return;
1832 		performInternalSet(getStoredValue());
1833 	}
1834 
1835 	/**
1836 	 * Sets the title of the restore button.
1837 	 * 
1838 	 * @param title new title
1839 	 */
1840 	public void setRestoreButtonTitle(String title) {
1841 		String oldValue = getRestoreButtonTitle();
1842 		if (oldValue != null && oldValue.equals(title)) return;
1843 		getInfoBar().setRestoreButtonTitle(title);
1844 		firePropertyChange("restoreButtonTitle",oldValue,title);
1845 	}
1846 
1847 	/**
1848 	 * Returns the title of the restore button.
1849 	 * 
1850 	 * @return the title
1851 	 */
1852 	public String getRestoreButtonTitle() {
1853 		return getInfoBar().getRestoreButtonTitle();
1854 	}
1855 
1856 	/**
1857 	 * Returns the initial delay time after button press to first auto increment/decrement
1858 	 * 
1859 	 * @return the continuousModeDelayTime
1860 	 */
1861 	public int getContinuousModeDelayTime() {
1862 		return getNavigationBar().getContinuousModeDelayTime();
1863 	}
1864 
1865 	/**
1866 	 * Sets the initial delay time in milliseconds betwean button pressed and hold for
1867 	 * increment/decrement and continuous mode is fired.
1868 	 * 
1869 	 * @param the continuousModeDelayTime in milliseconds
1870 	 */
1871 	public void setContinuousModeDelayTime(int continuousModeDelayTime) {
1872 		if (getContinuousModeDelayTime() == continuousModeDelayTime) return;
1873 		int old = getContinuousModeDelayTime();
1874 		getNavigationBar().setContinuousModeDelayTime(continuousModeDelayTime);
1875 		firePropertyChange("continuousModeDelayTime",old,continuousModeDelayTime);
1876 	}
1877 
1878 	/**
1879 	 * Returns continuousModeEnabled
1880 	 * 
1881 	 * @return the continuousModeDelayTime
1882 	 */
1883 	public boolean isContinuousModeEnabled() {
1884 		return continuousModeEnabled;
1885 	}
1886 
1887 	/**
1888 	 * Sets the continuousModeEnabled and updates combobox on InfoBar to appropriate index
1889 	 * 
1890 	 * @param the continuousModeEnabled
1891 	 */
1892 	public void setContinuousModeEnabled(boolean continuousModeEnabled) {
1893 		if (this.continuousModeEnabled == continuousModeEnabled) return;
1894 		this.continuousModeEnabled = continuousModeEnabled;
1895 		this.getInfoBar().setContinuousMode(continuousModeEnabled);
1896 		firePropertyChange("continuousModeEnabled",!continuousModeEnabled,continuousModeEnabled);
1897 	}
1898 
1899 	/**
1900 	 * Returns the continuousModeEnabled
1901 	 * 
1902 	 * @return the continuousModeEnabled
1903 	 */
1904 	public int getContinuousModeRepeatTime() {
1905 		return getNavigationBar().getContinuousModeRepeatTime();
1906 	}
1907 
1908 	/**
1909 	 * Sets the continuousModeRepeatTime time in milliseconds between individual
1910 	 * repetitions of contunuous increment/decrement.
1911 	 * 
1912 	 * @param the continuousModeRepeatTime in milliseconds
1913 	 */
1914 	public void setContinuousModeRepeatTime(int continuousModeRepeatTime) {
1915 		if (getContinuousModeRepeatTime() == continuousModeRepeatTime) return;
1916 		int old = getContinuousModeRepeatTime();
1917 		getNavigationBar().setContinuousModeRepeatTime(continuousModeRepeatTime);
1918 		firePropertyChange("continuousModeRepeatTime",old,continuousModeRepeatTime);
1919 	}
1920 
1921 	/**
1922 	 * Sets visibility of control ofr continuous mode.
1923 	 * 
1924 	 * @param the continuousControlVisible
1925 	 */
1926 	public void setContinuousControlVisible(boolean continuousControlVisible) {
1927 		if (this.continuousControlVisible == continuousControlVisible) return;
1928 		this.continuousControlVisible = continuousControlVisible;
1929 		this.getInfoBar().setContinuousModeVisible(continuousControlVisible);
1930 		firePropertyChange("continuousControlVisible",!continuousControlVisible,continuousControlVisible);
1931 	}
1932 	
1933 	/**
1934 	 * Returns <code>true</code> if contunyous mode checkbox is visible.
1935 	 * 
1936 	 * @return the isShowContinuousMode
1937 	 */
1938 	public boolean isContinuousControlVisible() {
1939 		return continuousControlVisible;
1940 	}
1941 
1942 	
1943 	/*
1944 	 * (non-Javadoc)
1945 	 * @see javax.swing.JComponent#setToolTipText(java.lang.String)
1946 	 */
1947 	@Override
1948 	public void setToolTipText(String tooltip) {
1949 		super.setToolTipText(tooltip);
1950 
1951 		this.slider.setToolTipText(tooltip);
1952 		this.getInfoBar().setToolTipText(tooltip);
1953 		this.getNavigationBar().setToolTipText(tooltip);
1954 	}
1955 
1956 	/**
1957 	 * Sets the snap to ticks property. If true the slider thumb can be moved on discrete
1958 	 * values defined by the slider ticks.
1959 	 * 
1960 	 * @param snapToTicks true if thumb should snap to ticks
1961 	 */
1962 	public void setSnapToTicks(boolean snapToTicks) {
1963 		if (this.snapToTicks == snapToTicks) return;
1964 		this.snapToTicks = snapToTicks;
1965 		firePropertyChange("snapToTicks",!snapToTicks, snapToTicks);
1966 		getRangedValue().setSnapToTicks(snapToTicks);
1967 	}
1968 	
1969 	/**
1970 	 * Returns true if snap to ticks is turned on.
1971 	 * 
1972 	 * @see #setSnapToTicks(boolean)
1973 	 * @return true if snap is turned on
1974 	 */
1975 	public boolean isSnapToTicks() {
1976 		return snapToTicks;
1977 	}
1978 	
1979 	
1980 	/**
1981 	 * Run test applet.
1982 	 * 
1983 	 * @param args command line parameters
1984 	 */
1985 	public static void main(String[] args) {
1986 		Slider sliderAmplitude = new Slider();
1987 		sliderAmplitude.setPopupEnabled(true);
1988 		sliderAmplitude.setTitle("Some title");
1989 		sliderAmplitude.setToolTipText("Some tool tip text");
1990 		sliderAmplitude.setUnits("Some unit");
1991 		sliderAmplitude.setUnitsVisible(true);
1992 		sliderAmplitude.setStorageButtonsVisible(true);
1993 		sliderAmplitude.setStoredValue(0.0f);
1994 		sliderAmplitude.setRestoreButtonTitle("OLD");
1995 		sliderAmplitude.setSyncButtonVisible(false);
1996 		sliderAmplitude.setAutoSynchronize(false);
1997 		sliderAmplitude.setFormat("%3.2f");
1998 		sliderAmplitude.setTicksFormat("%3.4f");
1999 //		sliderAmplitude.setMajorTicks(new double[] {0.0, 10.0, 20.0, 30.0, 40.0,
2000 //		50.0, 60.0, 70.0, 80.0, 90.0, 100.0});
2001 //		sliderAmplitude.setMajorTickLabels(new double[] {0.0, 10.0, 20.0, 30.0,
2002 //		40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0});
2003 		sliderAmplitude.setMinimum(0.0);
2004 		sliderAmplitude.setMaximum(100.0);
2005 		RunnerHelper.runComponent(sliderAmplitude, 400,400);
2006 	}
2007 
2008 }
2009 
2010 /* __oOo__ */