001/* An attribute that creates a text editor to edit a string attribute.
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.toolbox;
029
030import java.awt.Frame;
031import java.lang.reflect.Constructor;
032
033import javax.swing.text.Document;
034
035import ptolemy.actor.gui.EditorFactory;
036import ptolemy.data.IntToken;
037import ptolemy.data.expr.Parameter;
038import ptolemy.data.type.BaseType;
039import ptolemy.kernel.util.Attribute;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.NamedObj;
043import ptolemy.kernel.util.StringAttribute;
044import ptolemy.util.MessageHandler;
045
046///////////////////////////////////////////////////////////////////
047//// TextEditorConfigureFactory
048
049/**
050 If this class is contained by a actor, then double clicking on that
051 actor will invoke a text editor that edits the value of a specified
052 string attribute.  The string attribute must be contained by the
053 same container as this factory; its name is given by the
054 <i>attributeName</i> attribute of this factory. The number of
055 rows and columns displayed are given by the <i>rowsDisplayed</i>
056 and <i>columnsDisplayed</i> parameters. The default is 80 columns
057 and 40 rows.
058 <p>
059 This attribute is similar to TextEditorTableauFactory, except that
060 it opens the text editor when the containing actor is configured
061 (edit parameters), whereas TextEditorTableauFactory opens the text
062 editor when the user looks inside.
063 @see TextEditorTableauFactory
064
065 @author Edward A. Lee, contributor: Daniel Crawl
066 @version $Id$
067 @since Ptolemy II 4.0
068 @Pt.ProposedRating Yellow (eal)
069 @Pt.AcceptedRating Red (ptolemy)
070 */
071public class TextEditorConfigureFactory extends EditorFactory
072        implements TextEditorFactory {
073    /** Construct a factory with the specified container and name.
074     *  @param container The container.
075     *  @param name The name of the factory.
076     *  @exception IllegalActionException If the factory is not of an
077     *   acceptable attribute for the container.
078     *  @exception NameDuplicationException If the name coincides with
079     *   an attribute already in the container.
080     */
081    public TextEditorConfigureFactory(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    ///////////////////////////////////////////////////////////////////
100    ////                         parameters                        ////
101
102    /** The name of the string attribute that is to be edited. */
103    public StringAttribute attributeName;
104
105    /** The horizontal size of the display, in columns. This contains
106     *  an integer, and defaults to 40.
107     */
108    public Parameter columnsDisplayed;
109
110    /** The vertical size of the display, in rows. This contains an
111     *  integer, and defaults to 10.
112     */
113    public Parameter rowsDisplayed;
114
115    /** The style of the text to be edited. This may or may not be
116     *  supported. If the package "org.fife.ui.rsyntaxtextarea" is found in
117     *  the classpath, then the supported styles include
118     *  "text/plain", "text/c", "text/clojure", "text/cpp", "text/cs",
119     *  "text/css", "text/dtd", "text/fortran",
120     *  "text/groovy", "text/html", "text/java",
121     *  "text/javascript", "text/json", "text/jsp",
122     *  "text/latex", "text/makefile",
123     *  "text/perl", "text/php",
124     *  "text/properties", "text/python", "text/ruby", "text/sas",
125     *  "text/scala", "text/sql", "text/tcl", "text/unix", "text/vb",
126     *  "text/bat", and "text/xml".
127     */
128    public StringAttribute syntaxStyle;
129
130    ///////////////////////////////////////////////////////////////////
131    ////                         public methods                    ////
132
133    /** Remove any editor that may have been associated with this object
134     *  by a previous call to createEditor().
135     */
136    @Override
137    public void clear() {
138        _editor = null;
139    }
140
141    /** Create an editor for editing the string attribute specified
142     *  by the <i>attributeName</i> parameter.
143     *  @param object The object to configure (which is expected to
144     *   be the same as the container of this attribute).
145     *  @param parent The frame with respect to which to define the
146     *   editor.
147     */
148    @Override
149    public void createEditor(NamedObj object, Frame parent) {
150        if (_editor == null) {
151            try {
152                StringAttribute attributeToEdit = (StringAttribute) getContainer()
153                        .getAttribute(attributeName.getExpression(),
154                                StringAttribute.class);
155                int numberOfRows = ((IntToken) rowsDisplayed.getToken())
156                        .intValue();
157                int numberOfColumns = ((IntToken) columnsDisplayed.getToken())
158                        .intValue();
159
160                String style = syntaxStyle.getExpression();
161                if (style != null && !style.trim().equals("")) {
162                    // Attempt to specify a syntax-aware text editor.
163                    try {
164
165                        Document doc = null;
166                        try {
167                            // Attempt to create a styled document.
168                            // Use reflection here to avoid a hard dependency on an external package.
169                            Class<?> docClass = Class.forName(
170                                    "org.fife.ui.rsyntaxtextarea.RSyntaxDocument");
171                            Constructor<?> docClassConstructor = docClass
172                                    .getConstructor(String.class);
173                            doc = (Document) docClassConstructor
174                                    .newInstance(new Object[] { style });
175                        } catch (Throwable ex) {
176                            // Ignore and use default text editor.
177                            System.out.println(
178                                    "Note: failed to open syntax-directed editor: "
179                                            + ex.getMessage());
180                        }
181
182                        if (doc != null) {
183                            Class<?> editorClass = Class.forName(
184                                    "ptolemy.actor.gui.syntax.SyntaxTextEditorForStringAttributes");
185                            Constructor<?> constructor = editorClass
186                                    .getConstructor(new Class[] {
187                                            TextEditorFactory.class,
188                                            Attribute.class, Integer.TYPE,
189                                            Integer.TYPE, String.class,
190                                            Document.class });
191                            _editor = (TextEditorForStringAttributes) constructor
192                                    .newInstance(new Object[] { this,
193                                            attributeToEdit, numberOfRows,
194                                            numberOfColumns,
195                                            "Editor for "
196                                                    + attributeName
197                                                            .getExpression()
198                                                    + " of "
199                                                    + getContainer()
200                                                            .getFullName(),
201                                            doc });
202
203                            _editor.text.append(TextEditorTableauFactory
204                                    .getTextToEdit(attributeToEdit));
205                            // The above will mark the text object modified. Reverse this.
206                            _editor.setModified(false);
207
208                        }
209                    } catch (Throwable ex) {
210                        // Ignore and use default text editor.
211                        System.out.println(
212                                "Note: failed to open syntax-directed editor: "
213                                        + ex.getMessage());
214                    }
215                }
216                if (_editor == null) {
217                    _editor = new TextEditorForStringAttributes(this,
218                            attributeToEdit, numberOfRows, numberOfColumns,
219                            "Editor for " + attributeName.getExpression()
220                                    + " of " + getContainer().getFullName());
221                }
222            } catch (IllegalActionException ex) {
223                MessageHandler.error(
224                        "Cannot get specified string attribute to edit.", ex);
225            }
226            if (_editor == null) {
227                throw new NullPointerException("Could not create editor for "
228                        + object.getFullName() + " in frame " + parent);
229            } else {
230                // Can't just call show() here because after calling pack() and
231                // before making visible we need to call adjustFileMenu().
232                // Also, can't do this if the editor has already been created.
233                _editor.pack();
234                _editor.adjustFileMenu();
235                _editor.centerOnScreen();
236            }
237        }
238        _editor.setVisible(true);
239    }
240
241    /** Return the current text of the text editor.
242     *  @return The current text of the text editor, or null if there
243     *   is none.
244     */
245    @Override
246    public String getText() {
247        if (_editor != null) {
248            return _editor.text.getText();
249        }
250
251        return null;
252    }
253
254    ///////////////////////////////////////////////////////////////////
255    ////                         private members                   ////
256    // Keep track of an open editor so that it isn't opened more than
257    // once.
258    private TextEditorForStringAttributes _editor;
259}