0255e81d047f0ca73648254fa8afcd49841d6157
[scilab.git] / scilab / modules / console / src / java / org / scilab / modules / console / SciConsole.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2007-2008 - INRIA - Vincent COUVERT
4  *
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
10  *
11  */
12
13 package org.scilab.modules.console;
14
15 import java.awt.BorderLayout;
16 import java.awt.Color;
17 import java.awt.Cursor;
18 import java.awt.Dimension;
19 import java.awt.Font;
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;
25
26 import javax.swing.JLabel;
27 import javax.swing.JPanel;
28 import javax.swing.JScrollPane;
29 import javax.swing.JTextPane;
30 import javax.swing.SwingUtilities;
31 import javax.swing.text.BadLocationException;
32 import javax.swing.text.StyledDocument;
33 import javax.swing.KeyStroke;
34 import javax.xml.parsers.ParserConfigurationException;
35 import org.scilab.modules.localization.Messages;
36
37 import org.xml.sax.SAXException;
38
39 import com.artenum.rosetta.interfaces.core.ConsoleConfiguration;
40 import com.artenum.rosetta.interfaces.core.InputParsingManager;
41 import com.artenum.rosetta.interfaces.ui.InputCommandView;
42 import com.artenum.rosetta.interfaces.ui.OutputView;
43 import com.artenum.rosetta.interfaces.ui.PromptView;
44 import com.artenum.rosetta.ui.Console;
45 import com.artenum.rosetta.util.ConfigurationBuilder;
46 import com.artenum.rosetta.util.ConsoleBuilder;
47 import com.artenum.rosetta.util.StringConstants;
48
49 /**
50  * Main class for Scilab Console based on Generic Console from Artenum
51  * @author Vincent COUVERT
52  */
53 public abstract class SciConsole extends JPanel {
54
55         private static final long serialVersionUID = 1L;
56
57         private static final int LINE_NUMBER_IN_PROMPT = 2;
58
59         private static final String BACKSLASH_R = "\r";
60
61         /**
62          * Maximum length of a command send to Scilab
63          */
64         private static final int MAX_CMD_LENGTH = 512;
65
66         /**
67          * Configuration associated to the console oject
68          */
69         private ConsoleConfiguration config;
70
71         /**
72          * Scroll Pane used in Scilab Console
73          */
74         private JScrollPane jSP;
75
76         /**
77          * Generic console object
78          */
79         private Console sciConsole;
80
81         /**
82          * Flag indicating if the input command vieaw size has been forced to a value by a call to toHome
83          */
84         private boolean inputCommandViewSizeForced;
85
86         /**
87          * Value used to get one char from user input (when using [more y or n ?])
88          */
89         private int userInputValue;
90
91         /**
92          * Protection for userInputValue variable R/W
93          */
94         private Semaphore canReadUserInputValue = new Semaphore(1);
95
96         /**
97          * Boolean flag used to store the state of Scilab (true is all works done)
98          */
99         private boolean workDone;
100
101         /**
102          * Constructor
103          * @param configFilePath the configuration file to use
104          */
105         public SciConsole(String configFilePath) {
106                 super(new BorderLayout());
107
108                 try {
109                         config = ConfigurationBuilder.buildConfiguration(configFilePath);
110                         config.setActiveProfile("scilab");
111                         if (System.getProperty("os.name").toLowerCase().indexOf("mac") != -1)
112                                 {
113                                         ConsoleConfiguration configMac = ConfigurationBuilder.buildConfiguration(configFilePath);;
114                                         configMac.setActiveProfile("macosx");
115                                         for (KeyStroke key : config.getKeyMapping().keys()){
116                                                 config.getKeyMapping().put(key,"");
117                                         }
118                                         for (KeyStroke key : configMac.getKeyMapping().keys()){
119                                                 config.getKeyMapping().put(key, configMac.getKeyMapping().get(key));
120                                         }
121                                 }
122                 } catch (IllegalArgumentException e) {
123                         // TODO Auto-generated catch block
124                         e.printStackTrace();
125                 } catch (SAXException e) {
126                         // TODO Auto-generated catch block
127                         e.printStackTrace();
128                 } catch (IOException e) {
129                         // TODO Auto-generated catch block
130                         e.printStackTrace();
131                 } catch (ParserConfigurationException e) {
132                         // TODO Auto-generated catch block
133                         e.printStackTrace();
134                 }
135
136                 sciConsole = ConsoleBuilder.buildConsole(config, this);
137                 jSP = new JScrollPane(sciConsole);
138
139                 this.add(jSP, BorderLayout.CENTER);
140
141                 // The console is given to the outputView so that updateScrollPosition is is accessible
142                 ((SciOutputView) config.getOutputView()).setConsole(this);
143
144                 // The console is given to the outputView so that Drag&Drop can work
145                 ((SciInputCommandView) config.getInputCommandView()).setConsole(this);
146
147                 // The console is given to the CompletionWindow
148                 ((SciCompletionWindow) config.getCompletionWindow()).setConsole(this);
149                 ((SciCompletionWindow) config.getCompletionWindow()).setGraphicalContext(this);
150
151                 // The promptview is given to the Parsing Manager
152                 // Used to get the position of the CompletionWindow
153                 ((SciInputParsingManager) config.getInputParsingManager()).setPromptView(this.getConfiguration().getPromptView());
154
155                 // Reset history settings - bug 3612
156                 ((SciHistoryManager)config.getHistoryManager()).setInHistory(false);
157
158                 // Bug 8055 : update the lines/columns only when the console is resized
159                 addComponentListener(new ComponentAdapter() {
160                                 public void componentResized(ComponentEvent evt) {
161                                         scilabLinesUpdate();
162                                         jSP.getVerticalScrollBar().setBlockIncrement(jSP.getViewport().getExtentSize().height);
163                                         jSP.getHorizontalScrollBar().setBlockIncrement(jSP.getViewport().getExtentSize().width);
164                                 }
165                         });
166         }
167
168         /**
169          * Gets the configuration associated to the console
170          * @return the configuration
171          */
172         public ConsoleConfiguration getConfiguration() {
173                 return config;
174         }
175
176         /**
177          * Sets the configuration associated to the console
178          * @param newConfig the new config to set
179          */
180         public void setConfiguration(ConsoleConfiguration newConfig) {
181                 config = newConfig;
182         }
183
184         /**
185          * Updates Scilab internal variables containing the size of the console
186          * These variables are used to format data before displaying it
187          */
188         public void scilabLinesUpdate() {
189                 // Size of the console
190                 int outputViewWidth = jSP.getWidth();
191
192                 // Size of a char
193                 OutputView outputView = this.getConfiguration().getOutputView();
194                 int[] charsWidth = ((JTextPane) outputView).getFontMetrics(((JTextPane) outputView).getFont()).getWidths();
195
196                 // This loop is not needed for monospaced fonts !
197                 int maxCharWidth = charsWidth[33];
198                 // The range 33--126 corresponds to the usual characters in ASCII
199                 for (int i = 34; i < 126; i++) {
200                         if (charsWidth[i] > maxCharWidth) {
201                                 maxCharWidth = charsWidth[i];
202                         }
203                 }
204
205                 int numberOfLines = getNumberOfLines();
206                 int promptWidth = ((JPanel) this.getConfiguration().getPromptView()).getPreferredSize().width;
207
208                 int numberOfColumns = (outputViewWidth - promptWidth) / maxCharWidth - 1;
209                 /* -1 because of the margin between text prompt and command line text */
210
211                 GuiManagement.setScilabLines(numberOfLines, numberOfColumns);
212         }
213
214         /**
215          * Get the number of lines that can be displayed in the visible part of the console
216          * @return the number of lines
217          */
218         public int getNumberOfLines() {
219                 // Size of the console
220                 int outputViewHeight = jSP.getHeight();
221
222                 // Size of a char
223                 OutputView outputView = this.getConfiguration().getOutputView();
224                 int charHeight = ((JTextPane) outputView).getFontMetrics(((JTextPane) outputView).getFont()).getHeight();
225                 int[] charsWidth = ((JTextPane) outputView).getFontMetrics(((JTextPane) outputView).getFont()).getWidths();
226
227                 // This loop is not needed for monospaced fonts !
228                 int maxCharWidth = charsWidth[0];
229                 for (int i = 1; i < charsWidth.length; i++) {
230                         if (charsWidth[i] > maxCharWidth) {
231                                 maxCharWidth = charsWidth[i];
232                         }
233                 }
234
235                 return outputViewHeight / charHeight - 1; /* -1 because of the size of the InputCommandLine */
236         }
237
238         /**
239          * Updates the scroll bars according to the contents
240          */
241         public void updateScrollPosition() {
242                 SwingUtilities.invokeLater(new Runnable() {
243                                 public void run() {
244                                         jSP.getViewport().setViewPosition(new Point(0,
245                                                                                                                                 sciConsole.getPreferredSize().height - jSP.getViewport().getExtentSize().height));
246                                         //jSP.revalidate();
247                                 }
248                         });
249                 //jSP.getVerticalScrollBar().setValue(jSP.getVerticalScrollBar().getMaximum());
250                 //jSP.invalidate();
251                 //jSP.getViewport().setViewPosition(new Point(0, sciConsole.getPreferredSize().height - jSP.getViewport().getExtentSize().height));
252                 //jSP.revalidate();
253
254                 // Update the scrollbar properties
255                 jSP.getVerticalScrollBar().setBlockIncrement(jSP.getViewport().getExtentSize().height);
256                 jSP.getHorizontalScrollBar().setBlockIncrement(jSP.getViewport().getExtentSize().width);
257         }
258
259         /**
260          * Clears the console and the output view
261          */
262         public void clear() {
263                 try {
264                         config.getInputCommandViewStyledDocument().remove(0, config.getInputCommandViewStyledDocument().getLength());
265                 } catch (BadLocationException e) {
266                         e.printStackTrace();
267                 }
268                 config.getOutputView().reset();
269                 /* Bug 4014 */
270                 /* We add a space to add a line */
271                 /* clc , F2 and menus have same position */
272                 config.getOutputView().append(" ");
273         }
274
275         /**
276          * Clears lines from the end of the output view
277          * If nbLines == -1 ==> Called from SwingScilabConsole.getCharWithoutOutput() ([more y or n ?])
278          * If nbLines == 0 ==> Clear the InputCommandLine
279          * @param nbLines the number of lines to be deleted
280          */
281         public void clear(int nbLines) {
282
283                 if (nbLines == 0) {
284                         // Clear the prompt
285                         config.getInputCommandView().reset();
286                 } else {
287                         // Clear lines in output command view
288                         try {
289                                 // We have to remove the command entered by the user
290                                 int totalNumberOfLines = nbLines + LINE_NUMBER_IN_PROMPT;
291
292                                 StyledDocument outputStyle = config.getOutputViewStyledDocument();
293                                 String outputTxt =  outputStyle.getText(0, outputStyle.getLength());
294
295                                 // Are there enough lines in the output view ?
296                                 String[] allLines = outputTxt.split(StringConstants.NEW_LINE);
297                                 if (allLines.length < totalNumberOfLines) {
298                                         // Delete lines
299                                         config.getOutputView().reset();
300                                         config.getOutputView().append(Messages.gettext("Out of Screen"));
301                                 } else {
302                                         // Delete lines
303                                         int lastEOL;
304                                         for (int i = 0; i < totalNumberOfLines; i++) {
305                                                 outputTxt = outputStyle.getText(0, outputStyle.getLength());
306                                                 lastEOL = outputTxt.lastIndexOf(StringConstants.NEW_LINE);
307                                                 outputStyle.remove(lastEOL, outputStyle.getLength() - lastEOL);
308                                         }
309                                 }
310                         } catch (BadLocationException e) {
311                                 // TODO Auto-generated catch block
312                                 e.printStackTrace();
313                         }
314                 }
315         }
316
317         /**
318          * Puts the prompt in the top left corner of the console
319          */
320         public void toHome() {
321                 Dimension jSPExtSize = jSP.getViewport().getExtentSize();
322                 Dimension newDim = new Dimension(jSPExtSize.width - jSP.getVerticalScrollBar().getPreferredSize().width, jSPExtSize.height);
323                 ((JTextPane) config.getInputCommandView()).setPreferredSize(newDim);
324                 ((JTextPane) config.getInputCommandView()).invalidate();
325                 ((JTextPane) config.getInputCommandView()).doLayout();
326                 inputCommandViewSizeForced = true;
327         }
328
329         /**
330          * Sets the flags indicating if the input command view has been resize by calling toHome()
331          * @param status the new status
332          */
333         public void setInputCommandViewSizeForced(boolean status) {
334                 inputCommandViewSizeForced = status;
335         }
336
337         /**
338          * Gets the flags indicating if the input command view has been resize by calling toHome()
339          * @return true if a toHome() call is still affecting the size of the input command view
340          */
341         public boolean getInputCommandViewSizeForced() {
342                 return inputCommandViewSizeForced;
343         }
344
345         /**
346          * Gets the user input value
347          * @return the value entered by the used
348          */
349         public int getUserInputValue() {
350                 try {
351                         canReadUserInputValue.acquire();
352                 } catch (InterruptedException e) {
353                         e.printStackTrace();
354                 }
355                 return userInputValue;
356         }
357
358         /**
359          * Sets the value entered by the user
360          * @param userInputValue new value
361          */
362         public void setUserInputValue(int userInputValue) {
363                 this.userInputValue = userInputValue;
364                 canReadUserInputValue.release();
365         }
366
367         /**
368          * Gets the semaphore protection so that it can be acquired
369          * @return the semaphore
370          */
371         public Semaphore getCanReadUserInputValue() {
372                 return canReadUserInputValue;
373         }
374
375         /**
376          * Send commands to be executed by Scilab (after a copy/paste or drag&drop...)
377          * @param textToExec all text lines to executed
378          * @param displayCmdInOutput flag indicating if the input command has to be displayed in the output view
379          * @param storeInHistory flag indicating if the input command has to be stored in the history
380          */
381         public void sendCommandsToScilab(String textToExec, boolean displayCmdInOutput, boolean storeInHistory) {
382                 String[] linesToExec = textToExec.split(StringConstants.NEW_LINE);
383                 int nbStatements = 0;
384
385                 // Display Cursor to show Scilab is busy
386                 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
387
388                 while (nbStatements < linesToExec.length) {
389                         // This loop contains code very similar to the code of ValidationAction.java
390                         InputParsingManager inputParsingManager = config.getInputParsingManager();
391                         PromptView promptView = config.getPromptView();
392
393                         // Reset command line
394                         inputParsingManager.reset();
395                         promptView.updatePrompt();
396
397                         // Reset history settings
398                         config.getHistoryManager().setInHistory(false);
399
400                         // Hide the prompt and command line
401                         config.getInputCommandView().setEditable(false);
402                         config.getPromptView().setVisible(false);
403
404                         // Remove the prompt if present at the beginning of the text to execute
405                         // Bug 3002 fix: this "functionality" has been removed because:
406                         // - Remove the --> even if not from paste action
407                         // - Does not remove pause prompts
408
409                         // Store the command in the buffer so that Scilab can read it
410                         if (linesToExec[nbStatements].length() > MAX_CMD_LENGTH) {
411                                 config.getOutputView().append("Command is too long (more than " + MAX_CMD_LENGTH
412                                                                                           + " characters long): could not send it to Scilab\n");
413                                 ((SciInputCommandView) config.getInputCommandView()).setCmdBuffer("", false);
414                                 return;
415                         }
416
417                         ((SciInputCommandView) config.getInputCommandView())
418                                 .setCmdBuffer(linesToExec[nbStatements].replace(BACKSLASH_R, ""), displayCmdInOutput);
419                         if (storeInHistory) {
420                                 ((SciHistoryManager) config.getHistoryManager()).addEntry(linesToExec[nbStatements].replace(BACKSLASH_R, ""));
421                         }
422                         nbStatements++;
423                 }
424
425         }
426
427         /**
428          * Get the JScrollPane associated to the console
429          * @return the JScrollPane associated to the console
430          */
431         public JScrollPane getJScrollPane() {
432                 return jSP;
433         }
434
435         /**
436          * Get the Console object associated to the console
437          * @return the Console object associated to the console
438          */
439         public Console getSciConsole() {
440                 return sciConsole;
441         }
442
443         /**
444          * Get the current status of the console
445          * If the prompt view is visible, Scilab is waiting for commands
446          * @return true is Scilab is waiting for commands
447          */
448         public boolean isWaitingForInput() {
449                 return ((JTextPane) config.getInputCommandView()).isEditable();
450         }
451
452         /**
453          * This methods is used by Scilab to get a new command to execute
454          * @return the command to execute
455          */
456         public String readLine() {
457
458                 InputCommandView inputCmdView = this.getConfiguration().getInputCommandView();
459
460                 getConfiguration().getOutputView().setCaretPositionToEnd();
461
462                 displayPrompt();
463
464                 // Display Cursor to show Scilab is available.
465                 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
466
467                 // Reads the buffer
468                 return ((SciInputCommandView) inputCmdView).getCmdBuffer();
469         }
470
471         /**
472          * This method is used to display the prompt
473          */
474         public abstract void displayPrompt();
475
476         /**
477          * Does Scilab have finished its work ?
478          * @return true if Scilab is waiting for new commands
479          */
480         public boolean isWorkDone() {
481                 return workDone;
482         }
483
484         /**
485          * Set the font of the Console
486          * @param font the font to set
487          */
488         public void setFont(Font font) {
489                 if (sciConsole != null) {
490                         sciConsole.setFont(font);
491
492                         /* Have to update the output view contents with new font */
493                         String txt;
494                         try {
495                                 txt = config.getOutputViewStyledDocument().getText(0, config.getOutputViewStyledDocument().getLength());
496                                 config.getOutputViewStyledDocument().remove(0, config.getOutputViewStyledDocument().getLength());
497                                 config.getOutputView().append(txt);
498                         } catch (BadLocationException e) {
499                                 System.out.println(Messages.gettext("Could not change the Console Font."));
500                                 return;
501                         }
502
503                         /* Update the prompt */
504                         ((JLabel) ((SciPromptView) config.getPromptView()).getPromptUI()).setFont(font);
505                         config.getPromptView().updatePrompt();
506                         scilabLinesUpdate();
507                 }
508         }
509
510         /**
511          * Get the font of the Console
512          * @return the font
513          */
514         public Font getFont() {
515                 if (sciConsole != null) {
516                         return ((JLabel) ((SciPromptView) config.getPromptView()).getPromptUI()).getFont();
517                 } else {
518                         return null;
519                 }
520         }
521
522         /**
523          * Get the Foreground Color of the Console
524          * @return the Foreground Color
525          */
526         public Color getForeground() {
527                 if (sciConsole != null) {
528                         return sciConsole.getForeground();
529                 } else {
530                         return null;
531                 }
532         }
533
534         /**
535          * Get the Background Color of the Console
536          * @return the Background Color
537          */
538         public Color getBackground() {
539                 if (sciConsole != null) {
540                         return sciConsole.getBackground();
541                 } else {
542                         return null;
543                 }
544         }
545
546         /**
547          * Set the Foreground Color of the Console
548          * @param color the Foreground Color
549          */
550         public void setForeground(Color color) {
551                 if (sciConsole != null) {
552                         sciConsole.setForeground(color);
553
554                         /* Have to update the output view contents with new Foreground */
555                         String txt;
556                         try {
557                                 txt = config.getOutputViewStyledDocument().getText(0, config.getOutputViewStyledDocument().getLength());
558                                 config.getOutputViewStyledDocument().remove(0, config.getOutputViewStyledDocument().getLength());
559                                 config.getOutputView().append(txt);
560                         } catch (BadLocationException e) {
561                                 System.out.println(Messages.gettext("Could not change the Console Foreground."));
562                                 return;
563                         }
564
565                         /* Update the prompt */
566                         ((JLabel) ((SciPromptView) config.getPromptView()).getPromptUI()).setForeground(color);
567                         config.getPromptView().updatePrompt();
568                 }
569         }
570
571         /**
572          * Set the Background Color of the Console
573          * @param color the Background Color
574          */
575         public void setBackground(Color color) {
576                 if (sciConsole != null) {
577                         sciConsole.setBackground(color);
578                 }
579         }
580
581 }