/* An Icon is the graphical representation of an entity or attribute. Copyright (c) 1999-2014 The Regents of the University of California. All rights reserved. Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. PT_COPYRIGHT_VERSION_2 COPYRIGHTENDKEY */ package ptolemy.vergil.icon; import java.awt.Color; import java.awt.Font; import java.awt.geom.Rectangle2D; import java.util.Iterator; import java.util.List; import javax.swing.Icon; import javax.swing.SwingConstants; import ptolemy.actor.gui.PtolemyPreferences; import ptolemy.data.BooleanToken; import ptolemy.data.StringToken; import ptolemy.data.Token; import ptolemy.data.expr.Parameter; import ptolemy.kernel.util.Attribute; import ptolemy.kernel.util.IllegalActionException; import ptolemy.kernel.util.InternalErrorException; import ptolemy.kernel.util.Locatable; import ptolemy.kernel.util.NameDuplicationException; import ptolemy.kernel.util.Nameable; import ptolemy.kernel.util.NamedObj; import ptolemy.kernel.util.RelativeLocation; import ptolemy.kernel.util.Settable; import ptolemy.kernel.util.Workspace; import ptolemy.vergil.basic.RelativeLocatable; import ptolemy.vergil.kernel.RelativeLinkFigure; import ptolemy.vergil.kernel.attributes.FilledShapeAttribute; import diva.canvas.CanvasUtilities; import diva.canvas.CompositeFigure; import diva.canvas.Figure; import diva.canvas.toolbox.BasicFigure; import diva.canvas.toolbox.BasicRectangle; import diva.canvas.toolbox.LabelFigure; import diva.gui.toolbox.FigureIcon; /////////////////////////////////////////////////////////////////// //// EditorIcon /** An icon is the visual representation of an entity or attribute. The visual representation is a Diva Figure. This class is an attribute that serves as a factory for such figures. This base class creates the figure by composing the figures of any contained attributes that have icons. If there are no such contained attributes, then it creates a default figure that is a white rectangle. This class also provides a facility for generating a Swing icon (i.e. an instance of javax.swing.Icon) from that figure (the createIcon() method).
The icon consists of a background figure, created by the createBackgroundFigure() method, and a decorated version, created by the createFigure() method. The decorated version has, in this base class, a label showing the name of the entity, unless the entity contains a parameter called "_hideName" with value true. The swing icon created by createIcon() does not include the decorations, but rather is only the background figure.
The decorated version can also optionally show parameter values below the icon. If the preference named "_showParameters" has value "All", then all parameters are shown. If it has value "Overridden parameters only", then it will show only overridden parameters. In either case, only parameters that are visible and settable (see the Settable interface) will be shown, regardless of whether they are overridden.
When the preference "_showParameters" has value "Overridden parameters only", then some parameter values may be suppressed even if they are overridden. In particular, if an attribute contains a parameter named "_hide" with value true, then that parameter is not shown even if requested. If the container of the attribute contains a parameter named "_hideAllParameters" with value true, then none of its parameters are shown. This is useful, for example, if the icon itself shows the parameter, as with decorative visual elements.
Derived classes may simply populate this attribute with other visible attributes (attributes that contain icons), or they can override the createBackgroundFigure() method. This will affect both the Diva Figure and the Swing Icon representations. Derived classes can also create the figure or the icon in a different way entirely (for example, starting with a Swing icon and creating the figure using a SwingWrapper) by overriding both createBackgroundFigure() and createIcon(). However, the icon editor provided by EditIconFrame and EditIconTableau will only show (and allow editing) of those icon components created by populating this attribute with other visible attributes.
This attribute contains another attribute that is an
instance of EditIconTableau. This has the effect that
an instance of Configuration, when it attempts to open
an instance of this class, will use EditIconTableau,
which in turn uses EditIconFrame to provide an icon
editor.
@author Steve Neuendorffer, John Reekie, Edward A. Lee
@version $Id: EditorIcon.java 69607 2014-07-30 17:07:26Z cxh $
@since Ptolemy II 2.0
@Pt.ProposedRating Yellow (neuendor)
@Pt.AcceptedRating Red (johnr)
@see EditIconFrame
@see EditIconTableau
@see ptolemy.actor.gui.Configuration
*/
public class EditorIcon extends Attribute {
/** Construct an icon in the specified workspace and name.
* This constructor is typically used in conjunction with
* setContainerToBe() and createFigure() to create an icon
* and generate a figure without having to have write access
* to the workspace.
* If the workspace argument is null, then use the default workspace.
* The object is added to the directory of the workspace.
* @see #setContainerToBe(NamedObj)
* Increment the version number of the workspace.
* @param workspace The workspace that will list the attribute.
* @param name The name of this attribute.
* @exception IllegalActionException If the specified name contains
* a period.
*/
public EditorIcon(Workspace workspace, String name)
throws IllegalActionException {
super(workspace);
try {
setName(name);
// Create a tableau factory so that an instance
// of this class is opened using the EditIconTableau.
Attribute tableauFactory = new EditIconTableau.Factory(this,
"_tableauFactory");
tableauFactory.setPersistent(false);
} catch (NameDuplicationException ex) {
throw new InternalErrorException(ex);
}
}
/** Create a new icon with the given name in the given container.
* @param container The container.
* @param name The name of the attribute.
* @exception IllegalActionException If the attribute is not of an
* acceptable class for the container.
* @exception NameDuplicationException If the name coincides with
* an attribute already in the container.
*/
public EditorIcon(NamedObj container, String name)
throws IllegalActionException, NameDuplicationException {
super(container, name);
// Create a tableau factory so that an instance
// of this class is opened using the EditIconTableau.
Attribute tableauFactory = new EditIconTableau.Factory(this,
"_tableauFactory");
tableauFactory.setPersistent(false);
}
///////////////////////////////////////////////////////////////////
//// public methods ////
/** Clone the object into the specified workspace. The new object is
* not added to the directory of that workspace (you must do this
* yourself if you want it there).
* The result is an object with no container.
* @param workspace The workspace for the cloned object.
* @exception CloneNotSupportedException Not thrown in this base class
* @return The new Attribute.
*/
@Override
public Object clone(Workspace workspace) throws CloneNotSupportedException {
EditorIcon newObject = (EditorIcon) super.clone(workspace);
newObject._containerToBe = null;
newObject._iconCache = null;
return newObject;
}
/** Create a new background figure. This figure is a composition of
* the figures of any contained visible attributes. If there are no such
* visible attributes, then this figure is a simple white box.
* If you override this method, keep in mind that this method is expected
* to manufacture a new figure each time, since figures are
* inexpensive and contain their own location and transformations.
* This method should never return null.
* @return A new figure.
*/
public Figure createBackgroundFigure() {
// If this icon itself contains any visible attributes, then
// compose their background figures to make this one.
CompositeFigure figure = null;
Iterator attributes = attributeList().iterator();
while (attributes.hasNext()) {
Attribute attribute = (Attribute) attributes.next();
// There is a level of indirection where the "subIcon" is a
// "visible attribute" containing an attribute named "_icon"
// that actually has the icon.
Iterator subIcons = attribute.attributeList(EditorIcon.class)
.iterator();
while (subIcons.hasNext()) {
EditorIcon subIcon = (EditorIcon) subIcons.next();
if (figure == null) {
// NOTE: This used to use a constructor that
// takes a "background figure" argument, which would
// then treat this first figure specially. This is not
// right since getShape() on the figure will then return
// only the shape of the composite, which results in the
// wrong selection region being highlighted.
figure = new CompositeFigure();
}
Figure subFigure = subIcon.createBackgroundFigure();
// Translate the figure to the location of the subfigure,
// if there is a location. Also, center it if necessary.
try {
// NOTE: This is inelegant, but only the subclasses
// have the notion of centering.
// FIXME: Don't use FilledShapeAttribute... promote
// centered capability to a base class.
if (attribute instanceof FilledShapeAttribute
&& subFigure instanceof BasicFigure) {
boolean centeredValue = ((BooleanToken) ((FilledShapeAttribute) attribute).centered
.getToken()).booleanValue();
if (centeredValue) {
((BasicFigure) subFigure).setCentered(true);
}
}
Locatable location = (Locatable) attribute.getAttribute(
"_location", Locatable.class);
if (location != null) {
double[] locationValue = location.getLocation();
CanvasUtilities.translateTo(subFigure,
locationValue[0], locationValue[1]);
}
} catch (IllegalActionException e) {
throw new InternalErrorException(e);
}
figure.add(subFigure);
}
}
if (figure == null) {
// There are no component figures.
return _createDefaultBackgroundFigure();
} else {
return figure;
}
}
/** Create a new Diva figure that visually represents this icon.
* The figure will be an instance of CompositeFigure with the
* figure returned by createBackgroundFigure() as its background.
* This method adds a LabelFigure to the CompositeFigure that
* contains the name of the container of this icon, unless the
* container has a parameter called "_hideName" with value true.
* If the container has an attribute called "_centerName" with
* value true, then the name is rendered
* in the center of the background figure, rather than above it.
* This method should never return null, even if the icon has
* not been properly initialized.
* @return A new CompositeFigure consisting of the background figure
* and a label.
*/
public Figure createFigure() {
Figure background = createBackgroundFigure();
Rectangle2D backBounds;
try {
// Applets can throw a NPE here if there are problems getting
// the image.
backBounds = background.getBounds();
} catch (Exception ex) {
throw new InternalErrorException(this, ex,
"Failed to get the bounds of the background figure \""
+ (background == null ? "null" : background));
}
CompositeFigure figure = new CompositeFigure(background);
NamedObj container = (NamedObj) getContainerOrContainerToBe();
// RelativeLocatables are drawn with a line that indicates to which object
// they are connected. This line is drawn by RelativeLinkFigure.
if (container instanceof RelativeLocatable) {
List