001/* An attribute that causes look inside to open a text editor to 002 edit a string attribute in the container. 003 004 Copyright (c) 2003-2016 The Regents of the University of California. 005 All rights reserved. 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the above 009 copyright notice and the following two paragraphs appear in all copies 010 of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 ENHANCEMENTS, OR MODIFICATIONS. 024 025 PT_COPYRIGHT_VERSION_2 026 COPYRIGHTENDKEY 027 028 029 */ 030package ptolemy.vergil.toolbox; 031 032import java.lang.reflect.Constructor; 033import java.util.Iterator; 034 035import javax.swing.text.Document; 036 037import ptolemy.actor.gui.Effigy; 038import ptolemy.actor.gui.PtolemyEffigy; 039import ptolemy.actor.gui.Tableau; 040import ptolemy.actor.gui.TableauFactory; 041import ptolemy.actor.gui.TextEditorTableau; 042import ptolemy.actor.gui.TextEffigy; 043import ptolemy.data.IntToken; 044import ptolemy.data.StringToken; 045import ptolemy.data.Token; 046import ptolemy.data.expr.Parameter; 047import ptolemy.data.expr.Variable; 048import ptolemy.data.type.BaseType; 049import ptolemy.kernel.util.Attribute; 050import ptolemy.kernel.util.IllegalActionException; 051import ptolemy.kernel.util.NameDuplicationException; 052import ptolemy.kernel.util.NamedObj; 053import ptolemy.kernel.util.StringAttribute; 054 055/////////////////////////////////////////////////////////////////// 056//// TextEditorTableauFactory 057 058/** 059 This class is an attribute that creates a text editor to edit a specified 060 string attribute in the container of this attribute. It is similar to 061 TextEditorConfigureFactory, but instead of opening when the actor is configured, 062 it is opened when the user looks inside the actor. 063 064 @author Edward A. Lee 065 @version $Id$ 066 @since Ptolemy II 4.0 067 @Pt.ProposedRating Yellow (eal) 068 @Pt.AcceptedRating Red (ptolemy) 069 @see TextEditorConfigureFactory 070 */ 071public class TextEditorTableauFactory extends TableauFactory 072 implements TextEditorFactory { 073 /** Create a factory with the given name and container. 074 * @param container The container. 075 * @param name The name. 076 * @exception IllegalActionException If the container is incompatible 077 * with this attribute. 078 * @exception NameDuplicationException If the name coincides with 079 * an attribute already in the container. 080 */ 081 public TextEditorTableauFactory(NamedObj container, String name) 082 throws IllegalActionException, NameDuplicationException { 083 super(container, name); 084 085 attributeName = new StringAttribute(this, "attributeName"); 086 087 columnsDisplayed = new Parameter(this, "columnsDisplayed"); 088 columnsDisplayed.setTypeEquals(BaseType.INT); 089 columnsDisplayed.setExpression("80"); 090 091 rowsDisplayed = new Parameter(this, "rowsDisplayed"); 092 rowsDisplayed.setTypeEquals(BaseType.INT); 093 rowsDisplayed.setExpression("40"); 094 095 syntaxStyle = new StringAttribute(this, "syntaxStyle"); 096 } 097 098 /////////////////////////////////////////////////////////////////// 099 //// parameters //// 100 101 /** The name of the string attribute that is to be edited. */ 102 public StringAttribute attributeName; 103 104 /** The horizontal size of the display, in columns. This contains 105 * an integer, and defaults to 80. 106 */ 107 public Parameter columnsDisplayed; 108 109 /** The vertical size of the display, in rows. This contains an 110 * integer, and defaults to 40. 111 */ 112 public Parameter rowsDisplayed; 113 114 /** The style of the text to be edited. This may or may not be 115 * supported. If the package "org.fife.ui.rsyntaxtextarea" is found in 116 * the classpath, then the supported styles include 117 * "text/plain", "text/c", "text/clojure", "text/cpp", "text/cs", 118 * "text/css", "text/dtd", "text/fortran", 119 * "text/groovy", "text/html", "text/java", 120 * "text/javascript", "text/json", "text/jsp", 121 * "text/latex", "text/makefile", 122 * "text/perl", "text/php", 123 * "text/properties", "text/python", "text/ruby", "text/sas", 124 * "text/scala", "text/sql", "text/tcl", "text/unix", "text/vb", 125 * "text/bat", and "text/xml". 126 */ 127 public StringAttribute syntaxStyle; 128 129 /////////////////////////////////////////////////////////////////// 130 //// public methods //// 131 132 /** Remove any editor that may have been associated with this object 133 * by a previous call to createEditor(). 134 */ 135 @Override 136 public void clear() { 137 _editor = null; 138 } 139 140 /** Create a tableau for the specified effigy. The tableau will be 141 * created with a new unique name with the specified effigy as its 142 * container. If this factory cannot create a tableau 143 * for the given effigy (it is not an instance of PtolemyEffigy), 144 * then return null. 145 * @param effigy The component effigy. 146 * @return A tableau for the effigy, or null if one cannot be created. 147 * @exception Exception If the factory should be able to create a 148 * Tableau for the effigy, but something goes wrong. 149 */ 150 @Override 151 public Tableau createTableau(Effigy effigy) throws Exception { 152 if (!(effigy instanceof PtolemyEffigy)) { 153 return null; 154 } 155 156 NamedObj object = ((PtolemyEffigy) effigy).getModel(); 157 Attribute attribute = object 158 .getAttribute(attributeName.getExpression()); 159 160 if (!(attribute instanceof StringAttribute) 161 && !(attribute instanceof Variable)) { 162 throw new IllegalActionException(object, 163 "Expected " + object.getFullName() 164 + " to contain a StringAttribute or Variable named " 165 + attributeName.getExpression() 166 + ", but it does not."); 167 } 168 169 // effigy may already contain a texteffigy. 170 TextEffigy textEffigy = null; 171 Iterator subEffigies = effigy.entityList(TextEffigy.class).iterator(); 172 173 while (subEffigies.hasNext()) { 174 textEffigy = (TextEffigy) subEffigies.next(); 175 } 176 177 String text = getTextToEdit(attribute); 178 // If a syntaxStyle attribute is given, then use it. 179 if (textEffigy == null) { 180 textEffigy = _newTextEffigy(effigy, text); 181 textEffigy.identifier.setExpression(attribute.getName()); 182 } 183 184 // textEffigy may already have a tableau. 185 Iterator tableaux = textEffigy.entityList(TextEditorTableau.class) 186 .iterator(); 187 188 if (tableaux.hasNext()) { 189 return (TextEditorTableau) tableaux.next(); 190 } 191 192 // Need a new tableau, so create an editor for it. 193 if (_editor == null) { 194 int numberOfRows = ((IntToken) rowsDisplayed.getToken()).intValue(); 195 int numberOfColumns = ((IntToken) columnsDisplayed.getToken()) 196 .intValue(); 197 String name = "Editor for " + attributeName.getExpression() + " of " 198 + getContainer().getFullName(); 199 String style = syntaxStyle.getExpression(); 200 if (style != null && !style.trim().equals("")) { 201 // Attempt to specify a syntax-aware text editor. 202 try { 203 Class editorClass = Class.forName( 204 "ptolemy.actor.gui.syntax.SyntaxTextEditorForStringAttributes"); 205 Constructor constructor = editorClass.getConstructor( 206 new Class[] { TextEditorFactory.class, 207 Attribute.class, Integer.TYPE, Integer.TYPE, 208 String.class, Document.class }); 209 _editor = (TextEditorForStringAttributes) constructor 210 .newInstance(new Object[] { this, attribute, 211 numberOfRows, numberOfColumns, name, 212 textEffigy.getDocument() }); 213 } catch (Throwable ex) { 214 // Ignore and use default text editor. 215 System.out.println( 216 "Note: failed to open syntax-directed editor: " 217 + ex.getMessage()); 218 } 219 } 220 if (_editor == null) { 221 _editor = new TextEditorForStringAttributes(this, attribute, 222 numberOfRows, numberOfColumns, name, 223 textEffigy.getDocument()); 224 } 225 } 226 227 TextEditorTableau tableau = new TextEditorTableau(textEffigy, 228 "_tableau", _editor); 229 return tableau; 230 } 231 232 /** Return the current text of the text editor. 233 * @return The current text of the text editor, or null if there 234 * is none. 235 */ 236 @Override 237 public String getText() { 238 if (_editor != null) { 239 return _editor.text.getText(); 240 } 241 242 return null; 243 } 244 245 /** Return the text value of the specified attribute. 246 * @param attributeToEdit The attribute. 247 * @return The text contained by this attribute, or an error message 248 * if the attribute does not contain text. 249 */ 250 public static String getTextToEdit(Attribute attributeToEdit) { 251 String textToEdit = ""; 252 if (attributeToEdit instanceof StringAttribute) { 253 textToEdit = ((StringAttribute) attributeToEdit).getExpression(); 254 } else if (attributeToEdit instanceof Variable) { 255 try { 256 Token token = ((Variable) attributeToEdit).getToken(); 257 if (token instanceof StringToken) { 258 textToEdit = ((StringToken) token).stringValue(); 259 } else { 260 textToEdit = "Error: Expected a String, but got: " 261 + token.getType() + " with value " 262 + token.toString(); 263 } 264 } catch (IllegalActionException e) { 265 textToEdit = ((Variable) attributeToEdit).getExpression(); 266 } 267 } 268 return textToEdit; 269 } 270 271 /////////////////////////////////////////////////////////////////// 272 //// protected methods //// 273 274 /** Create a text effigy to be contained by the specified host 275 * effigy for the specified text. 276 * @param effigy The host effigy. 277 * @param text The text. 278 * @return the TextEffigy. 279 * @exception Exception If the text effigy cannot be contained 280 * by the specified container, or if the specified text cannot 281 * be inserted into the document. 282 */ 283 protected TextEffigy _newTextEffigy(Effigy effigy, String text) 284 throws Exception { 285 String style = syntaxStyle.getExpression(); 286 return TextEffigy.newTextEffigy(effigy, text, style); 287 } 288 289 /////////////////////////////////////////////////////////////////// 290 //// private members //// 291 292 /** Keep track of an open editor so that it isn't opened more than 293 * once. 294 */ 295 private TextEditorForStringAttributes _editor; 296}