//
// 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();
    }
  }

  // -- Componenet 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;
  }

}

@@ -3373,17 +3373,16 @@
 Componen
-e
 t API me

Fix typo.