001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.gui.dialog;
020
021import java.awt.BorderLayout;
022import java.awt.Dimension;
023import java.awt.GridBagConstraints;
024import java.awt.GridBagLayout;
025import java.awt.Insets;
026import java.awt.Rectangle;
027import java.awt.event.ActionEvent;
028import java.awt.event.ActionListener;
029import java.awt.geom.Rectangle2D;
030import java.io.IOException;
031import java.nio.channels.ClosedByInterruptException;
032import java.util.ArrayList;
033import java.util.List;
034import java.util.concurrent.Callable;
035
036import javax.swing.BorderFactory;
037import javax.swing.DefaultComboBoxModel;
038import javax.swing.JComboBox;
039import javax.swing.JLabel;
040import javax.swing.JPanel;
041import javax.swing.JSlider;
042import javax.swing.JToggleButton;
043import javax.swing.border.Border;
044import javax.swing.event.ChangeEvent;
045import javax.swing.event.ChangeListener;
046
047import icy.file.Loader;
048import icy.file.SequenceFileGroupImporter;
049import icy.file.SequenceFileImporter;
050import icy.file.SequenceFileSticher.SequenceFileGroup;
051import icy.gui.component.PopupPanel;
052import icy.gui.component.RangeComponent;
053import icy.gui.component.Region2DComponent;
054import icy.gui.component.SpecialValueSpinner;
055import icy.gui.component.ThumbnailComponent;
056import icy.gui.component.model.SpecialValueSpinnerModel;
057import icy.image.IcyBufferedImage;
058import icy.resource.ResourceUtil;
059import icy.sequence.MetaDataUtil;
060import icy.sequence.SequenceIdImporter;
061import icy.system.IcyExceptionHandler;
062import icy.system.thread.ThreadUtil;
063import icy.type.collection.CollectionUtil;
064import icy.util.OMEUtil;
065import ome.xml.meta.OMEXMLMetadata;
066import plugins.kernel.importer.LociImporterPlugin;
067
068public class LoaderOptionPanel extends JPanel
069{
070    public enum LoaderLoadingType
071    {
072        GROUP
073        {
074            @Override
075            public String toString()
076            {
077                return "Group files";
078            }
079        },
080        GROUP_NO_ORDERING
081        {
082            @Override
083            public String toString()
084            {
085                return "Group files (no ordering)";
086            }
087        },
088        NO_GROUP
089        {
090            @Override
091            public String toString()
092            {
093                return "Separate files";
094            }
095        }
096    }
097
098    private class PreviewSingleUpdate extends Thread
099    {
100        SequenceFileImporter importer;
101        List<SequenceFileImporter> importers;
102        List<String> files;
103        int z;
104        int t;
105        boolean imageRefreshOnly;
106        // boolean softInterrupted;
107
108        PreviewSingleUpdate(SequenceFileImporter importer, List<String> files, int z, int t, boolean imageRefreshOnly)
109        {
110            super("Preview single update");
111
112            this.importer = importer;
113            this.importers = null;
114            this.files = Loader.cleanNonImageFile(files);
115            this.z = z;
116            this.t = t;
117            this.imageRefreshOnly = imageRefreshOnly;
118            // softInterrupted = false;
119        }
120
121        public List<SequenceFileImporter> getSingleFileImporters()
122        {
123            // get importer and open image
124            final List<SequenceFileImporter> result = Loader.getSequenceFileImporters(files.get(0));
125
126            // adjust importers setting if needed
127            for (SequenceFileImporter imp : result)
128            {
129                // LOCI importer ?
130                if (imp instanceof LociImporterPlugin)
131                {
132                    // separate loading wanted ? --> disable grouping in LOCI importer
133                    if (isSeparateSequenceSelected())
134                        ((LociImporterPlugin) imp).setGroupFiles(false);
135                }
136            }
137
138            return result;
139        }
140
141        void getImporters()
142        {
143            // already done ? --> exit
144            if (importer != null)
145                return;
146
147            // importers for single file opening
148            importers = getSingleFileImporters();
149
150            // want to open a group of file ? --> insert the SequenceFileGroupImporter importer
151            if (isGroupedSequenceSelected() && isMultiFile())
152                importers.add(0, new SequenceFileGroupImporter());
153
154            // get default importer
155            importer = importers.get(0);
156        }
157
158        void close()
159        {
160            try
161            {
162                // close previous importer (shouldn't exist here)
163                if (importer != null)
164                    importer.close();
165            }
166            catch (IOException e)
167            {
168                // ignore
169            }
170
171            importer = null;
172        }
173
174        boolean internalOpen(SequenceFileImporter imp) throws ClosedByInterruptException
175        {
176            try
177            {
178                if (imp instanceof SequenceFileGroupImporter)
179                    ((SequenceFileGroupImporter) imp).open(files, SequenceIdImporter.FLAG_METADATA_MINIMUM);
180                else
181                    imp.open(files.get(0), SequenceIdImporter.FLAG_METADATA_MINIMUM);
182            }
183            catch (ClosedByInterruptException e)
184            {
185                throw e;
186            }
187            catch (Exception e)
188            {
189                try
190                {
191                    imp.close();
192                }
193                catch (Throwable e2)
194                {
195                    // ignore
196                }
197            }
198
199            // correctly opened ? --> use this importer
200            return imp.getOpened() != null;
201        }
202
203        boolean open() throws ClosedByInterruptException
204        {
205            // get importers first if not already done
206            getImporters();
207
208            // already opened ? --> exit
209            if (importer.getOpened() != null)
210                return true;
211
212            // case where we just have the good importer that we need to re-open
213            if (importers == null)
214                return internalOpen(importer);
215
216            // try to open from the given importers
217            for (SequenceFileImporter imp : importers)
218            {
219                // correctly opened ? --> use this importer
220                if (internalOpen(imp))
221                {
222                    importer = imp;
223                    return true;
224                }
225            }
226
227            // can't open
228            return false;
229        }
230
231        OMEXMLMetadata getMetaData() throws Exception
232        {
233            if (!open())
234                throw new Exception("Can't open importer !");
235
236            return importer.getOMEXMLMetaData();
237        }
238
239        IcyBufferedImage getThumbnail(int s) throws Exception
240        {
241            if (!open())
242                throw new Exception("Can't open importer !");
243
244            return importer.getThumbnail(s);
245        }
246
247        IcyBufferedImage getImage(int s, int res) throws Exception
248        {
249            if (!open())
250                throw new Exception("Can't open importer !");
251
252            if (pCh == -1)
253                return importer.getImage(s, res, z, t);
254
255            // specific channel
256            return importer.getImage(s, res, z, t, pCh);
257        }
258
259        public boolean isMultiFile()
260        {
261            try
262            {
263                // return true if we have multiple file selection
264                if (files.size() > 1)
265                    return true;
266
267                if (importer instanceof SequenceFileGroupImporter)
268                {
269                    final SequenceFileGroup group = ((SequenceFileGroupImporter) importer).getOpenedGroup();
270
271                    if (group != null)
272                        return (group.positions.size() > 1);
273                }
274            }
275            catch (Throwable t)
276            {
277                // ignore
278            }
279
280            return false;
281        }
282
283        // PreviewSingleUpdate(SequenceFileImporter importer, String[] files, int z, int t, boolean imageRefreshOnly)
284        // {
285        // this(importer, CollectionUtil.asList(files), z, t, imageRefreshOnly);
286        // }
287        //
288        // PreviewSingleUpdate(SequenceFileImporter importer, int z, int t)
289        // {
290        // this(importer, new String[0], z, t, true);
291        // }
292        //
293        // PreviewSingleUpdate(String[] files)
294        // {
295        // this(null, files, -1, -1, false);
296        // }
297        //
298        // PreviewSingleUpdate(List<String> files)
299        // {
300        // this(null, files, -1, -1, false);
301        // }
302
303        @Override
304        public void run()
305        {
306            // interrupt process
307            if (isInterrupted())
308                return;
309
310            // get current selected series
311            final int s = getSelectedSeries();
312
313            // only need to update image
314            if (imageRefreshOnly)
315            {
316                // can't re open importer ? --> nothing to do here
317                if ((importer == null) && (files.size() == 0))
318                    return;
319
320                try
321                {
322                    // use last - 1 resolution
323                    final int res = Math.max(0, resolutionSlider.getMaximum() - 1);
324
325                    try
326                    {
327                        // can't open ? --> error
328                        if (!open())
329                            throw new Exception("Can't open '" + files.get(0) + "' image file..");
330
331                        // interrupted ? --> stop here
332                        if (isInterrupted())
333                            return;
334
335                        // not defined --> use first
336                        if (z == -1)
337                            z = 0;
338                        // not defined --> use first
339                        if (t == -1)
340                            t = 0;
341
342                        // default position --> use thumbnail
343                        if ((z == 0) && (t == 0) && (pCh == -1))
344                            preview.setImage(getThumbnail(s));
345                        // specific image
346                        else
347                            preview.setImage(getImage(s, res));
348                    }
349                    finally
350                    {
351                        // just close internals importers
352                        if (importer instanceof SequenceFileGroupImporter)
353                            ((SequenceFileGroupImporter) importer).closeInternalsImporters();
354                    }
355                }
356                catch (ClosedByInterruptException e)
357                {
358                    // can't use importer anymore
359                    close();
360                }
361                catch (InterruptedException e2)
362                {
363                    // can't use importer anymore
364                    close();
365                }
366                catch (Throwable e)
367                {
368                    // can't use importer anymore
369                    if (e.getCause() instanceof ClosedByInterruptException)
370                        close();
371
372                    // no more update ? --> show that an error happened
373                    if (!previewUpdater.getNeedUpdate())
374                        preview.setImage(ResourceUtil.ICON_DELETE);
375                }
376
377                // image updated
378                return;
379            }
380
381            try
382            {
383                metadata = null;
384
385                // no files ?
386                if (files.size() == 0)
387                {
388                    preview.setImage(null);
389                    preview.setInfos("");
390                    metadata = OMEUtil.createOMEXMLMetadata();
391
392                    // use Callable as we can get interrupted here...
393                    ThreadUtil.invokeNow(new Callable<Boolean>()
394                    {
395                        @Override
396                        public Boolean call() throws Exception
397                        {
398                            // disable panel while we are loading metadata
399                            disablePanel();
400
401                            return Boolean.TRUE;
402                        }
403                    });
404
405                    // nothing more to do
406                    return;
407                }
408
409                // loading...
410                preview.setImage(ResourceUtil.ICON_WAIT);
411                preview.setInfos("loading...");
412
413                // use Callable as we can get interrupted here...
414                ThreadUtil.invokeNow(new Callable<Boolean>()
415                {
416
417                    @Override
418                    public Boolean call() throws Exception
419                    {
420                        // disable panel while we are loading metadata
421                        disablePanel();
422
423                        return Boolean.TRUE;
424                    }
425                });
426
427                // close previous importer (shouldn't exist here)
428                close();
429
430                // open file(s)...
431                if (!open())
432                    throw new Exception("Can't open '" + files.get(0) + "' image file..");
433
434                try
435                {
436                    // interrupted ? --> stop here
437                    if (isInterrupted())
438                        return;
439
440                    metadata = getMetaData();
441
442                    // update it as soon as possible (use Callable as we can get interrupted here...)
443                    ThreadUtil.invokeNow(new Callable<Boolean>()
444                    {
445
446                        @Override
447                        public Boolean call() throws Exception
448                        {
449                            // update panel
450                            updatePanel();
451
452                            return Boolean.TRUE;
453                        }
454                    });
455
456                    // now the different "metadata" fields are up to date
457                    metadataFieldsOk = true;
458
459                    // initial preview --> update default range and channel positions
460                    pZMin = getZMin();
461                    pZMax = getZMax();
462                    pTMin = getTMin();
463                    pTMax = getTMax();
464                    pCh = getChannel();
465
466                    final int sizeC = MetaDataUtil.getSizeC(metadata, s);
467
468                    // load metadata first
469                    preview.setInfos(MetaDataUtil.getSizeX(metadata, s) + " x " + MetaDataUtil.getSizeY(metadata, s)
470                            + " - " + MetaDataUtil.getSizeZ(metadata, s) + "Z x " + MetaDataUtil.getSizeT(metadata, s)
471                            + "T - " + sizeC + " ch (" + MetaDataUtil.getDataType(metadata, s) + ")");
472
473                    // interrupted ? --> stop here
474                    if (isInterrupted())
475                        return;
476
477                    // initial preview --> use thumbnail
478                    preview.setImage(getThumbnail(s));
479                }
480                finally
481                {
482                    // just close internals importers
483                    if (importer instanceof SequenceFileGroupImporter)
484                        ((SequenceFileGroupImporter) importer).closeInternalsImporters();
485                }
486            }
487            catch (ClosedByInterruptException e)
488            {
489                // can't use importer anymore
490                close();
491            }
492            catch (InterruptedException t)
493            {
494                // can't use importer anymore
495                close();
496            }
497            catch (Throwable t1)
498            {
499                // System.out.println("Preview: can't load all image information.");
500                // IcyExceptionHandler.showErrorMessage(t1, false);
501
502                // no more update ? --> show that an error happened
503                if (!previewUpdater.getNeedUpdate())
504                {
505                    // fatal error --> failed image
506                    preview.setImage(ResourceUtil.ICON_DELETE);
507
508                    // cannot even read metadata
509                    if (!metadataFieldsOk)
510                    {
511                        preview.setInfos("Cannot read file");
512
513                        try
514                        {
515                            // use Callable as we can get interrupted here...
516                            ThreadUtil.invokeNow(new Callable<Boolean>()
517                            {
518
519                                @Override
520                                public Boolean call() throws Exception
521                                {
522                                    // update panel
523                                    updatePanel();
524
525                                    return Boolean.TRUE;
526                                }
527                            });
528                        }
529                        catch (Throwable t2)
530                        {
531                            // probably interrupted...
532
533                        }
534                    }
535                }
536            }
537        }
538
539        // boolean isSoftInterrupted()
540        // {
541        // return softInterrupted;
542        // }
543        //
544        // void softInterrupt()
545        // {
546        // softInterrupted = true;
547        // }
548    }
549
550    private class PreviewUpdater extends Thread
551    {
552        SequenceFileImporter newImporter;
553        List<String> newFiles;
554        int newZ;
555        int newT;
556        boolean newImageRefreshOnly;
557
558        boolean needUpdate;
559
560        PreviewSingleUpdate singleUpdater;
561
562        PreviewUpdater()
563        {
564            super("Preview updater");
565
566            singleUpdater = null;
567            needUpdate = false;
568        }
569
570        public boolean getNeedUpdate()
571        {
572            return needUpdate;
573        }
574
575        public boolean isUpdating()
576        {
577            return getNeedUpdate() || ((singleUpdater != null) && singleUpdater.isAlive());
578        }
579
580        public boolean isMultiFile()
581        {
582            if (singleUpdater == null)
583                return false;
584
585            return singleUpdater.isMultiFile();
586        }
587
588        /**
589         * previous preview canceled ?
590         */
591        public boolean isPreviewCanceled()
592        {
593            if (singleUpdater == null)
594                return true;
595
596            return !singleUpdater.isAlive();
597        }
598
599        /**
600         * Cancel preview refresh
601         */
602        public void cancelPreview()
603        {
604            // interrupt (soft interrupt to not close IO channel) current preview update
605            if (singleUpdater != null)
606                singleUpdater.interrupt();
607        }
608
609        /**
610         * Asynchronous image preview refresh only.<br>
611         * ({@link #updatePreview(String)} should have be called once before to give the fileId)
612         */
613        protected synchronized void updatePreview(int z, int t)
614        {
615            // nothing to do
616            if (singleUpdater == null)
617                return;
618
619            // interrupt previous preview refresh
620            cancelPreview();
621
622            // prepare params
623            newImporter = singleUpdater.importer;
624            newFiles = singleUpdater.files;
625            // use previous value
626            if (z == -1)
627                newZ = singleUpdater.z;
628            else
629                newZ = z;
630            // use previous value
631            if (t == -1)
632                newT = singleUpdater.t;
633            else
634                newT = t;
635            newImageRefreshOnly = true;
636
637            // request preview update
638            needUpdate = true;
639        }
640
641        /**
642         * Asynchronous preview refresh (complete preview refresh)
643         */
644        public synchronized void updatePreview()
645        {
646            updatePreview(singleUpdater.files, series);
647        }
648
649        /**
650         * Asynchronous preview refresh.<br>
651         * ({@link #updatePreview(List, int)} should have be called once before to give the fileId)
652         */
653        public synchronized void updatePreview(int s)
654        {
655            updatePreview(singleUpdater.files, s);
656        }
657
658        /**
659         * Asynchronous preview refresh
660         */
661        public synchronized void updatePreview(String[] files, int s)
662        {
663            // interrupt previous preview refresh
664            cancelPreview();
665
666            // reset metadata and series index
667            metadata = null;
668            metadataFieldsOk = false;
669            series = s;
670
671            // prepare params
672            newImporter = null;
673            newFiles = CollectionUtil.asList(files);
674            newZ = -1;
675            newT = -1;
676            newImageRefreshOnly = false;
677
678            // request preview update
679            needUpdate = true;
680        }
681
682        /**
683         * Asynchronous preview refresh
684         */
685        public synchronized void updatePreview(List<String> files, int s)
686        {
687            // interrupt previous preview refresh
688            cancelPreview();
689
690            // reset metadata and series index
691            metadata = null;
692            metadataFieldsOk = false;
693            series = s;
694
695            // prepare params
696            newImporter = null;
697            newFiles = files;
698            newZ = -1;
699            newT = -1;
700            newImageRefreshOnly = false;
701
702            // request preview update
703            needUpdate = true;
704        }
705
706        // /**
707        // * Asynchronous preview refresh
708        // */
709        // public synchronized void updatePreview(String[] files)
710        // {
711        // updatePreview(files, -1);
712        // }
713
714        /**
715         * We are closing the Open Dialog.<br>
716         * Don't forget to call this method otherwise the updater thread will remain active !!<br>
717         * Ensure metadata are correctly loaded (it's important that this method is called from EDT)
718         */
719        public synchronized void close()
720        {
721            // interrupt preview
722            cancelPreview();
723
724            if (singleUpdater != null)
725            {
726                // need update metadata fields
727                if (!metadataFieldsOk)
728                {
729                    try
730                    {
731                        // open file(s) if needed...
732                        if (!singleUpdater.open())
733                            return;
734
735                        try
736                        {
737                            metadata = singleUpdater.importer.getOMEXMLMetaData();
738
739                            // update panel (we are on EDT)
740                            updatePanel();
741                        }
742                        finally
743                        {
744                            singleUpdater.close();
745                        }
746                    }
747                    catch (Throwable t)
748                    {
749                        // we tried...
750                    }
751                }
752            }
753
754            // finally interrupt the updater thread
755            interrupt();
756        }
757
758        @Override
759        public void run()
760        {
761            try
762            {
763                // interrupt process
764                while (!isInterrupted())
765                {
766                    // sleep a bit
767                    Thread.sleep(10);
768
769                    // need to be synchronized
770                    synchronized (this)
771                    {
772                        // previous preview done ?
773                        if (isPreviewCanceled())
774                        {
775                            // need preview update ?
776                            if (needUpdate)
777                            {
778                                // create new single updater and start it
779                                singleUpdater = new PreviewSingleUpdate(newImporter, newFiles, newZ, newT,
780                                        newImageRefreshOnly);
781                                singleUpdater.start();
782
783                                // done
784                                needUpdate = false;
785                            }
786                        }
787                    }
788                }
789            }
790            catch (InterruptedException t)
791            {
792                // no need to do more here...
793            }
794            catch (Throwable t)
795            {
796                System.out.println("Preview updater interrupted !");
797                IcyExceptionHandler.showErrorMessage(t, false);
798            }
799        }
800    }
801
802    /**
803     * 
804     */
805    private static final long serialVersionUID = 4180367632912879286L;
806
807    /**
808     * GUI
809     */
810    protected ThumbnailComponent preview;
811    protected JPanel optionsPanel;
812    protected PopupPanel popupPanel;
813    protected JComboBox loadingTypeCombo;
814    protected JLabel loadInSeparatedLabel;
815    protected JSlider resolutionSlider;
816    protected JLabel resolutionLevelLabel;
817    protected JLabel zRangeLabel;
818    protected JLabel tRangeLabel;
819    protected JLabel channelLabel;
820    protected RangeComponent zRangeComp;
821    protected RangeComponent tRangeComp;
822    protected SpecialValueSpinner channelSpinner;
823    protected JLabel resolutionFixLabel;
824    protected JToggleButton xyRegionLoadingToggle;
825    protected JLabel seriesLabel;
826    protected SpecialValueSpinner seriesSpinner;
827    protected Region2DComponent xyRegionComp;
828
829    // internals
830    protected boolean metadataFieldsOk;
831    protected PreviewUpdater previewUpdater;
832    protected OMEXMLMetadata metadata;
833    protected int series;
834    protected int pZMin;
835    protected int pZMax;
836    protected int pTMin;
837    protected int pTMax;
838    protected int pCh;
839    // protected boolean multiFile;
840    protected boolean updatingPanel;
841
842    /**
843     * Create the panel.
844     */
845    public LoaderOptionPanel(LoaderLoadingType loadType)
846    {
847        super();
848
849        metadataFieldsOk = false;
850        metadata = null;
851        series = -1;
852        pZMin = -1;
853        pZMax = -1;
854        pTMin = -1;
855        pTMax = -1;
856        pCh = -1;
857        updatingPanel = false;
858
859        initialize(loadType);
860
861        // need to be done *before* updatePanel
862        previewUpdater = new PreviewUpdater();
863        previewUpdater.start();
864
865        updatePanel();
866    }
867
868    private void initialize(LoaderLoadingType loadType)
869    {
870        setBorder(BorderFactory.createTitledBorder((Border) null));
871        setLayout(new BorderLayout());
872
873        preview = new ThumbnailComponent(false);
874        preview.setMinimumSize(new Dimension(200, 160));
875        preview.setPreferredSize(new Dimension(240, 200));
876        preview.setShortDisplay(true);
877
878        add(preview, BorderLayout.CENTER);
879
880        optionsPanel = new JPanel();
881        popupPanel = new PopupPanel("Advanced options", optionsPanel);
882        popupPanel.setExpanded(true);
883        add(popupPanel, BorderLayout.SOUTH);
884
885        GridBagLayout gbl_optionsPanel = new GridBagLayout();
886        gbl_optionsPanel.columnWidths = new int[] {0, 100, 0, 0, 0};
887        gbl_optionsPanel.rowHeights = new int[] {0, 0, 0, 0, 0, 0, 0};
888        gbl_optionsPanel.columnWeights = new double[] {0.0, 1.0, 1.0, 1.0, Double.MIN_VALUE};
889        gbl_optionsPanel.rowWeights = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
890        optionsPanel.setLayout(gbl_optionsPanel);
891        GridBagConstraints gbc_loadInSeparatedLabel = new GridBagConstraints();
892        gbc_loadInSeparatedLabel.anchor = GridBagConstraints.WEST;
893        gbc_loadInSeparatedLabel.insets = new Insets(0, 0, 5, 5);
894        gbc_loadInSeparatedLabel.gridx = 0;
895        gbc_loadInSeparatedLabel.gridy = 0;
896        loadInSeparatedLabel = new JLabel("Load type");
897        loadInSeparatedLabel.setToolTipText(
898                "Define if we try to group files / series or not (and eventually automatic set Z, T, C ordering from file name)");
899        optionsPanel.add(loadInSeparatedLabel, gbc_loadInSeparatedLabel);
900
901        loadingTypeCombo = new JComboBox();
902        loadingTypeCombo.setToolTipText(
903                "Define if we try to group files or not (and eventually automatic set Z, T, C ordering from file name)");
904        loadingTypeCombo.setModel(new DefaultComboBoxModel(LoaderLoadingType.values()));
905        loadingTypeCombo.setSelectedIndex(loadType.ordinal());
906        loadingTypeCombo.addActionListener(new ActionListener()
907        {
908            @Override
909            public void actionPerformed(ActionEvent e)
910            {
911                loadingTypeChanged();
912            }
913        });
914        GridBagConstraints gbc_loadingTypeCombo = new GridBagConstraints();
915        gbc_loadingTypeCombo.fill = GridBagConstraints.HORIZONTAL;
916        gbc_loadingTypeCombo.gridwidth = 3;
917        gbc_loadingTypeCombo.insets = new Insets(0, 0, 5, 0);
918        gbc_loadingTypeCombo.gridx = 1;
919        gbc_loadingTypeCombo.gridy = 0;
920        optionsPanel.add(loadingTypeCombo, gbc_loadingTypeCombo);
921
922        seriesLabel = new JLabel("Series");
923        seriesLabel.setToolTipText("Series to load (only for multi serie image)");
924        GridBagConstraints gbc_seriesLabel = new GridBagConstraints();
925        gbc_seriesLabel.anchor = GridBagConstraints.WEST;
926        gbc_seriesLabel.insets = new Insets(0, 0, 5, 5);
927        gbc_seriesLabel.gridx = 0;
928        gbc_seriesLabel.gridy = 1;
929        optionsPanel.add(seriesLabel, gbc_seriesLabel);
930
931        seriesSpinner = new SpecialValueSpinner(new SpecialValueSpinnerModel(-1, -1, 0, 1, -1, "ALL"));
932        seriesSpinner.setPreferredSize(new Dimension(50, 22));
933        seriesSpinner.addChangeListener(new ChangeListener()
934        {
935            @Override
936            public void stateChanged(ChangeEvent e)
937            {
938                seriesChanged();
939            }
940        });
941        seriesSpinner.setToolTipText("Series to load (only for multi serie image)");
942        GridBagConstraints gbc_seriesSpinner = new GridBagConstraints();
943        gbc_seriesSpinner.insets = new Insets(0, 0, 5, 5);
944        gbc_seriesSpinner.anchor = GridBagConstraints.WEST;
945        gbc_seriesSpinner.gridx = 1;
946        gbc_seriesSpinner.gridy = 1;
947        optionsPanel.add(seriesSpinner, gbc_seriesSpinner);
948
949        channelLabel = new JLabel("Channel");
950        channelLabel.setToolTipText("Channel to load");
951        GridBagConstraints gbc_channelLabel = new GridBagConstraints();
952        gbc_channelLabel.anchor = GridBagConstraints.EAST;
953        gbc_channelLabel.insets = new Insets(0, 0, 5, 5);
954        gbc_channelLabel.gridx = 2;
955        gbc_channelLabel.gridy = 1;
956        optionsPanel.add(channelLabel, gbc_channelLabel);
957
958        channelSpinner = new SpecialValueSpinner(new SpecialValueSpinnerModel(-1, -1, 0, 1, -1, "ALL"));
959        channelSpinner.setPreferredSize(new Dimension(50, 22));
960        channelSpinner.addChangeListener(new ChangeListener()
961        {
962            @Override
963            public void stateChanged(ChangeEvent e)
964            {
965                rangeChanged();
966            }
967        });
968        channelSpinner.setToolTipText("Channel to load");
969        GridBagConstraints gbc_channelSpinner = new GridBagConstraints();
970        gbc_channelSpinner.anchor = GridBagConstraints.EAST;
971        gbc_channelSpinner.insets = new Insets(0, 0, 5, 0);
972        gbc_channelSpinner.gridx = 3;
973        gbc_channelSpinner.gridy = 1;
974        optionsPanel.add(channelSpinner, gbc_channelSpinner);
975
976        resolutionFixLabel = new JLabel("Resolution");
977        resolutionFixLabel.setToolTipText("Select resolution level to open");
978        GridBagConstraints gbc_resolutionFixLabel = new GridBagConstraints();
979        gbc_resolutionFixLabel.anchor = GridBagConstraints.WEST;
980        gbc_resolutionFixLabel.insets = new Insets(0, 0, 5, 5);
981        gbc_resolutionFixLabel.gridx = 0;
982        gbc_resolutionFixLabel.gridy = 2;
983        optionsPanel.add(resolutionFixLabel, gbc_resolutionFixLabel);
984
985        resolutionSlider = new JSlider();
986        resolutionSlider.setSnapToTicks(true);
987        resolutionSlider.setMinimumSize(new Dimension(80, 22));
988        resolutionSlider.setMaximumSize(new Dimension(100, 22));
989        resolutionSlider.setPreferredSize(new Dimension(100, 22));
990        resolutionSlider.setToolTipText("Select resolution level to open");
991        resolutionSlider.setValue(0);
992        resolutionSlider.setMaximum(10);
993        resolutionSlider.setFocusable(false);
994        resolutionSlider.addChangeListener(new ChangeListener()
995        {
996            @Override
997            public void stateChanged(ChangeEvent e)
998            {
999                updateFinaleResolution();
1000            }
1001        });
1002
1003        resolutionLevelLabel = new JLabel("");
1004        GridBagConstraints gbc_resolutionLevelLabel = new GridBagConstraints();
1005        gbc_resolutionLevelLabel.anchor = GridBagConstraints.WEST;
1006        gbc_resolutionLevelLabel.insets = new Insets(0, 0, 5, 5);
1007        gbc_resolutionLevelLabel.gridx = 1;
1008        gbc_resolutionLevelLabel.gridy = 2;
1009        optionsPanel.add(resolutionLevelLabel, gbc_resolutionLevelLabel);
1010        GridBagConstraints gbc_resolutionSlider = new GridBagConstraints();
1011        gbc_resolutionSlider.fill = GridBagConstraints.BOTH;
1012        gbc_resolutionSlider.gridwidth = 2;
1013        gbc_resolutionSlider.insets = new Insets(0, 0, 5, 0);
1014        gbc_resolutionSlider.gridx = 2;
1015        gbc_resolutionSlider.gridy = 2;
1016        optionsPanel.add(resolutionSlider, gbc_resolutionSlider);
1017
1018        xyRegionLoadingToggle = new JToggleButton("XY region");
1019        xyRegionLoadingToggle.setFocusPainted(false);
1020        xyRegionLoadingToggle.setIconTextGap(0);
1021        xyRegionLoadingToggle.setMargin(new Insets(2, 4, 2, 4));
1022        xyRegionLoadingToggle
1023                .setToolTipText("Enable region loading (X,Y)-(W,H) in original resolution coordinates (pixel)");
1024        xyRegionLoadingToggle.addActionListener(new ActionListener()
1025        {
1026            @Override
1027            public void actionPerformed(ActionEvent e)
1028            {
1029                regionLoadingToggleChanged();
1030            }
1031        });
1032        GridBagConstraints gbc_xyRegionLoadingCheck = new GridBagConstraints();
1033        gbc_xyRegionLoadingCheck.fill = GridBagConstraints.BOTH;
1034        gbc_xyRegionLoadingCheck.insets = new Insets(0, 0, 5, 5);
1035        gbc_xyRegionLoadingCheck.gridx = 0;
1036        gbc_xyRegionLoadingCheck.gridy = 3;
1037        optionsPanel.add(xyRegionLoadingToggle, gbc_xyRegionLoadingCheck);
1038
1039        xyRegionComp = new Region2DComponent();
1040        xyRegionComp.setMaximumSize(new Dimension(160, 24));
1041        xyRegionComp.setInteger(true);
1042        xyRegionComp.setToolTipText("Rectangular region to load (X,Y,W,H) in original resolution coordinates");
1043        GridBagConstraints gbc_xyRegionComp = new GridBagConstraints();
1044        gbc_xyRegionComp.fill = GridBagConstraints.HORIZONTAL;
1045        gbc_xyRegionComp.gridwidth = 3;
1046        gbc_xyRegionComp.insets = new Insets(0, 0, 5, 0);
1047        gbc_xyRegionComp.gridx = 1;
1048        gbc_xyRegionComp.gridy = 3;
1049        optionsPanel.add(xyRegionComp, gbc_xyRegionComp);
1050
1051        zRangeLabel = new JLabel("Z range  ");
1052        zRangeLabel.setToolTipText("Z interval to load");
1053        GridBagConstraints gbc_zRangeLabel = new GridBagConstraints();
1054        gbc_zRangeLabel.anchor = GridBagConstraints.WEST;
1055        gbc_zRangeLabel.insets = new Insets(0, 0, 5, 5);
1056        gbc_zRangeLabel.gridx = 0;
1057        gbc_zRangeLabel.gridy = 4;
1058        optionsPanel.add(zRangeLabel, gbc_zRangeLabel);
1059
1060        zRangeComp = new RangeComponent();
1061        zRangeComp.addChangeListener(new ChangeListener()
1062        {
1063            @Override
1064            public void stateChanged(ChangeEvent e)
1065            {
1066                rangeChanged();
1067            }
1068        });
1069        zRangeComp.setToolTipText("Z interval to load");
1070        zRangeComp.setMinimumSize(new Dimension(130, 22));
1071        zRangeComp.setMaximumSize(new Dimension(180, 22));
1072        zRangeComp.getHighSpinner().setPreferredSize(new Dimension(50, 20));
1073        zRangeComp.getHighSpinner().setMaximumSize(new Dimension(50, 20));
1074        zRangeComp.getLowSpinner().setMaximumSize(new Dimension(50, 20));
1075        zRangeComp.getLowSpinner().setPreferredSize(new Dimension(50, 20));
1076        zRangeComp.setPreferredSize(new Dimension(180, 22));
1077        zRangeComp.getSlider().setPreferredSize(new Dimension(70, 22));
1078        zRangeComp.setSliderVisible(true);
1079        GridBagConstraints gbc_zRangeComp = new GridBagConstraints();
1080        gbc_zRangeComp.gridwidth = 3;
1081        gbc_zRangeComp.insets = new Insets(0, 0, 5, 0);
1082        gbc_zRangeComp.fill = GridBagConstraints.HORIZONTAL;
1083        gbc_zRangeComp.gridx = 1;
1084        gbc_zRangeComp.gridy = 4;
1085        optionsPanel.add(zRangeComp, gbc_zRangeComp);
1086
1087        tRangeLabel = new JLabel("T range  ");
1088        tRangeLabel.setToolTipText("T interval to load");
1089        GridBagConstraints gbc_tRangeLabel = new GridBagConstraints();
1090        gbc_tRangeLabel.anchor = GridBagConstraints.WEST;
1091        gbc_tRangeLabel.insets = new Insets(0, 0, 0, 5);
1092        gbc_tRangeLabel.gridx = 0;
1093        gbc_tRangeLabel.gridy = 5;
1094        optionsPanel.add(tRangeLabel, gbc_tRangeLabel);
1095
1096        tRangeComp = new RangeComponent();
1097        tRangeComp.addChangeListener(new ChangeListener()
1098        {
1099            @Override
1100            public void stateChanged(ChangeEvent e)
1101            {
1102                rangeChanged();
1103            }
1104        });
1105        tRangeComp.setToolTipText("T interval to load");
1106        tRangeComp.setMinimumSize(new Dimension(130, 22));
1107        tRangeComp.setMaximumSize(new Dimension(180, 22));
1108        tRangeComp.getLowSpinner().setPreferredSize(new Dimension(50, 20));
1109        tRangeComp.getHighSpinner().setPreferredSize(new Dimension(50, 20));
1110        tRangeComp.getHighSpinner().setMaximumSize(new Dimension(50, 20));
1111        tRangeComp.getLowSpinner().setMaximumSize(new Dimension(50, 20));
1112        tRangeComp.setPreferredSize(new Dimension(180, 22));
1113        tRangeComp.getSlider().setPreferredSize(new Dimension(70, 22));
1114        tRangeComp.setSliderVisible(true);
1115        GridBagConstraints gbc_tRangeComp = new GridBagConstraints();
1116        gbc_tRangeComp.gridwidth = 3;
1117        gbc_tRangeComp.fill = GridBagConstraints.HORIZONTAL;
1118        gbc_tRangeComp.gridx = 1;
1119        gbc_tRangeComp.gridy = 5;
1120        optionsPanel.add(tRangeComp, gbc_tRangeComp);
1121    }
1122
1123    protected void updatePreviewOnChange()
1124    {
1125        if (updatingPanel)
1126            return;
1127
1128        int v;
1129        int z = -1;
1130        int t = -1;
1131        boolean changed = false;
1132
1133        // detect change on z, t, c position in preview
1134        v = getZMin();
1135        if (pZMin != v)
1136        {
1137            z = v;
1138            pZMin = v;
1139            changed = true;
1140        }
1141        v = getZMax();
1142        if (pZMax != v)
1143        {
1144            z = v;
1145            pZMax = v;
1146            changed = true;
1147        }
1148
1149        v = getTMin();
1150        if (pTMin != v)
1151        {
1152            t = v;
1153            pTMin = v;
1154            changed = true;
1155        }
1156        v = getTMax();
1157        if (pTMax != v)
1158        {
1159            t = v;
1160            pTMax = v;
1161            changed = true;
1162        }
1163
1164        v = getChannel();
1165        if (pCh != v)
1166        {
1167            pCh = v;
1168            changed = true;
1169        }
1170
1171        // changed ? --> update preview...
1172        if (changed)
1173            updatePreview(z, t);
1174    }
1175
1176    protected void rangeChanged()
1177    {
1178        updatePreviewOnChange();
1179    }
1180
1181    protected void seriesChanged()
1182    {
1183        // update preview series index
1184        previewUpdater.updatePreview(((Integer) seriesSpinner.getValue()).intValue());
1185    }
1186
1187    void updateLoadingType()
1188    {
1189        final List<String> files = getFiles();
1190
1191        loadingTypeCombo.setEnabled((files != null) && !files.isEmpty());
1192        // loadingTypeCombo.setEnabled(metadata != null);
1193    }
1194
1195    void updateXYRegion()
1196    {
1197        if (metadata != null)
1198        {
1199            final Rectangle2D r = xyRegionComp.getRegion();
1200            final int s = getSelectedSeries();
1201            final int sizeX = MetaDataUtil.getSizeX(metadata, s);
1202            final int sizeY = MetaDataUtil.getSizeY(metadata, s);
1203
1204            xyRegionLoadingToggle.setEnabled(canUseAdvancedSetting());
1205            xyRegionComp.setEnabled(canUseAdvancedSetting() && xyRegionLoadingToggle.isSelected());
1206            // re-init region if out of bounds
1207            if ((r.getMaxX() > sizeX) || (r.getMaxY() > sizeY))
1208                xyRegionComp.setRegion(0, 0, sizeX, sizeY);
1209        }
1210        else
1211        {
1212            xyRegionComp.setEnabled(false);
1213            xyRegionLoadingToggle.setEnabled(false);
1214        }
1215    }
1216
1217    void updateZRange()
1218    {
1219        if (metadata != null)
1220        {
1221            final int s = getSelectedSeries();
1222            final int sizeZ = Math.max(MetaDataUtil.getSizeZ(metadata, s), 1);
1223
1224            zRangeComp.setMinMaxStep(0d, sizeZ - 1d, 1d);
1225            zRangeComp.setLowHigh(0d, sizeZ - 1d);
1226            zRangeComp.setEnabled(canUseAdvancedSetting() && (sizeZ > 1));
1227        }
1228        else
1229        {
1230            zRangeComp.setMinMaxStep(0d, 0d, 1d);
1231            zRangeComp.setEnabled(false);
1232        }
1233    }
1234
1235    void updateTRange()
1236    {
1237        if (metadata != null)
1238        {
1239            final int s = getSelectedSeries();
1240            final int sizeT = Math.max(MetaDataUtil.getSizeT(metadata, s), 1);
1241
1242            tRangeComp.setMinMaxStep(0d, sizeT - 1d, 1d);
1243            tRangeComp.setLowHigh(0d, sizeT - 1d);
1244            tRangeComp.setEnabled(canUseAdvancedSetting() && (sizeT > 1));
1245        }
1246        else
1247        {
1248            tRangeComp.setMinMaxStep(0d, 0d, 1d);
1249            tRangeComp.setEnabled(false);
1250        }
1251    }
1252
1253    void updateChannelRange()
1254    {
1255        if (metadata != null)
1256        {
1257            final int s = getSelectedSeries();
1258            final int sizeC = Math.max(MetaDataUtil.getSizeC(metadata, s), 1);
1259
1260            channelSpinner.setModel(new SpecialValueSpinnerModel(-1, -1, sizeC - 1, 1, -1, "ALL"));
1261            channelSpinner.setEnabled(canUseAdvancedSetting() && (sizeC > 1));
1262        }
1263        else
1264        {
1265            channelSpinner.setModel(new SpecialValueSpinnerModel(-1, -1, 0, 1, -1, "ALL"));
1266            channelSpinner.setEnabled(false);
1267        }
1268    }
1269
1270    void updateSeriesRange()
1271    {
1272        if (metadata != null)
1273        {
1274            final int numSeries = Math.max(MetaDataUtil.getNumSeries(metadata), 1);
1275
1276            seriesSpinner.setModel(new SpecialValueSpinnerModel(getSeries(), -1, numSeries - 1, 1, -1, "ALL"));
1277            seriesSpinner.setEnabled(canUseAdvancedSetting() && (numSeries > 1));
1278        }
1279        else
1280        {
1281            seriesSpinner.setModel(new SpecialValueSpinnerModel(-1, -1, 0, 1, -1, "ALL"));
1282            seriesSpinner.setEnabled(false);
1283        }
1284    }
1285
1286    void updateResolutionSlider()
1287    {
1288        // min sub resolution to open (full resolution by default)
1289        int minRes = 0;
1290        // max sub resolution to open
1291        int maxRes = 0;
1292
1293        if (metadata != null)
1294        {
1295            final int s = getSelectedSeries();
1296            long sizeXY;
1297
1298            // // size of XY plane
1299            // sizeXY = (long) MetaDataUtil.getSizeX(metadata, s) * (long) MetaDataUtil.getSizeY(metadata, s);
1300            //
1301            // // we can't handle that plane size
1302            // while (sizeXY > Integer.MAX_VALUE)
1303            // {
1304            // // reduce resolution until XY plane size is acceptable
1305            // minRes++;
1306            // sizeXY /= 4;
1307            // }
1308
1309            // size of XY plane
1310            sizeXY = (long) MetaDataUtil.getSizeX(metadata, s) * (long) MetaDataUtil.getSizeY(metadata, s);
1311
1312            // no need to get lower than 128x128
1313            while (sizeXY > 16384)
1314            {
1315                // increase max sub resolution until XY plane is too low
1316                maxRes++;
1317                sizeXY /= 4;
1318            }
1319        }
1320
1321        // apply
1322        resolutionSlider.setMinimum(minRes);
1323        resolutionSlider.setMaximum(maxRes);
1324        resolutionSlider.setValue(minRes);
1325
1326        // no need to enable it
1327        resolutionSlider.setEnabled(canUseAdvancedSetting() && (maxRes > 0));
1328
1329        updateFinaleResolution();
1330    }
1331
1332    void updateFinaleResolution()
1333    {
1334        if (metadata != null)
1335        {
1336            final int res = resolutionSlider.getValue();
1337            final int s = getSelectedSeries();
1338            final int baseX = MetaDataUtil.getSizeX(metadata, s);
1339            final int baseY = MetaDataUtil.getSizeY(metadata, s);
1340            final double diviser = Math.pow(2d, res);
1341
1342            resolutionLevelLabel.setText(res + " (" + Integer.toString((int) (baseX / diviser)) + " x "
1343                    + Integer.toString((int) (baseY / diviser)) + ")");
1344        }
1345        else
1346            resolutionLevelLabel.setText("");
1347    }
1348
1349    void disablePanel()
1350    {
1351        loadingTypeCombo.setEnabled(false);
1352        channelSpinner.setEnabled(false);
1353        seriesSpinner.setEnabled(false);
1354        resolutionLevelLabel.setText("");
1355        resolutionSlider.setEnabled(false);
1356        xyRegionLoadingToggle.setEnabled(false);
1357        xyRegionComp.setEnabled(false);
1358        tRangeComp.setEnabled(false);
1359        zRangeComp.setEnabled(false);
1360    }
1361
1362    void updatePanel()
1363    {
1364        updatingPanel = true;
1365        try
1366        {
1367            updateLoadingType();
1368            updateResolutionSlider();
1369            updateXYRegion();
1370            updateTRange();
1371            updateZRange();
1372            updateChannelRange();
1373            updateSeriesRange();
1374        }
1375        finally
1376        {
1377            updatingPanel = false;
1378        }
1379    }
1380
1381    void loadingTypeChanged()
1382    {
1383        updatePreview(-1);
1384    }
1385
1386    void regionLoadingToggleChanged()
1387    {
1388        xyRegionComp.setEnabled(xyRegionLoadingToggle.isEnabled() && xyRegionLoadingToggle.isSelected());
1389    }
1390
1391    public List<String> getFiles()
1392    {
1393        if (previewUpdater != null)
1394            return previewUpdater.newFiles;
1395
1396        return new ArrayList<String>();
1397    }
1398
1399    public boolean getOptionsVisible()
1400    {
1401        return popupPanel.isExpanded();
1402    }
1403
1404    public void setOptionsVisible(boolean value)
1405    {
1406        popupPanel.setExpanded(value);
1407    }
1408
1409    public boolean isSeparateSequenceSelected()
1410    {
1411        return loadingTypeCombo.getSelectedItem() == LoaderLoadingType.NO_GROUP;
1412    }
1413
1414    public boolean isGroupedSequenceSelected()
1415    {
1416        return loadingTypeCombo.getSelectedItem() == LoaderLoadingType.GROUP;
1417    }
1418
1419    public boolean isMultiFile()
1420    {
1421        return previewUpdater.isMultiFile();
1422    }
1423
1424    public boolean canUseAdvancedSetting()
1425    {
1426        return !(isSeparateSequenceSelected() && isMultiFile());
1427    }
1428
1429    public LoaderLoadingType getLoadingType()
1430    {
1431        return (LoaderLoadingType) loadingTypeCombo.getSelectedItem();
1432    }
1433
1434    void setLoadingType(LoaderLoadingType value)
1435    {
1436        loadingTypeCombo.setSelectedItem(value);
1437    }
1438
1439    public int getResolutionLevel()
1440    {
1441        if (resolutionSlider.isVisible())
1442            return resolutionSlider.getValue();
1443
1444        return 0;
1445    }
1446
1447    public Rectangle getXYRegion()
1448    {
1449        if (xyRegionComp.isVisible() && xyRegionComp.isEnabled())
1450            return (Rectangle) xyRegionComp.getRegion();
1451
1452        return null;
1453    }
1454
1455    public void setXYRegion(Rectangle region)
1456    {
1457        // enable region loading
1458        xyRegionLoadingToggle.setSelected(true);
1459        xyRegionComp.setEnabled(true);
1460        xyRegionComp.setRegion(region);
1461
1462        // we want options to be visible in that case
1463        setOptionsVisible(true);
1464    }
1465
1466    public int getZMin()
1467    {
1468        if (zRangeComp.isVisible())
1469            return (int) zRangeComp.getLow();
1470
1471        return 0;
1472    }
1473
1474    public int getZMax()
1475    {
1476        if (zRangeComp.isVisible())
1477            return (int) zRangeComp.getHigh();
1478
1479        return 0;
1480    }
1481
1482    public boolean getFullZRange()
1483    {
1484        return (getZMin() == 0) && (getZMax() == (int) zRangeComp.getMax());
1485    }
1486
1487    public int getTMin()
1488    {
1489        if (tRangeComp.isVisible())
1490            return (int) tRangeComp.getLow();
1491
1492        return 0;
1493    }
1494
1495    public int getTMax()
1496    {
1497        if (tRangeComp.isVisible())
1498            return (int) tRangeComp.getHigh();
1499
1500        return 0;
1501    }
1502
1503    public boolean getFullTRange()
1504    {
1505        return (getTMin() == 0) && (getTMax() == (int) tRangeComp.getMax());
1506    }
1507
1508    public int getChannel()
1509    {
1510        if (channelSpinner.isVisible())
1511            return ((Integer) channelSpinner.getValue()).intValue();
1512
1513        // all by default
1514        return -1;
1515    }
1516
1517    public int getSeries()
1518    {
1519        if (seriesSpinner.isVisible())
1520            // use cached value
1521            return series;
1522
1523        // all by default
1524        return -1;
1525    }
1526
1527    public void setSeries(int series)
1528    {
1529        // set default series
1530        seriesSpinner.setValue(Integer.valueOf(series));
1531
1532        // we want options to be visible in that case
1533        setOptionsVisible(true);
1534    }
1535
1536    /**
1537     * Same as {@link #getSeries()} except it returns 0 instead of -1 to ensure we have one selected series
1538     */
1539    public int getSelectedSeries()
1540    {
1541        final int result = getSeries();
1542
1543        return (result == -1) ? 0 : result;
1544    }
1545
1546    /**
1547     * Cancel preview refresh
1548     */
1549    public void cancelPreview()
1550    {
1551        previewUpdater.cancelPreview();
1552    }
1553
1554    protected void updatePreview(int series)
1555    {
1556        previewUpdater.updatePreview(series);
1557    }
1558
1559    /**
1560     * Asynchronous image preview refresh only, ({@link #updatePreview(String)} should have be called once before to
1561     * give
1562     * the fileId)
1563     */
1564    protected void updatePreview(int z, int t)
1565    {
1566        previewUpdater.updatePreview(z, t);
1567    }
1568
1569    /**
1570     * Asynchronous preview refresh
1571     */
1572    public void updatePreview(String[] files, int s)
1573    {
1574        // series or files selection changed ?
1575        if ((series != s) || !CollectionUtil.equals(getFiles(), CollectionUtil.asList(files)))
1576            previewUpdater.updatePreview(files, s);
1577    }
1578
1579    /**
1580     * Asynchronous preview refresh
1581     */
1582    public void updatePreview(List<String> files, int s)
1583    {
1584        // series or files selection changed ?
1585        if ((series != s) || !CollectionUtil.equals(getFiles(), files))
1586            previewUpdater.updatePreview(files, s);
1587    }
1588
1589    /**
1590     * Asynchronous preview refresh
1591     */
1592    public void updatePreview(String[] files)
1593    {
1594        // files selection changed ?
1595        if (!CollectionUtil.equals(getFiles(), CollectionUtil.asList(files)))
1596            previewUpdater.updatePreview(files, -1);
1597    }
1598
1599    /**
1600     * We are closing the Open Dialog.
1601     * Ensure metadata are correctly loaded (it's important that this method is called from EDT)
1602     */
1603    public void closingFromEDT()
1604    {
1605        previewUpdater.close();
1606    }
1607}