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
77 runButton.addActionListener(this);
\r
78 buttonPanel.add(runButton);
\r
79 haltButton.addActionListener(this);
\r
80 buttonPanel.add(haltButton);
\r
81 haltButton.setEnabled(false);*/
\r
83 clearButton = new JButton(GT._("Clear"));
\r
84 clearButton.addActionListener(this);
\r
85 buttonPanel.add(clearButton);
\r
87 historyButton = new JButton(GT._("History"));
\r
88 historyButton.addActionListener(this);
\r
89 buttonPanel.add(historyButton);
\r
91 stateButton = new JButton(GT._("State"));
\r
92 stateButton.addActionListener(this);
\r
93 buttonPanel.add(stateButton);
\r
95 closeButton = new JButton(GT._("Close"));
\r
96 closeButton.addActionListener(this);
\r
97 buttonPanel.add(closeButton);
\r
99 for(int i=0; i<buttonPanel.getComponentCount(); i++)
\r
101 // ((JButton)buttonPanel.getComponent(i))
\r
102 // .setMargin(new Insets(0, 0, 0, 0));
\r
107 public void sendConsoleEcho(String strEcho) {
\r
108 if (strEcho != null && !isError) {
\r
110 console.outputEcho(strEcho);
\r
116 boolean isError = false;
\r
117 void setError(boolean TF) {
\r
120 //console.recallCommand(true);
\r
123 public void sendConsoleMessage(String strStatus) {
\r
124 if (strStatus == null) {
\r
125 console.clearContent();
\r
126 console.outputStatus("");
\r
127 } else if (strStatus.indexOf("ERROR:") >= 0) {
\r
128 console.outputError(strStatus);
\r
130 } else if (!isError) {
\r
131 console.outputStatus(strStatus);
\r
135 public void notifyScriptTermination(String strMsg, int msWalltime) {
\r
136 if (strMsg != null && strMsg.indexOf("ERROR") >= 0) {
\r
137 console.outputError(strMsg);
\r
139 runButton.setEnabled(true);
\r
140 haltButton.setEnabled(false);
\r
143 public void enterPressed() {
\r
144 runButton.doClick(100);
\r
145 // executeCommand();
\r
149 class ExecuteCommandThread extends Thread {
\r
152 ExecuteCommandThread (String command) {
\r
153 strCommand = command;
\r
156 public void run() {
\r
158 executeCommand(strCommand);
\r
159 } catch (Exception ie) {
\r
160 Logger.debug("execution command interrupted!"+ie);
\r
165 ExecuteCommandThread execThread;
\r
166 void executeCommandAsThread(){
\r
167 String strCommand = console.getCommandString().trim();
\r
168 if (strCommand.length() > 0) {
\r
169 execThread = new ExecuteCommandThread(strCommand);
\r
170 execThread.start();
\r
174 void executeCommand(String strCommand) {
\r
177 console.appendNewline();
\r
178 console.setPrompt();
\r
179 if (strCommand.length() > 0) {
\r
180 String strErrorMessage = null;
\r
181 doWait = (strCommand.indexOf("WAIT ") == 0);
\r
182 if (doWait) { //for testing, mainly
\r
183 // demonstrates using the statusManager system.
\r
184 runButton.setEnabled(false);
\r
185 haltButton.setEnabled(true);
\r
187 Vector info = (Vector) viewer
\r
188 .scriptWaitStatus(strCommand.substring(5),
\r
189 "+fileLoaded,+scriptStarted,+scriptStatus,+scriptEcho,+scriptTerminated");
\r
190 runButton.setEnabled(true);
\r
191 haltButton.setEnabled(false);
\r
193 * info = [ statusRecortSet0, statusRecortSet1, statusRecortSet2, ...]
\r
194 * statusRecordSet = [ statusRecord0, statusRecord1, statusRecord2, ...]
\r
195 * statusRecord = [int msgPtr, String statusName, int intInfo, String msg]
\r
197 for (int i = 0; i < info.size(); i++) {
\r
198 Vector statusRecordSet = (Vector) info.get(i);
\r
199 for (int j = 0; j < statusRecordSet.size(); j++) {
\r
200 Vector statusRecord = (Vector) statusRecordSet.get(j);
\r
201 Logger.info("msg#=" + statusRecord.get(0) + " "
\r
202 + statusRecord.get(1) + " intInfo=" + statusRecord.get(2)
\r
203 + " stringInfo=" + statusRecord.get(3));
\r
206 console.appendNewline();
\r
208 boolean isScriptExecuting = viewer.isScriptExecuting();
\r
209 if (viewer.checkHalt(strCommand))
\r
210 strErrorMessage = (isScriptExecuting ? "string execution halted with " + strCommand : "no script was executing");
\r
212 strErrorMessage = "";//viewer.scriptCheck(strCommand);
\r
213 //the problem is that scriptCheck is synchronized, so these might get backed up.
\r
214 if (strErrorMessage != null && strErrorMessage.length() > 0) {
\r
215 console.outputError(strErrorMessage);
\r
217 //runButton.setEnabled(false);
\r
218 haltButton.setEnabled(true);
\r
219 viewer.script(strCommand);
\r
223 console.grabFocus();
\r
226 public void actionPerformed(ActionEvent e) {
\r
227 Object source = e.getSource();
\r
228 if (source == closeButton) {
\r
229 appJmol.showConsole(false);
\r
230 } else if (source == runButton) {
\r
231 executeCommandAsThread();
\r
232 } else if (source == clearButton) {
\r
233 console.clearContent();
\r
234 } else if (source == historyButton) {
\r
235 console.clearContent(viewer.getSetHistory(Integer.MAX_VALUE));
\r
236 } else if (source == stateButton) {
\r
237 console.clearContent(viewer.getStateInfo());
\r
238 } else if (source == haltButton) {
\r
239 viewer.haltScriptExecution();
\r
241 console.grabFocus(); // always grab the focus (e.g., after clear)
\r
245 class ConsoleTextPane extends JTextPane {
\r
247 ConsoleDocument consoleDoc;
\r
248 EnterListener enterListener;
\r
251 ConsoleTextPane(ScriptWindow scriptWindow) {
\r
252 super(new ConsoleDocument());
\r
253 consoleDoc = (ConsoleDocument)getDocument();
\r
254 consoleDoc.setConsoleTextPane(this);
\r
255 this.enterListener = (EnterListener) scriptWindow;
\r
256 this.viewer = scriptWindow.viewer;
\r
259 public String getCommandString() {
\r
260 String cmd = consoleDoc.getCommandString();
\r
264 public void setPrompt() {
\r
265 consoleDoc.setPrompt();
\r
268 public void appendNewline() {
\r
269 consoleDoc.appendNewline();
\r
272 public void outputError(String strError) {
\r
273 consoleDoc.outputError(strError);
\r
276 public void outputErrorForeground(String strError) {
\r
277 consoleDoc.outputErrorForeground(strError);
\r
280 public void outputEcho(String strEcho) {
\r
281 consoleDoc.outputEcho(strEcho);
\r
284 public void outputStatus(String strStatus) {
\r
285 consoleDoc.outputStatus(strStatus);
\r
288 public void enterPressed() {
\r
289 if (enterListener != null)
\r
290 enterListener.enterPressed();
\r
293 public void clearContent() {
\r
294 clearContent(null);
\r
296 public void clearContent(String text) {
\r
297 consoleDoc.clearContent();
\r
299 consoleDoc.outputEcho(text);
\r
304 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
\r
308 * Custom key event processing for command 0 implementation.
\r
310 * Captures key up and key down strokes to call command history
\r
311 * and redefines the same events with control down to allow
\r
312 * caret vertical shift.
\r
314 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
\r
316 protected void processKeyEvent(KeyEvent ke)
\r
318 // Id Control key is down, captures events does command
\r
319 // history recall and inhibits caret vertical shift.
\r
320 if (ke.getKeyCode() == KeyEvent.VK_UP
\r
321 && ke.getID() == KeyEvent.KEY_PRESSED
\r
322 && !ke.isControlDown())
\r
324 recallCommand(true);
\r
327 ke.getKeyCode() == KeyEvent.VK_DOWN
\r
328 && ke.getID() == KeyEvent.KEY_PRESSED
\r
329 && !ke.isControlDown())
\r
331 recallCommand(false);
\r
333 // If Control key is down, redefines the event as if it
\r
334 // where a key up or key down stroke without modifiers.
\r
335 // This allows to move the caret up and down
\r
336 // with no command history recall.
\r
338 (ke.getKeyCode() == KeyEvent.VK_DOWN
\r
339 || ke.getKeyCode() == KeyEvent.VK_UP)
\r
340 && ke.getID() == KeyEvent.KEY_PRESSED
\r
341 && ke.isControlDown())
\r
344 .processKeyEvent(new KeyEvent(
\r
345 (Component) ke.getSource(),
\r
351 ke.getKeyLocation()));
\r
353 // Standard processing for other events.
\r
356 super.processKeyEvent(ke);
\r
357 //check command for compiler-identifyable syntax issues
\r
358 //this may have to be taken out if people start complaining
\r
359 //that only some of the commands are being checked
\r
360 //that is -- that the script itself is not being fully checked
\r
362 //not perfect -- help here?
\r
363 if (ke.getID() == KeyEvent.KEY_RELEASED
\r
364 && (ke.getKeyCode() > KeyEvent.VK_DOWN) || ke.getKeyCode() == KeyEvent.VK_BACK_SPACE)
\r
370 * Recall command history.
\r
372 * @param up - history up or down
\r
374 void recallCommand(boolean up) {
\r
375 String cmd = viewer.getSetHistory(up ? -1 : 1);
\r
380 if (cmd.endsWith(CommandHistory.ERROR_FLAG)) {
\r
381 cmd = cmd.substring(0, cmd.indexOf(CommandHistory.ERROR_FLAG));
\r
382 consoleDoc.replaceCommand(cmd, true);
\r
384 consoleDoc.replaceCommand(cmd, false);
\r
386 } catch (BadLocationException e) {
\r
387 e.printStackTrace();
\r
391 void checkCommand() {
\r
392 String strCommand = consoleDoc.getCommandString();
\r
393 if (strCommand.length() == 0)
\r
396 .colorCommand(viewer.scriptCheck(strCommand) == null ? consoleDoc.attUserInput
\r
397 : consoleDoc.attError);
\r
403 class ConsoleDocument extends DefaultStyledDocument {
\r
405 ConsoleTextPane consoleTextPane;
\r
407 SimpleAttributeSet attError;
\r
408 SimpleAttributeSet attEcho;
\r
409 SimpleAttributeSet attPrompt;
\r
410 SimpleAttributeSet attUserInput;
\r
411 SimpleAttributeSet attStatus;
\r
413 ConsoleDocument() {
\r
416 attError = new SimpleAttributeSet();
\r
417 StyleConstants.setForeground(attError, Color.red);
\r
419 attPrompt = new SimpleAttributeSet();
\r
420 StyleConstants.setForeground(attPrompt, Color.magenta);
\r
422 attUserInput = new SimpleAttributeSet();
\r
423 StyleConstants.setForeground(attUserInput, Color.black);
\r
425 attEcho = new SimpleAttributeSet();
\r
426 StyleConstants.setForeground(attEcho, Color.blue);
\r
427 StyleConstants.setBold(attEcho, true);
\r
429 attStatus = new SimpleAttributeSet();
\r
430 StyleConstants.setForeground(attStatus, Color.black);
\r
431 StyleConstants.setItalic(attStatus, true);
\r
434 void setConsoleTextPane(ConsoleTextPane consoleTextPane) {
\r
435 this.consoleTextPane = consoleTextPane;
\r
438 Position positionBeforePrompt; // starts at 0, so first time isn't tracked (at least on Mac OS X)
\r
439 Position positionAfterPrompt; // immediately after $, so this will track
\r
440 int offsetAfterPrompt; // only still needed for the insertString override and replaceCommand
\r
443 * Removes all content of the script window, and add a new prompt.
\r
445 void clearContent() {
\r
447 super.remove(0, getLength());
\r
448 } catch (BadLocationException exception) {
\r
449 System.out.println("Could not clear script window content: " + exception.getMessage());
\r
455 super.insertString(getLength(), "$ ", attPrompt);
\r
456 setOffsetPositions();
\r
457 consoleTextPane.setCaretPosition(offsetAfterPrompt);
\r
458 } catch (BadLocationException e) {
\r
459 e.printStackTrace();
\r
463 void setOffsetPositions() {
\r
465 offsetAfterPrompt = getLength();
\r
466 positionBeforePrompt = createPosition(offsetAfterPrompt - 2);
\r
467 // after prompt should be immediately after $ otherwise tracks the end
\r
468 // of the line (and no command will be found) at least on Mac OS X it did.
\r
469 positionAfterPrompt = createPosition(offsetAfterPrompt - 1);
\r
470 } catch (BadLocationException e) {
\r
471 e.printStackTrace();
\r
475 void setNoPrompt() {
\r
477 offsetAfterPrompt = getLength();
\r
478 positionAfterPrompt = positionBeforePrompt = createPosition(offsetAfterPrompt);
\r
479 consoleTextPane.setCaretPosition(offsetAfterPrompt);
\r
480 } catch (BadLocationException e) {
\r
481 e.printStackTrace();
\r
485 // it looks like the positionBeforePrompt does not track when it started out as 0
\r
486 // and a insertString at location 0 occurs. It may be better to track the
\r
487 // position after the prompt in stead
\r
488 void outputBeforePrompt(String str, SimpleAttributeSet attribute) {
\r
490 int pt = consoleTextPane.getCaretPosition();
\r
491 Position caretPosition = createPosition(pt);
\r
492 pt = positionBeforePrompt.getOffset();
\r
493 super.insertString(pt, str+"\n", attribute);
\r
494 setOffsetPositions();
\r
495 pt = caretPosition.getOffset();
\r
496 consoleTextPane.setCaretPosition(pt);
\r
497 } catch (BadLocationException e) {
\r
498 e.printStackTrace();
\r
502 void outputError(String strError) {
\r
503 outputBeforePrompt(strError, attError);
\r
506 void outputErrorForeground(String strError) {
\r
508 super.insertString(getLength(), strError+"\n", attError);
\r
509 consoleTextPane.setCaretPosition(getLength());
\r
510 } catch (BadLocationException e) {
\r
511 e.printStackTrace();
\r
516 void outputEcho(String strEcho) {
\r
517 outputBeforePrompt(strEcho, attEcho);
\r
520 void outputStatus(String strStatus) {
\r
521 outputBeforePrompt(strStatus, attStatus);
\r
524 void appendNewline() {
\r
526 super.insertString(getLength(), "\n", attUserInput);
\r
527 consoleTextPane.setCaretPosition(getLength());
\r
528 } catch (BadLocationException e) {
\r
529 e.printStackTrace();
\r
533 // override the insertString to make sure everything typed ends up at the end
\r
534 // or in the 'command line' using the proper font, and the newline is processed.
\r
535 public void insertString(int offs, String str, AttributeSet a)
\r
536 throws BadLocationException {
\r
537 int ichNewline = str.indexOf('\n');
\r
538 if (ichNewline > 0)
\r
539 str = str.substring(0, ichNewline);
\r
540 if (ichNewline != 0) {
\r
541 if (offs < offsetAfterPrompt) {
\r
542 offs = getLength();
\r
544 super.insertString(offs, str, a == attError ? a : attUserInput);
\r
545 consoleTextPane.setCaretPosition(offs+str.length());
\r
547 if (ichNewline >= 0) {
\r
548 consoleTextPane.enterPressed();
\r
552 String getCommandString() {
\r
553 String strCommand = "";
\r
555 int cmdStart = positionAfterPrompt.getOffset();
\r
556 strCommand = getText(cmdStart, getLength() - cmdStart);
\r
557 while (strCommand.length() > 0 && strCommand.charAt(0) == ' ')
\r
558 strCommand = strCommand.substring(1);
\r
559 } catch (BadLocationException e) {
\r
560 e.printStackTrace();
\r
565 public void remove(int offs, int len)
\r
566 throws BadLocationException {
\r
567 if (offs < offsetAfterPrompt) {
\r
568 len -= offsetAfterPrompt - offs;
\r
571 offs = offsetAfterPrompt;
\r
573 super.remove(offs, len);
\r
574 // consoleTextPane.setCaretPosition(offs);
\r
577 public void replace(int offs, int length, String str, AttributeSet attrs)
\r
578 throws BadLocationException {
\r
579 if (offs < offsetAfterPrompt) {
\r
580 if (offs + length < offsetAfterPrompt) {
\r
581 offs = getLength();
\r
584 length -= offsetAfterPrompt - offs;
\r
585 offs = offsetAfterPrompt;
\r
588 super.replace(offs, length, str, attrs);
\r
589 // consoleTextPane.setCaretPosition(offs + str.length());
\r
593 * Replaces current command on script.
\r
595 * @param newCommand new command value
\r
596 * @param isError true to set error color ends with #??
\r
598 * @throws BadLocationException
\r
600 void replaceCommand(String newCommand, boolean isError) throws BadLocationException {
\r
601 if (positionAfterPrompt == positionBeforePrompt)
\r
603 replace(offsetAfterPrompt, getLength() - offsetAfterPrompt, newCommand,
\r
604 isError ? attError : attUserInput);
\r
607 void colorCommand(SimpleAttributeSet att) {
\r
608 if (positionAfterPrompt == positionBeforePrompt)
\r
610 setCharacterAttributes(offsetAfterPrompt, getLength() - offsetAfterPrompt, att, true);
\r
614 interface EnterListener {
\r
615 public void enterPressed();
\r