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}