001/* Animation renderer.
002
003 Copyright (c) 1999-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.vergil.kernel;
029
030import java.awt.Color;
031import java.awt.Stroke;
032import java.util.Hashtable;
033
034import javax.swing.SwingUtilities;
035
036import diva.canvas.Figure;
037import diva.canvas.FigureContainer;
038import diva.canvas.FigureDecorator;
039import diva.canvas.interactor.SelectionRenderer;
040import diva.canvas.toolbox.BasicHighlighter;
041
042///////////////////////////////////////////////////////////////////
043//// AnimationRenderer
044
045/**
046 An implementation of a selection renderer that is used for animation.
047 It highlights specified figures by wrapping them in an instance of
048 a FigureDecorator. The figure decorator is obtained by
049 cloning a prototype decorator, accessible through the
050 get/setFigureDecorator() methods. The default prototype
051 is an instance of BasicHighlighter that highlights in red.
052 <p>
053 This class is fashioned after BasicSelectionRenderer, but differs
054 in that it ensures that selection and deselection occurs in the event
055 thread. Also, it highlights objects in red rather than yellow.
056
057 @author Edward A. Lee
058 @version $Id$
059 @since Ptolemy II 2.0
060 @Pt.ProposedRating Red (eal)
061 @Pt.AcceptedRating Red (johnr)
062 */
063public class AnimationRenderer implements SelectionRenderer {
064    /** Create a new selection renderer with the default prototype
065     *  decorator.
066     */
067    public AnimationRenderer() {
068        this(Color.red);
069    }
070
071    /** Create a new selection renderer with the default prototype
072     *  decorator using the specified color.
073     *  @param color The color for the highlight.
074     */
075    public AnimationRenderer(Color color) {
076        _prototypeDecorator = new BasicHighlighter(color, 6.0f);
077    }
078
079    /** Create a new selection renderer with the default prototype
080     *  decorator using the specified color and halo size.
081     *  @param color The color for the highlight.
082     *  @param halo The halo size.
083     */
084    public AnimationRenderer(Color color, float halo) {
085        _prototypeDecorator = new BasicHighlighter(color, halo);
086    }
087
088    /** Create a new selection renderer with the default prototype
089     *  decorator using the specified color and stroke. This draws
090     *  an outline rather than a filled shape.
091     *  @param color The color for the highlight.
092     *  @param stroke The stroke for the highlight.
093     */
094    public AnimationRenderer(Color color, Stroke stroke) {
095        _prototypeDecorator = new BasicHighlighter(color, 4.0f, null, stroke);
096    }
097
098    ///////////////////////////////////////////////////////////////////
099    ////                         public methods                    ////
100
101    /** Create a new renderer with the given prototype decorator.
102     *  @param decorator The prototype decorator.
103     */
104    public AnimationRenderer(FigureDecorator decorator) {
105        _prototypeDecorator = decorator;
106    }
107
108    /** Get the prototype decorator.
109     *  @return The prototype decorator.
110     *  @see #setDecorator(FigureDecorator)
111     */
112    public FigureDecorator getDecorator() {
113        return _prototypeDecorator;
114    }
115
116    /** Test whether the given figure is currently rendered highlighted.
117     *  @param figure The figure that may be highlighted.
118     *  @return True if the figure is highlighted.
119     */
120    @Override
121    public synchronized boolean isRenderedSelected(Figure figure) {
122        return _decorators.containsKey(figure);
123    }
124
125    /** Set the rendering of the figure to not be highlighted. The figure
126     *  has the decorator unwrapped off it and is inserted back into
127     *  its parent figure container, if there is one. If the figure is
128     *  not rendered highlighted, do nothing.
129     *  The rendering is performed later in the event thread,
130     *  and the calling thread is suspended by calling Thread.yield(),
131     *  to give the event thread a chance to catch up.
132     *  @param figure The figure to deselect.
133     */
134    @Override
135    public void renderDeselected(final Figure figure) {
136        Runnable doUndecorate = new Runnable() {
137            @Override
138            public void run() {
139                synchronized (AnimationRenderer.this) {
140                    if (!_decorators.containsKey(figure)) {
141                        return;
142                    }
143
144                    // Rather than just get the parent of the
145                    // figure, we must get the decorator out of
146                    // the hashtable, since other wrappers may
147                    // have been inserted between the figure and
148                    // its decorator
149                    FigureDecorator d = (FigureDecorator) _decorators
150                            .get(figure);
151
152                    if (d.getParent() != null) {
153                        figure.repaint();
154                        ((FigureContainer) d.getParent()).undecorate(d);
155                    }
156
157                    _decorators.remove(figure);
158                }
159            }
160        };
161
162        SwingUtilities.invokeLater(doUndecorate);
163        Thread.yield();
164    }
165
166    /** Set the rendering of the figure as highlighted. If the figure is
167     *  already rendered highlighted, just repaint. Otherwise create a new
168     *  BasicHighlighter, and wrap the figure in the decorator,
169     *  inserting the decorator into the figure's parent.
170     *  The rendering is performed later in the event thread,
171     *  and the calling thread is suspended by calling Thread.yield(),
172     *  to give the event thread a chance to catch up.
173     *  @param figure The figure to highlight.
174     */
175    @Override
176    public void renderSelected(final Figure figure) {
177        Runnable doDecorate = new Runnable() {
178            @Override
179            public void run() {
180                synchronized (AnimationRenderer.this) {
181                    if (_decorators.containsKey(figure)) {
182                        ((Figure) _decorators.get(figure)).repaint();
183                    } else {
184                        FigureContainer parent = (FigureContainer) figure
185                                .getParent();
186                        if (parent != null) {
187                            FigureDecorator d = _prototypeDecorator
188                                    .newInstance(figure);
189                            parent.decorate(figure, d);
190                            _decorators.put(figure, d);
191                        }
192                    }
193                }
194            }
195        };
196
197        SwingUtilities.invokeLater(doDecorate);
198        Thread.yield();
199    }
200
201    /** Set the prototype decorator.
202     *  @param decorator The prototype decorator.
203     *  @see #getDecorator()
204     */
205    public void setDecorator(FigureDecorator decorator) {
206        _prototypeDecorator = decorator;
207    }
208
209    ///////////////////////////////////////////////////////////////////
210    ////                         protected variables               ////
211
212    /** The prototype decorator.
213     */
214    protected FigureDecorator _prototypeDecorator;
215
216    ///////////////////////////////////////////////////////////////////
217    ////                         private variables                 ////
218
219    /** Mapping from figures to decorators
220     */
221    private Hashtable _decorators = new Hashtable();
222}