2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
3 * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
20 import org.jmol.api.*;
23 import java.awt.event.*;
25 import javax.swing.text.*;
26 import java.util.Vector;
28 import org.jmol.i18n.GT;
29 import org.jmol.util.Logger;
30 import org.jmol.util.CommandHistory;
32 public final class ScriptWindow extends JPanel implements ActionListener,
36 private ConsoleTextPane console;
38 private JButton closeButton;
40 private JButton runButton;
42 private JButton haltButton;
44 private JButton clearButton;
46 private JButton historyButton;
48 private JButton stateButton;
54 public ScriptWindow(AppJmol appJmol)
56 this.viewer = appJmol.viewer;
57 this.appJmol = appJmol;
59 setLayout(new BorderLayout());
61 console = new ConsoleTextPane(this);
64 add(new JScrollPane(console), BorderLayout.CENTER);
66 JPanel buttonPanel = new JPanel();
67 add(buttonPanel, BorderLayout.SOUTH);
69 runButton = new JButton(GT._("Run"));
70 haltButton = new JButton(GT._("Halt"));
71 runButton.addActionListener(this);
72 // buttonPanel.add(runButton);
73 haltButton.addActionListener(this);
74 // buttonPanel.add(haltButton);
75 haltButton.setEnabled(false);
77 clearButton = new JButton(GT._("Clear"));
78 clearButton.addActionListener(this);
79 buttonPanel.add(clearButton);
81 historyButton = new JButton(GT._("History"));
82 historyButton.addActionListener(this);
83 buttonPanel.add(historyButton);
85 stateButton = new JButton(GT._("State"));
86 stateButton.addActionListener(this);
87 buttonPanel.add(stateButton);
89 closeButton = new JButton(GT._("Close"));
90 closeButton.addActionListener(this);
91 buttonPanel.add(closeButton);
93 for (int i = 0; i < buttonPanel.getComponentCount(); i++)
95 // ((JButton)buttonPanel.getComponent(i))
96 // .setMargin(new Insets(0, 0, 0, 0));
101 public void sendConsoleEcho(String strEcho)
103 if (strEcho != null && !isError)
106 console.outputEcho(strEcho);
112 boolean isError = false;
114 void setError(boolean TF)
118 // console.recallCommand(true);
121 public void sendConsoleMessage(String strStatus)
123 if (strStatus == null)
125 console.clearContent();
126 console.outputStatus("");
128 else if (strStatus.indexOf("ERROR:") >= 0)
130 console.outputError(strStatus);
135 console.outputStatus(strStatus);
139 public void notifyScriptTermination(String strMsg, int msWalltime)
141 if (strMsg != null && strMsg.indexOf("ERROR") >= 0)
143 console.outputError(strMsg);
145 runButton.setEnabled(true);
146 haltButton.setEnabled(false);
149 public void enterPressed()
151 runButton.doClick(100);
155 class ExecuteCommandThread extends Thread
160 ExecuteCommandThread(String command)
162 strCommand = command;
169 executeCommand(strCommand);
170 } catch (Exception ie)
172 Logger.debug("execution command interrupted!" + ie);
177 ExecuteCommandThread execThread;
179 void executeCommandAsThread()
181 String strCommand = console.getCommandString().trim();
182 if (strCommand.length() > 0)
184 execThread = new ExecuteCommandThread(strCommand);
189 void executeCommand(String strCommand)
193 console.appendNewline();
195 if (strCommand.length() > 0)
197 String strErrorMessage = null;
198 doWait = (strCommand.indexOf("WAIT ") == 0);
200 { // for testing, mainly
201 // demonstrates using the statusManager system.
202 runButton.setEnabled(false);
203 haltButton.setEnabled(true);
205 Vector info = (Vector) viewer
206 .scriptWaitStatus(strCommand.substring(5),
207 "+fileLoaded,+scriptStarted,+scriptStatus,+scriptEcho,+scriptTerminated");
208 runButton.setEnabled(true);
209 haltButton.setEnabled(false);
211 * info = [ statusRecortSet0, statusRecortSet1, statusRecortSet2, ...]
212 * statusRecordSet = [ statusRecord0, statusRecord1, statusRecord2, ...]
213 * statusRecord = [int msgPtr, String statusName, int intInfo, String
216 for (int i = 0; i < info.size(); i++)
218 Vector statusRecordSet = (Vector) info.get(i);
219 for (int j = 0; j < statusRecordSet.size(); j++)
221 Vector statusRecord = (Vector) statusRecordSet.get(j);
222 Logger.info("msg#=" + statusRecord.get(0) + " "
223 + statusRecord.get(1) + " intInfo="
224 + statusRecord.get(2) + " stringInfo="
225 + statusRecord.get(3));
228 console.appendNewline();
232 boolean isScriptExecuting = viewer.isScriptExecuting();
233 if (viewer.checkHalt(strCommand))
234 strErrorMessage = (isScriptExecuting ? "string execution halted with "
236 : "no script was executing");
238 strErrorMessage = "";// viewer.scriptCheck(strCommand);
239 // the problem is that scriptCheck is synchronized, so these might get
241 if (strErrorMessage != null && strErrorMessage.length() > 0)
243 console.outputError(strErrorMessage);
247 // runButton.setEnabled(false);
248 haltButton.setEnabled(true);
249 viewer.script(strCommand);
256 public void actionPerformed(ActionEvent e)
258 Object source = e.getSource();
259 if (source == closeButton)
261 appJmol.showConsole(false);
263 else if (source == runButton)
265 executeCommandAsThread();
267 else if (source == clearButton)
269 console.clearContent();
271 else if (source == historyButton)
273 console.clearContent(viewer.getSetHistory(Integer.MAX_VALUE));
275 else if (source == stateButton)
277 console.clearContent(viewer.getStateInfo());
279 else if (source == haltButton)
281 viewer.haltScriptExecution();
283 console.grabFocus(); // always grab the focus (e.g., after clear)
287 class ConsoleTextPane extends JTextPane
290 ConsoleDocument consoleDoc;
292 EnterListener enterListener;
296 ConsoleTextPane(ScriptWindow scriptWindow)
298 super(new ConsoleDocument());
299 consoleDoc = (ConsoleDocument) getDocument();
300 consoleDoc.setConsoleTextPane(this);
301 this.enterListener = (EnterListener) scriptWindow;
302 this.viewer = scriptWindow.viewer;
305 public String getCommandString()
307 String cmd = consoleDoc.getCommandString();
311 public void setPrompt()
313 consoleDoc.setPrompt();
316 public void appendNewline()
318 consoleDoc.appendNewline();
321 public void outputError(String strError)
323 consoleDoc.outputError(strError);
326 public void outputErrorForeground(String strError)
328 consoleDoc.outputErrorForeground(strError);
331 public void outputEcho(String strEcho)
333 consoleDoc.outputEcho(strEcho);
336 public void outputStatus(String strStatus)
338 consoleDoc.outputStatus(strStatus);
341 public void enterPressed()
343 if (enterListener != null)
344 enterListener.enterPressed();
347 public void clearContent()
352 public void clearContent(String text)
354 consoleDoc.clearContent();
356 consoleDoc.outputEcho(text);
363 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
367 * Custom key event processing for command 0 implementation.
369 * Captures key up and key down strokes to call command history and redefines
370 * the same events with control down to allow caret vertical shift.
372 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
374 protected void processKeyEvent(KeyEvent ke)
376 // Id Control key is down, captures events does command
377 // history recall and inhibits caret vertical shift.
378 if (ke.getKeyCode() == KeyEvent.VK_UP
379 && ke.getID() == KeyEvent.KEY_PRESSED && !ke.isControlDown())
383 else if (ke.getKeyCode() == KeyEvent.VK_DOWN
384 && ke.getID() == KeyEvent.KEY_PRESSED && !ke.isControlDown())
386 recallCommand(false);
388 // If Control key is down, redefines the event as if it
389 // where a key up or key down stroke without modifiers.
390 // This allows to move the caret up and down
391 // with no command history recall.
392 else if ((ke.getKeyCode() == KeyEvent.VK_DOWN || ke.getKeyCode() == KeyEvent.VK_UP)
393 && ke.getID() == KeyEvent.KEY_PRESSED && ke.isControlDown())
395 super.processKeyEvent(new KeyEvent((Component) ke.getSource(), ke
396 .getID(), ke.getWhen(), 0, // No modifiers
397 ke.getKeyCode(), ke.getKeyChar(), ke.getKeyLocation()));
399 // Standard processing for other events.
402 super.processKeyEvent(ke);
403 // check command for compiler-identifyable syntax issues
404 // this may have to be taken out if people start complaining
405 // that only some of the commands are being checked
406 // that is -- that the script itself is not being fully checked
408 // not perfect -- help here?
409 if (ke.getID() == KeyEvent.KEY_RELEASED
410 && (ke.getKeyCode() > KeyEvent.VK_DOWN)
411 || ke.getKeyCode() == KeyEvent.VK_BACK_SPACE)
417 * Recall command history.
420 * - history up or down
422 void recallCommand(boolean up)
424 String cmd = viewer.getSetHistory(up ? -1 : 1);
431 if (cmd.endsWith(CommandHistory.ERROR_FLAG))
433 cmd = cmd.substring(0, cmd.indexOf(CommandHistory.ERROR_FLAG));
434 consoleDoc.replaceCommand(cmd, true);
438 consoleDoc.replaceCommand(cmd, false);
440 } catch (BadLocationException e)
448 String strCommand = consoleDoc.getCommandString();
449 if (strCommand.length() == 0)
452 .colorCommand(viewer.scriptCheck(strCommand) == null ? consoleDoc.attUserInput
453 : consoleDoc.attError);
458 class ConsoleDocument extends DefaultStyledDocument
461 ConsoleTextPane consoleTextPane;
463 SimpleAttributeSet attError;
465 SimpleAttributeSet attEcho;
467 SimpleAttributeSet attPrompt;
469 SimpleAttributeSet attUserInput;
471 SimpleAttributeSet attStatus;
477 attError = new SimpleAttributeSet();
478 StyleConstants.setForeground(attError, Color.red);
480 attPrompt = new SimpleAttributeSet();
481 StyleConstants.setForeground(attPrompt, Color.magenta);
483 attUserInput = new SimpleAttributeSet();
484 StyleConstants.setForeground(attUserInput, Color.black);
486 attEcho = new SimpleAttributeSet();
487 StyleConstants.setForeground(attEcho, Color.blue);
488 StyleConstants.setBold(attEcho, true);
490 attStatus = new SimpleAttributeSet();
491 StyleConstants.setForeground(attStatus, Color.black);
492 StyleConstants.setItalic(attStatus, true);
495 void setConsoleTextPane(ConsoleTextPane consoleTextPane)
497 this.consoleTextPane = consoleTextPane;
500 Position positionBeforePrompt; // starts at 0, so first time isn't tracked
502 // (at least on Mac OS X)
504 Position positionAfterPrompt; // immediately after $, so this will track
506 int offsetAfterPrompt; // only still needed for the insertString override and
511 * Removes all content of the script window, and add a new prompt.
517 super.remove(0, getLength());
518 } catch (BadLocationException exception)
520 System.out.println("Could not clear script window content: "
521 + exception.getMessage());
529 super.insertString(getLength(), "$ ", attPrompt);
530 setOffsetPositions();
531 consoleTextPane.setCaretPosition(offsetAfterPrompt);
532 } catch (BadLocationException e)
538 void setOffsetPositions()
542 offsetAfterPrompt = getLength();
543 positionBeforePrompt = createPosition(offsetAfterPrompt - 2);
544 // after prompt should be immediately after $ otherwise tracks the end
545 // of the line (and no command will be found) at least on Mac OS X it did.
546 positionAfterPrompt = createPosition(offsetAfterPrompt - 1);
547 } catch (BadLocationException e)
557 offsetAfterPrompt = getLength();
558 positionAfterPrompt = positionBeforePrompt = createPosition(offsetAfterPrompt);
559 consoleTextPane.setCaretPosition(offsetAfterPrompt);
560 } catch (BadLocationException e)
566 // it looks like the positionBeforePrompt does not track when it started out
568 // and a insertString at location 0 occurs. It may be better to track the
569 // position after the prompt in stead
570 void outputBeforePrompt(String str, SimpleAttributeSet attribute)
574 int pt = consoleTextPane.getCaretPosition();
575 Position caretPosition = createPosition(pt);
576 pt = positionBeforePrompt.getOffset();
577 super.insertString(pt, str + "\n", attribute);
578 setOffsetPositions();
579 pt = caretPosition.getOffset();
580 consoleTextPane.setCaretPosition(pt);
581 } catch (BadLocationException e)
587 void outputError(String strError)
589 outputBeforePrompt(strError, attError);
592 void outputErrorForeground(String strError)
596 super.insertString(getLength(), strError + "\n", attError);
597 consoleTextPane.setCaretPosition(getLength());
598 } catch (BadLocationException e)
605 void outputEcho(String strEcho)
607 outputBeforePrompt(strEcho, attEcho);
610 void outputStatus(String strStatus)
612 outputBeforePrompt(strStatus, attStatus);
619 super.insertString(getLength(), "\n", attUserInput);
620 consoleTextPane.setCaretPosition(getLength());
621 } catch (BadLocationException e)
627 // override the insertString to make sure everything typed ends up at the end
628 // or in the 'command line' using the proper font, and the newline is
630 public void insertString(int offs, String str, AttributeSet a)
631 throws BadLocationException
633 int ichNewline = str.indexOf('\n');
635 str = str.substring(0, ichNewline);
638 if (offs < offsetAfterPrompt)
642 super.insertString(offs, str, a == attError ? a : attUserInput);
643 consoleTextPane.setCaretPosition(offs + str.length());
647 consoleTextPane.enterPressed();
651 String getCommandString()
653 String strCommand = "";
656 int cmdStart = positionAfterPrompt.getOffset();
657 strCommand = getText(cmdStart, getLength() - cmdStart);
658 while (strCommand.length() > 0 && strCommand.charAt(0) == ' ')
659 strCommand = strCommand.substring(1);
660 } catch (BadLocationException e)
667 public void remove(int offs, int len) throws BadLocationException
669 if (offs < offsetAfterPrompt)
671 len -= offsetAfterPrompt - offs;
674 offs = offsetAfterPrompt;
676 super.remove(offs, len);
677 // consoleTextPane.setCaretPosition(offs);
680 public void replace(int offs, int length, String str, AttributeSet attrs)
681 throws BadLocationException
683 if (offs < offsetAfterPrompt)
685 if (offs + length < offsetAfterPrompt)
692 length -= offsetAfterPrompt - offs;
693 offs = offsetAfterPrompt;
696 super.replace(offs, length, str, attrs);
697 // consoleTextPane.setCaretPosition(offs + str.length());
701 * Replaces current command on script.
706 * true to set error color ends with #??
708 * @throws BadLocationException
710 void replaceCommand(String newCommand, boolean isError)
711 throws BadLocationException
713 if (positionAfterPrompt == positionBeforePrompt)
715 replace(offsetAfterPrompt, getLength() - offsetAfterPrompt, newCommand,
716 isError ? attError : attUserInput);
719 void colorCommand(SimpleAttributeSet att)
721 if (positionAfterPrompt == positionBeforePrompt)
723 setCharacterAttributes(offsetAfterPrompt, getLength()
724 - offsetAfterPrompt, att, true);
728 interface EnterListener
730 public void enterPressed();