//
// PreviewPane.java
//
/*
LOCI Bio-Formats package for reading and converting biological file formats.
Copyright (C) 2005-@year@ Melissa Linkert, Curtis Rueden, Chris Allan,
Eric Kjellman and Brian Loranger.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package loci.formats.gui;
import java.awt.Dimension;
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.*;
import javax.swing.border.EmptyBorder;
import loci.formats.*;
/**
* PreviewPane is a panel for use as a JFileChooser accessory, displaying
* a thumbnail for the selected image, loaded in a separate thread.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/loci/formats/gui/PreviewPane.java">Trac</a>,
* <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/gui/PreviewPane.java">SVN</a></dd></dl>
*/
public class PreviewPane extends JPanel
implements PropertyChangeListener, Runnable
{
// -- Fields --
/** Reader for use when loading thumbnails. */
protected IFormatReader reader;
/** Labels containing thumbnail and dimensional information. */
protected JLabel iconLabel, resLabel, zctLabel, typeLabel;
/** Thumbnail loading thread. */
protected Thread loader;
/** Flag indicating whether loader thread should keep running. */
protected boolean loaderAlive;
/** Current ID to load. */
protected String loadId;
/** Last ID loaded. */
protected String lastId;
// -- Constructors --
/** Constructs a preview pane for the given file chooser. */
public PreviewPane(JFileChooser jc) {
super();
setBorder(new EmptyBorder(0, 10, 0, 0));
reader = new ImageReader();
reader.setNormalized(true);
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));
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);
if (jc != null) {
jc.setAccessory(this);
jc.addPropertyChangeListener(this);
// start separate loader thread
loaderAlive = true;
loader = new Thread(this, "Preview");
loader.start();
}
}
// -- Component API methods --
/* @see java.awt.Component.getPreferredSize() */
public Dimension getPreferredSize() {
Dimension prefSize = super.getPreferredSize();
return new Dimension(128, prefSize.height);
}
// -- PropertyChangeListener API methods --
/**
* Property change event, to listen for when a new
* file is selected, or the file chooser closes.
*/
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 selection = (File) e.getNewValue();
if (selection == null) return;
loadId = selection.getAbsolutePath();
}
// -- Runnable API methods --
/** Thumbnail loading routine. */
public void run() {
while (loaderAlive) {
try { Thread.sleep(100); }
catch (InterruptedException exc) { LogTools.trace(exc); }
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;
break;
}
}
if (found) continue;
}
lastId = id;
iconLabel.setIcon(null);
resLabel.setText(id == null ? "" : "Reading...");
zctLabel.setText("");
typeLabel.setText("");
if (id == null) continue;
try { reader.setId(id); }
catch (FormatException exc) {
LogTools.trace(exc);
resLabel.setText("Unsupported");
boolean badFormat = exc.getMessage().startsWith("Unknown file format");
zctLabel.setText(badFormat ? "format" : "file");
continue;
}
catch (IOException exc) {
LogTools.trace(exc);
resLabel.setText("Unsupported");
zctLabel.setText("file");
continue;
}
if (id != loadId) continue;
iconLabel.setIcon(id == null ? null :
new ImageIcon(makeImage("Loading...")));
resLabel.setText(reader.getSizeX() + " x " + reader.getSizeY());
zctLabel.setText(reader.getSizeZ() + "Z x " +
reader.getSizeT() + "T x " + reader.getSizeC() + "C");
typeLabel.setText(reader.getRGBChannelCount() + " x " +
FormatTools.getPixelTypeString(reader.getPixelType()));
// 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) { LogTools.trace(exc); }
catch (IOException exc) { LogTools.trace(exc); }
iconLabel.setIcon(new ImageIcon(thumb == null ?
makeImage("Failed") : thumb));
repaint();
}
}
// -- 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;
}
}