2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.1)
3 * Copyright (C) 2014 The Jalview Authors
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/>.
17 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 import org.jmol.api.*;
24 import java.awt.event.*;
26 import javax.swing.text.*;
27 import java.util.Vector;
29 import org.jmol.i18n.GT;
30 import org.jmol.util.Logger;
31 import org.jmol.util.CommandHistory;
33 // TODO: this class is copied in from jmol 11.0.2 - upgrade to 12.0.2 ?
34 public final class ScriptWindow extends JPanel implements ActionListener,
38 private ConsoleTextPane console;
40 private JButton closeButton;
42 private JButton runButton;
44 private JButton haltButton;
46 private JButton clearButton;
48 private JButton historyButton;
50 private JButton stateButton;
56 public ScriptWindow(AppJmol appJmol)
58 this.viewer = appJmol.jmb.viewer;
59 this.appJmol = appJmol;
61 setLayout(new BorderLayout());
63 console = new ConsoleTextPane(this);
66 add(new JScrollPane(console), BorderLayout.CENTER);
68 JPanel buttonPanel = new JPanel();
69 add(buttonPanel, BorderLayout.SOUTH);
71 runButton = new JButton(GT._("Run"));
72 haltButton = new JButton(GT._("Halt"));
73 runButton.addActionListener(this);
74 // buttonPanel.add(runButton);
75 haltButton.addActionListener(this);
76 // buttonPanel.add(haltButton);
77 haltButton.setEnabled(false);
79 clearButton = new JButton(GT._("Clear"));
80 clearButton.addActionListener(this);
81 buttonPanel.add(clearButton);
83 historyButton = new JButton(GT._("History"));
84 historyButton.addActionListener(this);
85 buttonPanel.add(historyButton);
87 stateButton = new JButton(GT._("State"));
88 stateButton.addActionListener(this);
89 buttonPanel.add(stateButton);
91 closeButton = new JButton(GT._("Close"));
92 closeButton.addActionListener(this);
93 buttonPanel.add(closeButton);
95 for (int i = 0; i < buttonPanel.getComponentCount(); i++)
97 // ((JButton)buttonPanel.getComponent(i))
98 // .setMargin(new Insets(0, 0, 0, 0));
103 public void sendConsoleEcho(String strEcho)
105 if (strEcho != null && !isError)
108 console.outputEcho(strEcho);
114 boolean isError = false;
116 void setError(boolean TF)
120 // console.recallCommand(true);
123 public void sendConsoleMessage(String strStatus)
125 if (strStatus == null)
127 console.clearContent();
128 console.outputStatus("");
130 else if (strStatus.indexOf("ERROR:") >= 0)
132 console.outputError(strStatus);
137 console.outputStatus(strStatus);
141 public void notifyScriptTermination(String strMsg, int msWalltime)
143 if (strMsg != null && strMsg.indexOf("ERROR") >= 0)
145 console.outputError(strMsg);
147 runButton.setEnabled(true);
148 haltButton.setEnabled(false);
151 public void enterPressed()
153 runButton.doClick(100);
157 class ExecuteCommandThread extends Thread
162 ExecuteCommandThread(String command)
164 strCommand = command;
171 executeCommand(strCommand);
172 } catch (Exception ie)
174 Logger.debug("execution command interrupted!" + ie);
179 ExecuteCommandThread execThread;
181 void executeCommandAsThread()
183 String strCommand = console.getCommandString().trim();
184 if (strCommand.length() > 0)
186 execThread = new ExecuteCommandThread(strCommand);
191 void executeCommand(String strCommand)
195 console.appendNewline();
197 if (strCommand.length() > 0)
199 String strErrorMessage = null;
200 doWait = (strCommand.indexOf("WAIT ") == 0);
202 { // for testing, mainly
203 // demonstrates using the statusManager system.
204 runButton.setEnabled(false);
205 haltButton.setEnabled(true);
207 Vector info = (Vector) viewer
208 .scriptWaitStatus(strCommand.substring(5),
209 "+fileLoaded,+scriptStarted,+scriptStatus,+scriptEcho,+scriptTerminated");
210 runButton.setEnabled(true);
211 haltButton.setEnabled(false);
213 * info = [ statusRecortSet0, statusRecortSet1, statusRecortSet2, ...]
214 * statusRecordSet = [ statusRecord0, statusRecord1, statusRecord2, ...]
215 * statusRecord = [int msgPtr, String statusName, int intInfo, String
218 for (int i = 0; i < info.size(); i++)
220 Vector statusRecordSet = (Vector) info.get(i);
221 for (int j = 0; j < statusRecordSet.size(); j++)
223 Vector statusRecord = (Vector) statusRecordSet.get(j);
224 Logger.info("msg#=" + statusRecord.get(0) + " "
225 + statusRecord.get(1) + " intInfo="
226 + statusRecord.get(2) + " stringInfo="
227 + statusRecord.get(3));
230 console.appendNewline();
234 boolean isScriptExecuting = viewer.isScriptExecuting();
235 if (viewer.checkHalt(strCommand, true))
236 strErrorMessage = (isScriptExecuting ? "string execution halted with "
238 : "no script was executing");
240 strErrorMessage = "";// viewer.scriptCheck(strCommand);
241 // the problem is that scriptCheck is synchronized, so these might get
243 if (strErrorMessage != null && strErrorMessage.length() > 0)
245 console.outputError(strErrorMessage);
249 // runButton.setEnabled(false);
250 haltButton.setEnabled(true);
251 viewer.script(strCommand);
258 public void actionPerformed(ActionEvent e)
260 Object source = e.getSource();
261 if (source == closeButton)
263 // appJmol.showConsole(false);
265 else if (source == runButton)
267 executeCommandAsThread();
269 else if (source == clearButton)
271 console.clearContent();
273 else if (source == historyButton)
275 console.clearContent(viewer.getSetHistory(Integer.MAX_VALUE));
277 else if (source == stateButton)
279 console.clearContent(viewer.getStateInfo());
281 else if (source == haltButton)
283 viewer.haltScriptExecution();
285 console.grabFocus(); // always grab the focus (e.g., after clear)
289 class ConsoleTextPane extends JTextPane
292 ConsoleDocument consoleDoc;
294 EnterListener enterListener;
298 ConsoleTextPane(ScriptWindow scriptWindow)
300 super(new ConsoleDocument());
301 consoleDoc = (ConsoleDocument) getDocument();
302 consoleDoc.setConsoleTextPane(this);
303 this.enterListener = (EnterListener) scriptWindow;
304 this.viewer = scriptWindow.viewer;
307 public String getCommandString()
309 String cmd = consoleDoc.getCommandString();
313 public void setPrompt()
315 consoleDoc.setPrompt();
318 public void appendNewline()
320 consoleDoc.appendNewline();
323 public void outputError(String strError)
325 consoleDoc.outputError(strError);
328 public void outputErrorForeground(String strError)
330 consoleDoc.outputErrorForeground(strError);
333 public void outputEcho(String strEcho)
335 consoleDoc.outputEcho(strEcho);
338 public void outputStatus(String strStatus)
340 consoleDoc.outputStatus(strStatus);
343 public void enterPressed()
345 if (enterListener != null)
346 enterListener.enterPressed();
349 public void clearContent()
354 public void clearContent(String text)
356 consoleDoc.clearContent();
358 consoleDoc.outputEcho(text);
365 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
369 * Custom key event processing for command 0 implementation.
371 * Captures key up and key down strokes to call command history and redefines
372 * the same events with control down to allow caret vertical shift.
374 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
376 protected void processKeyEvent(KeyEvent ke)
378 // Id Control key is down, captures events does command
379 // history recall and inhibits caret vertical shift.
380 if (ke.getKeyCode() == KeyEvent.VK_UP
381 && ke.getID() == KeyEvent.KEY_PRESSED && !ke.isControlDown())
385 else if (ke.getKeyCode() == KeyEvent.VK_DOWN
386 && ke.getID() == KeyEvent.KEY_PRESSED && !ke.isControlDown())
388 recallCommand(false);
390 // If Control key is down, redefines the event as if it
391 // where a key up or key down stroke without modifiers.
392 // This allows to move the caret up and down
393 // with no command history recall.
394 else if ((ke.getKeyCode() == KeyEvent.VK_DOWN || ke.getKeyCode() == KeyEvent.VK_UP)
395 && ke.getID() == KeyEvent.KEY_PRESSED && ke.isControlDown())
397 super.processKeyEvent(new KeyEvent((Component) ke.getSource(), ke
398 .getID(), ke.getWhen(), 0, // No modifiers
399 ke.getKeyCode(), ke.getKeyChar(), ke.getKeyLocation()));
401 // Standard processing for other events.
404 super.processKeyEvent(ke);
405 // check command for compiler-identifyable syntax issues
406 // this may have to be taken out if people start complaining
407 // that only some of the commands are being checked
408 // that is -- that the script itself is not being fully checked
410 // not perfect -- help here?
411 if (ke.getID() == KeyEvent.KEY_RELEASED
412 && (ke.getKeyCode() > KeyEvent.VK_DOWN)
413 || ke.getKeyCode() == KeyEvent.VK_BACK_SPACE)
419 * Recall command history.
422 * - history up or down
424 void recallCommand(boolean up)
426 String cmd = viewer.getSetHistory(up ? -1 : 1);
433 if (cmd.endsWith(CommandHistory.ERROR_FLAG))
435 cmd = cmd.substring(0, cmd.indexOf(CommandHistory.ERROR_FLAG));
436 consoleDoc.replaceCommand(cmd, true);
440 consoleDoc.replaceCommand(cmd, false);
442 } catch (BadLocationException e)
450 String strCommand = consoleDoc.getCommandString();
451 if (strCommand.length() == 0)
454 .colorCommand(viewer.scriptCheck(strCommand) == null ? consoleDoc.attUserInput
455 : consoleDoc.attError);
460 class ConsoleDocument extends DefaultStyledDocument
463 ConsoleTextPane consoleTextPane;
465 SimpleAttributeSet attError;
467 SimpleAttributeSet attEcho;
469 SimpleAttributeSet attPrompt;
471 SimpleAttributeSet attUserInput;
473 SimpleAttributeSet attStatus;
479 attError = new SimpleAttributeSet();
480 StyleConstants.setForeground(attError, Color.red);
482 attPrompt = new SimpleAttributeSet();
483 StyleConstants.setForeground(attPrompt, Color.magenta);
485 attUserInput = new SimpleAttributeSet();
486 StyleConstants.setForeground(attUserInput, Color.black);
488 attEcho = new SimpleAttributeSet();
489 StyleConstants.setForeground(attEcho, Color.blue);
490 StyleConstants.setBold(attEcho, true);
492 attStatus = new SimpleAttributeSet();
493 StyleConstants.setForeground(attStatus, Color.black);
494 StyleConstants.setItalic(attStatus, true);
497 void setConsoleTextPane(ConsoleTextPane consoleTextPane)
499 this.consoleTextPane = consoleTextPane;
502 Position positionBeforePrompt; // starts at 0, so first time isn't tracked
504 // (at least on Mac OS X)
506 Position positionAfterPrompt; // immediately after $, so this will track
508 int offsetAfterPrompt; // only still needed for the insertString override and
513 * Removes all content of the script window, and add a new prompt.
519 super.remove(0, getLength());
520 } catch (BadLocationException exception)
522 System.out.println("Could not clear script window content: "
523 + exception.getMessage());
531 super.insertString(getLength(), "$ ", attPrompt);
532 setOffsetPositions();
533 consoleTextPane.setCaretPosition(offsetAfterPrompt);
534 } catch (BadLocationException e)
540 void setOffsetPositions()
544 offsetAfterPrompt = getLength();
545 positionBeforePrompt = createPosition(offsetAfterPrompt - 2);
546 // after prompt should be immediately after $ otherwise tracks the end
547 // of the line (and no command will be found) at least on Mac OS X it did.
548 positionAfterPrompt = createPosition(offsetAfterPrompt - 1);
549 } catch (BadLocationException e)
559 offsetAfterPrompt = getLength();
560 positionAfterPrompt = positionBeforePrompt = createPosition(offsetAfterPrompt);
561 consoleTextPane.setCaretPosition(offsetAfterPrompt);
562 } catch (BadLocationException e)
568 // it looks like the positionBeforePrompt does not track when it started out
570 // and a insertString at location 0 occurs. It may be better to track the
571 // position after the prompt in stead
572 void outputBeforePrompt(String str, SimpleAttributeSet attribute)
576 int pt = consoleTextPane.getCaretPosition();
577 Position caretPosition = createPosition(pt);
578 pt = positionBeforePrompt.getOffset();
579 super.insertString(pt, str + "\n", attribute);
580 setOffsetPositions();
581 pt = caretPosition.getOffset();
582 consoleTextPane.setCaretPosition(pt);
583 } catch (BadLocationException e)
589 void outputError(String strError)
591 outputBeforePrompt(strError, attError);
594 void outputErrorForeground(String strError)
598 super.insertString(getLength(), strError + "\n", attError);
599 consoleTextPane.setCaretPosition(getLength());
600 } catch (BadLocationException e)
607 void outputEcho(String strEcho)
609 outputBeforePrompt(strEcho, attEcho);
612 void outputStatus(String strStatus)
614 outputBeforePrompt(strStatus, attStatus);
621 super.insertString(getLength(), "\n", attUserInput);
622 consoleTextPane.setCaretPosition(getLength());
623 } catch (BadLocationException e)
629 // override the insertString to make sure everything typed ends up at the end
630 // or in the 'command line' using the proper font, and the newline is
632 public void insertString(int offs, String str, AttributeSet a)
633 throws BadLocationException
635 int ichNewline = str.indexOf('\n');
637 str = str.substring(0, ichNewline);
640 if (offs < offsetAfterPrompt)
644 super.insertString(offs, str, a == attError ? a : attUserInput);
645 consoleTextPane.setCaretPosition(offs + str.length());
649 consoleTextPane.enterPressed();
653 String getCommandString()
655 String strCommand = "";
658 int cmdStart = positionAfterPrompt.getOffset();
659 strCommand = getText(cmdStart, getLength() - cmdStart);
660 while (strCommand.length() > 0 && strCommand.charAt(0) == ' ')
661 strCommand = strCommand.substring(1);
662 } catch (BadLocationException e)
669 public void remove(int offs, int len) throws BadLocationException
671 if (offs < offsetAfterPrompt)
673 len -= offsetAfterPrompt - offs;
676 offs = offsetAfterPrompt;
678 super.remove(offs, len);
679 // consoleTextPane.setCaretPosition(offs);
682 public void replace(int offs, int length, String str, AttributeSet attrs)
683 throws BadLocationException
685 if (offs < offsetAfterPrompt)
687 if (offs + length < offsetAfterPrompt)
694 length -= offsetAfterPrompt - offs;
695 offs = offsetAfterPrompt;
698 super.replace(offs, length, str, attrs);
699 // consoleTextPane.setCaretPosition(offs + str.length());
703 * Replaces current command on script.
708 * true to set error color ends with #??
710 * @throws BadLocationException
712 void replaceCommand(String newCommand, boolean isError)
713 throws BadLocationException
715 if (positionAfterPrompt == positionBeforePrompt)
717 replace(offsetAfterPrompt, getLength() - offsetAfterPrompt, newCommand,
718 isError ? attError : attUserInput);
721 void colorCommand(SimpleAttributeSet att)
723 if (positionAfterPrompt == positionBeforePrompt)
725 setCharacterAttributes(offsetAfterPrompt, getLength()
726 - offsetAfterPrompt, att, true);
730 interface EnterListener
732 public void enterPressed();