2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ 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.*;
25 import jalview.jbgui.GStructureViewer;
28 import java.awt.event.*;
31 import javax.swing.text.*;
33 import java.util.Vector;
35 import org.jmol.i18n.GT;
36 import org.jmol.util.Logger;
37 import org.jmol.util.CommandHistory;
39 // TODO: this class is copied in from jmol 11.0.2 - upgrade to 12.0.2 ?
40 public final class ScriptWindow extends JPanel implements ActionListener,
44 private ConsoleTextPane console;
46 private JButton closeButton;
48 private JButton runButton;
50 private JButton haltButton;
52 private JButton clearButton;
54 private JButton historyButton;
56 private JButton stateButton;
60 GStructureViewer appJmol;
62 public ScriptWindow(AppJmol appJmol)
64 this.viewer = appJmol.jmb.viewer;
65 this.appJmol = appJmol;
67 setLayout(new BorderLayout());
69 console = new ConsoleTextPane(this);
72 add(new JScrollPane(console), BorderLayout.CENTER);
74 JPanel buttonPanel = new JPanel();
75 add(buttonPanel, BorderLayout.SOUTH);
77 runButton = new JButton(GT._("Run"));
78 haltButton = new JButton(GT._("Halt"));
79 runButton.addActionListener(this);
80 // buttonPanel.add(runButton);
81 haltButton.addActionListener(this);
82 // buttonPanel.add(haltButton);
83 haltButton.setEnabled(false);
85 clearButton = new JButton(GT._("Clear"));
86 clearButton.addActionListener(this);
87 buttonPanel.add(clearButton);
89 historyButton = new JButton(GT._("History"));
90 historyButton.addActionListener(this);
91 buttonPanel.add(historyButton);
93 stateButton = new JButton(GT._("State"));
94 stateButton.addActionListener(this);
95 buttonPanel.add(stateButton);
97 closeButton = new JButton(GT._("Close"));
98 closeButton.addActionListener(this);
99 buttonPanel.add(closeButton);
101 for (int i = 0; i < buttonPanel.getComponentCount(); i++)
103 // ((JButton)buttonPanel.getComponent(i))
104 // .setMargin(new Insets(0, 0, 0, 0));
109 public void sendConsoleEcho(String strEcho)
111 if (strEcho != null && !isError)
114 console.outputEcho(strEcho);
120 boolean isError = false;
122 void setError(boolean TF)
126 // console.recallCommand(true);
129 public void sendConsoleMessage(String strStatus)
131 if (strStatus == null)
133 console.clearContent();
134 console.outputStatus("");
136 else if (strStatus.indexOf("ERROR:") >= 0)
138 console.outputError(strStatus);
143 console.outputStatus(strStatus);
147 public void notifyScriptTermination(String strMsg, int msWalltime)
149 if (strMsg != null && strMsg.indexOf("ERROR") >= 0)
151 console.outputError(strMsg);
153 runButton.setEnabled(true);
154 haltButton.setEnabled(false);
157 public void enterPressed()
159 runButton.doClick(100);
163 class ExecuteCommandThread extends Thread
168 ExecuteCommandThread(String command)
170 strCommand = command;
177 executeCommand(strCommand);
178 } catch (Exception ie)
180 Logger.debug("execution command interrupted!" + ie);
185 ExecuteCommandThread execThread;
187 void executeCommandAsThread()
189 String strCommand = console.getCommandString().trim();
190 if (strCommand.length() > 0)
192 execThread = new ExecuteCommandThread(strCommand);
197 void executeCommand(String strCommand)
201 console.appendNewline();
203 if (strCommand.length() > 0)
205 String strErrorMessage = null;
206 doWait = (strCommand.indexOf("WAIT ") == 0);
208 { // for testing, mainly
209 // demonstrates using the statusManager system.
210 runButton.setEnabled(false);
211 haltButton.setEnabled(true);
213 Vector info = (Vector) viewer
214 .scriptWaitStatus(strCommand.substring(5),
215 "+fileLoaded,+scriptStarted,+scriptStatus,+scriptEcho,+scriptTerminated");
216 runButton.setEnabled(true);
217 haltButton.setEnabled(false);
219 * info = [ statusRecortSet0, statusRecortSet1, statusRecortSet2, ...]
220 * statusRecordSet = [ statusRecord0, statusRecord1, statusRecord2, ...]
221 * statusRecord = [int msgPtr, String statusName, int intInfo, String
224 for (int i = 0; i < info.size(); i++)
226 Vector statusRecordSet = (Vector) info.get(i);
227 for (int j = 0; j < statusRecordSet.size(); j++)
229 Vector statusRecord = (Vector) statusRecordSet.get(j);
230 Logger.info("msg#=" + statusRecord.get(0) + " "
231 + statusRecord.get(1) + " intInfo="
232 + statusRecord.get(2) + " stringInfo="
233 + statusRecord.get(3));
236 console.appendNewline();
240 boolean isScriptExecuting = viewer.isScriptExecuting();
241 if (viewer.checkHalt(strCommand, true))
242 strErrorMessage = (isScriptExecuting ? "string execution halted with "
244 : "no script was executing");
246 strErrorMessage = "";// viewer.scriptCheck(strCommand);
247 // the problem is that scriptCheck is synchronized, so these might get
249 if (strErrorMessage != null && strErrorMessage.length() > 0)
251 console.outputError(strErrorMessage);
255 // runButton.setEnabled(false);
256 haltButton.setEnabled(true);
257 viewer.script(strCommand);
264 public void actionPerformed(ActionEvent e)
266 Object source = e.getSource();
267 if (source == closeButton)
269 // appJmol.showConsole(false);
271 else if (source == runButton)
273 executeCommandAsThread();
275 else if (source == clearButton)
277 console.clearContent();
279 else if (source == historyButton)
281 console.clearContent(viewer.getSetHistory(Integer.MAX_VALUE));
283 else if (source == stateButton)
285 console.clearContent(viewer.getStateInfo());
287 else if (source == haltButton)
289 viewer.haltScriptExecution();
291 console.grabFocus(); // always grab the focus (e.g., after clear)
295 class ConsoleTextPane extends JTextPane
298 ConsoleDocument consoleDoc;
300 EnterListener enterListener;
304 ConsoleTextPane(ScriptWindow scriptWindow)
306 super(new ConsoleDocument());
307 consoleDoc = (ConsoleDocument) getDocument();
308 consoleDoc.setConsoleTextPane(this);
309 this.enterListener = (EnterListener) scriptWindow;
310 this.viewer = scriptWindow.viewer;
313 public String getCommandString()
315 String cmd = consoleDoc.getCommandString();
319 public void setPrompt()
321 consoleDoc.setPrompt();
324 public void appendNewline()
326 consoleDoc.appendNewline();
329 public void outputError(String strError)
331 consoleDoc.outputError(strError);
334 public void outputErrorForeground(String strError)
336 consoleDoc.outputErrorForeground(strError);
339 public void outputEcho(String strEcho)
341 consoleDoc.outputEcho(strEcho);
344 public void outputStatus(String strStatus)
346 consoleDoc.outputStatus(strStatus);
349 public void enterPressed()
351 if (enterListener != null)
352 enterListener.enterPressed();
355 public void clearContent()
360 public void clearContent(String text)
362 consoleDoc.clearContent();
364 consoleDoc.outputEcho(text);
371 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
375 * Custom key event processing for command 0 implementation.
377 * Captures key up and key down strokes to call command history and redefines
378 * the same events with control down to allow caret vertical shift.
380 * @see java.awt.Component#processKeyEvent(java.awt.event.KeyEvent)
382 protected void processKeyEvent(KeyEvent ke)
384 // Id Control key is down, captures events does command
385 // history recall and inhibits caret vertical shift.
386 if (ke.getKeyCode() == KeyEvent.VK_UP
387 && ke.getID() == KeyEvent.KEY_PRESSED && !ke.isControlDown())
391 else if (ke.getKeyCode() == KeyEvent.VK_DOWN
392 && ke.getID() == KeyEvent.KEY_PRESSED && !ke.isControlDown())
394 recallCommand(false);
396 // If Control key is down, redefines the event as if it
397 // where a key up or key down stroke without modifiers.
398 // This allows to move the caret up and down
399 // with no command history recall.
400 else if ((ke.getKeyCode() == KeyEvent.VK_DOWN || ke.getKeyCode() == KeyEvent.VK_UP)
401 && ke.getID() == KeyEvent.KEY_PRESSED && ke.isControlDown())
403 super.processKeyEvent(new KeyEvent((Component) ke.getSource(), ke
404 .getID(), ke.getWhen(), 0, // No modifiers
405 ke.getKeyCode(), ke.getKeyChar(), ke.getKeyLocation()));
407 // Standard processing for other events.
410 super.processKeyEvent(ke);
411 // check command for compiler-identifyable syntax issues
412 // this may have to be taken out if people start complaining
413 // that only some of the commands are being checked
414 // that is -- that the script itself is not being fully checked
416 // not perfect -- help here?
417 if (ke.getID() == KeyEvent.KEY_RELEASED
418 && (ke.getKeyCode() > KeyEvent.VK_DOWN)
419 || ke.getKeyCode() == KeyEvent.VK_BACK_SPACE)
425 * Recall command history.
428 * - history up or down
430 void recallCommand(boolean up)
432 String cmd = viewer.getSetHistory(up ? -1 : 1);
439 if (cmd.endsWith(CommandHistory.ERROR_FLAG))
441 cmd = cmd.substring(0, cmd.indexOf(CommandHistory.ERROR_FLAG));
442 consoleDoc.replaceCommand(cmd, true);
446 consoleDoc.replaceCommand(cmd, false);
448 } catch (BadLocationException e)
456 String strCommand = consoleDoc.getCommandString();
457 if (strCommand.length() == 0)
460 .colorCommand(viewer.scriptCheck(strCommand) == null ? consoleDoc.attUserInput
461 : consoleDoc.attError);
466 class ConsoleDocument extends DefaultStyledDocument
469 ConsoleTextPane consoleTextPane;
471 SimpleAttributeSet attError;
473 SimpleAttributeSet attEcho;
475 SimpleAttributeSet attPrompt;
477 SimpleAttributeSet attUserInput;
479 SimpleAttributeSet attStatus;
485 attError = new SimpleAttributeSet();
486 StyleConstants.setForeground(attError, Color.red);
488 attPrompt = new SimpleAttributeSet();
489 StyleConstants.setForeground(attPrompt, Color.magenta);
491 attUserInput = new SimpleAttributeSet();
492 StyleConstants.setForeground(attUserInput, Color.black);
494 attEcho = new SimpleAttributeSet();
495 StyleConstants.setForeground(attEcho, Color.blue);
496 StyleConstants.setBold(attEcho, true);
498 attStatus = new SimpleAttributeSet();
499 StyleConstants.setForeground(attStatus, Color.black);
500 StyleConstants.setItalic(attStatus, true);
503 void setConsoleTextPane(ConsoleTextPane consoleTextPane)
505 this.consoleTextPane = consoleTextPane;
508 Position positionBeforePrompt; // starts at 0, so first time isn't tracked
510 // (at least on Mac OS X)
512 Position positionAfterPrompt; // immediately after $, so this will track
514 int offsetAfterPrompt; // only still needed for the insertString override and
519 * Removes all content of the script window, and add a new prompt.
525 super.remove(0, getLength());
526 } catch (BadLocationException exception)
528 System.out.println("Could not clear script window content: "
529 + exception.getMessage());
537 super.insertString(getLength(), "$ ", attPrompt);
538 setOffsetPositions();
539 consoleTextPane.setCaretPosition(offsetAfterPrompt);
540 } catch (BadLocationException e)
546 void setOffsetPositions()
550 offsetAfterPrompt = getLength();
551 positionBeforePrompt = createPosition(offsetAfterPrompt - 2);
552 // after prompt should be immediately after $ otherwise tracks the end
553 // of the line (and no command will be found) at least on Mac OS X it did.
554 positionAfterPrompt = createPosition(offsetAfterPrompt - 1);
555 } catch (BadLocationException e)
565 offsetAfterPrompt = getLength();
566 positionAfterPrompt = positionBeforePrompt = createPosition(offsetAfterPrompt);
567 consoleTextPane.setCaretPosition(offsetAfterPrompt);
568 } catch (BadLocationException e)
574 // it looks like the positionBeforePrompt does not track when it started out
576 // and a insertString at location 0 occurs. It may be better to track the
577 // position after the prompt in stead
578 void outputBeforePrompt(String str, SimpleAttributeSet attribute)
582 int pt = consoleTextPane.getCaretPosition();
583 Position caretPosition = createPosition(pt);
584 pt = positionBeforePrompt.getOffset();
585 super.insertString(pt, str + "\n", attribute);
586 setOffsetPositions();
587 pt = caretPosition.getOffset();
588 consoleTextPane.setCaretPosition(pt);
589 } catch (BadLocationException e)
595 void outputError(String strError)
597 outputBeforePrompt(strError, attError);
600 void outputErrorForeground(String strError)
604 super.insertString(getLength(), strError + "\n", attError);
605 consoleTextPane.setCaretPosition(getLength());
606 } catch (BadLocationException e)
613 void outputEcho(String strEcho)
615 outputBeforePrompt(strEcho, attEcho);
618 void outputStatus(String strStatus)
620 outputBeforePrompt(strStatus, attStatus);
627 super.insertString(getLength(), "\n", attUserInput);
628 consoleTextPane.setCaretPosition(getLength());
629 } catch (BadLocationException e)
635 // override the insertString to make sure everything typed ends up at the end
636 // or in the 'command line' using the proper font, and the newline is
638 public void insertString(int offs, String str, AttributeSet a)
639 throws BadLocationException
641 int ichNewline = str.indexOf('\n');
643 str = str.substring(0, ichNewline);
646 if (offs < offsetAfterPrompt)
650 super.insertString(offs, str, a == attError ? a : attUserInput);
651 consoleTextPane.setCaretPosition(offs + str.length());
655 consoleTextPane.enterPressed();
659 String getCommandString()
661 String strCommand = "";
664 int cmdStart = positionAfterPrompt.getOffset();
665 strCommand = getText(cmdStart, getLength() - cmdStart);
666 while (strCommand.length() > 0 && strCommand.charAt(0) == ' ')
667 strCommand = strCommand.substring(1);
668 } catch (BadLocationException e)
675 public void remove(int offs, int len) throws BadLocationException
677 if (offs < offsetAfterPrompt)
679 len -= offsetAfterPrompt - offs;
682 offs = offsetAfterPrompt;
684 super.remove(offs, len);
685 // consoleTextPane.setCaretPosition(offs);
688 public void replace(int offs, int length, String str, AttributeSet attrs)
689 throws BadLocationException
691 if (offs < offsetAfterPrompt)
693 if (offs + length < offsetAfterPrompt)
700 length -= offsetAfterPrompt - offs;
701 offs = offsetAfterPrompt;
704 super.replace(offs, length, str, attrs);
705 // consoleTextPane.setCaretPosition(offs + str.length());
709 * Replaces current command on script.
714 * true to set error color ends with #??
716 * @throws BadLocationException
718 void replaceCommand(String newCommand, boolean isError)
719 throws BadLocationException
721 if (positionAfterPrompt == positionBeforePrompt)
723 replace(offsetAfterPrompt, getLength() - offsetAfterPrompt, newCommand,
724 isError ? attError : attUserInput);
727 void colorCommand(SimpleAttributeSet att)
729 if (positionAfterPrompt == positionBeforePrompt)
731 setCharacterAttributes(offsetAfterPrompt, getLength()
732 - offsetAfterPrompt, att, true);
736 interface EnterListener
738 public void enterPressed();