View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of CosyBeans.
5    *
6    * CosyBeans 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 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.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.gui;
21  
22  import java.awt.BorderLayout;
23  import java.awt.event.ActionEvent;
24  import java.awt.event.ActionListener;
25  import java.beans.PropertyVetoException;
26  import java.util.Map;
27  
28  import javax.swing.AbstractAction;
29  import javax.swing.JButton;
30  import javax.swing.JFrame;
31  
32  import com.cosylab.events.SetEvent;
33  import com.cosylab.events.SetListener;
34  import com.cosylab.gui.adapters.Converter;
35  import com.cosylab.gui.components.Slider;
36  import com.cosylab.gui.components.util.PopupManager;
37  import com.cosylab.gui.displayers.ConvertibleDisplayer;
38  import com.cosylab.gui.displayers.DataConsumer;
39  import com.cosylab.gui.displayers.DataSource;
40  import com.cosylab.gui.displayers.DataSourceSupport;
41  import com.cosylab.gui.displayers.DataState;
42  import com.cosylab.gui.displayers.DisplayerUtilities;
43  import com.cosylab.gui.displayers.DoubleConsumer;
44  import com.cosylab.gui.displayers.DoubleConsumerMulticaster;
45  import com.cosylab.gui.displayers.DoubleDisplayer;
46  import com.cosylab.gui.displayers.NonblockingNumberConsumer;
47  import com.cosylab.util.CommonException;
48  
49  
50  /**
51   * Displays value on editable slider.
52   *
53   * @author <a href="mailto:ales.pucelj@cosylab.com">Ales Pucelj</a>
54   * @version $id$
55   */
56  public class SliderDisplayer extends Slider implements DoubleDisplayer,
57  	DataSource, ConvertibleDisplayer
58  {
59  	private static final long serialVersionUID = 1L;
60  	@SuppressWarnings("unchecked")
61  	private DataSourceSupport support = new DataSourceSupport(new Class[]{
62  			    NonblockingNumberConsumer.class
63  		    });
64  	private DataState dataState = new DataState(DataState.UNDEFINED);
65  	private boolean newUpdate = false;
66  	private DataSource dataSource;
67  	private double lastValue = 0;
68  	private InfoDialog infoDialog;
69  	private PopupManager popupManager;
70  
71  	private int suspendCount = 0;
72  
73  	/**
74  	 * Constructor for SliderDisplayer.
75  	 */
76  	public SliderDisplayer()
77  	{
78  		super();
79  		initializeDisplayer();
80  	}
81  
82  	/* (non-Javadoc)
83  	 * @see com.cosylab.gui.displayers.DataSource#getSupportedConsumersTypes()
84  	 */
85  	@SuppressWarnings("unchecked")
86  	public Class[] getAcceptableConsumerTypes()
87  	{
88  		return support.getAcceptableConsumerTypes();
89  	}
90  
91  	/* (non-Javadoc)
92  	 * @see com.cosylab.gui.displayers.DataConsumer#setCharacteristics(java.util.Map)
93  	 */
94  	public void setCharacteristics(Map characteristics)
95  	{
96  		if (characteristics == null) {
97  			throw new NullPointerException("characteristics");
98  		}
99  
100 		newUpdate = true;
101 
102 		DisplayerUtilities.setCharacteristics(characteristics, this);
103 	}
104 
105 	/* (non-Javadoc)
106 	 * @see com.cosylab.gui.displayers.DataSource#getConsumers()
107 	 */
108 	public DataConsumer[] getConsumers()
109 	{
110 		return support.getConsumers();
111 	}
112 
113 	/* (non-Javadoc)
114 	 * @see com.cosylab.gui.displayers.DataConsumer#getDataConsumer(java.lang.Class)
115 	 */
116 	public DataConsumer getDataConsumer(Class type)
117 	{
118 		if (type == DoubleConsumer.class) {
119 			return this;
120 		}
121 
122 		return DoubleConsumerMulticaster.createDataConsumer(type, this);
123 	}
124 
125 	/* (non-Javadoc)
126 	 * @see com.cosylab.gui.displayers.CommonDisplayer#getDataState()
127 	 */
128 	public DataState getDataState()
129 	{
130 		return dataState;
131 	}
132 
133 	/* (non-Javadoc)
134 	 * @see com.cosylab.gui.displayers.DataConsumer#getDefaultDataConsumer()
135 	 */
136 	public DataConsumer getDefaultDataConsumer()
137 	{
138 		return this;
139 	}
140 
141 	/* (non-Javadoc)
142 	 * @see com.cosylab.gui.displayers.DataConsumer#getSupportedCharacteristics()
143 	 */
144 	public String[] getSupportedCharacteristics()
145 	{
146 		return DisplayerUtilities.COMMON_NUMERIC_DISPLAYER_CHARACTERISTICS;
147 	}
148 
149 	/* (non-Javadoc)
150 	 * @see com.cosylab.gui.displayers.DataConsumer#getSupportedConsumerTypes()
151 	 */
152 	@SuppressWarnings("unchecked")
153 	public Class[] getSupportedConsumerTypes()
154 	{
155 		return DoubleConsumerMulticaster.PREFERED_CONSUMER_TYPES;
156 	}
157 
158 	/**
159 	 * Accepts only consumers, which support
160 	 * <code>NonblockingNumberConsumer</code> which is used for receiving
161 	 * updates from user.
162 	 *
163 	 * @see com.cosylab.gui.displayers.DataSource#addConsumer(com.cosylab.gui.displayers.DataConsumer)
164 	 */
165 	public void addConsumer(DataConsumer consumer) throws PropertyVetoException
166 	{
167 		if (consumer == null) {
168 			throw new NullPointerException("consumer");
169 		}
170 
171 		NonblockingNumberConsumer c = (NonblockingNumberConsumer)consumer
172 			.getDataConsumer(NonblockingNumberConsumer.class);
173 
174 		if (c == null) {
175 			throw new PropertyVetoException("Consumer '" + consumer
176 			    + "' must support NonblockingNumberConsumer.", null);
177 		}
178 
179 		support.addConsumer(c);
180 	}
181 
182 	/* (non-Javadoc)
183 	 * @see com.cosylab.gui.displayers.CommonDisplayer#cleanup()
184 	 */
185 	public void cleanup()
186 	{
187 		internalCleanup();
188 		updateDataState(new DataState(DataState.NOT_INITIALIZED));
189 	}
190 
191 	/* (non-Javadoc)
192 	 * @see com.cosylab.gui.core.CosyComponent#destroy()
193 	 */
194 	public void destroy()
195 	{
196 		cleanup();
197 		support.removeAllConsumers();
198 		dataState = null;
199 	}
200 
201 	/* (non-Javadoc)
202 	 * @see com.cosylab.gui.displayers.DataSource#removeConsumer(com.cosylab.gui.displayers.DataConsumer)
203 	 */
204 	public void removeConsumer(DataConsumer consumer)
205 	{
206 		support.removeConsumer(consumer);
207 	}
208 
209 	/*
210 	 * (non-Javadoc)
211 	 * @see com.cosylab.gui.components.Slider#isSuspended()
212 	 */
213 	public boolean isSuspended()
214 	{
215 		return (suspendCount > 0);
216 	}
217 
218 	/*
219 	 * (non-Javadoc)
220 	 * @see com.cosylab.gui.components.Slider#resume()
221 	 */
222 	public void resume()
223 	{
224 		if (suspendCount > 0) {
225 			suspendCount--;
226 		}
227 
228 		if (suspendCount == 0) {
229 			setEnabled(true);
230 			setReadback(lastValue);
231 
232 			if (newUpdate) {
233 				synchronize();
234 				newUpdate = false;
235 			}
236 		}
237 	}
238 
239 	/*
240 	 * (non-Javadoc)
241 	 * @see com.cosylab.gui.components.Slider#suspend()
242 	 */
243 	public void suspend()
244 	{
245 		setEnabled(false);
246 		suspendCount++;
247 	}
248 
249 	/* (non-Javadoc)
250 	 * @see com.cosylab.gui.displayers.DataConsumer#updateDataState(com.cosylab.gui.displayers.DataState)
251 	 */
252 	public void updateDataState(DataState state)
253 	{
254 		DataState old = dataState;
255 		dataState = state;
256 		firePropertyChange(DATA_STATE, old, dataState);
257 	}
258 
259 	protected short update = 0;
260 	private Converter converter;
261 	
262 	/* (non-Javadoc)
263 	 * @see com.cosylab.gui.displayers.DoubleConsumer#updateValue(long, double)
264 	 */
265 	public void updateValue(long timestamp, double value)
266 		throws CommonException
267 	{
268 	    lastValue = value;
269 	    
270 	    
271 	    if (!isSuspended()) {
272 			setReadback(value);
273 
274 			if (newUpdate) {
275 				synchronize();
276 				newUpdate = false;
277 			}
278 			
279 			if (update < 2) {
280 			    synchronize();
281 			    update++;
282 			}
283 					
284 		}
285 		
286 	}
287 
288 	private void initializeDisplayer()
289 	{
290 		addSetListener(new SetListener() {
291 				public void setPerformed(SetEvent e)
292 				{
293 					try {
294 						double value = e.getDoubleValue();
295 						
296 						DataConsumer[] d = getConsumers();
297 
298 						for (int i = 0; i < d.length; i++) {
299 							NonblockingNumberConsumer dd = (NonblockingNumberConsumer)d[i];
300 
301 							if (dd != null) {
302 								dd.updateNonblocking(new Double(value));
303 							}
304 						}
305 					} catch (Exception ex) {
306 						ex.printStackTrace();
307 					}
308 				}
309 			});
310 		setPopupEnabled(true);
311 		internalCleanup();
312 	}
313 
314 	private void internalCleanup()
315 	{
316 		setMinimum(0);
317 		setMaximum(100);
318 		setValue(0);
319 		setTitle("Slider");
320 		setUnits(null);
321 		setUnitsVisible(true);
322 		setFormat("%3.2f");
323 		setEditable(true);
324 	}
325 
326 	/* (non-Javadoc)
327 	 * @see com.cosylab.gui.displayers.Displayer#getDataSource()
328 	 */
329 	public DataSource getDataSource()
330 	{
331 	    return dataSource;
332 	}
333 
334 	/* (non-Javadoc)
335 	 * @see com.cosylab.gui.displayers.Displayer#setDataSource(com.cosylab.gui.displayers.DataSource)
336 	 */
337 	public void setDataSource(DataSource dataSource)
338 		throws PropertyVetoException
339 	{
340 		DisplayerUtilities.prepareNewDataSource(dataSource,this);
341 		
342 		DataSource old= this.dataSource;
343 		this.dataSource = dataSource;
344 		
345 		firePropertyChange(DATA_SOURCE,old,dataSource);
346 	}
347 	
348 	/*
349 	 * (non-Javadoc)
350 	 * @see com.cosylab.gui.displayers.ConvertibleDisplayer#getConverter()
351 	 */
352 	public Converter getConverter() {
353 		return converter;
354 	}
355 
356 	/*
357 	 * (non-Javadoc)
358 	 * @see com.cosylab.gui.displayers.ConvertibleDisplayer#setConverter(com.cosylab.gui.adapters.Converter)
359 	 */
360 	public void setConverter(Converter converter) throws PropertyVetoException {
361 		if (this.converter != null && this.converter.equals(converter) ||
362 				(this.converter == null && converter == null)) return;
363 		DisplayerUtilities.prepareNewConverter(converter,this);
364 		
365 		Converter old= this.converter;
366 		this.converter = converter;
367 		
368 		firePropertyChange(CONVERTER_PROPERTY,old,this.converter);
369 	}
370 
371 	/* (non-Javadoc)
372 	 * @see com.cosylab.gui.displayers.DataSource#removeAllConsumers()
373 	 */
374 	public void removeAllConsumers()
375 	{
376 		support.removeAllConsumers();
377 	}
378 	
379 	/**
380 	 * Returns the InfoDialog hooked to this Displayer.
381 	 * 
382 	 * @return
383 	 */
384 	public InfoDialog getInfoDialog() {
385 		if (infoDialog == null) {
386 			infoDialog = new InfoDialog(this);
387 		}
388 		return infoDialog;
389 	}
390 	
391 	/*
392 	 * (non-Javadoc)
393 	 * @see com.cosylab.gui.components.Slider#getPopupManager()
394 	 */
395 	public PopupManager getPopupManager()
396 	{
397 		if (popupManager == null) {
398 			popupManager = super.getPopupManager();
399 			popupManager.addAction(new AbstractAction("Info...") {
400 				private static final long serialVersionUID = 1L;
401 
402 					public void actionPerformed(ActionEvent e)
403 					{
404 						getInfoDialog().setVisible(true);
405 					}
406 				});
407 		}
408 		
409 		return popupManager;
410 	}
411 
412 	public static void main(String[] args)
413 	{
414 		JFrame f = new JFrame();
415 		f.getContentPane().setLayout(new BorderLayout());
416 		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
417 
418 		final SliderDisplayer s = new SliderDisplayer();
419 		f.getContentPane().add(s, BorderLayout.SOUTH);
420 
421 		JButton b = new JButton("S/R");
422 		f.getContentPane().add(b, BorderLayout.EAST);
423 		b.addActionListener(new ActionListener() {
424 				public void actionPerformed(ActionEvent e)
425 				{
426 					if (s.isSuspended()) {
427 						s.resume();
428 					} else {
429 						s.suspend();
430 					}
431 				}
432 			});
433 		f.show();
434 	}
435 
436 }
437 
438 /* __oOo__ */