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 JInternalFrame
\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
53 private JButton helpButton;
\r
56 public ScriptWindow(JmolViewer viewer)
\r
58 this.viewer = viewer;
\r
60 getContentPane().setLayout(new BorderLayout());
\r
62 console = new ConsoleTextPane(this);
\r
65 console.setPrompt();
\r
66 getContentPane().add(new JScrollPane(console)
\r
67 , BorderLayout.CENTER);
\r
69 JPanel buttonPanel = new JPanel();
\r
70 getContentPane().add(buttonPanel, BorderLayout.SOUTH);
\r
72 runButton = new JButton(GT._("Run"));
\r
73 runButton.addActionListener(this);
\r
74 buttonPanel.add(runButton);
\r
76 haltButton = new JButton(GT._("Halt"));
\r
77 haltButton.addActionListener(this);
\r
78 buttonPanel.add(haltButton);
\r
79 haltButton.setEnabled(false);
\r
81 clearButton = new JButton(GT._("Clear"));
\r
82 clearButton.addActionListener(this);
\r
83 buttonPanel.add(clearButton);
\r
85 historyButton = new JButton(GT._("History"));
\r
86 historyButton.addActionListener(this);
\r
87 buttonPanel.add(historyButton);
\r
89 stateButton = new JButton(GT._("State"));
\r
90 stateButton.addActionListener(this);
\r
91 buttonPanel.add(stateButton);
\r
93 helpButton = new JButton(GT._("Help"));
\r
94 helpButton.addActionListener(this);
\r
95 buttonPanel.add(helpButton);
\r
97 closeButton = new JButton(GT._("Close"));
\r
98 closeButton.addActionListener(this);
\r
99 buttonPanel.add(closeButton);
\r
103 public void sendConsoleEcho(String strEcho) {
\r
104 if (strEcho != null && !isError) {
\r
106 console.outputEcho(strEcho);
\r
112 boolean isError = false;
\r
113 void setError(boolean TF) {
\r
116 //console.recallCommand(true);
\r
119 public void sendConsoleMessage(String strStatus) {
\r
120 if (strStatus == null) {
\r
121 console.clearContent();
\r
122 console.outputStatus("");
\r
123 } else if (strStatus.indexOf("ERROR:") >= 0) {
\r
124 console.outputError(strStatus);
\r
126 } else if (!isError) {
\r
127 console.outputStatus(strStatus);
\r
131 public void notifyScriptTermination(String strMsg, int msWalltime) {
\r
132 if (strMsg != null && strMsg.indexOf("ERROR") >= 0) {
\r
133 console.outputError(strMsg);
\r
135 runButton.setEnabled(true);
\r
136 haltButton.setEnabled(false);
\r
139 public void enterPressed() {
\r
140 runButton.doClick(100);
\r
141 // executeCommand();
\r
145 class ExecuteCommandThread extends Thread {
\r
148 ExecuteCommandThread (String command) {
\r
149 strCommand = command;
\r
152 public void run() {
\r
154 executeCommand(strCommand);
\r
155 } catch (Exception ie) {
\r
156 Logger.debug("execution command interrupted!"+ie);
\r
161 ExecuteCommandThread execThread;
\r
162 void executeCommandAsThread(){
\r
163 String strCommand = console.getCommandString().trim();
\r
164 if (strCommand.length() > 0) {
\r
165 execThread = new ExecuteCommandThread(strCommand);
\r
166 execThread.start();
\r
170 void executeCommand(String strCommand) {
\r
173 console.appendNewline();
\r
174 console.setPrompt();
\r
175 if (strCommand.length() > 0) {
\r
176 String strErrorMessage = null;
\r
177 doWait = (strCommand.indexOf("WAIT ") == 0);
\r
178 if (doWait) { //for testing, mainly
\r
179 // demonstrates using the statusManager system.
\r
180 runButton.setEnabled(false);
\r
181 haltButton.setEnabled(true);
\r
183 Vector info = (Vector) viewer
\r
184 .scriptWaitStatus(strCommand.substring(5),
\r
185 "+fileLoaded,+scriptStarted,+scriptStatus,+scriptEcho,+scriptTerminated");
\r
186 runButton.setEnabled(true);
\r
187 haltButton.setEnabled(false);
\r
189 * info = [ statusRecortSet0, statusRecortSet1, statusRecortSet2, ...]
\r
190 * statusRecordSet = [ statusRecord0, statusRecord1, statusRecord2, ...]
\r
191 * statusRecord = [int msgPtr, String statusName, int intInfo, String msg]
\r
193 for (int i = 0; i < info.size(); i++) {
\r
194 Vector statusRecordSet = (Vector) info.get(i);
\r
195 for (int j = 0; j < statusRecordSet.size(); j++) {
\r
196 Vector statusRecord = (Vector) statusRecordSet.get(j);
\r
197 Logger.info("msg#=" + statusRecord.get(0) + " "
\r
198 + statusRecord.get(1) + " intInfo=" + statusRecord.get(2)
\r
199 + " stringInfo=" + statusRecord.get(3));
\r
202 console.appendNewline();
\r
204 boolean isScriptExecuting = viewer.isScriptExecuting();
\r
205 if (viewer.checkHalt(strCommand))
\r
206 strErrorMessage = (isScriptExecuting ? "string execution halted with " + strCommand : "no script was executing");
\r
208 strErrorMessage = "";//viewer.scriptCheck(strCommand);
\r
209 //the problem is that scriptCheck is synchronized, so these might get backed up.
\r
210 if (strErrorMessage != null && strErrorMessage.length() > 0) {
\r
211 console.outputError(strErrorMessage);
\r
213 //runButton.setEnabled(false);
\r
214 haltButton.setEnabled(true);
\r
215 viewer.script(strCommand);
\r
219 console.grabFocus();
\r
222 public void actionPerformed(ActionEvent e) {
\r
223 Object source = e.getSource();
\r
224 if (source == closeButton) {
\r
226 } else if (source == runButton) {
\r
227 executeCommandAsThread();
\r
228 } else if (source == clearButton) {
\r
229 console.clearContent();
\r
230 } else if (source == historyButton) {
\r
231 console.clearContent(viewer.getSetHistory(Integer.MAX_VALUE));
\r
232 } else if (source == stateButton) {
\r
233 console.clearContent(viewer.getStateInfo());
\r
234 } else if (source == haltButton) {
\r
235 viewer.haltScriptExecution();
\r
236 } else if (source == helpButton) {
\r
238 jalview.util.BrowserLauncher.openURL(
\r
239 "http://jmol.sourceforge.net/docs/JmolUserGuide/ch04.html");
\r
240 }catch(Exception ex){}
\r
243 console.grabFocus(); // always grab the focus (e.g., after clear)
\r
247 class ConsoleTextPane extends JTextPane {
\r
249 ConsoleDocument consoleDoc;
\r
250 EnterListener enterListener;
\r
253 ConsoleTextPane(ScriptWindow scriptWindow) {
\r
254 super(new ConsoleDocument());
\r
255 consoleDoc = (ConsoleDocument)getDocument();
\r
256 consoleDoc.setConsoleTextPane(this);
\r
257 this.enterListener = (EnterListener) scriptWindow;
\r
258 this.viewer = scriptWindow.viewer;
\r
261 public String getCommandString() {
\r
262 String cmd = consoleDoc.getCommandString();
\r
266 public void setPrompt() {
\r
267 consoleDoc.setPrompt();
\r
270 public void appendNewline() {
\r
271 consoleDoc.appendNewline();
\r
274 public void outputError(String strError) {
\r
275 consoleDoc.outputError(strError);
\r
278 public void outputErrorForeground(String strError) {
\r
279 consoleDoc.outputErrorForeground(strError);
\r
282 public void outputEcho(String strEcho) {
\r
283 consoleDoc.outputEcho(strEcho);
\r
286 public void outputStatus(String strStatus) {
\r
287 consoleDoc.outputStatus(strStatus);
\r
290 public void enterPressed() {
\r
291 if (enterListener != null)
\r
292 enterListener.enterPressed();
\r
295 public void clearContent() {
\r
296 clearContent(null);
\r
298 public void clearContent(String text) {
\r
299 consoleDoc.clearContent();
\r
301 consoleDoc.outputEcho(text);
\r
306 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
\r
310 * Custom key event processing for command 0 implementation.
\r
312 * Captures key up and key down strokes to call command history
\r
313 * and redefines the same events with control down to allow
\r
314 * caret vertical shift.
\r
316 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
\r
318 protected void processKeyEvent(KeyEvent ke)
\r
320 // Id Control key is down, captures events does command
\r
321 // history recall and inhibits caret vertical shift.
\r
322 if (ke.getKeyCode() == KeyEvent.VK_UP
\r
323 && ke.getID() == KeyEvent.KEY_PRESSED
\r
324 && !ke.isControlDown())
\r
326 recallCommand(true);
\r
329 ke.getKeyCode() == KeyEvent.VK_DOWN
\r
330 && ke.getID() == KeyEvent.KEY_PRESSED
\r
331 && !ke.isControlDown())
\r
333 recallCommand(false);
\r
335 // If Control key is down, redefines the event as if it
\r
336 // where a key up or key down stroke without modifiers.
\r
337 // This allows to move the caret up and down
\r
338 // with no command history recall.
\r
340 (ke.getKeyCode() == KeyEvent.VK_DOWN
\r
341 || ke.getKeyCode() == KeyEvent.VK_UP)
\r
342 && ke.getID() == KeyEvent.KEY_PRESSED
\r
343 && ke.isControlDown())
\r
346 .processKeyEvent(new KeyEvent(
\r
347 (Component) ke.getSource(),
\r
353 ke.getKeyLocation()));
\r
355 // Standard processing for other events.
\r
358 super.processKeyEvent(ke);
\r
359 //check command for compiler-identifyable syntax issues
\r
360 //this may have to be taken out if people start complaining
\r
361 //that only some of the commands are being checked
\r
362 //that is -- that the script itself is not being fully checked
\r
364 //not perfect -- help here?
\r
365 if (ke.getID() == KeyEvent.KEY_RELEASED
\r
366 && (ke.getKeyCode() > KeyEvent.VK_DOWN) || ke.getKeyCode() == KeyEvent.VK_BACK_SPACE)
\r
372 * Recall command history.
\r
374 * @param up - history up or down
\r
376 void recallCommand(boolean up) {
\r
377 String cmd = viewer.getSetHistory(up ? -1 : 1);
\r
382 if (cmd.endsWith(CommandHistory.ERROR_FLAG)) {
\r
383 cmd = cmd.substring(0, cmd.indexOf(CommandHistory.ERROR_FLAG));
\r
384 consoleDoc.replaceCommand(cmd, true);
\r
386 consoleDoc.replaceCommand(cmd, false);
\r
388 } catch (BadLocationException e) {
\r
389 e.printStackTrace();
\r
393 void checkCommand() {
\r
394 String strCommand = consoleDoc.getCommandString();
\r
395 if (strCommand.length() == 0)
\r
398 .colorCommand(viewer.scriptCheck(strCommand) == null ? consoleDoc.attUserInput
\r
399 : consoleDoc.attError);
\r
405 class ConsoleDocument extends DefaultStyledDocument {
\r
407 ConsoleTextPane consoleTextPane;
\r
409 SimpleAttributeSet attError;
\r
410 SimpleAttributeSet attEcho;
\r
411 SimpleAttributeSet attPrompt;
\r
412 SimpleAttributeSet attUserInput;
\r
413 SimpleAttributeSet attStatus;
\r
415 ConsoleDocument() {
\r
418 attError = new SimpleAttributeSet();
\r
419 StyleConstants.setForeground(attError, Color.red);
\r
421 attPrompt = new SimpleAttributeSet();
\r
422 StyleConstants.setForeground(attPrompt, Color.magenta);
\r
424 attUserInput = new SimpleAttributeSet();
\r
425 StyleConstants.setForeground(attUserInput, Color.black);
\r
427 attEcho = new SimpleAttributeSet();
\r
428 StyleConstants.setForeground(attEcho, Color.blue);
\r
429 StyleConstants.setBold(attEcho, true);
\r
431 attStatus = new SimpleAttributeSet();
\r
432 StyleConstants.setForeground(attStatus, Color.black);
\r
433 StyleConstants.setItalic(attStatus, true);
\r
436 void setConsoleTextPane(ConsoleTextPane consoleTextPane) {
\r
437 this.consoleTextPane = consoleTextPane;
\r
440 Position positionBeforePrompt; // starts at 0, so first time isn't tracked (at least on Mac OS X)
\r
441 Position positionAfterPrompt; // immediately after $, so this will track
\r
442 int offsetAfterPrompt; // only still needed for the insertString override and replaceCommand
\r
445 * Removes all content of the script window, and add a new prompt.
\r
447 void clearContent() {
\r
449 super.remove(0, getLength());
\r
450 } catch (BadLocationException exception) {
\r
451 System.out.println("Could not clear script window content: " + exception.getMessage());
\r
457 super.insertString(getLength(), "$ ", attPrompt);
\r
458 setOffsetPositions();
\r
459 consoleTextPane.setCaretPosition(offsetAfterPrompt);
\r
460 } catch (BadLocationException e) {
\r
461 e.printStackTrace();
\r
465 void setOffsetPositions() {
\r
467 offsetAfterPrompt = getLength();
\r
468 positionBeforePrompt = createPosition(offsetAfterPrompt - 2);
\r
469 // after prompt should be immediately after $ otherwise tracks the end
\r
470 // of the line (and no command will be found) at least on Mac OS X it did.
\r
471 positionAfterPrompt = createPosition(offsetAfterPrompt - 1);
\r
472 } catch (BadLocationException e) {
\r
473 e.printStackTrace();
\r
477 void setNoPrompt() {
\r
479 offsetAfterPrompt = getLength();
\r
480 positionAfterPrompt = positionBeforePrompt = createPosition(offsetAfterPrompt);
\r
481 consoleTextPane.setCaretPosition(offsetAfterPrompt);
\r
482 } catch (BadLocationException e) {
\r
483 e.printStackTrace();
\r
487 // it looks like the positionBeforePrompt does not track when it started out as 0
\r
488 // and a insertString at location 0 occurs. It may be better to track the
\r
489 // position after the prompt in stead
\r
490 void outputBeforePrompt(String str, SimpleAttributeSet attribute) {
\r
492 int pt = consoleTextPane.getCaretPosition();
\r
493 Position caretPosition = createPosition(pt);
\r
494 pt = positionBeforePrompt.getOffset();
\r
495 super.insertString(pt, str+"\n", attribute);
\r
496 setOffsetPositions();
\r
497 pt = caretPosition.getOffset();
\r
498 consoleTextPane.setCaretPosition(pt);
\r
499 } catch (BadLocationException e) {
\r
500 e.printStackTrace();
\r
504 void outputError(String strError) {
\r
505 outputBeforePrompt(strError, attError);
\r
508 void outputErrorForeground(String strError) {
\r
510 super.insertString(getLength(), strError+"\n", attError);
\r
511 consoleTextPane.setCaretPosition(getLength());
\r
512 } catch (BadLocationException e) {
\r
513 e.printStackTrace();
\r
518 void outputEcho(String strEcho) {
\r
519 outputBeforePrompt(strEcho, attEcho);
\r
522 void outputStatus(String strStatus) {
\r
523 outputBeforePrompt(strStatus, attStatus);
\r
526 void appendNewline() {
\r
528 super.insertString(getLength(), "\n", attUserInput);
\r
529 consoleTextPane.setCaretPosition(getLength());
\r
530 } catch (BadLocationException e) {
\r
531 e.printStackTrace();
\r
535 // override the insertString to make sure everything typed ends up at the end
\r
536 // or in the 'command line' using the proper font, and the newline is processed.
\r
537 public void insertString(int offs, String str, AttributeSet a)
\r
538 throws BadLocationException {
\r
539 int ichNewline = str.indexOf('\n');
\r
540 if (ichNewline > 0)
\r
541 str = str.substring(0, ichNewline);
\r
542 if (ichNewline != 0) {
\r
543 if (offs < offsetAfterPrompt) {
\r
544 offs = getLength();
\r
546 super.insertString(offs, str, a == attError ? a : attUserInput);
\r
547 consoleTextPane.setCaretPosition(offs+str.length());
\r
549 if (ichNewline >= 0) {
\r
550 consoleTextPane.enterPressed();
\r
554 String getCommandString() {
\r
555 String strCommand = "";
\r
557 int cmdStart = positionAfterPrompt.getOffset();
\r
558 strCommand = getText(cmdStart, getLength() - cmdStart);
\r
559 while (strCommand.length() > 0 && strCommand.charAt(0) == ' ')
\r
560 strCommand = strCommand.substring(1);
\r
561 } catch (BadLocationException e) {
\r
562 e.printStackTrace();
\r
567 public void remove(int offs, int len)
\r
568 throws BadLocationException {
\r
569 if (offs < offsetAfterPrompt) {
\r
570 len -= offsetAfterPrompt - offs;
\r
573 offs = offsetAfterPrompt;
\r
575 super.remove(offs, len);
\r
576 // consoleTextPane.setCaretPosition(offs);
\r
579 public void replace(int offs, int length, String str, AttributeSet attrs)
\r
580 throws BadLocationException {
\r
581 if (offs < offsetAfterPrompt) {
\r
582 if (offs + length < offsetAfterPrompt) {
\r
583 offs = getLength();
\r
586 length -= offsetAfterPrompt - offs;
\r
587 offs = offsetAfterPrompt;
\r
590 super.replace(offs, length, str, attrs);
\r
591 // consoleTextPane.setCaretPosition(offs + str.length());
\r
595 * Replaces current command on script.
\r
597 * @param newCommand new command value
\r
598 * @param isError true to set error color ends with #??
\r
600 * @throws BadLocationException
\r
602 void replaceCommand(String newCommand, boolean isError) throws BadLocationException {
\r
603 if (positionAfterPrompt == positionBeforePrompt)
\r
605 replace(offsetAfterPrompt, getLength() - offsetAfterPrompt, newCommand,
\r
606 isError ? attError : attUserInput);
\r
609 void colorCommand(SimpleAttributeSet att) {
\r
610 if (positionAfterPrompt == positionBeforePrompt)
\r
612 setCharacterAttributes(offsetAfterPrompt, getLength() - offsetAfterPrompt, att, true);
\r
616 interface EnterListener {
\r
617 public void enterPressed();
\r