Remove autogenerated comments
[scilab.git] / scilab / modules / console / src / java / org / scilab / modules / console / SciOutputView.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.Container;
16 import java.awt.Dimension;
17 import java.awt.Toolkit;
18 import java.awt.datatransfer.Clipboard;
19 import java.awt.datatransfer.DataFlavor;
20 import java.awt.datatransfer.Transferable;
21 import java.awt.datatransfer.UnsupportedFlavorException;
22 import java.awt.dnd.DnDConstants;
23 import java.awt.dnd.DropTarget;
24 import java.awt.event.FocusAdapter;
25 import java.awt.event.FocusEvent;
26 import java.awt.event.MouseEvent;
27 import java.io.IOException;
28 import java.io.Writer;
29 import java.util.LinkedList;
30 import java.util.concurrent.ArrayBlockingQueue;
31 import java.util.concurrent.BlockingQueue;
32
33 import javax.swing.BorderFactory;
34 import javax.swing.JEditorPane;
35 import javax.swing.JPanel;
36 import javax.swing.JScrollPane;
37 import javax.swing.JTextPane;
38 import javax.swing.SwingUtilities;
39 import javax.swing.text.BadLocationException;
40 import javax.swing.text.DefaultEditorKit;
41 import javax.swing.text.Element;
42 import javax.swing.text.JTextComponent;
43 import javax.swing.text.PlainDocument;
44 import javax.swing.text.StyleContext;
45 import javax.swing.text.StyledDocument;
46 import javax.swing.text.PlainView;
47 import javax.swing.text.View;
48 import javax.swing.text.ViewFactory;
49
50 import org.scilab.modules.commons.gui.ScilabCaret;
51
52 import com.artenum.rosetta.interfaces.ui.OutputView;
53 import com.artenum.rosetta.util.BufferedWriter;
54 import com.artenum.rosetta.util.StringConstants;
55
56 /**
57  * Scilab Console UI which contains the previous commands and their outputs
58  *
59  * @author Vincent COUVERT
60  */
61 public class SciOutputView extends JEditorPane implements OutputView, ViewFactory {
62     private static final long serialVersionUID = 1L;
63
64     private static final int TOP_BORDER = 0;
65
66     private static final int BOTTOM_BORDER = 0;
67
68     private static final int LEFT_BORDER = 0;
69
70     private static final int RIGHT_BORDER = 0;
71
72     private static final int BUFFER_SIZE = 10;
73
74     private String activeStyle;
75
76     private String lastAppendedStyle;
77
78     private BlockingQueue<StringBuffer> bufferQueue;
79
80     private LinkedList<String> styleQueue;
81
82     private StringBuffer currentWorkingBuffer;
83
84     private SciConsole console;
85
86     private Thread thread;
87
88     private int insertPosition;
89
90     private int maxNumberOfLines;
91
92     private boolean lastEOL;
93
94     /**
95      * Constructor
96      */
97     public SciOutputView() {
98         super();
99
100         setEditorKit(new DefaultEditorKit() {
101                 public ViewFactory getViewFactory() {
102                     return SciOutputView.this;
103                 }
104             });
105
106         /* A PlainDocument contains only "box" for lines not for all characters (as in a StyledDocument)
107            so there are less boxes to explore in a PlainDocument... */
108         setDocument(new PlainDocument());
109         setMaxSize(10000);
110         setBorder(BorderFactory.createEmptyBorder(TOP_BORDER, LEFT_BORDER, BOTTOM_BORDER, RIGHT_BORDER));
111
112         // Enabled Drag&Drop with this component
113         this.setDragEnabled(true);
114         this.setDoubleBuffered(true);
115
116         activeStyle = StyleContext.DEFAULT_STYLE;
117         bufferQueue = new ArrayBlockingQueue<StringBuffer>(BUFFER_SIZE);
118         styleQueue = new LinkedList<String>();
119
120         /**
121          * Default caret for output view (to handle paste actions using middle button)
122          * @author Vincent COUVERT
123          */
124         final class FixedCaret extends ScilabCaret {
125
126             private static final long serialVersionUID = 8230195712653828841L;
127
128             /**
129              * Constructor
130              */
131             private FixedCaret() {
132                 super(SciOutputView.this);
133             }
134
135             /**
136              * Manages mouse clicks
137              * @param e the event
138              * @see javax.swing.text.DefaultCaret#mouseClicked(java.awt.event.MouseEvent)
139              */
140             public void mouseClicked(MouseEvent e) {
141                 if (SwingUtilities.isMiddleMouseButton(e) && e.getClickCount() == 1) {
142                     /*** PASTE USING MIDDLE BUTTON ***/
143                     JTextComponent c = (JTextComponent) e.getSource();
144                     if (c != null) {
145                         Toolkit tk = c.getToolkit();
146                         Clipboard buffer = tk.getSystemSelection();
147                         if (buffer != null) {
148                             Transferable trans = buffer.getContents(null);
149                             if (trans.isDataFlavorSupported(DataFlavor.stringFlavor)) {
150                                 try {
151                                     String pastedText = (String) trans.getTransferData(DataFlavor.stringFlavor);
152                                     ((JTextPane) getConsole().getConfiguration().getInputCommandView()).replaceSelection(pastedText);
153                                 } catch (UnsupportedFlavorException e1) {
154                                     e1.printStackTrace();
155                                 } catch (IOException e1) {
156                                     e1.printStackTrace();
157                                 }
158                             }
159                         }
160                     }
161                 } else if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 1) {
162                     /*** SEND THE FOCUS TO THE INPUT COMMAND VIEW ***/
163                     ((JTextPane) getConsole().getConfiguration().getInputCommandView()).requestFocus();
164                     ((JTextPane) getConsole().getConfiguration().getInputCommandView()).getCaret().setVisible(true);
165                 } else {
166                     /*** DELEGATE TO THE SYSTEM ***/
167                     super.mouseClicked(e);
168                 }
169             }
170
171             public void mousePressed(MouseEvent e) {
172                 ((SciInputCommandView) console.getConfiguration().getInputCommandView()).removeSelection();
173                 super.mousePressed(e);
174             }
175         }
176         // Set the caret
177         setCaret(new FixedCaret());
178         // Selection is forced to be visible because the component is not editable
179         getCaret().setSelectionVisible(true);
180
181         addFocusListener(new FocusAdapter() {
182                 public void focusGained(FocusEvent e) {
183                     ((JTextPane) getConsole().getConfiguration().getInputCommandView()).requestFocus();
184                 }
185             });
186     }
187
188     /**
189      * Unselect text if selected one exists
190      */
191     public void removeSelection() {
192         if (getSelectionStart() != getSelectionEnd()) {
193             setSelectionEnd(getSelectionStart());
194         }
195     }
196
197     /**
198      * @param styledDocument
199      */
200     public void setStyledDocument(StyledDocument styledDocument) { }
201
202     public void resetLastEOL() {
203         lastEOL = false;
204     }
205
206     /**
207      * Display a buffer entry in the console
208      * @param buff the string  to write
209      * @param style the style to use to format the string
210      */
211     private void displayLineBuffer(String buff, String style) {
212         int sDocLength = getDocument().getLength();
213
214         if (buff.equals("\r")) {
215             /* If \r sent by mprintf then display nothing but prepare next display */
216             /* Insertion will be done just after last NEW_LINE */
217             try {
218                 String outputTxt = getDocument().getText(0, sDocLength);
219                 insertPosition = outputTxt.lastIndexOf(StringConstants.NEW_LINE) + 1;
220             } catch (BadLocationException e) {
221                 e.printStackTrace();
222             }
223             return;
224         } else {
225             /* Change position for insertion if a previous \r still influence display */
226             if ((insertPosition != 0) && (insertPosition < sDocLength)) {
227                 sDocLength = insertPosition;
228                 try {
229                     /* Remove chars to be replaced */
230                     if (insertPosition + buff.length() <= getDocument().getLength()) {
231                         getDocument().remove(insertPosition, buff.length());
232                     } else {
233                         /* Remove end of line */
234                         getDocument().remove(insertPosition, getDocument().getLength() - insertPosition);
235                     }
236                 } catch (BadLocationException e) {
237                     e.printStackTrace();
238                 }
239             } else {
240                 /* Reinit insertPosition: 0 is equivalent to insertPosition value ignored */
241                 insertPosition = 0;
242             }
243         }
244
245         boolean slastEOL = lastEOL;
246         lastEOL = !buff.isEmpty() && buff.charAt(buff.length() - 1) == '\n';
247         String str = buff;
248         if (lastEOL) {
249             str = buff.substring(0, buff.length() - 1);
250         }
251
252         if (slastEOL) {
253             str = "\n" + str;
254         }
255
256         try {
257             getDocument().insertString(sDocLength, str, null);
258
259             /* Move insertPosition to the end of last inserted data */
260             if (insertPosition != 0) {
261                 insertPosition += str.length();
262             }
263         } catch (BadLocationException e) {
264             e.printStackTrace();
265         }
266
267         int count = getDocument().getDefaultRootElement().getElementCount();
268         if (count > 1.5 * maxNumberOfLines) {
269             /* A removal is costly: array copy and with a gap buffer that leads to two array copies (when remove is followed by an insert).
270                So the idea is to minimize the number of removal: a removal only when 0.5*maxNumberOfLines useless lines are entered.
271             */
272             try {
273                 getDocument().remove(0, getDocument().getDefaultRootElement().getElement(count - maxNumberOfLines - 1).getEndOffset());
274             } catch (BadLocationException e) {
275                 e.printStackTrace();
276             }
277         }
278     }
279
280     /**
281      * Adds text to the output view and change the size of others components if
282      * necessary
283      *
284      * @param content
285      *            text to add
286      */
287     public void append(String content) {
288         //append(content, activeStyle);
289         displayLineBuffer(content, activeStyle);
290     }
291
292     /**
293      * Adds text to the output view and change the size of others components if
294      * necessary
295      *
296      * @param content
297      *            text to add
298      * @param styleName
299      *            style to set for content
300      */
301     public void append(String content, String styleName) {
302         if (styleName.equals(lastAppendedStyle) && bufferQueue.size() > 1) {
303             currentWorkingBuffer.append(content);
304         } else {
305             lastAppendedStyle = styleName;
306             styleQueue.add(lastAppendedStyle);
307             try {
308                 currentWorkingBuffer = new StringBuffer(content);
309                 bufferQueue.put(currentWorkingBuffer);
310             } catch (InterruptedException e) {
311                 e.printStackTrace();
312             }
313         }
314         if (!thread.isAlive()) {
315             thread.run();
316         }
317     }
318
319     /**
320      * Gets the error writer
321      *
322      * @return the error writer
323      * @see com.artenum.rosetta.interfaces.ui.OutputView#getErrorWriter()
324      */
325     public Writer getErrorWriter() {
326         return new BufferedWriter(StyleContext.DEFAULT_STYLE, bufferQueue, styleQueue);
327     }
328
329     /**
330      * Gets the writer
331      *
332      * @return the writer
333      * @see com.artenum.rosetta.interfaces.ui.OutputView#getWriter()
334      */
335     public Writer getWriter() {
336         return new BufferedWriter(StyleContext.DEFAULT_STYLE, bufferQueue, styleQueue);
337     }
338
339     /**
340      * Resets the output view (remove text)
341      *
342      * @see com.artenum.rosetta.interfaces.ui.OutputView#reset()
343      */
344     public void reset() {
345         setText("");
346         setCaretPosition(0);
347         lastEOL = false;
348     }
349
350     /**
351      * Move the caret to the beginning of the styled document
352      *
353      * @see com.artenum.rosetta.interfaces.ui.OutputView#setCaretPositionToBeginning()
354      */
355     public void setCaretPositionToBeginning() {
356         insertPosition = 0;
357         setCaretPosition(0);
358     }
359
360     /**
361      * Move the caret to the end of the styled document
362      *
363      * @see com.artenum.rosetta.interfaces.ui.OutputView#setCaretPositionToEnd()
364      */
365     public void setCaretPositionToEnd() {
366         insertPosition = 0;
367         setCaretPosition(getDocument().getLength());
368     }
369
370     /**
371      * Set the style for current text
372      *
373      * @param styleName
374      *            the style to set
375      * @see com.artenum.rosetta.interfaces.ui.OutputView#setStyleName(java.lang.String)
376      */
377     public void setStyleName(String styleName) {
378         activeStyle = styleName;
379     }
380
381     /**
382      * Sets the console object containing this output view
383      *
384      * @param c
385      *            the console associated
386      */
387     public void setConsole(SciConsole c) {
388         console = c;
389
390         // Drag n' Drop handling
391         this.setDropTarget(new DropTarget(this,
392                                           DnDConstants.ACTION_COPY_OR_MOVE, new SciDropTargetListener(console)));
393
394         // Commented because now done by the caret class
395         //FocusMouseListener focusGrabber = new FocusMouseListener(console);
396         //this.addMouseListener(focusGrabber);
397     }
398
399     /**
400      * Gets the console object containing this output view
401      *
402      * @return the console associated
403      */
404     public SciConsole getConsole() {
405         return console;
406     }
407
408     /**
409      * Get the current thread used to display
410      * @return the thread
411      */
412     public Thread getThread() {
413         return thread;
414     }
415
416     /**
417      * Set the maximum number of lines to keep before deleting the older one
418      * @param number the maximum
419      */
420     public void setMaxSize(int number) {
421         maxNumberOfLines = Math.max(1, number);
422     }
423
424     public View create(Element e) {
425         return new PlainView(e) {
426             public Container getContainer() {
427                 return SciOutputView.this;
428             }
429         };
430     }
431 }