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