001/* 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.event.KeyEvent;
031
032import javax.swing.JOptionPane;
033import javax.swing.text.Document;
034
035import ptolemy.actor.gui.TextEditor;
036import ptolemy.kernel.util.Attribute;
037import ptolemy.kernel.util.NamedObj;
038import ptolemy.moml.MoMLChangeRequest;
039import ptolemy.util.StringUtilities;
040
041/**
042 A text editor to edit a specified string attribute or parameter.
043
044 @author Edward A. Lee
045 @version $Id$
046 @since Ptolemy II 4.0
047 @Pt.ProposedRating Yellow (eal)
048 @Pt.AcceptedRating Red (ptolemy)
049 */
050@SuppressWarnings("serial")
051public class TextEditorForStringAttributes extends TextEditor {
052
053    /** Create a annotation text editor for the specified attribute.
054     *  @param factory The factory that created this editor.
055     *  @param attributeToEdit The string attribute to edit.
056     *  @param rows The number of rows.
057     *  @param columns The number of columns.
058     *  @param title The window title to use.
059     */
060    public TextEditorForStringAttributes(TextEditorFactory factory,
061            Attribute attributeToEdit, int rows, int columns, String title) {
062        this(factory, attributeToEdit, rows, columns, title, null);
063    }
064
065    /** Create a annotation text editor for the specified attribute.
066     *  @param factory The factory that created this editor.
067     *  @param attributeToEdit The string attribute to edit.
068     *  @param rows The number of rows.
069     *  @param columns The number of columns.
070     *  @param title The window title to use.
071     *  @param document The document to use.  If the value is null,
072     *  the the text from the attribute.
073     */
074    public TextEditorForStringAttributes(TextEditorFactory factory,
075            Attribute attributeToEdit, int rows, int columns, String title,
076            Document document) {
077        super(title, document);
078        _factory = factory;
079        _attributeToEdit = attributeToEdit;
080        if (document == null) {
081            text.append(
082                    TextEditorTableauFactory.getTextToEdit(_attributeToEdit));
083        }
084        text.setColumns(columns);
085        text.setRows(rows);
086
087        // The above will mark the text object modified. Reverse this.
088        setModified(false);
089    }
090
091    ///////////////////////////////////////////////////////////////////
092    ////                         public methods                    ////
093
094    /** Adjust the file menu so that only relevant items appear.
095     *  This has to be called after pack().
096     */
097    @Override
098    public void adjustFileMenu() {
099        // Rename Save command.
100        _fileMenuItems[3].setText("Apply");
101        _fileMenuItems[3].setMnemonic(KeyEvent.VK_A);
102
103        // Remove various menu item.
104        // _fileMenu.remove(7);
105
106        // _fileMenu.remove(6);
107        // _fileMenu.remove(5);
108        // _fileMenu.remove(4);
109        // _fileMenu.remove(2);
110        // _fileMenu.remove(1);
111        // _fileMenu.remove(0);
112    }
113
114    ///////////////////////////////////////////////////////////////////
115    ////                         protected methods                 ////
116
117    /** Override to query whether to apply the changes, if any.
118     *  @return False if the user cancels on a apply query.
119     */
120    @Override
121    protected boolean _close() {
122        // NOTE: The superclass doesn't do the right thing here,
123        // since it requires an associated Tableau.
124        // NOTE: We use dispose() here rather than just hiding the
125        // window.  This ensures that derived classes can react to
126        // windowClosed events rather than overriding the
127        // windowClosing behavior given here.
128        boolean returnValue = true;
129
130        if (isModified()) {
131            if (_queryForApply()) {
132                // Avoid opening Python Actor, modifying the script,
133                // clicking the x, being prompted for applying the
134                // change, applying it, then closing the model and
135                // *not* being prompted for save.
136                if (_modelModified) {
137                    setModified(true);
138                }
139                dispose();
140            } else {
141                return false;
142            }
143        } else {
144            // Window is not modified, so just dispose.  This avoids this issue:
145            // 1. $PTII/bin/vergil $PTII/ptolemy/actor/lib/python/demo/PythonScale/PythonScale.xml
146            // 2. Look inside the PythonScript actor, change
147            // self.output.broadcast(s.multiply(t))
148            // to
149            // self.output.broadcast(s.multiply(t).multiply(t))
150            // 3. In the Python editor window, click on Save and then close
151            // 4. In the model window, click on exit.
152            // The user should be prompted for save.
153            // 5. Rerun vergil, note that the change has not been saved.
154            if (_modelModified) {
155                setModified(true);
156            }
157            dispose();
158        }
159
160        // Ensure that a new editor is opened next time.
161        this._factory.clear();
162
163        return returnValue;
164    }
165
166    /** Override the base class to apply the change to the attribute.
167     *  @return True if the save succeeded.
168     */
169    @Override
170    protected boolean _save() {
171        // Issue a change request to ensure the change is
172        // applied at a safe time and that the model is marked
173        // modified.
174        NamedObj context = _attributeToEdit.getContainer();
175        String request = "<property name=\"" + _attributeToEdit.getName()
176                + "\" value=\""
177                + StringUtilities.escapeForXML(_factory.getText()) + "\"/>";
178        context.requestChange(new MoMLChangeRequest(this, context, request));
179        setModified(false);
180        _modelModified = true;
181        return true;
182    }
183
184    ///////////////////////////////////////////////////////////////////
185    ////                         private methods                   ////
186    // Open a dialog to prompt the user to apply the data.
187    // Return false if the user clicks "cancel", and otherwise return true.
188    private boolean _queryForApply() {
189        Object[] options = { "Apply", "Discard changes", "Cancel" };
190        String query = "Apply changes to " + _attributeToEdit.getFullName()
191                + "?";
192
193        // Show the MODAL dialog
194        int selected = JOptionPane.showOptionDialog(this, query,
195                "Apply Changes?", JOptionPane.YES_NO_CANCEL_OPTION,
196                JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
197
198        if (selected == 0) {
199            return _save();
200        } else if (selected == 1) {
201            return true;
202        }
203
204        return false;
205    }
206
207    ///////////////////////////////////////////////////////////////////
208    ////                         private members                   ////
209    private final TextEditorFactory _factory;
210
211    private Attribute _attributeToEdit;
212
213    /** True if this attribute was modified and saved, which caused.
214     *  the containing model to be modified.
215     */
216    private boolean _modelModified = false;
217}