001/* An icon that renders the name of the container. 002 003 Copyright (c) 2006-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.Font; 031import java.awt.Paint; 032import java.awt.geom.Point2D; 033import java.awt.geom.Rectangle2D; 034 035import javax.swing.Icon; 036import javax.swing.SwingConstants; 037 038import diva.canvas.CompositeFigure; 039import diva.canvas.Figure; 040import diva.canvas.toolbox.BasicRectangle; 041import diva.canvas.toolbox.LabelFigure; 042import diva.canvas.toolbox.RoundedRectangle; 043import diva.gui.toolbox.FigureIcon; 044import ptolemy.actor.gui.ColorAttribute; 045import ptolemy.data.BooleanToken; 046import ptolemy.data.DoubleToken; 047import ptolemy.data.expr.Parameter; 048import ptolemy.data.expr.SingletonParameter; 049import ptolemy.data.type.BaseType; 050import ptolemy.kernel.util.Attribute; 051import ptolemy.kernel.util.IllegalActionException; 052import ptolemy.kernel.util.NameDuplicationException; 053import ptolemy.kernel.util.NamedObj; 054import ptolemy.kernel.util.Settable; 055import ptolemy.vergil.toolbox.SnapConstraint; 056 057/////////////////////////////////////////////////////////////////// 058//// NameIcon 059 060/** 061 An icon that displays the name of the container in an appropriately 062 sized box. Put this into a composite actor or in any actor to 063 convert the icon for that actor into a simple box with the name 064 of the actor instance. You will probably also want to set the 065 actor instance to not display its name above its icon. You can 066 do that via the Customize Name dialog (obtained by right clicking 067 on the icon) or by creating a parameter named "_hideName" with 068 value true. 069 070 @author Edward A. Lee 071 @version $Id$ 072 @since Ptolemy II 5.2 073 @Pt.ProposedRating Yellow (eal) 074 @Pt.AcceptedRating Red (johnr) 075 */ 076public class NameIcon extends EditorIcon { 077 078 /** Create a new icon with the given name in the given container. 079 * The container is required to implement Settable, or an exception 080 * will be thrown. 081 * @param container The container for this attribute. 082 * @param name The name of this attribute. 083 * @exception IllegalActionException If thrown by the parent 084 * class or while setting an attribute. 085 * @exception NameDuplicationException If the name coincides with 086 * an attribute already in the container. 087 */ 088 public NameIcon(NamedObj container, String name) 089 throws NameDuplicationException, IllegalActionException { 090 super(container, name); 091 092 // Create an icon for this attribute. 093 // This has the side effect of making it visible 094 // in Vergil, and giving a reasonable rendition. 095 TextIcon icon = new TextIcon(this, "_icon"); 096 icon.setIconText("-N-"); 097 icon.setText( 098 "NameIcon attribute: This sets the icon to be a box with the name."); 099 icon.setPersistent(false); 100 101 // Hide the name. 102 SingletonParameter hide = new SingletonParameter(this, "_hideName"); 103 hide.setToken(BooleanToken.TRUE); 104 hide.setVisibility(Settable.EXPERT); 105 106 color = new ColorAttribute(this, "color"); 107 color.setExpression("{1.0,1.0,1.0,1.0}"); 108 109 rounding = new Parameter(this, "rounding"); 110 rounding.setTypeEquals(BaseType.DOUBLE); 111 rounding.setExpression("0.0"); 112 113 spacing = new Parameter(this, "spacing"); 114 spacing.setTypeEquals(BaseType.DOUBLE); 115 spacing.setExpression("0.0"); 116 } 117 118 /////////////////////////////////////////////////////////////////// 119 //// parameters //// 120 121 /** The background color to use in the box. 122 * This defaults to white. 123 */ 124 public ColorAttribute color; 125 126 /** The amount of rounding of the corners. 127 * This is a double that defaults to 0.0, which indicates no rounding. 128 */ 129 public Parameter rounding; 130 131 /** If greater than zero, then use a double box where the outside 132 * one is the specified size larger than the inside one. 133 * This is a double that defaults to 0.0, which indicates a single 134 * box. 135 */ 136 public Parameter spacing; 137 138 /////////////////////////////////////////////////////////////////// 139 //// public methods //// 140 141 /** React to a changes in the attributes by changing 142 * the icon. 143 * @param attribute The attribute that changed. 144 * @exception IllegalActionException If the change is not acceptable 145 * to this container (should not be thrown). 146 */ 147 @Override 148 public void attributeChanged(Attribute attribute) 149 throws IllegalActionException { 150 if (attribute == rounding) { 151 // Make sure that the new rounding value is valid. 152 double roundingValue = ((DoubleToken) rounding.getToken()) 153 .doubleValue(); 154 155 if (roundingValue < 0.0) { 156 throw new IllegalActionException(this, 157 "Invalid rounding value. Required to be non-negative."); 158 } 159 160 if (roundingValue != _roundingValue) { 161 _roundingValue = roundingValue; 162 } 163 } else if (attribute == spacing) { 164 // Make sure that the new spacing value is valid. 165 double spacingValue = ((DoubleToken) spacing.getToken()) 166 .doubleValue(); 167 168 if (spacingValue < 0.0) { 169 throw new IllegalActionException(this, 170 "Invalid spacing value. Required to be non-negative."); 171 } 172 173 if (spacingValue != _spacingValue) { 174 _spacingValue = spacingValue; 175 } 176 } else { 177 super.attributeChanged(attribute); 178 } 179 } 180 181 /** Create a new background figure. This overrides the base class 182 * to draw a box around the value display, where the width of the 183 * box depends on the value. 184 * @return A new figure. 185 */ 186 @Override 187 public Figure createBackgroundFigure() { 188 Point2D size = _getBackgroundSize(); 189 double width = size.getX(); 190 double height = size.getY(); 191 192 if (_spacingValue == 0.0) { 193 if (_roundingValue == 0.0) { 194 return new BasicRectangle(0, 0, width, height, _getFill(), 195 _getLineWidth()); 196 } else { 197 RoundedRectangle result = new RoundedRectangle(0, 0, width, 198 height, _getFill(), _getLineWidth(), _roundingValue, 199 _roundingValue); 200 // FIXME: For book. 201 // result.setStrokePaint(Color.WHITE); 202 return result; 203 } 204 } else { 205 CompositeFigure result; 206 if (_roundingValue == 0.0) { 207 result = new CompositeFigure(new BasicRectangle(-_spacingValue, 208 -_spacingValue, width + 2 * _spacingValue, 209 height + 2 * _spacingValue, null, _getLineWidth())); 210 result.add(new BasicRectangle(0, 0, width, height, _getFill(), 211 _getLineWidth())); 212 } else { 213 result = new CompositeFigure(new RoundedRectangle( 214 -_spacingValue, -_spacingValue, 215 width + 2 * _spacingValue, height + 2 * _spacingValue, 216 null, _getLineWidth(), _roundingValue + _spacingValue, 217 _roundingValue + _spacingValue)); 218 result.add(new RoundedRectangle(0, 0, width, height, _getFill(), 219 _getLineWidth(), _roundingValue, _roundingValue)); 220 } 221 return result; 222 } 223 } 224 225 /** Create a new Diva figure that visually represents this icon. 226 * The figure will be an instance of LabelFigure that renders the 227 * name of the container. 228 * @return A new CompositeFigure consisting of the label. 229 */ 230 @Override 231 public Figure createFigure() { 232 CompositeFigure result = (CompositeFigure) super.createFigure(); 233 234 String name = "No Name"; 235 NamedObj container = getContainer(); 236 if (container != null) { 237 name = container.getDisplayName(); 238 } 239 LabelFigure label = _getLabel(result, name); 240 result.add(label); 241 return result; 242 } 243 244 /** Create an icon. 245 * 246 * @return The icon. 247 */ 248 @Override 249 public Icon createIcon() { 250 if (_iconCache != null) { 251 return _iconCache; 252 } 253 254 RoundedRectangle figure = new RoundedRectangle(0, 0, 20, 10, _getFill(), 255 1.0f, 5.0, 5.0); 256 _iconCache = new FigureIcon(figure, 20, 15); 257 return _iconCache; 258 } 259 260 /** Override the base class to add or set a _hideName parameter. 261 * @param container The container to attach this attribute to.. 262 * @exception IllegalActionException If this attribute is not of the 263 * expected class for the container, or it has no name, 264 * or the attribute and container are not in the same workspace, or 265 * the proposed container would result in recursive containment. 266 * @exception NameDuplicationException If the container already has 267 * an attribute with the name of this attribute. 268 */ 269 @Override 270 public void setContainer(NamedObj container) 271 throws IllegalActionException, NameDuplicationException { 272 NamedObj previousContainer = getContainer(); 273 if (previousContainer != container && previousContainer != null) { 274 SingletonParameter hide = (SingletonParameter) previousContainer 275 .getAttribute("_hideName", SingletonParameter.class); 276 if (hide != null) { 277 hide.setToken(BooleanToken.FALSE); 278 } 279 } 280 super.setContainer(container); 281 if (previousContainer != container && container != null) { 282 // Hide the name. 283 SingletonParameter hide = new SingletonParameter(container, 284 "_hideName"); 285 hide.setToken(BooleanToken.TRUE); 286 hide.setVisibility(Settable.EXPERT); 287 } 288 } 289 290 /////////////////////////////////////////////////////////////////// 291 //// protected methods //// 292 293 /** Return the background size. 294 * @return the background size. 295 */ 296 protected Point2D _getBackgroundSize() { 297 String name = "No Name"; 298 NamedObj container = getContainer(); 299 if (container != null) { 300 name = container.getDisplayName(); 301 } 302 303 double width = 60; 304 double height = 30; 305 306 // Measure width of the text. Unfortunately, this 307 // requires generating a label figure that we will not use. 308 LabelFigure label = new LabelFigure(name, _labelFont, 1.0, 309 SwingConstants.CENTER); 310 Rectangle2D stringBounds = label.getBounds(); 311 312 // NOTE: Padding of 20. 313 width = Math.floor(stringBounds.getWidth()) + _xPadding; 314 height = Math.floor(stringBounds.getHeight()) + _yPadding; 315 // Quantize the height so that snap to grid still works. 316 // Round up so as to never overlap the text. 317 double defaultResolution = SnapConstraint.getDefaultResolution(); 318 height = (Math.floor(height / defaultResolution) + 1) 319 * defaultResolution; 320 // Quantize the width to an even multiple of the resolution. 321 // The icon is centered on snap to grid, so this ensures that both 322 // ends align with the grid. 323 width = Math 324 .floor((width + defaultResolution) / (2.0 * defaultResolution)) 325 * 2.0 * defaultResolution; 326 327 return new Point2D.Double(width, height); 328 } 329 330 /** Return the paint to use to fill the icon. 331 * This base class returns the value of the <i>color</i> attribute. 332 * @return The paint to use to fill the icon. 333 */ 334 protected Paint _getFill() { 335 return color.asColor(); 336 } 337 338 /** Get the label to put on the specified background 339 * figure based on the specified name. 340 * @param background The background figure on which to put the label. 341 * @param name The name on which to base the label. 342 * @return The label figure. 343 */ 344 protected LabelFigure _getLabel(CompositeFigure background, String name) { 345 // FIXME: For book. 346 // LabelFigure label = new LabelFigure(name, _labelFont, 1.0, SwingConstants.CENTER, Color.WHITE); 347 LabelFigure label = new LabelFigure(name, _labelFont, 1.0, 348 SwingConstants.CENTER); 349 Rectangle2D backBounds = background.getBackgroundFigure().getBounds(); 350 label.translateTo(backBounds.getCenterX(), backBounds.getCenterY()); 351 return label; 352 } 353 354 /** Return the line width to use in rendering the box. 355 * This base class returns 1.0f. 356 * @return The line width to use in rendering the box. 357 */ 358 protected float _getLineWidth() { 359 // FIXME: For book: 360 // return 3.0f; 361 return 1.0f; 362 } 363 364 /////////////////////////////////////////////////////////////////// 365 //// protected members //// 366 367 /** The font used. */ 368 protected static final Font _labelFont = new Font("SansSerif", Font.PLAIN, 369 12); 370 371 /** Most recent value of the rounding parameter. */ 372 protected double _roundingValue = 0.0; 373 374 /** Most recent value of the spacing parameter. */ 375 protected double _spacingValue = 0.0; 376 377 /** The horizontal padding on the left and right sides of the name. */ 378 protected double _xPadding = 20.0; 379 380 /** The vertical padding above and below the name. */ 381 protected double _yPadding = 8.0; 382}