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.GridBagConstraints;
27  import java.awt.GridBagLayout;
28  import java.awt.Insets;
29  import java.awt.event.ActionEvent;
30  import java.beans.PropertyChangeEvent;
31  import java.beans.PropertyChangeListener;
32  import java.beans.PropertyVetoException;
33  import java.util.ArrayList;
34  
35  import javax.swing.AbstractAction;
36  import javax.swing.Action;
37  import javax.swing.JPanel;
38  
39  import com.cosylab.gui.adapters.Converter;
40  import com.cosylab.gui.components.customizer.AbstractCustomizerPanel;
41  import com.cosylab.gui.components.util.CosyTransferHandler;
42  import com.cosylab.gui.components.util.PopupManageable;
43  import com.cosylab.gui.components.util.PopupManager;
44  import com.cosylab.gui.components.util.RunnerHelper;
45  import com.cosylab.gui.displayers.DataConsumer;
46  import com.cosylab.gui.displayers.DataSource;
47  import com.cosylab.gui.displayers.DataState;
48  import com.cosylab.gui.displayers.DoubleSeqConsumer;
49  import com.cosylab.gui.displayers.MultipleDisplayer;
50  import com.cosylab.gui.components.util.ColorManager;
51  import com.cosylab.gui.util.UserSettingsProtection;
52  import com.cosylab.util.CommonException;
53  
54  import de.desy.acop.chart.Acop;
55  import de.desy.acop.chart.AcopAdapter;
56  import de.desy.acop.chart.AcopCursorMarkerModeEnum;
57  import de.desy.acop.chart.AcopDisplayMode;
58  import de.desy.acop.chart.AcopEvent;
59  import de.desy.acop.chart.AcopGraphStyleEnum;
60  import de.desy.acop.displayers.chart.AcopChartConsumer;
61  import de.desy.acop.displayers.chart.AcopChartReorgTransferHandler;
62  import de.desy.acop.displayers.chart.LegendDialog;
63  import de.desy.acop.displayers.tools.AcopDisplayerParameters;
64  import de.desy.acop.displayers.tools.AcopGraphParameters;
65  import de.desy.acop.displayers.tools.MultipleAcopDisplayer;
66  import de.desy.acop.transport.ConnectionParameters;
67  import de.desy.acop.transport.adapters.AcopTransportDataSource;
68  import de.desy.acop.transport.adapters.AdapterFactory;
69  import de.desy.acop.transport.adapters.AdapterFactoryService;
70          
71  /**
72   * <code>AcopChartReorg</code> is a wrapper for <code>Acop</code> which
73   * can present multiple graphs and allows setting of the most common Acop properties.
74   * 
75   * @author ikriznar
76   *  
77   */
78  public class AcopChartReorg extends JPanel implements MultipleDisplayer, MultipleAcopDisplayer<AcopGraphParameters>, PopupManageable {
79  	
80  	private static final long serialVersionUID = -2579109976414960938L;
81  
82  	public static final String TITLE = "title";
83  	public static final String LINE_STYLE = "displayMode";
84  	public static final String CHUBBY_LINES = "chubbyLines";
85  	public static final String AXIS_TRACE_VISIBLE = "axisTraceVisible";
86  	public static final String X_AUTOSCALE = "xAutoScale";
87  	public static final String Y_AUTOSCALE = "yAutoScale";
88  	public static final String Y_LOG_SCALE = "yLogScale";
89  	public static final String X_RANGE_MAX = "xRangeMax";
90  	public static final String X_RANGE_MIN = "xRangeMin";
91  	public static final String Y_RANGE_MAX = "yRangeMax";
92  	public static final String Y_RANGE_MIN = "yRangeMin";
93  	public static final String CONSUMERS = "consumers";
94  	
95  	protected ColorManager colorManager = new ColorManager();
96  	
97  	private boolean axisTraceVisible;
98  	private boolean xAutoScale;
99  	private boolean yAutoScale;
100 	private boolean yLogScale;
101 	private boolean isZoomed;
102 	private boolean chubbyLines;
103 	private AcopDisplayMode displayMode;
104 	private boolean showGrid = false;
105 	private boolean bestScale = false;
106 	
107 	protected int maxLinkIndex=0;
108 	private int suspended=0;
109 	
110 	protected ArrayList<AcopChartConsumer> consumersList = new ArrayList<AcopChartConsumer>();
111 	private DataState dataState = new DataState(DataState.NORMAL);
112 	private PopupManager popupManager;
113 	protected AbstractCustomizerPanel customizer;
114 	private Acop acop;
115 	private LegendDialog legend;
116 	
117 	private class AcopZoomListener extends AcopAdapter {
118 
119 		/* (non-Javadoc)
120 		 * @see de.desy.acop.chart.AcopAdapter#acopMouseZoom(de.desy.acop.chart.AcopEvent)
121 		 */
122 		@Override
123 		public void acopMouseZoom(AcopEvent zoom) {
124 			if (getAcop().isZoomed()) {
125 				isZoomed = true;
126 			}
127 			else {
128 				isZoomed = false;
129 			}
130 		}
131 		
132 	}
133 	
134 	private class AcopChangeListener implements PropertyChangeListener {
135 		public void propertyChange(PropertyChangeEvent evt) {
136 			String property = evt.getPropertyName();
137 			if (property.equals("caption")) {
138 				firePropertyChange(TITLE, evt.getOldValue(), evt.getNewValue());
139 			}
140 			else if (property.equals("yMin")) {
141 				firePropertyChange(Y_RANGE_MIN, evt.getOldValue(), evt.getNewValue());
142 			}
143 			else if (property.equals("yMax")) {
144 				firePropertyChange(Y_RANGE_MAX, evt.getOldValue(), evt.getNewValue());
145 			}
146 			else if (property.equals("xMin")) {
147 				firePropertyChange(X_RANGE_MIN, evt.getOldValue(), evt.getNewValue());
148 			}
149 			else if (property.equals("xMax")) {
150 				firePropertyChange(X_RANGE_MAX, evt.getOldValue(), evt.getNewValue());
151 			}	
152 		}
153 	}
154 	
155 	/**
156 	 * Constructs a new AcopChartReorg.
157 	 *
158 	 */
159 	public AcopChartReorg() {
160         super();
161         setLayout(new GridBagLayout());
162         add(getAcop(), new GridBagConstraints(0,0,1,1,1,1,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,0,0),0,0));
163         new AcopChartReorgTransferHandler(this);
164 //		new ChartTransferHandler(this, true, true);
165 		UserSettingsProtection.setProtection(this,new String[]{"caption", "yMin", "yMax"},false);
166 		getPopupManager();
167 	}
168 	
169 	/**
170 	 * Returns this component's </code>Acop</code> chart.
171 	 * @return the </code>Acop</code>
172 	 */
173 	public Acop getAcop() {
174 		if (acop == null) {
175 			acop = new Acop();
176 			acop.setXBestScale(bestScale);
177 			acop.setYBestScale(bestScale);
178 			acop.setXGrid(showGrid);
179 			acop.setYGrid(showGrid);
180 			acop.addPropertyChangeListener(new AcopChangeListener());
181 			acop.addAcopListener(new AcopZoomListener());
182 			acop.setCursorMarkerColor(Color.white);
183 			setTitle("");
184 		}
185 		return acop;
186 	}
187 	
188 	/* (non-Javadoc)
189 	 * @see com.cosylab.gui.displayers.CommonDisplayer#cleanup()
190 	 */
191 	public void cleanup() {
192 		DataConsumer[] oldConsumers = getConsumers();
193 		for(AcopChartConsumer c: consumersList) {
194 			c.destroy();
195 		}
196 		consumersList.clear();
197 		firePropertyChange(CONSUMERS, oldConsumers, getConsumers());
198 	}
199 
200 	/* (non-Javadoc)
201 	 * @see com.cosylab.gui.displayers.CommonDisplayer#suspend()
202 	 */
203 	public void suspend() {
204 		suspended++;
205 	}
206 
207 	/* (non-Javadoc)
208 	 * @see com.cosylab.gui.displayers.CommonDisplayer#resume()
209 	 */
210 	public synchronized void resume() {
211 		if (suspended>0) {
212 			suspended--;
213 		}
214 	}
215 
216 	/* (non-Javadoc)
217 	 * @see com.cosylab.gui.displayers.CommonDisplayer#isSuspended()
218 	 */
219 	public boolean isSuspended() {
220 		return suspended>0;
221 	}
222 
223 	/* (non-Javadoc)
224 	 * @see com.cosylab.gui.displayers.DataStateProvider#getDataState()
225 	 */
226 	public DataState getDataState() {
227 		return dataState;
228 	}
229 
230 	/* (non-Javadoc)
231 	 * @see com.cosylab.gui.displayers.MultipleDataConsumer#getConsumer(java.lang.String, java.lang.Class)
232 	 */
233 	public <D extends DataConsumer> D getConsumer(String name, Class<D> type) {
234 		
235 		if (type.isAssignableFrom(AcopChartConsumer.class)) {
236 			for (AcopChartConsumer c : consumersList) {
237 				if (c.getName().equals(name)) return type.cast(c);
238 			}
239 			
240 			DataConsumer[] oldConsumers = getConsumers();
241 			AcopChartConsumer c= new AcopChartConsumer(this, name);
242 			c.setColor(colorManager.pickColor());
243 			c.setChubbyLines(isChubbyLines());
244 			c.setDisplayMode(getDisplayMode());
245 			maxLinkIndex++;
246 			consumersList.add(c);
247 			firePropertyChange(CONSUMERS, oldConsumers, getConsumers());
248 			return type.cast(c);
249 		}
250 		return null;
251 	}
252 
253 	/* (non-Javadoc)
254 	 * @see com.cosylab.gui.displayers.MultipleDataConsumer#releaseConsumer(com.cosylab.gui.displayers.DataConsumer)
255 	 */
256 	public void releaseConsumer(DataConsumer consumer) {
257 		int index = consumersList.indexOf(consumer);
258 		if (index >= 0 && index < consumersList.size()) {
259 			DataConsumer[] oldConsumers = getConsumers();
260 			consumersList.remove(index).destroy();
261 			firePropertyChange(CONSUMERS, oldConsumers, getConsumers());
262 		}
263 		updateYScaleOnRemove((AcopChartConsumer)consumer);
264 		updateXScaleOnRemove((AcopChartConsumer)consumer);
265 		colorManager.dropColor(((AcopChartConsumer)consumer).getColor());
266 	}
267 
268 	/* (non-Javadoc)
269 	 * @see com.cosylab.gui.displayers.MultipleDataConsumer#getConsumers()
270 	 */
271 	public DataConsumer[] getConsumers() {
272 		return consumersList.toArray(new DataConsumer[consumersList.size()]);
273 	}
274 
275 	/* (non-Javadoc)
276 	 * @see com.cosylab.gui.displayers.MultipleDataConsumer#getSupportedConsumerTypes()
277 	 */
278 	@SuppressWarnings("unchecked")
279 	public Class<DataConsumer>[] getSupportedConsumerTypes() {
280 		return new Class[]{DoubleSeqConsumer.class};
281 	}
282 	
283 	/*
284 	 * (non-Javadoc)
285 	 * @see de.desy.acop.displayers.AcopMultipleDisplayerConnector#addConnectionParameters(de.desy.acop.transport.ConnectionParameters)
286 	 */
287 	public void addDisplayerParameters(AcopGraphParameters parameters) throws CommonException, PropertyVetoException {
288 		if (parameters == null) return;
289 		//System.out.println("ADD: '"+parameters.toString()+"' "+parameters.hashCode());
290 		parameters = synchronizeGraphParameters(parameters);
291 		for (AcopChartConsumer c : consumersList) {
292 			if (c.getDisplayerParameters().equals(parameters)) return;
293 		}
294 		AcopGraphParameters[] old = getDisplayerParameters();
295 		AcopChartConsumer consumer = (AcopChartConsumer) getConsumer(parameters.toString(), DoubleSeqConsumer.class);
296 			
297 		consumer.setDisplayerParameters(parameters);
298 		AdapterFactory factory = AdapterFactoryService.getInstance().getAdapterFactory();
299 		AcopTransportDataSource ds = factory.createDataSource(parameters.getConnectionParameters());
300 		if (parameters.getConverter() != null){
301 			Converter c = parameters.getConverter();
302 			c.addConsumer(consumer);
303 			ds.addConsumer(c);
304 		}
305 		else {
306 			ds.addConsumer(consumer);
307 		}
308 		firePropertyChange(MultipleAcopDisplayer.DISPLAYER_PARAMETERS_PROPERTY, old, getDisplayerParameters());
309     }
310  	
311 	/*
312 	 * (non-Javadoc)
313 	 * @see de.desy.acop.displayers.tools.MultipleAcopDisplayer#addConnectionParameters(de.desy.acop.transport.ConnectionParameters)
314 	 */
315 	public void addConnectionParameters(ConnectionParameters parameters) throws CommonException, PropertyVetoException {
316 		
317 		addDisplayerParameters(new AcopGraphParameters(parameters));
318     }
319 	
320 	protected AcopGraphParameters synchronizeGraphParameters(AcopGraphParameters parameters) {
321 		
322 		Color color = null;
323 		if (parameters.getColor() != null)
324 			color = parameters.getColor();
325 		else
326 			color = colorManager.previewNextColor();
327 			
328 		if (parameters.getDrawStyle() != -1)
329 			getAcop().setDrawStyle(parameters.getDrawStyle());
330 		int graphStyle = getAcop().getDrawStyle();
331 				
332 		if (parameters.getFFT() != -1)
333 			getAcop().setFFT(parameters.getFFT());
334 		int fft = getAcop().getFFT();
335 		
336 		if (parameters.getMode() != -1)
337 			getAcop().setDisplayMode(parameters.getMode());
338 		int mode = getAcop().getDisplayMode();
339 		
340 		int width = parameters.getWidth();
341 		getAcop().setDrawWidth(width);
342 		
343 		ConnectionParameters cp =parameters.getConnectionParameters();
344 		if (parameters.isTrend())
345 			cp = cp.deriveWithPropertySize(1);
346 		return new AcopGraphParameters(new AcopDisplayerParameters(
347 						cp,
348 						parameters.getArrayIndex(), parameters.getMinimum(),
349 						parameters.getMaximum(), parameters.getUnits(),parameters.getFormat()),
350 						color,fft,graphStyle,mode,width,parameters.isTrend(),parameters.getTrendLength(),
351 						parameters.getConverter());
352 	}
353 	
354 
355 	/*
356 	 * (non-Javadoc)
357 	 * @see de.desy.acop.displayers.AcopMultipleDisplayerConnector#getConnectionParameters()
358 	 */
359 	public AcopGraphParameters[] getDisplayerParameters() {
360 		ArrayList<AcopGraphParameters> acopGraphParameters = new ArrayList<AcopGraphParameters>();
361 		for (AcopChartConsumer c : consumersList) {
362 			if (c.getDisplayerParameters() != null) acopGraphParameters.add(c.getDisplayerParameters());
363 		}
364 		AcopGraphParameters[] params = acopGraphParameters.toArray(new AcopGraphParameters[acopGraphParameters.size()]);
365 	    return params;
366     }
367 	
368 	/*
369 	 * (non-Javadoc)
370 	 * @see de.desy.acop.displayers.tools.MultipleAcopDisplayer#getConnectionParameters()
371 	 */
372 	public ConnectionParameters[] getConnectionParameters() {
373 		
374 		AcopGraphParameters[] agp = getDisplayerParameters();
375 		ConnectionParameters[] cp = new ConnectionParameters[agp.length];
376 		for (int i = 0; i < agp.length; i++) {
377 			cp[i] = agp[i].getConnectionParameters();
378 		}
379 		return cp;
380 	}
381 	
382 	/*
383 	 * (non-Javadoc)
384 	 * @see de.desy.acop.displayers.AcopMultipleDisplayerConnector#removeConnectionParameters(de.desy.acop.transport.ConnectionParameters)
385 	 */
386 	public DataSource removeDisplayerParameters(AcopGraphParameters parameters) {
387 		if (parameters == null) return null;
388 		//System.out.println("REM: '"+parameters.toString()+"' "+parameters.hashCode());
389 		AcopGraphParameters[] old = getDisplayerParameters();
390 		AdapterFactory factory = AdapterFactoryService.getInstance().getAdapterFactory();
391 		AcopChartConsumer consumer = null;
392 
393 		for (AcopChartConsumer c : consumersList) {
394 			if (parameters.equals(c.getDisplayerParameters())) {
395 				consumer = c;
396 				break;
397 			}
398 		}
399 		if (consumer == null) return null;
400 		DataSource ds = factory.releaseDataSource(parameters.getConnectionParameters());
401 		if (parameters.getConverter() != null) {
402 			ds.removeConsumer(parameters.getConverter());
403 			parameters.getConverter().removeConsumer(consumer);
404 		} else {
405 			ds.removeConsumer(consumer);
406 		}
407 		releaseConsumer(consumer);
408 
409 		firePropertyChange(MultipleAcopDisplayer.DISPLAYER_PARAMETERS_PROPERTY, old, getDisplayerParameters());
410 	    return ds;
411 	    
412     }
413 	
414 	/*
415 	 * (non-Javadoc)
416 	 * @see de.desy.acop.displayers.tools.MultipleAcopDisplayer#removeConnectionParameters(de.desy.acop.transport.ConnectionParameters)
417 	 */
418 	public DataSource removeConnectionParameters(ConnectionParameters parameters) {
419 		return removeDisplayerParameters(new AcopGraphParameters(parameters, getForeground(),getAcop().getFFT(), getAcop().getDrawStyle(), getAcop().getDisplayMode(), getAcop().getDrawWidth(), false, 100, null));
420 	}
421 	
422 	/*
423 	 * (non-Javadoc)
424 	 * @see de.desy.acop.displayers.AcopMultipleDisplayerConnector#setConnectionParameters(de.desy.acop.transport.ConnectionParameters[])
425 	 */
426 	public void setDisplayerParameters(AcopGraphParameters[] parameters) throws CommonException, PropertyVetoException {
427 		AcopGraphParameters[] old = getDisplayerParameters();
428 		ArrayList<AcopGraphParameters> toBeRemoved = new ArrayList<AcopGraphParameters>();
429 		for (AcopChartConsumer c : consumersList) {
430 			if (c.getDisplayerParameters() != null) toBeRemoved.add(c.getDisplayerParameters());
431 		}
432 		
433 		ArrayList<AcopGraphParameters> toBeAdded = new ArrayList<AcopGraphParameters>();
434 	
435 		for (AcopGraphParameters p : parameters) {
436 			if (toBeRemoved.contains(p)) {
437 				toBeRemoved.remove(p);
438 			} else {
439 				toBeAdded.add(p);
440 			}
441 		}
442 		
443 		for(AcopGraphParameters p : toBeRemoved) {
444 			removeDisplayerParameters(p);
445 		}
446 		
447 		for(AcopGraphParameters p : toBeAdded) {
448 			addDisplayerParameters(p);
449 		}
450 		
451 		firePropertyChange(MultipleAcopDisplayer.DISPLAYER_PARAMETERS_PROPERTY, old, parameters);
452     }
453 	
454 	/*
455 	 * (non-Javadoc)
456 	 * @see de.desy.acop.displayers.tools.MultipleAcopDisplayer#setConnectionParameters(de.desy.acop.transport.ConnectionParameters[])
457 	 */
458 	public void setConnectionParameters(ConnectionParameters[] parameters) throws CommonException, PropertyVetoException {
459 		  
460 		AcopGraphParameters[] agp = new AcopGraphParameters[parameters.length];
461 		for (int i = 0; i < parameters.length; i++) {
462 			agp[i] = new AcopGraphParameters(parameters[i]);
463 		}
464 		setDisplayerParameters(agp);
465 	}
466 		
467 	/**
468 	 * Updates chart's y scale using so the preferred min/max of the given
469 	 * consumer are included in this chart's bounds.
470 	 * 
471 	 * @param consumer the consumer, whose bounds changed
472 	 */
473 	public void updateYScale(AcopChartConsumer consumer) {
474 		if (!consumersList.contains(consumer)) return;
475 		double max;
476 		double min;
477 		if (getConsumers().length == 1) {
478 			max = consumer.getPreferredYMax();
479 			min = consumer.getPreferredYMin();
480 		} else {
481 			max = Math.max(getYRangeMax(), consumer.getPreferredYMax());
482 			min = Math.min(getYRangeMin(), consumer.getPreferredYMin());
483 		}
484 		setYRangeMax(max);
485 		setYRangeMin(min);
486 	}
487 	
488 	protected void updateYScaleOnRemove(AcopChartConsumer consumer) {
489 		if (!(consumer.getPreferredYMax() < getYRangeMax() || consumer.getPreferredYMin() > getYRangeMin())) {
490 			return;
491 		}
492 		if (consumersList.size() == 0) return;
493 		double max = -Double.MAX_VALUE;
494 		double min = Double.MAX_VALUE;
495 		for (AcopChartConsumer c : consumersList) {
496 			max = Math.max(max, c.getPreferredYMax());
497 			min = Math.min(min, c.getPreferredYMin());
498 		}
499 		setYRangeMax(max);
500 		setYRangeMin(min);
501 		
502 	}
503 	
504 	/**
505 	 * Updates chart's x scale using so the preferred min/max of the given
506 	 * consumer are included in this chart's bounds.
507 	 * 
508 	 * @param consumer the consumer, whose bounds changed
509 	 */
510 	public void updateXScale(AcopChartConsumer consumer) {
511 		if (!consumersList.contains(consumer)) return;
512 		double max;
513 		double min;
514 		if (getConsumers().length == 1) {
515 			max = consumer.getPreferredXMax();
516 			min = consumer.getPreferredXMin();
517 		} else {
518 			max = Math.max(getXRangeMax(), consumer.getPreferredXMax());
519 			min = Math.min(getXRangeMin(), consumer.getPreferredXMin());
520 		}
521 		setXRangeMax(max);
522 		setXRangeMin(min);
523 	}
524 	
525 	protected void updateXScaleOnRemove(AcopChartConsumer consumer) {
526 		if (!(consumer.getPreferredXMax() < getXRangeMax() || consumer.getPreferredXMin() > getXRangeMin())) {
527 			return;
528 		}
529 		
530 		if (consumersList.size() == 0) return;
531 		double max = -Double.MAX_VALUE;
532 		double min = Double.MAX_VALUE;
533 		for (AcopChartConsumer c : consumersList) {
534 			max = Math.max(max, c.getPreferredXMax());
535 			min = Math.min(min, c.getPreferredXMin());
536 		}
537 		setXRangeMax(max);
538 		setXRangeMin(min);
539 	}
540 	
541 	/**
542 	 * Enables/disables mouse dragging. Dragging can only be enabled if
543 	 * this component uses CosyTransferHandler.
544 	 * 
545 	 * @param enabled
546 	 */
547 	public void setDragEnabled(boolean enabled) {
548 		if (getTransferHandler() instanceof CosyTransferHandler) {
549 			if (((CosyTransferHandler)getTransferHandler()).isExportEnabled() == enabled) return;
550 			
551 			((CosyTransferHandler)getTransferHandler()).setExportEnabled(enabled, this);
552 		}
553 	}
554 	
555 	/**
556 	 * Returns true if drag is enabled.
557 	 * 
558 	 * @return true if drag is enabled
559 	 */
560 	public boolean isDragEnabled() {
561 		if (getTransferHandler() instanceof CosyTransferHandler) {
562 			return ((CosyTransferHandler)getTransferHandler()).isExportEnabled();
563 		} else {
564 			return getTransferHandler() != null;
565 		}
566 	}
567 	
568 	/**
569 	 * Enable/disable the mouse drop. Drop can only be enabled if this component uses
570 	 * CosyTransferHandler.
571 	 * 
572 	 * @param enabled
573 	 */
574 	public void setDropEnabled(boolean enabled) {
575 		if (getTransferHandler() instanceof CosyTransferHandler) {
576 			if (((CosyTransferHandler)getTransferHandler()).isReceiveEnabled() == enabled) return;
577 			
578 			((CosyTransferHandler)getTransferHandler()).setReceiveEnabled(enabled, this);
579 		}
580 	}
581 	
582 	/**
583 	 * Returns true if drop is enabled.
584 	 * 
585 	 * @return true if drop is enabled
586 	 */
587 	public boolean isDropEnabled() {
588 		if (getTransferHandler() instanceof CosyTransferHandler) {
589 			return ((CosyTransferHandler)getTransferHandler()).isReceiveEnabled();
590 		} else {
591 			return getTransferHandler() != null;
592 		}
593 	}
594 	
595 	/*
596 	 * (non-Javadoc)
597 	 * @see com.cosylab.gui.components.util.PopupManageable#getPopupManager()
598 	 */
599 	public PopupManager getPopupManager()
600 	{
601 		if (popupManager == null) {
602 			popupManager = getAcop().getPopupManager();
603 			for (Action action : popupManager.getActions()) {
604 				if ("Preferences...".equals(action.getValue(Action.NAME))) popupManager.removeAction(action);
605 			}
606 			popupManager.addAction(new AbstractAction("Preferences...") {
607 				
608 				private static final long serialVersionUID = 1L;
609 
610 				public void actionPerformed(ActionEvent e) {
611 					getCustomizer().showDialog();
612 					
613 				}});
614 			popupManager.addAction(new AbstractAction("Legend...") {
615 				
616 				private static final long serialVersionUID = 1L;
617 
618 				public void actionPerformed(ActionEvent e) {
619 					showLegendDialog();
620 					
621 				}});
622 		}
623 
624 		return popupManager;
625 	}
626 	
627 	/**
628 	 * Returns the Customizer instance for this component.
629 	 *
630 	 * @return the Customizer instance for this component.
631 	 */
632 	public AbstractCustomizerPanel getCustomizer()
633 	{
634 		if (customizer == null) {
635 			customizer = AbstractCustomizerPanel.findCustomizer(this);
636 			customizer.setObject(this);
637 		}
638 
639 		return customizer;
640 	}
641 	
642 	/**
643 	 * Enables/disables Preferences item in the popup menu.
644 	 * @param enable
645 	 */
646 	public void setPropertiesPopupEnabled(boolean enable) {
647 		Action[] actions = getPopupManager().getActions();
648 		for (Action a : actions) {
649 			if (a != null && "Preferences...".equals(a.getValue(Action.NAME))) {
650 				a.setEnabled(enable);
651 				return;
652 			}
653 		}
654 	}
655 	
656 	/**
657 	 * Returns true if Preferences item is enabled in the popup menu.
658 	 * @return
659 	 */
660 	public boolean isPropertiesPopupEnabled() {
661 		Action[] actions = getPopupManager().getActions();
662 		for (Action a : actions) {
663 			if (a != null && "Preferences...".equals(a.getValue(Action.NAME))) {
664 				return a.isEnabled();
665 			}
666 		}
667 		return false;
668 	}
669 	
670 	/**
671 	 * Autoscales the vertical and horizontal bounds to optimally include all
672 	 * values on all graphs.
673 	 *
674 	 */
675 	public void autoScale() {
676 		autoScale(isXAutoScale(), isXAutoScale(), isYAutoScale(), isYAutoScale());
677 	}
678 	
679 	protected void autoScale(boolean xmx, boolean xmn, boolean ymx, boolean ymn) {
680 		if (isZoomed || !canAutoScale()) return;
681 		// workaround: Acop doesn't fire property change when range is changed by autoscale
682 		double xMin = getXRangeMin();
683 		double xMax = getXRangeMax();
684 		double yMin = getYRangeMin();
685 		double yMax = getYRangeMax();
686 		getAcop().autoScale(xmx, xmn, ymx, ymn);
687 		if (xMin != getXRangeMin()) firePropertyChange(X_RANGE_MIN, xMin, getXRangeMin());
688 		if (xMax != getXRangeMax()) firePropertyChange(X_RANGE_MAX, xMax, getXRangeMax());
689 		if (yMin != getYRangeMin()) firePropertyChange(Y_RANGE_MIN, yMin, getYRangeMin());
690 		if (yMax != getYRangeMax()) firePropertyChange(Y_RANGE_MAX, yMax, getYRangeMax());
691 	}
692 	
693 	protected boolean canAutoScale() {
694 		for (AcopChartConsumer c : consumersList) {
695 			if (c.isDataDrawn()) return true;
696 		}
697 		return false;
698 	}
699 	
700 	/**
701 	 * Autoscales horizotantal axis. The axis is scaled only once. If the new
702 	 * value is out of bounds, tha axis won't be rescaled automatically.
703 	 *
704 	 */
705 	public void autoScaleXOnce() {
706 		autoScale(true, true, false, false);
707 	}
708 	
709 	/**
710 	 * Autoscales vertical axis. The axis is scaled only once. If the new
711 	 * value is out of bounds, tha axis won't be rescaled automatically.
712 	 *
713 	 */
714 	public void autoScaleYOnce() {
715 		autoScale(false, false, true, true);
716 	}
717 	
718 	/**
719 	 * If Acop is in zoom mode, this method will return it to the initial state.
720 	 *
721 	 */
722 	public void resetZoom() {
723 		getAcop().SetZoom(true, true, true, true, getAcop().getXMax(), getAcop().getXMin(), getAcop().getYMax(), getAcop().getYMin());
724 
725 	}
726 	
727 	/**
728 	 * Returns true if axis trace is visible.
729 	 * 
730 	 * @return true if axis trace is visible
731 	 */
732 	public boolean isAxisTraceVisible() {
733 		return axisTraceVisible;
734 	}
735 	
736 	/**
737 	 * Enables/disables the axis trace
738 	 * @param visible true if axis trace should be visible
739 	 * @see Acop#setCursorMarkerMode(int)
740 	 */
741 	public void setAxisTraceVisible(boolean visible) {
742 		if (visible == axisTraceVisible) return;
743 		axisTraceVisible = visible;
744 		if (visible) getAcop().setCursorMarkerMode((AcopCursorMarkerModeEnum.CrossHair).ordinal());
745 		else getAcop().setCursorMarkerMode((AcopCursorMarkerModeEnum.NoMarker).ordinal());
746 		firePropertyChange(AXIS_TRACE_VISIBLE, !axisTraceVisible, axisTraceVisible);
747 
748 	}
749 	
750 	/**
751 	 * Returns the title of the chart.
752 	 * 
753 	 * @return the title
754 	 */
755 	public String getTitle() {
756 		return getAcop().getCaption();
757 	}
758 	
759 	/**
760 	 * Sets the title for this chart.
761 	 * 
762 	 * @param title new title for the chart
763 	 */
764 	public void setTitle(String title) {
765 		String oldTitle = getTitle();
766 		getAcop().setCaption(title);
767 		firePropertyChange(TITLE, oldTitle, getTitle());
768 	}
769 	
770 	/**
771 	 * Returns true if horizontal axis is automatically scaled to include all values
772 	 * in the chart.
773 	 * 
774 	 * @return the xAutoScale
775 	 */
776 	public boolean isXAutoScale() {
777 		return xAutoScale;
778 	}
779 	
780 	/**
781 	 * Sets the horizontal axis auto scale option.
782 	 * 
783 	 * @param autoScale true if autoscale should be turned on
784 	 */
785 	public void setXAutoScale(boolean autoScale) {
786 		if (autoScale == xAutoScale) return;
787 		xAutoScale = autoScale;
788 		if (autoScale) 
789 			autoScale(isXAutoScale(), isXAutoScale(), isYAutoScale(), isYAutoScale());
790 		firePropertyChange(X_AUTOSCALE, !xAutoScale, xAutoScale);
791 	}
792 	
793 	/**
794 	 * Returns the maximum value for x axis.
795 	 * @return maximum horizontal value that the chart can show
796 	 */
797 	public double getXRangeMax() {
798 		return getAcop().getXMax();
799 	}
800 	/**
801 	 * Sets maximum value for x axis.
802 	 * @param new horizontal maximum 
803 	 */
804 	public void setXRangeMax(double rangeMax) {
805 		if (isZoomed) return;
806 		double oldValue = getXRangeMax();
807 		getAcop().setXMax(rangeMax);
808 		firePropertyChange(X_RANGE_MAX, oldValue, getXRangeMax());
809 	}
810 	/**
811 	 * Returns the minimum value for x axis.
812 	 * @return minimum horizontal value that the chart can show
813 	 */
814 	public double getXRangeMin() {
815 		return getAcop().getXMin();
816 	}
817 	/**
818 	 * Sets the minimum value for x axis.
819 	 * @param rangeMin new horizontal minimum 
820 	 */
821 	public void setXRangeMin(double rangeMin) {
822 		if (isZoomed) return;
823 		double oldValue = getXRangeMin();
824 		getAcop().setXMin(rangeMin);
825 		firePropertyChange(X_RANGE_MIN, oldValue, getXRangeMin());
826 	}
827 	/**
828 	 * Returns true if vertical axis is scaled automatically to include all 
829 	 * values in the chart.
830 	 * 
831 	 * @return true if autoscale is turned on
832 	 */
833 	public boolean isYAutoScale() {
834 		return yAutoScale;
835 	}
836 	/**
837 	 * Enables/disables vertical axis autoscaling.
838 	 * 
839 	 * @param autoScale true is autoscale should be turned on
840 	 */
841 	public void setYAutoScale(boolean autoScale) {
842 		if (autoScale == yAutoScale) return;
843 		yAutoScale = autoScale;
844 		if (autoScale)
845 			autoScale(isXAutoScale(), isXAutoScale(), isYAutoScale(), isYAutoScale());
846 		firePropertyChange(Y_AUTOSCALE, !yAutoScale, yAutoScale);
847 	}
848 	/**
849 	 * Returns true if the vertical axis is in logarithmic scale.
850 	 * 
851 	 * @return true if y axis is logarithmic
852 	 */
853 	public boolean isYLogScale() {
854 		return yLogScale;
855 	}
856 	
857 	/**
858 	 * Sets the logarithmic scale of the vertical axis.
859 	 * 
860 	 * @param logScale true if axis should have logarithmic scale
861 	 */
862 	public void setYLogScale(boolean logScale) {
863 		if (logScale == yLogScale) return;
864 		yLogScale = logScale;
865 		int style = getAcop().getGraphStyle();
866 		if (style == AcopGraphStyleEnum.LinLin.ordinal() || style == AcopGraphStyleEnum.LinLog.ordinal()) {
867 			if (logScale) getAcop().setGraphStyle((AcopGraphStyleEnum.LinLog).ordinal());
868 			else getAcop().setGraphStyle((AcopGraphStyleEnum.LinLin).ordinal());
869 		}
870 		else if (style == AcopGraphStyleEnum.TimeLin.ordinal() || style == AcopGraphStyleEnum.TimeLog.ordinal()) {
871 			if (logScale) getAcop().setGraphStyle((AcopGraphStyleEnum.TimeLog).ordinal());
872 			else getAcop().setGraphStyle((AcopGraphStyleEnum.TimeLin).ordinal());
873 		}
874 		else {
875 			if (logScale) getAcop().setGraphStyle((AcopGraphStyleEnum.LogLog).ordinal());
876 			else getAcop().setGraphStyle((AcopGraphStyleEnum.LogLin).ordinal());
877 		}
878 		firePropertyChange(Y_LOG_SCALE, !yLogScale, yLogScale);
879 	}
880 	
881 	/**
882 	 * Returns the maximum value for y axis.
883 	 * 
884 	 * @return maximum vertical value that the chart can show
885 	 */
886 	public double getYRangeMax() {
887 		return getAcop().getYMax();
888 	}
889 	
890 	/**
891 	 * Sets the maximum value for y axis.
892 	 * 
893 	 * @param new maximum vertical value
894 	 */
895 	public void setYRangeMax(double rangeMax) {
896 		if (isZoomed) return;
897 		double oldValue = getYRangeMax();
898 		getAcop().setYMax(rangeMax);
899 		firePropertyChange(Y_RANGE_MAX, oldValue, getYRangeMax());
900 	}
901 	
902 	/**
903 	 * Returns the minimum value for y axis.
904 	 * @return minimum vertical value that the chart can show
905 	 */
906 	public double getYRangeMin() {
907 		return getAcop().getYMin();
908 	}
909 	/**
910 	 * Sets the minimum value for y axis.
911 	 * @param new minimum vertical value
912 	 */
913 	public void setYRangeMin(double rangeMin) {
914 		if (isZoomed) return;
915 		double oldValue = getYRangeMin();
916 		getAcop().setYMin(rangeMin);
917 		firePropertyChange(Y_RANGE_MIN, oldValue, getYRangeMin());
918 	}
919 	
920 	/**
921 	 * Returns true if the lines are chubby.
922 	 * 
923 	 * @return true if chubby lines are drawn
924 	 */
925 	public boolean isChubbyLines() {
926 		return chubbyLines;
927 	}
928 	
929 	/**
930 	 * Sets chubby lines option. If chubby lines are selected, widths of all plots
931 	 * are doubled.
932 	 * @param chubbyLines true if chubby lines should be drawn
933 	 */
934 	public void setChubbyLines(boolean chubbyLines) {
935 		if (this.chubbyLines == chubbyLines) return;
936 		this.chubbyLines = chubbyLines;
937 		for (AcopChartConsumer c : consumersList) {
938 			c.setChubbyLines(chubbyLines);
939 		}
940 		firePropertyChange(CHUBBY_LINES, !chubbyLines, chubbyLines);
941 	}
942 	
943 	/**
944 	 * Returns display mode of this chart.
945 	 * @return the displayMode
946 	 */
947 	public AcopDisplayMode getDisplayMode() {
948 		return displayMode;
949 	}
950 	
951 	/**
952 	 * Sets the display mode to this chart (plots of all consumers have the same display 
953 	 * mode). If line style is </code>null</code> individual display modes of 
954 	 * consumers are allowed. 
955 	 * @param displayMode the displayMode to set
956 	 */
957 	public void setDisplayMode(AcopDisplayMode displayMode) {
958 		if (displayMode == this.displayMode) return;
959 		AcopDisplayMode oldMode = this.displayMode;
960 		this.displayMode = displayMode;
961 		for (AcopChartConsumer c : consumersList) {
962 			c.setDisplayMode(displayMode);
963 		}
964 		firePropertyChange(LINE_STYLE, oldMode, displayMode);
965 	}
966 	
967 	/**
968 	 * Toggles the best scale. When best scale is on, chart scales
969 	 * the axes according to best visibility of data.
970 	 * @param bestScale
971 	 */
972 	public void setBestScale(boolean bestScale) {
973 		if (this.bestScale == bestScale) return;
974 		this.bestScale = bestScale;
975 		getAcop().setXBestScale(bestScale);
976 		getAcop().setYBestScale(bestScale);
977 		firePropertyChange("bestScale", !bestScale, bestScale);
978 	}
979 	
980 	/**
981 	 * Returns true if best scale is switched on.
982 	 * @return true if best scale is on
983 	 */
984 	public boolean isBestScale() {
985 		return bestScale;
986 	}
987 	
988 	/**
989 	 * Toggles the visibility of grid lines.
990 	 * @param show true if grid is visible
991 	 */
992 	public void setShowGrid(boolean show) {
993 		if (this.showGrid == show) return;
994 		this.showGrid = show;
995 		getAcop().setXGrid(show);
996 		getAcop().setYGrid(show);
997 		firePropertyChange("showGrid", !showGrid, showGrid);
998 	}
999 	
1000 	/**
1001 	 * Returns true if grid lines are visible.
1002 	 * @return true if grid is visible
1003 	 */
1004 	public boolean isShowGrid() {
1005 		return showGrid;
1006 	}
1007 	
1008 	/**
1009 	 * Shows the legend dialog for this chart.
1010 	 *
1011 	 */
1012 	public void showLegendDialog() {
1013 		if (legend == null) {
1014 			legend = new LegendDialog(this);
1015 		}
1016 		legend.setVisible(true);
1017 		legend.toFront();
1018 	}
1019 	
1020 	public static void main(String[] args) {
1021 		RunnerHelper.runComponent(new AcopChartReorg(), 800, 600);
1022 	}
1023 }