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