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.event.ActionEvent;
022import java.awt.event.InputEvent;
023import java.awt.event.KeyEvent;
024import java.io.PrintWriter;
025import java.lang.reflect.Field;
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.List;
029
030import org.w3c.dom.Document;
031
032import icy.clipboard.Clipboard;
033import icy.file.FileUtil;
034import icy.gui.dialog.IdConfirmDialog;
035import icy.gui.dialog.MessageDialog;
036import icy.gui.dialog.OpenDialog;
037import icy.gui.dialog.SaveDialog;
038import icy.gui.inspector.RoisPanel;
039import icy.gui.main.MainFrame;
040import icy.main.Icy;
041import icy.resource.ResourceUtil;
042import icy.resource.icon.IcyIcon;
043import icy.roi.ROI;
044import icy.roi.ROI2D;
045import icy.roi.ROI3D;
046import icy.roi.ROI4D;
047import icy.roi.ROIUtil;
048import icy.sequence.Sequence;
049import icy.sequence.SequenceDataIterator;
050import icy.sequence.edit.ROIAddSequenceEdit;
051import icy.sequence.edit.ROIAddsSequenceEdit;
052import icy.sequence.edit.ROIReplacesSequenceEdit;
053import icy.system.SystemUtil;
054import icy.type.DataIteratorUtil;
055import icy.util.ClassUtil;
056import icy.util.ShapeUtil.BooleanOperator;
057import icy.util.StringUtil;
058import icy.util.XLSUtil;
059import icy.util.XMLUtil;
060import jxl.write.WritableSheet;
061import jxl.write.WritableWorkbook;
062import plugins.kernel.roi.roi2d.ROI2DRectangle;
063import plugins.kernel.roi.roi3d.ROI3DStackRectangle;
064import plugins.kernel.roi.roi4d.ROI4DStackRectangle;
065import plugins.kernel.roi.roi5d.ROI5DStackRectangle;
066
067/**
068 * Roi actions (open / save / copy / paste / merge...)
069 * 
070 * @author Stephane
071 */
072public class RoiActions
073{
074    public static final String DEFAULT_ROI_DIR = "roi";
075    public static final String DEFAULT_ROI_NAME = "roi.xml";
076
077    public static class SequenceRoiList
078    {
079        public final Sequence sequence;
080        public final List<ROI> rois;
081
082        public SequenceRoiList(Sequence sequence, List<ROI> rois)
083        {
084            super();
085
086            this.sequence = sequence;
087            this.rois = rois;
088        }
089    }
090
091    public static IcyAbstractAction loadAction = new IcyAbstractAction("Load ROI(s)",
092            new IcyIcon(ResourceUtil.ICON_OPEN), "Load ROI(s) from file",
093            "Load ROI(s) from a XML file and add them to the active sequence")
094    {
095        /**
096         * 
097         */
098        private static final long serialVersionUID = 2378084039864016238L;
099
100        @Override
101        public boolean doAction(ActionEvent e)
102        {
103            final String filename = OpenDialog.chooseFile("Load roi(s)...", DEFAULT_ROI_DIR, DEFAULT_ROI_NAME);
104            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
105
106            if ((filename != null) && (sequence != null))
107            {
108                final Document doc = XMLUtil.loadDocument(filename);
109
110                if (doc != null)
111                {
112                    final List<ROI> rois = ROI.loadROIsFromXML(XMLUtil.getRootElement(doc));
113
114                    sequence.beginUpdate();
115                    try
116                    {
117                        // add to sequence
118                        for (ROI roi : rois)
119                            sequence.addROI(roi);
120                    }
121                    finally
122                    {
123                        sequence.endUpdate();
124                    }
125
126                    // add to undo manager
127                    sequence.addUndoableEdit(new ROIAddsSequenceEdit(sequence, rois)
128                    {
129                        @Override
130                        public String getPresentationName()
131                        {
132                            if (getROIs().size() > 1)
133                                return "ROIs loaded from XML file";
134
135                            return "ROI loaded from XML file";
136                        };
137                    });
138
139                    return true;
140                }
141            }
142
143            return false;
144        }
145
146        @Override
147        public boolean isEnabled()
148        {
149            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
150        }
151    };
152
153    public static IcyAbstractAction saveAction = new IcyAbstractAction("Save ROI(s)",
154            new IcyIcon(ResourceUtil.ICON_SAVE), "Save selected ROI(s) to file",
155            "Save the selected ROI(s) from active sequence into a XML file")
156    {
157        /**
158         * 
159         */
160        private static final long serialVersionUID = 349358870716619748L;
161
162        @Override
163        public boolean doAction(ActionEvent e)
164        {
165            final String filename = SaveDialog.chooseFile("Save roi(s)...", DEFAULT_ROI_DIR, DEFAULT_ROI_NAME);
166            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
167
168            if ((filename != null) && (sequence != null))
169            {
170                final List<ROI> rois = sequence.getSelectedROIs();
171
172                if (rois.size() > 0)
173                {
174                    final Document doc = XMLUtil.createDocument(true);
175
176                    if (doc != null)
177                    {
178                        ROI.saveROIsToXML(XMLUtil.getRootElement(doc), rois);
179                        XMLUtil.saveDocument(doc, filename);
180                        return true;
181                    }
182                }
183            }
184
185            return false;
186        }
187
188        @Override
189        public boolean isEnabled()
190        {
191            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
192        }
193    };
194
195    public static IcyAbstractAction copyAction = new IcyAbstractAction("Copy", new IcyIcon(ResourceUtil.ICON_COPY),
196            "Copy selected ROI to clipboard (Ctrl+C)", KeyEvent.VK_C, SystemUtil.getMenuCtrlMask())
197    {
198        /**
199         * 
200         */
201        private static final long serialVersionUID = -4716027958152503425L;
202
203        @Override
204        public boolean doAction(ActionEvent e)
205        {
206            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
207
208            if (sequence != null)
209            {
210                final List<ROI> rois = sequence.getSelectedROIs();
211
212                if (rois.size() > 0)
213                {
214                    // need to get a copy of the ROI (as it can change meanwhile)
215                    for (int i = 0; i < rois.size(); i++)
216                    {
217                        final ROI roi = rois.get(i).getCopy();
218
219                        if (roi != null)
220                            rois.set(i, roi);
221                    }
222
223                    // save in the Icy clipboard
224                    Clipboard.put(Clipboard.TYPE_SEQUENCEROILIST, new SequenceRoiList(sequence, rois));
225                    // clear system clipboard
226                    Clipboard.clearSystem();
227
228                    pasteAction.setEnabled(true);
229
230                    return true;
231                }
232            }
233
234            return false;
235        }
236
237        @Override
238        public boolean isEnabled()
239        {
240            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
241            return super.isEnabled() && (sequence != null) && (sequence.getSelectedROIs().size() > 0);
242        }
243    };
244
245    public static IcyAbstractAction copyLinkAction = new IcyAbstractAction("Copy link",
246            new IcyIcon(ResourceUtil.ICON_LINK_COPY), "Copy link of selected ROI to clipboard (Alt+C)", KeyEvent.VK_C,
247            InputEvent.ALT_MASK)
248    {
249        /**
250         * 
251         */
252        private static final long serialVersionUID = -4716027958152503425L;
253
254        @Override
255        public boolean doAction(ActionEvent e)
256        {
257            final RoisPanel roisPanel = Icy.getMainInterface().getRoisPanel();
258
259            if (roisPanel != null)
260            {
261                final List<ROI> rois = roisPanel.getSelectedRois();
262
263                if (rois.size() > 0)
264                {
265                    // save in the Icy clipboard
266                    Clipboard.put(Clipboard.TYPE_ROILINKLIST, rois);
267                    // clear system clipboard
268                    Clipboard.clearSystem();
269
270                    pasteLinkAction.setEnabled(true);
271
272                    return true;
273                }
274            }
275
276            return false;
277        }
278
279        @Override
280        public boolean isEnabled()
281        {
282            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
283            return super.isEnabled() && (sequence != null) && (sequence.getSelectedROIs().size() > 0);
284        }
285    };
286
287    public static IcyAbstractAction pasteAction = new IcyAbstractAction("Paste", new IcyIcon(ResourceUtil.ICON_PASTE),
288            "Paste ROI from clipboard (Ctrl+V)", KeyEvent.VK_V, SystemUtil.getMenuCtrlMask())
289    {
290        /**
291         * 
292         */
293        private static final long serialVersionUID = 4878585451006567513L;
294
295        @Override
296        public boolean doAction(ActionEvent e)
297        {
298            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
299
300            if (sequence != null)
301            {
302                final SequenceRoiList sequenceRoiList = (SequenceRoiList) Clipboard.get(Clipboard.TYPE_SEQUENCEROILIST);
303                final Sequence sequenceSrc = sequenceRoiList.sequence;
304                final List<ROI> rois = sequenceRoiList.rois;
305
306                if ((rois != null) && (rois.size() > 0))
307                {
308                    final List<ROI> copyRois = new ArrayList<ROI>();
309                    sequence.beginUpdate();
310                    try
311                    {
312                        // unselect all rois
313                        sequence.setSelectedROI(null);
314
315                        // add copy to sequence (so we can do the paste operation severals time)
316                        for (ROI roi : rois)
317                        {
318                            // final ROI newROI = roi.getCopy();
319                            final ROI newROI = ROIUtil.adjustToSequence(roi, sequenceSrc, sequence, true, true, true);
320
321                            if (newROI != null)
322                            {
323                                copyRois.add(newROI);
324
325                                // select the ROI
326                                newROI.setSelected(true);
327                                // and add it
328                                sequence.addROI(newROI);
329                            }
330                        }
331                    }
332                    finally
333                    {
334                        sequence.endUpdate();
335                    }
336
337                    // add to undo manager
338                    sequence.addUndoableEdit(new ROIAddsSequenceEdit(sequence, copyRois)
339                    {
340                        @Override
341                        public String getPresentationName()
342                        {
343                            if (getROIs().size() > 1)
344                                return "ROIs added from clipboard";
345
346                            return "ROI added from clipboard";
347                        };
348                    });
349
350                    return true;
351                }
352            }
353
354            return false;
355        }
356
357        @Override
358        public boolean isEnabled()
359        {
360            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null)
361                    && Clipboard.getType().equals(Clipboard.TYPE_SEQUENCEROILIST);
362        }
363    };
364
365    public static IcyAbstractAction pasteLinkAction = new IcyAbstractAction("Paste link",
366            new IcyIcon(ResourceUtil.ICON_LINK_PASTE), "Paste ROI link from clipboard (Alt+V)", KeyEvent.VK_V,
367            InputEvent.ALT_MASK)
368    {
369        /**
370         * 
371         */
372        private static final long serialVersionUID = 4878585451006567513L;
373
374        @Override
375        public boolean doAction(ActionEvent e)
376        {
377            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
378
379            if (sequence != null)
380            {
381                @SuppressWarnings("unchecked")
382                final List<ROI> rois = (List<ROI>) Clipboard.get(Clipboard.TYPE_ROILINKLIST);
383
384                if ((rois != null) && (rois.size() > 0))
385                {
386                    sequence.beginUpdate();
387                    try
388                    {
389                        // add to sequence
390                        for (ROI roi : rois)
391                            sequence.addROI(roi);
392                    }
393                    finally
394                    {
395                        sequence.endUpdate();
396                    }
397
398                    // add to undo manager
399                    sequence.addUndoableEdit(new ROIAddsSequenceEdit(sequence, rois)
400                    {
401                        @Override
402                        public String getPresentationName()
403                        {
404                            if (getROIs().size() > 1)
405                                return "ROIs linked from clipboard";
406
407                            return "ROI linked from clipboard";
408                        };
409                    });
410
411                    return true;
412                }
413            }
414
415            return false;
416        }
417
418        @Override
419        public boolean isEnabled()
420        {
421            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null)
422                    && Clipboard.getType().equals(Clipboard.TYPE_ROILINKLIST);
423        }
424    };
425
426    // public static IcyAbstractAction clearClipboardAction = new IcyAbstractAction("Clear", new
427    // IcyIcon(
428    // ResourceUtil.ICON_CLIPBOARD_CLEAR), "Remove ROI saved in clipboard")
429    // {
430    // /**
431    // *
432    // */
433    // private static final long serialVersionUID = 4878585451006567513L;
434    //
435    // @Override
436    // public boolean doAction(ActionEvent e)
437    // {
438    // Clipboard.remove(ID_ROI_COPY_CLIPBOARD, false);
439    // pasteAction.setEnabled(false);
440    // }
441    //
442    // @Override
443    // public boolean isEnabled()
444    // {
445    // return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null)
446    // && Clipboard.hasObjects(RoiActions.ID_ROI_COPY_CLIPBOARD, false);
447    // }
448    // };
449
450    public static IcyAbstractAction selectAllAction = new IcyAbstractAction("SelectAll", (IcyIcon) null,
451            "Select all ROI(s)")
452    {
453        /**
454         * 
455         */
456        private static final long serialVersionUID = 3219000949426093919L;
457
458        @Override
459        public boolean doAction(ActionEvent e)
460        {
461            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
462
463            if (sequence != null)
464            {
465                sequence.setSelectedROIs((List<ROI>) sequence.getROIs());
466                return true;
467            }
468
469            return false;
470        }
471
472        @Override
473        public boolean isEnabled()
474        {
475            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
476
477            return super.isEnabled() && (sequence != null) && (sequence.getROIs().size() > 0);
478        }
479    };
480
481    public static IcyAbstractAction unselectAction = new IcyAbstractAction("Unselect", (IcyIcon) null,
482            "Unselect ROI(s)", KeyEvent.VK_ESCAPE)
483    {
484        /**
485         * 
486         */
487        private static final long serialVersionUID = -6136680076368815566L;
488
489        @Override
490        public boolean doAction(ActionEvent e)
491        {
492            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
493
494            if (sequence != null)
495            {
496                sequence.setSelectedROI(null);
497                return true;
498            }
499
500            return false;
501        }
502
503        @Override
504        public boolean isEnabled()
505        {
506            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
507        }
508    };
509
510    public static IcyAbstractAction deleteAction = new IcyAbstractAction("Delete",
511            new IcyIcon(ResourceUtil.ICON_DELETE), "Delete selected ROI(s)",
512            "Delete selected ROI(s) from the active sequence", KeyEvent.VK_DELETE, 0)
513    {
514        /**
515         * 
516         */
517        private static final long serialVersionUID = 9079403002834893222L;
518
519        @Override
520        public boolean doAction(ActionEvent e)
521        {
522            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
523
524            if (sequence != null)
525            {
526                sequence.removeSelectedROIs(false, true);
527                return true;
528            }
529
530            return false;
531        }
532
533        @Override
534        public boolean isEnabled()
535        {
536            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
537            return super.isEnabled() && (sequence != null) && (sequence.getSelectedROIs().size() > 0);
538        }
539    };
540
541    public static IcyAbstractAction boolNotAction = new IcyAbstractAction("Inversion",
542            new IcyIcon(ResourceUtil.ICON_ROI_NOT), "Boolean inversion operation",
543            "Create a new ROI representing the inverse of selected ROI", true, "Computing inverse...")
544    {
545        /**
546         * 
547         */
548        private static final long serialVersionUID = 6360796066188754099L;
549
550        @Override
551        public boolean doAction(ActionEvent e)
552        {
553            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
554            final RoisPanel roisPanel = Icy.getMainInterface().getRoisPanel();
555
556            if ((sequence != null) && (roisPanel != null))
557            {
558                // NOT operation
559                sequence.beginUpdate();
560                try
561                {
562                    final List<ROI> selectedROI = roisPanel.getSelectedRois();
563
564                    // work only on single ROI
565                    if (selectedROI.size() != 1)
566                        return false;
567
568                    final ROI roi = selectedROI.get(0);
569                    final ROI seqRoi;
570
571                    switch (roi.getDimension())
572                    {
573                        case 2:
574                            final ROI2D roi2d = (ROI2D) roi;
575                            final ROI2DRectangle seqRoi2d = new ROI2DRectangle(sequence.getBounds2D());
576                            // set on same position
577                            seqRoi2d.setZ(roi2d.getZ());
578                            seqRoi2d.setT(roi2d.getT());
579                            seqRoi2d.setC(roi2d.getC());
580                            seqRoi = seqRoi2d;
581                            break;
582
583                        case 3:
584                            final ROI3D roi3d = (ROI3D) roi;
585                            final ROI3DStackRectangle seqRoi3d = new ROI3DStackRectangle(
586                                    sequence.getBounds5D().toRectangle3D());
587                            // set on same position
588                            seqRoi3d.setT(roi3d.getT());
589                            seqRoi3d.setC(roi3d.getC());
590                            seqRoi = seqRoi3d;
591                            break;
592
593                        case 4:
594                            final ROI4D roi4d = (ROI4D) roi;
595                            final ROI4DStackRectangle seqRoi4d = new ROI4DStackRectangle(
596                                    sequence.getBounds5D().toRectangle4D());
597                            // set on same position
598                            seqRoi4d.setC(roi4d.getC());
599                            seqRoi = seqRoi4d;
600                            break;
601
602                        case 5:
603                            seqRoi = new ROI5DStackRectangle(sequence.getBounds5D());
604                            break;
605
606                        default:
607                            seqRoi = null;
608                            break;
609                    }
610
611                    if (seqRoi != null)
612                    {
613                        // we do the NOT operation by subtracting current ROI to sequence bounds ROI
614                        final ROI mergeROI = ROIUtil.subtract(seqRoi, roi);
615
616                        if (mergeROI != null)
617                        {
618                            mergeROI.setName("Inverse");
619
620                            sequence.addROI(mergeROI);
621                            sequence.setSelectedROI(mergeROI);
622
623                            // add to undo manager
624                            sequence.addUndoableEdit(new ROIAddSequenceEdit(sequence, mergeROI, "ROI Inverse"));
625                        }
626                    }
627                    else
628                        MessageDialog.showDialog("Operation not supported", "Input ROI has incorrect dimension !",
629                                MessageDialog.ERROR_MESSAGE);
630                }
631                catch (UnsupportedOperationException ex)
632                {
633                    MessageDialog.showDialog("Operation not supported", ex.getLocalizedMessage(),
634                            MessageDialog.ERROR_MESSAGE);
635                }
636                finally
637                {
638                    sequence.endUpdate();
639                }
640
641                return true;
642            }
643
644            return false;
645        }
646
647        @Override
648        public boolean isEnabled()
649        {
650            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
651        }
652    };
653
654    public static IcyAbstractAction boolOrAction = new IcyAbstractAction("Union", new IcyIcon(ResourceUtil.ICON_ROI_OR),
655            "Boolean union operation", "Create a new ROI representing the union of selected ROIs", true,
656            "Computing union...")
657    {
658        /**
659         * 
660         */
661        private static final long serialVersionUID = 1861052712498233441L;
662
663        @Override
664        public boolean doAction(ActionEvent e)
665        {
666            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
667            final RoisPanel roisPanel = Icy.getMainInterface().getRoisPanel();
668
669            if ((sequence != null) && (roisPanel != null))
670            {
671                // OR operation
672                sequence.beginUpdate();
673                try
674                {
675                    final List<ROI> selectedROIs = roisPanel.getSelectedRois();
676                    final ROI mergeROI = ROIUtil.getUnion(selectedROIs);
677
678                    if (mergeROI != null)
679                    {
680                        mergeROI.setName("Union");
681
682                        sequence.addROI(mergeROI);
683                        sequence.setSelectedROI(mergeROI);
684
685                        // add to undo manager
686                        sequence.addUndoableEdit(new ROIAddSequenceEdit(sequence, mergeROI, "ROI Union"));
687                    }
688                }
689                catch (UnsupportedOperationException ex)
690                {
691                    MessageDialog.showDialog("Operation not supported", ex.getLocalizedMessage(),
692                            MessageDialog.ERROR_MESSAGE);
693                }
694                finally
695                {
696                    sequence.endUpdate();
697                }
698
699                return true;
700            }
701
702            return false;
703        }
704
705        @Override
706        public boolean isEnabled()
707        {
708            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
709        }
710    };
711
712    public static IcyAbstractAction boolAndAction = new IcyAbstractAction("Intersection",
713            new IcyIcon(ResourceUtil.ICON_ROI_AND), "Boolean intersection operation",
714            "Create a new ROI representing the intersection of selected ROIs", true, "Computing intersection...")
715    {
716        /**
717         * 
718         */
719        private static final long serialVersionUID = -9103158044679039413L;
720
721        @Override
722        public boolean doAction(ActionEvent e)
723        {
724            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
725            final RoisPanel roisPanel = Icy.getMainInterface().getRoisPanel();
726
727            if ((sequence != null) && (roisPanel != null))
728            {
729                // AND operation
730                sequence.beginUpdate();
731                try
732                {
733                    final List<ROI> selectedROIs = roisPanel.getSelectedRois();
734                    final ROI mergeROI = ROIUtil.getIntersection(selectedROIs);
735
736                    if (mergeROI != null)
737                    {
738                        mergeROI.setName("Intersection");
739
740                        sequence.addROI(mergeROI);
741                        sequence.setSelectedROI(mergeROI);
742
743                        // add to undo manager
744                        sequence.addUndoableEdit(new ROIAddSequenceEdit(sequence, mergeROI, "ROI Intersection"));
745                    }
746                }
747                catch (UnsupportedOperationException ex)
748                {
749                    MessageDialog.showDialog("Operation not supported", ex.getLocalizedMessage(),
750                            MessageDialog.ERROR_MESSAGE);
751                }
752                finally
753                {
754                    sequence.endUpdate();
755                }
756
757                return true;
758            }
759
760            return false;
761        }
762
763        @Override
764        public boolean isEnabled()
765        {
766            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
767        }
768    };
769
770    public static IcyAbstractAction boolXorAction = new IcyAbstractAction("Exclusive union",
771            new IcyIcon(ResourceUtil.ICON_ROI_XOR), "Boolean exclusive union operation",
772            "Create a new ROI representing the exclusive union of selected ROIs", true, "Computing exclusive union...")
773    {
774        /**
775         * 
776         */
777        private static final long serialVersionUID = 1609345474914807703L;
778
779        @Override
780        public boolean doAction(ActionEvent e)
781        {
782            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
783            final RoisPanel roisPanel = Icy.getMainInterface().getRoisPanel();
784
785            if ((sequence != null) && (roisPanel != null))
786            {
787                // XOR operation
788                sequence.beginUpdate();
789                try
790                {
791                    final List<ROI> selectedROIs = roisPanel.getSelectedRois();
792                    final ROI mergeROI = ROIUtil.getExclusiveUnion(selectedROIs);
793
794                    if (mergeROI != null)
795                    {
796                        mergeROI.setName("Exclusive union");
797
798                        sequence.addROI(mergeROI);
799                        sequence.setSelectedROI(mergeROI);
800
801                        // add to undo manager
802                        sequence.addUndoableEdit(new ROIAddSequenceEdit(sequence, mergeROI, "ROI Exclusive Union"));
803                    }
804                }
805                catch (UnsupportedOperationException ex)
806                {
807                    MessageDialog.showDialog("Operation not supported", ex.getLocalizedMessage(),
808                            MessageDialog.ERROR_MESSAGE);
809                }
810                finally
811                {
812                    sequence.endUpdate();
813                }
814
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 boolSubtractAction = new IcyAbstractAction("Subtraction",
829            new IcyIcon(ResourceUtil.ICON_ROI_SUB), "Boolean subtraction",
830            "Create 2 ROIs representing the result of (A - B) and (B - A)", true, "Computing subtraction...")
831    {
832        /**
833         * 
834         */
835        private static final long serialVersionUID = 9094641559971542667L;
836
837        @Override
838        public boolean doAction(ActionEvent e)
839        {
840            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
841            final RoisPanel roisPanel = Icy.getMainInterface().getRoisPanel();
842
843            if ((sequence != null) && (roisPanel != null))
844            {
845                // SUB operation
846                sequence.beginUpdate();
847                try
848                {
849                    final List<ROI> selectedROI = roisPanel.getSelectedRois();
850                    final List<ROI> generatedROIs = new ArrayList<ROI>();
851
852                    // Subtraction work only when 2 ROI are selected
853                    if (selectedROI.size() != 2)
854                        return false;
855
856                    final ROI subtractAB = ROIUtil.subtract(selectedROI.get(0), selectedROI.get(1));
857                    final ROI subtractBA = ROIUtil.subtract(selectedROI.get(1), selectedROI.get(0));
858
859                    subtractAB.setName("Subtract A-B");
860                    subtractBA.setName("Subtract B-A");
861
862                    generatedROIs.add(subtractAB);
863                    generatedROIs.add(subtractBA);
864
865                    sequence.beginUpdate();
866                    try
867                    {
868                        for (ROI roi : generatedROIs)
869                            sequence.addROI(roi);
870
871                        sequence.setSelectedROIs(generatedROIs);
872
873                        // add to undo manager
874                        sequence.addUndoableEdit(new ROIAddsSequenceEdit(sequence, generatedROIs, "ROI Subtraction"));
875                    }
876                    finally
877                    {
878                        sequence.endUpdate();
879                    }
880                }
881                catch (UnsupportedOperationException ex)
882                {
883                    MessageDialog.showDialog("Operation not supported", ex.getLocalizedMessage(),
884                            MessageDialog.ERROR_MESSAGE);
885                }
886                finally
887                {
888                    sequence.endUpdate();
889                }
890
891                return true;
892            }
893
894            return false;
895        }
896
897        @Override
898        public boolean isEnabled()
899        {
900            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
901        }
902    };
903
904    public static IcyAbstractAction fillInteriorAction = new IcyAbstractAction("Fill interior",
905            new IcyIcon(ResourceUtil.ICON_ROI_INTERIOR), "Fill ROI(s) interior",
906            "Fill interior of the selected ROI(s) with specified value", true, "Fill ROI(s) interior...")
907    {
908        @Override
909        public boolean doAction(ActionEvent e)
910        {
911            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
912
913            if (sequence != null)
914            {
915                final MainFrame mainFrame = Icy.getMainInterface().getMainFrame();
916
917                if (mainFrame != null)
918                {
919                    final double value = mainFrame.getMainRibbon().getROIRibbonTask().getFillValue();
920                    // create undo point
921                    final boolean canUndo = sequence.createUndoDataPoint("ROI fill interior");
922
923                    // cannot backup
924                    if (!canUndo)
925                    {
926                        // ask confirmation to continue
927                        if (!IdConfirmDialog.confirm(
928                                "Not enough memory to undo the operation, do you want to continue ?",
929                                "ROIFillInteriorConfirm"))
930                            return false;
931                    }
932
933                    for (ROI roi : sequence.getSelectedROIs())
934                        DataIteratorUtil.set(new SequenceDataIterator(sequence, roi, true), value);
935
936                    sequence.dataChanged();
937
938                    // no undo, clear undo manager after modification
939                    if (!canUndo)
940                        sequence.clearUndoManager();
941
942                    return true;
943                }
944            }
945
946            return false;
947        }
948
949        @Override
950        public boolean isEnabled()
951        {
952            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
953
954            return super.isEnabled() && (sequence != null) && !sequence.isEmpty();
955        }
956    };
957
958    public static IcyAbstractAction fillExteriorAction = new IcyAbstractAction("Fill exterior",
959            new IcyIcon(ResourceUtil.ICON_ROI_NOT), "Fill ROI(s) exterior",
960            "Fill exterior of the selected ROI(s) with specified value", true, "Fill ROI(s) exterior...")
961    {
962        @Override
963        public boolean doAction(ActionEvent e)
964        {
965            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
966
967            if (sequence != null)
968            {
969                final MainFrame mainFrame = Icy.getMainInterface().getMainFrame();
970
971                if (mainFrame != null)
972                {
973                    final double value = mainFrame.getMainRibbon().getROIRibbonTask().getFillValue();
974                    // create undo point
975                    final boolean canUndo = sequence.createUndoDataPoint("ROI fill exterior");
976
977                    // cannot backup
978                    if (!canUndo)
979                    {
980                        // ask confirmation to continue
981                        if (!IdConfirmDialog.confirm(
982                                "Not enough memory to undo the operation, do you want to continue ?",
983                                "ROIFillExteriorConfirm"))
984                            return false;
985                    }
986
987                    try
988                    {
989                        final ROI roiUnion = ROIUtil.merge(sequence.getSelectedROIs(), BooleanOperator.OR);
990                        final ROI roiSeq = new ROI5DStackRectangle(sequence.getBounds5D());
991                        final ROI roi = roiSeq.getSubtraction(roiUnion);
992
993                        DataIteratorUtil.set(new SequenceDataIterator(sequence, roi), value);
994
995                        sequence.dataChanged();
996
997                        // no undo, clear undo manager after modification
998                        if (!canUndo)
999                            sequence.clearUndoManager();
1000
1001                        return true;
1002                    }
1003                    catch (UnsupportedOperationException ex)
1004                    {
1005                        // undo operation if possible
1006                        if (canUndo)
1007                            sequence.undo();
1008
1009                        MessageDialog.showDialog("Operation not supported", ex.getLocalizedMessage(),
1010                                MessageDialog.ERROR_MESSAGE);
1011
1012                        return false;
1013                    }
1014                }
1015            }
1016
1017            return false;
1018        }
1019
1020        @Override
1021        public boolean isEnabled()
1022        {
1023            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1024
1025            return super.isEnabled() && (sequence != null) && !sequence.isEmpty();
1026        }
1027    };
1028
1029    public static IcyAbstractAction xlsExportAction = new IcyAbstractAction("Excel export",
1030            new IcyIcon(ResourceUtil.ICON_XLS_EXPORT), "ROI Excel export",
1031            "Export the content of the ROI table into a XLS/CSV file", true, "Exporting ROI informations...")
1032    {
1033        /**
1034         * 
1035         */
1036        private static final long serialVersionUID = 9094641559971542667L;
1037
1038        @Override
1039        public boolean doAction(ActionEvent e)
1040        {
1041            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1042            final RoisPanel roisPanel = Icy.getMainInterface().getRoisPanel();
1043
1044            if ((sequence != null) && (roisPanel != null))
1045            {
1046                final String content = roisPanel.getCSVFormattedInfos();
1047
1048                if (StringUtil.isEmpty(content) || roisPanel.getVisibleRois().isEmpty())
1049                {
1050                    MessageDialog.showDialog("Nothing to export !", MessageDialog.INFORMATION_MESSAGE);
1051                    return true;
1052                }
1053
1054                final String filename = SaveDialog.chooseFileForResult("Export ROIs...", "result", ".xls");
1055
1056                if (filename != null)
1057                {
1058                    try
1059                    {
1060                        // CSV format wanted ?
1061                        if (!FileUtil.getFileExtension(filename, false).toLowerCase().startsWith("xls"))
1062                        {
1063                            // just write CSV content
1064                            final PrintWriter out = new PrintWriter(filename);
1065                            out.println(content);
1066                            out.close();
1067                        }
1068                        // XLS export
1069                        else
1070                        {
1071                            final WritableWorkbook workbook = XLSUtil.createWorkbook(filename);
1072                            final WritableSheet sheet = XLSUtil.createNewPage(workbook, "ROIS");
1073
1074                            if (XLSUtil.setFromCSV(sheet, content))
1075                                XLSUtil.saveAndClose(workbook);
1076                            else
1077                            {
1078                                MessageDialog.showDialog("Error",
1079                                        "Error while exporting ROIs table content to XLS file.",
1080                                        MessageDialog.ERROR_MESSAGE);
1081                                return false;
1082                            }
1083                        }
1084                    }
1085                    catch (Exception e1)
1086                    {
1087                        MessageDialog.showDialog("Error", e1.getMessage(), MessageDialog.ERROR_MESSAGE);
1088                        return false;
1089                    }
1090                }
1091
1092                return true;
1093            }
1094
1095            return false;
1096        }
1097
1098        @Override
1099        public boolean isEnabled()
1100        {
1101            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1102
1103            return super.isEnabled() && (sequence != null);
1104        }
1105    };
1106
1107    public static IcyAbstractAction settingAction = new IcyAbstractAction("Preferences",
1108            new IcyIcon(ResourceUtil.ICON_COG), "ROI table preferences")
1109    {
1110        @Override
1111        public boolean doAction(ActionEvent e)
1112        {
1113            final RoisPanel roisPanel = Icy.getMainInterface().getRoisPanel();
1114
1115            if (roisPanel != null)
1116            {
1117                roisPanel.showSettingPanel();
1118
1119                return true;
1120            }
1121
1122            return false;
1123        }
1124    };
1125
1126    public static IcyAbstractAction convertToStackAction = new IcyAbstractAction("to 3D stack",
1127            new IcyIcon(ResourceUtil.ICON_LAYER_V2), "Convert to 3D stack ROI",
1128            "Convert selected 2D ROI to 3D stack ROI by stacking it along the Z axis")
1129    {
1130        @Override
1131        public boolean doAction(ActionEvent e)
1132        {
1133            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1134
1135            if (sequence != null)
1136            {
1137                final int maxZ = sequence.getSizeZ() - 1;
1138
1139                // ROI Z stack conversion
1140                sequence.beginUpdate();
1141                try
1142                {
1143                    final List<ROI2D> selectedROIs = sequence.getSelectedROI2Ds();
1144                    final List<ROI> removedROIs = new ArrayList<ROI>();
1145                    final List<ROI> addedROIs = new ArrayList<ROI>();
1146
1147                    for (ROI2D roi : selectedROIs)
1148                    {
1149                        final ROI stackedRoi = ROIUtil.convertToStack(roi, 0, maxZ);
1150
1151                        if (stackedRoi != null)
1152                        {
1153                            // select it by default
1154                            stackedRoi.setSelected(true);
1155
1156                            sequence.removeROI(roi);
1157                            sequence.addROI(stackedRoi);
1158
1159                            // add to undo manager
1160                            removedROIs.add(roi);
1161                            addedROIs.add(stackedRoi);
1162                        }
1163                    }
1164
1165                    if (!addedROIs.isEmpty())
1166                        sequence.addUndoableEdit(new ROIReplacesSequenceEdit(sequence, removedROIs, addedROIs,
1167                                (addedROIs.size() > 1) ? "ROIs 3D stack conversion" : "ROI 3D stack conversion"));
1168                }
1169                catch (UnsupportedOperationException ex)
1170                {
1171                    MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1172                }
1173                finally
1174                {
1175                    sequence.endUpdate();
1176                }
1177
1178                return true;
1179            }
1180
1181            return false;
1182        }
1183
1184        @Override
1185        public boolean isEnabled()
1186        {
1187            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1188        }
1189    };
1190
1191    public static IcyAbstractAction convertToMaskAction = new IcyAbstractAction("to Mask",
1192            new IcyIcon(ResourceUtil.ICON_BOOL_MASK), "Convert Shape ROI to Mask ROI",
1193            "Convert selected Shape ROI to Mask ROI by using their boolean mask")
1194    {
1195        @Override
1196        public boolean doAction(ActionEvent e)
1197        {
1198            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1199
1200            if (sequence != null)
1201            {
1202                // ROI mask conversion
1203                sequence.beginUpdate();
1204                try
1205                {
1206                    final List<ROI> selectedROIs = sequence.getSelectedROIs();
1207                    final List<ROI> removedROIs = new ArrayList<ROI>();
1208                    final List<ROI> addedROIs = new ArrayList<ROI>();
1209
1210                    for (ROI roi : selectedROIs)
1211                    {
1212                        final ROI maskRoi = ROIUtil.convertToMask(roi);
1213
1214                        if (maskRoi != null)
1215                        {
1216                            // select it by default
1217                            maskRoi.setSelected(true);
1218
1219                            sequence.removeROI(roi);
1220                            sequence.addROI(maskRoi);
1221
1222                            // add to undo manager
1223                            removedROIs.add(roi);
1224                            addedROIs.add(maskRoi);
1225                        }
1226                    }
1227
1228                    if (!addedROIs.isEmpty())
1229                        sequence.addUndoableEdit(new ROIReplacesSequenceEdit(sequence, removedROIs, addedROIs,
1230                                (addedROIs.size() > 1) ? "ROIs mask conversion" : "ROI mask conversion"));
1231                }
1232                catch (UnsupportedOperationException ex)
1233                {
1234                    MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1235                }
1236                finally
1237                {
1238                    sequence.endUpdate();
1239                }
1240
1241                return true;
1242            }
1243
1244            return false;
1245        }
1246
1247        @Override
1248        public boolean isEnabled()
1249        {
1250            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1251        }
1252    };
1253
1254    public static IcyAbstractAction convertToPointAction = new IcyAbstractAction("to Point",
1255            new IcyIcon(ResourceUtil.ICON_ROI_POINT), "Convert ROI to Point ROI",
1256            "Converts selected ROI(s) to ROI Point (2D or 3D) representing the mass center of the input ROI(s)")
1257    {
1258        @Override
1259        public boolean doAction(ActionEvent e)
1260        {
1261            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1262
1263            if (sequence != null)
1264            {
1265                // ROI point conversion
1266                sequence.beginUpdate();
1267                try
1268                {
1269                    final List<ROI> selectedROIs = sequence.getSelectedROIs();
1270                    final List<ROI> removedROIs = new ArrayList<ROI>();
1271                    final List<ROI> addedROIs = new ArrayList<ROI>();
1272
1273                    for (ROI roi : selectedROIs)
1274                    {
1275                        final ROI roiPoint = ROIUtil.convertToPoint(roi);
1276
1277                        if (roiPoint != null)
1278                        {
1279                            // select it by default
1280                            roiPoint.setSelected(true);
1281
1282                            sequence.removeROI(roi);
1283                            sequence.addROI(roiPoint);
1284
1285                            // add to undo manager
1286                            removedROIs.add(roi);
1287                            addedROIs.add(roiPoint);
1288                        }
1289                    }
1290
1291                    if (!addedROIs.isEmpty())
1292                        sequence.addUndoableEdit(new ROIReplacesSequenceEdit(sequence, removedROIs, addedROIs,
1293                                (addedROIs.size() > 1) ? "ROIs point conversion" : "ROI point conversion"));
1294                }
1295                catch (UnsupportedOperationException ex)
1296                {
1297                    MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1298                }
1299                finally
1300                {
1301                    sequence.endUpdate();
1302                }
1303
1304                return true;
1305            }
1306
1307            return false;
1308        }
1309
1310        @Override
1311        public boolean isEnabled()
1312        {
1313            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1314        }
1315    };
1316
1317    public static IcyAbstractAction convertToEllipseAction = new IcyAbstractAction("to Circle",
1318            new IcyIcon(ResourceUtil.ICON_ROI_OVAL), "Convert ROI to Circle ROI",
1319            "Converts selected ROI(s) to Circle ROI centered on the mass center of the input ROI(s)")
1320    {
1321        @Override
1322        public boolean doAction(ActionEvent e)
1323        {
1324            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1325
1326            if (sequence != null)
1327            {
1328                final MainFrame mainFrame = Icy.getMainInterface().getMainFrame();
1329
1330                if (mainFrame != null)
1331                {
1332                    final double radius = mainFrame.getMainRibbon().getROIRibbonTask().getRadius();
1333
1334                    // ROI point conversion
1335                    sequence.beginUpdate();
1336                    try
1337                    {
1338                        final List<ROI> selectedROIs = sequence.getSelectedROIs();
1339                        final List<ROI> removedROIs = new ArrayList<ROI>();
1340                        final List<ROI> addedROIs = new ArrayList<ROI>();
1341
1342                        for (ROI roi : selectedROIs)
1343                        {
1344                            final ROI resultRoi;
1345
1346                            if (radius == 0)
1347                                resultRoi = ROIUtil.convertToPoint(roi);
1348                            else
1349                                resultRoi = ROIUtil.convertToEllipse(roi, radius, radius);
1350
1351                            if (resultRoi != null)
1352                            {
1353                                // select it by default
1354                                resultRoi.setSelected(true);
1355
1356                                sequence.removeROI(roi);
1357                                sequence.addROI(resultRoi);
1358
1359                                // add to undo manager
1360                                removedROIs.add(roi);
1361                                addedROIs.add(resultRoi);
1362                            }
1363                        }
1364
1365                        if (!addedROIs.isEmpty())
1366                            sequence.addUndoableEdit(new ROIReplacesSequenceEdit(sequence, removedROIs, addedROIs,
1367                                    (addedROIs.size() > 1) ? "ROIs circle conversion" : "ROI circle conversion"));
1368                    }
1369                    catch (UnsupportedOperationException ex)
1370                    {
1371                        MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1372                    }
1373                    finally
1374                    {
1375                        sequence.endUpdate();
1376                    }
1377                }
1378
1379                return true;
1380            }
1381
1382            return false;
1383        }
1384
1385        @Override
1386        public boolean isEnabled()
1387        {
1388            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1389        }
1390    };
1391
1392    public static IcyAbstractAction convertToRectangleAction = new IcyAbstractAction("to Square",
1393            new IcyIcon(ResourceUtil.ICON_ROI_RECTANGLE), "Convert ROI to Square ROI",
1394            "Converts selected ROI(s) to Square ROI centered on the mass center of the input ROI(s)")
1395    {
1396        @Override
1397        public boolean doAction(ActionEvent e)
1398        {
1399            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1400
1401            if (sequence != null)
1402            {
1403                final MainFrame mainFrame = Icy.getMainInterface().getMainFrame();
1404
1405                if (mainFrame != null)
1406                {
1407                    final double size = mainFrame.getMainRibbon().getROIRibbonTask().getRadius() * 2;
1408
1409                    // ROI point conversion
1410                    sequence.beginUpdate();
1411                    try
1412                    {
1413                        final List<ROI> selectedROIs = sequence.getSelectedROIs();
1414                        final List<ROI> removedROIs = new ArrayList<ROI>();
1415                        final List<ROI> addedROIs = new ArrayList<ROI>();
1416
1417                        for (ROI roi : selectedROIs)
1418                        {
1419                            final ROI resultRoi;
1420
1421                            if (size == 0)
1422                                resultRoi = ROIUtil.convertToPoint(roi);
1423                            else
1424                                resultRoi = ROIUtil.convertToRectangle(roi, size, size);
1425
1426                            if (resultRoi != null)
1427                            {
1428                                // select it by default
1429                                resultRoi.setSelected(true);
1430
1431                                sequence.removeROI(roi);
1432                                sequence.addROI(resultRoi);
1433
1434                                // add to undo manager
1435                                removedROIs.add(roi);
1436                                addedROIs.add(resultRoi);
1437                            }
1438                        }
1439
1440                        if (!addedROIs.isEmpty())
1441                            sequence.addUndoableEdit(new ROIReplacesSequenceEdit(sequence, removedROIs, addedROIs,
1442                                    (addedROIs.size() > 1) ? "ROIs square conversion" : "ROI square conversion"));
1443                    }
1444                    catch (UnsupportedOperationException ex)
1445                    {
1446                        MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1447                    }
1448                    finally
1449                    {
1450                        sequence.endUpdate();
1451                    }
1452                }
1453
1454                return true;
1455            }
1456
1457            return false;
1458        }
1459
1460        @Override
1461        public boolean isEnabled()
1462        {
1463            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1464        }
1465    };
1466
1467    public static IcyAbstractAction convertToShapeAction = new IcyAbstractAction("to Shape",
1468            new IcyIcon(ResourceUtil.ICON_ROI_POLYGON), "Convert Mask ROI to Polygon shape ROI",
1469            "Convert selected Mask ROI to Shape ROI using polygon approximation")
1470    {
1471        @Override
1472        public boolean doAction(ActionEvent e)
1473        {
1474            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1475
1476            if (sequence != null)
1477            {
1478                // ROI shape conversion
1479                sequence.beginUpdate();
1480                try
1481                {
1482                    final List<ROI> selectedROIs = sequence.getSelectedROIs();
1483                    final List<ROI> removedROIs = new ArrayList<ROI>();
1484                    final List<ROI> addedROIs = new ArrayList<ROI>();
1485
1486                    for (ROI roi : selectedROIs)
1487                    {
1488                        final ROI shapeRoi = ROIUtil.convertToShape(roi, -1);
1489
1490                        if (shapeRoi != null)
1491                        {
1492                            // select it by default
1493                            shapeRoi.setSelected(true);
1494
1495                            sequence.removeROI(roi);
1496                            sequence.addROI(shapeRoi);
1497
1498                            // add to undo manager
1499                            removedROIs.add(roi);
1500                            addedROIs.add(shapeRoi);
1501                        }
1502                    }
1503
1504                    if (!addedROIs.isEmpty())
1505                        sequence.addUndoableEdit(new ROIReplacesSequenceEdit(sequence, removedROIs, addedROIs,
1506                                (addedROIs.size() > 1) ? "ROIs shape conversion" : "ROI shape conversion"));
1507                }
1508                catch (UnsupportedOperationException ex)
1509                {
1510                    MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1511                }
1512                finally
1513                {
1514                    sequence.endUpdate();
1515                }
1516
1517                return true;
1518            }
1519
1520            return false;
1521        }
1522
1523        @Override
1524        public boolean isEnabled()
1525        {
1526            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1527        }
1528    };
1529
1530    public static IcyAbstractAction separateObjectsAction = new IcyAbstractAction("Separate",
1531            new IcyIcon(ResourceUtil.ICON_ROI_COMP), "Separate regions from selected Mask ROI(s)",
1532            "Separate unconnected regions from selected Mask ROI(s)")
1533    {
1534        @Override
1535        public boolean doAction(ActionEvent e)
1536        {
1537            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1538
1539            if (sequence != null)
1540            {
1541                sequence.beginUpdate();
1542                try
1543                {
1544                    final List<ROI> selectedROIs = sequence.getSelectedROIs();
1545                    final List<ROI> removedROIs = new ArrayList<ROI>();
1546                    final List<ROI> addedROIs = new ArrayList<ROI>();
1547
1548                    for (ROI roi : selectedROIs)
1549                    {
1550                        final List<ROI> components = ROIUtil.getConnectedComponents(roi);
1551
1552                        // nothing to do if we obtain only 1 component
1553                        if (components.size() > 1)
1554                        {
1555                            sequence.removeROI(roi);
1556                            removedROIs.add(roi);
1557
1558                            for (ROI component : components)
1559                            {
1560                                sequence.addROI(component);
1561                                // add to undo manager
1562                                addedROIs.add(component);
1563                            }
1564                        }
1565                    }
1566
1567                    if (!removedROIs.isEmpty())
1568                        sequence.addUndoableEdit(new ROIReplacesSequenceEdit(sequence, removedROIs, addedROIs,
1569                                (removedROIs.size() > 1) ? "ROIs separate objects" : "ROI separate objects"));
1570                }
1571                catch (UnsupportedOperationException ex)
1572                {
1573                    MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1574                }
1575                finally
1576                {
1577                    sequence.endUpdate();
1578                }
1579
1580                return true;
1581            }
1582
1583            return false;
1584        }
1585
1586        @Override
1587        public boolean isEnabled()
1588        {
1589            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1590        }
1591    };
1592
1593    public static IcyAbstractAction upscale2dAction = new IcyAbstractAction("Scale x2 (2D)",
1594            new IcyIcon(ResourceUtil.ICON_ROI_UPSCALE), "Create up scaled version of selected ROI(s) (2D)",
1595            "Create 2x factor up scaled version of selected ROI(s) (2D)")
1596    {
1597        @Override
1598        public boolean doAction(ActionEvent e)
1599        {
1600            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1601
1602            if (sequence != null)
1603            {
1604                sequence.beginUpdate();
1605                try
1606                {
1607                    final List<ROI> selectedROIs = sequence.getSelectedROIs();
1608                    final List<ROI> newROIs = new ArrayList<ROI>();
1609
1610                    for (ROI roi : selectedROIs)
1611                        newROIs.add(ROIUtil.getUpscaled(roi, false));
1612
1613                    if (!newROIs.isEmpty())
1614                    {
1615                        for (ROI roi : newROIs)
1616                            sequence.addROI(roi);
1617
1618                        sequence.addUndoableEdit(new ROIAddsSequenceEdit(sequence, newROIs,
1619                                (newROIs.size() > 1) ? "ROIs scale x2" : "ROI scale x2"));
1620                    }
1621                }
1622                catch (UnsupportedOperationException ex)
1623                {
1624                    MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1625                }
1626                finally
1627                {
1628                    sequence.endUpdate();
1629                }
1630
1631                return true;
1632            }
1633
1634            return false;
1635        }
1636
1637        @Override
1638        public boolean isEnabled()
1639        {
1640            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1641        }
1642    };
1643
1644    public static IcyAbstractAction upscaleAction = new IcyAbstractAction("Scale x2",
1645            new IcyIcon(ResourceUtil.ICON_ROI_UPSCALE), "Create x2 scaled version of selected ROI(s)",
1646            "Create x2 factor scaled version of selected ROI(s)")
1647    {
1648        @Override
1649        public boolean doAction(ActionEvent e)
1650        {
1651            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1652
1653            if (sequence != null)
1654            {
1655                sequence.beginUpdate();
1656                try
1657                {
1658                    final List<ROI> selectedROIs = sequence.getSelectedROIs();
1659                    final List<ROI> newROIs = new ArrayList<ROI>();
1660
1661                    for (ROI roi : selectedROIs)
1662                        newROIs.add(ROIUtil.getUpscaled(roi, true));
1663
1664                    if (!newROIs.isEmpty())
1665                    {
1666                        for (ROI roi : newROIs)
1667                            sequence.addROI(roi);
1668
1669                        sequence.addUndoableEdit(new ROIAddsSequenceEdit(sequence, newROIs,
1670                                (newROIs.size() > 1) ? "ROIs scale x2" : "ROI scale x2"));
1671                    }
1672                }
1673                catch (UnsupportedOperationException ex)
1674                {
1675                    MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1676                }
1677                finally
1678                {
1679                    sequence.endUpdate();
1680                }
1681
1682                return true;
1683            }
1684
1685            return false;
1686        }
1687
1688        @Override
1689        public boolean isEnabled()
1690        {
1691            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1692        }
1693    };
1694
1695    public static IcyAbstractAction downscale2dAction = new IcyAbstractAction("Scale /2 (2D)",
1696            new IcyIcon(ResourceUtil.ICON_ROI_DOWNSCALE), "Create /2 scaled version of selected ROI(s) (2D)",
1697            "Create /2 factor scaled version of selected ROI(s) (2D)")
1698    {
1699        @Override
1700        public boolean doAction(ActionEvent e)
1701        {
1702            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1703
1704            if (sequence != null)
1705            {
1706                sequence.beginUpdate();
1707                try
1708                {
1709                    final List<ROI> selectedROIs = sequence.getSelectedROIs();
1710                    final List<ROI> newROIs = new ArrayList<ROI>();
1711
1712                    for (ROI roi : newROIs)
1713                        newROIs.add(ROIUtil.getDownscaled(roi, false));
1714
1715                    if (!newROIs.isEmpty())
1716                    {
1717                        for (ROI roi : selectedROIs)
1718                            sequence.addROI(roi);
1719
1720                        sequence.addUndoableEdit(new ROIAddsSequenceEdit(sequence, newROIs,
1721                                (newROIs.size() > 1) ? "ROIs scale /2" : "ROI scale /2"));
1722                    }
1723                }
1724                catch (UnsupportedOperationException ex)
1725                {
1726                    MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1727                }
1728                finally
1729                {
1730                    sequence.endUpdate();
1731                }
1732
1733                return true;
1734            }
1735
1736            return false;
1737        }
1738
1739        @Override
1740        public boolean isEnabled()
1741        {
1742            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1743        }
1744    };
1745
1746    public static IcyAbstractAction downscaleAction = new IcyAbstractAction("Scale /2",
1747            new IcyIcon(ResourceUtil.ICON_ROI_DOWNSCALE), "Create down scaled version of selected ROI(s)",
1748            "Create 2x factor down scaled version of selected ROI(s)")
1749    {
1750        @Override
1751        public boolean doAction(ActionEvent e)
1752        {
1753            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1754
1755            if (sequence != null)
1756            {
1757                sequence.beginUpdate();
1758                try
1759                {
1760                    final List<ROI> selectedROIs = sequence.getSelectedROIs();
1761                    final List<ROI> newROIs = new ArrayList<ROI>();
1762
1763                    for (ROI roi : selectedROIs)
1764                        newROIs.add(ROIUtil.getDownscaled(roi, true));
1765
1766                    if (!newROIs.isEmpty())
1767                    {
1768                        for (ROI roi : newROIs)
1769                            sequence.addROI(roi);
1770
1771                        sequence.addUndoableEdit(new ROIAddsSequenceEdit(sequence, newROIs,
1772                                (newROIs.size() > 1) ? "ROIs scale /2" : "ROI scale /2"));
1773                    }
1774                }
1775                catch (UnsupportedOperationException ex)
1776                {
1777                    MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1778                }
1779                finally
1780                {
1781                    sequence.endUpdate();
1782                }
1783
1784                return true;
1785            }
1786
1787            return false;
1788        }
1789
1790        @Override
1791        public boolean isEnabled()
1792        {
1793            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1794        }
1795    };
1796
1797    public static IcyAbstractAction autoSplitAction = new IcyAbstractAction("Auto split",
1798            new IcyIcon("split_roi", true), "Automatic split selected ROI",
1799            "Automatic split selected ROI using shape and size information.")
1800    {
1801        @Override
1802        public boolean doAction(ActionEvent e)
1803        {
1804            final Sequence sequence = Icy.getMainInterface().getActiveSequence();
1805
1806            if (sequence != null)
1807            {
1808                sequence.beginUpdate();
1809                try
1810                {
1811                    final List<ROI2D> selectedROIs = sequence.getSelectedROI2Ds();
1812                    final List<ROI> removedROIs = new ArrayList<ROI>();
1813                    final List<ROI> addedROIs = new ArrayList<ROI>();
1814
1815                    for (ROI2D roi : selectedROIs)
1816                    {
1817                        // --> TODO
1818                        // final List<ROI> components = ROIUtil.split(roi);
1819                        //
1820                        // nothing to do if we obtain only 1 component
1821                        // if (components.size() > 1)
1822                        // {
1823                        // sequence.removeROI(roi);
1824                        // removedROIs.add(roi);
1825                        //
1826                        // for (ROI component : components)
1827                        // {
1828                        // sequence.addROI(component);
1829                        // // add to undo manager
1830                        // addedROIs.add(component);
1831                        // }
1832                        // }
1833                    }
1834
1835                    if (!removedROIs.isEmpty())
1836                        sequence.addUndoableEdit(new ROIReplacesSequenceEdit(sequence, removedROIs, addedROIs,
1837                                (removedROIs.size() > 1) ? "ROIs automatic split" : "ROI automatic split"));
1838                }
1839                catch (UnsupportedOperationException ex)
1840                {
1841                    MessageDialog.showDialog("Operation not supported", ex.toString(), MessageDialog.ERROR_MESSAGE);
1842                }
1843                finally
1844                {
1845                    sequence.endUpdate();
1846                }
1847
1848                return true;
1849            }
1850
1851            return false;
1852        }
1853
1854        @Override
1855        public boolean isEnabled()
1856        {
1857            return super.isEnabled() && (Icy.getMainInterface().getActiveSequence() != null);
1858        }
1859    };
1860
1861    /**
1862     * Return all actions of this class
1863     */
1864    public static List<IcyAbstractAction> getAllActions()
1865    {
1866        final List<IcyAbstractAction> result = new ArrayList<IcyAbstractAction>();
1867
1868        for (Field field : RoiActions.class.getFields())
1869        {
1870            final Class<?> type = field.getType();
1871
1872            try
1873            {
1874                if (ClassUtil.isSubClass(type, IcyAbstractAction[].class))
1875                    result.addAll(Arrays.asList(((IcyAbstractAction[]) field.get(null))));
1876                else if (ClassUtil.isSubClass(type, IcyAbstractAction.class))
1877                    result.add((IcyAbstractAction) field.get(null));
1878            }
1879            catch (Exception e)
1880            {
1881                // ignore
1882            }
1883        }
1884
1885        return result;
1886    }
1887}