View Javadoc

1   package de.desy.acop.video;
2   
3   import java.awt.BorderLayout;
4   import java.awt.Component;
5   import java.awt.Desktop;
6   import java.awt.Dimension;
7   import java.awt.GridBagConstraints;
8   import java.awt.GridBagLayout;
9   import java.awt.Insets;
10  import java.awt.Window;
11  import java.awt.event.ActionEvent;
12  import java.awt.event.ActionListener;
13  import java.awt.event.FocusEvent;
14  import java.awt.event.FocusListener;
15  import java.awt.event.ItemEvent;
16  import java.awt.event.ItemListener;
17  import java.awt.event.WindowListener;
18  import java.beans.PropertyChangeEvent;
19  import java.beans.PropertyChangeListener;
20  import java.net.URI;
21  import java.net.URL;
22  
23  import javax.swing.BorderFactory;
24  import javax.swing.GroupLayout;
25  import javax.swing.ImageIcon;
26  import javax.swing.JButton;
27  import javax.swing.JCheckBox;
28  import javax.swing.JComboBox;
29  import javax.swing.JFrame;
30  import javax.swing.JLabel;
31  import javax.swing.JOptionPane;
32  import javax.swing.JPanel;
33  import javax.swing.JTextField;
34  import javax.swing.SwingConstants;
35  import javax.swing.SwingUtilities;
36  import javax.swing.SwingWorker;
37  import javax.swing.UIManager;
38  import javax.swing.GroupLayout.Alignment;
39  import javax.swing.GroupLayout.ParallelGroup;
40  import javax.swing.border.BevelBorder;
41  
42  import com.cosylab.gui.components.customizer.AbstractCustomizerPanel;
43  import com.cosylab.gui.components.numberfield.AbstractNumberDocument;
44  import com.cosylab.gui.components.util.IconHelper;
45  import com.cosylab.gui.property.editors.IntegerEditor;
46  
47  import de.desy.acop.transport.AccessMode;
48  import de.desy.acop.transport.ConnectionParameters;
49  import de.desy.acop.video.displayer.ColorMap;
50  import de.desy.acop.video.displayer.ImageCLUT;
51  import de.desy.acop.video.displayer.ImageDisplayer;
52  import de.desy.acop.video.displayer.ImageZoom;
53  import de.desy.acop.video.displayer.OverlayState;
54  import de.desy.tine.queryUtils.TPropertyQuery;
55  import de.desy.tine.queryUtils.TQuery;
56  import de.desy.tine.startup.TInitializerFactory;
57  
58  /**
59   * 
60   * <code>AcopVideoApplication</code> is a demo application of the AcopVideo. It
61   * encapsulates the AcopVideo and offers a simple GUI for manipulation of the
62   * AcopVideo. It allows setting of all its properties and setting up a
63   * connection to a remote TINE point.
64   * 
65   * @author <a href="mailto:jaka.bobnar@cosylab.com">Jaka Bobnar</a>
66   * @author <a href="mailto:david.melkumyan@desy.de">David Melkumyan</a>
67   * @version $Id: Templates.xml,v 1.10 2004/01/13 16:17:13 jbobnar Exp $
68   * 
69   */
70  public class AcopVideoApplication extends JPanel {
71  
72  	private static final long serialVersionUID = 1L;
73  
74  	/**
75  	 * the main control panel with all the buttons and selectors
76  	 */
77  	private JPanel controlPanel;
78  
79  	/**
80  	 * the color map chooser
81  	 */
82  	private JComboBox colorMapBox;
83  
84  	/**
85  	 * the color map chooser
86  	 */
87  	private JComboBox imageZoomBox;
88  
89  	/**
90  	 * Overlay panel
91  	 */
92  	private JComboBox overlayStateBox;
93  
94  	/**
95  	 * AOIZoom switch
96  	 */
97  	private JCheckBox aoiZoomCheckBox;
98  
99  	/**
100 	 * keepAspectRatio switch
101 	 */
102 	private JCheckBox aspectRatioCheckBox;
103 
104 	/**
105 	 * histogramEqualisation switch
106 	 */
107 	private JCheckBox normalizationCheckBox;
108 
109 	/**
110 	 * histogram min text field
111 	 */
112 	private IntegerEditor histogramMinTF;
113 
114 	/**
115 	 * histogram min text field
116 	 */
117 	private IntegerEditor histogramMaxTF;
118 
119 	/**
120 	 * address field describe the remote connection point
121 	 */
122 	private JTextField addressTF;
123 
124 	/**
125 	 * address button opens the connection customizer
126 	 */
127 	private JButton browseButton;
128 
129 	/**
130 	 * starts the data acquisition
131 	 */
132 	private JButton startButton;
133 
134 	/**
135 	 * stops the data acquisition
136 	 */
137 	private JButton stopButton;
138 
139 	/**
140 	 * saves current image as PNG
141 	 */
142 	private JButton saveAsPNGButton;
143 
144 	/**
145 	 * exports current image to PNG
146 	 */
147 	private JButton exportToPngButton;
148 
149 	/**
150 	 * opens the help page
151 	 */
152 	private JButton helpButton;
153 
154 	/**
155 	 * the image displayer
156 	 */
157 	private AcopVideo acopVideo;
158 
159 	/**
160 	 * address text field focus listener
161 	 */
162 	private WindowListener addressFocusListener;
163 
164 	/**
165 	 * 
166 	 */
167 	private Window lastWindow;
168 
169 	/**
170 	 * file chooser for image saving
171 	 */
172 	private boolean isLiveMode;
173 
174 	private boolean changingAOIZoom;
175 	private boolean changingAspectRatio;
176 	private boolean changingNormalization;
177 	private boolean changingHistogramMin;
178 	private boolean changingHistogramMax;
179 	private boolean changingImageZoom;
180 	private boolean changingOverlayState;
181 	private boolean changingColorMap;
182 	private boolean changingLiveMode;
183 	private boolean changingDisplayClutHeader;
184 	private boolean changingAddress;
185 
186 	/**
187 	 * Constructs a new AcopVideoApplication.
188 	 * 
189 	 */
190 	public AcopVideoApplication() {
191 		super();
192 		initialize();
193 	}
194 
195 	private void initialize() {
196 		this.setLayout(new BorderLayout());
197 
198 		acopVideo = new AcopVideo();
199 		acopVideo.addPropertyChangeListener(new PropertyChangeListener() {
200 			public void propertyChange(PropertyChangeEvent e) {
201 				// System.out.println("[acopVideo]: " + e.getPropertyName() +
202 				// " changed from '" + e.getOldValue()
203 				// + "' to '" + e.getNewValue() + "'");
204 				acopVideoPropertyChanged(e.getPropertyName());
205 			}
206 		});
207 
208 		acopVideo.getImageDisplayer().addPropertyChangeListener(new PropertyChangeListener() {
209 			public void propertyChange(PropertyChangeEvent e) {
210 				// System.out.println("[imageDisplayer]: " + e.getPropertyName()
211 				// + " changed from '" + e.getOldValue()
212 				// + "' to '" + e.getNewValue() + "'");
213 				imageDisplayerPropertyChanged(e.getPropertyName());
214 			}
215 		});
216 
217 		this.add(acopVideo, BorderLayout.CENTER);
218 		this.add(getControlPanel(), BorderLayout.SOUTH);
219 		getControlPanel().addMouseListener(acopVideo.getPopupManager().getMouseHook());
220 	}
221 
222 	/**
223 	 * ImageDisplayer property changed
224 	 * 
225 	 * @author mdavid
226 	 * 
227 	 * @param propertyChanged
228 	 */
229 	private void imageDisplayerPropertyChanged(String propertyChanged) {
230 		if (propertyChanged.equals(ImageDisplayer.PROPERTY_AOI_ZOOM) && !changingAOIZoom) {
231 			changingAOIZoom = true;
232 			aoiZoomCheckBox.setSelected(acopVideo.isAOIZoom());
233 			changingAOIZoom = false;
234 
235 		} else if (propertyChanged.equals(ImageDisplayer.PROPERTY_KEEP_ASPECT_RATIO) && !changingAspectRatio) {
236 			changingAspectRatio = true;
237 			aspectRatioCheckBox.setSelected(acopVideo.isKeepAspectRatio());
238 			changingAspectRatio = false;
239 
240 		} else if (propertyChanged.equals(ImageDisplayer.PROPERTY_HISTOGRAM_EQUALISATION) && !changingNormalization) {
241 			changingNormalization = true;
242 			normalizationCheckBox.setSelected(acopVideo.isHistogramEqualisation());
243 			changingNormalization = false;
244 
245 		} else if (propertyChanged.equals(ImageDisplayer.PROPERTY_HISTOGRAM_MIN) && !changingHistogramMin) {
246 			// printMinMax("before min changed");
247 			changingHistogramMin = true;
248 			int value = acopVideo.getHistogramMin();
249 			histogramMinTF.setValue(value);
250 			changingHistogramMin = false;
251 			// printMinMax("after min changed");
252 
253 		} else if (propertyChanged.equals(ImageDisplayer.PROPERTY_HISTOGRAM_MAX) && !changingHistogramMax) {
254 			// printMinMax("before max changed");
255 			changingHistogramMax = true;
256 			int value = acopVideo.getHistogramMax();
257 			histogramMaxTF.setValue(value);
258 			changingHistogramMax = false;
259 			// printMinMax("after max changed");
260 
261 		} else if (propertyChanged.equals(ImageDisplayer.PROPERTY_COLOR_MAP) && !changingColorMap) {
262 			// printMinMax("before colorMap changed");
263 			changingColorMap = true;
264 			ImageCLUT clut = acopVideo.getImageDisplayer().getCLUT();
265 			if (((AbstractNumberDocument) histogramMaxTF.getDocument()).getMaxValue().intValue() < clut.getMaxValue()) {
266 				histogramMinTF.setMaxValue(clut.getMaxValue());
267 				histogramMaxTF.setMaxValue(clut.getMaxValue());
268 			}
269 			colorMapBox.setEnabled(clut.getColorMap() != null && !ColorMap.NONE.equals(clut.getColorMap()));
270 			colorMapBox.setSelectedItem(clut.getColorMap());
271 			changingColorMap = false;
272 			// printMinMax("before colorMap changed");
273 
274 		} else if (propertyChanged.equals(ImageDisplayer.PROPERTY_IMAGE_ZOOM) && !changingImageZoom) {
275 			changingImageZoom = true;
276 			imageZoomBox.setSelectedItem(acopVideo.getImageZoom());
277 			changingImageZoom = false;
278 
279 		} else if (propertyChanged.equals(ImageDisplayer.PROPERTY_OVERLAY_STATE) && !changingOverlayState) {
280 			changingOverlayState = true;
281 			overlayStateBox.setSelectedItem(acopVideo.getOverlayState());
282 			changingOverlayState = false;
283 		}
284 	}
285 
286 	/**
287 	 * AcopVideo property changed
288 	 * 
289 	 * @author mdavid
290 	 * 
291 	 * @param propertyChanged
292 	 */
293 	private void acopVideoPropertyChanged(String propertyChanged) {
294 		if (propertyChanged.equals(AcopVideo.PROPERTY_LIVE_MODE) && !changingLiveMode) {
295 			changingLiveMode = true;
296 			setLiveMode(acopVideo.isLiveMode());
297 			changingLiveMode = false;
298 
299 		} else if (propertyChanged.equals(AcopVideo.PROPERTY_DISPLAY_CLUT_HEADER) && !changingDisplayClutHeader) {
300 			changingDisplayClutHeader = true;
301 			setDisplayClutHeader(acopVideo.isDisplayClutHeader());
302 			changingDisplayClutHeader = false;
303 
304 		} else if (propertyChanged.equals(AcopVideo.CONNECTION_PARAMETERS_PROPERTY) && !changingAddress) {
305 			changingAddress = true;
306 			ConnectionParameters cp = acopVideo.getConnectionParameters();
307 			addressTF.setText(cp == null ? "" : //
308 					("/" + cp.getDeviceContext() //
309 							+ "/" + cp.getDeviceGroup() //
310 							+ "/" + cp.getDeviceName() //
311 							+ "/" + cp.getDeviceProperty()));
312 			changingAddress = false;
313 		}
314 	}
315 
316 	/**
317 	 * Construct the main control panel.
318 	 * 
319 	 * @return
320 	 */
321 	private JPanel getControlPanel() {
322 		if (controlPanel == null) {
323 			controlPanel = new JPanel();
324 			controlPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
325 
326 			GroupLayout layout = new GroupLayout(controlPanel);
327 			controlPanel.setLayout(layout);
328 			layout.setAutoCreateContainerGaps(true);
329 			layout.setAutoCreateGaps(true);
330 
331 			addressFocusListener = new java.awt.event.WindowAdapter() {
332 				public void windowOpened(java.awt.event.WindowEvent se) {
333 					addressTF.requestFocus();
334 				}
335 			};
336 
337 			Component connPanel = getAddressPanel();
338 			Component configPanel = getConfigurationPanel();
339 			Component filePanel = getFilePanel();
340 			Component helpPanel = getHelpPanel();
341 
342 			ParallelGroup hg = layout.createParallelGroup();
343 			layout.setHorizontalGroup(hg);
344 
345 			hg //
346 					// .addComponent(connPanel) //
347 					.addGroup(
348 							layout.createSequentialGroup().addComponent(connPanel, GroupLayout.PREFERRED_SIZE,
349 									GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE).addComponent(helpPanel, 0,
350 									GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)).addGroup(
351 							layout.createSequentialGroup() //
352 									.addComponent(configPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE,
353 											Short.MAX_VALUE) //
354 									.addComponent(filePanel));
355 
356 			GroupLayout.SequentialGroup vg = layout.createSequentialGroup();
357 			layout.setVerticalGroup(vg);
358 			vg //
359 					// .addComponent(connPanel) //
360 					.addGroup(
361 							layout.createParallelGroup().addComponent(connPanel, GroupLayout.PREFERRED_SIZE,
362 									GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE).addComponent(helpPanel,
363 									GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)).addGroup(
364 							layout.createParallelGroup() //
365 									.addComponent(configPanel) //
366 									.addComponent(filePanel, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE,
367 											Short.MAX_VALUE));
368 
369 		}
370 		return controlPanel;
371 	}
372 
373 	/**
374 	 * @return Address Panel
375 	 */
376 	private Component getAddressPanel() {
377 		JLabel addressLabel = new JLabel("Address:");
378 
379 		addressTF = new JTextField();
380 
381 		int height = addressTF.getPreferredSize().height - 1;
382 
383 		URL url = IconHelper.class.getClassLoader().getResource("icons/media/Play16.gif");
384 		if (url != null)
385 			startButton = new JButton(new ImageIcon(url));
386 		else
387 			startButton = new JButton("Start");
388 		startButton.setToolTipText("Start video");
389 		startButton.setPreferredSize(new Dimension(0, height));
390 		startButton.setEnabled(false);
391 		startButton.addActionListener(new ActionListener() {
392 			public void actionPerformed(ActionEvent e) {
393 				startVideo();
394 			}
395 		});
396 
397 		url = IconHelper.class.getClassLoader().getResource("icons/media/Stop16.gif");
398 		if (url != null)
399 			stopButton = new JButton(new ImageIcon(url));
400 		else
401 			stopButton = new JButton("Stop");
402 		stopButton.setToolTipText("Stop video");
403 		stopButton.setPreferredSize(new Dimension(0, height));
404 		stopButton.setEnabled(false);
405 		stopButton.addActionListener(new ActionListener() {
406 			public void actionPerformed(ActionEvent e) {
407 				acopVideo.stop();
408 			}
409 		});
410 
411 		browseButton = new JButton("...");
412 		browseButton.setToolTipText("Browse...");
413 		browseButton.setPreferredSize(new Dimension(height, height));
414 		browseButton.addActionListener(new ActionListener() {
415 			public void actionPerformed(ActionEvent e) {
416 				AbstractCustomizerPanel customizer = acopVideo.getCustomizer();
417 				customizer.showDialog();
418 				customizer.selectAspect("Connections");
419 			}
420 		});
421 
422 		addressTF.addActionListener(new ActionListener() {
423 			public void actionPerformed(ActionEvent e) {
424 				if (startButton.isEnabled())
425 					startVideo();
426 			}
427 		});
428 
429 		addressTF.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
430 
431 			/**
432 			 * Address validation swing worker
433 			 */
434 			private SwingWorker<Void, Void> validator;
435 
436 			public void changedUpdate(javax.swing.event.DocumentEvent e) {
437 				addressChanged();
438 			}
439 
440 			public void removeUpdate(javax.swing.event.DocumentEvent e) {
441 				addressChanged();
442 			}
443 
444 			public void insertUpdate(javax.swing.event.DocumentEvent e) {
445 				addressChanged();
446 			}
447 
448 			private void addressChanged() {
449 				if (validator != null && !validator.isDone()) {
450 					validator.cancel(true);
451 					validator = null;
452 				}
453 
454 				validator = new SwingWorker<Void, Void>() {
455 					String protocol = "";
456 					String context = "";
457 					String server = "";
458 					String device = "";
459 					String property = "";
460 
461 					@Override
462 					protected Void doInBackground() throws Exception {
463 						try {
464 							startButton.setEnabled(validate(addressTF.getText()));
465 
466 						} catch (Exception e) {
467 							startButton.setEnabled(false);
468 							System.err.println(e);
469 						}
470 
471 						if (startButton.isEnabled() && !changingAddress) {
472 							changingAddress = true;
473 							try {
474 								ConnectionParameters cp = acopVideo.getConnectionParameters();
475 								if (cp != null) {
476 									cp = new ConnectionParameters(protocol, context, server, device, property, //
477 											cp.getAccessMode(), //
478 											cp.getAccessRate(), //
479 											cp.getPropertySize(), //
480 											cp.getDynamicParameters());
481 								} else {
482 									cp = new ConnectionParameters(protocol, context, server, device, property,
483 											AccessMode.POLL, //
484 											1000);
485 								}
486 								acopVideo.setConnectionParameters(cp);
487 
488 							} catch (Exception ignored) {
489 							}
490 							changingAddress = false;
491 						}
492 						return null;
493 					}
494 
495 					/**
496 					 * Validates TINE address.
497 					 * 
498 					 * @param address
499 					 *            - Tine address to validate
500 					 * @return true if address is valid and accessible,
501 					 *         otherwise - false
502 					 */
503 					private boolean validate(String address) {
504 						if (address == null || (address = address.trim()).isEmpty())
505 							return false;
506 
507 						String[] split = address.split("/");
508 						switch (split.length) {
509 						case 3:
510 							server = split[0].trim();
511 							device = split[1].trim();
512 							property = split[2].trim();
513 							break;
514 						case 4:
515 							context = split[0].trim();
516 							server = split[1].trim();
517 							device = split[2].trim();
518 							property = split[3].trim();
519 							break;
520 						case 5:
521 							protocol = split[0].trim();
522 							context = split[1].trim();
523 							server = split[2].trim();
524 							device = split[3].trim();
525 							property = split[4].trim();
526 							break;
527 						default:
528 							return false;
529 						}
530 
531 						if (server.isEmpty() || device.isEmpty() || property.isEmpty())
532 							return false;
533 
534 						if (protocol.isEmpty())
535 							protocol = "TINE";
536 						if (context.isEmpty())
537 							context = "DEFAULT";
538 
539 						TPropertyQuery[] queries = TQuery.getPropertyInformation(context, server, device, property);
540 						if (queries == null || queries.length == 0)
541 							return false;
542 
543 						return true;
544 					}
545 				};
546 				validator.execute();
547 			}
548 
549 		});
550 
551 		String address = System.getProperty("acopvideo.connection.address");
552 		if (address != null)
553 			addressTF.setText(address);
554 
555 		JPanel panel = new JPanel(new BorderLayout());
556 		panel.setBorder(BorderFactory.createTitledBorder("Connection"));
557 		panel.setOpaque(false);
558 
559 		GroupLayout layout = new GroupLayout(panel);
560 		panel.setLayout(layout);
561 		layout.setAutoCreateContainerGaps(true);
562 		layout.setAutoCreateGaps(true);
563 
564 		GroupLayout.SequentialGroup hg = layout.createSequentialGroup();
565 		layout.setHorizontalGroup(hg);
566 		hg.addComponent(addressLabel).addComponent(addressTF). //
567 				addGap(0, 0, 0).addComponent(browseButton, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE,
568 						GroupLayout.PREFERRED_SIZE). //
569 				addComponent(startButton). //
570 				addGap(1, 1, 1).addComponent(stopButton);
571 
572 		GroupLayout.ParallelGroup vg = layout.createParallelGroup(Alignment.CENTER);
573 		layout.setVerticalGroup(vg);
574 		vg.addComponent(addressLabel). //
575 				addComponent(addressTF, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE,
576 						GroupLayout.PREFERRED_SIZE). //
577 				addComponent(browseButton, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
578 						GroupLayout.PREFERRED_SIZE). //
579 				addComponent(startButton, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
580 						GroupLayout.PREFERRED_SIZE). //
581 				addComponent(stopButton, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
582 						GroupLayout.PREFERRED_SIZE);
583 
584 		return panel;
585 	}
586 
587 	/**
588 	 * @return Configuration Panel
589 	 */
590 	private Component getConfigurationPanel() {
591 
592 		// Color map ComboBox
593 		JLabel colorMapLabel = new JLabel("Color Map:");
594 		colorMapBox = new JComboBox(ColorMap.values());
595 		colorMapBox.setEnabled(acopVideo.getColorMap() != null && !acopVideo.getColorMap().equals(ColorMap.NONE));
596 		colorMapBox.setSelectedItem(acopVideo.getColorMap());
597 		colorMapBox.addActionListener(new ActionListener() {
598 			public void actionPerformed(ActionEvent e) {
599 				if (!changingColorMap) {
600 					changingColorMap = true;
601 					acopVideo.setColorMap((ColorMap) colorMapBox.getSelectedItem());
602 					changingColorMap = false;
603 				}
604 			}
605 		});
606 
607 		// Image zoom ComboBox
608 		JLabel imageZoomLabel = new JLabel("Zoom:");
609 		imageZoomBox = new JComboBox(ImageZoom.values());
610 		imageZoomBox.setSelectedItem(acopVideo.getImageZoom());
611 		imageZoomBox.addActionListener(new ActionListener() {
612 			public void actionPerformed(ActionEvent e) {
613 				aspectRatioCheckBox.setEnabled((ImageZoom) imageZoomBox.getSelectedItem() == ImageZoom.AUTO);
614 				if (!changingImageZoom) {
615 					changingImageZoom = true;
616 					acopVideo.setImageZoom((ImageZoom) imageZoomBox.getSelectedItem());
617 					changingImageZoom = false;
618 				}
619 			}
620 		});
621 
622 		// Overlay State Information ComboBox
623 		JLabel overlayStateLabel = new JLabel("Overlay:");
624 		overlayStateBox = new JComboBox(OverlayState.values());
625 		overlayStateBox.setSelectedItem(acopVideo.getOverlayState());
626 		overlayStateBox.addActionListener(new ActionListener() {
627 			public void actionPerformed(ActionEvent e) {
628 				if (!changingOverlayState) {
629 					changingOverlayState = true;
630 					acopVideo.setOverlayState((OverlayState) overlayStateBox.getSelectedItem());
631 					changingOverlayState = false;
632 				}
633 			}
634 		});
635 
636 		// AOI Zoom CheckBox
637 		aoiZoomCheckBox = new JCheckBox("AOI Zoom");
638 		aoiZoomCheckBox.setSelected(acopVideo.isAOIZoom());
639 		aoiZoomCheckBox.addItemListener(new ItemListener() {
640 			public void itemStateChanged(ItemEvent e) {
641 				if (!changingAOIZoom) {
642 					changingAOIZoom = true;
643 					acopVideo.setAOIZoom(aoiZoomCheckBox.isSelected());
644 					changingAOIZoom = false;
645 				}
646 			}
647 		});
648 
649 		// Keep Aspect Ratio CheckBox
650 		aspectRatioCheckBox = new JCheckBox("Keep Aspect Ratio");
651 		aspectRatioCheckBox.setSelected(acopVideo.isKeepAspectRatio());
652 		aspectRatioCheckBox.addItemListener(new ItemListener() {
653 			public void itemStateChanged(ItemEvent e) {
654 				if (!changingAspectRatio) {
655 					changingAspectRatio = true;
656 					acopVideo.setKeepAspectRatio(aspectRatioCheckBox.isSelected());
657 					changingAspectRatio = false;
658 				}
659 			}
660 		});
661 
662 		// Histogram Equalization CheckBox
663 		normalizationCheckBox = new JCheckBox("Normalization:");
664 		normalizationCheckBox.setSelected(acopVideo.isHistogramEqualisation());
665 		normalizationCheckBox.addItemListener(new ItemListener() {
666 			public void itemStateChanged(ItemEvent e) {
667 				// System.out.println(acopVideo.getImageDisplayer().getCLUT());
668 				boolean isSelected = normalizationCheckBox.isSelected();
669 				histogramMinTF.setEditable(isSelected);
670 				histogramMaxTF.setEditable(isSelected);
671 				if (!changingNormalization) {
672 					changingNormalization = true;
673 					acopVideo.setHistogramEqualisation(isSelected);
674 					changingNormalization = false;
675 				}
676 			}
677 		});
678 
679 		JLabel minmaxLabel = new JLabel("_"); // TODO: implement set values
680 
681 		histogramMinTF = new IntegerEditor();
682 		histogramMinTF.setEditable(false);
683 		histogramMinTF.setNullAllowed(true);
684 		histogramMinTF.setToolTipText("min value");
685 		histogramMinTF.setPreferredSize(new Dimension(40, normalizationCheckBox.getPreferredSize().height));
686 		histogramMinTF.setMinValue(0);
687 		histogramMinTF.setValue(acopVideo.getHistogramMin());
688 		histogramMinTF.setMaxValue(acopVideo.getHistogramMax() - 1);
689 
690 		histogramMinTF.addActionListener(new ActionListener() {
691 			public void actionPerformed(ActionEvent e) {
692 				histogramMinTF.transferFocus();
693 			}
694 		});
695 		histogramMinTF.addFocusListener(new FocusListener() {
696 			@Override
697 			public void focusLost(FocusEvent e) {
698 				changedHistogramMin();
699 			}
700 
701 			@Override
702 			public void focusGained(FocusEvent e) {
703 			}
704 		});
705 
706 		histogramMaxTF = new IntegerEditor();
707 		histogramMaxTF.setEditable(false);
708 		histogramMaxTF.setNullAllowed(true);
709 		histogramMaxTF.setToolTipText("max value");
710 		histogramMaxTF.setPreferredSize(new Dimension(40, normalizationCheckBox.getPreferredSize().height));
711 		histogramMaxTF.setMinValue(acopVideo.getHistogramMin() + 1);
712 		histogramMaxTF.setValue(acopVideo.getHistogramMax());
713 		histogramMaxTF.setMaxValue(acopVideo.getImageDisplayer().getCLUT().getMaxValue());
714 
715 		// printMinMax("initialize");
716 
717 		histogramMaxTF.addActionListener(new ActionListener() {
718 			public void actionPerformed(ActionEvent e) {
719 				histogramMaxTF.transferFocusBackward();
720 			}
721 		});
722 		histogramMaxTF.addFocusListener(new FocusListener() {
723 			@Override
724 			public void focusLost(FocusEvent e) {
725 				changedHistogramMax();
726 			}
727 
728 			@Override
729 			public void focusGained(FocusEvent e) {
730 			}
731 		});
732 
733 		// Create panel
734 		JPanel panel = new JPanel(new BorderLayout());
735 		panel.setBorder(BorderFactory.createTitledBorder("Configuration"));
736 		panel.setOpaque(false);
737 
738 		GroupLayout layout = new GroupLayout(panel);
739 		panel.setLayout(layout);
740 		layout.setAutoCreateContainerGaps(true);
741 		layout.setAutoCreateGaps(true);
742 
743 		GroupLayout.SequentialGroup hg = layout.createSequentialGroup();
744 		layout.setHorizontalGroup(hg);
745 		layout.linkSize(SwingConstants.HORIZONTAL, colorMapBox, imageZoomBox, overlayStateBox);
746 
747 		hg.addGroup(layout.createParallelGroup(Alignment.LEADING) //
748 				.addComponent(colorMapLabel) //
749 				.addComponent(imageZoomLabel)//
750 				.addComponent(overlayStateLabel)) /**/
751 		.addGroup(
752 				layout.createParallelGroup(Alignment.LEADING) //
753 						.addComponent(colorMapBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
754 								GroupLayout.PREFERRED_SIZE) //
755 						.addComponent(imageZoomBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
756 								GroupLayout.PREFERRED_SIZE) //
757 						.addComponent(overlayStateBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
758 								GroupLayout.PREFERRED_SIZE)) /**/
759 		.addGap(16, 16, 16) /**/
760 		.addGroup(
761 				layout.createParallelGroup(Alignment.LEADING) //
762 						.addComponent(aoiZoomCheckBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
763 								GroupLayout.PREFERRED_SIZE) //
764 						.addComponent(aspectRatioCheckBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
765 								GroupLayout.PREFERRED_SIZE) //
766 
767 						.addGroup(
768 								layout.createSequentialGroup() //
769 										.addComponent(normalizationCheckBox, GroupLayout.PREFERRED_SIZE,
770 												GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) //
771 										.addComponent(histogramMinTF, GroupLayout.PREFERRED_SIZE,
772 												GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) //
773 										.addComponent(minmaxLabel, GroupLayout.PREFERRED_SIZE,
774 												GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) //
775 										.addComponent(histogramMaxTF, GroupLayout.PREFERRED_SIZE,
776 												GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) //
777 						) //
778 				);
779 
780 		// addComponent(normalizationCheckBox, GroupLayout.PREFERRED_SIZE,
781 		// GroupLayout.DEFAULT_SIZE,
782 		// GroupLayout.PREFERRED_SIZE));
783 
784 		GroupLayout.SequentialGroup vg = layout.createSequentialGroup();
785 		layout.setVerticalGroup(vg);
786 		vg.addGroup(
787 				layout.createParallelGroup(Alignment.CENTER) //
788 						.addComponent(colorMapLabel) //
789 						.addComponent(colorMapBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
790 								GroupLayout.PREFERRED_SIZE) //
791 						.addComponent(aoiZoomCheckBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
792 								GroupLayout.PREFERRED_SIZE)) /**/
793 		.addGroup(
794 				layout.createParallelGroup(Alignment.CENTER) //
795 						.addComponent(imageZoomLabel) //
796 						.addComponent(imageZoomBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
797 								GroupLayout.PREFERRED_SIZE) //
798 						.addComponent(aspectRatioCheckBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
799 								GroupLayout.PREFERRED_SIZE)) /**/
800 		.addGroup(
801 				layout.createParallelGroup(Alignment.CENTER) //
802 						.addComponent(overlayStateLabel) //
803 						.addComponent(overlayStateBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
804 								GroupLayout.PREFERRED_SIZE) //
805 						.addGroup(
806 								layout.createParallelGroup() //
807 										.addComponent(normalizationCheckBox, GroupLayout.PREFERRED_SIZE,
808 												GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) //
809 										.addComponent(histogramMinTF, GroupLayout.PREFERRED_SIZE,
810 												GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) //
811 										.addComponent(minmaxLabel, GroupLayout.PREFERRED_SIZE,
812 												GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) //
813 										.addComponent(histogramMaxTF, GroupLayout.PREFERRED_SIZE,
814 												GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) //
815 						));
816 
817 		return panel;
818 	}
819 
820 	private void changedHistogramMin() {
821 		if (!changingHistogramMin) {
822 			// printMinMax("before changedHistogramMin");
823 			changingHistogramMin = true;
824 			try {
825 				acopVideo.setHistogramMin(histogramMinTF.getValue().intValue());
826 
827 			} catch (java.lang.NullPointerException e) {
828 				histogramMinTF.setValue(acopVideo.getHistogramMin());
829 			} catch (IllegalArgumentException e) {
830 				histogramMinTF.setValue(acopVideo.getHistogramMin());
831 			}
832 			changingHistogramMin = false;
833 			// printMinMax("after changedHistogramMin");
834 		}
835 	}
836 
837 	private void changedHistogramMax() {
838 
839 		if (!changingHistogramMax) {
840 			// printMinMax("before changedHistogramMax");
841 			changingHistogramMax = true;
842 			try {
843 				acopVideo.setHistogramMax(histogramMaxTF.getValue().intValue());
844 
845 			} catch (java.lang.NullPointerException e) {
846 				histogramMaxTF.setValue(acopVideo.getHistogramMax());
847 			} catch (IllegalArgumentException e) {
848 				histogramMaxTF.setValue(acopVideo.getHistogramMax());
849 			}
850 			changingHistogramMax = false;
851 			// printMinMax("after changedHistogramMax");
852 		}
853 
854 	}
855 
856 	private Component getHelpPanel() {
857 		JPanel helpPanel = new JPanel(new GridBagLayout());
858 		helpPanel.setBorder(BorderFactory.createTitledBorder("Help"));
859 
860 		helpButton = new JButton("Help");
861 		helpButton.addActionListener(new ActionListener() {
862 			public void actionPerformed(ActionEvent e) {
863 				try {
864 					URI uri = new URI(System.getProperty("app.url",
865 							"http://cosylib.cosylab.com/pub/acop/site/AcopVideoManual.html"));
866 					Desktop.getDesktop().browse(uri);
867 				} catch (Exception ex) {
868 					ex.printStackTrace();
869 				}
870 			}
871 		});
872 
873 		helpPanel.add(helpButton, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER,
874 				GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0));
875 
876 		return helpPanel;
877 	}
878 
879 	/**
880 	 * @return Configuration Panel
881 	 */
882 	private Component getFilePanel() {
883 
884 		saveAsPNGButton = new JButton("Save As 1:1 PNG...");
885 		saveAsPNGButton.addActionListener(new ActionListener() {
886 			public void actionPerformed(ActionEvent e) {
887 				acopVideo.saveAsPNG();
888 			}
889 		});
890 
891 		exportToPngButton = new JButton("Export To PNG...");
892 		exportToPngButton.addActionListener(new ActionListener() {
893 			public void actionPerformed(ActionEvent e) {
894 				acopVideo.exportToPNG();
895 			}
896 		});
897 
898 		JPanel panel = new JPanel(new BorderLayout());
899 		panel.setBorder(BorderFactory.createTitledBorder("File"));
900 		panel.setOpaque(false);
901 
902 		GroupLayout layout = new GroupLayout(panel);
903 		panel.setLayout(layout);
904 		layout.setAutoCreateContainerGaps(true);
905 		layout.setAutoCreateGaps(true);
906 
907 		ParallelGroup hg = layout.createParallelGroup();
908 		layout.setHorizontalGroup(hg);
909 
910 		layout.linkSize(SwingConstants.HORIZONTAL, saveAsPNGButton, exportToPngButton);
911 
912 		hg.addComponent(saveAsPNGButton) //
913 				.addComponent(exportToPngButton);
914 
915 		GroupLayout.SequentialGroup vg = layout.createSequentialGroup();
916 		layout.setVerticalGroup(vg);
917 		vg.addComponent(saveAsPNGButton) //
918 				.addComponent(exportToPngButton);
919 
920 		return panel;
921 	}
922 
923 	// called when added to window
924 	public void addNotify() {
925 		super.addNotify();
926 
927 		Window window = SwingUtilities.windowForComponent(this);
928 
929 		if (window == null)
930 			throw new IllegalStateException("Unexpected");
931 
932 		if (lastWindow != null)
933 			lastWindow.removeWindowListener(addressFocusListener);
934 
935 		window.addWindowListener(addressFocusListener);
936 		lastWindow = window;
937 	}
938 
939 	/*
940 	 * (non-Javadoc)
941 	 * 
942 	 * @see javax.swing.JComponent#setEnabled(boolean)
943 	 */
944 	@Override
945 	public void setEnabled(boolean enabled) {
946 		super.setEnabled(enabled);
947 
948 		acopVideo.setEnabled(enabled);
949 
950 		startButton.setEnabled(!isLiveMode);
951 		stopButton.setEnabled(isLiveMode);
952 		saveAsPNGButton.setEnabled(!isLiveMode);
953 		exportToPngButton.setEnabled(!isLiveMode);
954 		addressTF.setEnabled(!isLiveMode);
955 		browseButton.setEnabled(!isLiveMode);
956 	}
957 
958 	public static void main(String[] args) {
959 		try {
960 			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
961 		} catch (Exception ignored) {
962 		}
963 
964 		TInitializerFactory.getInstance().getInitializer().setSrvPacketMtu(64000);
965 		TInitializerFactory.getInstance().getInitializer().setClnRcvBufferSize(131072);
966 		JFrame f = new JFrame();
967 		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
968 		f.setSize(new Dimension(580, 580));
969 		f.setMinimumSize(new Dimension(580, 340));
970 		f.setTitle("ACOP Video Application");
971 		f.setContentPane(new AcopVideoApplication());
972 		f.setVisible(true);
973 	}
974 
975 	/**
976 	 * Puts this applications into a live mode. When in live mode all connection
977 	 * point control are disabled.
978 	 */
979 	private void setLiveMode(boolean liveMode) {
980 		isLiveMode = liveMode;
981 		startButton.setEnabled(!liveMode);
982 		stopButton.setEnabled(liveMode);
983 		saveAsPNGButton.setEnabled(!liveMode);
984 		exportToPngButton.setEnabled(!liveMode);
985 		addressTF.setEnabled(!liveMode);
986 		browseButton.setEnabled(!liveMode);
987 	}
988 
989 	private void setDisplayClutHeader(boolean displayClutHeader) {
990 		// TODO Auto-generated method stub
991 	}
992 
993 	private void startVideo() {
994 		try {
995 			acopVideo.start();
996 		} catch (Exception e) {
997 			JOptionPane.showMessageDialog(this, "Failed to start video.\n" + e.getMessage(), "Error",
998 					JOptionPane.ERROR_MESSAGE);
999 		}
1000 	}
1001 
1002 	// /**
1003 	// * Only for debuging
1004 	// *
1005 	// * @param msg
1006 	// * @deprecated
1007 	// */
1008 	// private void printMinMax(String msg) {
1009 	// Number minOfMin = null, min = null, maxOfMin = null;
1010 	// if (histogramMaxTF != null) {
1011 	// minOfMin = ((AbstractNumberDocument)
1012 	// histogramMinTF.getDocument()).getMinValue();
1013 	// min = histogramMinTF.getValue();
1014 	// maxOfMin = ((AbstractNumberDocument)
1015 	// histogramMinTF.getDocument()).getMaxValue();
1016 	// }
1017 	//
1018 	// Number minOfMax = null, max = null, maxOfMax = null;
1019 	// if (histogramMaxTF != null) {
1020 	// minOfMax = ((AbstractNumberDocument)
1021 	// histogramMaxTF.getDocument()).getMinValue();
1022 	// max = histogramMaxTF.getValue();
1023 	// maxOfMax = ((AbstractNumberDocument)
1024 	// histogramMaxTF.getDocument()).getMaxValue();
1025 	// }
1026 	//
1027 	// StringBuilder sb = new StringBuilder(msg);
1028 	// sb.append(": ");
1029 	// sb.append("min = ").append(min);
1030 	// sb.append(" from [").append(minOfMin);
1031 	// sb.append(", ").append(maxOfMin);
1032 	// sb.append("], max = ").append(max);
1033 	// sb.append(" from [").append(minOfMax);
1034 	// sb.append(", ").append(maxOfMax).append("]");
1035 	//
1036 	// System.err.println(sb.toString());
1037 	// }
1038 }