/* * #%L * BSD implementations of Bio-Formats readers and writers * %% * Copyright (C) 2005 - 2015 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package loci.formats.gui; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import loci.formats.FormatException; import loci.formats.FormatTools; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * PreviewPane is a panel for use as a JFileChooser accessory, displaying * a thumbnail for the selected image, loaded in a separate thread. */ public class PreviewPane extends JPanel implements PropertyChangeListener, Runnable { // -- Constants -- private static final Logger LOGGER = LoggerFactory.getLogger(PreviewPane.class); // -- Fields -- /** Reader for use when loading thumbnails. */ protected BufferedImageReader reader; /** Current ID to load. */ protected String loadId; /** Last ID loaded. */ protected String lastId; /** Thumbnail loading thread. */ protected Thread loader; /** Flag indicating whether loader thread should keep running. */ protected boolean loaderAlive; /** Method for syncing the view to the model. */ protected Runnable refresher; // -- Fields - view -- /** Labels containing thumbnail and dimensional information. */ protected JLabel iconLabel, formatLabel, resLabel, zctLabel, typeLabel; // -- Fields - model -- protected ImageIcon icon; protected String iconText, formatText, resText, zctText, typeText; protected String iconTip, formatTip, resTip, zctTip, typeTip; // -- Constructor -- /** Constructs a preview pane for the given file chooser. */ public PreviewPane(JFileChooser jc) { super(); reader = new BufferedImageReader(); reader.setNormalized(true); // create view setBorder(new EmptyBorder(0, 10, 0, 10)); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); iconLabel = new JLabel(); iconLabel.setMinimumSize(new java.awt.Dimension(128, -1)); iconLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); add(iconLabel); add(Box.createVerticalStrut(7)); formatLabel = new JLabel(); formatLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); add(formatLabel); add(Box.createVerticalStrut(5)); resLabel = new JLabel(); resLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); add(resLabel); zctLabel = new JLabel(); zctLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); add(zctLabel); typeLabel = new JLabel(); typeLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); add(typeLabel); // smaller font for most labels Font font = formatLabel.getFont(); font = font.deriveFont(font.getSize2D() - 3); formatLabel.setFont(font); resLabel.setFont(font); zctLabel.setFont(font); typeLabel.setFont(font); // populate model icon = null; iconText = formatText = resText = zctText = typeText = ""; iconTip = formatTip = resTip = zctTip = typeTip = null; if (jc != null) { jc.setAccessory(this); jc.addPropertyChangeListener(this); refresher = new Runnable() { @Override public void run() { iconLabel.setIcon(icon); iconLabel.setText(iconText); iconLabel.setToolTipText(iconTip); formatLabel.setText(formatText); formatLabel.setToolTipText(formatTip); resLabel.setText(resText); resLabel.setToolTipText(resTip); zctLabel.setText(zctText); zctLabel.setToolTipText(zctTip); typeLabel.setText(typeText); typeLabel.setToolTipText(typeTip); } }; // start separate loader thread loaderAlive = true; loader = new Thread(this, "Preview"); loader.start(); } } // -- Component API methods -- /* @see java.awt.Component#getPreferredSize() */ @Override public Dimension getPreferredSize() { Dimension prefSize = super.getPreferredSize(); return new Dimension(148, prefSize.height); } // -- PropertyChangeListener API methods -- /** * Property change event, to listen for when a new * file is selected, or the file chooser closes. */ @Override public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if (prop.equals("JFileChooserDialogIsClosingProperty")) { // notify loader thread that it should stop loaderAlive = false; } if (!prop.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) return; File f = (File) e.getNewValue(); if (f != null && (f.isDirectory() || !f.exists())) f = null; loadId = f == null ? null : f.getAbsolutePath(); } // -- Runnable API methods -- /** Thumbnail loading routine. */ @Override public void run() { while (loaderAlive) { try { Thread.sleep(100); } catch (InterruptedException exc) { LOGGER.info("", exc); } try { // catch-all for unanticipated exceptions final String id = loadId; if (id == lastId) continue; if (id != null && lastId != null) { String[] files = reader.getUsedFiles(); boolean found = false; for (int i=0; i<files.length; i++) { if (id.equals(files[i])) { found = true; lastId = id; break; } } if (found) continue; } lastId = id; icon = null; iconText = id == null ? "" : "Reading..."; formatText = resText = zctText = typeText = ""; iconTip = id; formatTip = resTip = zctTip = typeTip = ""; if (id == null) { SwingUtilities.invokeLater(refresher); continue; } try { reader.setId(id); } catch (FormatException exc) { LOGGER.debug("Failed to initialize {}", id, exc); boolean badFormat = exc.getMessage().startsWith("Unknown file format"); iconText = "Unsupported " + (badFormat ? "format" : "file"); formatText = resText = ""; SwingUtilities.invokeLater(refresher); lastId = null; continue; } catch (IOException exc) { LOGGER.debug("Failed to initialize {}", id, exc); iconText = "Unsupported file"; formatText = resText = ""; SwingUtilities.invokeLater(refresher); lastId = null; continue; } if (id != loadId) { SwingUtilities.invokeLater(refresher); continue; } icon = new ImageIcon(makeImage("Loading...")); iconText = ""; String format = reader.getFormat(); formatText = format; formatTip = format; resText = reader.getSizeX() + " x " + reader.getSizeY(); zctText = reader.getSizeZ() + "Z x " + reader.getSizeT() + "T x " + reader.getSizeC() + "C"; typeText = reader.getRGBChannelCount() + " x " + FormatTools.getPixelTypeString(reader.getPixelType()); SwingUtilities.invokeLater(refresher); // open middle image thumbnail int z = reader.getSizeZ() / 2; int t = reader.getSizeT() / 2; int ndx = reader.getIndex(z, 0, t); BufferedImage thumb = null; try { thumb = reader.openThumbImage(ndx); } catch (FormatException exc) { LOGGER.debug("Failed to read thumbnail #{} from {}", new Object[] {ndx, id}, exc); } catch (IOException exc) { LOGGER.debug("Failed to read thumbnail #{} from {}", new Object[] {ndx, id}, exc); } icon = new ImageIcon(thumb == null ? makeImage("Failed") : thumb); iconText = ""; SwingUtilities.invokeLater(refresher); } catch (Exception exc) { LOGGER.info("", exc); icon = null; iconText = "Thumbnail failure"; formatText = resText = zctText = typeText = ""; iconTip = loadId; formatTip = resTip = zctTip = typeTip = ""; SwingUtilities.invokeLater(refresher); } } } // -- PreviewPane API methods -- /** Closes the underlying image reader. */ public void close() throws IOException { if (reader != null) { reader.close(); } } // -- Helper methods -- /** * Creates a blank image with the given message painted on top (e.g., * a loading or error message), matching the size of the active reader's * thumbnails. */ private BufferedImage makeImage(String message) { int w = reader.getThumbSizeX(), h = reader.getThumbSizeY(); if (w < 128) w = 128; if (h < 32) h = 32; BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); Graphics2D g = image.createGraphics(); Rectangle2D.Float r = (Rectangle2D.Float) g.getFont().getStringBounds(message, g.getFontRenderContext()); g.drawString(message, (w - r.width) / 2, (h - r.height) / 2 + r.height); g.dispose(); return image; } }