6 * Copyright (C) 2002-2005 The Jmol Development Team
\r
8 * Contact: jmol-developers@lists.sf.net
\r
10 * This library is free software; you can redistribute it and/or
\r
11 * modify it under the terms of the GNU Lesser General Public
\r
12 * License as published by the Free Software Foundation; either
\r
13 * version 2.1 of the License, or (at your option) any later version.
\r
15 * This library is distributed in the hope that it will be useful,
\r
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
18 * Lesser General Public License for more details.
\r
20 * You should have received a copy of the GNU Lesser General Public
\r
21 * License along with this library; if not, write to the Free Software
\r
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
\r
25 * Modified and added to Jalview by A Waterhouse to extend JInternalFrame
\r
29 package jalview.gui;
\r
31 import org.jmol.api.*;
\r
34 import java.awt.event.*;
\r
35 import javax.swing.*;
\r
36 import javax.swing.text.*;
\r
37 import java.util.Vector;
\r
39 import org.jmol.i18n.GT;
\r
40 import org.jmol.util.Logger;
\r
41 import org.jmol.util.CommandHistory;
\r
43 public final class ScriptWindow extends JPanel
\r
44 implements ActionListener, EnterListener{
\r
46 private ConsoleTextPane console;
\r
47 private JButton closeButton;
\r
48 private JButton runButton;
\r
49 private JButton haltButton;
\r
50 private JButton clearButton;
\r
51 private JButton historyButton;
\r
52 private JButton stateButton;
\r
56 public ScriptWindow(AppJmol appJmol)
\r
58 this.viewer = appJmol.viewer;
\r
59 this.appJmol = appJmol;
\r
62 setLayout(new BorderLayout());
\r
64 console = new ConsoleTextPane(this);
\r
67 console.setPrompt();
\r
68 add(new JScrollPane(console)
\r
69 , BorderLayout.CENTER);
\r
71 JPanel buttonPanel = new JPanel();
\r
72 add(buttonPanel, BorderLayout.SOUTH);
\r
74 runButton = new JButton(GT._("Run"));
\r
75 haltButton = new JButton(GT._("Halt"));
\r
76 runButton.addActionListener(this);
\r
77 // buttonPanel.add(runButton);
\r
78 haltButton.addActionListener(this);
\r
79 // buttonPanel.add(haltButton);
\r
80 haltButton.setEnabled(false);
\r
82 clearButton = new JButton(GT._("Clear"));
\r
83 clearButton.addActionListener(this);
\r
84 buttonPanel.add(clearButton);
\r
86 historyButton = new JButton(GT._("History"));
\r
87 historyButton.addActionListener(this);
\r
88 buttonPanel.add(historyButton);
\r
90 stateButton = new JButton(GT._("State"));
\r
91 stateButton.addActionListener(this);
\r
92 buttonPanel.add(stateButton);
\r
94 closeButton = new JButton(GT._("Close"));
\r
95 closeButton.addActionListener(this);
\r
96 buttonPanel.add(closeButton);
\r
98 for(int i=0; i<buttonPanel.getComponentCount(); i++)
\r
100 // ((JButton)buttonPanel.getComponent(i))
\r
101 // .setMargin(new Insets(0, 0, 0, 0));
\r
106 public void sendConsoleEcho(String strEcho) {
\r
107 if (strEcho != null && !isError) {
\r
109 console.outputEcho(strEcho);
\r
115 boolean isError = false;
\r
116 void setError(boolean TF) {
\r
119 //console.recallCommand(true);
\r
122 public void sendConsoleMessage(String strStatus) {
\r
123 if (strStatus == null) {
\r
124 console.clearContent();
\r
125 console.outputStatus("");
\r
126 } else if (strStatus.indexOf("ERROR:") >= 0) {
\r
127 console.outputError(strStatus);
\r
129 } else if (!isError) {
\r
130 console.outputStatus(strStatus);
\r
134 public void notifyScriptTermination(String strMsg, int msWalltime) {
\r
135 if (strMsg != null && strMsg.indexOf("ERROR") >= 0) {
\r
136 console.outputError(strMsg);
\r
138 runButton.setEnabled(true);
\r
139 haltButton.setEnabled(false);
\r
142 public void enterPressed() {
\r
143 runButton.doClick(100);
\r
144 // executeCommand();
\r
148 class ExecuteCommandThread extends Thread {
\r
151 ExecuteCommandThread (String command) {
\r
152 strCommand = command;
\r
155 public void run() {
\r
157 executeCommand(strCommand);
\r
158 } catch (Exception ie) {
\r
159 Logger.debug("execution command interrupted!"+ie);
\r
164 ExecuteCommandThread execThread;
\r
165 void executeCommandAsThread(){
\r
166 String strCommand = console.getCommandString().trim();
\r
167 if (strCommand.length() > 0) {
\r
168 execThread = new ExecuteCommandThread(strCommand);
\r
169 execThread.start();
\r
173 void executeCommand(String strCommand) {
\r
176 console.appendNewline();
\r
177 console.setPrompt();
\r
178 if (strCommand.length() > 0) {
\r
179 String strErrorMessage = null;
\r
180 doWait = (strCommand.indexOf("WAIT ") == 0);
\r
181 if (doWait) { //for testing, mainly
\r
182 // demonstrates using the statusManager system.
\r
183 runButton.setEnabled(false);
\r
184 haltButton.setEnabled(true);
\r
186 Vector info = (Vector) viewer
\r
187 .scriptWaitStatus(strCommand.substring(5),
\r
188 "+fileLoaded,+scriptStarted,+scriptStatus,+scriptEcho,+scriptTerminated");
\r
189 runButton.setEnabled(true);
\r
190 haltButton.setEnabled(false);
\r
192 * info = [ statusRecortSet0, statusRecortSet1, statusRecortSet2, ...]
\r
193 * statusRecordSet = [ statusRecord0, statusRecord1, statusRecord2, ...]
\r
194 * statusRecord = [int msgPtr, String statusName, int intInfo, String msg]
\r
196 for (int i = 0; i < info.size(); i++) {
\r
197 Vector statusRecordSet = (Vector) info.get(i);
\r
198 for (int j = 0; j < statusRecordSet.size(); j++) {
\r
199 Vector statusRecord = (Vector) statusRecordSet.get(j);
\r
200 Logger.info("msg#=" + statusRecord.get(0) + " "
\r
201 + statusRecord.get(1) + " intInfo=" + statusRecord.get(2)
\r
202 + " stringInfo=" + statusRecord.get(3));
\r
205 console.appendNewline();
\r
207 boolean isScriptExecuting = viewer.isScriptExecuting();
\r
208 if (viewer.checkHalt(strCommand))
\r
209 strErrorMessage = (isScriptExecuting ? "string execution halted with " + strCommand : "no script was executing");
\r
211 strErrorMessage = "";//viewer.scriptCheck(strCommand);
\r
212 //the problem is that scriptCheck is synchronized, so these might get backed up.
\r
213 if (strErrorMessage != null && strErrorMessage.length() > 0) {
\r
214 console.outputError(strErrorMessage);
\r
216 //runButton.setEnabled(false);
\r
217 haltButton.setEnabled(true);
\r
218 viewer.script(strCommand);
\r
222 console.grabFocus();
\r
225 public void actionPerformed(ActionEvent e) {
\r
226 Object source = e.getSource();
\r
227 if (source == closeButton) {
\r
228 appJmol.showConsole(false);
\r
229 } else if (source == runButton) {
\r
230 executeCommandAsThread();
\r
231 } else if (source == clearButton) {
\r
232 console.clearContent();
\r
233 } else if (source == historyButton) {
\r
234 console.clearContent(viewer.getSetHistory(Integer.MAX_VALUE));
\r
235 } else if (source == stateButton) {
\r
236 console.clearContent(viewer.getStateInfo());
\r
237 } else if (source == haltButton) {
\r
238 viewer.haltScriptExecution();
\r
240 console.grabFocus(); // always grab the focus (e.g., after clear)
\r
244 class ConsoleTextPane extends JTextPane {
\r
246 ConsoleDocument consoleDoc;
\r
247 EnterListener enterListener;
\r
250 ConsoleTextPane(ScriptWindow scriptWindow) {
\r
251 super(new ConsoleDocument());
\r
252 consoleDoc = (ConsoleDocument)getDocument();
\r
253 consoleDoc.setConsoleTextPane(this);
\r
254 this.enterListener = (EnterListener) scriptWindow;
\r
255 this.viewer = scriptWindow.viewer;
\r
258 public String getCommandString() {
\r
259 String cmd = consoleDoc.getCommandString();
\r
263 public void setPrompt() {
\r
264 consoleDoc.setPrompt();
\r
267 public void appendNewline() {
\r
268 consoleDoc.appendNewline();
\r
271 public void outputError(String strError) {
\r
272 consoleDoc.outputError(strError);
\r
275 public void outputErrorForeground(String strError) {
\r
276 consoleDoc.outputErrorForeground(strError);
\r
279 public void outputEcho(String strEcho) {
\r
280 consoleDoc.outputEcho(strEcho);
\r
283 public void outputStatus(String strStatus) {
\r
284 consoleDoc.outputStatus(strStatus);
\r
287 public void enterPressed() {
\r
288 if (enterListener != null)
\r
289 enterListener.enterPressed();
\r
292 public void clearContent() {
\r
293 clearContent(null);
\r
295 public void clearContent(String text) {
\r
296 consoleDoc.clearContent();
\r
298 consoleDoc.outputEcho(text);
\r
303 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
\r
307 * Custom key event processing for command 0 implementation.
\r
309 * Captures key up and key down strokes to call command history
\r
310 * and redefines the same events with control down to allow
\r
311 * caret vertical shift.
\r
313 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
\r
315 protected void processKeyEvent(KeyEvent ke)
\r
317 // Id Control key is down, captures events does command
\r
318 // history recall and inhibits caret vertical shift.
\r
319 if (ke.getKeyCode() == KeyEvent.VK_UP
\r
320 && ke.getID() == KeyEvent.KEY_PRESSED
\r
321 && !ke.isControlDown())
\r
323 recallCommand(true);
\r
326 ke.getKeyCode() == KeyEvent.VK_DOWN
\r
327 && ke.getID() == KeyEvent.KEY_PRESSED
\r
328 && !ke.isControlDown())
\r
330 recallCommand(false);
\r
332 // If Control key is down, redefines the event as if it
\r
333 // where a key up or key down stroke without modifiers.
\r
334 // This allows to move the caret up and down
\r
335 // with no command history recall.
\r
337 (ke.getKeyCode() == KeyEvent.VK_DOWN
\r
338 || ke.getKeyCode() == KeyEvent.VK_UP)
\r
339 && ke.getID() == KeyEvent.KEY_PRESSED
\r
340 && ke.isControlDown())
\r
343 .processKeyEvent(new KeyEvent(
\r
344 (Component) ke.getSource(),
\r
350 ke.getKeyLocation()));
\r
352 // Standard processing for other events.
\r
355 super.processKeyEvent(ke);
\r
356 //check command for compiler-identifyable syntax issues
\r
357 //this may have to be taken out if people start complaining
\r
358 //that only some of the commands are being checked
\r
359 //that is -- that the script itself is not being fully checked
\r
361 //not perfect -- help here?
\r
362 if (ke.getID() == KeyEvent.KEY_RELEASED
\r
363 && (ke.getKeyCode() > KeyEvent.VK_DOWN) || ke.getKeyCode() == KeyEvent.VK_BACK_SPACE)
\r
369 * Recall command history.
\r
371 * @param up - history up or down
\r
373 void recallCommand(boolean up) {
\r
374 String cmd = viewer.getSetHistory(up ? -1 : 1);
\r
379 if (cmd.endsWith(CommandHistory.ERROR_FLAG)) {
\r
380 cmd = cmd.substring(0, cmd.indexOf(CommandHistory.ERROR_FLAG));
\r
381 consoleDoc.replaceCommand(cmd, true);
\r
383 consoleDoc.replaceCommand(cmd, false);
\r
385 } catch (BadLocationException e) {
\r
386 e.printStackTrace();
\r
390 void checkCommand() {
\r
391 String strCommand = consoleDoc.getCommandString();
\r
392 if (strCommand.length() == 0)
\r
395 .colorCommand(viewer.scriptCheck(strCommand) == null ? consoleDoc.attUserInput
\r
396 : consoleDoc.attError);
\r
402 class ConsoleDocument extends DefaultStyledDocument {
\r
404 ConsoleTextPane consoleTextPane;
\r
406 SimpleAttributeSet attError;
\r
407 SimpleAttributeSet attEcho;
\r
408 SimpleAttributeSet attPrompt;
\r
409 SimpleAttributeSet attUserInput;
\r
410 SimpleAttributeSet attStatus;
\r
412 ConsoleDocument() {
\r
415 attError = new SimpleAttributeSet();
\r
416 StyleConstants.setForeground(attError, Color.red);
\r
418 attPrompt = new SimpleAttributeSet();
\r
419 StyleConstants.setForeground(attPrompt, Color.magenta);
\r
421 attUserInput = new SimpleAttributeSet();
\r
422 StyleConstants.setForeground(attUserInput, Color.black);
\r
424 attEcho = new SimpleAttributeSet();
\r
425 StyleConstants.setForeground(attEcho, Color.blue);
\r
426 StyleConstants.setBold(attEcho, true);
\r
428 attStatus = new SimpleAttributeSet();
\r
429 StyleConstants.setForeground(attStatus, Color.black);
\r
430 StyleConstants.setItalic(attStatus, true);
\r
433 void setConsoleTextPane(ConsoleTextPane consoleTextPane) {
\r
434 this.consoleTextPane = consoleTextPane;
\r
437 Position positionBeforePrompt; // starts at 0, so first time isn't tracked (at least on Mac OS X)
\r
438 Position positionAfterPrompt; // immediately after $, so this will track
\r
439 int offsetAfterPrompt; // only still needed for the insertString override and replaceCommand
\r
442 * Removes all content of the script window, and add a new prompt.
\r
444 void clearContent() {
\r
446 super.remove(0, getLength());
\r
447 } catch (BadLocationException exception) {
\r
448 System.out.println("Could not clear script window content: " + exception.getMessage());
\r
454 super.insertString(getLength(), "$ ", attPrompt);
\r
455 setOffsetPositions();
\r
456 consoleTextPane.setCaretPosition(offsetAfterPrompt);
\r
457 } catch (BadLocationException e) {
\r
458 e.printStackTrace();
\r
462 void setOffsetPositions() {
\r
464 offsetAfterPrompt = getLength();
\r
465 positionBeforePrompt = createPosition(offsetAfterPrompt - 2);
\r
466 // after prompt should be immediately after $ otherwise tracks the end
\r
467 // of the line (and no command will be found) at least on Mac OS X it did.
\r
468 positionAfterPrompt = createPosition(offsetAfterPrompt - 1);
\r
469 } catch (BadLocationException e) {
\r
470 e.printStackTrace();
\r
474 void setNoPrompt() {
\r
476 offsetAfterPrompt = getLength();
\r
477 positionAfterPrompt = positionBeforePrompt = createPosition(offsetAfterPrompt);
\r
478 consoleTextPane.setCaretPosition(offsetAfterPrompt);
\r
479 } catch (BadLocationException e) {
\r
480 e.printStackTrace();
\r
484 // it looks like the positionBeforePrompt does not track when it started out as 0
\r
485 // and a insertString at location 0 occurs. It may be better to track the
\r
486 // position after the prompt in stead
\r
487 void outputBeforePrompt(String str, SimpleAttributeSet attribute) {
\r
489 int pt = consoleTextPane.getCaretPosition();
\r
490 Position caretPosition = createPosition(pt);
\r
491 pt = positionBeforePrompt.getOffset();
\r
492 super.insertString(pt, str+"\n", attribute);
\r
493 setOffsetPositions();
\r
494 pt = caretPosition.getOffset();
\r
495 consoleTextPane.setCaretPosition(pt);
\r
496 } catch (BadLocationException e) {
\r
497 e.printStackTrace();
\r
501 void outputError(String strError) {
\r
502 outputBeforePrompt(strError, attError);
\r
505 void outputErrorForeground(String strError) {
\r
507 super.insertString(getLength(), strError+"\n", attError);
\r
508 consoleTextPane.setCaretPosition(getLength());
\r
509 } catch (BadLocationException e) {
\r
510 e.printStackTrace();
\r
515 void outputEcho(String strEcho) {
\r
516 outputBeforePrompt(strEcho, attEcho);
\r
519 void outputStatus(String strStatus) {
\r
520 outputBeforePrompt(strStatus, attStatus);
\r
523 void appendNewline() {
\r
525 super.insertString(getLength(), "\n", attUserInput);
\r
526 consoleTextPane.setCaretPosition(getLength());
\r
527 } catch (BadLocationException e) {
\r
528 e.printStackTrace();
\r
532 // override the insertString to make sure everything typed ends up at the end
\r
533 // or in the 'command line' using the proper font, and the newline is processed.
\r
534 public void insertString(int offs, String str, AttributeSet a)
\r
535 throws BadLocationException {
\r
536 int ichNewline = str.indexOf('\n');
\r
537 if (ichNewline > 0)
\r
538 str = str.substring(0, ichNewline);
\r
539 if (ichNewline != 0) {
\r
540 if (offs < offsetAfterPrompt) {
\r
541 offs = getLength();
\r
543 super.insertString(offs, str, a == attError ? a : attUserInput);
\r
544 consoleTextPane.setCaretPosition(offs+str.length());
\r
546 if (ichNewline >= 0) {
\r
547 consoleTextPane.enterPressed();
\r
551 String getCommandString() {
\r
552 String strCommand = "";
\r
554 int cmdStart = positionAfterPrompt.getOffset();
\r
555 strCommand = getText(cmdStart, getLength() - cmdStart);
\r
556 while (strCommand.length() > 0 && strCommand.charAt(0) == ' ')
\r
557 strCommand = strCommand.substring(1);
\r
558 } catch (BadLocationException e) {
\r
559 e.printStackTrace();
\r
564 public void remove(int offs, int len)
\r
565 throws BadLocationException {
\r
566 if (offs < offsetAfterPrompt) {
\r
567 len -= offsetAfterPrompt - offs;
\r
570 offs = offsetAfterPrompt;
\r
572 super.remove(offs, len);
\r
573 // consoleTextPane.setCaretPosition(offs);
\r
576 public void replace(int offs, int length, String str, AttributeSet attrs)
\r
577 throws BadLocationException {
\r
578 if (offs < offsetAfterPrompt) {
\r
579 if (offs + length < offsetAfterPrompt) {
\r
580 offs = getLength();
\r
583 length -= offsetAfterPrompt - offs;
\r
584 offs = offsetAfterPrompt;
\r
587 super.replace(offs, length, str, attrs);
\r
588 // consoleTextPane.setCaretPosition(offs + str.length());
\r
592 * Replaces current command on script.
\r
594 * @param newCommand new command value
\r
595 * @param isError true to set error color ends with #??
\r
597 * @throws BadLocationException
\r
599 void replaceCommand(String newCommand, boolean isError) throws BadLocationException {
\r
600 if (positionAfterPrompt == positionBeforePrompt)
\r
602 replace(offsetAfterPrompt, getLength() - offsetAfterPrompt, newCommand,
\r
603 isError ? attError : attUserInput);
\r
606 void colorCommand(SimpleAttributeSet att) {
\r
607 if (positionAfterPrompt == positionBeforePrompt)
\r
609 setCharacterAttributes(offsetAfterPrompt, getLength() - offsetAfterPrompt, att, true);
\r
613 interface EnterListener {
\r
614 public void enterPressed();
\r