2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3 * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, 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 // TODO: this class is copied in from jmol 11.0.2 - upgrade to 12.0.2 ?
33 public final class ScriptWindow extends JPanel implements ActionListener,
37 private ConsoleTextPane console;
39 private JButton closeButton;
41 private JButton runButton;
43 private JButton haltButton;
45 private JButton clearButton;
47 private JButton historyButton;
49 private JButton stateButton;
55 public ScriptWindow(AppJmol appJmol)
57 this.viewer = appJmol.jmb.viewer;
58 this.appJmol = appJmol;
60 setLayout(new BorderLayout());
62 console = new ConsoleTextPane(this);
65 add(new JScrollPane(console), BorderLayout.CENTER);
67 JPanel buttonPanel = new JPanel();
68 add(buttonPanel, BorderLayout.SOUTH);
70 runButton = new JButton(GT._("Run"));
71 haltButton = new JButton(GT._("Halt"));
72 runButton.addActionListener(this);
73 // buttonPanel.add(runButton);
74 haltButton.addActionListener(this);
75 // buttonPanel.add(haltButton);
76 haltButton.setEnabled(false);
78 clearButton = new JButton(GT._("Clear"));
79 clearButton.addActionListener(this);
80 buttonPanel.add(clearButton);
82 historyButton = new JButton(GT._("History"));
83 historyButton.addActionListener(this);
84 buttonPanel.add(historyButton);
86 stateButton = new JButton(GT._("State"));
87 stateButton.addActionListener(this);
88 buttonPanel.add(stateButton);
90 closeButton = new JButton(GT._("Close"));
91 closeButton.addActionListener(this);
92 buttonPanel.add(closeButton);
94 for (int i = 0; i < buttonPanel.getComponentCount(); i++)
96 // ((JButton)buttonPanel.getComponent(i))
97 // .setMargin(new Insets(0, 0, 0, 0));
102 public void sendConsoleEcho(String strEcho)
104 if (strEcho != null && !isError)
107 console.outputEcho(strEcho);
113 boolean isError = false;
115 void setError(boolean TF)
119 // console.recallCommand(true);
122 public void sendConsoleMessage(String strStatus)
124 if (strStatus == null)
126 console.clearContent();
127 console.outputStatus("");
129 else if (strStatus.indexOf("ERROR:") >= 0)
131 console.outputError(strStatus);
136 console.outputStatus(strStatus);
140 public void notifyScriptTermination(String strMsg, int msWalltime)
142 if (strMsg != null && strMsg.indexOf("ERROR") >= 0)
144 console.outputError(strMsg);
146 runButton.setEnabled(true);
147 haltButton.setEnabled(false);
150 public void enterPressed()
152 runButton.doClick(100);
156 class ExecuteCommandThread extends Thread
161 ExecuteCommandThread(String command)
163 strCommand = command;
170 executeCommand(strCommand);
171 } catch (Exception ie)
173 Logger.debug("execution command interrupted!" + ie);
178 ExecuteCommandThread execThread;
180 void executeCommandAsThread()
182 String strCommand = console.getCommandString().trim();
183 if (strCommand.length() > 0)
185 execThread = new ExecuteCommandThread(strCommand);
190 void executeCommand(String strCommand)
194 console.appendNewline();
196 if (strCommand.length() > 0)
198 String strErrorMessage = null;
199 doWait = (strCommand.indexOf("WAIT ") == 0);
201 { // for testing, mainly
202 // demonstrates using the statusManager system.
203 runButton.setEnabled(false);
204 haltButton.setEnabled(true);
206 Vector info = (Vector) viewer
207 .scriptWaitStatus(strCommand.substring(5),
208 "+fileLoaded,+scriptStarted,+scriptStatus,+scriptEcho,+scriptTerminated");
209 runButton.setEnabled(true);
210 haltButton.setEnabled(false);
212 * info = [ statusRecortSet0, statusRecortSet1, statusRecortSet2, ...]
213 * statusRecordSet = [ statusRecord0, statusRecord1, statusRecord2, ...]
214 * statusRecord = [int msgPtr, String statusName, int intInfo, String
217 for (int i = 0; i < info.size(); i++)
219 Vector statusRecordSet = (Vector) info.get(i);
220 for (int j = 0; j < statusRecordSet.size(); j++)
222 Vector statusRecord = (Vector) statusRecordSet.get(j);
223 Logger.info("msg#=" + statusRecord.get(0) + " "
224 + statusRecord.get(1) + " intInfo="
225 + statusRecord.get(2) + " stringInfo="
226 + statusRecord.get(3));
229 console.appendNewline();
233 boolean isScriptExecuting = viewer.isScriptExecuting();
234 if (viewer.checkHalt(strCommand, true))
235 strErrorMessage = (isScriptExecuting ? "string execution halted with "
237 : "no script was executing");
239 strErrorMessage = "";// viewer.scriptCheck(strCommand);
240 // the problem is that scriptCheck is synchronized, so these might get
242 if (strErrorMessage != null && strErrorMessage.length() > 0)
244 console.outputError(strErrorMessage);
248 // runButton.setEnabled(false);
249 haltButton.setEnabled(true);
250 viewer.script(strCommand);
257 public void actionPerformed(ActionEvent e)
259 Object source = e.getSource();
260 if (source == closeButton)
262 // appJmol.showConsole(false);
264 else if (source == runButton)
266 executeCommandAsThread();
268 else if (source == clearButton)
270 console.clearContent();
272 else if (source == historyButton)
274 console.clearContent(viewer.getSetHistory(Integer.MAX_VALUE));
276 else if (source == stateButton)
278 console.clearContent(viewer.getStateInfo());
280 else if (source == haltButton)
282 viewer.haltScriptExecution();
284 console.grabFocus(); // always grab the focus (e.g., after clear)
288 class ConsoleTextPane extends JTextPane
291 ConsoleDocument consoleDoc;
293 EnterListener enterListener;
297 ConsoleTextPane(ScriptWindow scriptWindow)
299 super(new ConsoleDocument());
300 consoleDoc = (ConsoleDocument) getDocument();
301 consoleDoc.setConsoleTextPane(this);
302 this.enterListener = (EnterListener) scriptWindow;
303 this.viewer = scriptWindow.viewer;
306 public String getCommandString()
308 String cmd = consoleDoc.getCommandString();
312 public void setPrompt()
314 consoleDoc.setPrompt();
317 public void appendNewline()
319 consoleDoc.appendNewline();
322 public void outputError(String strError)
324 consoleDoc.outputError(strError);
327 public void outputErrorForeground(String strError)
329 consoleDoc.outputErrorForeground(strError);
332 public void outputEcho(String strEcho)
334 consoleDoc.outputEcho(strEcho);
337 public void outputStatus(String strStatus)
339 consoleDoc.outputStatus(strStatus);
342 public void enterPressed()
344 if (enterListener != null)
345 enterListener.enterPressed();
348 public void clearContent()
353 public void clearContent(String text)
355 consoleDoc.clearContent();
357 consoleDoc.outputEcho(text);
364 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
368 * Custom key event processing for command 0 implementation.
370 * Captures key up and key down strokes to call command history and redefines
371 * the same events with control down to allow caret vertical shift.
373 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
375 protected void processKeyEvent(KeyEvent ke)
377 // Id Control key is down, captures events does command
378 // history recall and inhibits caret vertical shift.
379 if (ke.getKeyCode() == KeyEvent.VK_UP
380 && ke.getID() == KeyEvent.KEY_PRESSED && !ke.isControlDown())
384 else if (ke.getKeyCode() == KeyEvent.VK_DOWN
385 && ke.getID() == KeyEvent.KEY_PRESSED && !ke.isControlDown())
387 recallCommand(false);
389 // If Control key is down, redefines the event as if it
390 // where a key up or key down stroke without modifiers.
391 // This allows to move the caret up and down
392 // with no command history recall.
393 else if ((ke.getKeyCode() == KeyEvent.VK_DOWN || ke.getKeyCode() == KeyEvent.VK_UP)
394 && ke.getID() == KeyEvent.KEY_PRESSED && ke.isControlDown())
396 super.processKeyEvent(new KeyEvent((Component) ke.getSource(), ke
397 .getID(), ke.getWhen(), 0, // No modifiers
398 ke.getKeyCode(), ke.getKeyChar(), ke.getKeyLocation()));
400 // Standard processing for other events.
403 super.processKeyEvent(ke);
404 // check command for compiler-identifyable syntax issues
405 // this may have to be taken out if people start complaining
406 // that only some of the commands are being checked
407 // that is -- that the script itself is not being fully checked
409 // not perfect -- help here?
410 if (ke.getID() == KeyEvent.KEY_RELEASED
411 && (ke.getKeyCode() > KeyEvent.VK_DOWN)
412 || ke.getKeyCode() == KeyEvent.VK_BACK_SPACE)
418 * Recall command history.
421 * - history up or down
423 void recallCommand(boolean up)
425 String cmd = viewer.getSetHistory(up ? -1 : 1);
432 if (cmd.endsWith(CommandHistory.ERROR_FLAG))
434 cmd = cmd.substring(0, cmd.indexOf(CommandHistory.ERROR_FLAG));
435 consoleDoc.replaceCommand(cmd, true);
439 consoleDoc.replaceCommand(cmd, false);
441 } catch (BadLocationException e)
449 String strCommand = consoleDoc.getCommandString();
450 if (strCommand.length() == 0)
453 .colorCommand(viewer.scriptCheck(strCommand) == null ? consoleDoc.attUserInput
454 : consoleDoc.attError);
459 class ConsoleDocument extends DefaultStyledDocument
462 ConsoleTextPane consoleTextPane;
464 SimpleAttributeSet attError;
466 SimpleAttributeSet attEcho;
468 SimpleAttributeSet attPrompt;
470 SimpleAttributeSet attUserInput;
472 SimpleAttributeSet attStatus;
478 attError = new SimpleAttributeSet();
479 StyleConstants.setForeground(attError, Color.red);
481 attPrompt = new SimpleAttributeSet();
482 StyleConstants.setForeground(attPrompt, Color.magenta);
484 attUserInput = new SimpleAttributeSet();
485 StyleConstants.setForeground(attUserInput, Color.black);
487 attEcho = new SimpleAttributeSet();
488 StyleConstants.setForeground(attEcho, Color.blue);
489 StyleConstants.setBold(attEcho, true);
491 attStatus = new SimpleAttributeSet();
492 StyleConstants.setForeground(attStatus, Color.black);
493 StyleConstants.setItalic(attStatus, true);
496 void setConsoleTextPane(ConsoleTextPane consoleTextPane)
498 this.consoleTextPane = consoleTextPane;
501 Position positionBeforePrompt; // starts at 0, so first time isn't tracked
503 // (at least on Mac OS X)
505 Position positionAfterPrompt; // immediately after $, so this will track
507 int offsetAfterPrompt; // only still needed for the insertString override and
512 * Removes all content of the script window, and add a new prompt.
518 super.remove(0, getLength());
519 } catch (BadLocationException exception)
521 System.out.println("Could not clear script window content: "
522 + exception.getMessage());
530 super.insertString(getLength(), "$ ", attPrompt);
531 setOffsetPositions();
532 consoleTextPane.setCaretPosition(offsetAfterPrompt);
533 } catch (BadLocationException e)
539 void setOffsetPositions()
543 offsetAfterPrompt = getLength();
544 positionBeforePrompt = createPosition(offsetAfterPrompt - 2);
545 // after prompt should be immediately after $ otherwise tracks the end
546 // of the line (and no command will be found) at least on Mac OS X it did.
547 positionAfterPrompt = createPosition(offsetAfterPrompt - 1);
548 } catch (BadLocationException e)
558 offsetAfterPrompt = getLength();
559 positionAfterPrompt = positionBeforePrompt = createPosition(offsetAfterPrompt);
560 consoleTextPane.setCaretPosition(offsetAfterPrompt);
561 } catch (BadLocationException e)
567 // it looks like the positionBeforePrompt does not track when it started out
569 // and a insertString at location 0 occurs. It may be better to track the
570 // position after the prompt in stead
571 void outputBeforePrompt(String str, SimpleAttributeSet attribute)
575 int pt = consoleTextPane.getCaretPosition();
576 Position caretPosition = createPosition(pt);
577 pt = positionBeforePrompt.getOffset();
578 super.insertString(pt, str + "\n", attribute);
579 setOffsetPositions();
580 pt = caretPosition.getOffset();
581 consoleTextPane.setCaretPosition(pt);
582 } catch (BadLocationException e)
588 void outputError(String strError)
590 outputBeforePrompt(strError, attError);
593 void outputErrorForeground(String strError)
597 super.insertString(getLength(), strError + "\n", attError);
598 consoleTextPane.setCaretPosition(getLength());
599 } catch (BadLocationException e)
606 void outputEcho(String strEcho)
608 outputBeforePrompt(strEcho, attEcho);
611 void outputStatus(String strStatus)
613 outputBeforePrompt(strStatus, attStatus);
620 super.insertString(getLength(), "\n", attUserInput);
621 consoleTextPane.setCaretPosition(getLength());
622 } catch (BadLocationException e)
628 // override the insertString to make sure everything typed ends up at the end
629 // or in the 'command line' using the proper font, and the newline is
631 public void insertString(int offs, String str, AttributeSet a)
632 throws BadLocationException
634 int ichNewline = str.indexOf('\n');
636 str = str.substring(0, ichNewline);
639 if (offs < offsetAfterPrompt)
643 super.insertString(offs, str, a == attError ? a : attUserInput);
644 consoleTextPane.setCaretPosition(offs + str.length());
648 consoleTextPane.enterPressed();
652 String getCommandString()
654 String strCommand = "";
657 int cmdStart = positionAfterPrompt.getOffset();
658 strCommand = getText(cmdStart, getLength() - cmdStart);
659 while (strCommand.length() > 0 && strCommand.charAt(0) == ' ')
660 strCommand = strCommand.substring(1);
661 } catch (BadLocationException e)
668 public void remove(int offs, int len) throws BadLocationException
670 if (offs < offsetAfterPrompt)
672 len -= offsetAfterPrompt - offs;
675 offs = offsetAfterPrompt;
677 super.remove(offs, len);
678 // consoleTextPane.setCaretPosition(offs);
681 public void replace(int offs, int length, String str, AttributeSet attrs)
682 throws BadLocationException
684 if (offs < offsetAfterPrompt)
686 if (offs + length < offsetAfterPrompt)
693 length -= offsetAfterPrompt - offs;
694 offs = offsetAfterPrompt;
697 super.replace(offs, length, str, attrs);
698 // consoleTextPane.setCaretPosition(offs + str.length());
702 * Replaces current command on script.
707 * true to set error color ends with #??
709 * @throws BadLocationException
711 void replaceCommand(String newCommand, boolean isError)
712 throws BadLocationException
714 if (positionAfterPrompt == positionBeforePrompt)
716 replace(offsetAfterPrompt, getLength() - offsetAfterPrompt, newCommand,
717 isError ? attError : attUserInput);
720 void colorCommand(SimpleAttributeSet att)
722 if (positionAfterPrompt == positionBeforePrompt)
724 setCharacterAttributes(offsetAfterPrompt, getLength()
725 - offsetAfterPrompt, att, true);
729 interface EnterListener
731 public void enterPressed();