001package icy.gui.dialog;
002
003import java.awt.Dimension;
004import java.awt.Rectangle;
005import java.beans.PropertyChangeEvent;
006import java.beans.PropertyChangeListener;
007import java.io.File;
008import java.util.Collection;
009import java.util.List;
010
011import javax.swing.JFileChooser;
012import javax.swing.filechooser.FileFilter;
013
014import icy.file.FileImporter;
015import icy.file.FileUtil;
016import icy.file.Loader;
017import icy.file.SequenceFileImporter;
018import icy.file.SequenceFileSticher;
019import icy.file.SequenceFileSticher.SequenceFileGroup;
020import icy.gui.dialog.LoaderOptionPanel.LoaderLoadingType;
021import icy.main.Icy;
022import icy.preferences.ApplicationPreferences;
023import icy.preferences.GeneralPreferences;
024import icy.preferences.XMLPreferences;
025import icy.type.collection.CollectionUtil;
026import icy.util.StringUtil;
027
028/**
029 * Loader dialog used to load resource or image from the {@link FileImporter} or
030 * {@link SequenceFileImporter}.
031 * 
032 * @author Stephane
033 * @see Loader
034 */
035public class LoaderDialog extends JFileChooser implements PropertyChangeListener
036{
037    /**
038     * 
039     */
040    private static final long serialVersionUID = 5162434537949723956L;
041
042    public static class AllImagesFileFilter extends FileFilter
043    {
044        @Override
045        public boolean accept(File file)
046        {
047            if (file.isDirectory())
048                return true;
049
050            return !Loader.canDiscardImageFile(file.getName());
051        }
052
053        @Override
054        public String getDescription()
055        {
056            return "All images file";
057        }
058    }
059
060    public static class AllFileFilter extends FileFilter
061    {
062        @Override
063        public boolean accept(File file)
064        {
065            return file.exists();
066        }
067
068        @Override
069        public String getDescription()
070        {
071            return "All files";
072        }
073    }
074
075    public static AllImagesFileFilter allImagesFileFilter = new AllImagesFileFilter();
076    public static AllFileFilter allFileFilter = new AllFileFilter();
077
078    private static final String PREF_ID = "frame/imageLoader";
079
080    private static final String ID_WIDTH = "width";
081    private static final String ID_HEIGTH = "heigth";
082    private static final String ID_LOADTYPE = "loadtype";
083    private static final String ID_EXTENSION = "extension";
084
085    // GUI
086    protected final LoaderOptionPanel optionPanel;
087    protected final List<SequenceFileImporter> sequenceImporters;
088    protected final List<FileImporter> fileImporters;
089
090    /**
091     * Display a dialog to select image or resource file(s) and load them.<br>
092     * <br>
093     * To only get selected files from the dialog you must do:<br>
094     * <code> LoaderDialog dialog = new LoaderDialog(false);</code><br>
095     * <code> File[] selectedFiles = dialog.getSelectedFiles()</code><br>
096     * <br>
097     * To directly load selected files just use:<br>
098     * <code>new LoaderDialog(true);</code><br>
099     * or<br>
100     * <code>new LoaderDialog();</code>
101     * 
102     * @param defaultPath
103     *        default file path (can be null)
104     * @param region
105     *        default XY region (can be null)
106     * @param series
107     *        default series (can be -1)
108     * @param autoLoad
109     *        If true the selected file(s) are automatically loaded.
110     */
111    public LoaderDialog(String defaultPath, Rectangle region, int series, boolean autoLoad)
112    {
113        super();
114
115        final XMLPreferences preferences = ApplicationPreferences.getPreferences().node(PREF_ID);
116        sequenceImporters = Loader.getSequenceFileImporters();
117        fileImporters = Loader.getFileImporters();
118
119        // create option panel
120        optionPanel = new LoaderOptionPanel(
121                LoaderLoadingType.values()[preferences.getInt(ID_LOADTYPE, LoaderLoadingType.GROUP.ordinal())]);
122
123        // we can only store dimension for JFileChooser
124        setPreferredSize(new Dimension(preferences.getInt(ID_WIDTH, 600), preferences.getInt(ID_HEIGTH, 400)));
125
126        setMultiSelectionEnabled(true);
127        setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
128
129        setAcceptAllFileFilterUsed(false);
130        resetChoosableFileFilters();
131
132        // add file filter from importers
133        for (SequenceFileImporter importer : sequenceImporters)
134        {
135            final List<FileFilter> filters = importer.getFileFilters();
136
137            if (filters != null)
138            {
139                for (FileFilter filter : filters)
140                    addChoosableFileFilter(filter);
141            }
142        }
143        for (FileImporter importer : fileImporters)
144        {
145            final List<FileFilter> filters = importer.getFileFilters();
146
147            if (filters != null)
148            {
149                for (FileFilter filter : filters)
150                    addChoosableFileFilter(filter);
151            }
152        }
153        // add "all files" filter
154        addChoosableFileFilter(allFileFilter);
155
156        // we use a default path ?
157        if (defaultPath != null)
158        {
159            // set all files filter
160            setFileFilter(allFileFilter);
161
162            final File file = new File(defaultPath);
163
164            if (file.isDirectory())
165                setCurrentDirectory(file);
166            else
167                setSelectedFile(file);
168        }
169        else
170        {
171            // set last used file filter
172            setFileFilter(getFileFilter(preferences.get(ID_EXTENSION, allImagesFileFilter.getDescription())));
173            // set last used directory
174            setCurrentDirectory(new File(GeneralPreferences.getLoaderFolder()));
175        }
176
177        // setting GUI
178        updateGUI();
179
180        // we use a default path ?
181        if (defaultPath != null)
182        {
183            // refresh preview
184            optionPanel.updatePreview(new String[] {defaultPath}, series);
185            // updateOptionPanel();
186            // have a default XY region ?
187            if (region != null)
188                optionPanel.setXYRegion(region);
189        }
190
191        // listen file filter change
192        addPropertyChangeListener(this);
193
194        // display loader
195        final int value = showOpenDialog(Icy.getMainInterface().getMainFrame());
196
197        // action confirmed ?
198        if (value == JFileChooser.APPROVE_OPTION)
199        {
200            // store current path
201            GeneralPreferences.setLoaderFolder(getCurrentDirectory().getAbsolutePath());
202            preferences.putInt(ID_LOADTYPE, optionPanel.getLoadingType().ordinal());
203            preferences.put(ID_EXTENSION, getFileFilter().getDescription());
204
205            // load if requested
206            if (autoLoad)
207            {
208                // get selected paths
209                final List<String> paths = CollectionUtil.asList(FileUtil.toPaths(getSelectedFiles()));
210                // first path
211                final String firstPath = paths.get(0);
212                // get the selected importer from file filter
213                final Object importer = getSelectedImporter();
214
215                // multiple files or folder loading
216                if ((paths.size() > 1) || FileUtil.isDirectory(firstPath))
217                {
218                    if (importer instanceof FileImporter)
219                    {
220                        // load selected non image file(s)
221                        Loader.load((FileImporter) importer, paths, true);
222                    }
223                    else
224                    {
225                        if (isSeparateSequenceSelected())
226                            // load selected image file(s) separately
227                            Loader.load((SequenceFileImporter) importer, paths, true, false, true);
228                        else
229                        {
230                            // build groups
231                            final Collection<SequenceFileGroup> groups = SequenceFileSticher
232                                    .groupAllFiles((SequenceFileImporter) importer, paths, isAutoOrderSelected(), null);
233
234                            // then load them
235                            for (SequenceFileGroup group : groups)
236                            {
237                                // we don't have series for image grouped loading
238                                Loader.load(group, optionPanel.getResolutionLevel(), optionPanel.getXYRegion(),
239                                        optionPanel.getFullZRange() ? -1 : optionPanel.getZMin(),
240                                        optionPanel.getFullZRange() ? -1 : optionPanel.getZMax(),
241                                        optionPanel.getFullTRange() ? -1 : optionPanel.getTMin(),
242                                        optionPanel.getFullTRange() ? -1 : optionPanel.getTMax(),
243                                        optionPanel.getChannel(), false, true, true);
244                            }
245                        }
246                    }
247                }
248                else
249                {
250                    // single file loading
251                    if (importer instanceof FileImporter)
252                    {
253                        // load selected non image file
254                        Loader.load((FileImporter) importer, paths, true);
255                    }
256                    else
257                    {
258                        // load selected image file with advanced option
259                        Loader.load((SequenceFileImporter) importer, firstPath, optionPanel.getSeries(),
260                                optionPanel.getResolutionLevel(), optionPanel.getXYRegion(),
261                                optionPanel.getFullZRange() ? -1 : optionPanel.getZMin(),
262                                optionPanel.getFullZRange() ? -1 : optionPanel.getZMax(),
263                                optionPanel.getFullTRange() ? -1 : optionPanel.getTMin(),
264                                optionPanel.getFullTRange() ? -1 : optionPanel.getTMax(), optionPanel.getChannel(),
265                                isSeparateSequenceSelected(), true, true);
266                    }
267                }
268            }
269        }
270
271        // store interface option
272        preferences.putInt(ID_WIDTH, getWidth());
273        preferences.putInt(ID_HEIGTH, getHeight());
274    }
275
276    /**
277     * Display a dialog to select image or resource file(s) and load them.<br>
278     * <br>
279     * To only get selected files from the dialog you must do:<br>
280     * <code> LoaderDialog dialog = new LoaderDialog(false);</code><br>
281     * <code> File[] selectedFiles = dialog.getSelectedFiles()</code><br>
282     * <br>
283     * To directly load selected files just use:<br>
284     * <code>new LoaderDialog(true);</code><br>
285     * or<br>
286     * <code>new LoaderDialog();</code>
287     * 
288     * @param defaultPath
289     *        default file path (can be null)
290     * @param region
291     *        default XY region (can be null)
292     * @param autoLoad
293     *        If true the selected file(s) are automatically loaded.
294     */
295    public LoaderDialog(String defaultPath, Rectangle region, boolean autoLoad)
296    {
297        this(defaultPath, region, -1, autoLoad);
298    }
299
300    /**
301     * Display a dialog to select image or resource file(s) and load them.<br>
302     * <br>
303     * To only get selected files from the dialog you must do:<br>
304     * <code> LoaderDialog dialog = new LoaderDialog(false);</code><br>
305     * <code> File[] selectedFiles = dialog.getSelectedFiles()</code><br>
306     * <br>
307     * To directly load selected files just use:<br>
308     * <code>new LoaderDialog(true);</code><br>
309     * or<br>
310     * <code>new LoaderDialog();</code>
311     * 
312     * @param autoLoad
313     *        If true the selected file(s) are automatically loaded.
314     */
315    public LoaderDialog(boolean autoLoad)
316    {
317        this(null, null, autoLoad);
318    }
319
320    /**
321     * Display a dialog to select file(s) and load them.
322     */
323    public LoaderDialog()
324    {
325        this(null, null, true);
326    }
327
328    protected FileFilter getFileFilter(String description)
329    {
330        final FileFilter[] filters = getChoosableFileFilters();
331
332        for (FileFilter filter : filters)
333            if (StringUtil.equals(filter.getDescription(), description))
334                return filter;
335
336        // take first filter by default
337        if (filters.length > 0)
338            return filters[0];
339
340        return null;
341    }
342
343    protected int getFileFilterIndex()
344    {
345        final FileFilter[] filters = getChoosableFileFilters();
346        final FileFilter filter = getFileFilter();
347
348        for (int i = 0; i < filters.length; i++)
349            if (filter == filters[i])
350                return i;
351
352        return -1;
353    }
354
355    protected boolean isImageFilter()
356    {
357        return getSelectedImporter() instanceof SequenceFileImporter;
358    }
359
360    protected boolean isAllFileFilter()
361    {
362        return getFileFilter() == allFileFilter;
363    }
364
365    protected Object getImporter(int filterIndex)
366    {
367        int ind = 0;
368
369        for (SequenceFileImporter importer : sequenceImporters)
370        {
371            final List<FileFilter> filters = importer.getFileFilters();
372            final int count = (filters != null) ? filters.size() : 0;
373
374            if (filterIndex < (ind + count))
375                return importer;
376
377            ind += count;
378        }
379        for (FileImporter importer : fileImporters)
380        {
381            final List<FileFilter> filters = importer.getFileFilters();
382            final int count = (filters != null) ? filters.size() : 0;
383
384            if (filterIndex < (ind + count))
385                return importer;
386
387            ind += count;
388        }
389
390        return null;
391    }
392
393    public Object getSelectedImporter()
394    {
395        return getImporter(getFileFilterIndex());
396    }
397
398    /**
399     * Returns true if user checked the "Separate sequence" option.
400     */
401    public boolean isSeparateSequenceSelected()
402    {
403        return optionPanel.isSeparateSequenceSelected();
404    }
405
406    /**
407     * Returns true if user checked the "Auto Ordering" option.
408     */
409    public boolean isAutoOrderSelected()
410    {
411        return optionPanel.getLoadingType() == LoaderLoadingType.GROUP;
412    }
413
414    /**
415     * Get selected resolution level
416     */
417    public int getResolutionLevel()
418    {
419        return optionPanel.getResolutionLevel();
420    }
421
422    /**
423     * Get selected XY region (when region loading is enabled)
424     */
425    public Rectangle getXYRegion()
426    {
427        return optionPanel.getXYRegion();
428    }
429
430    /**
431     * Get minimum Z (Z range selection)
432     */
433    public int getZMin()
434    {
435        return optionPanel.getZMin();
436    }
437
438    /**
439     * Get maximum Z (Z range selection)
440     */
441    public int getZMax()
442    {
443        return optionPanel.getZMax();
444    }
445
446    /**
447     * Get minimum T (T range selection)
448     */
449    public int getTMin()
450    {
451        return optionPanel.getTMin();
452    }
453
454    /**
455     * Get maximum T (T range selection)
456     */
457    public int getTMax()
458    {
459        return optionPanel.getTMax();
460    }
461
462    /**
463     * Get channel selection (-1 for all)
464     */
465    public int getChannel()
466    {
467        return optionPanel.getChannel();
468    }
469
470    /**
471     * Get series selection (-1 for all)
472     */
473    public int getSeries()
474    {
475        return optionPanel.getSeries();
476    }
477
478    @Override
479    public void propertyChange(PropertyChangeEvent evt)
480    {
481        final String prop = evt.getPropertyName();
482
483        // filter change ?
484        if (prop.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY))
485            updateGUI();
486        // single selection change ?
487        // else if (prop.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY))
488        // {
489        // // multiple selection --> ignore this event
490        // if (getSelectedFiles().length > 1)
491        // return;
492        //
493        // final File f = getSelectedFile();
494        //
495        // // folder or file don't exist ?
496        // if ((f == null) || f.isDirectory() || !f.exists())
497        // optionPanel.updatePreview(new String[0]);
498        // else
499        // // refresh preview
500        // optionPanel.updatePreview(new String[] {f.getAbsolutePath()});
501        //
502        // // updateOptionPanel();
503        // }
504        // multi selection change ?
505        else if (prop.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY))
506        {
507            final File[] files = getSelectedFiles();
508
509            // single selection
510            if (files.length < 2)
511            {
512                final File f = getSelectedFile();
513
514                // folder or file don't exist ?
515                if ((f == null) || f.isDirectory() || !f.exists())
516                    optionPanel.updatePreview(new String[0]);
517                else
518                    // refresh preview
519                    optionPanel.updatePreview(new String[] {f.getAbsolutePath()});
520            }
521            else
522                optionPanel.updatePreview(FileUtil.toPaths(files));
523        }
524        // closing ? --> do some final operation on option panel
525        else if (prop.equals("JFileChooserDialogIsClosingProperty"))
526            optionPanel.closingFromEDT();
527        // else
528        // updateOptionPanel();
529    }
530
531    protected void updateGUI()
532    {
533        if (isImageFilter() || isAllFileFilter())
534        {
535            setDialogTitle("Load image file(s)");
536            setAccessory(optionPanel);
537            // updateOptionPanel();
538            revalidate();
539        }
540        else
541        {
542            setDialogTitle("Load file(s)");
543            setAccessory(null);
544            revalidate();
545        }
546    }
547
548    // protected void updateOptionPanel()
549    // {
550    // final int numFile = getSelectedFiles().length;
551    // final boolean multi;
552    //
553    // if (numFile > 1)
554    // multi = true;
555    // else
556    // {
557    // final File file = getSelectedFile();
558    // multi = (file != null) && file.isDirectory();
559    // }
560    //
561    // optionPanel.setMultiFile(multi);
562    // }
563}