2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2007-2008 - INRIA - Vincent COUVERT
5 * This file must be used under the terms of the CeCILL.
6 * This source file is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at
9 * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
13 package org.scilab.modules.console;
15 import java.awt.BorderLayout;
16 import java.awt.Color;
17 import java.awt.Cursor;
18 import java.awt.Dimension;
20 import java.awt.Point;
21 import java.awt.event.ComponentAdapter;
22 import java.awt.event.ComponentEvent;
23 import java.io.IOException;
24 import java.util.concurrent.Semaphore;
26 import javax.swing.BoundedRangeModel;
27 import javax.swing.DefaultBoundedRangeModel;
28 import javax.swing.JLabel;
29 import javax.swing.JPanel;
30 import javax.swing.JScrollPane;
31 import javax.swing.JEditorPane;
32 import javax.swing.JTextPane;
33 import javax.swing.JViewport;
34 import javax.swing.SwingUtilities;
35 import javax.swing.text.BadLocationException;
36 import javax.swing.text.Document;
37 import javax.swing.KeyStroke;
38 import javax.xml.parsers.ParserConfigurationException;
39 import org.scilab.modules.localization.Messages;
41 import org.xml.sax.SAXException;
43 import com.artenum.rosetta.interfaces.core.ConsoleConfiguration;
44 import com.artenum.rosetta.interfaces.core.InputParsingManager;
45 import com.artenum.rosetta.interfaces.ui.InputCommandView;
46 import com.artenum.rosetta.interfaces.ui.OutputView;
47 import com.artenum.rosetta.interfaces.ui.PromptView;
48 import com.artenum.rosetta.ui.Console;
49 import com.artenum.rosetta.util.ConfigurationBuilder;
50 import com.artenum.rosetta.util.ConsoleBuilder;
51 import com.artenum.rosetta.util.StringConstants;
54 * Main class for Scilab Console based on Generic Console from Artenum
55 * @author Vincent COUVERT
57 public abstract class SciConsole extends JPanel {
59 private static final long serialVersionUID = 1L;
61 private static final int LINE_NUMBER_IN_PROMPT = 2;
63 private static final String BACKSLASH_R = "\r";
66 * Maximum length of a command send to Scilab
68 private static final int MAX_CMD_LENGTH = 512;
71 * Configuration associated to the console oject
73 private ConsoleConfiguration config;
76 * Scroll Pane used in Scilab Console
78 private JScrollPane jSP;
81 * Generic console object
83 private Console sciConsole;
86 * Value used to get one char from user input (when using [more y or n ?])
88 private int userInputValue;
91 * Protection for userInputValue variable R/W
93 private Semaphore canReadUserInputValue = new Semaphore(1);
96 * Boolean flag used to store the state of Scilab (true is all works done)
98 private boolean workDone;
100 private boolean atBottom;
102 private boolean isToHome;
106 * @param configFilePath the configuration file to use
108 public SciConsole(String configFilePath) {
109 super(new BorderLayout());
112 config = ConfigurationBuilder.buildConfiguration(configFilePath);
113 config.setActiveProfile("scilab");
114 if (System.getProperty("os.name").toLowerCase().indexOf("mac") != -1)
116 ConsoleConfiguration configMac = ConfigurationBuilder.buildConfiguration(configFilePath);;
117 configMac.setActiveProfile("macosx");
118 for (KeyStroke key : config.getKeyMapping().keys()){
119 config.getKeyMapping().put(key,"");
121 for (KeyStroke key : configMac.getKeyMapping().keys()){
122 config.getKeyMapping().put(key, configMac.getKeyMapping().get(key));
125 } catch (IllegalArgumentException e) {
126 // TODO Auto-generated catch block
128 } catch (SAXException e) {
129 // TODO Auto-generated catch block
131 } catch (IOException e) {
132 // TODO Auto-generated catch block
134 } catch (ParserConfigurationException e) {
135 // TODO Auto-generated catch block
139 sciConsole = ConsoleBuilder.buildConsole(config, this);
140 jSP = new JScrollPane(sciConsole);
142 BoundedRangeModel model = jSP.getVerticalScrollBar().getModel();
143 jSP.getVerticalScrollBar().setModel(new DefaultBoundedRangeModel(model.getValue(), model.getExtent(), model.getMinimum(), model.getMaximum()) {
144 public void setRangeProperties(int newValue, int newExtent, int newMin, int newMax, boolean adjusting) {
145 // This method is overriden to keep the knob at the bottom during viewport resize
146 // and to keep the knob at an other place if the user decided it.
147 if (newMax != getMaximum()) {
150 super.setRangeProperties(newMax - newExtent, newExtent, newMin, newMax, false);
152 super.setRangeProperties(newValue, newExtent, newMin, newMax, false);
155 double percent = (double) Math.abs(newMax - newValue - newExtent) / (double) newMax;
156 if (atBottom && percent <= 0.03) {
157 super.setRangeProperties(newMax - newExtent, newExtent, newMin, newMax, true);
159 super.setRangeProperties(newValue, newExtent, newMin, newMax, true);
160 atBottom = percent <= 0.01;
164 super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting);
169 this.add(jSP, BorderLayout.CENTER);
171 // The console is given to the outputView so that updateScrollPosition is is accessible
172 ((SciOutputView) config.getOutputView()).setConsole(this);
174 // The console is given to the outputView so that Drag&Drop can work
175 ((SciInputCommandView) config.getInputCommandView()).setConsole(this);
177 // The console is given to the CompletionWindow
178 ((SciCompletionWindow) config.getCompletionWindow()).setConsole(this);
179 ((SciCompletionWindow) config.getCompletionWindow()).setGraphicalContext(this);
181 // The promptview is given to the Parsing Manager
182 // Used to get the position of the CompletionWindow
183 ((SciInputParsingManager) config.getInputParsingManager()).setPromptView(this.getConfiguration().getPromptView());
185 // Reset history settings - bug 3612
186 ((SciHistoryManager)config.getHistoryManager()).setInHistory(false);
188 // Bug 8055 : update the lines/columns only when the console is resized
189 addComponentListener(new ComponentAdapter() {
190 public void componentResized(ComponentEvent evt) {
191 SwingUtilities.invokeLater(new Runnable() {
194 jSP.getVerticalScrollBar().setBlockIncrement(jSP.getViewport().getExtentSize().height);
195 jSP.getHorizontalScrollBar().setBlockIncrement(jSP.getViewport().getExtentSize().width);
201 sciConsole.invalidate();
202 sciConsole.doLayout();
206 * Gets the configuration associated to the console
207 * @return the configuration
209 public ConsoleConfiguration getConfiguration() {
214 * Sets the configuration associated to the console
215 * @param newConfig the new config to set
217 public void setConfiguration(ConsoleConfiguration newConfig) {
222 * Updates Scilab internal variables containing the size of the console
223 * These variables are used to format data before displaying it
225 public void scilabLinesUpdate() {
226 // Size of the console
227 int outputViewWidth = jSP.getViewport().getExtentSize().width;
230 OutputView outputView = this.getConfiguration().getOutputView();
231 int[] charsWidth = ((JEditorPane) outputView).getFontMetrics(((JEditorPane) outputView).getFont()).getWidths();
233 // This loop is not needed for monospaced fonts !
234 int maxCharWidth = charsWidth[33];
235 // The range 33--126 corresponds to the usual characters in ASCII
236 for (int i = 34; i < 126; i++) {
237 if (charsWidth[i] > maxCharWidth) {
238 maxCharWidth = charsWidth[i];
242 int numberOfLines = Math.max(1, getNumberOfLines());
243 int promptWidth = ((JPanel) this.getConfiguration().getPromptView()).getPreferredSize().width;
245 int numberOfColumns = (outputViewWidth - promptWidth) / maxCharWidth - 1;
246 /* -1 because of the margin between text prompt and command line text */
248 GuiManagement.setScilabLines(numberOfLines, numberOfColumns);
252 * Get the number of lines that can be displayed in the visible part of the console
253 * @return the number of lines
255 public int getNumberOfLines() {
256 // Size of the console
257 int outputViewHeight = jSP.getViewport().getExtentSize().height;
260 OutputView outputView = this.getConfiguration().getOutputView();
261 int charHeight = ((JEditorPane) outputView).getFontMetrics(((JEditorPane) outputView).getFont()).getHeight();
262 int[] charsWidth = ((JEditorPane) outputView).getFontMetrics(((JEditorPane) outputView).getFont()).getWidths();
264 // This loop is not needed for monospaced fonts !
265 int maxCharWidth = charsWidth[0];
266 for (int i = 1; i < charsWidth.length; i++) {
267 if (charsWidth[i] > maxCharWidth) {
268 maxCharWidth = charsWidth[i];
272 return outputViewHeight / charHeight - 1; /* -1 because of the size of the InputCommandLine */
276 * Updates the scroll bars according to the contents
278 public void updateScrollPosition() {
279 // Update the scrollbar properties
280 jSP.getVerticalScrollBar().setBlockIncrement(jSP.getViewport().getExtentSize().height);
281 jSP.getHorizontalScrollBar().setBlockIncrement(jSP.getViewport().getExtentSize().width);
282 SwingUtilities.invokeLater(new Runnable() {
284 jSP.getVerticalScrollBar().getModel().setValue(jSP.getVerticalScrollBar().getModel().getMaximum() - jSP.getVerticalScrollBar().getModel().getExtent());
290 * Clears the console and the output view
292 public void clear() {
293 if (sciConsole.isPreferredSizeSet()) {
294 sciConsole.setPreferredSize(null);
295 sciConsole.invalidate();
296 sciConsole.doLayout();
300 config.getInputCommandViewStyledDocument().remove(0, config.getInputCommandViewStyledDocument().getLength());
301 } catch (BadLocationException e) {
304 config.getOutputView().reset();
306 /* We add a space to add a line */
307 /* clc , F2 and menus have same position */
308 config.getOutputView().append(" ");
312 * Clears lines from the end of the output view
313 * If nbLines == -1 ==> Called from SwingScilabConsole.getCharWithoutOutput() ([more y or n ?])
314 * If nbLines == 0 ==> Clear the InputCommandLine
315 * @param nbLines the number of lines to be deleted
317 public void clear(int nbLines) {
318 if (sciConsole.isPreferredSizeSet()) {
319 sciConsole.setPreferredSize(null);
320 sciConsole.invalidate();
321 sciConsole.doLayout();
326 config.getInputCommandView().reset();
328 // Clear lines in output command view
330 // We have to remove the command entered by the user
331 int totalNumberOfLines = nbLines + LINE_NUMBER_IN_PROMPT;
333 Document outputDoc = ((JEditorPane) config.getOutputView()).getDocument();
334 String outputTxt = outputDoc.getText(0, outputDoc.getLength());
336 // Are there enough lines in the output view ?
337 String[] allLines = outputTxt.split(StringConstants.NEW_LINE);
338 if (allLines.length < totalNumberOfLines) {
340 config.getOutputView().reset();
341 config.getOutputView().append(Messages.gettext("Out of Screen"));
345 for (int i = 0; i < totalNumberOfLines; i++) {
346 outputTxt = outputDoc.getText(0, outputDoc.getLength());
347 lastEOL = outputTxt.lastIndexOf(StringConstants.NEW_LINE);
348 outputDoc.remove(lastEOL, outputDoc.getLength() - lastEOL);
351 } catch (BadLocationException e) {
352 // TODO Auto-generated catch block
359 * Puts the prompt in the top left corner of the console
361 public void toHome() {
365 public void setToHome() {
367 Dimension jSPExtSize = jSP.getViewport().getExtentSize();
368 int caretH = ((SciInputCommandView) config.getInputCommandView()).getCaretHeight();
369 int height = jSPExtSize.height + ((SciPromptView) config.getPromptView()).getParent().getBounds().y - caretH;
370 Dimension newDim = new Dimension(sciConsole.getSize().width, height);
371 sciConsole.setPreferredSize(newDim);
372 sciConsole.invalidate();
373 sciConsole.doLayout();
374 ((SciOutputView) config.getOutputView()).addComponentListener(new ComponentAdapter() {
375 public void componentResized(ComponentEvent evt) {
376 if (evt.getComponent().getSize().height >= sciConsole.getSize().height) {
377 evt.getComponent().removeComponentListener(this);
378 sciConsole.setPreferredSize(null);
379 sciConsole.invalidate();
380 sciConsole.doLayout();
386 jSP.getVerticalScrollBar().getModel().setValue(jSP.getVerticalScrollBar().getModel().getMaximum() - jSP.getVerticalScrollBar().getModel().getExtent());
391 * Gets the user input value
392 * @return the value entered by the used
394 public int getUserInputValue() {
396 canReadUserInputValue.acquire();
397 } catch (InterruptedException e) {
400 return userInputValue;
404 * Sets the value entered by the user
405 * @param userInputValue new value
407 public void setUserInputValue(int userInputValue) {
408 this.userInputValue = userInputValue;
409 canReadUserInputValue.release();
413 * Gets the semaphore protection so that it can be acquired
414 * @return the semaphore
416 public Semaphore getCanReadUserInputValue() {
417 return canReadUserInputValue;
421 * Send commands to be executed by Scilab (after a copy/paste or drag&drop...)
422 * @param textToExec all text lines to executed
423 * @param displayCmdInOutput flag indicating if the input command has to be displayed in the output view
424 * @param storeInHistory flag indicating if the input command has to be stored in the history
426 public void sendCommandsToScilab(String textToExec, boolean displayCmdInOutput, boolean storeInHistory) {
427 String[] linesToExec = textToExec.split(StringConstants.NEW_LINE);
428 int nbStatements = 0;
431 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
433 while (nbStatements < linesToExec.length) {
434 // This loop contains code very similar to the code of ValidationAction.java
435 InputParsingManager inputParsingManager = config.getInputParsingManager();
436 PromptView promptView = config.getPromptView();
438 // Reset command line
439 inputParsingManager.reset();
441 // Reset history settings
442 config.getHistoryManager().setInHistory(false);
444 // Hide the prompt and command line
445 config.getInputCommandView().setEditable(false);
446 ((SciInputCommandView) config.getInputCommandView()).getCaret().setVisible(false);
448 config.getPromptView().setVisible(false);
450 // Remove the prompt if present at the beginning of the text to execute
451 // Bug 3002 fix: this "functionality" has been removed because:
452 // - Remove the --> even if not from paste action
453 // - Does not remove pause prompts
455 // Store the command in the buffer so that Scilab can read it
456 if (linesToExec[nbStatements].length() > MAX_CMD_LENGTH) {
457 config.getOutputView().append("Command is too long (more than " + MAX_CMD_LENGTH
458 + " characters long): could not send it to Scilab\n");
459 ((SciInputCommandView) config.getInputCommandView()).setCmdBuffer("", false);
463 ((SciInputCommandView) config.getInputCommandView())
464 .setCmdBuffer(linesToExec[nbStatements].replace(BACKSLASH_R, ""), displayCmdInOutput);
465 if (storeInHistory) {
466 ((SciHistoryManager) config.getHistoryManager()).addEntry(linesToExec[nbStatements].replace(BACKSLASH_R, ""));
474 * Get the JScrollPane associated to the console
475 * @return the JScrollPane associated to the console
477 public JScrollPane getJScrollPane() {
482 * Get the Console object associated to the console
483 * @return the Console object associated to the console
485 public Console getSciConsole() {
490 * Get the current status of the console
491 * If the prompt view is visible, Scilab is waiting for commands
492 * @return true is Scilab is waiting for commands
494 public boolean isWaitingForInput() {
495 return ((JTextPane) config.getInputCommandView()).isEditable();
499 * This methods is used by Scilab to get a new command to execute
500 * @return the command to execute
502 public String readLine() {
504 InputCommandView inputCmdView = this.getConfiguration().getInputCommandView();
506 getConfiguration().getOutputView().setCaretPositionToEnd();
510 // Display Cursor to show Scilab is available.
511 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
514 return ((SciInputCommandView) inputCmdView).getCmdBuffer();
518 * This method is used to display the prompt
520 public abstract void displayPrompt();
523 * Does Scilab have finished its work ?
524 * @return true if Scilab is waiting for new commands
526 public boolean isWorkDone() {
531 * Set the font of the Console
532 * @param font the font to set
534 public void setFont(Font font) {
535 if (sciConsole != null) {
536 sciConsole.setFont(font);
538 /* Have to update the output view contents with new font */
541 Document outputDoc = ((JEditorPane) config.getOutputView()).getDocument();
542 txt = outputDoc.getText(0, outputDoc.getLength());
543 outputDoc.remove(0, outputDoc.getLength());
544 config.getOutputView().append(txt);
545 } catch (BadLocationException e) {
546 System.out.println(Messages.gettext("Could not change the Console Font."));
550 /* Update the prompt */
551 ((JLabel) ((SciPromptView) config.getPromptView()).getPromptUI()).setFont(font);
552 config.getPromptView().updatePrompt();
558 * Get the font of the Console
561 public Font getFont() {
562 if (sciConsole != null) {
563 return ((JLabel) ((SciPromptView) config.getPromptView()).getPromptUI()).getFont();
570 * Get the Foreground Color of the Console
571 * @return the Foreground Color
573 public Color getForeground() {
574 if (sciConsole != null) {
575 return sciConsole.getForeground();
582 * Get the Background Color of the Console
583 * @return the Background Color
585 public Color getBackground() {
586 if (sciConsole != null) {
587 return sciConsole.getBackground();
594 * Set the Foreground Color of the Console
595 * @param color the Foreground Color
597 public void setForeground(Color color) {
598 if (sciConsole != null) {
599 sciConsole.setForeground(color);
601 /* Have to update the output view contents with new Foreground */
604 Document outputDoc = ((JEditorPane) config.getOutputView()).getDocument();
605 txt = outputDoc.getText(0, outputDoc.getLength());
606 outputDoc.remove(0, outputDoc.getLength());
607 config.getOutputView().append(txt);
608 } catch (BadLocationException e) {
609 System.out.println(Messages.gettext("Could not change the Console Foreground."));
613 /* Update the prompt */
614 ((JLabel) ((SciPromptView) config.getPromptView()).getPromptUI()).setForeground(color);
615 config.getPromptView().updatePrompt();
620 * Set the Background Color of the Console
621 * @param color the Background Color
623 public void setBackground(Color color) {
624 if (sciConsole != null) {
625 sciConsole.setBackground(color);