Bug 4840 fixed: The more there was text in the console, the slower was the display
[scilab.git] / scilab / modules / gui / src / java / org / scilab / modules / gui / bridge / console / SwingScilabConsole.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.gui.bridge.console;
14
15 import java.awt.Dimension;
16 import java.awt.Toolkit;
17 import java.awt.datatransfer.Clipboard;
18 import java.awt.datatransfer.DataFlavor;
19 import java.awt.datatransfer.StringSelection;
20 import java.awt.datatransfer.UnsupportedFlavorException;
21 import java.awt.event.ActionEvent;
22 import java.awt.event.ActionListener;
23 import java.awt.event.ComponentEvent;
24 import java.awt.event.ComponentListener;
25 import java.awt.event.ContainerEvent;
26 import java.awt.event.ContainerListener;
27 import java.awt.event.FocusEvent;
28 import java.awt.event.FocusListener;
29 import java.beans.PropertyChangeEvent;
30 import java.beans.PropertyChangeListener;
31 import java.io.IOException;
32
33 import javax.swing.JPanel;
34 import javax.swing.JEditorPane;
35 import javax.swing.JTextPane;
36 import javax.swing.SwingUtilities;
37 import javax.swing.event.ChangeEvent;
38 import javax.swing.event.ChangeListener;
39 import javax.swing.text.BadLocationException;
40 import javax.swing.text.StyleContext;
41 import javax.swing.text.StyledDocument;
42
43 import org.scilab.modules.action_binding.InterpreterManagement;
44 import org.scilab.modules.console.OneCharKeyEventListener;
45 import org.scilab.modules.console.SciConsole;
46 import org.scilab.modules.console.SciHistoryManager;
47 import org.scilab.modules.console.SciOutputView;
48 import org.scilab.modules.gui.bridge.contextmenu.SwingScilabContextMenu;
49 import org.scilab.modules.gui.bridge.menuitem.SwingScilabMenuItem;
50 import org.scilab.modules.gui.console.ScilabConsole;
51 import org.scilab.modules.gui.console.SimpleConsole;
52 import org.scilab.modules.gui.events.callback.ScilabCallBack;
53 import org.scilab.modules.gui.helpbrowser.ScilabHelpBrowser;
54 import org.scilab.modules.gui.utils.ConfigManager;
55 import org.scilab.modules.gui.utils.Position;
56 import org.scilab.modules.gui.utils.Size;
57 import org.scilab.modules.localization.Messages;
58 import com.artenum.rosetta.interfaces.ui.InputCommandView;
59 import com.artenum.rosetta.util.StringConstants;
60
61 /**
62  * Swing implementation for Scilab Console in GUIs
63  * @author Vincent COUVERT
64  */
65 public class SwingScilabConsole extends SciConsole implements SimpleConsole {
66
67         private static final long serialVersionUID = 1L;
68
69         /**
70          * Constructor
71          */
72         public SwingScilabConsole() {
73                 super(ConfigManager.getUserConfigFile());
74
75                 SwingScilabContextMenu menu = new SwingScilabContextMenu();
76
77                 SwingScilabMenuItem cutMenu = new SwingScilabMenuItem();
78                 cutMenu.setText(Messages.gettext("Cut"));
79                 cutMenu.setCallback(ScilabCallBack.createCallback(
80                                                                 "org.scilab.modules.gui.bridge.CallScilabBridge.cutConsoleSelection",
81                                                                 ScilabCallBack.JAVA));
82                 cutMenu.setMnemonic('U');
83
84                 SwingScilabMenuItem copyMenu = new SwingScilabMenuItem();
85                 copyMenu.setText(Messages.gettext("Copy"));
86                 copyMenu.setCallback(ScilabCallBack.createCallback(
87                                                                  "org.scilab.modules.gui.bridge.CallScilabBridge.copyConsoleSelection",
88                                                                  ScilabCallBack.JAVA));
89                 copyMenu.setMnemonic('C');
90
91                 SwingScilabMenuItem pasteMenu = new SwingScilabMenuItem();
92                 pasteMenu.setText(Messages.gettext("Paste"));
93                 pasteMenu.setCallback(ScilabCallBack.createCallback(
94                                                                   "org.scilab.modules.gui.bridge.CallScilabBridge.pasteClipboardIntoConsole",
95                                                                   ScilabCallBack.JAVA));
96                 pasteMenu.setMnemonic('P');
97
98                 SwingScilabMenuItem clearHistoryMenu = new SwingScilabMenuItem();
99                 clearHistoryMenu.setText(Messages.gettext("Clear History"));
100                 clearHistoryMenu.setCallback(ScilabCallBack.createCallback(
101                                                                                  "org.scilab.modules.gui.bridge.CallScilabBridge.clearHistory",
102                                                                                  ScilabCallBack.JAVA));
103                 clearHistoryMenu.setMnemonic('H');
104
105                 SwingScilabMenuItem clearMenu = new SwingScilabMenuItem();
106                 clearMenu.setText(Messages.gettext("Clear Console"));
107                 clearMenu.setCallback(ScilabCallBack.createCallback(
108                                                                   "org.scilab.modules.gui.bridge.CallScilabBridge.clear",
109                                                                   ScilabCallBack.JAVA));
110                 clearMenu.setMnemonic('O');
111
112                 SwingScilabMenuItem selectMenu = new SwingScilabMenuItem();
113                 selectMenu.setText(Messages.gettext("Select All"));
114                 selectMenu.setCallback(ScilabCallBack.createCallback(
115                                                                    "org.scilab.modules.gui.bridge.CallScilabBridge.selectAllConsoleContents",
116                                                                    ScilabCallBack.JAVA));
117                 selectMenu.setMnemonic('S');
118
119
120                 final SwingScilabMenuItem helpMenu = new SwingScilabMenuItem();
121                 helpMenu.setText(Messages.gettext("Help on a selected keyword"));
122                 helpMenu.setCallback(ScilabCallBack.createCallback(
123                                                                    "org.scilab.modules.gui.bridge.CallScilabBridge.helpOnTheKeyword",
124                                                                    ScilabCallBack.JAVA));
125                 helpMenu.setMnemonic('M');
126                 PropertyChangeListener listener = new PropertyChangeListener() {
127                         public void propertyChange(PropertyChangeEvent arg0) {
128                                 String keyword = getSelectedText();
129                                 if (keyword == null || keyword.length() == 0) {
130                                         helpMenu.setText(Messages.gettext("Help about a selected text"));
131                                 } else {
132                                         int nbOfDisplayedOnlyXChar=10;
133                                         if (keyword.length() > nbOfDisplayedOnlyXChar) {
134                                                 keyword = keyword.substring(0, nbOfDisplayedOnlyXChar) + "...";
135                                         }
136                                         helpMenu.setText(Messages.gettext("Help about '") +keyword+"'");
137                                 }
138                         }
139                 };
140                 helpMenu.addPropertyChangeListener(listener);
141
142                 menu.add(cutMenu);
143                 menu.add(copyMenu);
144                 menu.add(pasteMenu);
145
146                 menu.addSeparator();
147
148                 menu.add(clearHistoryMenu);
149                 menu.add(clearMenu);
150
151                 menu.addSeparator();
152
153                 menu.add(selectMenu);
154                 menu.addSeparator();
155
156                 menu.add(helpMenu);
157
158                 ((JEditorPane) getConfiguration().getOutputView()).setComponentPopupMenu(menu);
159                 ((JTextPane) getConfiguration().getInputCommandView()).setComponentPopupMenu(menu);
160                 ((JPanel) getConfiguration().getPromptView()).setComponentPopupMenu(menu);
161
162                 ((JTextPane) getConfiguration().getInputCommandView()).requestFocus();
163
164                 addFocusListener(new FocusListener() {
165                         public void focusGained(FocusEvent e) {
166                             ((JTextPane) getConfiguration().getInputCommandView()).requestFocus();
167                         }
168
169                         public void focusLost(FocusEvent e) { }
170                     });
171         }
172
173         /**
174          * Displays data in the console
175          * @param dataToDisplay the data to be displayed
176          * @see fr.scilab.console.HelpBrowser#display(java.lang.String)
177          */
178         public void display(String dataToDisplay) {
179                 this.getConfiguration().getOutputView().append(dataToDisplay);
180         }
181
182         /**
183          * This method is used to display the prompt
184          */
185         public void displayPrompt() {
186
187                 final InputCommandView inputCmdView = this.getConfiguration().getInputCommandView();
188                 // Show the prompt
189                 this.getConfiguration().getPromptView().setVisible(true);
190
191                 // Show the input command view and its hidden components
192                 inputCmdView.setEditable(true);
193
194                 ((JTextPane) inputCmdView).setCaretColor(((JTextPane) inputCmdView).getForeground());
195
196                 SwingUtilities.invokeLater(new Runnable() {
197                 public void run() {
198                         ((JTextPane) inputCmdView).getCaret().setVisible(true);
199                         }
200                 });
201
202                 // Remove last line returned given by Scilab (carriage return)
203                 try {
204                         StyledDocument outputStyledDoc = this.getConfiguration().getOutputViewStyledDocument();
205                         int lastEOL = outputStyledDoc.getText(0, outputStyledDoc.getLength()).lastIndexOf(StringConstants.NEW_LINE);
206
207                         // Condition added to avoid a "javax.swing.text.BadLocationException: Invalid remove" exception
208                         if (lastEOL > 1 && (outputStyledDoc.getLength() - lastEOL) == 1) {
209                                 outputStyledDoc.remove(lastEOL, outputStyledDoc.getLength() - lastEOL);
210                         }
211                 } catch (BadLocationException e) {
212                         e.printStackTrace();
213                 }
214
215                 updateScrollPosition();
216         }
217
218         /**
219          * Reads one user input char
220          * @return the data entered by the user
221          * @see fr.scilab.console.HelpBrowser#getCharWithoutOutput()
222          */
223         public int getCharWithoutOutput() {
224                 int retChar;
225
226                 updateScrollPosition();
227
228                 // Avoids reading of an empty buffer
229                 try {
230                         ((SciConsole) this).getCanReadUserInputValue().acquire();
231                 } catch (InterruptedException e) {
232                         // TODO Auto-generated catch block
233                         e.printStackTrace();
234                 }
235
236                 this.getConfiguration().getPromptView().setVisible(false);
237                 this.getConfiguration().getInputCommandView().setEditable(false);
238
239                 // Add a keylistener which will set the returned char
240                 OneCharKeyEventListener keyListener = new OneCharKeyEventListener(this);
241                 ((JTextPane) this.getConfiguration().getInputCommandView()).addKeyListener(keyListener);
242                 ((JEditorPane) this.getConfiguration().getOutputView()).addKeyListener(keyListener);
243
244                 // Reads the buffer
245                 retChar = this.getUserInputValue();
246                 ((SciConsole) this).getCanReadUserInputValue().release();
247
248                 // Remove the "more" message and replace it by an empty line
249                 this.clear(-1);
250                 this.display(StringConstants.NEW_LINE);
251
252                 // Remove the key listener
253                 ((JTextPane) this.getConfiguration().getInputCommandView()).removeKeyListener(keyListener);
254                 ((JEditorPane) this.getConfiguration().getOutputView()).removeKeyListener(keyListener);
255
256                 this.getConfiguration().getPromptView().setVisible(true);
257                 this.getConfiguration().getInputCommandView().setEditable(true);
258
259                 // Send back the focus the the input view
260                 this.getConfiguration().getInputCommandView().reset();
261                 this.getConfiguration().getInputCommandView().requestFocus();
262
263                 final JTextPane cmdView = (JTextPane) this.getConfiguration().getInputCommandView();
264                 SwingUtilities.invokeLater(new Runnable() {
265                     public void run() {
266                         cmdView.getCaret().setVisible(true);
267                     }
268                 });
269
270                 return retChar;
271         }
272
273         /**
274          * Draw a console
275          */
276         public void draw() {
277                 super.setVisible(true);
278                 super.doLayout();
279         }
280
281         /**
282          * Gets the dimensions (width and height) of a Scilab console
283          * @return the size of the console
284          */
285         public Size getDims() {
286                 return new Size(super.getWidth(), super.getHeight());
287         }
288
289         /**
290          * Gets the position (X-coordinate and Y-coordinates) of a Scilab console
291          * @return the position of the console
292          */
293         public Position getPosition() {
294                 return new Position(this.getX(), this.getY());
295         }
296
297         /**
298          * Gets the visibility status of a console
299          * @return the visibility status of the console (true if the console is visible, false if not)
300          */
301         public boolean isVisible() {
302                 return super.isVisible();
303         }
304
305         /**
306          * Sets the dimensions (width and height) of a Scilab console
307          * @param newSize the size we want to set to the console
308          */
309         public void setDims(Size newSize) {
310                 this.setPreferredSize(new Dimension(newSize.getWidth(), newSize.getHeight()));
311         }
312
313         /**
314          * Sets the position (X-coordinate and Y-coordinate) of a Scilab console
315          * @param newPosition the position we want to set to the console
316          */
317         public void setPosition(Position newPosition) {
318                 this.setLocation(newPosition.getX(), newPosition.getY());
319         }
320
321         /**
322          * Sets the visibility status of a Scilab console
323          * @param newVisibleState the visibility status we want to set to the console
324          */
325         public void setVisible(boolean newVisibleState) {
326                 super.setVisible(newVisibleState);
327         }
328
329         /**
330          * Clears the Console
331          */
332         public void clear() {
333                 super.clear();
334         }
335
336         /**
337           * Sets the prompt displayed in the console
338           * @param prompt the prompt to be displayed in the console
339           */
340         public void setPrompt(String prompt) {
341                 this.getConfiguration().getPromptView().setDefaultPrompt(prompt);
342         }
343
344         /**
345          * Clear the commands history
346          */
347         public void clearHistory() {
348                 ((SciHistoryManager) this.getConfiguration().getHistoryManager()).reset();
349         }
350
351         /**
352          * Paste clipboard contents in Console input line
353          */
354         public void pasteClipboard() {
355                 // Gets the contents of the clipboard
356                 Toolkit toolkit = Toolkit.getDefaultToolkit();
357                 Clipboard systemClipboard = toolkit.getSystemClipboard();
358
359                 // Verify that clibpboard data is of text type
360                 boolean dataAvailable;
361                 try {
362                         dataAvailable = systemClipboard.isDataFlavorAvailable(DataFlavor.stringFlavor);
363                 } catch (IllegalStateException exception) {
364                         return;
365                 }
366
367                 // Exit if text data not available
368                 if (!dataAvailable) {
369                         return;
370                 }
371
372                 // Read data
373                 String clipboardContents = null;
374                 try {
375                         clipboardContents = (String) systemClipboard.getData(DataFlavor.stringFlavor);
376                 } catch (UnsupportedFlavorException e1) {
377                         // Should never be here
378                         e1.printStackTrace();
379                 } catch (IOException e1) {
380                         // Should never be here
381                         e1.printStackTrace();
382                 }
383
384                 JTextPane input = ((JTextPane) this.getConfiguration().getInputCommandView());
385                 StyledDocument doc = input.getStyledDocument();
386
387                 // If some text selected then it is replaced
388                 if (input.getSelectedText() != null) {
389                         try {
390                                 doc.remove(input.getSelectionStart(), input.getSelectionEnd() - input.getSelectionStart());
391                         } catch (BadLocationException e) {
392                                 // TODO Auto-generated catch block
393                                 e.printStackTrace();
394                         }
395                 }
396                 // Insert clipboard contents
397                 try {
398                         doc.insertString(((JTextPane) this.getConfiguration().getInputCommandView()).getCaretPosition(),
399                                         clipboardContents, doc.getStyle(StyleContext.DEFAULT_STYLE));
400                 } catch (BadLocationException e) {
401                         // TODO Auto-generated catch block
402                         e.printStackTrace();
403                 }
404         }
405
406         /**
407          * Select all the console contents
408          */
409         public void selectAll() {
410                 JEditorPane output = (JEditorPane) this.getConfiguration().getOutputView();
411                 output.setSelectionStart(0);
412                 output.setSelectionEnd(output.getText().length());
413                 // TODO should also select the prompt and the input
414         }
415
416         /**
417          * Return the selected text in the console
418          * @return The selected text in the console
419          */
420         private String getSelectedText() {
421                 JEditorPane output = (JEditorPane) this.getConfiguration().getOutputView();
422                 JTextPane input = (JTextPane) this.getConfiguration().getInputCommandView();
423
424                 String selection = "";
425                 if (output.getSelectedText() != null) {
426                         selection += output.getSelectedText();
427                 }
428                 // TODO should also copy the prompt
429                 if (input.getSelectedText() != null) {
430                         selection += input.getSelectedText();
431                 }
432                 return selection;
433
434         }
435
436         /**
437          * Launch the help on the selected text into the Console
438          */
439         public void helpOnTheKeyword() {
440                 String keyword = getSelectedText();
441                 /* Double the quote/double quote in order to avoid
442                  * and error with the call of help()
443                  */
444                 keyword=keyword.replaceAll("'", "''");
445                 keyword=keyword.replaceAll("\"", "\"\"");
446
447                 /* @TODO: Check if it is possible to call directly
448                  * from the Java engine the help
449                  * Last time I check, we needed some information
450                  * provided by the Scilab native engine
451                  */
452                 InterpreterManagement.requestScilabExec("help('"+keyword+"')");
453         }
454
455         /**
456          * Put the console selected text in the clipboard
457          */
458         public void copyToClipboard() {
459                 String selection = getSelectedText();
460                 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(selection), null);
461
462         }
463
464         /**
465          * Cut selected text in the Console input line
466          */
467         public void cutSelection() {
468                 JTextPane input = (JTextPane) this.getConfiguration().getInputCommandView();
469                 StyledDocument doc = input.getStyledDocument();
470
471                 // If some text selected then it is replaced
472                 if (input.getSelectedText() != null) {
473                         try {
474                                 /* Put the selection in the clipboard */
475                                 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(input.getSelectedText()), null);
476                                 /* Remove selected text */
477                                 doc.remove(input.getSelectionStart(), input.getSelectionEnd() - input.getSelectionStart());
478                         } catch (BadLocationException e) {
479                                 // TODO Auto-generated catch block
480                                 e.printStackTrace();
481                         }
482                 }
483         }
484
485         /**
486          * Set the maximum number of lines stored in the Output
487          * @param nbLines the number of lines
488          */
489         public void setMaxOutputSize(int nbLines) {
490                 ((SciOutputView) this.getConfiguration().getOutputView()).setMaxSize(nbLines);
491         }
492
493 }