View Javadoc

1   package com.cosylab.gui;
2   
3   import java.awt.Component;
4   import java.awt.event.ActionEvent;
5   import java.awt.event.ActionListener;
6   import java.awt.event.MouseAdapter;
7   import java.awt.event.MouseEvent;
8   import java.beans.Beans;
9   import java.beans.PropertyVetoException;
10  import java.util.Map;
11  
12  import javax.swing.AbstractAction;
13  import javax.swing.JCheckBox;
14  import javax.swing.JDialog;
15  
16  import com.cosylab.gui.adapters.Converter;
17  import com.cosylab.gui.components.customizer.AbstractCustomizerPanel;
18  import com.cosylab.gui.components.util.PopupManager;
19  import com.cosylab.gui.displayers.ConvertibleDisplayer;
20  import com.cosylab.gui.displayers.DataConsumer;
21  import com.cosylab.gui.displayers.DataSource;
22  import com.cosylab.gui.displayers.DataSourceSupport;
23  import com.cosylab.gui.displayers.DataState;
24  import com.cosylab.gui.displayers.DisplayerUtilities;
25  import com.cosylab.gui.displayers.DoubleConsumer;
26  import com.cosylab.gui.displayers.DoubleConsumerMulticaster;
27  import com.cosylab.gui.displayers.DoubleDisplayer;
28  import com.cosylab.gui.displayers.NonblockingNumberConsumer;
29  import com.cosylab.util.CommonException;
30  
31  /**
32   * CheckBox for displaying and switching double properties between two values.
33   * To set the values, see methods {@link #setSelectedValue(Number)} and
34   * {@link #setDeselectedValue(Number)}.
35   * 
36   * @author <a href="mailto:blaz.hostnik@cosylab.com">Blaz Hostnik, Cosylab</a>
37   */
38  public class CheckBoxController extends JCheckBox implements DoubleDisplayer, DataSource, ConvertibleDisplayer {
39  	
40  	private static final long serialVersionUID = -84774768633096095L;
41  	/** This value is going to be set to consumers when CheckBox is selected. */
42  	public static final String SELECTED_VALUE = "selectedValue";
43  	/** This value is going to be set to consumers when CheckBox is deselected. */
44  	public static final String DESELECTED_VALUE = "deselectedValue";
45  	
46  	protected static final String DEFAULT_TEXT = "Enable";
47  	
48  	private String title;
49  	private DataSource dataSource;
50  	private DataState dataState = new DataState(DataState.UNDEFINED);
51  	private int suspendCount = 0;
52  	private PopupManager popupManager;
53  	private InfoDialog infoDialog;
54  	private AbstractCustomizerPanel customizer;	
55  	@SuppressWarnings("unchecked")
56  	private DataSourceSupport support = new DataSourceSupport(new Class[]{
57  		    NonblockingNumberConsumer.class
58  	    });
59  	private Converter converter;
60  
61  	private Number selectedValue = 1;
62  
63  	private Number deselectedValue = 0;	
64  
65  	private boolean popupEnabled = false;
66  
67  	private double minimum;
68  
69  	private double maximum;
70  
71  	private String units;
72  
73  	private double value;
74  
75  	private String format;
76  	
77  	/*
78  	 * Used for responding to selections and deselections.
79  	 */
80  	private class CheckBoxListener implements ActionListener {
81  		
82  		public void actionPerformed(ActionEvent e) {
83  			DataConsumer[] d = getConsumers();
84  			boolean sel = isSelected();
85  
86  			for (int i = 0; i < d.length; i++) {
87  				NonblockingNumberConsumer dd = (NonblockingNumberConsumer)d[i];
88  
89  				if (dd != null) {
90  					if (sel) dd.updateNonblocking(selectedValue);
91  					else dd.updateNonblocking(deselectedValue);
92  				}
93  			}
94  
95  		}
96  		
97  	}
98  
99  	
100 	/**
101 	 * Default constructor: with {@link #DEFAULT_TEXT} text in front of the check box.
102 	 */
103 	public CheckBoxController() {
104 		this(DEFAULT_TEXT);
105 	}
106 	
107 	/**
108 	 * Constructor with parameters.
109 	 * 
110 	 * @param text in front of the check box
111 	 */
112 	public CheckBoxController(String text) {
113 		setText(text);
114 		initialize();
115 	}
116 	
117 	protected void initialize(){
118 		this.addActionListener(new CheckBoxListener());
119 		
120 		addMouseMotionListener(new MouseAdapter() {			
121 			public void mouseDragged(MouseEvent e) {
122 				//this is necessary for check box to be released
123 				processMouseEvent(new MouseEvent((Component)e.getSource(), MouseEvent.MOUSE_EXITED, e.getWhen(),
124 						e.getModifiers(), e.getX(), e.getY(), e.getClickCount(), false));
125 			}
126 		});
127 		
128 		setPopupEnabled(true);
129 	}
130 	
131 	/**
132 	 * Set the value that will be set on consumers, when the CheckBox will be selected.
133 	 */
134 	public void setSelectedValue(Number value){
135 		if (selectedValue == value) return;
136 		
137 		Number old = selectedValue;
138 		selectedValue = value;
139 		
140 		firePropertyChange(SELECTED_VALUE,old,selectedValue);		
141 	}
142 
143 	/**
144 	 * Get the value that will be set on consumers, when the CheckBox will be selected.
145 	 */
146 	public Number getSelectedValue(){
147 		return selectedValue;
148 	}
149 	
150 	/**
151 	 * Set the value that will be set on consumers, when the CheckBox will be deselected.
152 	 */
153 	public void setDeselectedValue(Number value){
154 		if (deselectedValue == value) return;
155 		
156 		Number old = deselectedValue;
157 		deselectedValue = value;
158 		
159 		firePropertyChange(DESELECTED_VALUE,old,deselectedValue);		
160 	}
161 
162 	/**
163 	 * Get the value that will be set on consumers, when the CheckBox will be deselected.
164 	 */
165 	public Number getDeselectedValue(){
166 		return deselectedValue;
167 	}
168 	
169 	/*
170 	 * (non-Javadoc)
171 	 * @see com.cosylab.gui.displayers.DoubleDisplayer#getFormat()
172 	 */
173 	public String getFormat() {
174 		return format;
175 	}
176 
177 	/*
178 	 * (non-Javadoc)
179 	 * @see com.cosylab.gui.displayers.DoubleDisplayer#getMaximum()
180 	 */
181 	public double getMaximum() {
182 		return maximum;
183 	}
184 
185 	/*
186 	 * (non-Javadoc)
187 	 * @see com.cosylab.gui.displayers.DoubleDisplayer#getMinimum()
188 	 */
189 	public double getMinimum() {
190 		return minimum;
191 	}
192 
193 	/*
194 	 * (non-Javadoc)
195 	 * @see com.cosylab.gui.displayers.DoubleDisplayer#getUnits()
196 	 */
197 	public String getUnits() {
198 		return units;
199 	}
200 
201 	/*
202 	 * (non-Javadoc)
203 	 * @see com.cosylab.gui.displayers.DoubleDisplayer#getValue()
204 	 */
205 	public double getValue() {
206 		return value;
207 	}
208 
209 	/*
210 	 * (non-Javadoc)
211 	 * @see com.cosylab.gui.displayers.DoubleDisplayer#setFormat(java.lang.String)
212 	 */
213 	public void setFormat(String value) {
214 		if (format == null) {
215 			if (value == null) return;
216 		} else if (format.equals(value)) return;		
217 
218 		String oldValue = format;
219 		format = value;
220 
221 		firePropertyChange("format", oldValue, format);
222 	}
223 
224 	/*
225 	 * (non-Javadoc)
226 	 * @see com.cosylab.gui.displayers.DoubleDisplayer#setMaximum(double)
227 	 */
228 	public void setMaximum(double value) {
229 		if (value == maximum) return;
230 
231 		double oldValue = maximum;
232 		maximum = value;
233 
234 		firePropertyChange("maximum", oldValue, maximum);
235 	}
236 
237 	/*
238 	 * (non-Javadoc)
239 	 * @see com.cosylab.gui.displayers.DoubleDisplayer#setMinimum(double)
240 	 */
241 	public void setMinimum(double value) {
242 		if (value == minimum) return;
243 
244 		double oldValue = minimum;
245 		minimum = value;
246 
247 		firePropertyChange("minimum", oldValue, minimum);
248 	}
249 
250 	/*
251 	 * (non-Javadoc)
252 	 * @see com.cosylab.gui.displayers.DoubleDisplayer#setUnits(java.lang.String)
253 	 */
254 	public void setUnits(String value) {
255 		if (units == null) {
256 			if (value == null) return;
257 		} else if (units.equals(value)) return;		
258 
259 		String oldValue = units;
260 		units = value;
261 
262 		firePropertyChange("units", oldValue, units);
263 	}
264 
265 	/*
266 	 * (non-Javadoc)
267 	 * @see com.cosylab.gui.displayers.DoubleDisplayer#setValue(double)
268 	 */
269 	public void setValue(double val) {
270 		if (val == value) return;
271 
272 		double oldValue = value;
273 		value = val;
274 
275 		firePropertyChange("value", oldValue, value);
276 	}
277 
278 	/*
279 	 * (non-Javadoc)
280 	 * @see com.cosylab.gui.displayers.Displayer#getDataSource()
281 	 */
282 	public DataSource getDataSource() {
283 		return dataSource;
284 	}
285 
286 	/*
287 	 * (non-Javadoc)
288 	 * @see com.cosylab.gui.displayers.Displayer#getTitle()
289 	 */
290 	public String getTitle() {
291 		return title;
292 	}
293 
294 	/*
295 	 * (non-Javadoc)
296 	 * @see com.cosylab.gui.displayers.Displayer#isEditable()
297 	 */
298 	public boolean isEditable() {
299 		return true;
300 	}
301 
302 	/*
303 	 * (non-Javadoc)
304 	 * @see com.cosylab.gui.displayers.Displayer#setDataSource(com.cosylab.gui.displayers.DataSource)
305 	 */
306 	public void setDataSource(DataSource dataSource)
307 			throws PropertyVetoException {
308 		DisplayerUtilities.prepareNewDataSource(dataSource,this);
309 		
310 		DataSource old= this.dataSource;
311 		this.dataSource = dataSource;
312 		
313 		firePropertyChange(DATA_SOURCE,old,dataSource);
314 	}
315 
316 	protected void internalSetTitle()
317 	{
318 		String value = title;
319 
320 		if (value == null && Beans.isDesignTime()) {
321 			value = "<title>";
322 		}
323 	}
324 	
325 	/*
326 	 * (non-Javadoc)
327 	 * @see com.cosylab.gui.displayers.Displayer#setTitle(java.lang.String)
328 	 */
329 	public void setTitle(String value) {
330 		String oldVal = title;
331 		title = value;
332 		internalSetTitle();
333 		firePropertyChange("title", oldVal, value);
334 	}
335 
336 	/*
337 	 * (non-Javadoc)
338 	 * @see com.cosylab.gui.displayers.DataConsumer#getDataConsumer(java.lang.Class)
339 	 */
340 	@SuppressWarnings("unchecked")
341 	public DataConsumer getDataConsumer(Class type) {
342 		if (type == DoubleConsumer.class) {
343 			return this;
344 		}
345 
346 		return DoubleConsumerMulticaster.createDataConsumer(type, this);
347 	}
348 
349 	/*
350 	 * (non-Javadoc)
351 	 * @see com.cosylab.gui.displayers.DataConsumer#getDefaultDataConsumer()
352 	 */
353 	public DataConsumer getDefaultDataConsumer() {
354 		return this;
355 	}
356 
357 	/*
358 	 * (non-Javadoc)
359 	 * @see com.cosylab.gui.displayers.DataConsumer#getSupportedCharacteristics()
360 	 */
361 	public String[] getSupportedCharacteristics() {
362 		return DisplayerUtilities.COMMON_NUMERIC_DISPLAYER_CHARACTERISTICS;
363 	}
364 
365 	/*
366 	 * (non-Javadoc)
367 	 * @see com.cosylab.gui.displayers.DataConsumer#getSupportedConsumerTypes()
368 	 */
369 	@SuppressWarnings("unchecked")
370 	public Class<DataConsumer>[] getSupportedConsumerTypes() {
371 		return DoubleConsumerMulticaster.PREFERED_CONSUMER_TYPES;
372 	}
373 
374 	/*
375 	 * (non-Javadoc)
376 	 * @see com.cosylab.gui.displayers.DataConsumer#setCharacteristics(java.util.Map)
377 	 */
378 	@SuppressWarnings("unchecked")
379 	public void setCharacteristics(Map characteristics) {
380 		if (characteristics == null) {
381 			throw new NullPointerException("characteristics");
382 		}
383 
384 		DisplayerUtilities.setCharacteristics(characteristics, this);
385 	}
386 
387 	/*
388 	 * (non-Javadoc)
389 	 * @see com.cosylab.gui.displayers.DataConsumer#updateDataState(com.cosylab.gui.displayers.DataState)
390 	 */
391 	public void updateDataState(DataState state) {
392 		DataState old = dataState;
393 		dataState = state;
394 		firePropertyChange(DATA_STATE, old, dataState);
395 	}
396 
397 	private void internalCleanup() {
398 		setText(DEFAULT_TEXT);
399 		setTitle(null);
400 		setSelected(false);
401 	}
402 
403 	/*
404 	 * (non-Javadoc)
405 	 * @see com.cosylab.gui.displayers.CommonDisplayer#cleanup()
406 	 */
407 	public void cleanup() {
408 		internalCleanup();
409 		updateDataState(new DataState(DataState.NOT_INITIALIZED));
410 	}
411 
412 	/*
413 	 * (non-Javadoc)
414 	 * @see com.cosylab.gui.displayers.CommonDisplayer#isSuspended()
415 	 */
416 	public boolean isSuspended() {
417 		return suspendCount > 0;
418 	}
419 
420 	/*
421 	 * (non-Javadoc)
422 	 * @see com.cosylab.gui.displayers.CommonDisplayer#resume()
423 	 */
424 	public void resume() {
425 		if (suspendCount > 0) {
426 			suspendCount--;
427 		}
428 
429 		if (suspendCount == 0) {
430 			setEnabled(true);
431 		}
432 	}
433 
434 	/*
435 	 * (non-Javadoc)
436 	 * @see com.cosylab.gui.displayers.CommonDisplayer#suspend()
437 	 */
438 	public void suspend() {
439 		setEnabled(false);
440 		suspendCount++;
441 	}
442 
443 	/*
444 	 * (non-Javadoc)
445 	 * @see com.cosylab.gui.components.util.PopupManageable#getPopupManager()
446 	 */
447 	public PopupManager getPopupManager() {
448 		if (popupManager == null) {
449 			popupManager = new PopupManager(this, false);
450 			popupManager.addAction(new AbstractAction("Preferences...") {
451 				private static final long serialVersionUID = 1L;
452 					public void actionPerformed(ActionEvent e)
453 					{
454 						getCustomizer().showDialog();
455 					}
456 				});
457 			
458 			popupManager.addAction(new AbstractAction("Info...") {
459 				private static final long serialVersionUID = 1L;
460 
461 					public void actionPerformed(ActionEvent e)
462 					{
463 						getInfoDialog().setVisible(true);
464 					}
465 				});
466 		}
467 		
468 		return popupManager;
469 	}
470 	
471 	/**
472 	 * Return true if the popup menu is enabled or false otherwise.
473 	 * 
474 	 * @return true if popup is enabled
475 	 */
476 	public boolean isPopupEnabled() {
477 		return popupEnabled;
478 	}
479 	
480 	/**
481 	 * Enables or disables the popup menu.
482 	 * 
483 	 * @param enabled true if enable or false if disableds
484 	 */
485 	public void setPopupEnabled(boolean enabled) {
486 		if (popupEnabled == enabled) return;
487 		popupEnabled = enabled;
488 		if (enabled) {
489 			addMouseListener(getPopupManager().getMouseHook());
490 		} else {
491 			removeMouseListener(getPopupManager().getMouseHook());
492 		}		
493 		firePropertyChange("popupEnabled",!popupEnabled,popupEnabled);
494 	}	
495 
496 	protected JDialog getInfoDialog() {
497 		if (infoDialog == null) {
498 			infoDialog = new InfoDialog(this);
499 		}
500 		return infoDialog;
501 	}
502 
503 	protected AbstractCustomizerPanel getCustomizer() {
504 		if (customizer == null) {
505 			customizer = AbstractCustomizerPanel.findCustomizer(this);
506 		}
507 		return customizer;
508 	}
509 
510 	/*
511 	 * (non-Javadoc)
512 	 * @see com.cosylab.gui.displayers.DataStateProvider#getDataState()
513 	 */
514 	public DataState getDataState() {
515 		return dataState;
516 	}
517 
518 	/*
519 	 * (non-Javadoc)
520 	 * @see com.cosylab.gui.displayers.DoubleConsumer#updateValue(long, double)
521 	 */
522 	public void updateValue(long timestamp, double value)
523 			throws CommonException {
524 		if (value == selectedValue.doubleValue()) {
525 			setSelected(true);
526 			updateDataState(new DataState(DataState.NORMAL));
527 		}
528 		else if (value == deselectedValue.doubleValue()){
529 			setSelected(false);
530 			updateDataState(new DataState(DataState.NORMAL));
531 		}
532 		else {
533 			updateDataState(new DataState(DataState.WARNING));
534 		}
535 	}
536 
537 	/**
538 	 * Accepts only consumers, which support
539 	 * <code>NonblockingNumberConsumer</code> which is used for receiving
540 	 * updates from user.
541 	 *
542 	 * @see com.cosylab.gui.displayers.DataSource#addConsumer(com.cosylab.gui.displayers.DataConsumer)
543 	 */
544 	public void addConsumer(DataConsumer consumer) throws PropertyVetoException {
545 		if (consumer == null) {
546 			throw new NullPointerException("consumer");
547 		}
548 
549 		NonblockingNumberConsumer c = (NonblockingNumberConsumer)consumer
550 			.getDataConsumer(NonblockingNumberConsumer.class);
551 
552 		if (c == null) {
553 			throw new PropertyVetoException("Consumer '" + consumer
554 			    + "' must support NonblockingNumberConsumer.", null);
555 		}
556 
557 		support.addConsumer(c);
558 	}
559 
560 	/*
561 	 * (non-Javadoc)
562 	 * @see com.cosylab.gui.displayers.DataSource#getAcceptableConsumerTypes()
563 	 */
564 	public Class<DataConsumer>[] getAcceptableConsumerTypes() {
565 		return support.getAcceptableConsumerTypes();
566 	}
567 
568 	/*
569 	 * (non-Javadoc)
570 	 * @see com.cosylab.gui.displayers.DataSource#getConsumers()
571 	 */
572 	public DataConsumer[] getConsumers() {
573 		return support.getConsumers();
574 	}
575 
576 	/*
577 	 * (non-Javadoc)
578 	 * @see com.cosylab.gui.displayers.DataSource#removeAllConsumers()
579 	 */
580 	public void removeAllConsumers() {
581 		support.removeAllConsumers();
582 	}
583 
584 	/*
585 	 * (non-Javadoc)
586 	 * @see com.cosylab.gui.displayers.DataSource#removeConsumer(com.cosylab.gui.displayers.DataConsumer)
587 	 */
588 	public void removeConsumer(DataConsumer consumer) {
589 		support.removeConsumer(consumer);
590 	}
591 
592 	/*
593 	 * (non-Javadoc)
594 	 * @see com.cosylab.gui.displayers.ConvertibleDisplayer#getConverter()
595 	 */
596 	public Converter getConverter() {
597 		return converter;
598 	}
599 
600 	/*
601 	 * (non-Javadoc)
602 	 * @see com.cosylab.gui.displayers.ConvertibleDisplayer#setConverter(com.cosylab.gui.adapters.Converter)
603 	 */
604 	public void setConverter(Converter converter) throws PropertyVetoException {
605 		if (this.converter != null && this.converter.equals(converter) ||
606 				(this.converter == null && converter == null)) return;
607 		DisplayerUtilities.prepareNewConverter(converter,this);
608 		
609 		Converter old= this.converter;
610 		this.converter = converter;
611 		
612 		firePropertyChange(CONVERTER_PROPERTY,old,this.converter);
613 	}
614 
615 }