2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
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
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import org.jmol.api.*;
26 import java.awt.event.*;
28 import javax.swing.text.*;
29 import java.util.Vector;
31 import org.jmol.i18n.GT;
32 import org.jmol.util.Logger;
33 import org.jmol.util.CommandHistory;
35 // TODO: this class is copied in from jmol 11.0.2 - upgrade to 12.0.2 ?
36 public final class ScriptWindow extends JPanel implements ActionListener,
40 private ConsoleTextPane console;
42 private JButton closeButton;
44 private JButton runButton;
46 private JButton haltButton;
48 private JButton clearButton;
50 private JButton historyButton;
52 private JButton stateButton;
58 public ScriptWindow(AppJmol appJmol)
60 this.viewer = appJmol.jmb.viewer;
61 this.appJmol = appJmol;
63 setLayout(new BorderLayout());
65 console = new ConsoleTextPane(this);
68 add(new JScrollPane(console), BorderLayout.CENTER);
70 JPanel buttonPanel = new JPanel();
71 add(buttonPanel, BorderLayout.SOUTH);
73 runButton = new JButton(GT._("Run"));
74 haltButton = new JButton(GT._("Halt"));
75 runButton.addActionListener(this);
76 // buttonPanel.add(runButton);
77 haltButton.addActionListener(this);
78 // buttonPanel.add(haltButton);
79 haltButton.setEnabled(false);
81 clearButton = new JButton(GT._("Clear"));
82 clearButton.addActionListener(this);
83 buttonPanel.add(clearButton);
85 historyButton = new JButton(GT._("History"));
86 historyButton.addActionListener(this);
87 buttonPanel.add(historyButton);
89 stateButton = new JButton(GT._("State"));
90 stateButton.addActionListener(this);
91 buttonPanel.add(stateButton);
93 closeButton = new JButton(GT._("Close"));
94 closeButton.addActionListener(this);
95 buttonPanel.add(closeButton);
97 for (int i = 0; i < buttonPanel.getComponentCount(); i++)
99 // ((JButton)buttonPanel.getComponent(i))
100 // .setMargin(new Insets(0, 0, 0, 0));
105 public void sendConsoleEcho(String strEcho)
107 if (strEcho != null && !isError)
110 console.outputEcho(strEcho);
116 boolean isError = false;
118 void setError(boolean TF)
122 // console.recallCommand(true);
125 public void sendConsoleMessage(String strStatus)
127 if (strStatus == null)
129 console.clearContent();
130 console.outputStatus("");
132 else if (strStatus.indexOf("ERROR:") >= 0)
134 console.outputError(strStatus);
139 console.outputStatus(strStatus);
143 public void notifyScriptTermination(String strMsg, int msWalltime)
145 if (strMsg != null && strMsg.indexOf("ERROR") >= 0)
147 console.outputError(strMsg);
149 runButton.setEnabled(true);
150 haltButton.setEnabled(false);
153 public void enterPressed()
155 runButton.doClick(100);
159 class ExecuteCommandThread extends Thread
164 ExecuteCommandThread(String command)
166 strCommand = command;
173 executeCommand(strCommand);
174 } catch (Exception ie)
176 Logger.debug("execution command interrupted!" + ie);
181 ExecuteCommandThread execThread;
183 void executeCommandAsThread()
185 String strCommand = console.getCommandString().trim();
186 if (strCommand.length() > 0)
188 execThread = new ExecuteCommandThread(strCommand);
193 void executeCommand(String strCommand)
197 console.appendNewline();
199 if (strCommand.length() > 0)
201 String strErrorMessage = null;
202 doWait = (strCommand.indexOf("WAIT ") == 0);
204 { // for testing, mainly
205 // demonstrates using the statusManager system.
206 runButton.setEnabled(false);
207 haltButton.setEnabled(true);
209 Vector info = (Vector) viewer
210 .scriptWaitStatus(strCommand.substring(5),
211 "+fileLoaded,+scriptStarted,+scriptStatus,+scriptEcho,+scriptTerminated");
212 runButton.setEnabled(true);
213 haltButton.setEnabled(false);
215 * info = [ statusRecortSet0, statusRecortSet1, statusRecortSet2, ...]
216 * statusRecordSet = [ statusRecord0, statusRecord1, statusRecord2, ...]
217 * statusRecord = [int msgPtr, String statusName, int intInfo, String
220 for (int i = 0; i < info.size(); i++)
222 Vector statusRecordSet = (Vector) info.get(i);
223 for (int j = 0; j < statusRecordSet.size(); j++)
225 Vector statusRecord = (Vector) statusRecordSet.get(j);
226 Logger.info("msg#=" + statusRecord.get(0) + " "
227 + statusRecord.get(1) + " intInfo="
228 + statusRecord.get(2) + " stringInfo="
229 + statusRecord.get(3));
232 console.appendNewline();
236 boolean isScriptExecuting = viewer.isScriptExecuting();
237 if (viewer.checkHalt(strCommand, true))
238 strErrorMessage = (isScriptExecuting ? "string execution halted with "
240 : "no script was executing");
242 strErrorMessage = "";// viewer.scriptCheck(strCommand);
243 // the problem is that scriptCheck is synchronized, so these might get
245 if (strErrorMessage != null && strErrorMessage.length() > 0)
247 console.outputError(strErrorMessage);
251 // runButton.setEnabled(false);
252 haltButton.setEnabled(true);
253 viewer.script(strCommand);
260 public void actionPerformed(ActionEvent e)
262 Object source = e.getSource();
263 if (source == closeButton)
265 // appJmol.showConsole(false);
267 else if (source == runButton)
269 executeCommandAsThread();
271 else if (source == clearButton)
273 console.clearContent();
275 else if (source == historyButton)
277 console.clearContent(viewer.getSetHistory(Integer.MAX_VALUE));
279 else if (source == stateButton)
281 console.clearContent(viewer.getStateInfo());
283 else if (source == haltButton)
285 viewer.haltScriptExecution();
287 console.grabFocus(); // always grab the focus (e.g., after clear)
291 class ConsoleTextPane extends JTextPane
294 ConsoleDocument consoleDoc;
296 EnterListener enterListener;
300 ConsoleTextPane(ScriptWindow scriptWindow)
302 super(new ConsoleDocument());
303 consoleDoc = (ConsoleDocument) getDocument();
304 consoleDoc.setConsoleTextPane(this);
305 this.enterListener = (EnterListener) scriptWindow;
306 this.viewer = scriptWindow.viewer;
309 public String getCommandString()
311 String cmd = consoleDoc.getCommandString();
315 public void setPrompt()
317 consoleDoc.setPrompt();
320 public void appendNewline()
322 consoleDoc.appendNewline();
325 public void outputError(String strError)
327 consoleDoc.outputError(strError);
330 public void outputErrorForeground(String strError)
332 consoleDoc.outputErrorForeground(strError);
335 public void outputEcho(String strEcho)
337 consoleDoc.outputEcho(strEcho);
340 public void outputStatus(String strStatus)
342 consoleDoc.outputStatus(strStatus);
345 public void enterPressed()
347 if (enterListener != null)
348 enterListener.enterPressed();
351 public void clearContent()
356 public void clearContent(String text)
358 consoleDoc.clearContent();
360 consoleDoc.outputEcho(text);
367 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
371 * Custom key event processing for command 0 implementation.
373 * Captures key up and key down strokes to call command history and redefines
374 * the same events with control down to allow caret vertical shift.
376 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
378 protected void processKeyEvent(KeyEvent ke)
380 // Id Control key is down, captures events does command
381 // history recall and inhibits caret vertical shift.
382 if (ke.getKeyCode() == KeyEvent.VK_UP
383 && ke.getID() == KeyEvent.KEY_PRESSED && !ke.isControlDown())
387 else if (ke.getKeyCode() == KeyEvent.VK_DOWN
388 && ke.getID() == KeyEvent.KEY_PRESSED && !ke.isControlDown())
390 recallCommand(false);
392 // If Control key is down, redefines the event as if it
393 // where a key up or key down stroke without modifiers.
394 // This allows to move the caret up and down
395 // with no command history recall.
396 else if ((ke.getKeyCode() == KeyEvent.VK_DOWN || ke.getKeyCode() == KeyEvent.VK_UP)
397 && ke.getID() == KeyEvent.KEY_PRESSED && ke.isControlDown())
399 super.processKeyEvent(new KeyEvent((Component) ke.getSource(), ke
400 .getID(), ke.getWhen(), 0, // No modifiers
401 ke.getKeyCode(), ke.getKeyChar(), ke.getKeyLocation()));
403 // Standard processing for other events.
406 super.processKeyEvent(ke);
407 // check command for compiler-identifyable syntax issues
408 // this may have to be taken out if people start complaining
409 // that only some of the commands are being checked
410 // that is -- that the script itself is not being fully checked
412 // not perfect -- help here?
413 if (ke.getID() == KeyEvent.KEY_RELEASED
414 && (ke.getKeyCode() > KeyEvent.VK_DOWN)
415 || ke.getKeyCode() == KeyEvent.VK_BACK_SPACE)
421 * Recall command history.
424 * - history up or down
426 void recallCommand(boolean up)
428 String cmd = viewer.getSetHistory(up ? -1 : 1);
435 if (cmd.endsWith(CommandHistory.ERROR_FLAG))
437 cmd = cmd.substring(0, cmd.indexOf(CommandHistory.ERROR_FLAG));
438 consoleDoc.replaceCommand(cmd, true);
442 consoleDoc.replaceCommand(cmd, false);
444 } catch (BadLocationException e)
452 String strCommand = consoleDoc.getCommandString();
453 if (strCommand.length() == 0)
456 .colorCommand(viewer.scriptCheck(strCommand) == null ? consoleDoc.attUserInput
457 : consoleDoc.attError);
462 class ConsoleDocument extends DefaultStyledDocument
465 ConsoleTextPane consoleTextPane;
467 SimpleAttributeSet attError;
469 SimpleAttributeSet attEcho;
471 SimpleAttributeSet attPrompt;
473 SimpleAttributeSet attUserInput;
475 SimpleAttributeSet attStatus;
481 attError = new SimpleAttributeSet();
482 StyleConstants.setForeground(attError, Color.red);
484 attPrompt = new SimpleAttributeSet();
485 StyleConstants.setForeground(attPrompt, Color.magenta);
487 attUserInput = new SimpleAttributeSet();
488 StyleConstants.setForeground(attUserInput, Color.black);
490 attEcho = new SimpleAttributeSet();
491 StyleConstants.setForeground(attEcho, Color.blue);
492 StyleConstants.setBold(attEcho, true);
494 attStatus = new SimpleAttributeSet();
495 StyleConstants.setForeground(attStatus, Color.black);
496 StyleConstants.setItalic(attStatus, true);
499 void setConsoleTextPane(ConsoleTextPane consoleTextPane)
501 this.consoleTextPane = consoleTextPane;
504 Position positionBeforePrompt; // starts at 0, so first time isn't tracked
506 // (at least on Mac OS X)
508 Position positionAfterPrompt; // immediately after $, so this will track
510 int offsetAfterPrompt; // only still needed for the insertString override and
515 * Removes all content of the script window, and add a new prompt.
521 super.remove(0, getLength());
522 } catch (BadLocationException exception)
524 System.out.println("Could not clear script window content: "
525 + exception.getMessage());
533 super.insertString(getLength(), "$ ", attPrompt);
534 setOffsetPositions();
535 consoleTextPane.setCaretPosition(offsetAfterPrompt);
536 } catch (BadLocationException e)
542 void setOffsetPositions()
546 offsetAfterPrompt = getLength();
547 positionBeforePrompt = createPosition(offsetAfterPrompt - 2);
548 // after prompt should be immediately after $ otherwise tracks the end
549 // of the line (and no command will be found) at least on Mac OS X it did.
550 positionAfterPrompt = createPosition(offsetAfterPrompt - 1);
551 } catch (BadLocationException e)
561 offsetAfterPrompt = getLength();
562 positionAfterPrompt = positionBeforePrompt = createPosition(offsetAfterPrompt);
563 consoleTextPane.setCaretPosition(offsetAfterPrompt);
564 } catch (BadLocationException e)
570 // it looks like the positionBeforePrompt does not track when it started out
572 // and a insertString at location 0 occurs. It may be better to track the
573 // position after the prompt in stead
574 void outputBeforePrompt(String str, SimpleAttributeSet attribute)
578 int pt = consoleTextPane.getCaretPosition();
579 Position caretPosition = createPosition(pt);
580 pt = positionBeforePrompt.getOffset();
581 super.insertString(pt, str + "\n", attribute);
582 setOffsetPositions();
583 pt = caretPosition.getOffset();
584 consoleTextPane.setCaretPosition(pt);
585 } catch (BadLocationException e)
591 void outputError(String strError)
593 outputBeforePrompt(strError, attError);
596 void outputErrorForeground(String strError)
600 super.insertString(getLength(), strError + "\n", attError);
601 consoleTextPane.setCaretPosition(getLength());
602 } catch (BadLocationException e)
609 void outputEcho(String strEcho)
611 outputBeforePrompt(strEcho, attEcho);
614 void outputStatus(String strStatus)
616 outputBeforePrompt(strStatus, attStatus);
623 super.insertString(getLength(), "\n", attUserInput);
624 consoleTextPane.setCaretPosition(getLength());
625 } catch (BadLocationException e)
631 // override the insertString to make sure everything typed ends up at the end
632 // or in the 'command line' using the proper font, and the newline is
634 public void insertString(int offs, String str, AttributeSet a)
635 throws BadLocationException
637 int ichNewline = str.indexOf('\n');
639 str = str.substring(0, ichNewline);
642 if (offs < offsetAfterPrompt)
646 super.insertString(offs, str, a == attError ? a : attUserInput);
647 consoleTextPane.setCaretPosition(offs + str.length());
651 consoleTextPane.enterPressed();
655 String getCommandString()
657 String strCommand = "";
660 int cmdStart = positionAfterPrompt.getOffset();
661 strCommand = getText(cmdStart, getLength() - cmdStart);
662 while (strCommand.length() > 0 && strCommand.charAt(0) == ' ')
663 strCommand = strCommand.substring(1);
664 } catch (BadLocationException e)
671 public void remove(int offs, int len) throws BadLocationException
673 if (offs < offsetAfterPrompt)
675 len -= offsetAfterPrompt - offs;
678 offs = offsetAfterPrompt;
680 super.remove(offs, len);
681 // consoleTextPane.setCaretPosition(offs);
684 public void replace(int offs, int length, String str, AttributeSet attrs)
685 throws BadLocationException
687 if (offs < offsetAfterPrompt)
689 if (offs + length < offsetAfterPrompt)
696 length -= offsetAfterPrompt - offs;
697 offs = offsetAfterPrompt;
700 super.replace(offs, length, str, attrs);
701 // consoleTextPane.setCaretPosition(offs + str.length());
705 * Replaces current command on script.
710 * true to set error color ends with #??
712 * @throws BadLocationException
714 void replaceCommand(String newCommand, boolean isError)
715 throws BadLocationException
717 if (positionAfterPrompt == positionBeforePrompt)
719 replace(offsetAfterPrompt, getLength() - offsetAfterPrompt, newCommand,
720 isError ? attError : attUserInput);
723 void colorCommand(SimpleAttributeSet att)
725 if (positionAfterPrompt == positionBeforePrompt)
727 setCharacterAttributes(offsetAfterPrompt, getLength()
728 - offsetAfterPrompt, att, true);
732 interface EnterListener
734 public void enterPressed();