/**
*
*/
package icy.gui.dialog;
import icy.file.FileUtil;
import icy.file.ImageFileFormat;
import icy.file.Saver;
import icy.file.SequenceFileExporter;
import icy.main.Icy;
import icy.preferences.ApplicationPreferences;
import icy.preferences.XMLPreferences;
import icy.sequence.Sequence;
import icy.system.thread.ThreadUtil;
import icy.util.StringUtil;
import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;
import loci.formats.IFormatWriter;
import loci.formats.gui.ExtensionFileFilter;
import loci.plugins.out.Exporter;
/**
* Saver dialog used to save resource or image from the {@link Exporter} or {@link SequenceFileExporter}.
*
* @author Stephane
* @see Saver
*/
public class SaverDialog extends JFileChooser
{
private static final String PREF_ID = "frame/imageSaver";
private static final String ID_WIDTH = "width";
private static final String ID_HEIGHT = "height";
private static final String ID_PATH = "path";
private static final String ID_MULTIPLEFILE = "multipleFile";
private static final String ID_OVERWRITENAME = "overwriteName";
private static final String ID_FPS = "fps";
private static final String ID_EXTENSION = "extension";
// GUI
private SaverOptionPanel settingPanel;
// internal
private final XMLPreferences preferences;
private final boolean singleZ;
private final boolean singleT;
private final boolean singleImage;
/**
* <b>Saver Dialog</b><br>
* <br>
* Display a dialog to select the destination file then save the specified sequence.<br>
* <br>
* To only get selected file from the dialog you must do:<br>
* <code> ImageSaverDialog dialog = new ImageSaverDialog(sequence, false);</code><br>
* <code> File selectedFile = dialog.getSelectedFile()</code><br>
* <br>
* To directly save specified sequence to the selected file just use:<br>
* <code>new ImageSaverDialog(sequence, true);</code><br>
* or<br>
* <code>new ImageSaverDialog(sequence);</code>
*
* @param sequence
* The {@link Sequence} we want to save.
* @param autoSave
* If true the sequence is automatically saved to selected file.
*/
public SaverDialog(Sequence sequence, boolean autoSave)
{
super();
preferences = ApplicationPreferences.getPreferences().node(PREF_ID);
singleZ = (sequence.getSizeZ() == 1);
singleT = (sequence.getSizeT() == 1);
singleImage = singleZ && singleT;
// can't use WindowsPositionSaver as JFileChooser is a fake JComponent
// only dimension is stored
setCurrentDirectory(new File(preferences.get(ID_PATH, "")));
setPreferredSize(new Dimension(preferences.getInt(ID_WIDTH, 600), preferences.getInt(ID_HEIGHT, 400)));
setDialogTitle("Save image file");
// remove default filter
removeChoosableFileFilter(getAcceptAllFileFilter());
// then add our supported save format
addChoosableFileFilter(ImageFileFormat.TIFF.getExtensionFileFilter());
addChoosableFileFilter(ImageFileFormat.PNG.getExtensionFileFilter());
addChoosableFileFilter(ImageFileFormat.JPG.getExtensionFileFilter());
addChoosableFileFilter(ImageFileFormat.AVI.getExtensionFileFilter());
// set last used file filter
setFileFilter(getFileFilter(preferences.get(ID_EXTENSION, ImageFileFormat.TIFF.getDescription())));
setMultiSelectionEnabled(false);
// setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
// so the filename information is not lost when changing directory
setFileSelectionMode(JFileChooser.FILES_ONLY);
// get filename without extension
String filename = FileUtil.getFileName(sequence.getOutputFilename(false), false);
// empty filename --> use sequence name as default filename
if (StringUtil.isEmpty(filename))
filename = sequence.getName();
if (!StringUtil.isEmpty(filename))
{
filename = FileUtil.cleanPath(filename);
// test if filename has already a valid extension
final String ext = getDialogExtension(filename);
// remove file extension
if (ext != null)
FileUtil.setExtension(filename, "");
// set dialog filename
setSelectedFile(new File(filename));
}
// create extra setting panel
settingPanel = new SaverOptionPanel();
settingPanel.setMultipleFiles(preferences.getBoolean(ID_MULTIPLEFILE, false));
settingPanel.setOverwriteMetadata(preferences.getBoolean(ID_OVERWRITENAME, false));
settingPanel.setFramePerSecond(preferences.getInt(ID_FPS, 15));
setAccessory(settingPanel);
updateSettingPanel();
// listen file filter change
addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent evt)
{
updateSettingPanel();
}
});
ImageFileFormat fileFormat = null;
IFormatWriter writer = null;
boolean accepted = false;
while (!accepted)
{
// display Saver dialog
final int value = showSaveDialog(Icy.getMainInterface().getMainFrame());
// action canceled --> stop here
if (value != JFileChooser.APPROVE_OPTION)
break;
// get selected file format and associated writer
fileFormat = getSelectedFileFormat();
writer = Saver.getWriter(fileFormat);
// selected writer is not compatible ?
if (!isCompatible(fileFormat, sequence))
{
// incompatible saver for this sequence
// new IncompatibleImageFormatDialog();
// return;
// display a confirm dialog about possible loss in save operation
accepted = ConfirmDialog.confirm("Warning", "Some information will be lost in the " + fileFormat
+ " saved file(s). Do you want to continue ?");
}
else
accepted = true;
}
// only if accepted...
if (accepted)
{
File file = getSelectedFile();
final String outFilename = file.getAbsolutePath();
// destination is a folder ?
if (isFolderRequired())
{
// remove extension
file = new File(FileUtil.setExtension(outFilename, ""));
// set it so we can get it from getSelectedFile()
setSelectedFile(file);
}
else
{
// test and add extension if needed
final ExtensionFileFilter extensionFilter = (ExtensionFileFilter) getFileFilter();
// add file filter extension to filename if not already present
if (!hasExtension(outFilename.toLowerCase(), extensionFilter))
{
file = new File(outFilename + "." + extensionFilter.getExtension());
// set it so we can get it from getSelectedFile()
setSelectedFile(file);
}
}
// save requested ?
if (autoSave)
{
// ask for confirmation as file already exists
if (!file.exists() || ConfirmDialog.confirm("Overwrite existing file(s) ?"))
{
if (file.exists())
FileUtil.delete(file, true);
// store current path
preferences.put(ID_PATH, getCurrentDirectory().getAbsolutePath());
// overwrite sequence name with filename
if (isOverwriteNameEnabled())
sequence.setName(FileUtil.getFileName(file.getAbsolutePath(), false));
final Sequence s = sequence;
final File f = file;
final IFormatWriter w = writer;
// do save in background process
ThreadUtil.bgRun(new Runnable()
{
@Override
public void run()
{
Saver.save(w, s, f, getFps(), isSaveAsMultipleFilesEnabled(), true, true);
}
});
}
}
// store interface option
preferences.putInt(ID_WIDTH, getWidth());
preferences.putInt(ID_HEIGHT, getHeight());
// save this information only for TIFF format
if (fileFormat == ImageFileFormat.TIFF)
preferences.putBoolean(ID_MULTIPLEFILE, isSaveAsMultipleFilesEnabled());
preferences.putBoolean(ID_OVERWRITENAME, settingPanel.getOverwriteMetadata());
// save this information only for AVI format
if (fileFormat == ImageFileFormat.AVI)
preferences.putInt(ID_FPS, getFps());
preferences.put(ID_EXTENSION, getFileFilter().getDescription());
}
}
/**
* <b>Saver Dialog</b><br>
* <br>
* Display a dialog to select the destination file then save the specified sequence.
*
* @param sequence
* The {@link Sequence} we want to save.
*/
public SaverDialog(Sequence sequence)
{
this(sequence, true);
}
/**
* @deprecated Use {@link #SaverDialog(Sequence, boolean)} instead
*/
@Deprecated
public SaverDialog(Sequence sequence, int defZ, int defT, boolean autoSave)
{
this(sequence, autoSave);
}
/**
* @deprecated Use {@link #SaverDialog(Sequence)} instead
*/
@Deprecated
public SaverDialog(Sequence sequence, int defZ, int defT)
{
this(sequence, true);
}
protected FileFilter getFileFilter(String description)
{
final FileFilter[] filters = getChoosableFileFilters();
for (FileFilter filter : filters)
if (StringUtil.equals(filter.getDescription(), description))
return filter;
// default one
return ImageFileFormat.TIFF.getExtensionFileFilter();
}
private static boolean hasExtension(String name, ExtensionFileFilter extensionFilter)
{
return getExtension(name, extensionFilter) != null;
}
private static String getExtension(String name, ExtensionFileFilter extensionFilter)
{
for (String ext : extensionFilter.getExtensions())
if (name.endsWith(ext.toLowerCase()))
return ext;
return null;
}
private String getDialogExtension(String name)
{
for (FileFilter filter : getChoosableFileFilters())
{
final String ext = getExtension(name, (ExtensionFileFilter) filter);
if (ext != null)
return ext;
}
return null;
}
public ImageFileFormat getSelectedFileFormat()
{
final FileFilter ff = getFileFilter();
// default
if ((ff == null) || !(ff instanceof ExtensionFileFilter))
return ImageFileFormat.TIFF;
return ImageFileFormat.getWriteFormat(((ExtensionFileFilter) ff).getExtension(), ImageFileFormat.TIFF);
}
/**
* Returns <code>true</code> if we require a folder to save the sequence with selected options.
*/
public boolean isFolderRequired()
{
return !singleImage && isSaveAsMultipleFilesEnabled();
}
/**
* Returns <code>true</code> if user chosen to save the sequence as multiple image files.
*/
public boolean isSaveAsMultipleFilesEnabled()
{
return settingPanel.isMultipleFilesVisible() && settingPanel.getMultipleFiles();
}
/**
* Returns <code>true</code> if user chosen to overwrite the sequence internal name by filename.
*/
public boolean isOverwriteNameEnabled()
{
return settingPanel.isOverwriteMetadataVisible() && settingPanel.getOverwriteMetadata();
}
/**
* Returns the desired FPS (Frame Per Second, only for AVI file).
*/
public int getFps()
{
if (settingPanel.isFramePerSecondVisible())
return settingPanel.getFramePerSecond();
return 1;
}
/**
* @deprecated
*/
@Deprecated
@SuppressWarnings("static-method")
public int getZMin()
{
return 0;
}
/**
* @deprecated
*/
@Deprecated
@SuppressWarnings("static-method")
public int getZMax()
{
return 0;
}
/**
* @deprecated
*/
@Deprecated
@SuppressWarnings("static-method")
public int getTMin()
{
return 0;
}
/**
* @deprecated
*/
@Deprecated
@SuppressWarnings("static-method")
public int getTMax()
{
return 0;
}
void updateSettingPanel()
{
final ImageFileFormat fileFormat = getSelectedFileFormat();
// single image, no need to display selection option
if (singleImage)
{
settingPanel.setMultipleFilesVisible(false);
settingPanel.setForcedMultipleFilesOff();
}
else
{
switch (fileFormat)
{
case AVI:
settingPanel.setMultipleFilesVisible(true);
settingPanel.setForcedMultipleFilesOff();
break;
case JPG:
case PNG:
settingPanel.setMultipleFilesVisible(true);
settingPanel.setForcedMultipleFilesOn();
break;
case TIFF:
settingPanel.setMultipleFilesVisible(true);
settingPanel.removeForcedMultipleFiles();
settingPanel.setMultipleFiles(preferences.getBoolean(ID_MULTIPLEFILE, false));
break;
}
}
settingPanel.setFramePerSecondVisible(fileFormat == ImageFileFormat.AVI);
}
private boolean isCompatible(ImageFileFormat fileFormat, Sequence sequence)
{
if (fileFormat == ImageFileFormat.AVI)
{
// with AVI we force single file saving so we can't save 3D image.
if (!singleZ)
return false;
}
// just need to test against colormodel now
return Saver.isCompatible(fileFormat, sequence.getColorModel());
}
}