View Javadoc

1   /*
2    * Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton,
3    * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
4    *
5    * THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
6    * WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
7    * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
8    * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
9    * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
10   * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
11   * THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
12   * IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
13   * CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
14   * NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
15   * DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
16   * OR MODIFICATIONS.
17   * THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
18   * USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
19   * PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
20   * AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
21   */
22  
23  package de.desy.acop.displayers;
24    
25  import java.awt.Color;
26  import java.awt.datatransfer.DataFlavor;
27  import java.awt.event.ActionEvent;
28  import java.beans.PropertyVetoException;
29  import java.util.ArrayList;
30  import java.util.HashMap;
31  import java.util.Iterator;
32  import java.util.Map;
33  
34  import javax.swing.AbstractAction;
35  import javax.swing.Action;
36  
37  import com.cosylab.gui.adapters.Converter;
38  import com.cosylab.gui.components.util.CosyTransferHandler;
39  import com.cosylab.gui.components.util.PopupManager;
40  import com.cosylab.gui.displayers.DataConsumer;
41  import com.cosylab.gui.displayers.DataSource;
42  import com.cosylab.gui.displayers.DataState;
43  import com.cosylab.gui.displayers.DoubleSeqConsumer;
44  import com.cosylab.gui.displayers.MultipleDisplayer;
45  import com.cosylab.gui.components.util.ColorManager;
46  import com.cosylab.gui.util.MultipleDisplayerParametersFlavor;
47  import com.cosylab.gui.util.UserSettingsProtection;
48  import com.cosylab.util.CommonException;
49  
50  import de.desy.acop.chart.Acop;
51  import de.desy.acop.displayers.tools.AcopChartConsumer;
52  import de.desy.acop.displayers.tools.AcopDisplayerParametersFlavor;
53  import de.desy.acop.displayers.tools.AcopDisplayerParameters;
54  import de.desy.acop.displayers.tools.AcopGraphParameters;
55  import de.desy.acop.displayers.tools.AcopGraphParametersFlavor;
56  import de.desy.acop.displayers.tools.AcopMultipleDisplayerInfoDialog;
57  import de.desy.acop.displayers.tools.ConnectionParametersFlavor;
58  import de.desy.acop.displayers.tools.MultipleAcopDisplayer;
59  import de.desy.acop.displayers.tools.AcopChartTransferHandler;
60  import de.desy.acop.transport.ConnectionParameters;
61  import de.desy.acop.transport.adapters.AcopTransportDataSource;
62  import de.desy.acop.transport.adapters.AdapterFactory;
63  import de.desy.acop.transport.adapters.AdapterFactoryService;
64          
65  /**
66   * <code>AcopChart</code> is an extension of <code>Acop</code> which enables
67   * the use of <code>ConnectionParameters</code> and can connect to a remote 
68   * property. This chart be connected to numerous connection points using
69   * the methods {@link #addDisplayerParameters(AcopGraphParameters)} and
70   * {@link #setDisplayerParameters(AcopGraphParameters[])}. 
71   * <p>
72   * This chart can be used as a trend or histogram chart. The mode which is
73   * used can be selected by applying the appropriate AcopGraphParameters,
74   * which also specify other settable properties that concern a particular
75   * graph.
76   * </p>
77   * <p>
78   * AcopChart also enables Drag & Drop, where AcopGraphParameters can be 
79   * transferred to/from the chart using the standard mouse drag and drop.
80   * </p>  
81   *  
82   *  
83   * @author ikriznar
84   * @see AcopGraphParameters
85   */
86  public class AcopChart extends Acop implements MultipleDisplayer, MultipleAcopDisplayer<AcopGraphParameters> {
87  	
88  	private static final long serialVersionUID = -2579109976414960938L;
89  	
90  	private int maxLinkIndex=0;
91  	private int suspended=0;
92  	private Map<String,AcopChartConsumer> consumers= new HashMap<String,AcopChartConsumer>();
93  	private HashMap<AcopGraphParameters, AcopChartConsumer> connections = new HashMap<AcopGraphParameters, AcopChartConsumer>();
94  	private DataState dataState = new DataState(DataState.NORMAL);
95  	private AcopMultipleDisplayerInfoDialog dialog;
96  	private PopupManager popupManager;
97  	private ColorManager colorManager = new ColorManager();
98  	
99  	/**
100 	 * Constructs a new AcopChart.
101 	 *
102 	 */
103 	public AcopChart() {
104         super(); 
105         DataFlavor[] flavor = new DataFlavor[]{AcopGraphParametersFlavor.FLAVOR,
106         		MultipleDisplayerParametersFlavor.FLAVOR, AcopDisplayerParametersFlavor.FLAVOR,
107         		ConnectionParametersFlavor.FLAVOR,
108         		DataFlavor.stringFlavor};
109 		new AcopChartTransferHandler(this, true, true, flavor, true);
110 //		new AcopTransferHandler(this, true, true);
111 		UserSettingsProtection.setProtection(this,new String[]{"caption", "yMin", "yMax"},false);
112 	}
113 	/* (non-Javadoc)
114 	 * @see com.cosylab.gui.displayers.CommonDisplayer#cleanup()
115 	 */
116 	public void cleanup() {
117 		for(AcopChartConsumer c: consumers.values()) {
118 			c.destroy();
119 		}
120 		consumers.clear();
121 	}
122 
123 	/* (non-Javadoc)
124 	 * @see com.cosylab.gui.displayers.CommonDisplayer#suspend()
125 	 */
126 	public void suspend() {
127 		suspended++;
128 	}
129 
130 	/* (non-Javadoc)
131 	 * @see com.cosylab.gui.displayers.CommonDisplayer#resume()
132 	 */
133 	public synchronized void resume() {
134 		if (suspended>0) {
135 			suspended--;
136 		}
137 	}
138 
139 	/* (non-Javadoc)
140 	 * @see com.cosylab.gui.displayers.CommonDisplayer#isSuspended()
141 	 */
142 	public boolean isSuspended() {
143 		return suspended>0;
144 	}
145 
146 	/* (non-Javadoc)
147 	 * @see com.cosylab.gui.displayers.DataStateProvider#getDataState()
148 	 */
149 	public DataState getDataState() {
150 		return dataState;
151 	}
152 
153 	/* (non-Javadoc)
154 	 * @see com.cosylab.gui.displayers.MultipleDataConsumer#getConsumer(java.lang.String, java.lang.Class)
155 	 */
156 	public <D extends DataConsumer> D getConsumer(String name, Class<D> type) {
157 		
158 		if (type.isAssignableFrom(AcopChartConsumer.class)) {
159 			if (consumers.containsKey(name)) {
160 				return type.cast(consumers.get(name));
161 			}
162 			
163 			AcopChartConsumer c= new AcopChartConsumer(this, name);
164 			c.setColor(colorManager.pickColor());
165 			maxLinkIndex++;
166 			consumers.put(name,c);
167 			return type.cast(c);
168 		}
169 		return null;
170 	}
171 
172 	/* (non-Javadoc)
173 	 * @see com.cosylab.gui.displayers.MultipleDataConsumer#releaseConsumer(com.cosylab.gui.displayers.DataConsumer)
174 	 */
175 	public void releaseConsumer(DataConsumer consumer) {
176 		consumers.remove(consumer.getName()).destroy();
177 		updateYScaleOnRemove((AcopChartConsumer)consumer);
178 		updateXScaleOnRemove((AcopChartConsumer)consumer);
179 		colorManager.dropColor(((AcopChartConsumer)consumer).getColor());
180 	}
181 
182 	/* (non-Javadoc)
183 	 * @see com.cosylab.gui.displayers.MultipleDataConsumer#getConsumers()
184 	 */
185 	public DataConsumer[] getConsumers() {
186 		return consumers.values().toArray(new DataConsumer[consumers.size()]);
187 	}
188 
189 	/* (non-Javadoc)
190 	 * @see com.cosylab.gui.displayers.MultipleDataConsumer#getSupportedConsumerTypes()
191 	 */
192 	@SuppressWarnings("unchecked")
193 	public Class<DataConsumer>[] getSupportedConsumerTypes() {
194 		return new Class[]{DoubleSeqConsumer.class};
195 	}
196 	
197 	/*
198 	 * (non-Javadoc)
199 	 * @see de.desy.acop.displayers.AcopMultipleDisplayerConnector#addConnectionParameters(de.desy.acop.transport.ConnectionParameters)
200 	 */
201 	public void addDisplayerParameters(AcopGraphParameters parameters) throws CommonException, PropertyVetoException {
202 		if (parameters == null) return;
203 		//System.out.println("ADD: '"+parameters.toString()+"' "+parameters.hashCode());
204 		parameters = synchronizeGraphParameters(parameters);
205 		if (connections.get(parameters) != null) {
206 			return;
207 		}
208 		AcopGraphParameters[] old = getDisplayerParameters();
209 		AcopChartConsumer consumer = (AcopChartConsumer) getConsumer(parameters.toString(), DoubleSeqConsumer.class);
210 			
211 		consumer.setDisplayerParameters(parameters);
212 		AdapterFactory factory = AdapterFactoryService.getInstance().getAdapterFactory();
213 		AcopTransportDataSource ds = factory.createDataSource(parameters.getConnectionParameters());
214 		if (parameters.getConverter() != null){
215 			Converter c = parameters.getConverter();
216 			c.addConsumer(consumer);
217 			ds.addConsumer(c);
218 		}
219 		else {
220 			ds.addConsumer(consumer);
221 		}
222 		connections.put(parameters, consumer);
223 		firePropertyChange(MultipleAcopDisplayer.DISPLAYER_PARAMETERS_PROPERTY, old, getDisplayerParameters());
224     }
225  	
226 	/**
227 	 * 
228 	 * @see de.desy.acop.displayers.tools.MultipleAcopDisplayer#addConnectionParameters(de.desy.acop.transport.ConnectionParameters)
229 	 * @deprecated use {@link #addDisplayerParameters(AcopGraphParameters)}
230 	 */
231 	public void addConnectionParameters(ConnectionParameters parameters) throws CommonException, PropertyVetoException {
232 		
233 		addDisplayerParameters(new AcopGraphParameters(parameters));
234     }
235 	
236 	private AcopGraphParameters synchronizeGraphParameters(AcopGraphParameters parameters) {
237 		
238 		Color color = null;
239 		if (parameters.getColor() != null)
240 			color = parameters.getColor();
241 		else
242 			color = colorManager.previewNextColor();
243 			
244 		if (parameters.getDrawStyle() != -1)
245 			setDrawStyle(parameters.getDrawStyle());
246 		int graphStyle = getDrawStyle();
247 				
248 		if (parameters.getFFT() != -1)
249 			setFFT(parameters.getFFT());
250 		int fft = getFFT();
251 		
252 		if (parameters.getMode() != -1)
253 			setDisplayMode(parameters.getMode());
254 		int mode = getDisplayMode();
255 		
256 		int width = parameters.getWidth();
257 		setDrawWidth(width);
258 		
259 		ConnectionParameters cp =parameters.getConnectionParameters();
260 		if (parameters.isTrend())
261 			cp = cp.deriveWithPropertySize(1);
262 		return new AcopGraphParameters(new AcopDisplayerParameters(
263 						cp,
264 						parameters.getArrayIndex(), parameters.getMinimum(),
265 						parameters.getMaximum(), parameters.getUnits(),parameters.getFormat()),
266 						color,fft,graphStyle,mode,width,parameters.isTrend(),parameters.getTrendLength(),
267 						parameters.getConverter());
268 	}
269 	
270 
271 	/*
272 	 * (non-Javadoc)
273 	 * @see de.desy.acop.displayers.AcopMultipleDisplayerConnector#getConnectionParameters()
274 	 */
275 	public AcopGraphParameters[] getDisplayerParameters() {
276 		AcopGraphParameters[] params = new AcopGraphParameters[connections.keySet().size()];
277 	    return connections.keySet().toArray(params);
278     }
279 	
280 	/**
281 	 * 
282 	 * @see de.desy.acop.displayers.tools.MultipleAcopDisplayer#getConnectionParameters()
283 	 * @deprecated use {@link #getDisplayerParameters()}
284 	 */
285 	public ConnectionParameters[] getConnectionParameters() {
286 		
287 		AcopGraphParameters[] agp = getDisplayerParameters();
288 		ConnectionParameters[] cp = new ConnectionParameters[agp.length];
289 		for (int i = 0; i < agp.length; i++) {
290 			cp[i] = agp[i].getConnectionParameters();
291 		}
292 		return cp;
293 	}
294 	
295 	/*
296 	 * (non-Javadoc)
297 	 * @see de.desy.acop.displayers.AcopMultipleDisplayerConnector#removeConnectionParameters(de.desy.acop.transport.ConnectionParameters)
298 	 */
299 	public DataSource removeDisplayerParameters(AcopGraphParameters parameters) {
300 		//System.out.println("REM: '"+parameters.toString()+"' "+parameters.hashCode());
301 		AcopGraphParameters[] old = getDisplayerParameters();
302 		AdapterFactory factory = AdapterFactoryService.getInstance().getAdapterFactory();
303 		AcopChartConsumer consumer = connections.remove(parameters);
304 		if (consumer == null) return null;
305 		DataSource ds = factory.releaseDataSource(parameters.getConnectionParameters());
306 		if (parameters.getConverter() != null) {
307 			ds.removeConsumer(parameters.getConverter());
308 			parameters.getConverter().removeConsumer(consumer);
309 		} else {
310 			ds.removeConsumer(consumer);
311 		}
312 		releaseConsumer(consumer);
313 
314 		firePropertyChange(MultipleAcopDisplayer.DISPLAYER_PARAMETERS_PROPERTY, old, getDisplayerParameters());
315 	    return ds;
316 	    
317     }
318 	
319 	/**
320 	 * 
321 	 * @see de.desy.acop.displayers.tools.MultipleAcopDisplayer#removeConnectionParameters(de.desy.acop.transport.ConnectionParameters)
322 	 * @deprecated use {@link #removeDisplayerParameters(AcopGraphParameters)}
323 	 */
324 	public DataSource removeConnectionParameters(ConnectionParameters parameters) {
325 		
326 		return removeDisplayerParameters(new AcopGraphParameters(parameters, getForeground(),getFFT(), getDrawStyle(), getDisplayMode(), getDrawWidth(), false, 100, null));
327 	}
328 	
329 	/*
330 	 * (non-Javadoc)
331 	 * @see de.desy.acop.displayers.AcopMultipleDisplayerConnector#setConnectionParameters(de.desy.acop.transport.ConnectionParameters[])
332 	 */
333 	public void setDisplayerParameters(AcopGraphParameters[] parameters) throws CommonException, PropertyVetoException {
334 		AcopGraphParameters[] old = getDisplayerParameters();
335 		ArrayList<AcopGraphParameters> toBeRemoved = new ArrayList<AcopGraphParameters>();
336 		Iterator<AcopGraphParameters> it = connections.keySet().iterator();
337 		while(it.hasNext()) {
338 			toBeRemoved.add(it.next());
339 		}
340 		
341 		ArrayList<AcopGraphParameters> toBeAdded = new ArrayList<AcopGraphParameters>();
342 	
343 		for (AcopGraphParameters p : parameters) {
344 			if (toBeRemoved.contains(p)) {
345 				toBeRemoved.remove(p);
346 			} else {
347 				toBeAdded.add(p);
348 			}
349 		}
350 		
351 		for(AcopGraphParameters p : toBeRemoved) {
352 			removeDisplayerParameters(p);
353 		}
354 		
355 		for(AcopGraphParameters p : toBeAdded) {
356 			addDisplayerParameters(p);
357 		}
358 		
359 		firePropertyChange(MultipleAcopDisplayer.DISPLAYER_PARAMETERS_PROPERTY, old, parameters);
360     }
361 	
362 	/*
363 	 * (non-Javadoc)
364 	 * @see de.desy.acop.displayers.tools.MultipleAcopDisplayer#setConnectionParameters(de.desy.acop.transport.ConnectionParameters[])
365 	 */
366 	public void setConnectionParameters(ConnectionParameters[] parameters) throws CommonException, PropertyVetoException {
367 		  
368 		AcopGraphParameters[] agp = new AcopGraphParameters[parameters.length];
369 		for (int i = 0; i < parameters.length; i++) {
370 			agp[i] = new AcopGraphParameters(parameters[i]);
371 		}
372 		setDisplayerParameters(agp);
373 	}
374 		
375 	/**
376 	 * Updates chart's y scale using so the preferred min/max of the given
377 	 * consumer are included in this chart's bounds.
378 	 * 
379 	 * @param consumer the consumer, whose bounds changed
380 	 */
381 	public void updateYScale(AcopChartConsumer consumer) {
382 		if (!consumers.values().contains(consumer)) return;
383 		double max;
384 		double min;
385 		if (getConsumers().length == 1) {
386 			max = consumer.getPreferredYMax();
387 			min = consumer.getPreferredYMin();
388 		} else {
389 			max = Math.max(getYMax(), consumer.getPreferredYMax());
390 			min = Math.min(getYMin(), consumer.getPreferredYMin());
391 		}
392 		setYMax(max);
393 		setYMin(min);
394 	}
395 	
396 	private void updateYScaleOnRemove(AcopChartConsumer consumer) {
397 		if (!(consumer.getPreferredYMax() < getYMax() || consumer.getPreferredYMin() > getYMin())) {
398 			return;
399 		}
400 		Iterator<AcopChartConsumer> it = consumers.values().iterator();
401 		double max;
402 		double min;
403 		if (it.hasNext()) {
404 			AcopChartConsumer c = it.next();
405 			max = c.getPreferredYMax();
406 			min = c.getPreferredYMin(); 
407 		} else {
408 			return;
409 		}
410 		while (it.hasNext()) {
411 			AcopChartConsumer c = it.next();
412 			max = Math.max(max, c.getPreferredYMax());
413 			min = Math.min(min, c.getPreferredYMin());
414 		}
415 		setYMax(max);
416 		setYMin(min);
417 		
418 	}
419 	
420 	/**
421 	 * Updates chart's x scale using so the preferred min/max of the given
422 	 * consumer are included in this chart's bounds.
423 	 * 
424 	 * @param consumer the consumer, whose bounds changed
425 	 */
426 	public void updateXScale(AcopChartConsumer consumer) {
427 		if (!consumers.values().contains(consumer)) return;
428 		double max;
429 		if (getConsumers().length == 1) {
430 			max = consumer.getArraySize();
431 		} else {
432 			max = Math.max(getXMax(), consumer.getArraySize());
433 		}
434 		setXMax(max);
435 	}
436 	
437 	private void updateXScaleOnRemove(AcopChartConsumer consumer) {
438 		if (!(consumer.getArraySize() < getXMax())) {
439 			return;
440 		}
441 		Iterator<AcopChartConsumer> it = consumers.values().iterator();
442 		double max;
443 		if (it.hasNext()) {
444 			AcopChartConsumer c = it.next();
445 			max = c.getArraySize();
446 		} else {
447 			return;
448 		}
449 		while (it.hasNext()) {
450 			AcopChartConsumer c = it.next();
451 			max = Math.max(max, c.getArraySize());
452 		}
453 		setXMax(max);				
454 	}
455 	
456 	/**
457 	 * Enables/disables mouse dragging. Dragging can only be enabled if
458 	 * this component uses CosyTransferHandler.
459 	 * 
460 	 * @param enabled
461 	 */
462 	public void setDragEnabled(boolean enabled) {
463 		if (getTransferHandler() instanceof CosyTransferHandler) {
464 			if (((CosyTransferHandler)getTransferHandler()).isExportEnabled() == enabled) return;
465 			
466 			((CosyTransferHandler)getTransferHandler()).setExportEnabled(enabled, this);
467 		}
468 	}
469 	
470 	/**
471 	 * Returns true if drag is enabled.
472 	 * 
473 	 * @return true if drag is enabled
474 	 */
475 	public boolean isDragEnabled() {
476 		if (getTransferHandler() instanceof CosyTransferHandler) {
477 			return ((CosyTransferHandler)getTransferHandler()).isExportEnabled();
478 		} else {
479 			return getTransferHandler() != null;
480 		}
481 	}
482 	
483 	/**
484 	 * Enable/disable the mouse drop. Drop can only be enabled if this component uses
485 	 * CosyTransferHandler.
486 	 * 
487 	 * @param enabled
488 	 */
489 	public void setDropEnabled(boolean enabled) {
490 		if (getTransferHandler() instanceof CosyTransferHandler) {
491 			if (((CosyTransferHandler)getTransferHandler()).isReceiveEnabled() == enabled) return;
492 			
493 			((CosyTransferHandler)getTransferHandler()).setReceiveEnabled(enabled, this);
494 		}
495 	}
496 	
497 	/**
498 	 * Returns true if drop is enabled.
499 	 * 
500 	 * @return true if drop is enabled
501 	 */
502 	public boolean isDropEnabled() {
503 		if (getTransferHandler() instanceof CosyTransferHandler) {
504 			return ((CosyTransferHandler)getTransferHandler()).isReceiveEnabled();
505 		} else {
506 			return getTransferHandler() != null;
507 		}
508 	}
509 	
510 	/**
511 	 * Returns the <code>InfoDialog</code> associated with this displayer.
512 	 * 
513 	 * @return the info dialog
514 	 */
515 	public AcopMultipleDisplayerInfoDialog getInfoDialog() {
516 		if (dialog == null) {
517 			dialog = new AcopMultipleDisplayerInfoDialog(this);
518 		}
519 		return dialog;
520 	}
521 	
522 	/*
523 	 * (non-Javadoc)
524 	 * @see de.desy.acop.chart.Acop#getPopupManager()
525 	 */
526 	@Override
527 	public PopupManager getPopupManager()
528 	{
529 		if (popupManager == null) {
530 			popupManager = super.getPopupManager();
531 			popupManager.addAction(new AbstractAction("Info...") {
532 				private static final long serialVersionUID = 1L;
533 
534 					public void actionPerformed(ActionEvent e)
535 					{
536 						getInfoDialog().setVisible(true);
537 					}
538 				});
539 		}
540 
541 		return popupManager;
542 	}
543 	
544 	/**
545 	 * Enables/disables Properties item in the popup menu.
546 	 * @param enable
547 	 */
548 	public void setPropertiesPopupEnabled(boolean enable) {
549 		Action[] actions = getPopupManager().getActions();
550 		for (Action a : actions) {
551 			if (a != null && "Preferences...".equals(a.getValue(Action.NAME))) {
552 				a.setEnabled(enable);
553 				return;
554 			}
555 		}
556 	}
557 	
558 	/**
559 	 * Returns true if Properties item is enabled in the popup menu.
560 	 * @return
561 	 */
562 	public boolean isPropertiesPopupEnabled() {
563 		Action[] actions = getPopupManager().getActions();
564 		for (Action a : actions) {
565 			if (a != null && "Preferences...".equals(a.getValue(Action.NAME))) {
566 				return a.isEnabled();
567 			}
568 		}
569 		return false;
570 	}
571 	
572 }