View Javadoc

1   package de.desy.acop.video;
2   
3   import java.awt.BorderLayout;
4   import java.awt.Color;
5   import java.awt.Cursor;
6   import java.awt.Dimension;
7   import java.awt.Graphics;
8   import java.awt.Graphics2D;
9   import java.awt.GridBagConstraints;
10  import java.awt.GridBagLayout;
11  import java.awt.Insets;
12  import java.awt.Rectangle;
13  import java.awt.Shape;
14  import java.awt.datatransfer.DataFlavor;
15  import java.awt.datatransfer.Transferable;
16  import java.awt.dnd.DnDConstants;
17  import java.awt.dnd.DragGestureEvent;
18  import java.awt.dnd.DragGestureListener;
19  import java.awt.dnd.DragSource;
20  import java.awt.dnd.DragSourceAdapter;
21  import java.awt.dnd.DropTarget;
22  import java.awt.dnd.DropTargetAdapter;
23  import java.awt.dnd.DropTargetDragEvent;
24  import java.awt.dnd.DropTargetDropEvent;
25  import java.awt.event.ActionEvent;
26  import java.awt.event.MouseEvent;
27  import java.awt.geom.Rectangle2D;
28  import java.beans.PropertyChangeEvent;
29  import java.beans.PropertyChangeListener;
30  import java.beans.PropertyVetoException;
31  import java.io.File;
32  
33  import javax.swing.AbstractAction;
34  import javax.swing.Action;
35  import javax.swing.JComponent;
36  import javax.swing.JFileChooser;
37  import javax.swing.JOptionPane;
38  import javax.swing.JPanel;
39  import javax.swing.JPopupMenu;
40  import javax.swing.JScrollPane;
41  import javax.swing.JViewport;
42  import javax.swing.border.LineBorder;
43  import javax.swing.filechooser.FileNameExtensionFilter;
44  
45  import com.cosylab.gui.components.customizer.AbstractCustomizerPanel;
46  import com.cosylab.gui.components.util.PopupManageable;
47  import com.cosylab.gui.components.util.PopupManager;
48  import com.cosylab.gui.components.util.RunnerHelper;
49  import com.cosylab.util.CommonException;
50  
51  import de.desy.acop.displayers.selector.ConnectionCustomizer;
52  import de.desy.acop.displayers.tools.AcopDisplayerParameters;
53  import de.desy.acop.displayers.tools.AcopDisplayerParametersFlavor;
54  import de.desy.acop.displayers.tools.ConnectionParametersFlavor;
55  import de.desy.acop.displayers.tools.ConnectionParametersReceiver;
56  import de.desy.acop.transport.ConnectionParameters;
57  import de.desy.acop.video.analysis.AImage;
58  import de.desy.acop.video.analysis.AnalysisMode;
59  import de.desy.acop.video.analysis.ImageAnalysisEngine;
60  import de.desy.acop.video.analysis.LuminosityColorDecoder;
61  import de.desy.acop.video.displayer.ColorLookupPanel;
62  import de.desy.acop.video.displayer.ColorMap;
63  import de.desy.acop.video.displayer.ImageCLUT;
64  import de.desy.acop.video.displayer.ImageDisplayer;
65  import de.desy.acop.video.displayer.ImageZoom;
66  import de.desy.acop.video.displayer.OverlayState;
67  import de.desy.acop.video.displayer.TineHandler;
68  import de.desy.acop.video.displayer.TineImageReceiver;
69  import de.desy.tine.types.IMAGE;
70  
71  /**
72   * 
73   * <code>AcopVideo</code> is a an AcopBean which can display a video image. This
74   * bean encapsulates the {@link ImageDisplayer} which handles all the rendering
75   * of the image, while this wrapper handles the common ACOP features. It
76   * implements {@link ConnectionParametersReceiver}, which allows it to use the
77   * common {@link ConnectionCustomizer}. However, in contrary to other AcopBeans
78   * the the AcopVideo connection is not created automatically. Data acquisition
79   * is not started until the {@link #start()} method is called. AcopVideo also
80   * implements other standard ACOP features such as drag and drop (drag and drop
81   * requires holding down of the Control key), common customizer, info dialog
82   * etc.
83   * 
84   * @author <a href="mailto:jaka.bobnar@cosylab.com">Jaka Bobnar</a>
85   * @version $Id: Templates.xml,v 1.10 2004/01/13 16:17:13 jbobnar Exp $
86   * 
87   */
88  public class AcopVideo extends JComponent implements ConnectionParametersReceiver, PopupManageable, TineImageReceiver {
89  
90  	private static final long serialVersionUID = 1L;
91  
92  	public static final String PROPERTY_LIVE_MODE = "liveMode";
93  	public static final String PROPERTY_AIMAGE = "aImage";
94  	public static final String PROPERTY_CAN_CHANGE_SETTINGS = "canChangeSettings";
95  	public static final String PROPERTY_DISPLAY_CLUT_HEADER = "displayClutHeader";
96  	public static final String DISPLAY_STATISTISC = "displayStatistics";
97  
98  	/**
99  	 * ImageDisplayer for image displaying
100 	 */
101 	private ImageDisplayer imageDisplayer;
102 
103 	/**
104 	 * SideProfileDisplayers for horizontal and vertical profile display
105 	 */
106 	private SideProfileDisplayer horizontalDisplayer;
107 	private SideProfileDisplayer verticalDisplayer;
108 
109 	/**
110 	 * NumericDataDisplayer displays image analysis data
111 	 */
112 	private NumericDataDisplayer numericDataDisplayer;
113 
114 	/**
115 	 * The layout of displayer panel
116 	 */
117 	private GridBagLayout layout;
118 
119 	/**
120 	 * The panel for displayers
121 	 */
122 	private JPanel displayerPanel;
123 
124 	/**
125 	 * The panel for Color LookUp Table (CLUT).
126 	 */
127 	private JViewport clutHeader;
128 
129 	/**
130 	 * The selected analysis mode
131 	 */
132 	private AnalysisMode analysisMode = AnalysisMode.NO_ANALYSIS;
133 
134 	/**
135 	 * Can analysis settings be changed?
136 	 */
137 	private boolean canChangeSettings = false;
138 
139 	/**
140 	 * Toggle display numerical analysis values
141 	 */
142 	private boolean displayNumericalValues = false;
143 
144 	/**
145 	 * Toggle display CLUT (Color LookUp Table) panel
146 	 */
147 	private boolean displayClutHeader = false;
148 
149 	/**
150 	 * The weight of display decorations
151 	 */
152 	private double decorationsWeight = 0.2;
153 
154 	/**
155 	 * Takes care of analyzing images
156 	 */
157 	private ImageAnalysisEngine imageAnalysisEngine;
158 
159 	/**
160 	 * TineHandler which is used for data acquisition from the remote point
161 	 */
162 	private TineHandler tineHandler;
163 
164 	/**
165 	 * Currently selected and applied ConnectionParameters
166 	 */
167 	private ConnectionParameters connParams;
168 
169 	/**
170 	 * the customizer for this AcopBean
171 	 */
172 	private AbstractCustomizerPanel customizer;
173 
174 	/**
175 	 * Popup manager
176 	 */
177 	private PopupManager popupManager;
178 
179 	/**
180 	 * ACOP video information dialog
181 	 */
182 	private AcopVideoInfoDialog infoDialog;
183 
184 	/**
185 	 * Bean is live mode state
186 	 */
187 	private boolean isLiveMode;
188 
189 	/**
190 	 * File chooser for saving PNG images
191 	 */
192 	private JFileChooser chooser;
193 
194 	/**
195 	 * Action start/stop from context menu
196 	 */
197 	private Action actionStart, actionStop;
198 
199 	/**
200 	 * Displayer manager that manages the displays
201 	 */
202 	private DisplayerManager displayerManager;
203 
204 	/**
205 	 * The current </code>AImage</code>
206 	 */
207 	private AImage aImage;
208 
209 	/**
210 	 * Determines whether popup is enabled or not
211 	 */
212 	private boolean popupEnabled;
213 
214 	/**
215 	 * Constructs a new AcopVideo.
216 	 * 
217 	 */
218 	public AcopVideo() {
219 		super();
220 		initialize();
221 
222 		Thread th = new Thread(new Runnable() {
223 			public void run() {
224 				if (tineHandler != null) {
225 					tineHandler.closeLink();
226 				}
227 				getImageAnalysisEngine().stopRemoteAnalysis();
228 
229 			}
230 		});
231 		th.setDaemon(false);
232 		Runtime.getRuntime().addShutdownHook(th);
233 		// demo
234 		// Thread th1 = new Thread(new Runnable(){
235 		// public void run() {
236 		// IMAGE splashScreen = new IMAGE(0);
237 		// int c = 0;
238 		// int num = 0;
239 		// while(true) {
240 		// createSplashScreen(splashScreen,c);
241 		// splashScreen.getFrameHeader().frameNumber = num++;
242 		// updateValue(splashScreen);
243 		// c++;
244 		// c = c%10;
245 		// // System.out.println("update " + c);
246 		// try {
247 		// Thread.sleep(1000);
248 		// } catch (Exception e) {
249 		// e.printStackTrace();
250 		// }
251 		// }
252 		// }
253 		//			
254 		// private void createSplashScreen(IMAGE aSplashScreen, int cc) {
255 		// // ImageParser.loadImageFile("g:/temp/Data/Free_"+ cc + ".bmp",
256 		// aSplashScreen);
257 		// ImageParser.loadImageFile("g:/temp/Data/test0.bmp", aSplashScreen);
258 		// // ImageParser.loadImageFile("g:/Bg1.png", aSplashScreen);
259 		// // if (cc % 2 == 0) {
260 		// // ImageParser.loadImageFile("g:/Bg1.png", aSplashScreen);
261 		// // } else {
262 		// // ImageParser.loadImageFile("g:/Bg2.png", aSplashScreen);
263 		// // }
264 		// // aSplashScreen.getFrameHeader().bytesPerPixel=3;
265 		// // aSplashScreen.getFrameHeader().effectiveBitsPerPixel=24;
266 		// }
267 		// });
268 		// th1.start();
269 	}
270 
271 	private void initialize() {
272 		imageDisplayer = new ImageDisplayer() {
273 
274 			private static final long serialVersionUID = 310376L;
275 
276 			/*
277 			 * (non-Javadoc)
278 			 * 
279 			 * @see
280 			 * de.desy.acop.video.displayer.ImageDisplayer#paintComponent(java
281 			 * .awt.Graphics)
282 			 */
283 			@Override
284 			protected void paintComponent(Graphics g) {
285 				super.paintComponent(g);
286 				Graphics2D g2 = (Graphics2D) g;
287 				if (getDisplayerManager().getDraggingRectangle() != null) {
288 					Rectangle2D r = getDisplayerManager().getDraggingRectangle();
289 					if (r != null) {
290 						g2.setPaint(Definitions.getRoiSelectingColor());
291 						g2.draw(r);
292 					}
293 				}
294 				if (getDisplayerManager().isRoiSelected()) {
295 					Rectangle2D r = getDisplayerManager().getScaledSelectedRectangle();
296 					if (r != null) {
297 						getDisplayerManager().updateImageDisplayerSize(getSize());
298 						g2.setPaint(Definitions.getRoiColor());
299 						g2.draw(r);
300 					}
301 				}
302 				if (getDisplayerManager().isThresholdROISelected()) {
303 					Rectangle2D r = getDisplayerManager().getScaledThresholdROIRectangle();
304 					if (r != null) {
305 						getDisplayerManager().updateImageDisplayerSize(getSize());
306 						g2.setPaint(Definitions.getThresholdRoiColor());
307 						g2.draw(r);
308 					}
309 				}
310 				if (getDisplayerManager().isCrossAvailable()) {
311 					Shape c = getDisplayerManager().getCross();
312 					if (c != null) {
313 						g2.setPaint(Definitions.getStatisticsColor());
314 						g2.draw(c);
315 					}
316 				}
317 				if (getDisplayerManager().isCrossFitAvailable()) {
318 					Shape c = getDisplayerManager().getCrossFit();
319 					if (c != null) {
320 						g2.setPaint(Definitions.getFitColor());
321 						g2.draw(c);
322 					}
323 				}
324 			}
325 		};
326 
327 		setPopupEnabled(true);
328 
329 		// imageDisplayer.addPropertyChangeListener(ImageDisplayer.PROPERTY_CLUT,
330 		// new PropertyChangeListener() {
331 		imageDisplayer.addPropertyChangeListener(ImageDisplayer.PROPERTY_COLOR_MAP, new PropertyChangeListener() {
332 			@Override
333 			public void propertyChange(PropertyChangeEvent evt) {
334 				ColorLookupPanel clutPanel = (ColorLookupPanel) getClutHeader().getComponent(0);
335 				clutPanel.setCLUT((ImageCLUT) evt.getNewValue());
336 				clutPanel.repaint();
337 			}
338 		});
339 
340 		// create handler for receiving TINE IMAGE
341 		tineHandler = new TineHandler(this);
342 
343 		// prepare main GUI
344 		setLayout(new BorderLayout());
345 		add(getDisplayerPanel(), BorderLayout.CENTER);
346 		add(getNumericalDataDisplayer(), BorderLayout.SOUTH);
347 
348 		// Add DragAndDrop handler
349 		setDropTarget(new DropTarget(this, new DropTargetAdapter() {
350 
351 			/**
352 			 * Checks the flavors and the action of the given event and returns
353 			 * true if the event is acceptable.
354 			 */
355 			private boolean isAcceptable(DropTargetDragEvent dtde) {
356 				DataFlavor[] f = dtde.getCurrentDataFlavors();
357 
358 				boolean accept = false;
359 
360 				for (int i = 0; i < f.length; i++) {
361 					if (f[i] == AcopVideoParametersFlavor.FLAVOR || f[i] == AcopDisplayerParametersFlavor.FLAVOR
362 							|| f[i] == ConnectionParametersFlavor.FLAVOR || f[i] == DataFlavor.stringFlavor) {
363 						accept = true;
364 
365 						break;
366 					}
367 				}
368 
369 				return (accept & (dtde.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE) != 0);
370 			}
371 
372 			public void dragEnter(DropTargetDragEvent dtde) {
373 				if (!isAcceptable(dtde)) {
374 					dtde.rejectDrag();
375 					return;
376 				}
377 			}
378 
379 			public void dragOver(DropTargetDragEvent dtde) {
380 				if (!isAcceptable(dtde)) {
381 					dtde.rejectDrag();
382 					return;
383 				}
384 			}
385 
386 			public void dropActionChanged(DropTargetDragEvent dtde) {
387 				if (!isAcceptable(dtde)) {
388 					dtde.rejectDrag();
389 					return;
390 				}
391 			}
392 
393 			public void drop(DropTargetDropEvent dtde) {
394 				// accept drop
395 				dtde.acceptDrop(DnDConstants.ACTION_COPY);
396 				Transferable transferable = dtde.getTransferable();
397 				try {
398 					// check the supported data flavors and apply the
399 					// transferred data to this bean
400 					if (transferable.isDataFlavorSupported(AcopVideoParametersFlavor.FLAVOR)) {
401 						AcopVideoParameters p = (AcopVideoParameters) transferable
402 								.getTransferData(AcopVideoParametersFlavor.FLAVOR);
403 
404 						setConnectionParameters(p.getConnectionParameters());
405 						setImageZoom(p.getImageZoom());
406 						setOverlayState(p.getOverlayState());
407 						setAOIZoom(p.isAOIZoom());
408 						setHistogramEqualisation(p.isHistogramEqualisation());
409 						setHistogramMin(p.getHistogramMin());
410 						setHistogramMax(p.getHistogramMax());
411 						setKeepAspectRatio(p.isKeepAspectRatio());
412 
413 						imageDisplayer.setCLUT(new ImageCLUT(p.getColorMap(), imageDisplayer.getCLUT()
414 								.getComponentSize()));
415 
416 					} else if (transferable.isDataFlavorSupported(AcopDisplayerParametersFlavor.FLAVOR)) {
417 						AcopDisplayerParameters p = (AcopDisplayerParameters) transferable
418 								.getTransferData(AcopDisplayerParametersFlavor.FLAVOR);
419 						setConnectionParameters(p.getConnectionParameters());
420 					} else if (transferable.isDataFlavorSupported(ConnectionParametersFlavor.FLAVOR)) {
421 						ConnectionParameters p = (ConnectionParameters) transferable
422 								.getTransferData(ConnectionParametersFlavor.FLAVOR);
423 						setConnectionParameters(p);
424 					} else if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {
425 						String p = (String) transferable.getTransferData(DataFlavor.stringFlavor);
426 						try {
427 							setConnectionParameters(new ConnectionParameters(p));
428 						} catch (RuntimeException e) {
429 							dtde.dropComplete(false);
430 							return;
431 						}
432 					} else {
433 						dtde.dropComplete(false);
434 						return;
435 					}
436 					dtde.dropComplete(true);
437 				} catch (Exception e) {
438 					e.printStackTrace();
439 				}
440 			}
441 
442 		}));
443 
444 		// initialize the image displayer as drag source
445 		DragSource dragSource = DragSource.getDefaultDragSource();
446 		dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE,
447 				new DragGestureListener() {
448 					public void dragGestureRecognized(DragGestureEvent dge) {
449 						if (dge.getTriggerEvent() instanceof MouseEvent) {
450 							if (!((MouseEvent) dge.getTriggerEvent()).isControlDown())
451 								return;
452 						}
453 
454 						AcopVideoParameters p = new AcopVideoParameters( //
455 								getConnectionParameters(), //
456 								imageDisplayer.isAOIZoom(), //
457 								imageDisplayer.getImageZoom(), //
458 								imageDisplayer.getOverlayState(), //
459 								imageDisplayer.isKeepAspectRatio(), //
460 								imageDisplayer.isHistogramEqualisation(), //
461 								imageDisplayer.getHistogramMin(), //
462 								imageDisplayer.getHistogramMax(), //
463 								// _imageDisplayer.getColorMap(), //
464 								imageDisplayer.getCLUT().getColorMap(), //
465 								getClutHeader().isVisible());
466 
467 						Transferable transferable = new AcopVideoTransferable(p);
468 						dge.startDrag(new Cursor(Cursor.MOVE_CURSOR), transferable, new DragSourceAdapter() {
469 						});
470 					}
471 				});
472 
473 	}
474 
475 	private SideProfileDisplayer getHorizontalDisplayer() {
476 		if (horizontalDisplayer == null) {
477 			horizontalDisplayer = new SideProfileDisplayer(true);
478 			horizontalDisplayer.setVisible(isAnalysisEnabled());
479 		}
480 		return horizontalDisplayer;
481 	}
482 
483 	private SideProfileDisplayer getVerticalDisplayer() {
484 		if (verticalDisplayer == null) {
485 			verticalDisplayer = new SideProfileDisplayer(false);
486 			verticalDisplayer.setVisible(isAnalysisEnabled());
487 		}
488 		return verticalDisplayer;
489 	}
490 
491 	private NumericDataDisplayer getNumericalDataDisplayer() {
492 		if (numericDataDisplayer == null) {
493 			numericDataDisplayer = new NumericDataDisplayer();
494 			numericDataDisplayer.setVisible(displayNumericalValues);
495 		}
496 		return numericDataDisplayer;
497 	}
498 
499 	protected DisplayerManager getDisplayerManager() {
500 		if (displayerManager == null) {
501 			displayerManager = new DisplayerManager(getImageAnalysisEngine(), getImageDisplayer(),
502 					getHorizontalDisplayer(), getVerticalDisplayer());
503 		}
504 		return displayerManager;
505 	}
506 
507 	public void setAnalysisServerConnectionParameters(ConnectionParameters param) {
508 		getImageAnalysisEngine().setAnalysisServerConnectionParameters(param);
509 	}
510 
511 	/**
512 	 * Sets the background image to be used by this
513 	 * </code>ImageAnalysisEngine</code>.
514 	 * 
515 	 * @param background
516 	 *            the image to set
517 	 * @deprecated use {@link #setPreciseBackgroundImage(double[], int)}
518 	 */
519 	public synchronized void setBackgroundImage(IMAGE background) {
520 		getImageAnalysisEngine().setBackground(background);
521 	}
522 
523 	/**
524 	 * Gets the background image used by this </code>ImageAnalysisEngine</code>.
525 	 * 
526 	 * @return the background image
527 	 * @deprecated use {@link #getPreciseBackgroundImage()}
528 	 */
529 	public synchronized IMAGE getBackgroundImage() {
530 		return getImageAnalysisEngine().getBackground();
531 	}
532 
533 	/**
534 	 * Sets the region of interest for the analysis.
535 	 * 
536 	 * @param rect
537 	 *            the region of interest rectangle
538 	 */
539 	public void setROI(Rectangle rect) {
540 		getImageAnalysisEngine().setRoi(rect.x, rect.y, rect.width, rect.height);
541 	}
542 
543 	/**
544 	 * Returns the region of interest.
545 	 * 
546 	 * @return the region of interest
547 	 */
548 	public Rectangle getROI() {
549 		return getImageAnalysisEngine().getROI();
550 	}
551 
552 	/**
553 	 * Sets the region of interest used for threshold calculation.
554 	 * 
555 	 * @param rect
556 	 *            the region of interest used for threshold calculation
557 	 */
558 	public void setThresholdROI(Rectangle rect) {
559 		getImageAnalysisEngine().setRoi2(rect.x, rect.y, rect.width, rect.height);
560 	}
561 
562 	/**
563 	 * Returns the region of interest used for threshold calculation.
564 	 * 
565 	 * @return the region of interest used for thereshold calculation
566 	 */
567 	public Rectangle getThresholdROI() {
568 		return getImageAnalysisEngine().getThresholdROI();
569 	}
570 
571 	/**
572 	 * Sets a flag if the threshold should be calculated automatically within
573 	 * the threshold region of interest.
574 	 * 
575 	 * @param automatic
576 	 *            true if threshold is calculated automatically or false
577 	 *            otherwise
578 	 * @see #setThresholdROI(Rectangle)
579 	 */
580 	public void setAutomaticThresholdCalculation(boolean automatic) {
581 		getImageAnalysisEngine().setCalculateThreshold(automatic);
582 	}
583 
584 	/**
585 	 * Returns true if the threshold is calculated automatically within the
586 	 * threshold region of interest.
587 	 * 
588 	 * @return true if threshold is calculated automatically or false otherwise
589 	 */
590 	public boolean isAutomaticThresholdCalculation() {
591 		return getImageAnalysisEngine().isCalculateThreshold();
592 	}
593 
594 	/**
595 	 * Sets the precise background image double array and width of the image.
596 	 * Negative values in the image array are also accepted and will be used
597 	 * during analysis but not for presentation.
598 	 * 
599 	 * @param image
600 	 *            the image double array
601 	 * @param width
602 	 *            the width of the background image
603 	 */
604 	public synchronized void setPreciseBackgroundImage(double[] image, int width) {
605 		getImageAnalysisEngine().setPreciseBackground(image, width);
606 	}
607 
608 	/**
609 	 * Returns the precise background image double array.
610 	 * 
611 	 * @return the background image double array
612 	 */
613 	public synchronized double[] getPreciseBackgroundImage() {
614 		return getImageAnalysisEngine().getPreciseBackground();
615 	}
616 
617 	/**
618 	 * Returns the background image width as set on the analysis engine.
619 	 * 
620 	 * @return the background width
621 	 */
622 	public synchronized int getBackgroundImageWidth() {
623 		return getImageAnalysisEngine().getBackgroundWidth();
624 	}
625 
626 	/**
627 	 * Sets the threshold value, which discards all pixels which have returned
628 	 * by <code>ColorDecoder</code> lover than threshold value. Threshold value
629 	 * has effect only between values Const2D and Amplitude2D as described by
630 	 * <code>AImage</code>.
631 	 * 
632 	 * @see AImage
633 	 * @see AImage#getAmplitude2D()
634 	 * @see AImage#getConst2D()
635 	 * 
636 	 * @param threshold
637 	 *            the threshold value to set
638 	 */
639 	public synchronized void setThreshold(final double threshold) {
640 		ImageAnalysisEngine engine = getImageAnalysisEngine();
641 		if (engine != null) {
642 			engine.setThreshold(threshold);
643 		}
644 	}
645 
646 	/**
647 	 * Gets the threshold value.
648 	 * 
649 	 * @see #setThreshold(double)
650 	 * 
651 	 * @return the threshold value
652 	 */
653 	public synchronized double getThreshold() {
654 		return getImageAnalysisEngine().getThreshold();
655 	}
656 
657 	protected synchronized ImageAnalysisEngine getImageAnalysisEngine() {
658 		if (imageAnalysisEngine == null) {
659 			imageAnalysisEngine = new ImageAnalysisEngine(new LuminosityColorDecoder());
660 			imageAnalysisEngine.addPropertyChangeListener(new PropertyChangeListener() {
661 				@Override
662 				public void propertyChange(PropertyChangeEvent evt) {
663 					String propertyName = evt.getPropertyName();
664 					if (propertyName == ImageAnalysisEngine.PROPERTY_A_IMAGE) {
665 						AImage aImage = (AImage) evt.getNewValue();
666 						setAImage(aImage);
667 					} else if (propertyName == ImageAnalysisEngine.PROPERTY_RESET) {
668 						getImageDisplayer().resetForReceiving();
669 					}
670 				}
671 			});
672 		}
673 		return imageAnalysisEngine;
674 	}
675 
676 	private GridBagLayout getDisplayerPanelLayout() {
677 		if (layout == null) {
678 			layout = new GridBagLayout();
679 		}
680 		return layout;
681 	}
682 
683 	private JPanel getDisplayerPanel() {
684 		if (displayerPanel == null) {
685 			displayerPanel = new JPanel(getDisplayerPanelLayout());
686 			GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER,
687 					GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0);
688 
689 			JScrollPane scroller = new JScrollPane(imageDisplayer);
690 			scroller.setAutoscrolls(true);
691 			scroller.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
692 			scroller.getViewport().addChangeListener(getDisplayerManager().getViewPortListener());
693 			scroller.setColumnHeader(getClutHeader());
694 			displayerPanel.add(scroller, gbc);
695 
696 			gbc.gridy++;
697 			gbc.weighty = decorationsWeight;
698 			gbc.insets = new Insets(0, 1, 0, 1);
699 			getHorizontalDisplayer().setBorder(new LineBorder(Color.BLACK, 1));
700 			displayerPanel.add(getHorizontalDisplayer(), gbc);
701 			gbc.gridy--;
702 			gbc.gridx++;
703 			gbc.weightx = decorationsWeight;
704 			gbc.insets = new Insets(1, 0, 1, 0);
705 			getVerticalDisplayer().setBorder(new LineBorder(Color.BLACK, 1));
706 			displayerPanel.add(getVerticalDisplayer(), gbc);
707 		}
708 		return displayerPanel;
709 	}
710 
711 	private JViewport getClutHeader() {
712 		if (clutHeader == null) {
713 			clutHeader = new JViewport();
714 			clutHeader.add(new ColorLookupPanel(null));
715 			clutHeader.setVisible(displayClutHeader);
716 			clutHeader.setPreferredSize(new Dimension(0, 30));
717 		}
718 		return clutHeader;
719 	}
720 
721 	/**
722 	 * Sets the color map for the image.
723 	 * 
724 	 * @param newValue
725 	 *            - new color map
726 	 */
727 	public void setColorMap(ColorMap newValue) {
728 		ColorMap oldValue = getColorMap();
729 		imageDisplayer.setColorMap(newValue);
730 		firePropertyChange(ImageDisplayer.PROPERTY_COLOR_MAP, oldValue, newValue);
731 	}
732 
733 	/**
734 	 * Returns the color map used to render the image.
735 	 * 
736 	 * @return the applied color map
737 	 * @see ImageDisplayer#getColorMap()
738 	 */
739 	public ColorMap getColorMap() {
740 		return imageDisplayer.getColorMap();
741 	}
742 
743 	/**
744 	 * Sets the image zoom
745 	 * 
746 	 * @param newValue
747 	 */
748 	public void setImageZoom(ImageZoom newValue) {
749 		ImageZoom oldValue = getImageZoom();
750 		imageDisplayer.setImageZoom(newValue);
751 		firePropertyChange(ImageDisplayer.PROPERTY_IMAGE_ZOOM, oldValue, newValue);
752 	}
753 
754 	/**
755 	 * Returns image zoom.
756 	 */
757 	public ImageZoom getImageZoom() {
758 		return imageDisplayer.getImageZoom();
759 	}
760 
761 	/**
762 	 * @param newValue
763 	 *            set overlay mode always on (true) or off(false)
764 	 */
765 	public void setOverlayState(OverlayState newValue) {
766 		OverlayState oldValue = getOverlayState();
767 		imageDisplayer.setOverlayState(newValue);
768 		firePropertyChange(ImageDisplayer.PROPERTY_OVERLAY_STATE, oldValue, newValue);
769 	}
770 
771 	/**
772 	 * Returns overlay information state.
773 	 */
774 	public OverlayState getOverlayState() {
775 		return imageDisplayer.getOverlayState();
776 	}
777 
778 	/**
779 	 * Switches 'histogram equalization' on or off. Get/Set. This method will do
780 	 * nothing if the histogram equalization mode is already in the state about
781 	 * to be set. After new assignment, drawing as well as repainting is
782 	 * enforced to update all involved parts of code so that Histogram
783 	 * Equalization is immediately applied to image.
784 	 * 
785 	 * @param newValue
786 	 *            - switch histogram equalization on (true) or off (false)
787 	 */
788 	public void setHistogramEqualisation(boolean newValue) {
789 		boolean oldValue = isHistogramEqualisation();
790 		imageDisplayer.setHistogramEqualisation(newValue);
791 		firePropertyChange(ImageDisplayer.PROPERTY_HISTOGRAM_EQUALISATION, oldValue, newValue);
792 	}
793 
794 	/**
795 	 * returns whether histogram equalization (normalization) is currently
796 	 * enabled. Get/Set.
797 	 * 
798 	 * @return true - histogram equalization is switched on<br>
799 	 *         false - histogram equalization is switched off
800 	 */
801 	public boolean isHistogramEqualisation() {
802 		return imageDisplayer.isHistogramEqualisation();
803 	}
804 
805 	/** see {@link ImageDisplayer#setHistogramMin(int)} */
806 	public void setHistogramMin(int newValue) {
807 		// System.out.println("AcopVideo.setHistogramMin(" + newValue + ")");
808 		int oldValue = getHistogramMin();
809 		imageDisplayer.setHistogramMin(newValue);
810 		firePropertyChange(ImageDisplayer.PROPERTY_HISTOGRAM_MIN, oldValue, newValue);
811 	}
812 
813 	/** @see {@link ImageDisplayer#getHistogramMin()} */
814 	public int getHistogramMin() {
815 		return imageDisplayer.getHistogramMin();
816 	}
817 
818 	/** see {@link ImageDisplayer#setHistogramMax(int)} */
819 	public void setHistogramMax(int newValue) {
820 		// System.out.println("AcopVideo.setHistogramMax(" + newValue + ")");
821 		int oldValue = getHistogramMax();
822 		imageDisplayer.setHistogramMax(newValue);
823 		firePropertyChange(ImageDisplayer.PROPERTY_HISTOGRAM_MAX, oldValue, newValue);
824 	}
825 
826 	/** @see {@link ImageDisplayer#getHistogramMax()} */
827 	public int getHistogramMax() {
828 		return imageDisplayer.getHistogramMax();
829 	}
830 
831 	/**
832 	 * Switches 'AOI zoom' on or off. Get/Set. This method will do nothing if
833 	 * the AOI zoom mode is already in the state about to be set. Repainting is
834 	 * enforced to have an immediate update.
835 	 * 
836 	 * @param newValue
837 	 *            enable (true) or disable (false) AOI zooming
838 	 */
839 	public void setAOIZoom(boolean newValue) {
840 		boolean oldValue = isAOIZoom();
841 		imageDisplayer.setAOIZoom(newValue);
842 		firePropertyChange(ImageDisplayer.PROPERTY_AOI_ZOOM, oldValue, newValue);
843 	}
844 
845 	/**
846 	 * returns whether out zooming of AOI (no black border around AOI which
847 	 * depicts full frame) is currently enabled. Get/Set.
848 	 * 
849 	 * @return true - AOI Zoom is switched on<br>
850 	 *         false - AOI Zoom is switched off
851 	 */
852 	public boolean isAOIZoom() {
853 		return imageDisplayer.isAOIZoom();
854 	}
855 
856 	/**
857 	 * Switches 'aspect ratio is kept' on or off. Get/Set. This method will do
858 	 * nothing if the aspect ratio mode is already in the state about to be set.
859 	 * Repainting is enforced to have an immediate update.
860 	 * 
861 	 * @param newValue
862 	 *            enable (true) or disable (false) keeping of aspect ratio
863 	 */
864 	public void setKeepAspectRatio(boolean newValue) {
865 		boolean oldValue = isKeepAspectRatio();
866 		imageDisplayer.setKeepAspectRatio(newValue);
867 		firePropertyChange(ImageDisplayer.PROPERTY_KEEP_ASPECT_RATIO, oldValue, newValue);
868 	}
869 
870 	/**
871 	 * returns whether 'aspect ratio is kept' is currently enabled. Get/Set.
872 	 * 
873 	 * @return true - 'aspect ratio is kept' is switched on<br>
874 	 *         false - 'aspect ratio is kept' is switched off
875 	 */
876 	public boolean isKeepAspectRatio() {
877 		return imageDisplayer.isKeepAspectRatio();
878 	}
879 
880 	// mdavid: changed
881 	/**
882 	 * Returns true if this bean is in live mode (ConnectionParameters have been
883 	 * set and start method has been invoked).
884 	 * 
885 	 * @return true if in live mode, false otherwise
886 	 */
887 	public boolean isLiveMode() {
888 		return isLiveMode;
889 	}
890 
891 	/**
892 	 * Sets live mode (only internal used)
893 	 * 
894 	 * @param newValue
895 	 *            - <code>true</code> if in live mode, otherwise
896 	 *            <code>false</code>
897 	 */
898 	private void setLiveMode(boolean newValue) {
899 		boolean oldValue = isLiveMode;
900 		isLiveMode = newValue;
901 		firePropertyChange(PROPERTY_LIVE_MODE, oldValue, newValue);
902 	}
903 
904 	/**
905 	 * Gets the current </code>AImage</code> object for this
906 	 * </code>AcopVideo</code>.
907 	 * 
908 	 * @return the current </code>AImage</code>
909 	 */
910 	public AImage getAImage() {
911 		return aImage;
912 	}
913 
914 	/**
915 	 * Sets an </code>AImage</code> object to this </code>AcopVideo</code>.
916 	 * 
917 	 * @param image
918 	 *            the </code>AImage</code> to set
919 	 */
920 	private void setAImage(AImage image) {
921 		if (this.aImage == image)
922 			return;
923 		AImage old = aImage;
924 		aImage = image;
925 		getDisplayerManager().updateAImage(aImage);
926 		if (aImage == null)
927 			getNumericalDataDisplayer().clearData();
928 		else
929 			getNumericalDataDisplayer().setData(aImage.getImageW(), aImage.getImageH(), aImage.getRoiX(),
930 					aImage.getRoiY(), aImage.getRoiW(), aImage.getRoiH(), aImage.getThreshold(), aImage.getMeanX(),
931 					aImage.getStdX(), aImage.getSideViewAmplitudeX(), aImage.getSideViewConstX(), aImage.getMeanXFit(),
932 					aImage.getStdXFit(), aImage.getAmplitudeXFit(), aImage.getConstXFit(), aImage.getSlopeXFit(),
933 					aImage.getMeanY(), aImage.getStdY(), aImage.getSideViewAmplitudeY(), aImage.getSideViewConstY(),
934 					aImage.getMeanYFit(), aImage.getStdYFit(), aImage.getAmplitudeYFit(), aImage.getConstYFit(),
935 					aImage.getSlopeYFit(), aImage.getStdA(), aImage.getStdB(), aImage.getAngle2D(),
936 					aImage.getAmplitude2D(), aImage.getConst2D());
937 		firePropertyChange(PROPERTY_AIMAGE, old, aImage);
938 	}
939 
940 	/**
941 	 * Gets layout weight for decorations.
942 	 * 
943 	 * @return the layout weight for decorations
944 	 */
945 	public double getDecorationsWeight() {
946 		return decorationsWeight;
947 	}
948 
949 	/**
950 	 * Sets layout weight for decorations.
951 	 * 
952 	 * @param decorationsWeight
953 	 *            the layout weight for decorations to set
954 	 */
955 	public void setDecorationsWeight(double decorationsWeight) {
956 		if (this.decorationsWeight == decorationsWeight)
957 			return;
958 		this.decorationsWeight = decorationsWeight;
959 		GridBagConstraints gbc = getDisplayerPanelLayout().getConstraints(getHorizontalDisplayer());
960 		gbc.weighty = decorationsWeight;
961 		getDisplayerPanelLayout().setConstraints(getHorizontalDisplayer(), gbc);
962 		gbc = getDisplayerPanelLayout().getConstraints(getVerticalDisplayer());
963 		gbc.weightx = decorationsWeight;
964 		getDisplayerPanelLayout().setConstraints(getVerticalDisplayer(), gbc);
965 		getDisplayerPanelLayout().layoutContainer(getDisplayerPanel());
966 	}
967 
968 	/**
969 	 * Gets the </code>AnalysisMode</code> for this </code>AcopVideo</code>.
970 	 * 
971 	 * @return the selected </code>AnalysisMode</code>
972 	 */
973 	public AnalysisMode getAnalysisMode() {
974 		return analysisMode;
975 	}
976 
977 	/**
978 	 * Sets the </code>AnalysisMode</code> for this </code>AcopVideo</code>.
979 	 * 
980 	 * @param analysisMode
981 	 *            the the </code>AnalysisMode</code>
982 	 */
983 	public void setAnalysisMode(AnalysisMode analysisMode) {
984 		if (this.analysisMode == analysisMode)
985 			return;
986 		this.analysisMode = analysisMode;
987 		getHorizontalDisplayer().setVisible(isAnalysisEnabled());
988 		getVerticalDisplayer().setVisible(isAnalysisEnabled());
989 		setAImage(null);
990 		boolean local = analysisMode != AnalysisMode.REMOTE_ANALYSIS;
991 		getImageAnalysisEngine().setLocalAnalysisMode(local);
992 		setCanChangeSettings(analysisMode == AnalysisMode.LOCAL_ANALYSIS);
993 	}
994 
995 	/**
996 	 * Sets the flag whether fitting should be performed during the analysis.
997 	 * 
998 	 * @param perform
999 	 *            true to perform fit or false otherwise
1000 	 */
1001 	public void setPerformFit(boolean perform) {
1002 		if (getImageAnalysisEngine().isPerformFit())
1003 			return;
1004 		getImageAnalysisEngine().setPerformFit(perform);
1005 		firePropertyChange("performFit", !perform, perform);
1006 	}
1007 
1008 	/**
1009 	 * Returns true if fitting is performed or false otherwise. Fitting is
1010 	 * performed only if analysis is turned on (local or remote).
1011 	 * 
1012 	 * @return true if fitting is performed or false otherwise
1013 	 */
1014 	public boolean isPerformFit() {
1015 		return getImageAnalysisEngine().isPerformFit();
1016 	}
1017 
1018 	/**
1019 	 * Returns true if statistics are displayed or false otherwise.
1020 	 * 
1021 	 * @return true if statistics are displayed in the sideview or false if not
1022 	 */
1023 	public boolean isDisplayStatistics() {
1024 		return getDisplayerManager().isDisplayStatistics();
1025 	}
1026 
1027 	/**
1028 	 * Sets the flag whether the statistics are displayed on the canvas or not.
1029 	 * 
1030 	 * @param display
1031 	 *            true if statistics are displayed or false otherwise
1032 	 */
1033 	public void setDisplayStatistics(boolean display) {
1034 		// always fire an event
1035 		getDisplayerManager().setDisplayStatistics(display);
1036 		firePropertyChange(DISPLAY_STATISTISC, !display, display);
1037 		repaint();
1038 	}
1039 
1040 	private boolean isAnalysisEnabled() {
1041 		if (analysisMode == AnalysisMode.LOCAL_ANALYSIS || analysisMode == AnalysisMode.REMOTE_ANALYSIS)
1042 			return true;
1043 		return false;
1044 	}
1045 
1046 	/**
1047 	 * Checks if analysis settings can be changed.
1048 	 * 
1049 	 * @return </code>true</code> if analysis settings can be changed and
1050 	 *         </code>false</code> otherwise
1051 	 */
1052 	public boolean isCanChangeSettings() {
1053 		return canChangeSettings;
1054 	}
1055 
1056 	/**
1057 	 * Sets if analysis settings can be changed.
1058 	 * 
1059 	 * @param canChange
1060 	 *            if </code>true</code> analysis settings can be changed and
1061 	 *            </code>false</code> if not
1062 	 */
1063 	public void setCanChangeSettings(boolean canChange) {
1064 		if (canChangeSettings == canChange)
1065 			return;
1066 		canChangeSettings = canChange;
1067 		firePropertyChange(PROPERTY_CAN_CHANGE_SETTINGS, !canChange, canChange);
1068 		getDisplayerManager().setCanChangeSettings(canChange);
1069 	}
1070 
1071 	/**
1072 	 * Gets the display numerical values option.
1073 	 * 
1074 	 * @return </code>true</code> if numerical values are displayed and
1075 	 *         </code>false</code> if they are not
1076 	 */
1077 	public boolean isDisplayNumericalValues() {
1078 		return displayNumericalValues;
1079 	}
1080 
1081 	/**
1082 	 * Sets the display numerical values option.
1083 	 * 
1084 	 * @param displayNumericalValues
1085 	 *            if </code>true</code> numerical values are displayed and if
1086 	 *            </code>false</code> they are not
1087 	 */
1088 	public void setDisplayNumericalValues(boolean displayNumericalValues) {
1089 		this.displayNumericalValues = displayNumericalValues;
1090 		getNumericalDataDisplayer().setVisible(displayNumericalValues);
1091 	}
1092 
1093 	/**
1094 	 * Returns the display CLUT panel option.
1095 	 * 
1096 	 * @return </code>true</code> if CLUT panel is visible, otherwise
1097 	 *         </code>false</code>
1098 	 */
1099 	public boolean isDisplayClutHeader() {
1100 		return displayClutHeader;
1101 	}
1102 
1103 	/**
1104 	 * Makes the CLUT panel visible or invisible.
1105 	 * 
1106 	 * @param displayClutHeader
1107 	 *            - true to make the CLUT panel visible; false to make it
1108 	 *            invisible
1109 	 */
1110 	public void setDisplayClutHeader(boolean displayClutHeader) {
1111 		if (this.displayClutHeader == displayClutHeader)
1112 			return;
1113 		boolean oldValue = this.displayClutHeader;
1114 		this.displayClutHeader = displayClutHeader;
1115 		getClutHeader().setVisible(displayClutHeader);
1116 		firePropertyChange(PROPERTY_DISPLAY_CLUT_HEADER, oldValue, displayClutHeader);
1117 	}
1118 
1119 	/**
1120 	 * Finds the customizer for this bean.
1121 	 * 
1122 	 * @return the customized
1123 	 */
1124 	public AbstractCustomizerPanel getCustomizer() {
1125 		if (customizer == null)
1126 			customizer = AbstractCustomizerPanel.findCustomizer(this);
1127 		return customizer;
1128 	}
1129 
1130 	/*
1131 	 * (non-Javadoc)
1132 	 * 
1133 	 * @see com.cosylab.gui.components.util.PopupManageable#getPopupManager()
1134 	 */
1135 	public PopupManager getPopupManager() {
1136 		if (popupManager == null) {
1137 			popupManager = new PopupManager(this, true);
1138 			popupManager.addAction(new AbstractAction("Preferences...") {
1139 				private static final long serialVersionUID = 1L;
1140 
1141 				public void actionPerformed(ActionEvent e) {
1142 					getCustomizer().showDialog();
1143 				}
1144 			});
1145 
1146 			popupManager.addAction(new AbstractAction("Capture screen...") {
1147 				private static final long serialVersionUID = 1L;
1148 
1149 				public void actionPerformed(ActionEvent e) {
1150 					saveAsPNG();
1151 				}
1152 			});
1153 
1154 			popupManager.addAction(new AbstractAction("Info...") {
1155 				private static final long serialVersionUID = 310376L;
1156 
1157 				public void actionPerformed(ActionEvent e) {
1158 					getInfoDialog().setVisible(true);
1159 					getInfoDialog().toFront();
1160 				}
1161 			});
1162 
1163 			actionStart = new AbstractAction("Start") {
1164 				private static final long serialVersionUID = 310376L;
1165 
1166 				public void actionPerformed(ActionEvent e) {
1167 					start();
1168 				}
1169 			};
1170 			actionStart.setEnabled(false); // mdavid: added
1171 			popupManager.addAction(actionStart);
1172 
1173 			actionStop = new AbstractAction("Stop") {
1174 				private static final long serialVersionUID = 1310376L;
1175 
1176 				public void actionPerformed(ActionEvent e) {
1177 					stop();
1178 				}
1179 			};
1180 			actionStop.setEnabled(false); // mdavid: added
1181 			popupManager.addAction(actionStop);
1182 
1183 			// disable this, in order for popup to be drawn on the awt canvas
1184 			JPopupMenu.setDefaultLightWeightPopupEnabled(false);
1185 		}
1186 		return popupManager;
1187 	}
1188 
1189 	/**
1190 	 * Saves TINE Image to PNG (in "archival" mode)
1191 	 */
1192 	public void saveAsPNG() {
1193 		JFileChooser chooser = getPngFileChooser("Save As");
1194 		int returnVal = chooser.showSaveDialog(this);
1195 		if (returnVal == JFileChooser.APPROVE_OPTION) {
1196 			String fileNamePath = chooser.getSelectedFile().getAbsolutePath();
1197 			if (!fileNamePath.toLowerCase().endsWith(".png")) {
1198 				int index = fileNamePath.lastIndexOf('.');
1199 				if (index >= 0) {
1200 					fileNamePath = fileNamePath.substring(0, index) + ".png";
1201 				} else {
1202 					fileNamePath = fileNamePath + ".png";
1203 				}
1204 			}
1205 			if (!imageDisplayer.saveAsPNGImage(fileNamePath))
1206 				JOptionPane.showMessageDialog(this, "Error saving image.", "Error", JOptionPane.ERROR_MESSAGE);
1207 		}
1208 	}
1209 
1210 	/**
1211 	 * Exports TINE Image to PNG (in "export" mode)
1212 	 */
1213 	public void exportToPNG() {
1214 		JFileChooser chooser = getPngFileChooser("Export");
1215 		int returnVal = chooser.showSaveDialog(this);
1216 		if (returnVal == JFileChooser.APPROVE_OPTION) {
1217 			String fileNamePath = chooser.getSelectedFile().getAbsolutePath();
1218 			if (!fileNamePath.toLowerCase().endsWith(".png")) {
1219 				int index = fileNamePath.lastIndexOf('.');
1220 				if (index >= 0)
1221 					fileNamePath = fileNamePath.substring(0, index) + ".png";
1222 				else
1223 					fileNamePath = fileNamePath + ".png";
1224 			}
1225 			if (!imageDisplayer.exportToPNG(fileNamePath))
1226 				JOptionPane.showMessageDialog(this, "Error saving image.", "Error", JOptionPane.ERROR_MESSAGE);
1227 		}
1228 	}
1229 
1230 	private JFileChooser getPngFileChooser(String dialogTitle) {
1231 		if (chooser == null) {
1232 			chooser = new JFileChooser(new File("."));
1233 			String[] fileType = { "png" };
1234 			FileNameExtensionFilter PNGfilter = new FileNameExtensionFilter("PNG files", fileType);
1235 			chooser.addChoosableFileFilter(PNGfilter);
1236 		}
1237 		chooser.setDialogTitle(dialogTitle);
1238 		return chooser;
1239 	}
1240 
1241 	private AcopVideoInfoDialog getInfoDialog() {
1242 		if (infoDialog == null) {
1243 			infoDialog = new AcopVideoInfoDialog(this);
1244 		}
1245 		return infoDialog;
1246 	}
1247 
1248 	/*
1249 	 * (non-Javadoc)
1250 	 * 
1251 	 * @seede.desy.acop.displayers.tools.ConnectionParametersReceiver#
1252 	 * getConnectionParameters()
1253 	 */
1254 	public ConnectionParameters getConnectionParameters() {
1255 		return connParams;
1256 	}
1257 
1258 	/*
1259 	 * (non-Javadoc)
1260 	 * 
1261 	 * @seede.desy.acop.displayers.tools.ConnectionParametersReceiver#
1262 	 * setConnectionParameters(de.desy.acop.transport.ConnectionParameters)
1263 	 */
1264 	public void setConnectionParameters(ConnectionParameters cp) throws CommonException, PropertyVetoException {
1265 
1266 		if (connParams == null) {
1267 			if (cp == null)
1268 				return;
1269 		} else if (connParams.equals(cp))
1270 			return;
1271 
1272 		ConnectionParameters oldValue = this.connParams;
1273 		connParams = cp;
1274 		stop();
1275 
1276 		firePropertyChange(CONNECTION_PARAMETERS_PROPERTY, oldValue, connParams);
1277 	}
1278 
1279 	//
1280 	// /**
1281 	// * Construct the remote name from the given parameters to be handled by
1282 	// the {@link TineHandler}.
1283 	// *
1284 	// * @param p
1285 	// * source parameters
1286 	// * @return context/server/device
1287 	// */
1288 	// private String getRemoteName(ConnectionParameters p) {
1289 	// if (p == null)
1290 	// return null;
1291 	// String remoteName = "/" + p.getDeviceContext() + "/" + p.getDeviceGroup()
1292 	// + "/";
1293 	// if (p.getDeviceName() != null && p.getDeviceName().length() != 0) {
1294 	// remoteName += p.getDeviceName();
1295 	// }
1296 	// return remoteName;
1297 	// }
1298 
1299 	/**
1300 	 * Starts data acquisition. Due to high duty task of rendering, the
1301 	 * AcopVideo will display image only after this method is invoked.
1302 	 * 
1303 	 * @see ImageDisplayer#setLiveTransfer(boolean)
1304 	 * @see TineHandler#openLink(String, de.desy.acop.transport.AccessMode, int)
1305 	 */
1306 	public void start() {
1307 		if (connParams == null)
1308 			return;
1309 
1310 		setEnabled(false);
1311 		// getCustomizer().closeDialog();
1312 		new Thread(new Runnable() {
1313 			public void run() {
1314 				try {
1315 					if (analysisMode == AnalysisMode.REMOTE_ANALYSIS) {
1316 						getImageAnalysisEngine().setAnalysisServerConnectionParameters(connParams);
1317 						getImageAnalysisEngine().startRemoteAnalysis();
1318 
1319 					} else
1320 						tineHandler.openLink(connParams);
1321 
1322 					imageDisplayer.setLiveTransfer(true);
1323 					setLiveMode(true);
1324 
1325 				} catch (Exception e) {
1326 					System.out.println(e);
1327 					JOptionPane.showMessageDialog(AcopVideo.this, "Error connecting to the address '"
1328 							+ connParams.getRemoteName() + "'.\n" + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
1329 				} finally {
1330 					setEnabled(true);
1331 				}
1332 			}
1333 
1334 		}).start();
1335 	}
1336 
1337 	/*
1338 	 * (non-Javadoc)
1339 	 * 
1340 	 * @see javax.swing.JComponent#setEnabled(boolean)
1341 	 */
1342 	@Override
1343 	public void setEnabled(boolean enabled) {
1344 		super.setEnabled(enabled);
1345 		actionStart.setEnabled(!isLiveMode); // mdavid: added
1346 		actionStop.setEnabled(isLiveMode); // mdavid: added
1347 	}
1348 
1349 	/**
1350 	 * Stops data acquisition and image rendering.
1351 	 * 
1352 	 * @see ImageDisplayer#setLiveTransfer(boolean)
1353 	 * @see TineHandler#closeLink()
1354 	 */
1355 	public void stop() {
1356 		getImageAnalysisEngine().stopRemoteAnalysis();
1357 		tineHandler.closeLink();
1358 		imageDisplayer.setLiveTransfer(false);
1359 		setLiveMode(false);
1360 		setEnabled(false);
1361 	}
1362 
1363 	public static void main(String[] args) {
1364 		AcopVideo v = new AcopVideo();
1365 		RunnerHelper.runComponent(v, 500, 500);
1366 
1367 		// TODO why is this needed?
1368 		// ImageIO.write((BufferedImage) v._imageDisplayer.getSnapshotImage(),
1369 		// "PNG", new java.io.File("C:/test.png"));
1370 	}
1371 
1372 	public ImageDisplayer getImageDisplayer() {
1373 		return imageDisplayer;
1374 	}
1375 
1376 	@Override
1377 	public void updateValue(IMAGE newImage) {
1378 		if (analysisMode == AnalysisMode.LOCAL_ANALYSIS) {
1379 			getImageAnalysisEngine().updateValue(newImage);
1380 		} else if (analysisMode == AnalysisMode.NO_ANALYSIS) {
1381 			imageDisplayer.updateValue(newImage);
1382 		}
1383 	}
1384 
1385 	@Override
1386 	public void resetForReceiving() {
1387 		if (analysisMode == AnalysisMode.LOCAL_ANALYSIS) {
1388 			getImageAnalysisEngine().resetForReceiving();
1389 		} else if (analysisMode == AnalysisMode.NO_ANALYSIS) {
1390 			imageDisplayer.resetForReceiving();
1391 		}
1392 	}
1393 
1394 	/**
1395 	 * Return true if the popup menu is enabled, otherwise false.
1396 	 * 
1397 	 * @return true if popup is enabled
1398 	 */
1399 	public boolean isPopupEnabled() {
1400 		return popupEnabled;
1401 	}
1402 
1403 	/**
1404 	 * Enables or disables the popup menu.
1405 	 * 
1406 	 * @param enabled
1407 	 *            true if enable or false if disabled
1408 	 */
1409 	public void setPopupEnabled(boolean enabled) {
1410 		if (popupEnabled == enabled)
1411 			return;
1412 		popupEnabled = enabled;
1413 		if (enabled) {
1414 			getImageDisplayer().addMouseListener(getPopupManager().getMouseHook());
1415 		} else {
1416 			getImageDisplayer().removeMouseListener(getPopupManager().getMouseHook());
1417 		}
1418 		firePropertyChange("popupEnabled", !popupEnabled, popupEnabled);
1419 	}
1420 }