Java tutorial
/* * RDV * Real-time Data Viewer * http://rdv.googlecode.com/ * * Copyright (c) 2005-2007 University at Buffalo * Copyright (c) 2005-2007 NEES Cyberinfrastructure Center * Copyright (c) 2008 Palta Software * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * $URL$ * $Revision$ * $Date$ * $Author$ */ package org.rdv.viz.image; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import javax.imageio.ImageIO; import javax.swing.AbstractButton; import javax.swing.ButtonGroup; import javax.swing.ButtonModel; import javax.swing.JButton; import javax.swing.JCheckBoxMenuItem; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JRadioButtonMenuItem; import javax.swing.JSeparator; import javax.swing.SwingUtilities; import javax.swing.event.MouseInputAdapter; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import javax.swing.filechooser.FileFilter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rdv.DataPanelManager; import org.rdv.DataViewer; import org.rdv.Extension; import org.rdv.auth.Authentication; import org.rdv.auth.AuthenticationEvent; import org.rdv.auth.AuthenticationListener; import org.rdv.auth.AuthenticationManager; import org.rdv.data.Channel; import org.rdv.datapanel.AbstractDataPanel; import org.rdv.rbnb.Player; import org.rdv.ui.UIUtilities; import com.rbnb.sapi.ChannelMap; /** * A visualization extension for viewing images. * * @author Jason P. Hanley */ public class ImageViz extends AbstractDataPanel implements AuthenticationListener { /** the logger for this class */ static Log log = LogFactory.getLog(ImageViz.class.getName()); /** the data panel property for the scale */ private static final String DATA_PANEL_PROPERTY_SCALE = "scale"; /** the data panel property for the origin */ private static final String DATA_PANEL_PROPERTY_ORIGIN = "origin"; /** the data panel property for the navigation panel visibility */ private static final String DATA_PANEL_PROPERTY_SHOW_NAVIGATION_IMAGE = "showNavigationImage"; /** the data panel property for filmstrip mode */ private static final String DATA_PANEL_PROPERTY_FILMSTRIP_MODE = "filmstripMode"; /** the data panel property for the maximum number of images to display in filmstrip mode */ private static final String DATA_PANEL_PROPERTY_MAXIMUM_FILMSTRIP_IMAGES = "maximumFilmstripImages"; /** the data panel property for the usage of the thumbnail images */ private static final String DATA_PANEL_PROPERTY_USE_THUMBNAIL_IMAGE = "useThumbnailImage"; /** the data panel property for the robotic controls visibility */ private static final String DATA_PANEL_PROPERTY_HIDE_ROBOTIC_CONTROLS = "hideRoboticControls"; /** the main panel */ private JPanel panel; /** the panel to display the image */ private ImagePanel imagePanel; /** the panel to display images as a filmstrip */ private FilmstripPanel filmstripPanel; // the panels for the robotic controls private JPanel topControls; private JPanel irisControls; private JPanel focusControls; private JPanel zoomControls; private JPanel tiltControls; private JPanel panControls; // menu items for the popup menu private JCheckBoxMenuItem autoScaleMenuItem; private JCheckBoxMenuItem showNavigationImageMenuItem; private JCheckBoxMenuItem hideRoboticControlsMenuItem; /** the group of radio buttons for the maximum filmstrip images */ private ButtonGroup maximumFilmstripImagesButtonGroup; /** the mouse click listener for robotic control */ MouseInputAdapter roboticMouseClickListener; /** a flag to enabled/disable filmstrip mode */ private boolean filmstripMode; /** the values for the maximum number of filmstrip image */ private static final int[] MAXIMUM_FILMSTRIP_IMAGES_VALUES = new int[] { 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20 }; /** a flag to enable/disable usage of a thumbnail image */ private boolean useThumbnailImage; /** the name of the thumbnail plugin */ private static final String THUMBNAIL_PLUGIN_NAME = "_Thumbnails"; /** the time for the currently displayed image */ private double displayedImageTime; /** the data for the currently displayed image */ private byte[] displayedImageData; /** the flexTPS stream object for the current channel */ private FlexTPSStream flexTPSStream; public ImageViz() { super(); filmstripMode = false; useThumbnailImage = false; displayedImageTime = -1; initUI(); setDataComponent(panel); } public void openPanel(final DataPanelManager dataPanelManager) { super.openPanel(dataPanelManager); AuthenticationManager.getInstance().addAuthenticationListener(this); } public void closePanel() { super.closePanel(); AuthenticationManager.getInstance().removeAuthenticationListener(this); } private void initUI() { panel = new JPanel(); panel.setLayout(new BorderLayout()); initImagePanel(); initFilmstripPanel(); initTopControlsPanel(); initIrisControls(); initFocusControls(); initZoomControls(); initTiltControls(); initPanControls(); initRoboticMouseClickListener(); initPopupMenu(); } private void initImagePanel() { imagePanel = new ImagePanel(); imagePanel.setBackground(Color.black); imagePanel.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent pce) { String propertyName = pce.getPropertyName(); if (propertyName.equals(ImagePanel.AUTO_SCALING_PROPERTY)) { boolean autoScaling = (Boolean) pce.getNewValue(); autoScaleMenuItem.setSelected(autoScaling); if (autoScaling) { properties.remove(DATA_PANEL_PROPERTY_SCALE); properties.remove(DATA_PANEL_PROPERTY_ORIGIN); } else { properties.setProperty(DATA_PANEL_PROPERTY_SCALE, Double.toString(imagePanel.getScale())); properties.setProperty(DATA_PANEL_PROPERTY_ORIGIN, pointToString(imagePanel.getOrigin())); } } else if (propertyName.equals(ImagePanel.SCALE_PROPERTY) && !imagePanel.isAutoScaling()) { properties.setProperty(DATA_PANEL_PROPERTY_SCALE, pce.getNewValue().toString()); } else if (propertyName.equals(ImagePanel.ORIGIN_PROPERTY) && !imagePanel.isAutoScaling()) { Point origin = (Point) pce.getNewValue(); String originString = pointToString(origin); properties.setProperty(DATA_PANEL_PROPERTY_ORIGIN, originString); } else if (propertyName.equals(ImagePanel.NAVIGATION_IMAGE_ENABLED_PROPERTY)) { boolean showNavigationImage = (Boolean) pce.getNewValue(); showNavigationImageMenuItem.setSelected(showNavigationImage); if (showNavigationImage) { properties.setProperty(DATA_PANEL_PROPERTY_SHOW_NAVIGATION_IMAGE, "true"); } else { properties.remove(DATA_PANEL_PROPERTY_SHOW_NAVIGATION_IMAGE); } } } }); panel.add(imagePanel, BorderLayout.CENTER); } private void initFilmstripPanel() { filmstripPanel = new FilmstripPanel(); filmstripPanel.setBackground(Color.black); filmstripPanel.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent pce) { String propertyName = pce.getPropertyName(); if (propertyName.equals(FilmstripPanel.MAXIMUM_IMAGES_PROPERTY)) { if (filmstripPanel.getMaximumImages() != FilmstripPanel.MAXIMUM_IMAGES_DEFAULT) { properties.setProperty(DATA_PANEL_PROPERTY_MAXIMUM_FILMSTRIP_IMAGES, pce.getNewValue().toString()); } else { properties.remove(DATA_PANEL_PROPERTY_MAXIMUM_FILMSTRIP_IMAGES); } updateMaximumFilmstripImagesRadioButtons(); } } }); filmstripPanel.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) { setFilmstripMode(false); } } }); } private void initTopControlsPanel() { topControls = new JPanel(); topControls.setLayout(new BorderLayout()); panel.add(topControls, BorderLayout.NORTH); } private void initIrisControls() { irisControls = new JPanel(); irisControls.setLayout(new BorderLayout()); JButton closeIrisButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-close.gif")); closeIrisButton.setBorder(null); closeIrisButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { irisClose(); } }); irisControls.add(closeIrisButton, BorderLayout.WEST); JButton irisControlButton = new StrechIconButton( DataViewer.getIcon("icons/ptz/robotic_control-irisbar-medium.gif")); irisControlButton.setBorder(null); irisControlButton.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { JButton button = (JButton) e.getComponent(); int iris = Math.round(100f * e.getPoint().x / button.getWidth()); iris(iris); } }); irisControls.add(irisControlButton, BorderLayout.CENTER); JPanel irisControlsRight = new JPanel(); irisControlsRight.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0)); JButton openIrisButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-open.gif")); openIrisButton.setBorder(null); openIrisButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { irisOpen(); } }); irisControlsRight.add(openIrisButton); JButton autoIrisButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-auto.gif")); autoIrisButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { irisAuto(); } }); autoIrisButton.setBorder(null); irisControlsRight.add(autoIrisButton); irisControls.add(irisControlsRight, BorderLayout.EAST); } private void initFocusControls() { focusControls = new JPanel(); focusControls.setLayout(new BorderLayout()); JButton nearFocusButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-near.gif")); nearFocusButton.setBorder(null); nearFocusButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { focusNear(); } }); focusControls.add(nearFocusButton, BorderLayout.WEST); JButton focusControlButton = new StrechIconButton( DataViewer.getIcon("icons/ptz/robotic_control-focusbar-medium.gif")); focusControlButton.setBorder(null); focusControlButton.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { JButton button = (JButton) e.getComponent(); int focus = Math.round(100f * e.getPoint().x / button.getWidth()); focus(focus); } }); focusControls.add(focusControlButton, BorderLayout.CENTER); JPanel focusControlsRight = new JPanel(); focusControlsRight.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0)); JButton farFocusButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-far.gif")); farFocusButton.setBorder(null); farFocusButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { focusFar(); } }); focusControlsRight.add(farFocusButton); JButton autoFocusButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-auto.gif")); autoFocusButton.setBorder(null); autoFocusButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { focusAuto(); } }); focusControlsRight.add(autoFocusButton); focusControls.add(focusControlsRight, BorderLayout.EAST); } private void initZoomControls() { zoomControls = new JPanel(); zoomControls.setLayout(new BorderLayout()); JButton zoomOutButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-minus.gif")); zoomOutButton.setBorder(null); zoomOutButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { zoomOut(); } }); zoomControls.add(zoomOutButton, BorderLayout.WEST); JButton zoomControlButton = new StrechIconButton( DataViewer.getIcon("icons/ptz/robotic_control-zoombar-medium.gif")); zoomControlButton.setBorder(null); zoomControlButton.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { JButton button = (JButton) e.getComponent(); int zoom = Math.round(100f * e.getPoint().x / button.getWidth()); zoom(zoom); } }); zoomControls.add(zoomControlButton, BorderLayout.CENTER); JPanel topRightControls = new JPanel(); topRightControls.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0)); JButton zoomInButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-plus.gif")); zoomInButton.setBorder(null); zoomInButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { zoomIn(); } }); topRightControls.add(zoomInButton); JButton topHomeButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-home.gif")); topHomeButton.setBorder(null); topHomeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { home(); } }); topRightControls.add(topHomeButton); zoomControls.add(topRightControls, BorderLayout.EAST); } private void initTiltControls() { tiltControls = new JPanel(); tiltControls.setLayout(new BorderLayout()); JButton tiltUpButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-up.gif")); tiltUpButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { tiltUp(); } }); tiltUpButton.setBorder(null); tiltControls.add(tiltUpButton, BorderLayout.NORTH); JButton tiltControlButton = new StrechIconButton( DataViewer.getIcon("icons/ptz/robotic_control-tiltbar-medium.gif")); tiltControlButton.setBorder(null); tiltControlButton.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { JButton button = (JButton) e.getComponent(); int tilt = Math.round(100f * e.getPoint().y / button.getHeight()); tilt(tilt); } }); tiltControls.add(tiltControlButton, BorderLayout.CENTER); JButton tiltDownButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-down.gif")); tiltDownButton.setBorder(null); tiltDownButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { tiltDown(); } }); tiltControls.add(tiltDownButton, BorderLayout.SOUTH); } private void initPanControls() { panControls = new JPanel(); panControls.setLayout(new BorderLayout()); JButton panLeftButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-left.gif")); panLeftButton.setBorder(null); panLeftButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { panLeft(); } }); panControls.add(panLeftButton, BorderLayout.WEST); JButton panControlButton = new StrechIconButton( DataViewer.getIcon("icons/ptz/robotic_control-panbar-medium.gif")); panControlButton.setBorder(null); panControlButton.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { JButton button = (JButton) e.getComponent(); int pan = Math.round(100f * e.getPoint().x / button.getWidth()); pan(pan); } }); panControls.add(panControlButton, BorderLayout.CENTER); JPanel bottomRightControls = new JPanel(); bottomRightControls.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0)); JButton panRightButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-right.gif")); panRightButton.setBorder(null); panRightButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { panRight(); } }); bottomRightControls.add(panRightButton); JButton bottomHomeButton = new JButton(DataViewer.getIcon("icons/ptz/robotic_control-home.gif")); bottomHomeButton.setBorder(null); bottomHomeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { home(); } }); bottomRightControls.add(bottomHomeButton); panControls.add(bottomRightControls, BorderLayout.EAST); } private void initRoboticMouseClickListener() { roboticMouseClickListener = new MouseInputAdapter() { public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { Point imagePoint = imagePanel.panelToScaledImagePoint(e.getPoint()); if (imagePoint != null) { center(imagePoint); } } } }; } private void initPopupMenu() { final JPopupMenu popupMenu = new JPopupMenu(); // create a popup to copy an image to the clipboard final JMenuItem copyImageMenuItem = new JMenuItem("Copy"); copyImageMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { copyImage(); } }); popupMenu.add(copyImageMenuItem); popupMenu.addSeparator(); // create a popup to save an image final JMenuItem saveImageMenuItem = new JMenuItem("Save as..."); saveImageMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { saveImage(); } }); popupMenu.add(saveImageMenuItem); popupMenu.addSeparator(); // create a popup to copy an image to the clipboard final JMenuItem printImageMenuItem = new JMenuItem("Print..."); printImageMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { printImage(); } }); popupMenu.add(printImageMenuItem); popupMenu.addSeparator(); final JMenuItem zoomInMenuItem = new JMenuItem("Zoom in"); zoomInMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { imagePanel.zoomIn(); } }); popupMenu.add(zoomInMenuItem); final JMenuItem zoomOutMenuItem = new JMenuItem("Zoom out"); zoomOutMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { imagePanel.zoomOut(); } }); popupMenu.add(zoomOutMenuItem); final JSeparator zoomMenuSeparator = new JPopupMenu.Separator(); popupMenu.add(zoomMenuSeparator); autoScaleMenuItem = new JCheckBoxMenuItem("Auto scale", true); autoScaleMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { setAutoScale(autoScaleMenuItem.isSelected()); } }); popupMenu.add(autoScaleMenuItem); final JMenuItem resetScaleMenuItem = new JMenuItem("Reset scale"); resetScaleMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { imagePanel.setScale(1); } }); popupMenu.add(resetScaleMenuItem); showNavigationImageMenuItem = new JCheckBoxMenuItem("Show navigation image", imagePanel.isNavigationImageEnabled()); showNavigationImageMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { setShowNavigationImage(showNavigationImageMenuItem.isSelected()); } }); popupMenu.add(showNavigationImageMenuItem); final JMenu maximumFilmstripImagesMenu = new JMenu("Maximum number of images"); maximumFilmstripImagesButtonGroup = new ButtonGroup(); for (final int maximumFilmstripImagesValue : MAXIMUM_FILMSTRIP_IMAGES_VALUES) { boolean selected = filmstripPanel.getMaximumImages() == maximumFilmstripImagesValue; JMenuItem maximumFilmStripImagesMenuItem = new JRadioButtonMenuItem( Integer.toString(maximumFilmstripImagesValue), selected); maximumFilmStripImagesMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { filmstripPanel.setMaximumImages(maximumFilmstripImagesValue); } }); maximumFilmstripImagesButtonGroup.add(maximumFilmStripImagesMenuItem); maximumFilmstripImagesMenu.add(maximumFilmStripImagesMenuItem); } popupMenu.add(maximumFilmstripImagesMenu); popupMenu.addSeparator(); final JCheckBoxMenuItem showAsFilmstripMenuItem = new JCheckBoxMenuItem("Show as filmstrip", filmstripMode); showAsFilmstripMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { boolean filmstripMode = showAsFilmstripMenuItem.isSelected(); setFilmstripMode(filmstripMode); } }); popupMenu.add(showAsFilmstripMenuItem); final JCheckBoxMenuItem useThumbnailImageMenuItem = new JCheckBoxMenuItem("Use thumbnail image", useThumbnailImage); useThumbnailImageMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setUseThumbnailImage(useThumbnailImageMenuItem.isSelected()); } }); popupMenu.add(useThumbnailImageMenuItem); hideRoboticControlsMenuItem = new JCheckBoxMenuItem("Disable robotic controls", false); hideRoboticControlsMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { setRoboticControls(); } }); popupMenu.add(hideRoboticControlsMenuItem); // enable the save image popup if an image is being displayed popupMenu.addPopupMenuListener(new PopupMenuListener() { public void popupMenuWillBecomeVisible(PopupMenuEvent arg0) { boolean hasImage = displayedImageData != null; saveImageMenuItem.setEnabled(hasImage); copyImageMenuItem.setEnabled(hasImage); printImageMenuItem.setEnabled(hasImage); boolean enableZoom = hasImage && !filmstripMode; zoomInMenuItem.setEnabled(enableZoom); zoomInMenuItem.setVisible(!filmstripMode); zoomOutMenuItem.setEnabled(enableZoom); zoomOutMenuItem.setVisible(!filmstripMode); zoomMenuSeparator.setVisible(!filmstripMode); autoScaleMenuItem.setVisible(!filmstripMode); resetScaleMenuItem.setVisible(!filmstripMode); showNavigationImageMenuItem.setVisible(!filmstripMode); maximumFilmstripImagesMenu.setVisible(filmstripMode); showAsFilmstripMenuItem.setSelected(filmstripMode); useThumbnailImageMenuItem.setSelected(useThumbnailImage); useThumbnailImageMenuItem.setVisible(imageHasThumbnail()); hideRoboticControlsMenuItem.setVisible(flexTPSStream != null); } public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0) { } public void popupMenuCanceled(PopupMenuEvent arg0) { } }); // set component popup and mouselistener to trigger it panel.setComponentPopupMenu(popupMenu); imagePanel.setComponentPopupMenu(popupMenu); filmstripPanel.setComponentPopupMenu(popupMenu); panel.addMouseListener(new MouseInputAdapter() { }); } /** * Sets filmstrip mode. If enabled, images will be displayed like a filmstrip, * if disbaled only the latest image will be displayed. * * @param filmstripMode if true, display images like a filmstrip */ private void setFilmstripMode(boolean filmstripMode) { if (this.filmstripMode == filmstripMode) { return; } this.filmstripMode = filmstripMode; BufferedImage displayedImage = getDisplayedImage(); if (filmstripMode) { panel.remove(imagePanel); imagePanel.setImage(null, -1); if (displayedImage != null) { filmstripPanel.addImage(displayedImage, displayedImageTime); } panel.add(filmstripPanel, BorderLayout.CENTER); } else { panel.remove(filmstripPanel); filmstripPanel.clearImages(); if (displayedImage != null) { imagePanel.setImage(displayedImage, displayedImageTime); } panel.add(imagePanel, BorderLayout.CENTER); } setRoboticControls(); panel.revalidate(); panel.repaint(); if (filmstripMode) { properties.setProperty(DATA_PANEL_PROPERTY_FILMSTRIP_MODE, "true"); } else { properties.remove(DATA_PANEL_PROPERTY_FILMSTRIP_MODE); } } /** * Select the correct radio button for the maximum number of filmstrip images. */ private void updateMaximumFilmstripImagesRadioButtons() { int maximumFilmstripImages = filmstripPanel.getMaximumImages(); int index = Arrays.binarySearch(MAXIMUM_FILMSTRIP_IMAGES_VALUES, maximumFilmstripImages); if (index >= 0) { Enumeration<AbstractButton> elements = maximumFilmstripImagesButtonGroup.getElements(); AbstractButton button = null; while (index-- >= 0) { button = elements.nextElement(); } maximumFilmstripImagesButtonGroup.setSelected(button.getModel(), true); } else { ButtonModel buttonModel = maximumFilmstripImagesButtonGroup.getSelection(); if (buttonModel != null) { buttonModel.setSelected(false); } } } /** * Controls the usage of thumbnail images. * * @param useThumbnailImage if true, use thumbnail images, otherwise don't */ private void setUseThumbnailImage(boolean useThumbnailImage) { if (this.useThumbnailImage == useThumbnailImage) { return; } if (channels.size() != 0 && useThumbnailImage && !imageHasThumbnail()) { return; } this.useThumbnailImage = useThumbnailImage; if (channels.size() != 0) { clearImage(); String channelName = (String) channels.iterator().next(); String thumbnailChannelName = THUMBNAIL_PLUGIN_NAME + "/" + channelName; if (useThumbnailImage) { rbnbController.unsubscribe(channelName, this); rbnbController.subscribe(thumbnailChannelName, this); } else { rbnbController.unsubscribe(thumbnailChannelName, this); rbnbController.subscribe(channelName, this); } } if (useThumbnailImage) { properties.setProperty(DATA_PANEL_PROPERTY_USE_THUMBNAIL_IMAGE, "true"); } else { properties.remove(DATA_PANEL_PROPERTY_USE_THUMBNAIL_IMAGE); } } /** * Returns true if there is a thumbnail image channel available for this * image. * * @return */ private boolean imageHasThumbnail() { if (channels.size() == 0) { return false; } String channelName = (String) channels.iterator().next(); String thumbnailChannelName = THUMBNAIL_PLUGIN_NAME + "/" + channelName; Channel thumbnailChannel = rbnbController.getChannel(thumbnailChannelName); return thumbnailChannel != null; } private void addRoboticControls() { if (flexTPSStream == null) { return; } if (flexTPSStream.canIris()) { topControls.add(irisControls, BorderLayout.NORTH); } else { topControls.remove(irisControls); } if (flexTPSStream.canFocus()) { topControls.add(focusControls, BorderLayout.CENTER); } else { topControls.remove(focusControls); } if (flexTPSStream.canZoom()) { topControls.add(zoomControls, BorderLayout.SOUTH); } else { topControls.remove(zoomControls); } if (flexTPSStream.canTilt() && flexTPSStream.canPan()) { panel.add(tiltControls, BorderLayout.EAST); panel.add(panControls, BorderLayout.SOUTH); } else { panel.remove(tiltControls); panel.remove(panControls); } panel.revalidate(); imagePanel.removeMouseListener(roboticMouseClickListener); imagePanel.addMouseListener(roboticMouseClickListener); } private void removeRoboticControls() { topControls.remove(irisControls); topControls.remove(focusControls); topControls.remove(zoomControls); panel.remove(tiltControls); panel.remove(panControls); panel.revalidate(); imagePanel.removeMouseListener(roboticMouseClickListener); } private void setRoboticControls() { if (filmstripMode) { removeRoboticControls(); return; } if (hideRoboticControlsMenuItem.isSelected()) { properties.setProperty(DATA_PANEL_PROPERTY_HIDE_ROBOTIC_CONTROLS, "true"); removeRoboticControls(); } else { properties.remove(DATA_PANEL_PROPERTY_HIDE_ROBOTIC_CONTROLS); if (state == Player.STATE_MONITORING) { addRoboticControls(); } else { removeRoboticControls(); } } } /** * Gets the displayed image. If no image is displayed or there is an error * decoding the displayed image, null is returned. * * @return the displayed image, or null if there is one or if there is an * error decoding the image */ private BufferedImage getDisplayedImage() { if (displayedImageData == null) { return null; } InputStream in = new ByteArrayInputStream(displayedImageData); BufferedImage bufferedImage = null; try { bufferedImage = ImageIO.read(in); } catch (IOException e) { log.error("Failed to decode displayed image."); e.printStackTrace(); } return bufferedImage; } /** * If an image is currently being displayed, save it to a file. This will * bring up a file chooser to select the file. */ private void saveImage() { saveImage(UIUtilities.getCurrentDirectory()); } /** * If an image is currently being displayed, save it to a file. This will * bring up a file chooser to select the file. * * @param directory the directory to start the file chooser in */ private void saveImage(File directory) { if (displayedImageData != null) { // create default file name String channelName = (String) channels.iterator().next(); String fileName = channelName.replace("/", " - "); if (!fileName.endsWith(".jpg")) { fileName += ".jpg"; } File selectedFile = new File(directory, fileName); // create filter to only show files that end with '.jpg' FileFilter fileFilter = new FileFilter() { public boolean accept(File f) { return (f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg")); } public String getDescription() { return "JPEG Image Files"; } }; // show dialog File outFile = UIUtilities.getFile(fileFilter, "Save", null, selectedFile); if (outFile != null) { // prompt for overwrite if file already exists if (outFile.exists()) { int overwriteReturn = JOptionPane.showConfirmDialog(null, outFile.getName() + " already exists. Do you want to replace it?", "Replace image?", JOptionPane.YES_NO_OPTION); if (overwriteReturn == JOptionPane.NO_OPTION) { saveImage(outFile.getParentFile()); return; } } // write image file try { FileOutputStream out = new FileOutputStream(outFile); out.write(displayedImageData); out.close(); } catch (Exception e) { JOptionPane.showMessageDialog(null, "Filed to write image file.", "Save Image Error", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); } } } } /** * Copy the currently displayed image to the clipboard. If no image is being * displayed, nothing will be copied to the clipboard. */ private void copyImage() { // get the displayed image Image displayedImage = getDisplayedImage(); if (displayedImage == null) { return; } // get the system clipboard Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); // create the transferable to transfer an image ImageSelection contents = new ImageSelection(displayedImage); // set the clipboard contents to the image transferable clipboard.setContents(contents, null); } /** * Print the displayed image. If no image is being displayed, this will method * will do nothing. */ private void printImage() { // get the displayed image final Image displayedImage = getDisplayedImage(); if (displayedImage == null) { return; } // setup a print job PrinterJob printJob = PrinterJob.getPrinterJob(); // set the renderer for the image printJob.setPrintable(new Printable() { public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException { //we only have one page to print if (pageIndex != 0) { return Printable.NO_SUCH_PAGE; } Graphics2D g2d = (Graphics2D) g; // move to corner of imageable page g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); // get page dimensions double pageWidth = pageFormat.getImageableWidth(); double pageHeight = pageFormat.getImageableHeight(); // get image dimensions int imageWidth = displayedImage.getWidth(null); int imageHeight = displayedImage.getHeight(null); // get scale factor for image double widthScale = pageWidth / imageWidth; double heightScale = pageHeight / imageHeight; double scale = Math.min(widthScale, heightScale); // draw image with width and height scaled to page int scaledWidth = (int) (scale * imageWidth); int scaledHeight = (int) (scale * imageHeight); g2d.drawImage(displayedImage, 0, 0, scaledWidth, scaledHeight, null); return Printable.PAGE_EXISTS; } }); // set the job name to the channel name (plus jpg extension) // this is used as a hint for a file name when printing to file String channelName = (String) channels.iterator().next(); String jobName = channelName.replace("/", " - "); if (!jobName.endsWith(".jpg")) { jobName += ".jpg"; } printJob.setJobName(jobName); // show the print dialog and print if ok clicked if (printJob.printDialog()) { try { printJob.print(); } catch (PrinterException pe) { JOptionPane.showMessageDialog(null, "Failed to print image.", "Print Image Error", JOptionPane.ERROR_MESSAGE); pe.printStackTrace(); } } } private void setShowNavigationImage(boolean showNavigation) { imagePanel.setNavigationImageEnabled(showNavigation); } private void setAutoScale(boolean autoScale) { imagePanel.setAutoScaling(autoScale); } private void panLeft() { if (flexTPSStream != null) { flexTPSStream.panLeft(); } } private void panRight() { if (flexTPSStream != null) { flexTPSStream.panRight(); } } private void pan(int pan) { if (flexTPSStream != null) { flexTPSStream.pan(pan); } } private void tiltUp() { if (flexTPSStream != null) { flexTPSStream.tiltUp(); } } private void tiltDown() { if (flexTPSStream != null) { flexTPSStream.tiltDown(); } } private void tilt(int tilt) { if (flexTPSStream != null) { flexTPSStream.tilt(tilt); } } private void home() { if (flexTPSStream != null) { flexTPSStream.goHome(); } } private void center(Point p) { if (flexTPSStream != null) { flexTPSStream.center(p, imagePanel.getScaledImageSize()); } } private void zoomIn() { if (flexTPSStream != null) { flexTPSStream.zoomIn(); } } private void zoomOut() { if (flexTPSStream != null) { flexTPSStream.zoomOut(); } } private void zoom(int zoom) { if (flexTPSStream != null) { flexTPSStream.zoom(zoom); } } private void irisClose() { if (flexTPSStream != null) { flexTPSStream.irisClose(); } } private void irisOpen() { if (flexTPSStream != null) { flexTPSStream.irisOpen(); } } private void iris(int iris) { if (flexTPSStream != null) { flexTPSStream.iris(iris); } } private void irisAuto() { if (flexTPSStream != null) { flexTPSStream.irisAuto(); } } private void focusNear() { if (flexTPSStream != null) { flexTPSStream.focusNear(); } } private void focusFar() { if (flexTPSStream != null) { flexTPSStream.focusFar(); } } private void focus(int focus) { if (flexTPSStream != null) { flexTPSStream.focus(focus); } } private void focusAuto() { if (flexTPSStream != null) { flexTPSStream.focusAuto(); } } private void setupFlexTPSStream() { String channelName = (String) channels.iterator().next(); Channel channel = rbnbController.getChannel(channelName); if (channel == null) { return; } String host = channel.getMetadata("flexTPS_host"); String feed = channel.getMetadata("flexTPS_feed"); String stream = channel.getMetadata("flexTPS_stream"); // If user successfully login, gaSession should be valid Authentication auth = AuthenticationManager.getInstance().getAuthentication(); String gaSession = null; if (auth != null) { gaSession = auth.get("session"); } if (host != null && feed != null && stream != null) { flexTPSStream = new FlexTPSStream(host, feed, stream, gaSession); if (flexTPSStream.canDoRobotic()) { setRoboticControls(); } else { flexTPSStream = null; removeRoboticControls(); } } } public boolean supportsMultipleChannels() { return false; } public boolean setChannel(String channelName) { if (!isChannelSupported(channelName)) { return false; } return super.setChannel(channelName); } private boolean isChannelSupported(String channelName) { Channel channel = rbnbController.getChannel(channelName); if (channel == null) { return false; } String mimeType = channel.getMetadata("mime"); Extension extension = dataPanelManager.getExtension(this.getClass()); if (extension != null) { List<String> mimeTypes = extension.getMimeTypes(); for (int i = 0; i < mimeTypes.size(); i++) { String mime = mimeTypes.get(i); if (mime.equals(mimeType)) { return true; } } } return false; } @Override public boolean addChannel(String channelName) { if (!super.addChannel(channelName)) { return false; } // subscribe to the thumbnail channel if it exists if (useThumbnailImage) { if (imageHasThumbnail()) { // TODO we are still subscribing to the channel initially and this will // cause a fetch of 1 image, so we should suppress this somehow rbnbController.unsubscribe(channelName, this); String thumbnailChannelName = THUMBNAIL_PLUGIN_NAME + "/" + channelName; rbnbController.subscribe(thumbnailChannelName, this); } else { setUseThumbnailImage(false); } } return true; } @Override protected void channelAdded(String channelName) { setupFlexTPSStream(); } @Override protected void channelRemoved(String channelName) { if (useThumbnailImage) { String thumbnailChannelName = THUMBNAIL_PLUGIN_NAME + "/" + channelName; rbnbController.unsubscribe(thumbnailChannelName, this); } clearImage(); flexTPSStream = null; removeRoboticControls(); } public void postData(ChannelMap channelMap) { super.postData(channelMap); } public void postTime(final double time) { super.postTime(time); SwingUtilities.invokeLater(new Runnable() { public void run() { filmstripPanel.setTime(time); } }); postImage(); //clear stale images if (displayedImageTime != -1 && (displayedImageTime <= time - timeScale || displayedImageTime > time)) { SwingUtilities.invokeLater(new Runnable() { public void run() { clearImage(); } }); } } @Override public void timeScaleChanged(final double timeScale) { super.timeScaleChanged(timeScale); SwingUtilities.invokeLater(new Runnable() { public void run() { filmstripPanel.setTimescale(timeScale); } }); } private void postImage() { if (channelMap == null) { //no data to display yet return; } Iterator<String> it = channels.iterator(); if (!it.hasNext()) { //no channels to post to return; } final String channelName; if (useThumbnailImage) { channelName = THUMBNAIL_PLUGIN_NAME + "/" + it.next(); } else { channelName = it.next(); } try { int channelIndex = channelMap.GetIndex(channelName); //See if there is an data for us in the channel map if (channelIndex == -1) { return; } if (channelMap.GetType(channelIndex) != ChannelMap.TYPE_BYTEARRAY) { log.error("Expected byte array for JPEG data in channel " + channelName + "."); return; } int imageIndex = -1; // TODO replace with function in the Abstract class double[] times = channelMap.GetTimes(channelIndex); for (int i = times.length - 1; i >= 0; i--) { if (times[i] <= time && times[i] > time - timeScale) { imageIndex = i; break; } } if (imageIndex == -1) { //no data in this time for us to display return; } final double imageTime = times[imageIndex]; if (imageTime == displayedImageTime) { //we are already displaying this image return; } byte[][] imageData = channelMap.GetDataAsByteArray(channelIndex); if (imageData.length > 0) { // save raw image data displayedImageData = imageData[imageIndex]; //draw image on screen final InputStream in = new ByteArrayInputStream(displayedImageData); SwingUtilities.invokeLater(new Runnable() { public void run() { try { BufferedImage bufferedImage = ImageIO.read(in); if (filmstripMode) { filmstripPanel.addImage(bufferedImage, imageTime); } else { imagePanel.setImage(bufferedImage, imageTime); } } catch (IOException e) { log.error("Failed to decode image for " + channelName + "."); e.printStackTrace(); } } }); //update the image index currently displayed for this channel map displayedImageTime = imageTime; } else { log.error("Data array empty for channel " + channelName + "."); } } catch (Exception e) { log.error("Failed to receive data for channel " + channelName + "."); e.printStackTrace(); } } public void postState(int newState, int oldState) { super.postState(newState, oldState); setRoboticControls(); } private void clearImage() { if (filmstripMode) { filmstripPanel.clearImages(); } else { imagePanel.setImage(null, -1); } displayedImageData = null; displayedImageTime = -1; } @Override public void setProperty(String key, String value) { super.setProperty(key, value); if (key == null) { return; } if (key.equals(DATA_PANEL_PROPERTY_SCALE)) { try { double scale = Double.parseDouble(value); imagePanel.setScale(scale); } catch (NumberFormatException e) { log.warn("Unable to set scale: " + value + "."); } } else if (key.equals(DATA_PANEL_PROPERTY_ORIGIN)) { String[] pointComponents = value.split(","); if (pointComponents.length == 2) { try { int x = Integer.parseInt(pointComponents[0]); int y = Integer.parseInt(pointComponents[1]); imagePanel.setOrigin(x, y); } catch (NumberFormatException e) { log.warn("Unable to set origin: " + value + "."); } } else { log.warn("Unable to set origin: " + value + "."); } } else if (key.equals(DATA_PANEL_PROPERTY_SHOW_NAVIGATION_IMAGE) && Boolean.parseBoolean(value)) { imagePanel.setNavigationImageEnabled(true); } else if (key.equals(DATA_PANEL_PROPERTY_FILMSTRIP_MODE) && Boolean.parseBoolean(value)) { setFilmstripMode(true); } else if (key.equals(DATA_PANEL_PROPERTY_MAXIMUM_FILMSTRIP_IMAGES)) { try { int maximumFilmstripImages = Integer.parseInt(value); filmstripPanel.setMaximumImages(maximumFilmstripImages); } catch (NumberFormatException e) { log.warn("Unable to set maximum filmstrip images: " + value + "."); } } else if (key.equals(DATA_PANEL_PROPERTY_USE_THUMBNAIL_IMAGE) && Boolean.parseBoolean(value)) { setUseThumbnailImage(true); } else if (key.equals(DATA_PANEL_PROPERTY_HIDE_ROBOTIC_CONTROLS) && Boolean.parseBoolean(value)) { hideRoboticControlsMenuItem.setSelected(true); setRoboticControls(); } } /** * Converts the coordinates of a point to a string of the format "x,y". * * @param p the point * @return the coordinates of the point as a string */ private static String pointToString(Point p) { return p.x + "," + p.y; } public void authenticationChanged(AuthenticationEvent event) { setupFlexTPSStream(); } }