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.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;
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;
50 import org.scilab.modules.commons.gui.ScilabCaret;
52 import com.artenum.rosetta.interfaces.ui.OutputView;
53 import com.artenum.rosetta.util.BufferedWriter;
54 import com.artenum.rosetta.util.StringConstants;
57 * Scilab Console UI which contains the previous commands and their outputs
59 * @author Vincent COUVERT
61 public class SciOutputView extends JEditorPane implements OutputView, ViewFactory {
62 private static final long serialVersionUID = 1L;
64 private static final int TOP_BORDER = 0;
66 private static final int BOTTOM_BORDER = 0;
68 private static final int LEFT_BORDER = 0;
70 private static final int RIGHT_BORDER = 0;
72 private static final int BUFFER_SIZE = 10;
74 private String activeStyle;
76 private String lastAppendedStyle;
78 private BlockingQueue<StringBuffer> bufferQueue;
80 private LinkedList<String> styleQueue;
82 private StringBuffer currentWorkingBuffer;
84 private SciConsole console;
86 private Thread thread;
88 private int insertPosition;
90 private int maxNumberOfLines;
92 private boolean lastEOL;
97 public SciOutputView() {
100 setEditorKit(new DefaultEditorKit() {
101 public ViewFactory getViewFactory() {
102 return SciOutputView.this;
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());
110 setBorder(BorderFactory.createEmptyBorder(TOP_BORDER, LEFT_BORDER, BOTTOM_BORDER, RIGHT_BORDER));
112 // Enabled Drag&Drop with this component
113 this.setDragEnabled(true);
114 this.setDoubleBuffered(true);
116 activeStyle = StyleContext.DEFAULT_STYLE;
117 bufferQueue = new ArrayBlockingQueue<StringBuffer>(BUFFER_SIZE);
118 styleQueue = new LinkedList<String>();
121 * Default caret for output view (to handle paste actions using middle button)
122 * @author Vincent COUVERT
124 final class FixedCaret extends ScilabCaret {
126 private static final long serialVersionUID = 8230195712653828841L;
131 private FixedCaret() {
132 super(SciOutputView.this);
136 * Manages mouse clicks
138 * @see javax.swing.text.DefaultCaret#mouseClicked(java.awt.event.MouseEvent)
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();
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)) {
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();
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);
166 /*** DELEGATE TO THE SYSTEM ***/
167 super.mouseClicked(e);
171 public void mousePressed(MouseEvent e) {
172 ((SciInputCommandView) console.getConfiguration().getInputCommandView()).removeSelection();
173 super.mousePressed(e);
177 setCaret(new FixedCaret());
178 // Selection is forced to be visible because the component is not editable
179 getCaret().setSelectionVisible(true);
181 addFocusListener(new FocusAdapter() {
182 public void focusGained(FocusEvent e) {
183 ((JTextPane) getConsole().getConfiguration().getInputCommandView()).requestFocus();
189 * Unselect text if selected one exists
191 public void removeSelection() {
192 if (getSelectionStart() != getSelectionEnd()) {
193 setSelectionEnd(getSelectionStart());
198 * @param styledDocument
200 public void setStyledDocument(StyledDocument styledDocument) { }
202 public void resetLastEOL() {
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
211 private void displayLineBuffer(String buff, String style) {
212 int sDocLength = getDocument().getLength();
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 */
218 String outputTxt = getDocument().getText(0, sDocLength);
219 insertPosition = outputTxt.lastIndexOf(StringConstants.NEW_LINE) + 1;
220 } catch (BadLocationException e) {
225 /* Change position for insertion if a previous \r still influence display */
226 if ((insertPosition != 0) && (insertPosition < sDocLength)) {
227 sDocLength = insertPosition;
229 /* Remove chars to be replaced */
230 if (insertPosition + buff.length() <= getDocument().getLength()) {
231 getDocument().remove(insertPosition, buff.length());
233 /* Remove end of line */
234 getDocument().remove(insertPosition, getDocument().getLength() - insertPosition);
236 } catch (BadLocationException e) {
240 /* Reinit insertPosition: 0 is equivalent to insertPosition value ignored */
245 boolean slastEOL = lastEOL;
246 lastEOL = !buff.isEmpty() && buff.charAt(buff.length() - 1) == '\n';
249 str = buff.substring(0, buff.length() - 1);
257 getDocument().insertString(sDocLength, str, null);
259 /* Move insertPosition to the end of last inserted data */
260 if (insertPosition != 0) {
261 insertPosition += str.length();
263 } catch (BadLocationException e) {
264 // TODO Auto-generated catch block
268 int count = getDocument().getDefaultRootElement().getElementCount();
269 if (count > 1.5 * maxNumberOfLines) {
270 /* A removal is costly: array copy and with a gap buffer that leads to two array copies (when remove is followed by an insert).
271 So the idea is to minimize the number of removal: a removal only when 0.5*maxNumberOfLines useless lines are entered.
274 getDocument().remove(0, getDocument().getDefaultRootElement().getElement(count - maxNumberOfLines - 1).getEndOffset());
275 } catch (BadLocationException e) {
282 * Adds text to the output view and change the size of others components if
288 public void append(String content) {
289 //append(content, activeStyle);
290 displayLineBuffer(content, activeStyle);
294 * Adds text to the output view and change the size of others components if
300 * style to set for content
302 public void append(String content, String styleName) {
303 if (styleName.equals(lastAppendedStyle) && bufferQueue.size() > 1) {
304 currentWorkingBuffer.append(content);
306 lastAppendedStyle = styleName;
307 styleQueue.add(lastAppendedStyle);
309 currentWorkingBuffer = new StringBuffer(content);
310 bufferQueue.put(currentWorkingBuffer);
311 } catch (InterruptedException e) {
315 if (!thread.isAlive()) {
321 * Gets the error writer
323 * @return the error writer
324 * @see com.artenum.rosetta.interfaces.ui.OutputView#getErrorWriter()
326 public Writer getErrorWriter() {
327 return new BufferedWriter(StyleContext.DEFAULT_STYLE, bufferQueue, styleQueue);
334 * @see com.artenum.rosetta.interfaces.ui.OutputView#getWriter()
336 public Writer getWriter() {
337 return new BufferedWriter(StyleContext.DEFAULT_STYLE, bufferQueue, styleQueue);
341 * Resets the output view (remove text)
343 * @see com.artenum.rosetta.interfaces.ui.OutputView#reset()
345 public void reset() {
352 * Move the caret to the beginning of the styled document
354 * @see com.artenum.rosetta.interfaces.ui.OutputView#setCaretPositionToBeginning()
356 public void setCaretPositionToBeginning() {
362 * Move the caret to the end of the styled document
364 * @see com.artenum.rosetta.interfaces.ui.OutputView#setCaretPositionToEnd()
366 public void setCaretPositionToEnd() {
368 setCaretPosition(getDocument().getLength());
372 * Set the style for current text
376 * @see com.artenum.rosetta.interfaces.ui.OutputView#setStyleName(java.lang.String)
378 public void setStyleName(String styleName) {
379 activeStyle = styleName;
383 * Sets the console object containing this output view
386 * the console associated
388 public void setConsole(SciConsole c) {
391 // Drag n' Drop handling
392 this.setDropTarget(new DropTarget(this,
393 DnDConstants.ACTION_COPY_OR_MOVE, new SciDropTargetListener(console)));
395 // Commented because now done by the caret class
396 //FocusMouseListener focusGrabber = new FocusMouseListener(console);
397 //this.addMouseListener(focusGrabber);
401 * Gets the console object containing this output view
403 * @return the console associated
405 public SciConsole getConsole() {
410 * Get the current thread used to display
413 public Thread getThread() {
418 * Set the maximum number of lines to keep before deleting the older one
419 * @param number the maximum
421 public void setMaxSize(int number) {
422 maxNumberOfLines = Math.max(1, number);
425 public View create(Element e) {
426 return new PlainView(e) {
427 public Container getContainer() {
428 return SciOutputView.this;