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
53 private JButton helpButton;
\r
57 public ScriptWindow(AppJMol appJmol)
\r
59 this.viewer = appJmol.viewer;
\r
60 this.appJmol = appJmol;
\r
63 setLayout(new BorderLayout());
\r
65 console = new ConsoleTextPane(this);
\r
68 console.setPrompt();
\r
69 add(new JScrollPane(console)
\r
70 , BorderLayout.CENTER);
\r
72 JPanel buttonPanel = new JPanel();
\r
73 add(buttonPanel, BorderLayout.SOUTH);
\r
75 runButton = new JButton(GT._("Run"));
\r
76 runButton.addActionListener(this);
\r
77 buttonPanel.add(runButton);
\r
79 haltButton = new JButton(GT._("Halt"));
\r
80 haltButton.addActionListener(this);
\r
81 buttonPanel.add(haltButton);
\r
82 haltButton.setEnabled(false);
\r
84 clearButton = new JButton(GT._("Clear"));
\r
85 clearButton.addActionListener(this);
\r
86 buttonPanel.add(clearButton);
\r
88 historyButton = new JButton(GT._("History"));
\r
89 historyButton.addActionListener(this);
\r
90 buttonPanel.add(historyButton);
\r
92 stateButton = new JButton(GT._("State"));
\r
93 stateButton.addActionListener(this);
\r
94 buttonPanel.add(stateButton);
\r
96 helpButton = new JButton(GT._("Help"));
\r
97 helpButton.addActionListener(this);
\r
98 buttonPanel.add(helpButton);
\r
100 closeButton = new JButton(GT._("Close"));
\r
101 closeButton.addActionListener(this);
\r
102 buttonPanel.add(closeButton);
\r
104 for(int i=0; i<buttonPanel.getComponentCount(); i++)
\r
106 ((JButton)buttonPanel.getComponent(i))
\r
107 .setMargin(new Insets(0, 0, 0, 0));
\r
112 public void sendConsoleEcho(String strEcho) {
\r
113 if (strEcho != null && !isError) {
\r
115 console.outputEcho(strEcho);
\r
121 boolean isError = false;
\r
122 void setError(boolean TF) {
\r
125 //console.recallCommand(true);
\r
128 public void sendConsoleMessage(String strStatus) {
\r
129 if (strStatus == null) {
\r
130 console.clearContent();
\r
131 console.outputStatus("");
\r
132 } else if (strStatus.indexOf("ERROR:") >= 0) {
\r
133 console.outputError(strStatus);
\r
135 } else if (!isError) {
\r
136 console.outputStatus(strStatus);
\r
140 public void notifyScriptTermination(String strMsg, int msWalltime) {
\r
141 if (strMsg != null && strMsg.indexOf("ERROR") >= 0) {
\r
142 console.outputError(strMsg);
\r
144 runButton.setEnabled(true);
\r
145 haltButton.setEnabled(false);
\r
148 public void enterPressed() {
\r
149 runButton.doClick(100);
\r
150 // executeCommand();
\r
154 class ExecuteCommandThread extends Thread {
\r
157 ExecuteCommandThread (String command) {
\r
158 strCommand = command;
\r
161 public void run() {
\r
163 executeCommand(strCommand);
\r
164 } catch (Exception ie) {
\r
165 Logger.debug("execution command interrupted!"+ie);
\r
170 ExecuteCommandThread execThread;
\r
171 void executeCommandAsThread(){
\r
172 String strCommand = console.getCommandString().trim();
\r
173 if (strCommand.length() > 0) {
\r
174 execThread = new ExecuteCommandThread(strCommand);
\r
175 execThread.start();
\r
179 void executeCommand(String strCommand) {
\r
182 console.appendNewline();
\r
183 console.setPrompt();
\r
184 if (strCommand.length() > 0) {
\r
185 String strErrorMessage = null;
\r
186 doWait = (strCommand.indexOf("WAIT ") == 0);
\r
187 if (doWait) { //for testing, mainly
\r
188 // demonstrates using the statusManager system.
\r
189 runButton.setEnabled(false);
\r
190 haltButton.setEnabled(true);
\r
192 Vector info = (Vector) viewer
\r
193 .scriptWaitStatus(strCommand.substring(5),
\r
194 "+fileLoaded,+scriptStarted,+scriptStatus,+scriptEcho,+scriptTerminated");
\r
195 runButton.setEnabled(true);
\r
196 haltButton.setEnabled(false);
\r
198 * info = [ statusRecortSet0, statusRecortSet1, statusRecortSet2, ...]
\r
199 * statusRecordSet = [ statusRecord0, statusRecord1, statusRecord2, ...]
\r
200 * statusRecord = [int msgPtr, String statusName, int intInfo, String msg]
\r
202 for (int i = 0; i < info.size(); i++) {
\r
203 Vector statusRecordSet = (Vector) info.get(i);
\r
204 for (int j = 0; j < statusRecordSet.size(); j++) {
\r
205 Vector statusRecord = (Vector) statusRecordSet.get(j);
\r
206 Logger.info("msg#=" + statusRecord.get(0) + " "
\r
207 + statusRecord.get(1) + " intInfo=" + statusRecord.get(2)
\r
208 + " stringInfo=" + statusRecord.get(3));
\r
211 console.appendNewline();
\r
213 boolean isScriptExecuting = viewer.isScriptExecuting();
\r
214 if (viewer.checkHalt(strCommand))
\r
215 strErrorMessage = (isScriptExecuting ? "string execution halted with " + strCommand : "no script was executing");
\r
217 strErrorMessage = "";//viewer.scriptCheck(strCommand);
\r
218 //the problem is that scriptCheck is synchronized, so these might get backed up.
\r
219 if (strErrorMessage != null && strErrorMessage.length() > 0) {
\r
220 console.outputError(strErrorMessage);
\r
222 //runButton.setEnabled(false);
\r
223 haltButton.setEnabled(true);
\r
224 viewer.script(strCommand);
\r
228 console.grabFocus();
\r
231 public void actionPerformed(ActionEvent e) {
\r
232 Object source = e.getSource();
\r
233 if (source == closeButton) {
\r
234 appJmol.showConsole(false);
\r
235 } else if (source == runButton) {
\r
236 executeCommandAsThread();
\r
237 } else if (source == clearButton) {
\r
238 console.clearContent();
\r
239 } else if (source == historyButton) {
\r
240 console.clearContent(viewer.getSetHistory(Integer.MAX_VALUE));
\r
241 } else if (source == stateButton) {
\r
242 console.clearContent(viewer.getStateInfo());
\r
243 } else if (source == haltButton) {
\r
244 viewer.haltScriptExecution();
\r
245 } else if (source == helpButton) {
\r
247 jalview.util.BrowserLauncher.openURL(
\r
248 "http://jmol.sourceforge.net/docs/JmolUserGuide/ch04.html");
\r
249 }catch(Exception ex){}
\r
252 console.grabFocus(); // always grab the focus (e.g., after clear)
\r
256 class ConsoleTextPane extends JTextPane {
\r
258 ConsoleDocument consoleDoc;
\r
259 EnterListener enterListener;
\r
262 ConsoleTextPane(ScriptWindow scriptWindow) {
\r
263 super(new ConsoleDocument());
\r
264 consoleDoc = (ConsoleDocument)getDocument();
\r
265 consoleDoc.setConsoleTextPane(this);
\r
266 this.enterListener = (EnterListener) scriptWindow;
\r
267 this.viewer = scriptWindow.viewer;
\r
270 public String getCommandString() {
\r
271 String cmd = consoleDoc.getCommandString();
\r
275 public void setPrompt() {
\r
276 consoleDoc.setPrompt();
\r
279 public void appendNewline() {
\r
280 consoleDoc.appendNewline();
\r
283 public void outputError(String strError) {
\r
284 consoleDoc.outputError(strError);
\r
287 public void outputErrorForeground(String strError) {
\r
288 consoleDoc.outputErrorForeground(strError);
\r
291 public void outputEcho(String strEcho) {
\r
292 consoleDoc.outputEcho(strEcho);
\r
295 public void outputStatus(String strStatus) {
\r
296 consoleDoc.outputStatus(strStatus);
\r
299 public void enterPressed() {
\r
300 if (enterListener != null)
\r
301 enterListener.enterPressed();
\r
304 public void clearContent() {
\r
305 clearContent(null);
\r
307 public void clearContent(String text) {
\r
308 consoleDoc.clearContent();
\r
310 consoleDoc.outputEcho(text);
\r
315 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
\r
319 * Custom key event processing for command 0 implementation.
\r
321 * Captures key up and key down strokes to call command history
\r
322 * and redefines the same events with control down to allow
\r
323 * caret vertical shift.
\r
325 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
\r
327 protected void processKeyEvent(KeyEvent ke)
\r
329 // Id Control key is down, captures events does command
\r
330 // history recall and inhibits caret vertical shift.
\r
331 if (ke.getKeyCode() == KeyEvent.VK_UP
\r
332 && ke.getID() == KeyEvent.KEY_PRESSED
\r
333 && !ke.isControlDown())
\r
335 recallCommand(true);
\r
338 ke.getKeyCode() == KeyEvent.VK_DOWN
\r
339 && ke.getID() == KeyEvent.KEY_PRESSED
\r
340 && !ke.isControlDown())
\r
342 recallCommand(false);
\r
344 // If Control key is down, redefines the event as if it
\r
345 // where a key up or key down stroke without modifiers.
\r
346 // This allows to move the caret up and down
\r
347 // with no command history recall.
\r
349 (ke.getKeyCode() == KeyEvent.VK_DOWN
\r
350 || ke.getKeyCode() == KeyEvent.VK_UP)
\r
351 && ke.getID() == KeyEvent.KEY_PRESSED
\r
352 && ke.isControlDown())
\r
355 .processKeyEvent(new KeyEvent(
\r
356 (Component) ke.getSource(),
\r
362 ke.getKeyLocation()));
\r
364 // Standard processing for other events.
\r
367 super.processKeyEvent(ke);
\r
368 //check command for compiler-identifyable syntax issues
\r
369 //this may have to be taken out if people start complaining
\r
370 //that only some of the commands are being checked
\r
371 //that is -- that the script itself is not being fully checked
\r
373 //not perfect -- help here?
\r
374 if (ke.getID() == KeyEvent.KEY_RELEASED
\r
375 && (ke.getKeyCode() > KeyEvent.VK_DOWN) || ke.getKeyCode() == KeyEvent.VK_BACK_SPACE)
\r
381 * Recall command history.
\r
383 * @param up - history up or down
\r
385 void recallCommand(boolean up) {
\r
386 String cmd = viewer.getSetHistory(up ? -1 : 1);
\r
391 if (cmd.endsWith(CommandHistory.ERROR_FLAG)) {
\r
392 cmd = cmd.substring(0, cmd.indexOf(CommandHistory.ERROR_FLAG));
\r
393 consoleDoc.replaceCommand(cmd, true);
\r
395 consoleDoc.replaceCommand(cmd, false);
\r
397 } catch (BadLocationException e) {
\r
398 e.printStackTrace();
\r
402 void checkCommand() {
\r
403 String strCommand = consoleDoc.getCommandString();
\r
404 if (strCommand.length() == 0)
\r
407 .colorCommand(viewer.scriptCheck(strCommand) == null ? consoleDoc.attUserInput
\r
408 : consoleDoc.attError);
\r
414 class ConsoleDocument extends DefaultStyledDocument {
\r
416 ConsoleTextPane consoleTextPane;
\r
418 SimpleAttributeSet attError;
\r
419 SimpleAttributeSet attEcho;
\r
420 SimpleAttributeSet attPrompt;
\r
421 SimpleAttributeSet attUserInput;
\r
422 SimpleAttributeSet attStatus;
\r
424 ConsoleDocument() {
\r
427 attError = new SimpleAttributeSet();
\r
428 StyleConstants.setForeground(attError, Color.red);
\r
430 attPrompt = new SimpleAttributeSet();
\r
431 StyleConstants.setForeground(attPrompt, Color.magenta);
\r
433 attUserInput = new SimpleAttributeSet();
\r
434 StyleConstants.setForeground(attUserInput, Color.black);
\r
436 attEcho = new SimpleAttributeSet();
\r
437 StyleConstants.setForeground(attEcho, Color.blue);
\r
438 StyleConstants.setBold(attEcho, true);
\r
440 attStatus = new SimpleAttributeSet();
\r
441 StyleConstants.setForeground(attStatus, Color.black);
\r
442 StyleConstants.setItalic(attStatus, true);
\r
445 void setConsoleTextPane(ConsoleTextPane consoleTextPane) {
\r
446 this.consoleTextPane = consoleTextPane;
\r
449 Position positionBeforePrompt; // starts at 0, so first time isn't tracked (at least on Mac OS X)
\r
450 Position positionAfterPrompt; // immediately after $, so this will track
\r
451 int offsetAfterPrompt; // only still needed for the insertString override and replaceCommand
\r
454 * Removes all content of the script window, and add a new prompt.
\r
456 void clearContent() {
\r
458 super.remove(0, getLength());
\r
459 } catch (BadLocationException exception) {
\r
460 System.out.println("Could not clear script window content: " + exception.getMessage());
\r
466 super.insertString(getLength(), "$ ", attPrompt);
\r
467 setOffsetPositions();
\r
468 consoleTextPane.setCaretPosition(offsetAfterPrompt);
\r
469 } catch (BadLocationException e) {
\r
470 e.printStackTrace();
\r
474 void setOffsetPositions() {
\r
476 offsetAfterPrompt = getLength();
\r
477 positionBeforePrompt = createPosition(offsetAfterPrompt - 2);
\r
478 // after prompt should be immediately after $ otherwise tracks the end
\r
479 // of the line (and no command will be found) at least on Mac OS X it did.
\r
480 positionAfterPrompt = createPosition(offsetAfterPrompt - 1);
\r
481 } catch (BadLocationException e) {
\r
482 e.printStackTrace();
\r
486 void setNoPrompt() {
\r
488 offsetAfterPrompt = getLength();
\r
489 positionAfterPrompt = positionBeforePrompt = createPosition(offsetAfterPrompt);
\r
490 consoleTextPane.setCaretPosition(offsetAfterPrompt);
\r
491 } catch (BadLocationException e) {
\r
492 e.printStackTrace();
\r
496 // it looks like the positionBeforePrompt does not track when it started out as 0
\r
497 // and a insertString at location 0 occurs. It may be better to track the
\r
498 // position after the prompt in stead
\r
499 void outputBeforePrompt(String str, SimpleAttributeSet attribute) {
\r
501 int pt = consoleTextPane.getCaretPosition();
\r
502 Position caretPosition = createPosition(pt);
\r
503 pt = positionBeforePrompt.getOffset();
\r
504 super.insertString(pt, str+"\n", attribute);
\r
505 setOffsetPositions();
\r
506 pt = caretPosition.getOffset();
\r
507 consoleTextPane.setCaretPosition(pt);
\r
508 } catch (BadLocationException e) {
\r
509 e.printStackTrace();
\r
513 void outputError(String strError) {
\r
514 outputBeforePrompt(strError, attError);
\r
517 void outputErrorForeground(String strError) {
\r
519 super.insertString(getLength(), strError+"\n", attError);
\r
520 consoleTextPane.setCaretPosition(getLength());
\r
521 } catch (BadLocationException e) {
\r
522 e.printStackTrace();
\r
527 void outputEcho(String strEcho) {
\r
528 outputBeforePrompt(strEcho, attEcho);
\r
531 void outputStatus(String strStatus) {
\r
532 outputBeforePrompt(strStatus, attStatus);
\r
535 void appendNewline() {
\r
537 super.insertString(getLength(), "\n", attUserInput);
\r
538 consoleTextPane.setCaretPosition(getLength());
\r
539 } catch (BadLocationException e) {
\r
540 e.printStackTrace();
\r
544 // override the insertString to make sure everything typed ends up at the end
\r
545 // or in the 'command line' using the proper font, and the newline is processed.
\r
546 public void insertString(int offs, String str, AttributeSet a)
\r
547 throws BadLocationException {
\r
548 int ichNewline = str.indexOf('\n');
\r
549 if (ichNewline > 0)
\r
550 str = str.substring(0, ichNewline);
\r
551 if (ichNewline != 0) {
\r
552 if (offs < offsetAfterPrompt) {
\r
553 offs = getLength();
\r
555 super.insertString(offs, str, a == attError ? a : attUserInput);
\r
556 consoleTextPane.setCaretPosition(offs+str.length());
\r
558 if (ichNewline >= 0) {
\r
559 consoleTextPane.enterPressed();
\r
563 String getCommandString() {
\r
564 String strCommand = "";
\r
566 int cmdStart = positionAfterPrompt.getOffset();
\r
567 strCommand = getText(cmdStart, getLength() - cmdStart);
\r
568 while (strCommand.length() > 0 && strCommand.charAt(0) == ' ')
\r
569 strCommand = strCommand.substring(1);
\r
570 } catch (BadLocationException e) {
\r
571 e.printStackTrace();
\r
576 public void remove(int offs, int len)
\r
577 throws BadLocationException {
\r
578 if (offs < offsetAfterPrompt) {
\r
579 len -= offsetAfterPrompt - offs;
\r
582 offs = offsetAfterPrompt;
\r
584 super.remove(offs, len);
\r
585 // consoleTextPane.setCaretPosition(offs);
\r
588 public void replace(int offs, int length, String str, AttributeSet attrs)
\r
589 throws BadLocationException {
\r
590 if (offs < offsetAfterPrompt) {
\r
591 if (offs + length < offsetAfterPrompt) {
\r
592 offs = getLength();
\r
595 length -= offsetAfterPrompt - offs;
\r
596 offs = offsetAfterPrompt;
\r
599 super.replace(offs, length, str, attrs);
\r
600 // consoleTextPane.setCaretPosition(offs + str.length());
\r
604 * Replaces current command on script.
\r
606 * @param newCommand new command value
\r
607 * @param isError true to set error color ends with #??
\r
609 * @throws BadLocationException
\r
611 void replaceCommand(String newCommand, boolean isError) throws BadLocationException {
\r
612 if (positionAfterPrompt == positionBeforePrompt)
\r
614 replace(offsetAfterPrompt, getLength() - offsetAfterPrompt, newCommand,
\r
615 isError ? attError : attUserInput);
\r
618 void colorCommand(SimpleAttributeSet att) {
\r
619 if (positionAfterPrompt == positionBeforePrompt)
\r
621 setCharacterAttributes(offsetAfterPrompt, getLength() - offsetAfterPrompt, att, true);
\r
625 interface EnterListener {
\r
626 public void enterPressed();
\r