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