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.canvas;
020
021import java.awt.BorderLayout;
022import java.awt.Component;
023import java.awt.Graphics2D;
024import java.awt.Point;
025import java.awt.event.KeyEvent;
026import java.awt.event.KeyListener;
027import java.awt.event.MouseEvent;
028import java.awt.event.MouseWheelEvent;
029import java.awt.image.BufferedImage;
030import java.lang.reflect.Constructor;
031import java.util.ArrayList;
032import java.util.Collections;
033import java.util.Comparator;
034import java.util.HashMap;
035import java.util.HashSet;
036import java.util.List;
037import java.util.Map;
038import java.util.Set;
039
040import javax.swing.JPanel;
041import javax.swing.JToolBar;
042import javax.swing.event.ChangeEvent;
043
044import icy.action.CanvasActions;
045import icy.action.GeneralActions;
046import icy.action.RoiActions;
047import icy.action.WindowActions;
048import icy.canvas.CanvasLayerEvent.LayersEventType;
049import icy.canvas.IcyCanvasEvent.IcyCanvasEventType;
050import icy.canvas.Layer.LayerListener;
051import icy.common.CollapsibleEvent;
052import icy.common.UpdateEventHandler;
053import icy.common.listener.ChangeListener;
054import icy.common.listener.ProgressListener;
055import icy.gui.util.GuiUtil;
056import icy.gui.viewer.MouseImageInfosPanel;
057import icy.gui.viewer.TNavigationPanel;
058import icy.gui.viewer.Viewer;
059import icy.gui.viewer.ViewerEvent;
060import icy.gui.viewer.ViewerListener;
061import icy.gui.viewer.ZNavigationPanel;
062import icy.image.IcyBufferedImage;
063import icy.image.colormodel.IcyColorModel;
064import icy.image.lut.LUT;
065import icy.image.lut.LUTEvent;
066import icy.image.lut.LUTEvent.LUTEventType;
067import icy.image.lut.LUTListener;
068import icy.main.Icy;
069import icy.painter.Overlay;
070import icy.painter.OverlayWrapper;
071import icy.painter.Painter;
072import icy.plugin.PluginDescriptor;
073import icy.plugin.PluginLoader;
074import icy.plugin.interface_.PluginCanvas;
075import icy.roi.ROI;
076import icy.sequence.DimensionId;
077import icy.sequence.Sequence;
078import icy.sequence.SequenceEvent;
079import icy.sequence.SequenceEvent.SequenceEventType;
080import icy.sequence.SequenceListener;
081import icy.system.IcyExceptionHandler;
082import icy.system.thread.ThreadUtil;
083import icy.type.point.Point5D;
084import icy.util.ClassUtil;
085import icy.util.EventUtil;
086import icy.util.OMEUtil;
087import plugins.kernel.canvas.Canvas2DPlugin;
088import plugins.kernel.canvas.VtkCanvasPlugin;
089
090/**
091 * @author Fabrice de Chaumont & Stephane Dallongeville<br>
092 *         <br>
093 *         An IcyCanvas is a basic Canvas used into the viewer. It contains a visual representation
094 *         of the sequence and provides some facilities as basic transformation and view
095 *         synchronization.<br>
096 *         Also IcyCanvas receives key events from Viewer when they are not consumed.<br>
097 *         <br>
098 *         By default transformations are applied in following order :<br>
099 *         Rotation, Translation then Scaling.<br>
100 *         The rotation transformation is relative to canvas center.<br>
101 *         <br>
102 *         Free feel to implement and override this design or not. <br>
103 *         <br>
104 *         (Canvas2D and Canvas3D derives from IcyCanvas)<br>
105 */
106public abstract class IcyCanvas extends JPanel
107        implements KeyListener, ViewerListener, SequenceListener, LUTListener, ChangeListener, LayerListener
108{
109    protected class IcyCanvasImageOverlay extends Overlay
110    {
111        public IcyCanvasImageOverlay()
112        {
113            super((getSequence() == null) ? "Image" : getSequence().getName(), OverlayPriority.IMAGE_NORMAL);
114
115            // we fix the image overlay
116            canBeRemoved = false;
117            readOnly = false;
118        }
119
120        @Override
121        public void paint(Graphics2D g, Sequence sequence, IcyCanvas canvas)
122        {
123            // default lazy implementation (very slow)
124            if (g != null)
125                g.drawImage(getCurrentImage(), null, 0, 0);
126        }
127    }
128
129    /**
130     * Returns all {@link PluginCanvas} plugins (kernel plugin are returned first).
131     */
132    public static List<PluginDescriptor> getCanvasPlugins()
133    {
134        // get all canvas plugins
135        final List<PluginDescriptor> result = PluginLoader.getPlugins(PluginCanvas.class);
136
137        // VTK is not loaded ?
138        if (!Icy.isVtkLibraryLoaded())
139        {
140            // remove VtkCanvas
141            final int ind = PluginDescriptor.getIndex(result, VtkCanvasPlugin.class.getName());
142            if (ind != -1)
143                result.remove(ind);
144        }
145
146        // sort plugins list
147        Collections.sort(result, new Comparator<PluginDescriptor>()
148        {
149            @Override
150            public int compare(PluginDescriptor o1, PluginDescriptor o2)
151            {
152                return Integer.valueOf(getOrder(o1)).compareTo(Integer.valueOf(getOrder(o2)));
153            }
154
155            int getOrder(PluginDescriptor p)
156            {
157                if (p.getClassName().equals(Canvas2DPlugin.class.getName()))
158                    return 0;
159                if (p.getClassName().equals(VtkCanvasPlugin.class.getName()))
160                    return 1;
161
162                return 10;
163            }
164        });
165
166        return result;
167    }
168
169    /**
170     * Returns all {@link PluginCanvas} plugins class name (kernel plugin are returned first).
171     */
172    public static List<String> getCanvasPluginNames()
173    {
174        // get all canvas plugins
175        final List<PluginDescriptor> plugins = getCanvasPlugins();
176        final List<String> result = new ArrayList<String>();
177
178        for (PluginDescriptor plugin : plugins)
179            result.add(plugin.getClassName());
180
181        return result;
182    }
183
184    /**
185     * Returns the plugin class name corresponding to the specified Canvas class name.<br>
186     * Returns <code>null</code> if we can't find a corresponding plugin.
187     */
188    public static String getPluginClassName(String canvasClassName)
189    {
190        for (PluginDescriptor plugin : IcyCanvas.getCanvasPlugins())
191        {
192            final String className = getCanvasClassName(plugin);
193
194            // we found the corresponding plugin
195            if (canvasClassName.equals(className))
196                // get plugin class name
197                return plugin.getClassName();
198        }
199
200        return null;
201    }
202
203    /**
204     * Returns the canvas class name corresponding to the specified {@link PluginCanvas} plugin.<br>
205     * Returns <code>null</code> if we can't retrieve the corresponding canvas class name.
206     */
207    public static String getCanvasClassName(PluginDescriptor plugin)
208    {
209        try
210        {
211            if (plugin != null)
212            {
213                final PluginCanvas pluginCanvas = (PluginCanvas) plugin.getPluginClass().newInstance();
214                // return canvas class name
215                return pluginCanvas.getCanvasClassName();
216            }
217        }
218        catch (Exception e)
219        {
220            IcyExceptionHandler.showErrorMessage(e, true);
221        }
222
223        return null;
224    }
225
226    /**
227     * Returns the canvas class name corresponding to the specified {@link PluginCanvas} class name. <br>
228     * Returns <code>null</code> if we can't find retrieve the corresponding canvas class name.
229     */
230    public static String getCanvasClassName(String pluginClassName)
231    {
232        return getCanvasClassName(PluginLoader.getPlugin(pluginClassName));
233    }
234
235    // /**
236    // * Return the class name of all {@link PluginCanvas}.
237    // */
238    // public static List<String> getCanvasPlugins()
239    // {
240    // // get all canvas plugins
241    // final List<PluginDescriptor> plugins = PluginLoader.getPlugins(PluginCanvas.class);
242    // final List<String> result = new ArrayList<String>();
243    //
244    // // we want the default 2D and 3D canvas to be first
245    // result.add(Canvas2DPlugin.class.getName());
246    // if (Icy.isVtkLibraryLoaded())
247    // result.add(VtkCanvasPlugin.class.getName());
248    //
249    // for (PluginDescriptor plugin : plugins)
250    // {
251    // final String className = plugin.getClassName();
252    //
253    // // ignore default canvas as they have been already added
254    // if (Canvas2DPlugin.class.getName().equals(className))
255    // continue;
256    // if (VtkCanvasPlugin.class.getName().equals(className))
257    // continue;
258    //
259    // CollectionUtil.addUniq(result, plugin.getClassName());
260    // }
261    //
262    // return result;
263    // }
264
265    /**
266     * Create a {@link IcyCanvas} object from its class name or {@link PluginCanvas} class name.<br>
267     * Throws an exception if an error occurred (canvas class was not found or it could not be
268     * creatd).
269     * 
270     * @param viewer
271     *        {@link Viewer} to which to canvas is attached.
272     * @throws ClassCastException
273     *         if the specified class name is not a canvas plugin or canvas class name
274     * @throws Exception
275     *         if the specified canvas cannot be created for some reasons
276     */
277    public static IcyCanvas create(String className, Viewer viewer) throws ClassCastException, Exception
278    {
279        // search for the specified className
280        final Class<?> clazz = ClassUtil.findClass(className);
281        final Class<? extends PluginCanvas> pluginCanvasClazz;
282
283        try
284        {
285            // we first check if we have a IcyCanvas Plugin class here
286            pluginCanvasClazz = clazz.asSubclass(PluginCanvas.class);
287        }
288        catch (ClassCastException e0)
289        {
290            // check if this is a IcyCanvas class
291            final Class<? extends IcyCanvas> canvasClazz = clazz.asSubclass(IcyCanvas.class);
292
293            // get constructor (Viewer)
294            final Constructor<? extends IcyCanvas> constructor = canvasClazz.getConstructor(new Class[] {Viewer.class});
295            // build canvas
296            return constructor.newInstance(new Object[] {viewer});
297        }
298
299        // create canvas from plugin
300        return pluginCanvasClazz.newInstance().createCanvas(viewer);
301    }
302
303    public static void addVisibleLayerToList(final Layer layer, ArrayList<Layer> list)
304    {
305        if ((layer != null) && (layer.isVisible()))
306            list.add(layer);
307    }
308
309    private static final long serialVersionUID = -8461229450296203011L;
310
311    public static final String PROPERTY_LAYERS_VISIBLE = "layersVisible";
312
313    /**
314     * Navigations bar
315     */
316    final protected ZNavigationPanel zNav;
317    final protected TNavigationPanel tNav;
318
319    /**
320     * The panel where mouse informations are displayed
321     */
322    protected final MouseImageInfosPanel mouseInfPanel;
323
324    /**
325     * The panel contains all settings and informations data such as<br>
326     * scale factor, rendering mode...
327     * Will be retrieved by the inspector to get information on the current canvas.
328     */
329    protected JPanel panel;
330
331    /**
332     * attached viewer
333     */
334    protected final Viewer viewer;
335    /**
336     * layers visible flag
337     */
338    protected boolean layersVisible;
339    /**
340     * synchronization group :<br>
341     * 0 = unsynchronized
342     * 1 = full synchronization group 1
343     * 2 = full synchronization group 2
344     * 3 = view synchronization group (T and Z navigation are not synchronized)
345     * 4 = slice synchronization group (only T and Z navigation are synchronized)
346     */
347    protected int syncId;
348
349    /**
350     * Overlay/Layer used to display sequence image
351     */
352    protected final Overlay imageOverlay;
353    protected final Layer imageLayer;
354
355    /**
356     * Layers attached to canvas<br>
357     * There are representing sequence overlays with some visualization properties
358     */
359    protected final Map<Overlay, Layer> layers;
360    /**
361     * Priority ordered layers.
362     */
363    protected List<Layer> orderedLayers;
364
365    /**
366     * internal updater
367     */
368    protected final UpdateEventHandler updater;
369    /**
370     * listeners
371     */
372    protected final List<IcyCanvasListener> listeners;
373    protected final List<CanvasLayerListener> layerListeners;
374
375    /**
376     * Current X position (should be -1 when canvas handle multi X dimension view).
377     */
378    protected int posX;
379    /**
380     * Current Y position (should be -1 when canvas handle multi Y dimension view).
381     */
382    protected int posY;
383    /**
384     * Current Z position (should be -1 when canvas handle multi Z dimension view).
385     */
386    protected int posZ;
387    /**
388     * Current T position (should be -1 when canvas handle multi T dimension view).
389     */
390    protected int posT;
391    /**
392     * Current C position (should be -1 when canvas handle multi C dimension view).
393     */
394    protected int posC;
395
396    /**
397     * Current mouse position (canvas coordinate space)
398     */
399    protected Point mousePos;
400
401    /**
402     * internals
403     */
404    protected LUT lut;
405    protected boolean synchMaster;
406    protected boolean orderedLayersOutdated;
407    private Runnable guiUpdater;
408
409    /**
410     * Constructor
411     * 
412     * @param viewer
413     */
414    public IcyCanvas(Viewer viewer)
415    {
416        super();
417
418        // default
419        this.viewer = viewer;
420
421        layersVisible = true;
422        layers = new HashMap<Overlay, Layer>();
423        orderedLayers = new ArrayList<Layer>();
424        syncId = 0;
425        synchMaster = false;
426        orderedLayersOutdated = false;
427        updater = new UpdateEventHandler(this, false);
428
429        // default position
430        mousePos = new Point(0, 0);
431        posX = -1;
432        posY = -1;
433        posZ = -1;
434        posT = -1;
435        posC = -1;
436
437        // GUI stuff
438        panel = new JPanel();
439
440        listeners = new ArrayList<IcyCanvasListener>();
441        layerListeners = new ArrayList<CanvasLayerListener>();
442
443        // Z navigation
444        zNav = new ZNavigationPanel();
445        zNav.addChangeListener(new javax.swing.event.ChangeListener()
446        {
447            @Override
448            public void stateChanged(ChangeEvent e)
449            {
450                // set the new Z position
451                setPositionZ(zNav.getValue());
452            }
453        });
454
455        // T navigation
456        tNav = new TNavigationPanel();
457        tNav.addChangeListener(new javax.swing.event.ChangeListener()
458        {
459            @Override
460            public void stateChanged(ChangeEvent e)
461            {
462                // set the new T position
463                setPositionT(tNav.getValue());
464            }
465        });
466
467        // mouse info panel
468        mouseInfPanel = new MouseImageInfosPanel();
469
470        // default canvas layout
471        setLayout(new BorderLayout());
472
473        add(zNav, BorderLayout.WEST);
474        add(GuiUtil.createPageBoxPanel(tNav, mouseInfPanel), BorderLayout.SOUTH);
475
476        // asynchronous updater for GUI
477        guiUpdater = new Runnable()
478        {
479            @Override
480            public void run()
481            {
482                ThreadUtil.invokeNow(new Runnable()
483                {
484                    @Override
485                    public void run()
486                    {
487                        // update sliders bounds if needed
488                        updateZNav();
489                        updateTNav();
490
491                        // adjust X position if needed
492                        final int maxX = getMaxPositionX();
493                        final int curX = getPositionX();
494                        if ((curX != -1) && (curX > maxX))
495                            setPositionX(maxX);
496
497                        // adjust Y position if needed
498                        final int maxY = getMaxPositionY();
499                        final int curY = getPositionY();
500                        if ((curY != -1) && (curY > maxY))
501                            setPositionY(maxY);
502
503                        // adjust C position if needed
504                        final int maxC = getMaxPositionC();
505                        final int curC = getPositionC();
506                        if ((curC != -1) && (curC > maxC))
507                            setPositionC(maxC);
508
509                        // adjust Z position if needed
510                        final int maxZ = getMaxPositionZ();
511                        final int curZ = getPositionZ();
512                        if ((curZ != -1) && (curZ > maxZ))
513                            setPositionZ(maxZ);
514
515                        // adjust T position if needed
516                        final int maxT = getMaxPositionT();
517                        final int curT = getPositionT();
518                        if ((curT != -1) && (curT > maxT))
519                            setPositionT(maxT);
520
521                        // refresh mouse panel informations (data values can have changed)
522                        mouseInfPanel.updateInfos(IcyCanvas.this);
523                    }
524                });
525            }
526        };
527
528        // create image overlay
529        imageOverlay = createImageOverlay();
530
531        // create layers from overlays
532        beginUpdate();
533        try
534        {
535            // first add image layer
536            imageLayer = addLayer(getImageOverlay());
537
538            final Sequence sequence = getSequence();
539
540            if (sequence != null)
541            {
542                // then add sequence overlays to layer list
543                for (Overlay overlay : sequence.getOverlays())
544                    addLayer(overlay);
545            }
546            else
547                System.err.println("Sequence null when canvas created");
548        }
549        finally
550        {
551            endUpdate();
552        }
553
554        // add listeners
555        viewer.addListener(this);
556        final Sequence seq = getSequence();
557        if (seq != null)
558            seq.addListener(this);
559
560        // set lut (no event wanted here)
561        lut = null;
562        setLut(viewer.getLut(), false);
563    }
564
565    /**
566     * Called by the viewer when canvas is closed to release some resources.<br/>
567     * Be careful to not restore previous state here (as the colormap) because generally <code>shutdown</code> is called
568     * <b>after</b> the creation of the other canvas.
569     */
570    public void shutDown()
571    {
572        // remove navigation panel listener
573        zNav.removeAllChangeListener();
574        tNav.removeAllChangeListener();
575
576        // remove listeners
577        if (lut != null)
578            lut.removeListener(this);
579        final Sequence seq = getSequence();
580        if (seq != null)
581            seq.removeListener(this);
582        viewer.removeListener(this);
583
584        // remove all layers
585        beginUpdate();
586        try
587        {
588            for (Layer layer : getLayers())
589                removeLayer(layer);
590        }
591        finally
592        {
593            endUpdate();
594        }
595
596        // release layers
597        synchronized (orderedLayers)
598        {
599            orderedLayers.clear();
600        }
601
602        // remove all IcyCanvas & Layer listeners
603        synchronized (listeners)
604        {
605            listeners.clear();
606        }
607        synchronized (layerListeners)
608        {
609            layerListeners.clear();
610        }
611    }
612
613    /**
614     * Force canvas refresh
615     */
616    public abstract void refresh();
617
618    protected Overlay createImageOverlay()
619    {
620        // default image overlay
621        return new IcyCanvasImageOverlay();
622    }
623
624    /**
625     * Returns the {@link Overlay} used to display the current sequence image
626     */
627    public Overlay getImageOverlay()
628    {
629        return imageOverlay;
630    }
631
632    /**
633     * Returns the {@link Layer} object used to display the current sequence image
634     */
635    public Layer getImageLayer()
636    {
637        return imageLayer;
638    }
639
640    /**
641     * @deprecated Use {@link #isLayersVisible()} instead.
642     */
643    @Deprecated
644    public boolean getDrawLayers()
645    {
646        return isLayersVisible();
647    }
648
649    /**
650     * @deprecated Use {@link #setLayersVisible(boolean)} instead.
651     */
652    @Deprecated
653    public void setDrawLayers(boolean value)
654    {
655        setLayersVisible(value);
656    }
657
658    /**
659     * Return true if layers are visible on the canvas.
660     */
661    public boolean isLayersVisible()
662    {
663        return layersVisible;
664    }
665
666    /**
667     * Make layers visible on this canvas (default = true).
668     */
669    public void setLayersVisible(boolean value)
670    {
671        if (layersVisible != value)
672        {
673            layersVisible = value;
674            layersVisibleChanged();
675            firePropertyChange(PROPERTY_LAYERS_VISIBLE, !value, value);
676        }
677    }
678
679    /**
680     * Global layers visibility changed
681     */
682    protected void layersVisibleChanged()
683    {
684        final Component comp = getViewComponent();
685
686        if (comp != null)
687            comp.repaint();
688    }
689
690    /**
691     * @return the viewer
692     */
693    public Viewer getViewer()
694    {
695        return viewer;
696    }
697
698    /**
699     * @return the sequence
700     */
701    public Sequence getSequence()
702    {
703        return viewer.getSequence();
704    }
705
706    /**
707     * @return the main view component
708     */
709    public abstract Component getViewComponent();
710
711    /**
712     * @return the Z navigation bar panel
713     */
714    public ZNavigationPanel getZNavigationPanel()
715    {
716        return zNav;
717    }
718
719    /**
720     * @return the T navigation bar panel
721     */
722    public TNavigationPanel getTNavigationPanel()
723    {
724        return tNav;
725    }
726
727    /**
728     * @return the mouse image informations panel
729     */
730    public MouseImageInfosPanel getMouseImageInfosPanel()
731    {
732        return mouseInfPanel;
733    }
734
735    /**
736     * @return the LUT
737     */
738    public LUT getLut()
739    {
740        // ensure we have the good lut
741        setLut(viewer.getLut(), true);
742
743        return lut;
744    }
745
746    /**
747     * set canvas LUT
748     */
749    private void setLut(LUT lut, boolean event)
750    {
751        if (this.lut != lut)
752        {
753            if (this.lut != null)
754                this.lut.removeListener(this);
755
756            this.lut = lut;
757
758            // add listener to the new lut
759            if (lut != null)
760                lut.addListener(this);
761
762            // launch a lutChanged event if wanted
763            if (event)
764                lutChanged(new LUTEvent(lut, -1, LUTEventType.COLORMAP_CHANGED));
765        }
766    }
767
768    /**
769     * @deprecated Use {@link #customizeToolbar(JToolBar)} instead.
770     */
771    @SuppressWarnings("unused")
772    @Deprecated
773    public void addViewerToolbarComponents(JToolBar toolBar)
774    {
775
776    }
777
778    /**
779     * Called by the parent viewer when building the toolbar.<br>
780     * This way the canvas can customize it by adding specific command for instance.<br>
781     * 
782     * @param toolBar
783     *        the parent toolbar to customize
784     */
785    public void customizeToolbar(JToolBar toolBar)
786    {
787        addViewerToolbarComponents(toolBar);
788    }
789
790    /**
791     * Returns the setting panel of this canvas.<br>
792     * The setting panel is displayed in the inspector so user can change canvas parameters.
793     */
794    public JPanel getPanel()
795    {
796        return panel;
797    }
798
799    /**
800     * Returns all layers attached to this canvas.<br/>
801     * 
802     * @param sorted
803     *        If <code>true</code> the returned list is sorted on the layer priority.<br>
804     *        Sort operation is cached so the method could take sometime when sort cache need to be
805     *        rebuild.
806     */
807    public List<Layer> getLayers(boolean sorted)
808    {
809        if (sorted)
810        {
811            // need to rebuild sorted layer list ?
812            if (orderedLayersOutdated)
813            {
814                // build and sort the list
815                synchronized (layers)
816                {
817                    orderedLayers = new ArrayList<Layer>(layers.values());
818                }
819
820                try
821                {
822                    Collections.sort(orderedLayers);
823                }
824                catch (Exception e)
825                {
826                    // catch exceptions here as some we can have "IllegalArgumentException: Comparison method violates
827                    // its general contract!"
828                }
829
830                orderedLayersOutdated = false;
831            }
832
833            return new ArrayList<Layer>(orderedLayers);
834        }
835
836        synchronized (layers)
837        {
838            return new ArrayList<Layer>(layers.values());
839        }
840    }
841
842    /**
843     * Returns all layers attached to this canvas.<br/>
844     * The returned list is sorted on the layer priority.<br>
845     * Sort operation is cached so the method could take sometime when cache need to be rebuild.
846     */
847    public List<Layer> getLayers()
848    {
849        return getLayers(true);
850    }
851
852    /**
853     * Returns all visible layers (visible property set to <code>true</code>) attached to this
854     * canvas.
855     * 
856     * @param sorted
857     *        If <code>true</code> the returned list is sorted on the layer priority.<br>
858     *        Sort operation is cached so the method could take sometime when sort cache need to be
859     *        rebuild.
860     */
861    public List<Layer> getVisibleLayers(boolean sorted)
862    {
863        final List<Layer> olayers = getLayers(sorted);
864        final List<Layer> result = new ArrayList<Layer>(olayers.size());
865
866        for (Layer l : olayers)
867            if (l.isVisible())
868                result.add(l);
869
870        return result;
871    }
872
873    /**
874     * Returns all visible layers (visible property set to <code>true</code>) attached to this
875     * canvas.<br/>
876     * The list is sorted on the layer priority.
877     */
878    public ArrayList<Layer> getVisibleLayers()
879    {
880        return (ArrayList<Layer>) getVisibleLayers(true);
881    }
882
883    /**
884     * @deprecated Use {@link #getLayers()} instead (sorted on Layer priority).
885     */
886    @Deprecated
887    public List<Layer> getOrderedLayersForEvent()
888    {
889        return getLayers();
890    }
891
892    /**
893     * @deprecated Use {@link #getVisibleLayers()} instead (sorted on Layer priority).
894     */
895    @Deprecated
896    public List<Layer> getVisibleOrderedLayersForEvent()
897    {
898        return getVisibleLayers();
899    }
900
901    /**
902     * @deprecated Use {@link #getOverlays()} instead.
903     */
904    @Deprecated
905    public List<Painter> getLayersPainter()
906    {
907        final ArrayList<Painter> result = new ArrayList<Painter>();
908
909        for (Overlay overlay : getOverlays())
910        {
911            if (overlay instanceof OverlayWrapper)
912                result.add(((OverlayWrapper) overlay).getPainter());
913            else
914                result.add(overlay);
915        }
916
917        return result;
918    }
919
920    /**
921     * Directly returns a {@link Set} of all Overlay displayed by this canvas.
922     */
923    public Set<Overlay> getOverlays()
924    {
925        synchronized (layers)
926        {
927            return new HashSet<Overlay>(layers.keySet());
928        }
929    }
930
931    /**
932     * @return the SyncId
933     */
934    public int getSyncId()
935    {
936        return syncId;
937    }
938
939    /**
940     * Set the synchronization group id (0 means unsynchronized).<br>
941     * 
942     * @return <code>false</code> if the canvas do not support synchronization group.
943     * @param id
944     *        the syncId to set
945     */
946    public boolean setSyncId(int id)
947    {
948        if (!isSynchronizationSupported())
949            return false;
950
951        if (this.syncId != id)
952        {
953            this.syncId = id;
954
955            // notify sync has changed
956            updater.changed(new IcyCanvasEvent(this, IcyCanvasEventType.SYNC_CHANGED));
957        }
958
959        return true;
960    }
961
962    /**
963     * Return true if this canvas support synchronization
964     */
965    public boolean isSynchronizationSupported()
966    {
967        // default (override it when supported)
968        return false;
969    }
970
971    /**
972     * Return true if this canvas is synchronized
973     */
974    public boolean isSynchronized()
975    {
976        return syncId > 0;
977    }
978
979    /**
980     * Return true if current canvas is synchronized and is currently the synchronize leader.
981     */
982    public boolean isSynchMaster()
983    {
984        return synchMaster;
985    }
986
987    /**
988     * @deprecated Use {@link #isSynchMaster()} instead.
989     */
990    @Deprecated
991    public boolean isSynchHeader()
992    {
993        return isSynchMaster();
994    }
995
996    /**
997     * Return true if current canvas is synchronized and it's not the synchronize master
998     */
999    public boolean isSynchSlave()
1000    {
1001        if (isSynchronized())
1002        {
1003            if (isSynchMaster())
1004                return false;
1005
1006            // search for a master in synchronized canvas
1007            for (IcyCanvas cnv : getSynchronizedCanvas())
1008                if (cnv.isSynchMaster())
1009                    return true;
1010        }
1011
1012        return false;
1013    }
1014
1015    /**
1016     * Return true if this canvas is synchronized on view (offset, zoom and rotation).
1017     */
1018    public boolean isSynchOnView()
1019    {
1020        return (syncId == 1) || (syncId == 2) || (syncId == 3);
1021    }
1022
1023    /**
1024     * Return true if this canvas is synchronized on slice (T and Z position)
1025     */
1026    public boolean isSynchOnSlice()
1027    {
1028        return (syncId == 1) || (syncId == 2) || (syncId == 4);
1029    }
1030
1031    /**
1032     * Return true if this canvas is synchronized on cursor (mouse cursor)
1033     */
1034    public boolean isSynchOnCursor()
1035    {
1036        return (syncId > 0);
1037    }
1038
1039    /**
1040     * Return true if we get the synchronizer master from synchronized canvas
1041     */
1042    protected boolean getSynchMaster()
1043    {
1044        return getSynchMaster(getSynchronizedCanvas());
1045    }
1046
1047    /**
1048     * @deprecated Use {@link #getSynchMaster()} instead.
1049     */
1050    @Deprecated
1051    protected boolean getSynchHeader()
1052    {
1053        return getSynchMaster();
1054    }
1055
1056    /**
1057     * Return true if we get the synchronizer master from specified canvas list.
1058     */
1059    protected boolean getSynchMaster(List<IcyCanvas> canvasList)
1060    {
1061        for (IcyCanvas canvas : canvasList)
1062            if (canvas.isSynchMaster())
1063                return canvas == this;
1064
1065        // no master found so we are master
1066        synchMaster = true;
1067
1068        return true;
1069    }
1070
1071    /**
1072     * @deprecated Use {@link #getSynchMaster(List)} instead.
1073     */
1074    @Deprecated
1075    protected boolean getSynchHeader(List<IcyCanvas> canvasList)
1076    {
1077        return getSynchMaster(canvasList);
1078    }
1079
1080    /**
1081     * Release synchronizer master
1082     */
1083    protected void releaseSynchMaster()
1084    {
1085        synchMaster = false;
1086    }
1087
1088    /**
1089     * @deprecated Use {@link #releaseSynchMaster()} instead.
1090     */
1091    @Deprecated
1092    protected void releaseSynchHeader()
1093    {
1094        releaseSynchMaster();
1095    }
1096
1097    /**
1098     * Return the list of canvas which are synchronized with the current one
1099     */
1100    private List<IcyCanvas> getSynchronizedCanvas()
1101    {
1102        final List<IcyCanvas> result = new ArrayList<IcyCanvas>();
1103
1104        if (isSynchronized())
1105        {
1106            final List<Viewer> viewers = Icy.getMainInterface().getViewers();
1107
1108            for (int i = viewers.size() - 1; i >= 0; i--)
1109            {
1110                final IcyCanvas cnv = viewers.get(i).getCanvas();
1111
1112                if ((cnv == this) || (cnv.getSyncId() != syncId))
1113                    viewers.remove(i);
1114            }
1115
1116            for (Viewer v : viewers)
1117            {
1118                final IcyCanvas cnv = v.getCanvas();
1119
1120                // only permit same class
1121                if (cnv.getClass().isInstance(this))
1122                    result.add(cnv);
1123            }
1124        }
1125
1126        return result;
1127    }
1128
1129    /**
1130     * Synchronize views of specified list of canvas
1131     */
1132    protected void synchronizeCanvas(List<IcyCanvas> canvasList, IcyCanvasEvent event, boolean processAll)
1133    {
1134        final IcyCanvasEventType type = event.getType();
1135        final DimensionId dim = event.getDim();
1136
1137        // position synchronization
1138        if (isSynchOnSlice())
1139        {
1140            if (processAll || (type == IcyCanvasEventType.POSITION_CHANGED))
1141            {
1142                // no information about dimension --> set all
1143                if (processAll || (dim == DimensionId.NULL))
1144                {
1145                    final int x = getPositionX();
1146                    final int y = getPositionY();
1147                    final int z = getPositionZ();
1148                    final int t = getPositionT();
1149                    final int c = getPositionC();
1150
1151                    for (IcyCanvas cnv : canvasList)
1152                    {
1153                        if (x != -1)
1154                            cnv.setPositionX(x);
1155                        if (y != -1)
1156                            cnv.setPositionY(y);
1157                        if (z != -1)
1158                            cnv.setPositionZ(z);
1159                        if (t != -1)
1160                            cnv.setPositionT(t);
1161                        if (c != -1)
1162                            cnv.setPositionC(c);
1163                    }
1164                }
1165                else
1166                {
1167                    for (IcyCanvas cnv : canvasList)
1168                    {
1169                        final int pos = getPosition(dim);
1170                        if (pos != -1)
1171                            cnv.setPosition(dim, pos);
1172                    }
1173                }
1174            }
1175        }
1176
1177        // view synchronization
1178        if (isSynchOnView())
1179        {
1180            if (processAll || (type == IcyCanvasEventType.SCALE_CHANGED))
1181            {
1182                // no information about dimension --> set all
1183                if (processAll || (dim == DimensionId.NULL))
1184                {
1185                    final double sX = getScaleX();
1186                    final double sY = getScaleY();
1187                    final double sZ = getScaleZ();
1188                    final double sT = getScaleT();
1189                    final double sC = getScaleC();
1190
1191                    for (IcyCanvas cnv : canvasList)
1192                    {
1193                        cnv.setScaleX(sX);
1194                        cnv.setScaleY(sY);
1195                        cnv.setScaleZ(sZ);
1196                        cnv.setScaleT(sT);
1197                        cnv.setScaleC(sC);
1198                    }
1199                }
1200                else
1201                {
1202                    for (IcyCanvas cnv : canvasList)
1203                        cnv.setScale(dim, getScale(dim));
1204                }
1205            }
1206
1207            if (processAll || (type == IcyCanvasEventType.ROTATION_CHANGED))
1208            {
1209                // no information about dimension --> set all
1210                if (processAll || (dim == DimensionId.NULL))
1211                {
1212                    final double rotX = getRotationX();
1213                    final double rotY = getRotationY();
1214                    final double rotZ = getRotationZ();
1215                    final double rotT = getRotationT();
1216                    final double rotC = getRotationC();
1217
1218                    for (IcyCanvas cnv : canvasList)
1219                    {
1220                        cnv.setRotationX(rotX);
1221                        cnv.setRotationY(rotY);
1222                        cnv.setRotationZ(rotZ);
1223                        cnv.setRotationT(rotT);
1224                        cnv.setRotationC(rotC);
1225                    }
1226                }
1227                else
1228                {
1229                    for (IcyCanvas cnv : canvasList)
1230                        cnv.setRotation(dim, getRotation(dim));
1231                }
1232            }
1233
1234            // process offset in last as it can be limited depending destination scale value
1235            if (processAll || (type == IcyCanvasEventType.OFFSET_CHANGED))
1236            {
1237                // no information about dimension --> set all
1238                if (processAll || (dim == DimensionId.NULL))
1239                {
1240                    final int offX = getOffsetX();
1241                    final int offY = getOffsetY();
1242                    final int offZ = getOffsetZ();
1243                    final int offT = getOffsetT();
1244                    final int offC = getOffsetC();
1245
1246                    for (IcyCanvas cnv : canvasList)
1247                    {
1248                        cnv.setOffsetX(offX);
1249                        cnv.setOffsetY(offY);
1250                        cnv.setOffsetZ(offZ);
1251                        cnv.setOffsetT(offT);
1252                        cnv.setOffsetC(offC);
1253                    }
1254                }
1255                else
1256                {
1257                    for (IcyCanvas cnv : canvasList)
1258                        cnv.setOffset(dim, getOffset(dim));
1259                }
1260            }
1261        }
1262
1263        // cursor synchronization
1264        if (isSynchOnCursor())
1265        {
1266            // mouse synchronization
1267            if (processAll || (type == IcyCanvasEventType.MOUSE_IMAGE_POSITION_CHANGED))
1268            {
1269                // no information about dimension --> set all
1270                if (processAll || (dim == DimensionId.NULL))
1271                {
1272                    final double mipX = getMouseImagePosX();
1273                    final double mipY = getMouseImagePosY();
1274                    final double mipZ = getMouseImagePosZ();
1275                    final double mipT = getMouseImagePosT();
1276                    final double mipC = getMouseImagePosC();
1277
1278                    for (IcyCanvas cnv : canvasList)
1279                    {
1280                        cnv.setMouseImagePosX(mipX);
1281                        cnv.setMouseImagePosY(mipY);
1282                        cnv.setMouseImagePosZ(mipZ);
1283                        cnv.setMouseImagePosT(mipT);
1284                        cnv.setMouseImagePosC(mipC);
1285                    }
1286                }
1287                else
1288                {
1289                    for (IcyCanvas cnv : canvasList)
1290                        cnv.setMouseImagePos(dim, getMouseImagePos(dim));
1291                }
1292            }
1293        }
1294    }
1295
1296    /**
1297     * Get position for specified dimension
1298     */
1299    public int getPosition(DimensionId dim)
1300    {
1301        switch (dim)
1302        {
1303            case X:
1304                return getPositionX();
1305            case Y:
1306                return getPositionY();
1307            case Z:
1308                return getPositionZ();
1309            case T:
1310                return getPositionT();
1311            case C:
1312                return getPositionC();
1313        }
1314
1315        return 0;
1316    }
1317
1318    /**
1319     * @return current X (-1 if all selected)
1320     */
1321    public int getPositionX()
1322    {
1323        return -1;
1324    }
1325
1326    /**
1327     * @return current Y (-1 if all selected)
1328     */
1329    public int getPositionY()
1330    {
1331        return -1;
1332    }
1333
1334    /**
1335     * @return current Z (-1 if all selected)
1336     */
1337    public int getPositionZ()
1338    {
1339        return posZ;
1340    }
1341
1342    /**
1343     * @return current T (-1 if all selected)
1344     */
1345    public int getPositionT()
1346    {
1347        return posT;
1348    }
1349
1350    /**
1351     * @return current C (-1 if all selected)
1352     */
1353    public int getPositionC()
1354    {
1355        return posC;
1356    }
1357
1358    /**
1359     * Returns the 5D canvas position (-1 mean that the complete dimension is selected)
1360     */
1361    public Point5D.Integer getPosition5D()
1362    {
1363        return new Point5D.Integer(getPositionX(), getPositionY(), getPositionZ(), getPositionT(), getPositionC());
1364    }
1365
1366    /**
1367     * @return current Z (-1 if all selected)
1368     * @deprecated uses getPositionZ() instead
1369     */
1370    @Deprecated
1371    public int getZ()
1372    {
1373        return getPositionZ();
1374    }
1375
1376    /**
1377     * @return current T (-1 if all selected)
1378     * @deprecated uses getPositionT() instead
1379     */
1380    @Deprecated
1381    public int getT()
1382    {
1383        return getPositionT();
1384    }
1385
1386    /**
1387     * @return current C (-1 if all selected)
1388     * @deprecated uses getPositionC() instead
1389     */
1390    @Deprecated
1391    public int getC()
1392    {
1393        return getPositionC();
1394    }
1395
1396    /**
1397     * Get maximum position for specified dimension
1398     */
1399    public double getMaxPosition(DimensionId dim)
1400    {
1401        switch (dim)
1402        {
1403            case X:
1404                return getMaxPositionX();
1405            case Y:
1406                return getMaxPositionY();
1407            case Z:
1408                return getMaxPositionZ();
1409            case T:
1410                return getMaxPositionT();
1411            case C:
1412                return getMaxPositionC();
1413        }
1414
1415        return 0;
1416    }
1417
1418    /**
1419     * Get maximum X value
1420     */
1421    public int getMaxPositionX()
1422    {
1423        final Sequence sequence = getSequence();
1424
1425        // have to test this as we release sequence reference on closed
1426        if (sequence == null)
1427            return 0;
1428
1429        return Math.max(0, getImageSizeX() - 1);
1430    }
1431
1432    /**
1433     * Get maximum Y value
1434     */
1435    public int getMaxPositionY()
1436    {
1437        final Sequence sequence = getSequence();
1438
1439        // have to test this as we release sequence reference on closed
1440        if (sequence == null)
1441            return 0;
1442
1443        return Math.max(0, getImageSizeY() - 1);
1444    }
1445
1446    /**
1447     * Get maximum Z value
1448     */
1449    public int getMaxPositionZ()
1450    {
1451        final Sequence sequence = getSequence();
1452
1453        // have to test this as we release sequence reference on closed
1454        if (sequence == null)
1455            return 0;
1456
1457        return Math.max(0, getImageSizeZ() - 1);
1458    }
1459
1460    /**
1461     * Get maximum T value
1462     */
1463    public int getMaxPositionT()
1464    {
1465        final Sequence sequence = getSequence();
1466
1467        // have to test this as we release sequence reference on closed
1468        if (sequence == null)
1469            return 0;
1470
1471        return Math.max(0, getImageSizeT() - 1);
1472    }
1473
1474    /**
1475     * Get maximum C value
1476     */
1477    public int getMaxPositionC()
1478    {
1479        final Sequence sequence = getSequence();
1480
1481        // have to test this as we release sequence reference on closed
1482        if (sequence == null)
1483            return 0;
1484
1485        return Math.max(0, getImageSizeC() - 1);
1486    }
1487
1488    /**
1489     * Get the maximum 5D position for the canvas.
1490     * 
1491     * @see #getPosition5D()
1492     */
1493    public Point5D.Integer getMaxPosition5D()
1494    {
1495        return new Point5D.Integer(getMaxPositionX(), getMaxPositionY(), getMaxPositionZ(), getMaxPositionT(),
1496                getMaxPositionC());
1497    }
1498
1499    /**
1500     * @deprecated Use {@link #getMaxPosition(DimensionId)} instead
1501     */
1502    @Deprecated
1503    public double getMax(DimensionId dim)
1504    {
1505        return getMaxPosition(dim);
1506    }
1507
1508    /**
1509     * @deprecated Use {@link #getMaxPositionX()} instead
1510     */
1511    @Deprecated
1512    public int getMaxX()
1513    {
1514        return getMaxPositionX();
1515    }
1516
1517    /**
1518     * @deprecated Use {@link #getMaxPositionY()} instead
1519     */
1520    @Deprecated
1521    public int getMaxY()
1522    {
1523        return getMaxPositionY();
1524    }
1525
1526    /**
1527     * @deprecated Use {@link #getMaxPositionZ()} instead
1528     */
1529    @Deprecated
1530    public int getMaxZ()
1531    {
1532        return getMaxPositionZ();
1533    }
1534
1535    /**
1536     * @deprecated Use {@link #getMaxPositionT()} instead
1537     */
1538    @Deprecated
1539    public int getMaxT()
1540    {
1541        return getMaxPositionT();
1542    }
1543
1544    /**
1545     * @deprecated Use {@link #getMaxPositionC()} instead
1546     */
1547    @Deprecated
1548    public int getMaxC()
1549    {
1550        return getMaxPositionC();
1551    }
1552
1553    /**
1554     * Get canvas view size for specified Dimension
1555     */
1556    public int getCanvasSize(DimensionId dim)
1557    {
1558        switch (dim)
1559        {
1560            case X:
1561                return getCanvasSizeX();
1562            case Y:
1563                return getCanvasSizeY();
1564            case Z:
1565                return getCanvasSizeZ();
1566            case T:
1567                return getCanvasSizeT();
1568            case C:
1569                return getCanvasSizeC();
1570        }
1571
1572        // size not supported
1573        return -1;
1574    }
1575
1576    /**
1577     * Returns the canvas view size X.
1578     */
1579    public int getCanvasSizeX()
1580    {
1581        final Component comp = getViewComponent();
1582        int res = 0;
1583
1584        if (comp != null)
1585        {
1586            // by default we use view component width
1587            res = comp.getWidth();
1588            // preferred width if size not yet set
1589            if (res == 0)
1590                res = comp.getPreferredSize().width;
1591        }
1592
1593        return res;
1594    }
1595
1596    /**
1597     * Returns the canvas view size Y.
1598     */
1599    public int getCanvasSizeY()
1600    {
1601        final Component comp = getViewComponent();
1602        int res = 0;
1603
1604        if (comp != null)
1605        {
1606            // by default we use view component width
1607            res = comp.getHeight();
1608            // preferred width if size not yet set
1609            if (res == 0)
1610                res = comp.getPreferredSize().height;
1611        }
1612
1613        return res;
1614    }
1615
1616    /**
1617     * Returns the canvas view size Z.
1618     */
1619    public int getCanvasSizeZ()
1620    {
1621        // by default : no Z dimension
1622        return 1;
1623    }
1624
1625    /**
1626     * Returns the canvas view size T.
1627     */
1628    public int getCanvasSizeT()
1629    {
1630        // by default : no T dimension
1631        return 1;
1632    }
1633
1634    /**
1635     * Returns the canvas view size C.
1636     */
1637    public int getCanvasSizeC()
1638    {
1639        // by default : no C dimension
1640        return 1;
1641    }
1642
1643    /**
1644     * Returns the mouse position (in canvas coordinate space).
1645     */
1646    public Point getMousePos()
1647    {
1648        return (Point) mousePos.clone();
1649    }
1650
1651    /**
1652     * Get mouse image position for specified Dimension
1653     */
1654    public double getMouseImagePos(DimensionId dim)
1655    {
1656        switch (dim)
1657        {
1658            case X:
1659                return getMouseImagePosX();
1660            case Y:
1661                return getMouseImagePosY();
1662            case Z:
1663                return getMouseImagePosZ();
1664            case T:
1665                return getMouseImagePosT();
1666            case C:
1667                return getMouseImagePosC();
1668        }
1669
1670        return 0;
1671    }
1672
1673    /**
1674     * mouse X image position
1675     */
1676    public double getMouseImagePosX()
1677    {
1678        // default implementation
1679        return getPositionX();
1680    }
1681
1682    /**
1683     * mouse Y image position
1684     */
1685    public double getMouseImagePosY()
1686    {
1687        // default implementation
1688        return getPositionY();
1689    }
1690
1691    /**
1692     * mouse Z image position
1693     */
1694    public double getMouseImagePosZ()
1695    {
1696        // default implementation
1697        return getPositionZ();
1698    }
1699
1700    /**
1701     * mouse T image position
1702     */
1703    public double getMouseImagePosT()
1704    {
1705        // default implementation
1706        return getPositionT();
1707    }
1708
1709    /**
1710     * mouse C image position
1711     */
1712    public double getMouseImagePosC()
1713    {
1714        // default implementation
1715        return getPositionC();
1716    }
1717
1718    /**
1719     * Returns the 5D mouse image position
1720     */
1721    public Point5D.Double getMouseImagePos5D()
1722    {
1723        return new Point5D.Double(getMouseImagePosX(), getMouseImagePosY(), getMouseImagePosZ(), getMouseImagePosT(),
1724                getMouseImagePosC());
1725    }
1726
1727    /**
1728     * Get offset for specified Dimension
1729     */
1730    public int getOffset(DimensionId dim)
1731    {
1732        switch (dim)
1733        {
1734            case X:
1735                return getOffsetX();
1736            case Y:
1737                return getOffsetY();
1738            case Z:
1739                return getOffsetZ();
1740            case T:
1741                return getOffsetT();
1742            case C:
1743                return getOffsetC();
1744        }
1745
1746        return 0;
1747    }
1748
1749    /**
1750     * X offset
1751     */
1752    public int getOffsetX()
1753    {
1754        return 0;
1755    }
1756
1757    /**
1758     * Y offset
1759     */
1760    public int getOffsetY()
1761    {
1762        return 0;
1763    }
1764
1765    /**
1766     * Z offset
1767     */
1768    public int getOffsetZ()
1769    {
1770        return 0;
1771    }
1772
1773    /**
1774     * T offset
1775     */
1776    public int getOffsetT()
1777    {
1778        return 0;
1779    }
1780
1781    /**
1782     * C offset
1783     */
1784    public int getOffsetC()
1785    {
1786        return 0;
1787    }
1788
1789    /**
1790     * Returns the 5D offset.
1791     */
1792    public Point5D.Integer getOffset5D()
1793    {
1794        return new Point5D.Integer(getOffsetX(), getOffsetY(), getOffsetZ(), getOffsetT(), getOffsetC());
1795    }
1796
1797    /**
1798     * X image offset
1799     * 
1800     * @deprecated use getOffsetX() instead
1801     */
1802    @Deprecated
1803    public int getImageOffsetX()
1804    {
1805        return 0;
1806    }
1807
1808    /**
1809     * Y image offset
1810     * 
1811     * @deprecated use getOffsetY() instead
1812     */
1813    @Deprecated
1814    public int getImageOffsetY()
1815    {
1816        return 0;
1817    }
1818
1819    /**
1820     * Z image offset
1821     * 
1822     * @deprecated use getOffsetZ() instead
1823     */
1824    @Deprecated
1825    public int getImageOffsetZ()
1826    {
1827        return 0;
1828    }
1829
1830    /**
1831     * T image offset
1832     * 
1833     * @deprecated use getOffsetT() instead
1834     */
1835    @Deprecated
1836    public int getImageOffsetT()
1837    {
1838        return 0;
1839    }
1840
1841    /**
1842     * C image offset
1843     * 
1844     * @deprecated use getOffsetC() instead
1845     */
1846    @Deprecated
1847    public int getImageOffsetC()
1848    {
1849        return 0;
1850    }
1851
1852    /**
1853     * X canvas offset
1854     * 
1855     * @deprecated use getOffsetX() instead
1856     */
1857    @Deprecated
1858    public int getCanvasOffsetX()
1859    {
1860        return 0;
1861    }
1862
1863    /**
1864     * Y canvas offset
1865     * 
1866     * @deprecated use getOffsetY() instead
1867     */
1868    @Deprecated
1869    public int getCanvasOffsetY()
1870    {
1871        return 0;
1872    }
1873
1874    /**
1875     * Z canvas offset
1876     * 
1877     * @deprecated use getOffsetZ() instead
1878     */
1879    @Deprecated
1880    public int getCanvasOffsetZ()
1881    {
1882        return 0;
1883    }
1884
1885    /**
1886     * T canvas offset
1887     * 
1888     * @deprecated use getOffsetT() instead
1889     */
1890    @Deprecated
1891    public int getCanvasOffsetT()
1892    {
1893        return 0;
1894    }
1895
1896    /**
1897     * C canvas offset
1898     * 
1899     * @deprecated use getOffsetC() instead
1900     */
1901    @Deprecated
1902    public int getCanvasOffsetC()
1903    {
1904        return 0;
1905    }
1906
1907    /**
1908     * X scale factor
1909     * 
1910     * @deprecated use getScaleX() instead
1911     */
1912    @Deprecated
1913    public double getScaleFactorX()
1914    {
1915        return getScaleX();
1916    }
1917
1918    /**
1919     * Y scale factor
1920     * 
1921     * @deprecated use getScaleY() instead
1922     */
1923    @Deprecated
1924    public double getScaleFactorY()
1925    {
1926        return getScaleY();
1927    }
1928
1929    /**
1930     * Z scale factor
1931     * 
1932     * @deprecated use getScaleZ() instead
1933     */
1934    @Deprecated
1935    public double getScaleFactorZ()
1936    {
1937        return getScaleZ();
1938    }
1939
1940    /**
1941     * T scale factor
1942     * 
1943     * @deprecated use getScaleT() instead
1944     */
1945    @Deprecated
1946    public double getScaleFactorT()
1947    {
1948        return getScaleT();
1949    }
1950
1951    /**
1952     * C scale factor
1953     * 
1954     * @deprecated use getScaleC() instead
1955     */
1956    @Deprecated
1957    public double getScaleFactorC()
1958    {
1959        return getScaleC();
1960    }
1961
1962    /**
1963     * Get scale factor for specified Dimension
1964     */
1965    public double getScale(DimensionId dim)
1966    {
1967        switch (dim)
1968        {
1969            case X:
1970                return getScaleX();
1971            case Y:
1972                return getScaleY();
1973            case Z:
1974                return getScaleZ();
1975            case T:
1976                return getScaleT();
1977            case C:
1978                return getScaleC();
1979        }
1980
1981        return 1d;
1982    }
1983
1984    /**
1985     * X scale factor
1986     */
1987    public double getScaleX()
1988    {
1989        return 1d;
1990    }
1991
1992    /**
1993     * Y scale factor
1994     */
1995    public double getScaleY()
1996    {
1997        return 1d;
1998    }
1999
2000    /**
2001     * Z scale factor
2002     */
2003    public double getScaleZ()
2004    {
2005        return 1d;
2006    }
2007
2008    /**
2009     * T scale factor
2010     */
2011    public double getScaleT()
2012    {
2013        return 1d;
2014    }
2015
2016    /**
2017     * C scale factor
2018     */
2019    public double getScaleC()
2020    {
2021        return 1d;
2022    }
2023
2024    /**
2025     * Get rotation angle (radian) for specified Dimension
2026     */
2027    public double getRotation(DimensionId dim)
2028    {
2029        switch (dim)
2030        {
2031            case X:
2032                return getRotationX();
2033            case Y:
2034                return getRotationY();
2035            case Z:
2036                return getRotationZ();
2037            case T:
2038                return getRotationT();
2039            case C:
2040                return getRotationC();
2041        }
2042
2043        return 1d;
2044    }
2045
2046    /**
2047     * X rotation angle (radian)
2048     */
2049    public double getRotationX()
2050    {
2051        return 0d;
2052    }
2053
2054    /**
2055     * Y rotation angle (radian)
2056     */
2057    public double getRotationY()
2058    {
2059        return 0d;
2060    }
2061
2062    /**
2063     * Z rotation angle (radian)
2064     */
2065    public double getRotationZ()
2066    {
2067        return 0d;
2068    }
2069
2070    /**
2071     * T rotation angle (radian)
2072     */
2073    public double getRotationT()
2074    {
2075        return 0d;
2076    }
2077
2078    /**
2079     * C rotation angle (radian)
2080     */
2081    public double getRotationC()
2082    {
2083        return 0d;
2084    }
2085
2086    /**
2087     * Get image size for specified Dimension
2088     */
2089    public int getImageSize(DimensionId dim)
2090    {
2091        switch (dim)
2092        {
2093            case X:
2094                return getImageSizeX();
2095            case Y:
2096                return getImageSizeY();
2097            case Z:
2098                return getImageSizeZ();
2099            case T:
2100                return getImageSizeT();
2101            case C:
2102                return getImageSizeC();
2103        }
2104
2105        return 0;
2106    }
2107
2108    /**
2109     * Get image size X
2110     */
2111    public int getImageSizeX()
2112    {
2113        final Sequence seq = getSequence();
2114
2115        if (seq != null)
2116            return seq.getSizeX();
2117
2118        return 0;
2119    }
2120
2121    /**
2122     * Get image size Y
2123     */
2124    public int getImageSizeY()
2125    {
2126        final Sequence seq = getSequence();
2127
2128        if (seq != null)
2129            return seq.getSizeY();
2130
2131        return 0;
2132    }
2133
2134    /**
2135     * Get image size Z
2136     */
2137    public int getImageSizeZ()
2138    {
2139        final Sequence seq = getSequence();
2140
2141        if (seq != null)
2142            return seq.getSizeZ();
2143
2144        return 0;
2145    }
2146
2147    /**
2148     * Get image size T
2149     */
2150    public int getImageSizeT()
2151    {
2152        final Sequence seq = getSequence();
2153
2154        if (seq != null)
2155            return seq.getSizeT();
2156
2157        return 0;
2158    }
2159
2160    /**
2161     * Get image size C
2162     */
2163    public int getImageSizeC()
2164    {
2165        final Sequence seq = getSequence();
2166
2167        if (seq != null)
2168            return seq.getSizeC();
2169
2170        return 0;
2171    }
2172
2173    /**
2174     * Get image size in canvas pixel coordinate for specified Dimension
2175     * 
2176     * @deprecated doesn't take rotation transformation in account.<br>
2177     *             Use IcyCanvasXD.getImageCanvasSize(..) instead
2178     */
2179    @Deprecated
2180    public int getImageCanvasSize(DimensionId dim)
2181    {
2182        switch (dim)
2183        {
2184            case X:
2185                return getImageCanvasSizeX();
2186            case Y:
2187                return getImageCanvasSizeY();
2188            case Z:
2189                return getImageCanvasSizeZ();
2190            case T:
2191                return getImageCanvasSizeT();
2192            case C:
2193                return getImageCanvasSizeC();
2194        }
2195
2196        return 0;
2197    }
2198
2199    /**
2200     * Get image size X in canvas pixel coordinate
2201     * 
2202     * @deprecated doesn't take rotation transformation in account.<br>
2203     *             Use IcyCanvasXD.getImageCanvasSize(..) instead
2204     */
2205    @Deprecated
2206    public int getImageCanvasSizeX()
2207    {
2208        return imageToCanvasDeltaX(getImageSizeX());
2209    }
2210
2211    /**
2212     * Get image size Y in canvas pixel coordinate
2213     * 
2214     * @deprecated doesn't take rotation transformation in account.<br>
2215     *             Use IcyCanvasXD.getImageCanvasSize(..) instead
2216     */
2217    @Deprecated
2218    public int getImageCanvasSizeY()
2219    {
2220        return imageToCanvasDeltaY(getImageSizeY());
2221    }
2222
2223    /**
2224     * Get image size Z in canvas pixel coordinate
2225     * 
2226     * @deprecated doesn't take rotation transformation in account.<br>
2227     *             Use IcyCanvasXD.getImageCanvasSize(..) instead
2228     */
2229    @Deprecated
2230    public int getImageCanvasSizeZ()
2231    {
2232        return imageToCanvasDeltaZ(getImageSizeZ());
2233    }
2234
2235    /**
2236     * Get image size T in canvas pixel coordinate
2237     * 
2238     * @deprecated doesn't take rotation transformation in account.<br>
2239     *             Use IcyCanvasXD.getImageCanvasSize(..) instead
2240     */
2241    @Deprecated
2242    public int getImageCanvasSizeT()
2243    {
2244        return imageToCanvasDeltaT(getImageSizeT());
2245    }
2246
2247    /**
2248     * Get image size C in canvas pixel coordinate
2249     * 
2250     * @deprecated doesn't take rotation transformation in account.<br>
2251     *             Use IcyCanvasXD.getImageCanvasSize(..) instead
2252     */
2253    @Deprecated
2254    public int getImageCanvasSizeC()
2255    {
2256        return imageToCanvasDeltaC(getImageSizeC());
2257    }
2258
2259    /**
2260     * Set position for specified dimension
2261     */
2262    public void setPosition(DimensionId dim, int value)
2263    {
2264        switch (dim)
2265        {
2266            case X:
2267                setPositionX(value);
2268                break;
2269            case Y:
2270                setPositionY(value);
2271                break;
2272            case Z:
2273                setPositionZ(value);
2274                break;
2275            case T:
2276                setPositionT(value);
2277                break;
2278            case C:
2279                setPositionC(value);
2280                break;
2281        }
2282    }
2283
2284    /**
2285     * Set Z position
2286     * 
2287     * @deprecated uses setPositionZ(int) instead
2288     */
2289    @Deprecated
2290    public void setZ(int z)
2291    {
2292        setPositionZ(z);
2293    }
2294
2295    /**
2296     * Set T position
2297     * 
2298     * @deprecated uses setPositionT(int) instead
2299     */
2300    @Deprecated
2301    public void setT(int t)
2302    {
2303        setPositionT(t);
2304    }
2305
2306    /**
2307     * Set C position
2308     * 
2309     * @deprecated uses setPositionC(int) instead
2310     */
2311    @Deprecated
2312    public void setC(int c)
2313    {
2314        setPositionC(c);
2315    }
2316
2317    /**
2318     * Set X position
2319     */
2320    public void setPositionX(int x)
2321    {
2322        final int adjX = Math.max(-1, Math.min(x, getMaxPositionX()));
2323
2324        if (getPositionX() != adjX)
2325            setPositionXInternal(adjX);
2326    }
2327
2328    /**
2329     * Set Y position
2330     */
2331    public void setPositionY(int y)
2332    {
2333        final int adjY = Math.max(-1, Math.min(y, getMaxPositionY()));
2334
2335        if (getPositionY() != adjY)
2336            setPositionYInternal(adjY);
2337    }
2338
2339    /**
2340     * Set Z position
2341     */
2342    public void setPositionZ(int z)
2343    {
2344        final int adjZ = Math.max(-1, Math.min(z, getMaxPositionZ()));
2345
2346        if (getPositionZ() != adjZ)
2347            setPositionZInternal(adjZ);
2348    }
2349
2350    /**
2351     * Set T position
2352     */
2353    public void setPositionT(int t)
2354    {
2355        final int adjT = Math.max(-1, Math.min(t, getMaxPositionT()));
2356
2357        if (getPositionT() != adjT)
2358            setPositionTInternal(adjT);
2359    }
2360
2361    /**
2362     * Set C position
2363     */
2364    public void setPositionC(int c)
2365    {
2366        final int adjC = Math.max(-1, Math.min(c, getMaxPositionC()));
2367
2368        if (getPositionC() != adjC)
2369            setPositionCInternal(adjC);
2370    }
2371
2372    /**
2373     * Set X position internal
2374     */
2375    protected void setPositionXInternal(int x)
2376    {
2377        posX = x;
2378        // common process on position change
2379        positionChanged(DimensionId.X);
2380    }
2381
2382    /**
2383     * Set Y position internal
2384     */
2385    protected void setPositionYInternal(int y)
2386    {
2387        posY = y;
2388        // common process on position change
2389        positionChanged(DimensionId.Y);
2390    }
2391
2392    /**
2393     * Set Z position internal
2394     */
2395    protected void setPositionZInternal(int z)
2396    {
2397        posZ = z;
2398        // common process on position change
2399        positionChanged(DimensionId.Z);
2400    }
2401
2402    /**
2403     * Set T position internal
2404     */
2405    protected void setPositionTInternal(int t)
2406    {
2407        posT = t;
2408        // common process on position change
2409        positionChanged(DimensionId.T);
2410    }
2411
2412    /**
2413     * Set C position internal
2414     */
2415    protected void setPositionCInternal(int c)
2416    {
2417        posC = c;
2418        // common process on position change
2419        positionChanged(DimensionId.C);
2420    }
2421
2422    /**
2423     * Set mouse position (in canvas coordinate space).<br>
2424     * The method returns <code>true</code> if the mouse position actually changed.
2425     */
2426    public boolean setMousePos(int x, int y)
2427    {
2428        if ((mousePos.x != x) || (mousePos.y != y))
2429        {
2430            mousePos.x = x;
2431            mousePos.y = y;
2432
2433            // mouse image position probably changed so this method should be overridden
2434            // to implement the correct calculation for the mouse image position change
2435
2436            return true;
2437        }
2438
2439        return false;
2440    }
2441
2442    /**
2443     * Set mouse position (in canvas coordinate space)
2444     */
2445    public void setMousePos(Point point)
2446    {
2447        setMousePos(point.x, point.y);
2448    }
2449
2450    /**
2451     * Set mouse image position for specified dimension (required for synchronization)
2452     */
2453    public void setMouseImagePos(DimensionId dim, double value)
2454    {
2455        switch (dim)
2456        {
2457            case X:
2458                setMouseImagePosX(value);
2459                break;
2460            case Y:
2461                setMouseImagePosY(value);
2462                break;
2463            case Z:
2464                setMouseImagePosZ(value);
2465                break;
2466            case T:
2467                setMouseImagePosT(value);
2468                break;
2469            case C:
2470                setMouseImagePosC(value);
2471                break;
2472        }
2473    }
2474
2475    /**
2476     * Set mouse X image position
2477     */
2478    public void setMouseImagePosX(double value)
2479    {
2480        if (getMouseImagePosX() != value)
2481            // internal set
2482            setMouseImagePosXInternal(value);
2483    }
2484
2485    /**
2486     * Set mouse Y image position
2487     */
2488    public void setMouseImagePosY(double value)
2489    {
2490        if (getMouseImagePosY() != value)
2491            // internal set
2492            setMouseImagePosYInternal(value);
2493    }
2494
2495    /**
2496     * Set mouse Z image position
2497     */
2498    public void setMouseImagePosZ(double value)
2499    {
2500        if (getMouseImagePosZ() != value)
2501            // internal set
2502            setMouseImagePosZInternal(value);
2503    }
2504
2505    /**
2506     * Set mouse T image position
2507     */
2508    public void setMouseImagePosT(double value)
2509    {
2510        if (getMouseImagePosT() != value)
2511            // internal set
2512            setMouseImagePosTInternal(value);
2513    }
2514
2515    /**
2516     * Set mouse C image position
2517     */
2518    public void setMouseImagePosC(double value)
2519    {
2520        if (getMouseImagePosC() != value)
2521            // internal set
2522            setMouseImagePosCInternal(value);
2523    }
2524
2525    /**
2526     * Set offset X internal
2527     */
2528    protected void setMouseImagePosXInternal(double value)
2529    {
2530        // notify change
2531        mouseImagePositionChanged(DimensionId.X);
2532    }
2533
2534    /**
2535     * Set offset Y internal
2536     */
2537    protected void setMouseImagePosYInternal(double value)
2538    {
2539        // notify change
2540        mouseImagePositionChanged(DimensionId.Y);
2541    }
2542
2543    /**
2544     * Set offset Z internal
2545     */
2546    protected void setMouseImagePosZInternal(double value)
2547    {
2548        // notify change
2549        mouseImagePositionChanged(DimensionId.Z);
2550    }
2551
2552    /**
2553     * Set offset T internal
2554     */
2555    protected void setMouseImagePosTInternal(double value)
2556    {
2557        // notify change
2558        mouseImagePositionChanged(DimensionId.T);
2559    }
2560
2561    /**
2562     * Set offset C internal
2563     */
2564    protected void setMouseImagePosCInternal(double value)
2565    {
2566        // notify change
2567        mouseImagePositionChanged(DimensionId.C);
2568    }
2569
2570    /**
2571     * Set offset for specified dimension
2572     */
2573    public void setOffset(DimensionId dim, int value)
2574    {
2575        switch (dim)
2576        {
2577            case X:
2578                setOffsetX(value);
2579                break;
2580            case Y:
2581                setOffsetY(value);
2582                break;
2583            case Z:
2584                setOffsetZ(value);
2585                break;
2586            case T:
2587                setOffsetT(value);
2588                break;
2589            case C:
2590                setOffsetC(value);
2591                break;
2592        }
2593    }
2594
2595    /**
2596     * Set offset X
2597     */
2598    public void setOffsetX(int value)
2599    {
2600        if (getOffsetX() != value)
2601            // internal set
2602            setOffsetXInternal(value);
2603    }
2604
2605    /**
2606     * Set offset Y
2607     */
2608    public void setOffsetY(int value)
2609    {
2610        if (getOffsetY() != value)
2611            // internal set
2612            setOffsetYInternal(value);
2613    }
2614
2615    /**
2616     * Set offset Z
2617     */
2618    public void setOffsetZ(int value)
2619    {
2620        if (getOffsetZ() != value)
2621            // internal set
2622            setOffsetZInternal(value);
2623    }
2624
2625    /**
2626     * Set offset T
2627     */
2628    public void setOffsetT(int value)
2629    {
2630        if (getOffsetT() != value)
2631            // internal set
2632            setOffsetTInternal(value);
2633    }
2634
2635    /**
2636     * Set offset C
2637     */
2638    public void setOffsetC(int value)
2639    {
2640        if (getOffsetC() != value)
2641            // internal set
2642            setOffsetCInternal(value);
2643    }
2644
2645    /**
2646     * Set offset X internal
2647     */
2648    protected void setOffsetXInternal(int value)
2649    {
2650        // notify change
2651        offsetChanged(DimensionId.X);
2652    }
2653
2654    /**
2655     * Set offset Y internal
2656     */
2657    protected void setOffsetYInternal(int value)
2658    {
2659        // notify change
2660        offsetChanged(DimensionId.Y);
2661    }
2662
2663    /**
2664     * Set offset Z internal
2665     */
2666    protected void setOffsetZInternal(int value)
2667    {
2668        // notify change
2669        offsetChanged(DimensionId.Z);
2670    }
2671
2672    /**
2673     * Set offset T internal
2674     */
2675    protected void setOffsetTInternal(int value)
2676    {
2677        // notify change
2678        offsetChanged(DimensionId.T);
2679    }
2680
2681    /**
2682     * Set offset C internal
2683     */
2684    protected void setOffsetCInternal(int value)
2685    {
2686        // notify change
2687        offsetChanged(DimensionId.C);
2688    }
2689
2690    /**
2691     * Set scale factor for specified dimension
2692     */
2693    public void setScale(DimensionId dim, double value)
2694    {
2695        switch (dim)
2696        {
2697            case X:
2698                setScaleX(value);
2699                break;
2700            case Y:
2701                setScaleY(value);
2702                break;
2703            case Z:
2704                setScaleZ(value);
2705                break;
2706            case T:
2707                setScaleT(value);
2708                break;
2709            case C:
2710                setScaleC(value);
2711                break;
2712        }
2713    }
2714
2715    /**
2716     * Set scale factor X
2717     */
2718    public void setScaleX(double value)
2719    {
2720        if (getScaleX() != value)
2721            // internal set
2722            setScaleXInternal(value);
2723    }
2724
2725    /**
2726     * Set scale factor Y
2727     */
2728    public void setScaleY(double value)
2729    {
2730        if (getScaleY() != value)
2731            // internal set
2732            setScaleYInternal(value);
2733    }
2734
2735    /**
2736     * Set scale factor Z
2737     */
2738    public void setScaleZ(double value)
2739    {
2740        if (getScaleZ() != value)
2741            // internal set
2742            setScaleZInternal(value);
2743    }
2744
2745    /**
2746     * Set scale factor T
2747     */
2748    public void setScaleT(double value)
2749    {
2750        if (getScaleT() != value)
2751            // internal set
2752            setScaleTInternal(value);
2753    }
2754
2755    /**
2756     * Set scale factor C
2757     */
2758    public void setScaleC(double value)
2759    {
2760        if (getScaleC() != value)
2761            // internal set
2762            setScaleCInternal(value);
2763    }
2764
2765    /**
2766     * Set scale factor X internal
2767     */
2768    protected void setScaleXInternal(double value)
2769    {
2770        // notify change
2771        scaleChanged(DimensionId.X);
2772    }
2773
2774    /**
2775     * Set scale factor Y internal
2776     */
2777    protected void setScaleYInternal(double value)
2778    {
2779        // notify change
2780        scaleChanged(DimensionId.Y);
2781    }
2782
2783    /**
2784     * Set scale factor Z internal
2785     */
2786    protected void setScaleZInternal(double value)
2787    {
2788        // notify change
2789        scaleChanged(DimensionId.Z);
2790    }
2791
2792    /**
2793     * Set scale factor T internal
2794     */
2795    protected void setScaleTInternal(double value)
2796    {
2797        // notify change
2798        scaleChanged(DimensionId.T);
2799    }
2800
2801    /**
2802     * Set scale factor C internal
2803     */
2804    protected void setScaleCInternal(double value)
2805    {
2806        // notify change
2807        scaleChanged(DimensionId.C);
2808    }
2809
2810    /**
2811     * Set rotation angle (radian) for specified dimension
2812     */
2813    public void setRotation(DimensionId dim, double value)
2814    {
2815        switch (dim)
2816        {
2817            case X:
2818                setRotationX(value);
2819                break;
2820            case Y:
2821                setRotationY(value);
2822                break;
2823            case Z:
2824                setRotationZ(value);
2825                break;
2826            case T:
2827                setRotationT(value);
2828                break;
2829            case C:
2830                setRotationC(value);
2831                break;
2832        }
2833    }
2834
2835    /**
2836     * Set X rotation angle (radian)
2837     */
2838    public void setRotationX(double value)
2839    {
2840        if (getRotationX() != value)
2841            // internal set
2842            setRotationXInternal(value);
2843    }
2844
2845    /**
2846     * Set Y rotation angle (radian)
2847     */
2848    public void setRotationY(double value)
2849    {
2850        if (getRotationY() != value)
2851            // internal set
2852            setRotationYInternal(value);
2853    }
2854
2855    /**
2856     * Set Z rotation angle (radian)
2857     */
2858    public void setRotationZ(double value)
2859    {
2860        if (getRotationZ() != value)
2861            // internal set
2862            setRotationZInternal(value);
2863    }
2864
2865    /**
2866     * Set T rotation angle (radian)
2867     */
2868    public void setRotationT(double value)
2869    {
2870        if (getRotationT() != value)
2871            // internal set
2872            setRotationTInternal(value);
2873    }
2874
2875    /**
2876     * Set C rotation angle (radian)
2877     */
2878    public void setRotationC(double value)
2879    {
2880        if (getRotationC() != value)
2881            // internal set
2882            setRotationCInternal(value);
2883    }
2884
2885    /**
2886     * Set X rotation angle internal
2887     */
2888    protected void setRotationXInternal(double value)
2889    {
2890        // notify change
2891        rotationChanged(DimensionId.X);
2892    }
2893
2894    /**
2895     * Set Y rotation angle internal
2896     */
2897    protected void setRotationYInternal(double value)
2898    {
2899        // notify change
2900        rotationChanged(DimensionId.Y);
2901    }
2902
2903    /**
2904     * Set Z rotation angle internal
2905     */
2906    protected void setRotationZInternal(double value)
2907    {
2908        // notify change
2909        rotationChanged(DimensionId.Z);
2910    }
2911
2912    /**
2913     * Set T rotation angle internal
2914     */
2915    protected void setRotationTInternal(double value)
2916    {
2917        // notify change
2918        rotationChanged(DimensionId.T);
2919    }
2920
2921    /**
2922     * Set C rotation angle internal
2923     */
2924    protected void setRotationCInternal(double value)
2925    {
2926        // notify change
2927        rotationChanged(DimensionId.C);
2928    }
2929
2930    /**
2931     * Called when mouse image position changed
2932     */
2933    public void mouseImagePositionChanged(DimensionId dim)
2934    {
2935        // handle with updater
2936        updater.changed(new IcyCanvasEvent(this, IcyCanvasEventType.MOUSE_IMAGE_POSITION_CHANGED, dim));
2937    }
2938
2939    /**
2940     * Called when canvas offset changed
2941     */
2942    public void offsetChanged(DimensionId dim)
2943    {
2944        // handle with updater
2945        updater.changed(new IcyCanvasEvent(this, IcyCanvasEventType.OFFSET_CHANGED, dim));
2946    }
2947
2948    /**
2949     * Called when scale factor changed
2950     */
2951    public void scaleChanged(DimensionId dim)
2952    {
2953        // handle with updater
2954        updater.changed(new IcyCanvasEvent(this, IcyCanvasEventType.SCALE_CHANGED, dim));
2955    }
2956
2957    /**
2958     * Called when rotation angle changed
2959     */
2960    public void rotationChanged(DimensionId dim)
2961    {
2962        // handle with updater
2963        updater.changed(new IcyCanvasEvent(this, IcyCanvasEventType.ROTATION_CHANGED, dim));
2964    }
2965
2966    /**
2967     * Convert specified canvas delta X to image delta X.<br>
2968     * WARNING: Does not take in account the rotation transformation.<br>
2969     * Use the IcyCanvasXD.canvasToImageDelta(...) method instead for rotation transformed delta.
2970     */
2971    public double canvasToImageDeltaX(int value)
2972    {
2973        return value / getScaleX();
2974    }
2975
2976    /**
2977     * Convert specified canvas delta Y to image delta Y.<br>
2978     * WARNING: Does not take in account the rotation transformation.<br>
2979     * Use the IcyCanvasXD.canvasToImageDelta(...) method instead for rotation transformed delta.
2980     */
2981    public double canvasToImageDeltaY(int value)
2982    {
2983        return value / getScaleY();
2984    }
2985
2986    /**
2987     * Convert specified canvas delta Z to image delta Z.<br>
2988     * WARNING: Does not take in account the rotation transformation.<br>
2989     * Use the IcyCanvasXD.canvasToImageDelta(...) method instead for rotation transformed delta.
2990     */
2991    public double canvasToImageDeltaZ(int value)
2992    {
2993        return value / getScaleZ();
2994    }
2995
2996    /**
2997     * Convert specified canvas delta T to image delta T.<br>
2998     * WARNING: Does not take in account the rotation transformation.<br>
2999     * Use the IcyCanvasXD.canvasToImageDelta(...) method instead for rotation transformed delta.
3000     */
3001    public double canvasToImageDeltaT(int value)
3002    {
3003        return value / getScaleT();
3004    }
3005
3006    /**
3007     * Convert specified canvas delta C to image delta C.<br>
3008     * WARNING: Does not take in account the rotation transformation.<br>
3009     * Use the IcyCanvasXD.canvasToImageDelta(...) method instead for rotation transformed delta.
3010     */
3011    public double canvasToImageDeltaC(int value)
3012    {
3013        return value / getScaleC();
3014    }
3015
3016    /**
3017     * Convert specified canvas delta X to log image delta X.<br>
3018     * The conversion is still affected by zoom ratio but with specified logarithm form.<br>
3019     * WARNING: Does not take in account the rotation transformation.<br>
3020     * Use the IcyCanvasXD.canvasToImageLogDelta(...) method instead for rotation transformed delta.
3021     */
3022    public double canvasToImageLogDeltaX(int value, double logFactor)
3023    {
3024        final double scaleFactor = getScaleX();
3025        // keep the zoom ratio but in a log perspective
3026        return value / (scaleFactor / Math.pow(10, Math.log10(scaleFactor) / logFactor));
3027    }
3028
3029    /**
3030     * Convert specified canvas delta X to log image delta X.<br>
3031     * The conversion is still affected by zoom ratio but with logarithm form.<br>
3032     * WARNING: Does not take in account the rotation transformation.<br>
3033     * Use the IcyCanvasXD.canvasToImageLogDelta(...) method instead for rotation transformed delta.
3034     */
3035    public double canvasToImageLogDeltaX(int value)
3036    {
3037        return canvasToImageLogDeltaX(value, 5d);
3038    }
3039
3040    /**
3041     * Convert specified canvas delta Y to log image delta Y.<br>
3042     * The conversion is still affected by zoom ratio but with specified logarithm form.<br>
3043     * WARNING: Does not take in account the rotation transformation.<br>
3044     * Use the IcyCanvasXD.canvasToImageLogDelta(...) method instead for rotation transformed delta.
3045     */
3046    public double canvasToImageLogDeltaY(int value, double logFactor)
3047    {
3048        final double scaleFactor = getScaleY();
3049        // keep the zoom ratio but in a log perspective
3050        return value / (scaleFactor / Math.pow(10, Math.log10(scaleFactor) / logFactor));
3051    }
3052
3053    /**
3054     * Convert specified canvas delta Y to log image delta Y.<br>
3055     * The conversion is still affected by zoom ratio but with logarithm form.<br>
3056     * WARNING: Does not take in account the rotation transformation.<br>
3057     * Use the IcyCanvasXD.canvasToImageLogDelta(...) method instead for rotation transformed delta.
3058     */
3059    public double canvasToImageLogDeltaY(int value)
3060    {
3061        return canvasToImageLogDeltaY(value, 5d);
3062    }
3063
3064    /**
3065     * Convert specified canvas delta Z to log image delta Z.<br>
3066     * The conversion is still affected by zoom ratio but with specified logarithm form.<br>
3067     * WARNING: Does not take in account the rotation transformation.<br>
3068     * Use the IcyCanvasXD.canvasToImageLogDelta(...) method instead for rotation transformed delta.
3069     */
3070    public double canvasToImageLogDeltaZ(int value, double logFactor)
3071    {
3072        final double scaleFactor = getScaleZ();
3073        // keep the zoom ratio but in a log perspective
3074        return value / (scaleFactor / Math.pow(10, Math.log10(scaleFactor) / logFactor));
3075    }
3076
3077    /**
3078     * Convert specified canvas delta Z to log image delta Z.<br>
3079     * The conversion is still affected by zoom ratio but with logarithm form.<br>
3080     * WARNING: Does not take in account the rotation transformation.<br>
3081     * Use the IcyCanvasXD.canvasToImageLogDelta(...) method instead for rotation transformed delta.
3082     */
3083    public double canvasToImageLogDeltaZ(int value)
3084    {
3085        return canvasToImageLogDeltaZ(value, 5d);
3086    }
3087
3088    /**
3089     * Convert specified canvas X coordinate to image X coordinate
3090     * 
3091     * @deprecated Cannot give correct result if rotation is applied so use
3092     *             IcyCanvasXD.canvasToImage(...) instead
3093     */
3094    @Deprecated
3095    public double canvasToImageX(int value)
3096    {
3097        return canvasToImageDeltaX(value - getOffsetX());
3098    }
3099
3100    /**
3101     * Convert specified canvas Y coordinate to image Y coordinate
3102     * 
3103     * @deprecated Cannot give correct result if rotation is applied so use
3104     *             IcyCanvasXD.canvasToImage(...) instead
3105     */
3106    @Deprecated
3107    public double canvasToImageY(int value)
3108    {
3109        return canvasToImageDeltaY(value - getOffsetY());
3110    }
3111
3112    /**
3113     * Convert specified canvas Z coordinate to image Z coordinate
3114     * 
3115     * @deprecated Cannot give correct result if rotation is applied so use
3116     *             IcyCanvasXD.canvasToImage(...) instead
3117     */
3118    @Deprecated
3119    public double canvasToImageZ(int value)
3120    {
3121        return canvasToImageDeltaZ(value - getOffsetZ());
3122    }
3123
3124    /**
3125     * Convert specified canvas T coordinate to image T coordinate
3126     * 
3127     * @deprecated Cannot give correct result if rotation is applied so use
3128     *             IcyCanvasXD.canvasToImage(...) instead
3129     */
3130    @Deprecated
3131    public double canvasToImageT(int value)
3132    {
3133        return canvasToImageDeltaT(value - getOffsetT());
3134    }
3135
3136    /**
3137     * Convert specified canvas C coordinate to image C coordinate
3138     * 
3139     * @deprecated Cannot give correct result if rotation is applied so use
3140     *             IcyCanvasXD.canvasToImage(...) instead
3141     */
3142    @Deprecated
3143    public double canvasToImageC(int value)
3144    {
3145        return canvasToImageDeltaC(value - getOffsetC());
3146    }
3147
3148    /**
3149     * Convert specified image delta X to canvas delta X.<br>
3150     * WARNING: Does not take in account the rotation transformation.<br>
3151     * Use the IcyCanvasXD.imageToCanvasDelta(...) method instead for rotation transformed delta.
3152     */
3153    public int imageToCanvasDeltaX(double value)
3154    {
3155        return (int) (value * getScaleX());
3156    }
3157
3158    /**
3159     * Convert specified image delta Y to canvas delta Y.<br>
3160     * WARNING: Does not take in account the rotation transformation.<br>
3161     * Use the IcyCanvasXD.imageToCanvasDelta(...) method instead for rotation transformed delta.
3162     */
3163    public int imageToCanvasDeltaY(double value)
3164    {
3165        return (int) (value * getScaleY());
3166    }
3167
3168    /**
3169     * Convert specified image delta Z to canvas delta Z.<br>
3170     * WARNING: Does not take in account the rotation transformation.<br>
3171     * Use the IcyCanvasXD.imageToCanvasDelta(...) method instead for rotation transformed delta.
3172     */
3173    public int imageToCanvasDeltaZ(double value)
3174    {
3175        return (int) (value * getScaleZ());
3176    }
3177
3178    /**
3179     * Convert specified image delta T to canvas delta T.<br>
3180     * WARNING: Does not take in account the rotation transformation.<br>
3181     * Use the IcyCanvasXD.imageToCanvasDelta(...) method instead for rotation transformed delta.
3182     */
3183    public int imageToCanvasDeltaT(double value)
3184    {
3185        return (int) (value * getScaleT());
3186    }
3187
3188    /**
3189     * Convert specified image delta C to canvas delta C.<br>
3190     * WARNING: Does not take in account the rotation transformation.<br>
3191     * Use the IcyCanvasXD.imageToCanvasDelta(...) method instead for rotation transformed delta.
3192     */
3193    public int imageToCanvasDeltaC(double value)
3194    {
3195        return (int) (value * getScaleC());
3196    }
3197
3198    /**
3199     * Convert specified image X coordinate to canvas X coordinate
3200     * 
3201     * @deprecated Cannot give correct result if rotation is applied so use
3202     *             IcyCanvasXD.imageToCanvas(...) instead
3203     */
3204    @Deprecated
3205    public int imageToCanvasX(double value)
3206    {
3207        return imageToCanvasDeltaX(value) + getOffsetX();
3208    }
3209
3210    /**
3211     * Convert specified image Y coordinate to canvas Y coordinate
3212     * 
3213     * @deprecated Cannot give correct result if rotation is applied so use
3214     *             IcyCanvasXD.imageToCanvas(...) instead
3215     */
3216    @Deprecated
3217    public int imageToCanvasY(double value)
3218    {
3219        return imageToCanvasDeltaY(value) + getOffsetY();
3220    }
3221
3222    /**
3223     * Convert specified image Z coordinate to canvas Z coordinate
3224     * 
3225     * @deprecated Cannot give correct result if rotation is applied so use
3226     *             IcyCanvasXD.imageToCanvas(...) instead
3227     */
3228    @Deprecated
3229    public int imageToCanvasZ(double value)
3230    {
3231        return imageToCanvasDeltaZ(value) + getOffsetZ();
3232    }
3233
3234    /**
3235     * Convert specified image T coordinate to canvas T coordinate
3236     * 
3237     * @deprecated Cannot give correct result if rotation is applied so use
3238     *             IcyCanvasXD.imageToCanvas(...) instead
3239     */
3240    @Deprecated
3241    public int imageToCanvasT(double value)
3242    {
3243        return imageToCanvasDeltaT(value) + getOffsetT();
3244    }
3245
3246    /**
3247     * Convert specified image C coordinate to canvas C coordinate
3248     * 
3249     * @deprecated Cannot give correct result if rotation is applied so use
3250     *             IcyCanvasXD.imageToCanvas(...) instead
3251     */
3252    @Deprecated
3253    public int imageToCanvasC(double value)
3254    {
3255        return imageToCanvasDeltaC(value) + getOffsetC();
3256    }
3257
3258    /**
3259     * Helper to forward mouse press event to the overlays.
3260     * 
3261     * @param event
3262     *        original mouse event
3263     * @param pt
3264     *        mouse image position
3265     */
3266    public void mousePressed(MouseEvent event, Point5D.Double pt)
3267    {
3268        final boolean globalVisible = isLayersVisible();
3269
3270        // send mouse event to overlays after so mouse canvas position is ok
3271        for (Layer layer : getLayers(true))
3272        {
3273            if ((globalVisible && layer.isVisible()) || layer.getReceiveMouseEventOnHidden())
3274                layer.getOverlay().mousePressed(event, pt, this);
3275        }
3276    }
3277
3278    /**
3279     * Helper to forward mouse press event to the overlays.
3280     * 
3281     * @param event
3282     *        original mouse event
3283     */
3284    public void mousePressed(MouseEvent event)
3285    {
3286        mousePressed(event, getMouseImagePos5D());
3287    }
3288
3289    /**
3290     * Helper to forward mouse release event to the overlays.
3291     * 
3292     * @param event
3293     *        original mouse event
3294     * @param pt
3295     *        mouse image position
3296     */
3297    public void mouseReleased(MouseEvent event, Point5D.Double pt)
3298    {
3299        final boolean globalVisible = isLayersVisible();
3300
3301        // send mouse event to overlays after so mouse canvas position is ok
3302        for (Layer layer : getLayers(true))
3303        {
3304            if ((globalVisible && layer.isVisible()) || layer.getReceiveMouseEventOnHidden())
3305                layer.getOverlay().mouseReleased(event, pt, this);
3306        }
3307    }
3308
3309    /**
3310     * Helper to forward mouse release event to the overlays.
3311     * 
3312     * @param event
3313     *        original mouse event
3314     */
3315    public void mouseReleased(MouseEvent event)
3316    {
3317        mouseReleased(event, getMouseImagePos5D());
3318    }
3319
3320    /**
3321     * Helper to forward mouse click event to the overlays.
3322     * 
3323     * @param event
3324     *        original mouse event
3325     * @param pt
3326     *        mouse image position
3327     */
3328    public void mouseClick(MouseEvent event, Point5D.Double pt)
3329    {
3330        final boolean globalVisible = isLayersVisible();
3331
3332        // send mouse event to overlays after so mouse canvas position is ok
3333        for (Layer layer : getLayers(true))
3334        {
3335            if ((globalVisible && layer.isVisible()) || layer.getReceiveMouseEventOnHidden())
3336                layer.getOverlay().mouseClick(event, pt, this);
3337        }
3338    }
3339
3340    /**
3341     * Helper to forward mouse click event to the overlays.
3342     * 
3343     * @param event
3344     *        original mouse event
3345     */
3346    public void mouseClick(MouseEvent event)
3347    {
3348        mouseClick(event, getMouseImagePos5D());
3349    }
3350
3351    /**
3352     * Helper to forward mouse move event to the overlays.
3353     * 
3354     * @param event
3355     *        original mouse event
3356     * @param pt
3357     *        mouse image position
3358     */
3359    public void mouseMove(MouseEvent event, Point5D.Double pt)
3360    {
3361        final boolean globalVisible = isLayersVisible();
3362
3363        // send mouse event to overlays after so mouse canvas position is ok
3364        for (Layer layer : getLayers(true))
3365        {
3366            if ((globalVisible && layer.isVisible()) || layer.getReceiveMouseEventOnHidden())
3367                layer.getOverlay().mouseMove(event, pt, this);
3368        }
3369    }
3370
3371    /**
3372     * Helper to forward mouse mouse event to the overlays.
3373     * 
3374     * @param event
3375     *        original mouse event
3376     */
3377    public void mouseMove(MouseEvent event)
3378    {
3379        mouseMove(event, getMouseImagePos5D());
3380    }
3381
3382    /**
3383     * Helper to forward mouse drag event to the overlays.
3384     * 
3385     * @param event
3386     *        original mouse event
3387     * @param pt
3388     *        mouse image position
3389     */
3390    public void mouseDrag(MouseEvent event, Point5D.Double pt)
3391    {
3392        final boolean globalVisible = isLayersVisible();
3393
3394        // send mouse event to overlays after so mouse canvas position is ok
3395        for (Layer layer : getLayers(true))
3396        {
3397            if ((globalVisible && layer.isVisible()) || layer.getReceiveMouseEventOnHidden())
3398                layer.getOverlay().mouseDrag(event, pt, this);
3399        }
3400    }
3401
3402    /**
3403     * Helper to forward mouse drag event to the overlays.
3404     * 
3405     * @param event
3406     *        original mouse event
3407     */
3408    public void mouseDrag(MouseEvent event)
3409    {
3410        mouseDrag(event, getMouseImagePos5D());
3411    }
3412
3413    /**
3414     * Helper to forward mouse enter event to the overlays.
3415     * 
3416     * @param event
3417     *        original mouse event
3418     * @param pt
3419     *        mouse image position
3420     */
3421    public void mouseEntered(MouseEvent event, Point5D.Double pt)
3422    {
3423        final boolean globalVisible = isLayersVisible();
3424
3425        // send mouse event to overlays after so mouse canvas position is ok
3426        for (Layer layer : getLayers(true))
3427        {
3428            if ((globalVisible && layer.isVisible()) || layer.getReceiveMouseEventOnHidden())
3429                layer.getOverlay().mouseEntered(event, pt, this);
3430        }
3431    }
3432
3433    /**
3434     * Helper to forward mouse entered event to the overlays.
3435     * 
3436     * @param event
3437     *        original mouse event
3438     */
3439    public void mouseEntered(MouseEvent event)
3440    {
3441        mouseEntered(event, getMouseImagePos5D());
3442    }
3443
3444    /**
3445     * Helper to forward mouse exit event to the overlays.
3446     * 
3447     * @param event
3448     *        original mouse event
3449     * @param pt
3450     *        mouse image position
3451     */
3452    public void mouseExited(MouseEvent event, Point5D.Double pt)
3453    {
3454        final boolean globalVisible = isLayersVisible();
3455
3456        // send mouse event to overlays after so mouse canvas position is ok
3457        for (Layer layer : getLayers(true))
3458        {
3459            if ((globalVisible && layer.isVisible()) || layer.getReceiveMouseEventOnHidden())
3460                layer.getOverlay().mouseExited(event, pt, this);
3461        }
3462    }
3463
3464    /**
3465     * Helper to forward mouse exited event to the overlays.
3466     * 
3467     * @param event
3468     *        original mouse event
3469     */
3470    public void mouseExited(MouseEvent event)
3471    {
3472        mouseExited(event, getMouseImagePos5D());
3473    }
3474
3475    /**
3476     * Helper to forward mouse wheel event to the overlays.
3477     * 
3478     * @param event
3479     *        original mouse event
3480     * @param pt
3481     *        mouse image position
3482     */
3483    public void mouseWheelMoved(MouseWheelEvent event, Point5D.Double pt)
3484    {
3485        final boolean globalVisible = isLayersVisible();
3486
3487        // send mouse event to overlays after so mouse canvas position is ok
3488        for (Layer layer : getLayers(true))
3489        {
3490            if ((globalVisible && layer.isVisible()) || layer.getReceiveMouseEventOnHidden())
3491                layer.getOverlay().mouseWheelMoved(event, pt, this);
3492        }
3493    }
3494
3495    /**
3496     * Helper to forward mouse wheel event to the overlays.
3497     * 
3498     * @param event
3499     *        original mouse event
3500     */
3501    public void mouseWheelMoved(MouseWheelEvent event)
3502    {
3503        mouseWheelMoved(event, getMouseImagePos5D());
3504    }
3505
3506    @Override
3507    public void keyPressed(KeyEvent e)
3508    {
3509        final boolean globalVisible = isLayersVisible();
3510        final Point5D.Double pt = getMouseImagePos5D();
3511
3512        // forward event to overlays
3513        for (Layer layer : getLayers(true))
3514        {
3515            if ((globalVisible && layer.isVisible()) || layer.getReceiveKeyEventOnHidden())
3516                layer.getOverlay().keyPressed(e, pt, this);
3517        }
3518
3519        if (!e.isConsumed())
3520        {
3521            switch (e.getKeyCode())
3522            {
3523                case KeyEvent.VK_0:
3524                    if (EventUtil.isShiftDown(e, true))
3525                    {
3526                        if (CanvasActions.globalDisableSyncAction.isEnabled())
3527                        {
3528                            CanvasActions.globalDisableSyncAction.execute();
3529                            e.consume();
3530                        }
3531                    }
3532                    else if (EventUtil.isNoModifier(e))
3533                    {
3534                        if (CanvasActions.disableSyncAction.isEnabled())
3535                        {
3536                            CanvasActions.disableSyncAction.execute();
3537                            e.consume();
3538                        }
3539                    }
3540                    break;
3541
3542                case KeyEvent.VK_1:
3543                    if (EventUtil.isShiftDown(e, true))
3544                    {
3545                        if (CanvasActions.globalSyncGroup1Action.isEnabled())
3546                        {
3547                            CanvasActions.globalSyncGroup1Action.execute();
3548                            e.consume();
3549                        }
3550                    }
3551                    else if (EventUtil.isNoModifier(e))
3552                    {
3553                        if (CanvasActions.syncGroup1Action.isEnabled())
3554                        {
3555                            CanvasActions.syncGroup1Action.execute();
3556                            e.consume();
3557                        }
3558                    }
3559                    break;
3560
3561                case KeyEvent.VK_2:
3562                    if (EventUtil.isShiftDown(e, true))
3563                    {
3564                        if (CanvasActions.globalSyncGroup2Action.isEnabled())
3565                        {
3566                            CanvasActions.globalSyncGroup2Action.execute();
3567                            e.consume();
3568                        }
3569                    }
3570                    else if (EventUtil.isNoModifier(e))
3571                    {
3572                        if (CanvasActions.syncGroup2Action.isEnabled())
3573                        {
3574                            CanvasActions.syncGroup2Action.execute();
3575                            e.consume();
3576                        }
3577                    }
3578                    break;
3579
3580                case KeyEvent.VK_3:
3581                    if (EventUtil.isShiftDown(e, true))
3582                    {
3583                        if (CanvasActions.globalSyncGroup3Action.isEnabled())
3584                        {
3585                            CanvasActions.globalSyncGroup3Action.execute();
3586                            e.consume();
3587                        }
3588                    }
3589                    else if (EventUtil.isNoModifier(e))
3590                    {
3591                        if (CanvasActions.syncGroup3Action.isEnabled())
3592                        {
3593                            CanvasActions.syncGroup3Action.execute();
3594                            e.consume();
3595                        }
3596                    }
3597                    break;
3598
3599                case KeyEvent.VK_4:
3600                    if (EventUtil.isShiftDown(e, true))
3601                    {
3602                        if (CanvasActions.globalSyncGroup4Action.isEnabled())
3603                        {
3604                            CanvasActions.globalSyncGroup4Action.execute();
3605                            e.consume();
3606                        }
3607                    }
3608                    else if (EventUtil.isNoModifier(e))
3609                    {
3610                        if (CanvasActions.syncGroup4Action.isEnabled())
3611                        {
3612                            CanvasActions.syncGroup4Action.execute();
3613                            e.consume();
3614                        }
3615                    }
3616                    break;
3617
3618                case KeyEvent.VK_G:
3619                    if (EventUtil.isShiftDown(e, true))
3620                    {
3621                        if (WindowActions.gridTileAction.isEnabled())
3622                        {
3623                            WindowActions.gridTileAction.execute();
3624                            e.consume();
3625                        }
3626                    }
3627                    break;
3628
3629                case KeyEvent.VK_H:
3630                    if (EventUtil.isShiftDown(e, true))
3631                    {
3632                        if (WindowActions.horizontalTileAction.isEnabled())
3633                        {
3634                            WindowActions.horizontalTileAction.execute();
3635                            e.consume();
3636                        }
3637                    }
3638                    break;
3639
3640                case KeyEvent.VK_A:
3641                    if (EventUtil.isMenuControlDown(e, true))
3642                    {
3643                        if (RoiActions.selectAllAction.isEnabled())
3644                        {
3645                            RoiActions.selectAllAction.execute();
3646                            e.consume();
3647                        }
3648                    }
3649                    break;
3650
3651                case KeyEvent.VK_V:
3652                    if (EventUtil.isShiftDown(e, true))
3653                    {
3654                        if (WindowActions.verticalTileAction.isEnabled())
3655                        {
3656                            WindowActions.verticalTileAction.execute();
3657                            e.consume();
3658                        }
3659                    }
3660                    else if (EventUtil.isMenuControlDown(e, true))
3661                    {
3662                        if (GeneralActions.pasteImageAction.isEnabled())
3663                        {
3664                            GeneralActions.pasteImageAction.execute();
3665                            e.consume();
3666                        }
3667                        else if (RoiActions.pasteAction.isEnabled())
3668                        {
3669                            RoiActions.pasteAction.execute();
3670                            e.consume();
3671                        }
3672                    }
3673                    else if (EventUtil.isAltDown(e, true))
3674                    {
3675                        if (RoiActions.pasteLinkAction.isEnabled())
3676                        {
3677                            RoiActions.pasteLinkAction.execute();
3678                            e.consume();
3679                        }
3680                    }
3681                    break;
3682
3683                case KeyEvent.VK_C:
3684                    if (EventUtil.isMenuControlDown(e, true))
3685                    {
3686                        // do this one first else copyImage hide it
3687                        if (RoiActions.copyAction.isEnabled())
3688                        {
3689                            // copy them to icy clipboard
3690                            RoiActions.copyAction.execute();
3691                            e.consume();
3692                        }
3693                        else if (GeneralActions.copyImageAction.isEnabled())
3694                        {
3695                            // copy image to system clipboard
3696                            GeneralActions.copyImageAction.execute();
3697                            e.consume();
3698                        }
3699                    }
3700                    else if (EventUtil.isAltDown(e, true))
3701                    {
3702                        if (RoiActions.copyLinkAction.isEnabled())
3703                        {
3704                            // copy link of selected ROI to clipboard
3705                            RoiActions.copyLinkAction.execute();
3706                            e.consume();
3707                        }
3708                    }
3709                    break;
3710
3711                case KeyEvent.VK_SPACE:
3712                    if (tNav.isPlaying())
3713                        tNav.stopPlay();
3714                    else
3715                        tNav.startPlay();
3716                    e.consume();
3717                    break;
3718            }
3719        }
3720    }
3721
3722    @Override
3723    public void keyReleased(KeyEvent e)
3724    {
3725        final boolean globalVisible = isLayersVisible();
3726        final Point5D.Double pt = getMouseImagePos5D();
3727
3728        // forward event to overlays
3729        for (Layer layer : getLayers(true))
3730        {
3731            if ((globalVisible && layer.isVisible()) || layer.getReceiveKeyEventOnHidden())
3732                layer.getOverlay().keyReleased(e, pt, this);
3733        }
3734    }
3735
3736    @Override
3737    public void keyTyped(KeyEvent e)
3738    {
3739        // nothing to do by default
3740    }
3741
3742    /**
3743     * Gets the image at position (t, z, c).
3744     */
3745    public IcyBufferedImage getImage(int t, int z, int c)
3746    {
3747        if ((t == -1) || (z == -1))
3748            return null;
3749
3750        final Sequence sequence = getSequence();
3751
3752        // have to test this as sequence reference can be release in viewer
3753        if (sequence != null)
3754            return sequence.getImage(t, z, c);
3755
3756        return null;
3757    }
3758
3759    /**
3760     * @deprecated Use {@link #getImage(int, int, int)} with C = -1 instead.
3761     */
3762    @Deprecated
3763    public IcyBufferedImage getImage(int t, int z)
3764    {
3765        return getImage(t, z, -1);
3766    }
3767
3768    /**
3769     * Get the current image.
3770     */
3771    public IcyBufferedImage getCurrentImage()
3772    {
3773        return getImage(getPositionT(), getPositionZ(), getPositionC());
3774    }
3775
3776    /**
3777     * @deprecated use {@link #getRenderedImage(int, int, int, boolean)} instead
3778     */
3779    @Deprecated
3780    public final BufferedImage getRenderedImage(int t, int z, int c, int imageType, boolean canvasView)
3781    {
3782        return getRenderedImage(t, z, c, canvasView);
3783    }
3784
3785    /**
3786     * @deprecated use {@link #getRenderedSequence(boolean)} instead
3787     */
3788    @Deprecated
3789    public final Sequence getRenderedSequence(int imageType, boolean canvasView)
3790    {
3791        return getRenderedSequence(canvasView);
3792    }
3793
3794    /**
3795     * Returns a RGB or ARGB (depending support) BufferedImage representing the canvas view for
3796     * image at position (t, z, c).<br>
3797     * Free feel to the canvas to handle or not a specific dimension.
3798     * 
3799     * @param t
3800     *        T position of wanted image (-1 for complete sequence)
3801     * @param z
3802     *        Z position of wanted image (-1 for complete stack)
3803     * @param c
3804     *        C position of wanted image (-1 for all channels)
3805     * @param canvasView
3806     *        render with canvas view if true else use default sequence dimension
3807     */
3808    public abstract BufferedImage getRenderedImage(int t, int z, int c, boolean canvasView);
3809
3810    /**
3811     * @deprecated Use {@link #getRenderedImage(int, int, int, boolean)} instead.
3812     */
3813    @Deprecated
3814    public BufferedImage getRenderedImage(int t, int z, int c)
3815    {
3816        return getRenderedImage(t, z, c, true);
3817    }
3818
3819    /**
3820     * Return a sequence which contains rendered images.<br>
3821     * Default implementation, override it if needed in your canvas.
3822     * 
3823     * @param canvasView
3824     *        render with canvas view if true else use default sequence dimension
3825     * @param progressListener
3826     *        progress listener which receive notifications about progression
3827     */
3828    public Sequence getRenderedSequence(boolean canvasView, ProgressListener progressListener)
3829    {
3830        final Sequence seqIn = getSequence();
3831        // create output sequence
3832        final Sequence result = new Sequence();
3833
3834        if (seqIn != null)
3835        {
3836            // derive original metadata
3837            result.setMetaData(OMEUtil.createOMEXMLMetadata(seqIn.getOMEXMLMetadata()));
3838
3839            int t = getPositionT();
3840            int z = getPositionZ();
3841            int c = getPositionC();
3842            final int sizeT = getImageSizeT();
3843            final int sizeZ = getImageSizeZ();
3844            final int sizeC = getImageSizeC();
3845
3846            int pos = 0;
3847            int len = 1;
3848            if (t != -1)
3849                len *= sizeT;
3850            if (z != -1)
3851                len *= sizeZ;
3852            if (c != -1)
3853                len *= sizeC;
3854
3855            result.beginUpdate();
3856            // This cause position changed event to not be sent during rendering.
3857            // Painters have to take care of that, they should check the canvas position
3858            // in the paint() method
3859            beginUpdate();
3860            try
3861            {
3862                if (t != -1)
3863                {
3864                    for (t = 0; t < sizeT; t++)
3865                    {
3866                        if (z != -1)
3867                        {
3868                            for (z = 0; z < sizeZ; z++)
3869                            {
3870                                if (c != -1)
3871                                {
3872                                    final List<BufferedImage> images = new ArrayList<BufferedImage>();
3873
3874                                    for (c = 0; c < sizeC; c++)
3875                                    {
3876                                        images.add(getRenderedImage(t, z, c, canvasView));
3877                                        pos++;
3878                                        if (progressListener != null)
3879                                            progressListener.notifyProgress(pos, len);
3880                                    }
3881
3882                                    result.setImage(t, z, IcyBufferedImage.createFrom(images));
3883                                }
3884                                else
3885                                {
3886                                    result.setImage(t, z, getRenderedImage(t, z, -1, canvasView));
3887                                    pos++;
3888                                    if (progressListener != null)
3889                                        progressListener.notifyProgress(pos, len);
3890                                }
3891                            }
3892                        }
3893                        else
3894                        {
3895                            result.setImage(t, 0, getRenderedImage(t, -1, -1, canvasView));
3896                            pos++;
3897                            if (progressListener != null)
3898                                progressListener.notifyProgress(pos, len);
3899                        }
3900                    }
3901                }
3902                else
3903                {
3904                    if (z != -1)
3905                    {
3906                        for (z = 0; z < sizeZ; z++)
3907                        {
3908                            if (c != -1)
3909                            {
3910                                final ArrayList<BufferedImage> images = new ArrayList<BufferedImage>();
3911
3912                                for (c = 0; c < sizeC; c++)
3913                                {
3914                                    images.add(getRenderedImage(-1, z, c, canvasView));
3915                                    pos++;
3916                                    if (progressListener != null)
3917                                        progressListener.notifyProgress(pos, len);
3918                                }
3919
3920                                result.setImage(0, z, IcyBufferedImage.createFrom(images));
3921                            }
3922                            else
3923                            {
3924                                result.setImage(0, z, getRenderedImage(-1, z, -1, canvasView));
3925                                pos++;
3926                                if (progressListener != null)
3927                                    progressListener.notifyProgress(pos, len);
3928                            }
3929                        }
3930                    }
3931                    else
3932                    {
3933                        if (c != -1)
3934                        {
3935                            final ArrayList<BufferedImage> images = new ArrayList<BufferedImage>();
3936
3937                            for (c = 0; c < sizeC; c++)
3938                            {
3939                                images.add(getRenderedImage(-1, -1, c, canvasView));
3940                                pos++;
3941                                if (progressListener != null)
3942                                    progressListener.notifyProgress(pos, len);
3943                            }
3944
3945                            result.setImage(0, 0, IcyBufferedImage.createFrom(images));
3946                        }
3947                        else
3948                        {
3949                            result.setImage(0, 0, getRenderedImage(-1, -1, -1, canvasView));
3950                            pos++;
3951                            if (progressListener != null)
3952                                progressListener.notifyProgress(pos, len);
3953                        }
3954                    }
3955                }
3956            }
3957            finally
3958            {
3959                endUpdate();
3960                result.endUpdate();
3961            }
3962        }
3963
3964        return result;
3965    }
3966
3967    /**
3968     * @deprecated Use {@link #getRenderedSequence(boolean, ProgressListener)} instead.
3969     */
3970    @Deprecated
3971    public Sequence getRenderedSequence(boolean canvasView)
3972    {
3973        return getRenderedSequence(canvasView, null);
3974    }
3975
3976    /**
3977     * @deprecated Use {@link #getRenderedSequence(boolean, ProgressListener)} instead.
3978     */
3979    @Deprecated
3980    public Sequence getRenderedSequence()
3981    {
3982        return getRenderedSequence(true, null);
3983    }
3984
3985    /**
3986     * Return the number of "selected" samples
3987     */
3988    public int getNumSelectedSamples()
3989    {
3990        final Sequence sequence = getSequence();
3991
3992        // have to test this as we release sequence reference on closed
3993        if (sequence == null)
3994            return 0;
3995
3996        final int base_len = getImageSizeX() * getImageSizeY() * getImageSizeC();
3997
3998        if (getPositionT() == -1)
3999        {
4000            if (getPositionZ() == -1)
4001                return base_len * getImageSizeZ() * getImageSizeT();
4002
4003            return base_len * getImageSizeT();
4004        }
4005
4006        if (getPositionZ() == -1)
4007            return base_len * getImageSizeZ();
4008
4009        return base_len;
4010    }
4011
4012    /**
4013     * Returns the frame rate (given in frame per second) for play command (T navigation panel).
4014     */
4015    public int getFrameRate()
4016    {
4017        return tNav.getFrameRate();
4018    }
4019
4020    /**
4021     * Sets the frame rate (given in frame per second) for play command (T navigation panel).
4022     */
4023    public void setFrameRate(int fps)
4024    {
4025        tNav.setFrameRate(fps);
4026    }
4027
4028    /**
4029     * update Z slider state
4030     */
4031    protected void updateZNav()
4032    {
4033        final int maxZ = getMaxPositionZ();
4034        final int z = getPositionZ();
4035
4036        zNav.setMaximum(maxZ);
4037        if (z != -1)
4038        {
4039            zNav.setValue(z);
4040            zNav.setVisible(maxZ > 0);
4041        }
4042        else
4043            zNav.setVisible(false);
4044    }
4045
4046    /**
4047     * update T slider state
4048     */
4049    protected void updateTNav()
4050    {
4051        final int maxT = getMaxPositionT();
4052        final int t = getPositionT();
4053
4054        tNav.setMaximum(maxT);
4055        if (t != -1)
4056        {
4057            tNav.setValue(t);
4058            tNav.setVisible(maxT > 0);
4059        }
4060        else
4061            tNav.setVisible(false);
4062    }
4063
4064    /**
4065     * @deprecated Use {@link #getLayer(Overlay)} instead.
4066     */
4067    @Deprecated
4068    public Layer getLayer(Painter painter)
4069    {
4070        for (Layer layer : getLayers(false))
4071            if (layer.getPainter() == painter)
4072                return layer;
4073
4074        return null;
4075    }
4076
4077    /**
4078     * Find the layer corresponding to the specified Overlay
4079     */
4080    public Layer getLayer(Overlay overlay)
4081    {
4082        return layers.get(overlay);
4083    }
4084
4085    /**
4086     * Find the layer corresponding to the specified ROI (use the ROI overlay internally).
4087     */
4088    public Layer getLayer(ROI roi)
4089    {
4090        return getLayer(roi.getOverlay());
4091    }
4092
4093    /**
4094     * @deprecated Use {@link #hasLayer(Overlay)} instead.
4095     */
4096    @Deprecated
4097    public boolean hasLayer(Painter painter)
4098    {
4099        return getLayer(painter) != null;
4100    }
4101
4102    /**
4103     * Returns true if the canvas contains a layer for the specified {@link Overlay}.
4104     */
4105    public boolean hasLayer(Overlay overlay)
4106    {
4107        synchronized (layers)
4108        {
4109            return layers.containsKey(overlay);
4110        }
4111    }
4112
4113    public boolean hasLayer(Layer layer)
4114    {
4115        final Overlay overlay = layer.getOverlay();
4116
4117        // faster to test from overlay
4118        if (overlay != null)
4119            return hasLayer(overlay);
4120
4121        synchronized (layers)
4122        {
4123            return layers.containsValue(layer);
4124        }
4125    }
4126
4127    /**
4128     * @deprecated Use {@link #addLayer(Overlay)} instead.
4129     */
4130    @Deprecated
4131    public void addLayer(Painter painter)
4132    {
4133        if (!hasLayer(painter))
4134            addLayer(new Layer(painter));
4135    }
4136
4137    public Layer addLayer(Overlay overlay)
4138    {
4139        if (!hasLayer(overlay))
4140            return addLayer(new Layer(overlay));
4141
4142        return null;
4143    }
4144
4145    protected Layer addLayer(Layer layer)
4146    {
4147        if (layer != null)
4148        {
4149            // listen layer
4150            layer.addListener(this);
4151
4152            // add to list
4153            synchronized (layers)
4154            {
4155                layers.put(layer.getOverlay(), layer);
4156                if (Layer.DEFAULT_NAME.equals(layer))
4157                    layer.setName("layer " + layers.size());
4158            }
4159
4160            // added
4161            layerAdded(layer);
4162        }
4163
4164        return layer;
4165    }
4166
4167    /**
4168     * @deprecated Use {@link #removeLayer(Overlay)} instead.
4169     */
4170    @Deprecated
4171    public void removeLayer(Painter painter)
4172    {
4173        removeLayer(getLayer(painter));
4174    }
4175
4176    /**
4177     * Remove the layer for the specified {@link Overlay} from the canvas.<br/>
4178     * Returns <code>true</code> if the method succeed.
4179     */
4180    public boolean removeLayer(Overlay overlay)
4181    {
4182        final Layer layer;
4183
4184        // remove from list
4185        synchronized (layers)
4186        {
4187            layer = layers.remove(overlay);
4188        }
4189
4190        if (layer != null)
4191        {
4192            // stop listening layer
4193            layer.removeListener(this);
4194            // notify remove
4195            layerRemoved(layer);
4196
4197            return true;
4198        }
4199
4200        return false;
4201    }
4202
4203    /**
4204     * Remove the specified layer from the canvas.
4205     */
4206    public void removeLayer(Layer layer)
4207    {
4208        removeLayer(layer.getOverlay());
4209    }
4210
4211    /**
4212     * Returns <code>true</code> if the specified overlay is visible in the canvas.<br>
4213     */
4214    public boolean isVisible(Overlay overlay)
4215    {
4216        final Layer layer = getLayer(overlay);
4217
4218        if (layer != null)
4219            return layer.isVisible();
4220
4221        return false;
4222    }
4223
4224    /**
4225     * @deprecated Use {@link #addLayerListener(CanvasLayerListener)} instead.
4226     */
4227    @Deprecated
4228    public void addLayersListener(CanvasLayerListener listener)
4229    {
4230        addLayerListener(listener);
4231    }
4232
4233    /**
4234     * @deprecated Use {@link #removeLayerListener(CanvasLayerListener)} instead.
4235     */
4236    @Deprecated
4237    public void removeLayersListener(CanvasLayerListener listener)
4238    {
4239        removeLayerListener(listener);
4240    }
4241
4242    /**
4243     * Returns all canvas layer listener
4244     */
4245    public List<CanvasLayerListener> getLayerListeners()
4246    {
4247        synchronized (layerListeners)
4248        {
4249            return new ArrayList<CanvasLayerListener>(layerListeners);
4250        }
4251    }
4252
4253    /**
4254     * Add a layer listener
4255     * 
4256     * @param listener
4257     */
4258    public void addLayerListener(CanvasLayerListener listener)
4259    {
4260        synchronized (layerListeners)
4261        {
4262            if (listener != null)
4263                layerListeners.add(listener);
4264        }
4265    }
4266
4267    /**
4268     * Remove a layer listener
4269     * 
4270     * @param listener
4271     */
4272    public void removeLayerListener(CanvasLayerListener listener)
4273    {
4274        synchronized (layerListeners)
4275        {
4276            layerListeners.remove(listener);
4277        }
4278    }
4279
4280    protected void fireLayerChangedEvent(CanvasLayerEvent event)
4281    {
4282        for (CanvasLayerListener listener : getLayerListeners())
4283            listener.canvasLayerChanged(event);
4284    }
4285
4286    /**
4287     * Returns all canvas listener
4288     */
4289    public List<IcyCanvasListener> getListeners()
4290    {
4291        synchronized (listeners)
4292        {
4293            return new ArrayList<IcyCanvasListener>(listeners);
4294        }
4295    }
4296
4297    /**
4298     * Add a IcyCanvas listener
4299     * 
4300     * @param listener
4301     */
4302    public void addCanvasListener(IcyCanvasListener listener)
4303    {
4304        synchronized (listeners)
4305        {
4306            listeners.add(listener);
4307        }
4308    }
4309
4310    /**
4311     * Remove a IcyCanvas listener
4312     * 
4313     * @param listener
4314     */
4315    public void removeCanvasListener(IcyCanvasListener listener)
4316    {
4317        synchronized (listeners)
4318        {
4319            listeners.remove(listener);
4320        }
4321    }
4322
4323    protected void fireCanvasChangedEvent(IcyCanvasEvent event)
4324    {
4325        for (IcyCanvasListener listener : getListeners())
4326            listener.canvasChanged(event);
4327    }
4328
4329    public void beginUpdate()
4330    {
4331        updater.beginUpdate();
4332    }
4333
4334    public void endUpdate()
4335    {
4336        updater.endUpdate();
4337    }
4338
4339    public boolean isUpdating()
4340    {
4341        return updater.isUpdating();
4342    }
4343
4344    /**
4345     * layer added
4346     * 
4347     * @param layer
4348     */
4349    protected void layerAdded(Layer layer)
4350    {
4351        // handle with updater
4352        updater.changed(new CanvasLayerEvent(layer, LayersEventType.ADDED));
4353    }
4354
4355    /**
4356     * layer removed
4357     * 
4358     * @param layer
4359     */
4360    protected void layerRemoved(Layer layer)
4361    {
4362        // handle with updater
4363        updater.changed(new CanvasLayerEvent(layer, LayersEventType.REMOVED));
4364    }
4365
4366    /**
4367     * layer has changed
4368     */
4369    @Override
4370    public void layerChanged(Layer layer, String propertyName)
4371    {
4372        // handle with updater
4373        updater.changed(new CanvasLayerEvent(layer, LayersEventType.CHANGED, propertyName));
4374    }
4375
4376    /**
4377     * canvas changed (packed event).<br>
4378     * do global changes processing here
4379     */
4380    public void changed(IcyCanvasEvent event)
4381    {
4382        final IcyCanvasEventType eventType = event.getType();
4383
4384        // handle synchronized canvas
4385        if (isSynchronized())
4386        {
4387            final List<IcyCanvas> synchCanvasList = getSynchronizedCanvas();
4388
4389            // this is the synchronizer master so dispatch view changes to others canvas
4390            if (getSynchMaster(synchCanvasList))
4391            {
4392                try
4393                {
4394                    // synchronize all events when the view has just been synchronized
4395                    final boolean synchAll = (eventType == IcyCanvasEventType.SYNC_CHANGED);
4396                    synchronizeCanvas(synchCanvasList, event, synchAll);
4397                }
4398                finally
4399                {
4400                    releaseSynchMaster();
4401                }
4402            }
4403        }
4404
4405        switch (eventType)
4406        {
4407            case POSITION_CHANGED:
4408                final int curZ = getPositionZ();
4409                final int curT = getPositionT();
4410                final int curC = getPositionC();
4411
4412                switch (event.getDim())
4413                {
4414                    case Z:
4415                        // ensure Z slider position
4416                        if (curZ != -1)
4417                            zNav.setValue(curZ);
4418                        break;
4419
4420                    case T:
4421                        // ensure T slider position
4422                        if (curT != -1)
4423                            tNav.setValue(curT);
4424                        break;
4425
4426                    case C:
4427                        // single channel mode
4428                        final int maxC = getMaxPositionC();
4429
4430                        // disabled others channels
4431                        for (int c = 0; c <= maxC; c++)
4432                            getLut().getLutChannel(c).setEnabled((curC == -1) || (curC == c));
4433                        break;
4434
4435                    case NULL:
4436                        // ensure Z slider position
4437                        if (curZ != -1)
4438                            zNav.setValue(curZ);
4439                        // ensure T slider position
4440                        if (curT != -1)
4441                            tNav.setValue(curT);
4442                        break;
4443                }
4444                // refresh mouse panel informations
4445                mouseInfPanel.updateInfos(this);
4446                break;
4447
4448            case MOUSE_IMAGE_POSITION_CHANGED:
4449                // refresh mouse panel informations
4450                mouseInfPanel.updateInfos(this);
4451                break;
4452        }
4453
4454        // notify listeners that canvas have changed
4455        fireCanvasChangedEvent(event);
4456    }
4457
4458    /**
4459     * layer property has changed (packed event)
4460     */
4461    protected void layerChanged(CanvasLayerEvent event)
4462    {
4463        final String property = event.getProperty();
4464
4465        // we need to rebuild sorted layer list
4466        if ((event.getType() != LayersEventType.CHANGED) || (property == null) || (property == Layer.PROPERTY_PRIORITY))
4467            orderedLayersOutdated = true;
4468
4469        // notify listeners that layers have changed
4470        fireLayerChangedEvent(event);
4471    }
4472
4473    /**
4474     * position has changed<br>
4475     * 
4476     * @param dim
4477     *        define the position which has changed
4478     */
4479    protected void positionChanged(DimensionId dim)
4480    {
4481        // handle with updater
4482        updater.changed(new IcyCanvasEvent(this, IcyCanvasEventType.POSITION_CHANGED, dim));
4483    }
4484
4485    @Override
4486    public void lutChanged(LUTEvent event)
4487    {
4488        final int curC = getPositionC();
4489
4490        // single channel mode ?
4491        if (curC != -1)
4492        {
4493            final int channel = event.getComponent();
4494
4495            // channel is enabled --> change C position
4496            if ((channel != -1) && getLut().getLutChannel(channel).isEnabled())
4497                setPositionC(channel);
4498            else
4499                // ensure we have 1 channel enable
4500                getLut().getLutChannel(curC).setEnabled(true);
4501        }
4502
4503        lutChanged(event.getComponent());
4504    }
4505
4506    /**
4507     * lut changed
4508     * 
4509     * @param component
4510     */
4511    protected void lutChanged(int component)
4512    {
4513        // nothing to do by default
4514    }
4515
4516    /**
4517     * sequence meta data has changed
4518     */
4519    protected void sequenceMetaChanged(String metadataName)
4520    {
4521        // nothing to do by default
4522    }
4523
4524    /**
4525     * sequence type has changed
4526     */
4527    protected void sequenceTypeChanged()
4528    {
4529        // nothing to do by default
4530    }
4531
4532    /**
4533     * sequence component bounds has changed
4534     * 
4535     * @param colorModel
4536     * @param component
4537     */
4538    protected void sequenceComponentBoundsChanged(IcyColorModel colorModel, int component)
4539    {
4540        // nothing to do by default
4541    }
4542
4543    /**
4544     * sequence component bounds has changed
4545     * 
4546     * @param colorModel
4547     * @param component
4548     */
4549    protected void sequenceColorMapChanged(IcyColorModel colorModel, int component)
4550    {
4551        // nothing to do by default
4552    }
4553
4554    /**
4555     * sequence data has changed
4556     * 
4557     * @param image
4558     *        image which has changed (null if global data changed)
4559     * @param type
4560     *        event type
4561     */
4562    protected void sequenceDataChanged(IcyBufferedImage image, SequenceEventType type)
4563    {
4564        ThreadUtil.runSingle(guiUpdater);
4565    }
4566
4567    /**
4568     * @deprecated Use {@link #sequenceOverlayChanged(Overlay, SequenceEventType)} instead.
4569     */
4570    @Deprecated
4571    protected void sequencePainterChanged(Painter painter, SequenceEventType type)
4572    {
4573        // no more stuff here
4574    }
4575
4576    /**
4577     * Sequence overlay has changed
4578     * 
4579     * @param overlay
4580     *        overlay which has changed
4581     * @param type
4582     *        event type
4583     */
4584    protected void sequenceOverlayChanged(Overlay overlay, SequenceEventType type)
4585    {
4586        switch (type)
4587        {
4588            case ADDED:
4589                addLayer(overlay);
4590                break;
4591
4592            case REMOVED:
4593                removeLayer(overlay);
4594                break;
4595
4596            case CHANGED:
4597                // nothing to do here
4598                break;
4599        }
4600    }
4601
4602    /**
4603     * sequence roi has changed
4604     * 
4605     * @param roi
4606     *        roi which has changed (null if global roi changed)
4607     * @param type
4608     *        event type
4609     */
4610    protected void sequenceROIChanged(ROI roi, SequenceEventType type)
4611    {
4612        // nothing here
4613
4614    }
4615
4616    @Override
4617    public void viewerChanged(ViewerEvent event)
4618    {
4619        switch (event.getType())
4620        {
4621            case POSITION_CHANGED:
4622                // ignore this event as we are launching it
4623                break;
4624
4625            case LUT_CHANGED:
4626                // set new lut
4627                setLut(viewer.getLut(), true);
4628                break;
4629
4630            case CANVAS_CHANGED:
4631                // nothing to do
4632                break;
4633        }
4634    }
4635
4636    @Override
4637    public void viewerClosed(Viewer viewer)
4638    {
4639        // nothing to do here
4640    }
4641
4642    @Override
4643    public final void sequenceChanged(SequenceEvent event)
4644    {
4645        switch (event.getSourceType())
4646        {
4647            case SEQUENCE_META:
4648                sequenceMetaChanged((String) event.getSource());
4649                break;
4650
4651            case SEQUENCE_TYPE:
4652                sequenceTypeChanged();
4653                break;
4654
4655            case SEQUENCE_COMPONENTBOUNDS:
4656                sequenceComponentBoundsChanged((IcyColorModel) event.getSource(), event.getParam());
4657                break;
4658
4659            case SEQUENCE_COLORMAP:
4660                sequenceColorMapChanged((IcyColorModel) event.getSource(), event.getParam());
4661                break;
4662
4663            case SEQUENCE_DATA:
4664                sequenceDataChanged((IcyBufferedImage) event.getSource(), event.getType());
4665                break;
4666
4667            case SEQUENCE_OVERLAY:
4668                final Overlay overlay = (Overlay) event.getSource();
4669
4670                sequenceOverlayChanged(overlay, event.getType());
4671
4672                // backward compatibility
4673                @SuppressWarnings("deprecation")
4674                final Painter painter;
4675
4676                if (overlay instanceof OverlayWrapper)
4677                    painter = ((OverlayWrapper) overlay).getPainter();
4678                else
4679                    painter = overlay;
4680
4681                sequencePainterChanged(painter, event.getType());
4682                break;
4683
4684            case SEQUENCE_ROI:
4685                sequenceROIChanged((ROI) event.getSource(), event.getType());
4686                break;
4687        }
4688    }
4689
4690    @Override
4691    public void sequenceClosed(Sequence sequence)
4692    {
4693        // nothing to do here
4694    }
4695
4696    @Override
4697    public void onChanged(CollapsibleEvent event)
4698    {
4699        if (event instanceof CanvasLayerEvent)
4700            layerChanged((CanvasLayerEvent) event);
4701
4702        if (event instanceof IcyCanvasEvent)
4703            changed((IcyCanvasEvent) event);
4704    }
4705}