001/* A text area for shell-style interactions. 002 003 Copyright (c) 1998-2018 The Regents of the University of California. 004 All rights reserved. 005 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 009 above copyright notice and the following two paragraphs appear in all 010 copies 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 */ 028package ptolemy.gui; 029 030import java.awt.BorderLayout; 031import java.awt.Color; 032import java.awt.Font; 033import java.awt.event.KeyAdapter; 034import java.awt.event.KeyEvent; 035import java.awt.event.WindowAdapter; 036import java.awt.event.WindowEvent; 037import java.awt.event.WindowListener; 038 039import javax.swing.BorderFactory; 040import javax.swing.JFrame; 041import javax.swing.JPanel; 042import javax.swing.JScrollPane; 043import javax.swing.JTextArea; 044import javax.swing.SwingUtilities; 045 046import ptolemy.util.StringUtilities; 047 048/////////////////////////////////////////////////////////////////// 049//// UserDialog 050 051/** 052 A panel with two text areas, one for user input, and one in which 053 to display responses. 054 055 @author Edward A. Lee 056 @version $Id$ 057 @since Ptolemy II 11.0 058 @Pt.ProposedRating Red (cxh) 059 @Pt.AcceptedRating Red (cxh) 060 */ 061@SuppressWarnings("serial") 062public class UserDialog extends JPanel { 063 /** Create a new instance with no initial message. 064 */ 065 public UserDialog() { 066 this(null); 067 } 068 069 /** Create a new instance with the specified initial message. 070 * @param initialMessage The initial message. 071 */ 072 public UserDialog(String initialMessage) { 073 // Graphics 074 super(new BorderLayout()); 075 _initialMessage = initialMessage; 076 077 // Create a one-line input text area. 078 // FIXME: Size needs to be configurable. 079 _userInputTextArea = new JTextArea("", 1, 80); 080 // Event handling 081 _userInputTextArea.addKeyListener(new ShellKeyListener()); 082 add(_userInputTextArea, BorderLayout.PAGE_START); 083 084 // Now create the responses text area. 085 // FIXME: Size needs to be configurable. 086 _responseTextArea = new JTextArea("", 20, 80); 087 _responseTextArea.setEditable(false); 088 _jScrollPane = new JScrollPane(_responseTextArea); 089 add(_jScrollPane, BorderLayout.PAGE_END); 090 091 // Set the fonts. 092 _userInputTextArea.setFont(new Font("Monospaced", 0, 14)); 093 _responseTextArea.setFont(new Font("Monospaced", 0, 14)); 094 095 _userInputTextArea.setBorder(BorderFactory.createTitledBorder( 096 BorderFactory.createLineBorder(Color.black), "")); 097 _responseTextArea.setBorder(BorderFactory.createTitledBorder( 098 BorderFactory.createLineBorder(Color.black), "")); 099 } 100 101 /////////////////////////////////////////////////////////////////// 102 //// public methods //// 103 104 /** Override the base class to output the first prompt. 105 * We need to do this here because we can't write to 106 * the TextArea until the peer has been created. 107 */ 108 @Override 109 public void addNotify() { 110 super.addNotify(); 111 initialize(_initialMessage); 112 } 113 114 /** Append the specified text to the response text area with a newline at the end. 115 * The text will actually be appended in the swing thread, not immediately. 116 * This method immediately returns. 117 * @param text The text to append to the text area. 118 */ 119 public void appendText(final String text) { 120 Runnable doAppendJTextArea = new Runnable() { 121 @Override 122 public void run() { 123 _responseTextArea.append(text + "\n"); 124 // Scroll down as we generate text. 125 _responseTextArea 126 .setCaretPosition(_responseTextArea.getText().length()); 127 } 128 }; 129 SwingUtilities.invokeLater(doAppendJTextArea); 130 } 131 132 /** Get the interpreter that has been registered with setInterpreter(). 133 * @return The interpreter, or null if none has been set. 134 * @see #setInterpreter(ShellInterpreter) 135 */ 136 public ShellInterpreter getInterpreter() { 137 return _interpreter; 138 } 139 140 /** Initialize the text area with the given starting message. 141 * This is just like appendText(), except that it clears the display first. 142 * @param initialMessage The initial message. 143 */ 144 public void initialize(final String initialMessage) { 145 if (_responseTextArea == null) { 146 _initialMessage = initialMessage; 147 } else { 148 _initialMessage = null; 149 Runnable doInitialize = new Runnable() { 150 @Override 151 public void run() { 152 _userInputTextArea.setText(""); 153 _userInputTextArea.setCaretPosition(0); 154 if (initialMessage != null) { 155 _responseTextArea.setText(initialMessage + "\n"); 156 } else { 157 _responseTextArea.setText(""); 158 } 159 } 160 }; 161 SwingUtilities.invokeLater(doInitialize); 162 } 163 } 164 165 /** Main method used for testing. To run a simple test, use: 166 * <pre> 167 * java -classpath $PTII ptolemy.gui.UserDialog 168 * </pre> 169 * @param args Currently ignored. 170 */ 171 public static void main(String[] args) { 172 try { 173 // Run this in the Swing Event Thread. 174 Runnable doActions = new Runnable() { 175 @Override 176 public void run() { 177 try { 178 JFrame jFrame = new JFrame("UserDialog Example"); 179 WindowListener windowListener = new WindowAdapter() { 180 @Override 181 public void windowClosing(WindowEvent e) { 182 StringUtilities.exit(0); 183 } 184 }; 185 186 jFrame.addWindowListener(windowListener); 187 188 final UserDialog exec = new UserDialog(); 189 jFrame.getContentPane().add(exec); 190 jFrame.pack(); 191 jFrame.setVisible(true); 192 } catch (Exception ex) { 193 System.err.println(ex.toString()); 194 ex.printStackTrace(); 195 } 196 } 197 }; 198 SwingUtilities.invokeAndWait(doActions); 199 } catch (Exception ex) { 200 System.err.println(ex.toString()); 201 ex.printStackTrace(); 202 } 203 } 204 205 /** Set the interpreter. 206 * @param interpreter The interpreter. 207 * @see #getInterpreter() 208 */ 209 public void setInterpreter(ShellInterpreter interpreter) { 210 _interpreter = interpreter; 211 } 212 213 /////////////////////////////////////////////////////////////////// 214 //// private methods //// 215 216 // Evaluate the command. 217 // NOTE: This must be called in the swing event thread. 218 private void _evalCommand() { 219 String command = _userInputTextArea.getText(); 220 221 if (_interpreter != null) { 222 // Process it. 223 // Clear the command text area. 224 _userInputTextArea.setText(""); 225 appendText(command); 226 227 String result; 228 229 try { 230 result = _interpreter.evaluateCommand(command); 231 if (result != null && !result.trim().equals("")) { 232 appendText(result); 233 } 234 } catch (Throwable e) { 235 appendText("ERROR: " + e.toString()); 236 } 237 } 238 } 239 240 /////////////////////////////////////////////////////////////////// 241 //// private variables //// 242 243 /** The scroll pane containing the output text. */ 244 private JScrollPane _jScrollPane; 245 246 // The initial message, if there is one. 247 private String _initialMessage = null; 248 249 // The interpreter. 250 private ShellInterpreter _interpreter; 251 252 // The TextArea widget for responses. 253 private JTextArea _responseTextArea; 254 255 // The TextArea widget for user input. 256 private JTextArea _userInputTextArea; 257 258 /////////////////////////////////////////////////////////////////// 259 //// inner classes //// 260 261 // The key listener 262 private class ShellKeyListener extends KeyAdapter { 263 @Override 264 public void keyPressed(KeyEvent keyEvent) { 265 // Process keys 266 switch (keyEvent.getKeyCode()) { 267 case KeyEvent.VK_ENTER: 268 // Consume the keypress so it is not displayed. 269 keyEvent.consume(); 270 _evalCommand(); 271 break; 272 } 273 } 274 } 275}