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.action;
020
021import java.awt.Image;
022import java.awt.event.ActionEvent;
023import java.awt.event.KeyEvent;
024import java.awt.image.BufferedImage;
025import java.lang.reflect.Field;
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.List;
029
030import javax.swing.JToggleButton;
031
032import icy.gui.dialog.IdConfirmDialog;
033import icy.gui.dialog.MessageDialog;
034import icy.gui.frame.progress.FailedAnnounceFrame;
035import icy.gui.main.MainFrame;
036import icy.gui.sequence.tools.SequenceCanvasResizeFrame;
037import icy.gui.sequence.tools.SequenceDimensionAdjustFrame;
038import icy.gui.sequence.tools.SequenceDimensionConvertFrame;
039import icy.gui.sequence.tools.SequenceDimensionExtendFrame;
040import icy.gui.sequence.tools.SequenceDimensionMergeFrame;
041import icy.gui.sequence.tools.SequenceResizeFrame;
042import icy.gui.viewer.Viewer;
043import icy.image.cache.ImageCache;
044import icy.main.Icy;
045import icy.resource.ResourceUtil;
046import icy.resource.icon.IcyIcon;
047import icy.roi.ROI;
048import icy.sequence.DimensionId;
049import icy.sequence.Sequence;
050import icy.sequence.SequenceDataIterator;
051import icy.sequence.SequenceUtil;
052import icy.system.SystemUtil;
053import icy.system.thread.ThreadUtil;
054import icy.type.DataIteratorUtil;
055import icy.type.DataType;
056import icy.undo.IcyUndoManager;
057import icy.util.ClassUtil;
058
059/**
060 * Actions for "Sequence Operation" tab.
061 * 
062 * @author Stephane
063 */
064public class SequenceOperationActions
065{
066    static class SequenceConvertAction extends IcyAbstractAction
067    {
068        /**
069         * 
070         */
071        private static final long serialVersionUID = 614601313456867774L;
072
073        final DataType dataType;
074        final boolean scaled;
075
076        public SequenceConvertAction(DataType dataType, boolean scaled)
077        {
078            super(dataType.toString(true), new IcyIcon(ResourceUtil.ICON_BAND_RIGHT),
079                    "Convert to " + dataType.toString(true), "Convert sequence data type to " + dataType.toString(true),
080                    true, "Converting sequence to " + dataType.toString(false) + " ...");
081
082            this.dataType = dataType;
083            this.scaled = scaled;
084        }
085
086        @Override
087        public boolean doAction(ActionEvent e)
088        {
089            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
090
091            if (viewer != null)
092            {
093                final Sequence sequence = viewer.getSequence();
094
095                if (sequence != null)
096                {
097                    final Sequence out = SequenceUtil.convertToType(Icy.getMainInterface().getActiveSequence(),
098                            dataType, scaled);
099
100                    ThreadUtil.invokeLater(new Runnable()
101                    {
102                        @Override
103                        public void run()
104                        {
105                            // get output viewer
106                            final Viewer vout = new Viewer(out);
107                            // restore colormap from input viewer
108                            vout.getLut().setColorMaps(viewer.getLut(), false);
109                        }
110                    });
111
112                    return true;
113                }
114            }
115
116            return false;
117        }
118
119        @Override
120        public boolean isEnabled()
121        {
122            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
123        }
124    }
125
126    static class SequenceColorAction extends IcyAbstractAction
127    {
128        /**
129         * 
130         */
131        private static final long serialVersionUID = 3775617713982984867L;
132
133        private static final Image images[] = {null, ResourceUtil.ICON_RGB_COLOR, ResourceUtil.ICON_ARGB_COLOR, null,
134                null, null, null, null, null, null, ResourceUtil.ICON_GRAY_COLOR, null, null, null, null, null};
135        private static final String names[] = {null, "RGB image", "ARGB image", null, null, null, null, null, null,
136                null, "Gray image", null, null, null, null, null};
137        private static final String titles[] = {null, "Build RGB image", "Build ARGB image", null, null, null, null,
138                null, null, null, "Build gray image", null, null, null, null, null};
139        private static final String tooltips[] = {null,
140                "Create a RGB color rendered version of the current sequence.\nResulting sequence is 3 channels with unsigned byte (8 bits) data type.",
141                "Create an ARGB color (support transparency) rendered version of the current sequence.\nResulting sequence is 4 channels with unsigned byte (8 bits) data type.",
142                null, null, null, null, null, null, null,
143                "Create a gray rendered version of the current sequence.\nResulting sequence is single channel with unsigned byte (8 bits) data type.",
144                null, null, null, null, null};
145        private static final String processMessages[] = {null, "Converting to RGB image...",
146                "Converting to ARGB image...", null, null, null, null, null, null, null, "Converting to gray image...",
147                null, null, null, null, null};
148
149        final int imageType;
150
151        public SequenceColorAction(int imageType)
152        {
153            super(names[imageType], new IcyIcon(images[imageType], false), titles[imageType], tooltips[imageType], true,
154                    processMessages[imageType]);
155
156            this.imageType = imageType;
157        }
158
159        @Override
160        public boolean doAction(ActionEvent e)
161        {
162            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
163
164            if (viewer != null)
165            {
166                final Sequence sequence = viewer.getSequence();
167
168                if (sequence != null)
169                {
170                    // convert the sequence
171                    final Sequence out = SequenceUtil.convertColor(sequence, imageType, viewer.getLut());
172                    Icy.getMainInterface().addSequence(out);
173                    return true;
174                }
175            }
176
177            return false;
178        }
179
180        @Override
181        public boolean isEnabled()
182        {
183            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
184
185            return super.isEnabled() && (sequence != null) && !sequence.isEmpty();
186        }
187    }
188
189    public static class ExtractChannelAction extends IcyAbstractAction
190    {
191        private static final long serialVersionUID = -8722922231336771871L;
192
193        final int channel;
194
195        public ExtractChannelAction(int channel)
196        {
197            super((channel == -1) ? "all channels" : "channel " + channel,
198                    new IcyIcon(ResourceUtil.ICON_INDENT_DECREASE),
199                    (channel == -1) ? "Extract all channels" : "Extract channel " + channel,
200                    (channel == -1) ? "Separate all channels of active sequence"
201                            : "Create a new single channel sequence from channel " + channel + " of active sequence",
202                    true, (channel == -1) ? "Extracting channel(s)..." : "Extracting channel " + channel + "...");
203
204            this.channel = channel;
205        }
206
207        @Override
208        public boolean doAction(ActionEvent e)
209        {
210            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
211
212            if (sequence != null)
213            {
214                if (channel == -1)
215                {
216                    for (int c = 0; c < sequence.getSizeC(); c++)
217                        Icy.getMainInterface().addSequence(SequenceUtil.extractChannel(sequence, c));
218                }
219                else
220                    Icy.getMainInterface().addSequence(SequenceUtil.extractChannel(sequence, channel));
221
222                return true;
223            }
224
225            return false;
226        }
227
228        @Override
229        public boolean isEnabled()
230        {
231            final Sequence seq = Icy.getMainInterface().getActiveSequence();
232
233            return super.isEnabled() && (seq != null) && (channel < seq.getSizeC());
234        }
235    }
236
237    public static class RemoveChannelAction extends IcyAbstractAction
238    {
239        private static final long serialVersionUID = 66288944320765300L;
240
241        final int channel;
242
243        public RemoveChannelAction(int channel)
244        {
245            super("channel " + channel, new IcyIcon(ResourceUtil.ICON_INDENT_REMOVE), "Remove channel " + channel,
246                    "Remove channel " + channel + " from active sequence", true, "Removing channel " + channel + "...");
247
248            this.channel = channel;
249        }
250
251        @Override
252        public boolean doAction(ActionEvent e)
253        {
254            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
255
256            if (sequence != null)
257            {
258                // create undo point
259                final boolean canUndo = sequence.createUndoPoint("Channel " + channel + "removed");
260
261                // cannot backup
262                if (!canUndo)
263                {
264                    // ask confirmation to continue
265                    if (!IdConfirmDialog.confirm("Not enough memory to undo the operation, do you want to continue ?",
266                            "ChannelRemoveNoUndoConfirm"))
267                        return false;
268                }
269
270                SequenceUtil.removeChannel(sequence, channel);
271
272                // no undo, clear undo manager after modification
273                if (!canUndo)
274                    sequence.clearUndoManager();
275
276                return true;
277            }
278
279            return false;
280        }
281
282        @Override
283        public boolean isEnabled()
284        {
285            final Sequence seq = Icy.getMainInterface().getActiveSequence();
286
287            return super.isEnabled() && (seq != null) && (channel < seq.getSizeC());
288        }
289    }
290
291    public static class MergeDimensionAction extends IcyAbstractAction
292    {
293        private static final long serialVersionUID = -3859065456632266213L;
294
295        private static final String titles[] = {null, null, null, "Merge channels", "Merge Z slices", "Merge T frames"};
296        private static final String tooltips[] = {null, null, null,
297                "Merge channels from severals input sequences to build a new sequence.",
298                "Merge Z slices from severals input sequences to build a new sequence.",
299                "Merge T frames from severals input sequences to build a new sequence."};
300
301        final DimensionId dim;
302
303        public MergeDimensionAction(DimensionId dim)
304        {
305            super("Merge...", new IcyIcon(ResourceUtil.ICON_INDENT_INCREASE), titles[dim.ordinal()],
306                    tooltips[dim.ordinal()]);
307
308            this.dim = dim;
309        }
310
311        @Override
312        public boolean doAction(ActionEvent e)
313        {
314            new SequenceDimensionMergeFrame(dim);
315            return true;
316        }
317    }
318
319    public static class ToggleVirtualSequenceAction extends IcyAbstractAction
320    {
321        /**
322         * 
323         */
324        private static final long serialVersionUID = -2210853124199344868L;
325
326        public ToggleVirtualSequenceAction(boolean selected)
327        {
328            super("Virtual", new IcyIcon(ResourceUtil.ICON_HDD_STREAM),
329                    "Enable/Disable virtual mode (data streaming) for this sequence");
330
331            setSelected(selected);
332        }
333
334        public ToggleVirtualSequenceAction()
335        {
336            this(false);
337        }
338
339        @Override
340        public boolean doAction(ActionEvent e)
341        {
342            final JToggleButton btn = (e.getSource() instanceof JToggleButton) ? (JToggleButton) e.getSource() : null;
343            final boolean value = (btn != null) ? btn.isSelected() : isSelected();
344
345            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
346            boolean result = false;
347            String errMess = "";
348
349            try
350            {
351                // apply on sequence
352                if (sequence != null)
353                    sequence.setVirtual(value);
354
355                result = true;
356            }
357            catch (OutOfMemoryError error)
358            {
359                errMess = "Not enough available memory to put back the sequence in memory (still in virtual state).";
360                result = false;
361            }
362            catch (UnsupportedOperationException error)
363            {
364                errMess = "Image cache engine is disable.";
365                result = false;
366            }
367            catch (Throwable error)
368            {
369                errMess = error.getMessage();
370                result = false;
371            }
372
373            // restore previous state if operation failed
374            if (!result)
375            {
376                // display error
377                if (!value)
378                    new FailedAnnounceFrame(errMess);
379
380                // revert to original state
381                if (btn != null)
382                    btn.setSelected(!value);
383                setSelected(!value);
384            }
385
386            if (btn != null)
387            {
388                if (btn.isSelected())
389                    btn.setToolTipText("Disable virtual sequence (caching)");
390                else
391                    btn.setToolTipText("Enable virtual sequence (caching)");
392            }
393
394            return result;
395        }
396
397        @Override
398        public void setSelected(boolean value)
399        {
400            super.setSelected(value);
401
402            if (!ImageCache.isEnabled())
403                setDescription("Image cache is disabled, cannot use virtual sequence");
404            else if (value)
405                setDescription("Disable virtual sequence (caching)");
406            else
407                setDescription("Enable virtual sequence (caching)");
408        }
409
410        @Override
411        public boolean isEnabled()
412        {
413            return super.isEnabled() && ImageCache.isEnabled();
414        }
415    };
416
417    public static IcyAbstractAction cloneSequenceAction = new IcyAbstractAction("Duplicate",
418            new IcyIcon(ResourceUtil.ICON_COPY), "Duplicate sequence", "Create a fresh copy of the sequence", true,
419            "Duplicating sequence...")
420    {
421        /**
422         * 
423         */
424        private static final long serialVersionUID = 6907103082567189377L;
425
426        @Override
427        public boolean doAction(ActionEvent e)
428        {
429            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
430            if (viewer == null)
431                return false;
432
433            final Sequence seq = viewer.getSequence();
434            if (seq == null)
435                return false;
436
437            // create output sequence
438            final Sequence out = SequenceUtil.getCopy(seq);
439
440            ThreadUtil.invokeLater(new Runnable()
441            {
442                @Override
443                public void run()
444                {
445                    // get output viewer
446                    final Viewer vout = new Viewer(out);
447                    // copy colormap from input viewer
448                    vout.getLut().copyFrom(viewer.getLut());
449                }
450            });
451
452            return true;
453        }
454
455        @Override
456        public boolean isEnabled()
457        {
458            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
459        }
460    };
461
462    public static IcyAbstractAction convertUByteScaledSequenceAction = new SequenceConvertAction(DataType.UBYTE, true);
463    public static IcyAbstractAction convertUByteSequenceAction = new SequenceConvertAction(DataType.UBYTE, false);
464    public static IcyAbstractAction convertByteScaledSequenceAction = new SequenceConvertAction(DataType.BYTE, true);
465    public static IcyAbstractAction convertByteSequenceAction = new SequenceConvertAction(DataType.BYTE, false);
466    public static IcyAbstractAction convertUShortScaledSequenceAction = new SequenceConvertAction(DataType.USHORT,
467            true);
468    public static IcyAbstractAction convertUShortSequenceAction = new SequenceConvertAction(DataType.USHORT, false);
469    public static IcyAbstractAction convertShortScaledSequenceAction = new SequenceConvertAction(DataType.SHORT, true);
470    public static IcyAbstractAction convertShortSequenceAction = new SequenceConvertAction(DataType.SHORT, false);
471    public static IcyAbstractAction convertUIntScaledSequenceAction = new SequenceConvertAction(DataType.UINT, true);
472    public static IcyAbstractAction convertUIntSequenceAction = new SequenceConvertAction(DataType.UINT, false);
473    public static IcyAbstractAction convertIntScaledSequenceAction = new SequenceConvertAction(DataType.INT, true);
474    public static IcyAbstractAction convertIntSequenceAction = new SequenceConvertAction(DataType.INT, false);
475    public static IcyAbstractAction convertFloatScaledSequenceAction = new SequenceConvertAction(DataType.FLOAT, true);
476    public static IcyAbstractAction convertFloatSequenceAction = new SequenceConvertAction(DataType.FLOAT, false);
477    public static IcyAbstractAction convertDoubleScaledSequenceAction = new SequenceConvertAction(DataType.DOUBLE,
478            true);
479    public static IcyAbstractAction convertDoubleSequenceAction = new SequenceConvertAction(DataType.DOUBLE, false);
480
481    // color operations
482    public static IcyAbstractAction argbSequenceAction = new SequenceColorAction(BufferedImage.TYPE_INT_ARGB);
483    public static IcyAbstractAction rgbSequenceAction = new SequenceColorAction(BufferedImage.TYPE_INT_RGB);
484    public static IcyAbstractAction graySequenceAction = new SequenceColorAction(BufferedImage.TYPE_BYTE_GRAY);
485
486    // XY plan operations
487    public static IcyAbstractAction cropSequenceAction = new IcyAbstractAction("Fast crop",
488            new IcyIcon(ResourceUtil.ICON_CUT), "Fast crop image", "Crop an image from a ROI", true, "Doing image crop...")
489    {
490        private static final long serialVersionUID = 2928113834852115366L;
491
492        @Override
493        public boolean doAction(ActionEvent e)
494        {
495            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
496            if (viewer == null)
497                return false;
498
499            final Sequence seq = viewer.getSequence();
500            if (seq == null)
501                return false;
502
503            List<ROI> rois = seq.getROIs();
504            int size = rois.size();
505
506            if (size == 0)
507            {
508                MessageDialog.showDialog(
509                        "There is no ROI in the current sequence.\nYou need a ROI to define the region to crop.",
510                        MessageDialog.INFORMATION_MESSAGE);
511                return false;
512            }
513            else if (size > 1)
514            {
515                rois = seq.getSelectedROIs();
516                size = rois.size();
517
518                if (size == 0)
519                {
520                    MessageDialog.showDialog("You need to select a ROI to do this operation.",
521                            MessageDialog.INFORMATION_MESSAGE);
522                    return false;
523                }
524                else if (size > 1)
525                {
526                    MessageDialog.showDialog("You must have only one selected ROI to do this operation.",
527                            MessageDialog.INFORMATION_MESSAGE);
528                    return false;
529                }
530            }
531
532            final ROI roi = rois.get(0);
533
534            // create output sequence
535            final Sequence out = SequenceUtil.getSubSequence(seq, roi);
536
537            ThreadUtil.invokeLater(new Runnable()
538            {
539                @Override
540                public void run()
541                {
542                    // get output viewer
543                    final Viewer vout = new Viewer(out);
544                    // copy colormap from input viewer
545                    vout.getLut().copyFrom(viewer.getLut());
546                }
547            });
548
549            return true;
550        }
551
552        @Override
553        public boolean isEnabled()
554        {
555            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
556        }
557    };
558
559    public static IcyAbstractAction canvasResizeAction = new IcyAbstractAction("Canvas size...",
560            new IcyIcon(ResourceUtil.ICON_CROP), "Canvas resize", "Resize the canvas without changing image size.")
561    {
562        private static final long serialVersionUID = 9156831541828750627L;
563
564        @Override
565        public boolean doAction(ActionEvent e)
566        {
567            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
568
569            if (sequence != null)
570            {
571                new SequenceCanvasResizeFrame(sequence);
572                return true;
573            }
574
575            return false;
576        }
577
578        @Override
579        public boolean isEnabled()
580        {
581            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
582        }
583    };
584
585    public static IcyAbstractAction imageResizeAction = new IcyAbstractAction("Image size...",
586            new IcyIcon(ResourceUtil.ICON_FIT_CANVAS), "Image resize", "Resize the image.")
587    {
588        private static final long serialVersionUID = -4731940627380446776L;
589
590        @Override
591        public boolean doAction(ActionEvent e)
592        {
593            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
594
595            if (sequence != null)
596            {
597                new SequenceResizeFrame(sequence);
598                return true;
599            }
600
601            return false;
602        }
603
604        @Override
605        public boolean isEnabled()
606        {
607            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
608        }
609    };
610
611    // channel operations
612    public static IcyAbstractAction extractAllChannelAction = new ExtractChannelAction(-1);
613    public static IcyAbstractAction extractChannelActions[] = {new ExtractChannelAction(0), new ExtractChannelAction(1),
614            new ExtractChannelAction(2), new ExtractChannelAction(3), new ExtractChannelAction(4),
615            new ExtractChannelAction(5)};
616    public static IcyAbstractAction removeChannelActions[] = {new RemoveChannelAction(0), new RemoveChannelAction(1),
617            new RemoveChannelAction(2), new RemoveChannelAction(3), new RemoveChannelAction(4),
618            new RemoveChannelAction(5)};
619    public static IcyAbstractAction mergeChannelsAction = new MergeDimensionAction(DimensionId.C);
620
621    // Z operations
622    public static IcyAbstractAction reverseSlicesAction = new IcyAbstractAction("Reverse order",
623            new IcyIcon(ResourceUtil.ICON_LAYER_REVERSE_V), "Reverse Z slices", "Reverse Z slices order", true,
624            "Reversing slices...")
625    {
626        private static final long serialVersionUID = -4731940627380446776L;
627
628        @Override
629        public boolean doAction(ActionEvent e)
630        {
631            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
632
633            if (sequence != null)
634            {
635                SequenceUtil.reverseZ(sequence);
636                return true;
637            }
638
639            return false;
640        }
641    };
642
643    public static IcyAbstractAction extractSliceAction = new IcyAbstractAction("Extract slice",
644            new IcyIcon(ResourceUtil.ICON_LAYER_EXTRACT_V), "Extract current Z slice",
645            "Create a new sequence by extracting current Z slice of active sequence.", false, "Extracting slice...")
646    {
647        /**
648         * 
649         */
650        private static final long serialVersionUID = -3731161374656240419L;
651
652        @Override
653        public boolean doAction(ActionEvent e)
654        {
655            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
656
657            if (viewer != null)
658            {
659                final Sequence sequence = viewer.getSequence();
660
661                if (sequence != null)
662                {
663                    final int z = viewer.getPositionZ();
664
665                    if (z != -1)
666                    {
667                        final Sequence out = SequenceUtil.extractSlice(sequence, z);
668
669                        ThreadUtil.invokeLater(new Runnable()
670                        {
671                            @Override
672                            public void run()
673                            {
674                                // get output viewer
675                                final Viewer vout = new Viewer(out);
676                                // copy colormap from input viewer
677                                vout.getLut().copyFrom(viewer.getLut());
678                            }
679                        });
680
681                        return true;
682                    }
683                }
684            }
685
686            return false;
687        }
688
689        @Override
690        public boolean isEnabled()
691        {
692            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
693            final int z = (viewer == null) ? -1 : viewer.getPositionZ();
694
695            return super.isEnabled() && (z != -1);
696        }
697    };
698
699    public static IcyAbstractAction removeSliceAction = new IcyAbstractAction("Remove slice",
700            new IcyIcon(ResourceUtil.ICON_LAYER_REMOVE_V), "Remove current Z slice",
701            "Remove the current Z slice of active sequence.", false, "Removing slice...")
702    {
703        /**
704         * 
705         */
706        private static final long serialVersionUID = -6588564641490390145L;
707
708        @Override
709        public boolean doAction(ActionEvent e)
710        {
711            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
712            final int z = (viewer == null) ? -1 : viewer.getPositionZ();
713
714            if (z != -1)
715            {
716                SequenceUtil.removeZAndShift(viewer.getSequence(), z);
717                return true;
718            }
719
720            return false;
721        }
722
723        @Override
724        public boolean isEnabled()
725        {
726            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
727            final int z = (viewer == null) ? -1 : viewer.getPositionZ();
728
729            return super.isEnabled() && (z != -1);
730        }
731    };
732
733    public static IcyAbstractAction addSlicesAction = new IcyAbstractAction("Add...",
734            new IcyIcon(ResourceUtil.ICON_LAYER_ADD_V), "Add slice(s)",
735            "Extends Z dimension by adding empty or duplicating slices.")
736    {
737        /**
738         * 
739         */
740        private static final long serialVersionUID = -1967473595758834348L;
741
742        @Override
743        public boolean doAction(ActionEvent e)
744        {
745            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
746
747            if (sequence != null)
748            {
749                new SequenceDimensionExtendFrame(Icy.getMainInterface().getActiveSequence(), DimensionId.Z);
750                return true;
751            }
752
753            return false;
754        }
755
756        @Override
757        public boolean isEnabled()
758        {
759            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
760
761            return super.isEnabled() && (sequence != null) && !sequence.isEmpty();
762        }
763    };
764
765    public static IcyAbstractAction mergeSlicesAction = new MergeDimensionAction(DimensionId.Z);
766
767    public static IcyAbstractAction removeSlicesAction = new IcyAbstractAction("Remove...",
768            new IcyIcon(ResourceUtil.ICON_LAYER_REMOVE_ADV_V), "Advanced slice remove",
769            "Advanced Z slice remove operation.")
770    {
771        /**
772         * 
773         */
774        private static final long serialVersionUID = -1899409406755437158L;
775
776        @Override
777        public boolean doAction(ActionEvent e)
778        {
779            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
780
781            if (sequence != null)
782            {
783                new SequenceDimensionAdjustFrame(sequence, DimensionId.Z);
784                return true;
785            }
786
787            return false;
788        }
789
790        @Override
791        public boolean isEnabled()
792        {
793            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
794        }
795    };
796
797    // T operations
798    public static IcyAbstractAction reverseFramesAction = new IcyAbstractAction("Reverse order",
799            new IcyIcon(ResourceUtil.ICON_LAYER_REVERSE_H), "Reverse T frames", "Reverse T frames order", true,
800            "Reversing frames...")
801    {
802        /**
803         * 
804         */
805        private static final long serialVersionUID = 2403122454093281595L;
806
807        @Override
808        public boolean doAction(ActionEvent e)
809        {
810            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
811
812            if (sequence != null)
813            {
814                SequenceUtil.reverseT(sequence);
815                return true;
816            }
817
818            return false;
819        }
820
821        @Override
822        public boolean isEnabled()
823        {
824            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
825        }
826    };
827
828    public static IcyAbstractAction extractFrameAction = new IcyAbstractAction("Extract frame",
829            new IcyIcon(ResourceUtil.ICON_LAYER_EXTRACT_H), "Extract current T frame",
830            "Create a new sequence by extracting current T frame of active sequence.", false, "Extracting frame...")
831    {
832        /**
833         * 
834         */
835        private static final long serialVersionUID = -5809053788547447661L;
836
837        @Override
838        public boolean doAction(ActionEvent e)
839        {
840            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
841
842            if (viewer != null)
843            {
844                final Sequence sequence = viewer.getSequence();
845
846                if (sequence != null)
847                {
848                    final int t = viewer.getPositionT();
849
850                    if (t != -1)
851                    {
852                        final Sequence out = SequenceUtil.extractFrame(sequence, t);
853
854                        ThreadUtil.invokeLater(new Runnable()
855                        {
856                            @Override
857                            public void run()
858                            {
859                                // get output viewer
860                                final Viewer vout = new Viewer(out);
861                                // copy colormap from input viewer
862                                vout.getLut().copyFrom(viewer.getLut());
863                            }
864                        });
865
866                        return true;
867                    }
868                }
869            }
870
871            return false;
872        }
873
874        @Override
875        public boolean isEnabled()
876        {
877            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
878            final int t = (viewer == null) ? -1 : viewer.getPositionT();
879
880            return super.isEnabled() && (t != -1);
881        }
882    };
883
884    public static IcyAbstractAction removeFrameAction = new IcyAbstractAction("Remove frame",
885            new IcyIcon(ResourceUtil.ICON_LAYER_REMOVE_H), "Remove current T frame",
886            "Remove the current T frame of active sequence.", false, "Removing frame...")
887    {
888        /**
889         * 
890         */
891        private static final long serialVersionUID = -6113522706924858672L;
892
893        @Override
894        public boolean doAction(ActionEvent e)
895        {
896            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
897            final int t = (viewer == null) ? -1 : viewer.getPositionT();
898
899            if (t != -1)
900            {
901                SequenceUtil.removeTAndShift(viewer.getSequence(), t);
902                return true;
903            }
904
905            return false;
906        }
907
908        @Override
909        public boolean isEnabled()
910        {
911            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
912            final int t = (viewer == null) ? -1 : viewer.getPositionT();
913
914            return super.isEnabled() && (t != -1);
915        }
916    };
917
918    public static IcyAbstractAction addFramesAction = new IcyAbstractAction("Add...",
919            new IcyIcon(ResourceUtil.ICON_LAYER_ADD_H), "Add frame(s)",
920            "Extends T dimension by adding empty or duplicating frames.")
921    {
922        /**
923         * 
924         */
925        private static final long serialVersionUID = -6106326145960291510L;
926
927        @Override
928        public boolean doAction(ActionEvent e)
929        {
930            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
931
932            if (sequence != null)
933            {
934                new SequenceDimensionExtendFrame(Icy.getMainInterface().getActiveSequence(), DimensionId.T);
935                return true;
936            }
937
938            return false;
939        }
940
941        @Override
942        public boolean isEnabled()
943        {
944            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
945
946            return super.isEnabled() && (sequence != null) && !sequence.isEmpty();
947        }
948    };
949
950    public static IcyAbstractAction mergeFramesAction = new MergeDimensionAction(DimensionId.T);
951
952    public static IcyAbstractAction removeFramesAction = new IcyAbstractAction("Remove...",
953            new IcyIcon(ResourceUtil.ICON_LAYER_REMOVE_ADV_H), "Advanced frame remove",
954            "Advanced T frame remove operation.")
955    {
956        /**
957         * 
958         */
959        private static final long serialVersionUID = -7963804798009814712L;
960
961        @Override
962        public boolean doAction(ActionEvent e)
963        {
964            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
965
966            if (sequence != null)
967            {
968                new SequenceDimensionAdjustFrame(sequence, DimensionId.T);
969                return true;
970            }
971
972            return false;
973        }
974
975        @Override
976        public boolean isEnabled()
977        {
978            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
979        }
980    };
981
982    // ZT conversion
983    public static IcyAbstractAction convertToSlicesAction = new IcyAbstractAction("Convert to stack",
984            new IcyIcon(ResourceUtil.ICON_LAYER_V1), "Convert to stack", "Set all images in Z dimension.", true,
985            "Converting to stack...")
986    {
987        /**
988         * 
989         */
990        private static final long serialVersionUID = 5987495169612852524L;
991
992        @Override
993        public boolean doAction(ActionEvent e)
994        {
995            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
996
997            if (viewer != null)
998            {
999                final Sequence sequence = viewer.getSequence();
1000                final int t = viewer.getPositionT();
1001
1002                if (sequence != null)
1003                {
1004                    SequenceUtil.convertToStack(sequence);
1005                    viewer.setPositionZ(t);
1006                    return true;
1007                }
1008            }
1009
1010            return false;
1011        }
1012
1013        @Override
1014        public boolean isEnabled()
1015        {
1016            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1017        }
1018    };
1019
1020    public static IcyAbstractAction convertToFramesAction = new IcyAbstractAction("Convert to time",
1021            new IcyIcon(ResourceUtil.ICON_LAYER_H1), "Convert to time sequence", "Set all images in T dimension.", true,
1022            "Converting to time...")
1023    {
1024        /**
1025         * 
1026         */
1027        private static final long serialVersionUID = -6555855298812635009L;
1028
1029        @Override
1030        public boolean doAction(ActionEvent e)
1031        {
1032            final Viewer viewer = Icy.getMainInterface().getActiveViewer();
1033
1034            if (viewer != null)
1035            {
1036                final Sequence sequence = viewer.getSequence();
1037                final int z = viewer.getPositionZ();
1038
1039                if (sequence != null)
1040                {
1041                    SequenceUtil.convertToTime(sequence);
1042                    viewer.setPositionT(z);
1043                    return true;
1044                }
1045            }
1046
1047            return false;
1048        }
1049
1050        @Override
1051        public boolean isEnabled()
1052        {
1053            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1054        }
1055    };
1056
1057    public static IcyAbstractAction advancedZTConvertAction = new IcyAbstractAction("Advanced...",
1058            new IcyIcon(ResourceUtil.ICON_COG), "Advanced dimension conversion",
1059            "Advanced dimension conversion operation.")
1060    {
1061        /**
1062         * 
1063         */
1064        private static final long serialVersionUID = 110261266295404071L;
1065
1066        @Override
1067        public boolean doAction(ActionEvent e)
1068        {
1069            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1070
1071            if (sequence != null)
1072            {
1073                new SequenceDimensionConvertFrame(sequence);
1074                return true;
1075            }
1076
1077            return false;
1078        }
1079
1080        @Override
1081        public boolean isEnabled()
1082        {
1083            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1084
1085            return super.isEnabled() && (sequence != null) && !sequence.isEmpty();
1086        }
1087    };
1088
1089    /**
1090     * @deprecated Use {@link RoiActions#fillInteriorAction} instead
1091     */
1092
1093    @Deprecated
1094    public static IcyAbstractAction fillSequenceAction = new IcyAbstractAction("Fill",
1095            new IcyIcon(ResourceUtil.ICON_BRUSH), "Fill ROI content",
1096            "Fill content of the selected ROI with specified value", true, "Fill ROI content")
1097    {
1098        /**
1099         * 
1100         */
1101        private static final long serialVersionUID = 110261266295404071L;
1102
1103        @Override
1104        public boolean doAction(ActionEvent e)
1105        {
1106            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1107
1108            if (sequence != null)
1109            {
1110                final MainFrame mainFrame = Icy.getMainInterface().getMainFrame();
1111
1112                if (mainFrame != null)
1113                {
1114                    final double value = mainFrame.getMainRibbon().getSequenceOperationTask().getFillValue();
1115
1116                    for (ROI roi : sequence.getSelectedROIs())
1117                        DataIteratorUtil.set(new SequenceDataIterator(sequence, roi, true), value);
1118
1119                    sequence.dataChanged();
1120
1121                    return true;
1122                }
1123            }
1124
1125            return false;
1126        }
1127
1128        @Override
1129        public boolean isEnabled()
1130        {
1131            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1132
1133            return super.isEnabled() && (sequence != null) && !sequence.isEmpty();
1134        }
1135    };
1136
1137    public static IcyAbstractAction getConvertSequenceAction(DataType dataType, boolean scaled)
1138    {
1139        switch (dataType)
1140        {
1141            case UBYTE:
1142                if (scaled)
1143                    return convertUByteScaledSequenceAction;
1144                return convertUByteSequenceAction;
1145
1146            case BYTE:
1147                if (scaled)
1148                    return convertByteScaledSequenceAction;
1149                return convertByteSequenceAction;
1150
1151            case USHORT:
1152                if (scaled)
1153                    return convertUShortScaledSequenceAction;
1154                return convertUShortSequenceAction;
1155
1156            case SHORT:
1157                if (scaled)
1158                    return convertShortScaledSequenceAction;
1159                return convertShortSequenceAction;
1160
1161            case UINT:
1162                if (scaled)
1163                    return convertUIntScaledSequenceAction;
1164                return convertUIntSequenceAction;
1165
1166            case INT:
1167                if (scaled)
1168                    return convertIntScaledSequenceAction;
1169                return convertIntSequenceAction;
1170
1171            case FLOAT:
1172                if (scaled)
1173                    return convertFloatScaledSequenceAction;
1174                return convertFloatSequenceAction;
1175
1176            case DOUBLE:
1177                if (scaled)
1178                    return convertDoubleScaledSequenceAction;
1179                return convertDoubleSequenceAction;
1180
1181            default:
1182                // not supported
1183                return null;
1184        }
1185    }
1186
1187    public static IcyAbstractAction undoAction = new IcyAbstractAction("Undo", new IcyIcon(ResourceUtil.ICON_UNDO),
1188            "Undo last operation (Ctrl+Z)", KeyEvent.VK_Z, SystemUtil.getMenuCtrlMask())
1189    {
1190        /**
1191         * 
1192         */
1193        private static final long serialVersionUID = 5773755313377178022L;
1194
1195        @Override
1196        public boolean doAction(ActionEvent e)
1197        {
1198            return Icy.getMainInterface().undo();
1199        }
1200
1201        @Override
1202        public boolean isEnabled()
1203        {
1204            final IcyUndoManager undoManager = Icy.getMainInterface().getUndoManager();
1205
1206            if (super.isEnabled() && (undoManager != null))
1207                return undoManager.canUndo();
1208
1209            return false;
1210        }
1211    };
1212
1213    public static IcyAbstractAction redoAction = new IcyAbstractAction("Redo", new IcyIcon(ResourceUtil.ICON_REDO),
1214            "Redo last operation (Ctrl+Y)", KeyEvent.VK_Y, SystemUtil.getMenuCtrlMask())
1215    {
1216        /**
1217         * 
1218         */
1219        private static final long serialVersionUID = 1288382252962040008L;
1220
1221        @Override
1222        public boolean doAction(ActionEvent e)
1223        {
1224            return Icy.getMainInterface().redo();
1225        }
1226
1227        @Override
1228        public boolean isEnabled()
1229        {
1230            final IcyUndoManager undoManager = Icy.getMainInterface().getUndoManager();
1231
1232            if (super.isEnabled() && (undoManager != null))
1233                return undoManager.canRedo();
1234
1235            return false;
1236        }
1237    };
1238
1239    public static IcyAbstractAction undoClearAction = new IcyAbstractAction("Clear history",
1240            new IcyIcon(ResourceUtil.ICON_TRASH), "Clear all history (will release some memory")
1241    {
1242        /**
1243         * 
1244         */
1245        private static final long serialVersionUID = 1251314072585735122L;
1246
1247        @Override
1248        public boolean doAction(ActionEvent e)
1249        {
1250            final IcyUndoManager undoManager = Icy.getMainInterface().getUndoManager();
1251
1252            if (undoManager != null)
1253            {
1254                undoManager.discardAllEdits();
1255                return true;
1256            }
1257
1258            return false;
1259        }
1260
1261        @Override
1262        public boolean isEnabled()
1263        {
1264            final IcyUndoManager undoManager = Icy.getMainInterface().getUndoManager();
1265
1266            if (super.isEnabled() && (undoManager != null))
1267                return undoManager.canUndo() || undoManager.canRedo();
1268
1269            return false;
1270        }
1271    };
1272
1273    public static IcyAbstractAction undoClearAllButLastAction = new IcyAbstractAction("Clear all but last",
1274            new IcyIcon(ResourceUtil.ICON_CLEAR_BEFORE),
1275            "Clear all history but the last operation (can release some memory)")
1276    {
1277        /**
1278         * 
1279         */
1280        private static final long serialVersionUID = 5773755313377178022L;
1281
1282        @Override
1283        public boolean doAction(ActionEvent e)
1284        {
1285            final IcyUndoManager undoManager = Icy.getMainInterface().getUndoManager();
1286
1287            if (undoManager != null)
1288            {
1289                undoManager.discardOldEdits(1);
1290                undoManager.discardFutureEdits();
1291                return true;
1292            }
1293
1294            return false;
1295        }
1296
1297        @Override
1298        public boolean isEnabled()
1299        {
1300            final IcyUndoManager undoManager = Icy.getMainInterface().getUndoManager();
1301
1302            if (super.isEnabled() && (undoManager != null))
1303                return undoManager.canUndo();
1304
1305            return false;
1306        }
1307    };
1308
1309    /**
1310     * Return all actions of this class
1311     */
1312    public static List<IcyAbstractAction> getAllActions()
1313    {
1314        final List<IcyAbstractAction> result = new ArrayList<IcyAbstractAction>();
1315
1316        for (Field field : SequenceOperationActions.class.getFields())
1317        {
1318            final Class<?> type = field.getType();
1319
1320            try
1321            {
1322                if (ClassUtil.isSubClass(type, IcyAbstractAction[].class))
1323                    result.addAll(Arrays.asList(((IcyAbstractAction[]) field.get(null))));
1324                else if (ClassUtil.isSubClass(type, IcyAbstractAction.class))
1325                    result.add((IcyAbstractAction) field.get(null));
1326            }
1327            catch (Exception e)
1328            {
1329                // ignore
1330            }
1331        }
1332
1333        return result;
1334    }
1335}