001/* An icon that displays specified text.
002
003 Copyright (c) 2003-2016 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.icon;
029
030import java.awt.Color;
031import java.awt.Font;
032import java.util.Iterator;
033
034import javax.swing.SwingConstants;
035import javax.swing.SwingUtilities;
036
037import diva.canvas.Figure;
038import diva.canvas.toolbox.LabelFigure;
039import diva.gui.toolbox.FigureIcon;
040import ptolemy.gui.Top;
041import ptolemy.kernel.util.IllegalActionException;
042import ptolemy.kernel.util.NameDuplicationException;
043import ptolemy.kernel.util.NamedObj;
044import ptolemy.kernel.util.Workspace;
045
046///////////////////////////////////////////////////////////////////
047//// TextIcon
048
049/**
050 An icon that displays specified text.
051
052 @author Edward A. Lee
053 @version $Id$
054 @since Ptolemy II 4.0
055 @Pt.ProposedRating Yellow (eal)
056 @Pt.AcceptedRating Red (johnr)
057 */
058public class TextIcon extends DynamicEditorIcon {
059    /** Create a new icon with the given name in the given container.
060     *  @param container The container.
061     *  @param name The name of the attribute.
062     *  @exception IllegalActionException If the attribute is not of an
063     *   acceptable class for the container.
064     *  @exception NameDuplicationException If the name coincides with
065     *   an attribute already in the container.
066     */
067    public TextIcon(NamedObj container, String name)
068            throws IllegalActionException, NameDuplicationException {
069        super(container, name);
070    }
071
072    ///////////////////////////////////////////////////////////////////
073    ////                         public methods                    ////
074
075    /** Clone the object into the specified workspace. The new object is
076     *  <i>not</i> added to the directory of that workspace (you must do this
077     *  yourself if you want it there).
078     *  The result is an object with no container.
079     *  @param workspace The workspace for the cloned object.
080     *  @exception CloneNotSupportedException Not thrown in this base class
081     *  @return The new Attribute.
082     */
083    @Override
084    public Object clone(Workspace workspace) throws CloneNotSupportedException {
085        TextIcon newObject = (TextIcon) super.clone(workspace);
086        return newObject;
087    }
088
089    /** Create a new default background figure, which is the text set
090     *  by setText, if it has been called, or default text if not.
091     *  This must be called in the Swing thread, or a concurrent
092     *  modification exception could occur.
093     *  @return A figure representing the specified shape.
094     */
095    @Override
096    public Figure createBackgroundFigure() {
097        // NOTE: This gets called every time that the graph gets
098        // repainted, which seems excessive to me.  This will happen
099        // every time there is a modification to the model that is
100        // carried out by a MoMLChangeRequest.
101        // The Diva graph package implements a model-view-controller
102        // architecture, which implies that this needs to return a new
103        // figure each time it is called.  The reason is that the figure
104        // may go into a different view, and transformations may be applied
105        // to that figure in that view.  However, this class needs to be
106        // able to update that figure when setShape() is called.  Hence,
107        // this class keeps a list of all the figures it has created.
108        // The references to these figures, however, have to be weak
109        // references, so that this class does not interfere with garbage
110        // collection of the figure when the view is destroyed.
111        LabelFigure newFigure;
112
113        if (_text != null) {
114            newFigure = new LabelFigure(_text, _font);
115        } else {
116            newFigure = new LabelFigure(_DEFAULT_TEXT, _font);
117        }
118
119        newFigure.setAnchor(_anchor);
120        newFigure.setFillPaint(_textColor);
121
122        _addLiveFigure(newFigure);
123        return newFigure;
124    }
125
126    /** Create a new Swing icon.  This returns an icon with the text
127     *  "-A-", or if it has been called, the text specified by
128     *  setIconText().
129     *  @see #setIconText(String)
130     *  @return A new Swing Icon.
131     */
132    @Override
133    public javax.swing.Icon createIcon() {
134        // In this class, we cache the rendered icon, since creating icons from
135        // figures is expensive.
136        if (_iconCache != null) {
137            return _iconCache;
138        }
139
140        // No cached object, so rerender the icon.
141        LabelFigure figure = new LabelFigure(_iconText, _font);
142        figure.setFillPaint(_textColor);
143        _iconCache = new FigureIcon(figure, 20, 15);
144        return _iconCache;
145    }
146
147    /** Specify origin of the text. The anchor should be one of the constants
148     *  defined in {@link SwingConstants}.
149     *  @param anchor The anchor of the text.
150     */
151    public void setAnchor(final int anchor) {
152        _anchor = anchor;
153
154        Runnable doSet = new Runnable() {
155            @Override
156            public void run() {
157                synchronized (_figures) {
158                    Iterator figures = _liveFigureIterator();
159
160                    while (figures.hasNext()) {
161                        Object figure = figures.next();
162                        ((LabelFigure) figure).setAnchor(anchor);
163                    }
164                }
165            }
166        };
167
168        Top.deferIfNecessary(doSet);
169    }
170
171    /** Specify the font to use.  This is deferred and executed
172     *  in the Swing thread.
173     *  @param font The font to use.
174     */
175    public void setFont(Font font) {
176        _font = font;
177
178        // Update the shapes of all the figures that this icon has
179        // created (which may be in multiple views). This has to be
180        // done in the Swing thread.  Assuming that createBackgroundFigure()
181        // is also called in the Swing thread, there is no possibility of
182        // conflict here where that method is trying to add to the _figures
183        // list while this method is traversing it.
184        Runnable doSet = new Runnable() {
185            @Override
186            public void run() {
187                synchronized (_figures) {
188                    Iterator figures = _liveFigureIterator();
189
190                    while (figures.hasNext()) {
191                        Object figure = figures.next();
192                        ((LabelFigure) figure).setFont(_font);
193                    }
194                }
195            }
196        };
197
198        SwingUtilities.invokeLater(doSet);
199    }
200
201    /** Specify the text to display in the icon.
202     *  If this is not called, then the text displayed
203     *  is "-A-".
204     *  @param text The text to display in the icon.
205     */
206    public void setIconText(String text) {
207        _iconText = text;
208    }
209
210    /** Specify text to display.  This is deferred and executed
211     *  in the Swing thread.
212     *  @param text The text to display.
213     */
214    public void setText(String text) {
215        _text = text;
216
217        // Update the shapes of all the figures that this icon has
218        // created (which may be in multiple views). This has to be
219        // done in the Swing thread.  Assuming that createBackgroundFigure()
220        // is also called in the Swing thread, there is no possibility of
221        // conflict here where that method is trying to add to the _figures
222        // list while this method is traversing it.
223        Runnable doSet = new Runnable() {
224            @Override
225            public void run() {
226                synchronized (_figures) {
227                    Iterator figures = _liveFigureIterator();
228
229                    while (figures.hasNext()) {
230                        Object figure = figures.next();
231                        ((LabelFigure) figure).setString(_text);
232                    }
233                }
234            }
235        };
236
237        Top.deferIfNecessary(doSet);
238    }
239
240    /** Specify the text color to use.  This is deferred and executed
241     *  in the Swing thread.
242     *  @param textColor The fill color to use.
243     */
244    public void setTextColor(Color textColor) {
245        _textColor = textColor;
246
247        // Update the shapes of all the figures that this icon has
248        // created (which may be in multiple views). This has to be
249        // done in the Swing thread.  Assuming that createBackgroundFigure()
250        // is also called in the Swing thread, there is no possibility of
251        // conflict here in adding the figure to the list of live figures.
252        Runnable doSet = new Runnable() {
253            @Override
254            public void run() {
255                synchronized (_figures) {
256                    Iterator figures = _liveFigureIterator();
257
258                    while (figures.hasNext()) {
259                        Object figure = figures.next();
260                        ((LabelFigure) figure).setFillPaint(_textColor);
261                    }
262                }
263            }
264        };
265
266        SwingUtilities.invokeLater(doSet);
267    }
268
269    ///////////////////////////////////////////////////////////////////
270    ////                         private variables                 ////
271    // Default text.
272    private String _DEFAULT_TEXT = "Double click to edit text.";
273
274    // The origin of the text. By default, the origin should be the upper left.
275    private int _anchor = SwingConstants.NORTH_WEST;
276
277    // The font to use.
278    private Font _font = new Font("SansSerif", Font.PLAIN, 12);
279
280    // Default text.
281    private String _iconText = "-A-";
282
283    // The text that is rendered.
284    private String _text;
285
286    // The specified text color.
287    private Color _textColor = Color.blue;
288}