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 icy.main.Icy;
022import icy.painter.Overlay;
023import icy.painter.Overlay.OverlayPriority;
024import icy.painter.OverlayEvent;
025import icy.painter.OverlayEvent.OverlayEventType;
026import icy.painter.OverlayListener;
027import icy.painter.OverlayWrapper;
028import icy.painter.Painter;
029import icy.painter.WeakOverlayListener;
030import icy.roi.ROI;
031
032import java.lang.ref.WeakReference;
033import java.util.ArrayList;
034import java.util.List;
035
036/**
037 * Layer class.<br>
038 * This class encapsulate {@link Overlay} in a canvas to<br>
039 * add specific display properties (visibility, transparency...).
040 */
041public class Layer implements OverlayListener, Comparable<Layer>
042{
043    public interface LayerListener
044    {
045        public void layerChanged(Layer source, String propertyName);
046    }
047
048    public final static String PROPERTY_NAME = Overlay.PROPERTY_NAME;
049    public final static String PROPERTY_PRIORITY = Overlay.PROPERTY_PRIORITY;
050    public final static String PROPERTY_READONLY = Overlay.PROPERTY_READONLY;
051    public final static String PROPERTY_CANBEREMOVED = Overlay.PROPERTY_CANBEREMOVED;
052    public final static String PROPERTY_RECEIVEKEYEVENTONHIDDEN = Overlay.PROPERTY_RECEIVEKEYEVENTONHIDDEN;
053    public final static String PROPERTY_RECEIVEMOUSEEVENTONHIDDEN = Overlay.PROPERTY_RECEIVEMOUSEEVENTONHIDDEN;
054    /**
055     * @deprecated Use {@link #PROPERTY_OPACITY} instead
056     */
057    @Deprecated
058    public final static String PROPERTY_ALPHA = "alpha";
059    public final static String PROPERTY_OPACITY = "opacity";
060    public final static String PROPERTY_VISIBLE = "visible";
061
062    public final static String DEFAULT_NAME = "layer";
063
064    /**
065     * Returns true if the Layer need to be repainted when the specified property has changed.
066     */
067    public static boolean isPaintProperty(String propertyName)
068    {
069        if (propertyName == null)
070            return false;
071
072        return propertyName.equals(PROPERTY_OPACITY) || propertyName.equals(PROPERTY_PRIORITY)
073                || propertyName.equals(PROPERTY_VISIBLE);
074    }
075
076    static Overlay createOverlayWrapper(@SuppressWarnings("deprecation") Painter painter, String name)
077    {
078        if (painter instanceof Overlay)
079            return (Overlay) painter;
080
081        final Overlay result = new OverlayWrapper(painter, name);
082
083        if (name == null)
084            result.setName(DEFAULT_NAME);
085
086        // default priority
087        result.setPriority(OverlayPriority.SHAPE_NORMAL);
088
089        return result;
090    }
091
092    private final Overlay overlay;
093    // cache for ROI
094    private WeakReference<ROI> roi;
095
096    private boolean visible;
097    private float alpha;
098
099    /**
100     * listeners
101     */
102    protected final List<LayerListener> listeners;
103
104    public Layer(Overlay overlay)
105    {
106        this.overlay = overlay;
107
108        overlay.addOverlayListener(new WeakOverlayListener(this));
109
110        visible = true;
111        alpha = 1f;
112        roi = null;
113
114        listeners = new ArrayList<LayerListener>();
115    }
116
117    /**
118     * @deprecated Use {@link #Layer(Overlay)} instead.
119     */
120    @Deprecated
121    public Layer(Painter painter, String name)
122    {
123        this(createOverlayWrapper(painter, name));
124    }
125
126    /**
127     * @deprecated Use {@link #Layer(Overlay)} instead.
128     */
129    @Deprecated
130    public Layer(Painter painter)
131    {
132        this(painter, null);
133    }
134
135    /**
136     * Returns the attached {@link Overlay}.
137     */
138    public Overlay getOverlay()
139    {
140        return overlay;
141    }
142
143    /**
144     * @deprecated Use {@link #getOverlay()} instead.
145     */
146    @Deprecated
147    public Painter getPainter()
148    {
149        final Overlay result = getOverlay();
150
151        if (result instanceof OverlayWrapper)
152            return ((OverlayWrapper) result).getPainter();
153
154        return result;
155    }
156
157    /**
158     * Returns layer priority (internally use the overlay priority).
159     * 
160     * @see Overlay#getPriority()
161     */
162    public OverlayPriority getPriority()
163    {
164        return overlay.getPriority();
165    }
166
167    /**
168     * Set the layer priority (internally set the overlay priority).
169     * 
170     * @see Overlay#setPriority(OverlayPriority)
171     */
172    public void setPriority(OverlayPriority priority)
173    {
174        overlay.setPriority(priority);
175    }
176
177    /**
178     * Returns layer name (internally use the overlay name).
179     * 
180     * @see Overlay#getName()
181     */
182    public String getName()
183    {
184        return overlay.getName();
185    }
186
187    /**
188     * Set the layer name (internally set the overlay name)
189     * 
190     * @see Overlay#setName(String)
191     */
192    public void setName(String name)
193    {
194        overlay.setName(name);
195    }
196
197    /**
198     * Returns the read only property name (internally use the overlay read only property).
199     * 
200     * @see Overlay#isReadOnly()
201     */
202    public boolean isReadOnly()
203    {
204        return overlay.isReadOnly();
205    }
206
207    /**
208     * Set read only property (internally set the overlay read only property).
209     * 
210     * @see Overlay#setReadOnly(boolean)
211     */
212    public void setReadOnly(boolean readOnly)
213    {
214        overlay.setReadOnly(readOnly);
215    }
216
217    /**
218     * Returns fixed property.
219     * 
220     * @deprecated Use {@link #getCanBeRemoved()} instead.
221     */
222    @Deprecated
223    public boolean isFixed()
224    {
225        return !getCanBeRemoved();
226    }
227
228    /**
229     * @deprecated Use {@link #setCanBeRemoved(boolean)} instead.
230     */
231    @Deprecated
232    public void setFixed(boolean value)
233    {
234        setCanBeRemoved(value);
235    }
236
237    /**
238     * Returns <code>true</code> if the layer can be freely removed from the Canvas where it
239     * appears and <code>false</code> otherwise.<br/>
240     * 
241     * @see Overlay#getCanBeRemoved()
242     */
243    public boolean getCanBeRemoved()
244    {
245        return overlay.getCanBeRemoved();
246    }
247
248    /**
249     * Set the <code>canBeRemoved</code> property.<br/>
250     * Set it to false if you want to prevent the layer to be removed from the Canvas where it
251     * appears.
252     * 
253     * @see Overlay#setCanBeRemoved(boolean)
254     */
255    public void setCanBeRemoved(boolean value)
256    {
257        overlay.setCanBeRemoved(value);
258    }
259
260    /**
261     * @see Overlay#getReceiveKeyEventOnHidden()
262     */
263    public boolean getReceiveKeyEventOnHidden()
264    {
265        return overlay.getReceiveKeyEventOnHidden();
266    }
267
268    /**
269     * @see Overlay#setReceiveKeyEventOnHidden(boolean)
270     */
271    public void setReceiveKeyEventOnHidden(boolean value)
272    {
273        overlay.setReceiveKeyEventOnHidden(value);
274    }
275
276    /**
277     * @see Overlay#getReceiveMouseEventOnHidden()
278     */
279    public boolean getReceiveMouseEventOnHidden()
280    {
281        return overlay.getReceiveMouseEventOnHidden();
282    }
283
284    /**
285     * @see Overlay#setReceiveMouseEventOnHidden(boolean)
286     */
287    public void setReceiveMouseEventOnHidden(boolean value)
288    {
289        overlay.setReceiveMouseEventOnHidden(value);
290    }
291
292    /**
293     * @return the attachedROI
294     */
295    public ROI getAttachedROI()
296    {
297        if (roi == null)
298            // search for attached ROI
299            roi = new WeakReference<ROI>(Icy.getMainInterface().getROI(overlay));
300
301        return roi.get();
302    }
303
304    /**
305     * @return the visible
306     */
307    public boolean isVisible()
308    {
309        return visible;
310    }
311
312    /**
313     * @param visible
314     *        the visible to set
315     */
316    public void setVisible(boolean visible)
317    {
318        if (this.visible != visible)
319        {
320            this.visible = visible;
321            changed(PROPERTY_VISIBLE);
322        }
323    }
324
325    /**
326     * @return the layer opacity
327     */
328    public float getOpacity()
329    {
330        return alpha;
331    }
332
333    /**
334     * Set the layer opacity
335     */
336    public void setOpacity(float value)
337    {
338        if (alpha != value)
339        {
340            alpha = value;
341            changed(PROPERTY_OPACITY);
342        }
343    }
344
345    /**
346     * @deprecated Use {@link #getOpacity()} instead
347     */
348    @Deprecated
349    public float getAlpha()
350    {
351        return getOpacity();
352    }
353
354    /**
355     * @deprecated Use {@link #setOpacity(float)} instead.
356     */
357    @Deprecated
358    public void setAlpha(float value)
359    {
360        setOpacity(value);
361    }
362
363    /**
364     * Called on layer property change
365     */
366    protected void changed(String propertyName)
367    {
368        // notify listener
369        fireChangedEvent(propertyName);
370    }
371
372    /**
373     * fire event
374     */
375    private void fireChangedEvent(String propertyName)
376    {
377        final List<LayerListener> list;
378
379        synchronized (listeners)
380        {
381            list = new ArrayList<Layer.LayerListener>(listeners);
382        }
383
384        for (LayerListener listener : list)
385            listener.layerChanged(this, propertyName);
386    }
387
388    /**
389     * Add a listener
390     * 
391     * @param listener
392     */
393    public void addListener(LayerListener listener)
394    {
395        synchronized (listeners)
396        {
397            listeners.add(listener);
398        }
399    }
400
401    /**
402     * Remove a listener
403     * 
404     * @param listener
405     */
406    public void removeListener(LayerListener listener)
407    {
408        synchronized (listeners)
409        {
410            listeners.remove(listener);
411        }
412    }
413
414    @Override
415    public void overlayChanged(OverlayEvent event)
416    {
417        // only interested by property change here
418        if (event.getType() == OverlayEventType.PROPERTY_CHANGED)
419            changed(event.getPropertyName());
420    }
421
422    @Override
423    public int compareTo(Layer layer)
424    {
425        // compare with overlay
426        return getOverlay().compareTo(layer.getOverlay());
427    }
428}