001/* Attribute specifying a title for a model or component in a model. 002 003 Copyright (c) 2011-2018 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 */ 028 029package ptolemy.vergil.basic.export.web; 030 031import java.awt.Color; 032import java.awt.Font; 033import java.awt.GraphicsEnvironment; 034import java.util.Collection; 035 036import javax.swing.SwingConstants; 037 038import ptolemy.actor.gui.ColorAttribute; 039import ptolemy.data.BooleanToken; 040import ptolemy.data.IntToken; 041import ptolemy.data.expr.Parameter; 042import ptolemy.data.expr.SingletonParameter; 043import ptolemy.data.expr.StringParameter; 044import ptolemy.data.type.BaseType; 045import ptolemy.kernel.util.Attribute; 046import ptolemy.kernel.util.ConfigurableAttribute; 047import ptolemy.kernel.util.IllegalActionException; 048import ptolemy.kernel.util.NameDuplicationException; 049import ptolemy.kernel.util.NamedObj; 050import ptolemy.kernel.util.Settable; 051import ptolemy.kernel.util.SingletonAttribute; 052import ptolemy.kernel.util.Workspace; 053import ptolemy.vergil.icon.TextIcon; 054import ptolemy.vergil.toolbox.VisibleParameterEditorFactory; 055 056/////////////////////////////////////////////////////////////////// 057//// Title 058/** 059 * Attribute specifying a title for a model or a component in a model. 060 * This attribute provides a visual title in the model, 061 * rendered more suitably for a title than a normal annotation. 062 * Moreover, if you export to web, this is used as the title for the 063 * containing component and for any exported web page. 064 * By default, the title is not shown on the web page except 065 * as part of the image of the model. If you wish for the title 066 * to also be shown in the HTML text before the image, then set 067 * the <i>showTitleInHTML</i> parameter to true. 068 * 069 * @author Edward A. Lee 070 * @version $Id$ 071 * @since Ptolemy II 10.0 072 * @Pt.ProposedRating Red (cxh) 073 * @Pt.AcceptedRating Red (cxh) 074 */ 075public class Title extends StringParameter implements WebExportable { 076 077 /** Create an instance of this parameter. 078 * @param container The container. 079 * @param name The name. 080 * @exception IllegalActionException If the superclass throws it. 081 * @exception NameDuplicationException If the superclass throws it. 082 */ 083 public Title(NamedObj container, String name) 084 throws IllegalActionException, NameDuplicationException { 085 super(container, name); 086 087 // Unfortunately, much of this class is copied from AbstractTextAttribute, 088 // but we have no choice because we need it to be a StringParameter. 089 090 _icon = new TextIcon(this, "_icon"); 091 _icon.setPersistent(false); 092 _icon.setIconText("T"); 093 094 showTitleInHTML = new Parameter(this, "showTitleInHTML"); 095 showTitleInHTML.setExpression("false"); 096 showTitleInHTML.setTypeEquals(BaseType.BOOLEAN); 097 098 textSize = new Parameter(this, "textSize"); 099 textSize.setExpression("24"); 100 textSize.setTypeEquals(BaseType.INT); 101 textSize.addChoice("9"); 102 textSize.addChoice("10"); 103 textSize.addChoice("11"); 104 textSize.addChoice("12"); 105 textSize.addChoice("14"); 106 textSize.addChoice("18"); 107 textSize.addChoice("24"); 108 textSize.addChoice("32"); 109 110 textColor = new ColorAttribute(this, "textColor"); 111 textColor.setExpression("{0.0, 0.0, 0.0, 1.0}"); 112 113 // Get font family names from the Font class in Java. 114 // This includes logical font names, per Font class in Java: 115 // Dialog, DialogInput, Monospaced, Serif, SansSerif, or Symbol. 116 fontFamily = new StringParameter(this, "fontFamily"); 117 fontFamily.setExpression("SansSerif"); 118 119 String[] families = GraphicsEnvironment.getLocalGraphicsEnvironment() 120 .getAvailableFontFamilyNames(); 121 122 for (String familie : families) { 123 fontFamily.addChoice(familie); 124 } 125 126 bold = new Parameter(this, "bold"); 127 bold.setExpression("false"); 128 bold.setTypeEquals(BaseType.BOOLEAN); 129 130 italic = new Parameter(this, "italic"); 131 italic.setExpression("false"); 132 italic.setTypeEquals(BaseType.BOOLEAN); 133 134 center = new Parameter(this, "center"); 135 center.setToken(BooleanToken.FALSE); 136 center.setTypeEquals(BaseType.BOOLEAN); 137 138 // Hide the name. 139 SingletonParameter hide = new SingletonParameter(this, "_hideName"); 140 hide.setToken(BooleanToken.TRUE); 141 hide.setVisibility(Settable.EXPERT); 142 143 // No need to display any parameters when the "_showParameters" 144 // preference asks for such display because presumably all the 145 // parameters are reflected in the visual display already. 146 Parameter hideAllParameters = new Parameter(this, "_hideAllParameters"); 147 hideAllParameters.setVisibility(Settable.EXPERT); 148 hideAllParameters.setExpression("true"); 149 150 // The following ensures that double click edits the text of the title. 151 new VisibleParameterEditorFactory(this, "_editorFactory"); 152 153 // Add a small icon. 154 ConfigurableAttribute smallIcon = new ConfigurableAttribute(this, 155 "_smallIconDescription"); 156 try { 157 smallIcon.configure(null, null, 158 "<svg><text x=\"20\" style=\"font-size:14; font-family:SansSerif; fill:blue\" y=\"20\">title</text></svg>"); 159 } catch (Exception e) { 160 // Show exception on the console. Should not occur. 161 e.printStackTrace(); 162 } 163 } 164 165 /////////////////////////////////////////////////////////////////// 166 //// parameters //// 167 168 /** A boolean indicating whether the font should be bold. 169 * This defaults to false. 170 */ 171 public Parameter bold; 172 173 /** A boolean parameter that controls whether the origin of the text is 174 * center (if true) or north-west. 175 */ 176 public Parameter center; 177 178 /** The font family. This is a string that defaults to "SansSerif". 179 */ 180 public StringParameter fontFamily; 181 182 /** A boolean indicating whether the font should be italic. 183 * This defaults to false. 184 */ 185 public Parameter italic; 186 187 /** If set to true, then the title given by this parameter 188 * will be shown in the HTML prior to the image of the model 189 * (as well as in the image of the model, if it is visible 190 * when the export to web occurs). This is a boolean that 191 * defaults to false. 192 */ 193 public Parameter showTitleInHTML; 194 195 /** The text color. This is a string representing an array with 196 * four elements, red, green, blue, and alpha, where alpha is 197 * transparency. The default is "{0.0, 0.0, 0.0, 1.0}", which 198 * represents an opaque black. 199 */ 200 public ColorAttribute textColor; 201 202 /** The text size. This is an int that defaults to 14. 203 */ 204 public Parameter textSize; 205 206 /////////////////////////////////////////////////////////////////// 207 //// public methods //// 208 209 /** React to a changes in the attributes by changing the icon. 210 * @param attribute The attribute that changed. 211 * @exception IllegalActionException If the change is not acceptable 212 * to this container (should not be thrown). 213 */ 214 @Override 215 public void attributeChanged(Attribute attribute) 216 throws IllegalActionException { 217 if (attribute == center) { 218 if (((BooleanToken) center.getToken()).booleanValue()) { 219 _icon.setAnchor(SwingConstants.CENTER); 220 } else { 221 _icon.setAnchor(SwingConstants.NORTH_WEST); 222 } 223 } else if ((attribute == fontFamily || attribute == textSize 224 || attribute == bold || attribute == italic) 225 && !_inAttributeChanged) { 226 try { 227 // Prevent redundant actions here... When we evaluate the 228 // _other_ attribute here (whichever one did _not_ trigger 229 // this call, it will likely trigger another call to 230 // attributeChanged(), which will result in this action 231 // being performed twice. 232 _inAttributeChanged = true; 233 234 int sizeValue = ((IntToken) textSize.getToken()).intValue(); 235 String familyValue = fontFamily.stringValue(); 236 int styleValue = Font.PLAIN; 237 238 if (((BooleanToken) bold.getToken()).booleanValue()) { 239 styleValue = styleValue | Font.BOLD; 240 } 241 242 if (((BooleanToken) italic.getToken()).booleanValue()) { 243 styleValue = styleValue | Font.ITALIC; 244 } 245 246 Font fontValue = new Font(familyValue, styleValue, sizeValue); 247 _icon.setFont(fontValue); 248 } finally { 249 _inAttributeChanged = false; 250 } 251 } else if (attribute == textColor) { 252 Color colorValue = textColor.asColor(); 253 _icon.setTextColor(colorValue); 254 } else { 255 super.attributeChanged(attribute); 256 } 257 } 258 259 /** Clone the object into the specified workspace. 260 * @param workspace The workspace for the new object. 261 * @return A new AbstractTextAttribute. 262 * @exception CloneNotSupportedException If any of the attributes 263 * cannot be cloned. 264 */ 265 @Override 266 public Object clone(Workspace workspace) throws CloneNotSupportedException { 267 Title result = (Title) super.clone(workspace); 268 result._icon = (TextIcon) result.getAttribute("_icon"); 269 return result; 270 } 271 272 /** A title is of type text/html. 273 * 274 * @return The string text/html 275 */ 276 @Override 277 public String getMimeType() { 278 return "text/html"; 279 } 280 281 /** Return true, since new title content should overwrite old title content. 282 * 283 * @return True, since new title content should overwrite old title content. 284 */ 285 @Override 286 public boolean isOverwriteable() { 287 return true; 288 } 289 290 /** Move this object to the first position in the list 291 * of attributes of the container. This overrides the base 292 * class to create an attribute named "_renderFirst" and to 293 * remove an attribute named "_renderLast", if it is present. 294 * This attribute is recognized by vergil, which then renders this 295 * attribute before entities, connections, and other attributes. 296 * This method gets write access on workspace 297 * and increments the version. 298 * @return The index of the specified object prior to moving it, 299 * or -1 if it is not moved. 300 * @exception IllegalActionException If this object has 301 * no container. 302 */ 303 @Override 304 public int moveToFirst() throws IllegalActionException { 305 try { 306 new SingletonAttribute(this, "_renderFirst"); 307 Attribute renderLast = getAttribute("_renderLast"); 308 309 if (renderLast != null) { 310 renderLast.setContainer(null); 311 } 312 } catch (NameDuplicationException e) { 313 // Ignore. This will result in a rendering error, 314 // but that is better than trashing user data. 315 } 316 317 return super.moveToFirst(); 318 } 319 320 /** Move this object to the last position in the list 321 * of attributes of the container. This overrides the base 322 * class to create an attribute named "_renderLast" and to 323 * remove an attribute named "_renderFirst" if it is present. 324 * This attribute is recognized by vergil, which then renders this 325 * attribute after entities, connections, and other attributes. 326 * This method gets write access on workspace 327 * and increments the version. 328 * @return The index of the specified object prior to moving it, 329 * or -1 if it is not moved. 330 * @exception IllegalActionException If this object has 331 * no container. 332 */ 333 @Override 334 public int moveToLast() throws IllegalActionException { 335 try { 336 new SingletonAttribute(this, "_renderLast"); 337 Attribute renderFirst = getAttribute("_renderFirst"); 338 339 if (renderFirst != null) { 340 renderFirst.setContainer(null); 341 } 342 } catch (NameDuplicationException e) { 343 // Ignore. This will result in a rendering error, 344 // but that is better than trashing user data. 345 } 346 347 return super.moveToLast(); 348 } 349 350 /** Return a title for the model. The title can be returned as an attribute 351 * or an element, but not a document. 352 * 353 * @param exporter The web exporter to which to write content. 354 * @exception IllegalActionException If there is a problem creating 355 * the content or setting the attribute. 356 */ 357 @Override 358 public void provideContent(WebExporter exporter) 359 throws IllegalActionException { 360 // Provide a WebElement containing the title. Title does not 361 // provide any WebAttributes. 362 //_provideElements(exporter); 363 _provideAttributes(exporter); 364 } 365 366 /** Provide a title for this object to the specified web exporter. 367 * 368 * @param exporter The WebExporter to add content to 369 * @exception IllegalActionException If something is wrong with the 370 * specification of the content. 371 */ 372 protected void _provideAttributes(WebExporter exporter) 373 throws IllegalActionException { 374 375 // FIXME: Refactor so we don't need this method 376 exporter.setTitle(stringValue(), 377 ((BooleanToken) showTitleInHTML.getToken()).booleanValue()); 378 379 // Create a WebAttribute for title and add to exporter. 380 // Content should only be added once (onceOnly -> true). 381 /* title attribute is now used for displaying parameter table. 382 WebAttribute webAttribute = WebAttribute.createWebAttribute( 383 getContainer(), "titleWebAttribute", "title"); 384 webAttribute.setExpression(stringValue()); 385 exporter.defineAttribute(webAttribute, true); 386 */ 387 } 388 389 /** Provide the <title> </title> element to the specified web exporter. 390 * This element should be included in the <head> section. 391 * 392 * @param exporter The WebExporter to add content to 393 * @exception IllegalActionException If something is wrong with the 394 * specification of the content. 395 */ 396 397 /* 398 protected void _provideElements(WebExporter exporter) 399 throws IllegalActionException { 400 401 // Create a WebElement for title and add to exporter. 402 // Content should only be added once (onceOnly -> true). 403 WebElement webElement = WebElement. 404 createWebElement(getContainer(), "titleWebElement", "title"); 405 webElement.setParent(WebElement.HEAD); 406 webElement.setExpression(stringValue()); 407 exporter.defineElement(webElement, true); 408 } 409 */ 410 411 /** Override the base class to set the text to be displayed 412 * in the icon. 413 */ 414 @Override 415 public Collection validate() throws IllegalActionException { 416 Collection result = super.validate(); 417 _icon.setText(stringValue()); 418 return result; 419 } 420 421 /////////////////////////////////////////////////////////////////// 422 //// protected members //// 423 424 /** The text icon. */ 425 protected TextIcon _icon; 426 427 /////////////////////////////////////////////////////////////////// 428 //// private variables //// 429 private boolean _inAttributeChanged = false; 430}