2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2010 - 2011 - Calixte DENIZET
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.1-en.txt
13 package org.scilab.modules.scinotes;
15 import java.awt.Color;
16 import java.awt.Cursor;
18 import java.awt.Graphics;
19 import java.awt.Point;
20 import java.awt.Rectangle;
21 import java.awt.Shape;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.awt.event.FocusEvent;
25 import java.awt.event.FocusListener;
26 import java.awt.event.KeyEvent;
27 import java.awt.event.KeyListener;
28 import java.awt.event.MouseEvent;
29 import java.awt.event.MouseListener;
30 import java.awt.event.MouseMotionListener;
31 import java.awt.event.MouseWheelEvent;
32 import java.awt.event.MouseWheelListener;
34 import java.util.ArrayList;
35 import java.util.EventObject;
36 import java.util.HashMap;
37 import java.util.List;
39 import java.util.UUID;
40 import java.util.Vector;
41 import java.util.regex.Matcher;
42 import java.util.regex.Pattern;
44 import javax.swing.JEditorPane;
45 import javax.swing.JScrollBar;
46 import javax.swing.JScrollPane;
47 import javax.swing.JSplitPane;
48 import javax.swing.SwingUtilities;
49 import javax.swing.Timer;
50 import javax.swing.UIManager;
51 import javax.swing.event.CaretEvent;
52 import javax.swing.event.CaretListener;
53 import javax.swing.event.EventListenerList;
54 import javax.swing.text.BadLocationException;
55 import javax.swing.text.Caret;
56 import javax.swing.text.DefaultHighlighter;
57 import javax.swing.text.Document;
58 import javax.swing.text.Element;
59 import javax.swing.text.Highlighter;
60 import javax.swing.text.JTextComponent;
61 import javax.swing.text.View;
63 import org.scilab.modules.commons.OS;
64 import org.scilab.modules.commons.gui.ScilabCaret;
65 import org.scilab.modules.console.utils.ScilabLaTeXViewer;
66 import org.scilab.modules.gui.messagebox.MessageBox;
67 import org.scilab.modules.gui.messagebox.ScilabMessageBox;
68 import org.scilab.modules.gui.messagebox.ScilabModalDialog;
69 import org.scilab.modules.gui.messagebox.ScilabModalDialog.AnswerOption;
70 import org.scilab.modules.gui.messagebox.ScilabModalDialog.ButtonType;
71 import org.scilab.modules.gui.messagebox.ScilabModalDialog.IconType;
72 import org.scilab.modules.gui.utils.WebBrowser;
73 import org.scilab.modules.scinotes.actions.CopyAsHTMLAction;
74 import org.scilab.modules.scinotes.actions.OpenSourceFileOnKeywordAction;
75 import org.scilab.modules.scinotes.utils.NavigatorWindow;
76 import org.scilab.modules.scinotes.utils.ScilabScrollPane;
77 import org.scilab.modules.scinotes.utils.SciNotesMessages;
80 * Class ScilabEditorPane
81 * @author Calixte DENIZET
84 public class ScilabEditorPane extends JEditorPane implements Highlighter.HighlightPainter,
85 CaretListener, MouseListener,
86 MouseMotionListener, Cloneable,
89 private static final long serialVersionUID = 4322071415211939097L;
91 private static final String TIRET = " - ";
92 private static final Cursor HANDCURSOR = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
93 private static final Cursor TEXTCURSOR = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);
94 private static final DefaultHighlighter.DefaultHighlightPainter HIGHLIGHTER = new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW);
96 private static ScilabEditorPane focused;
98 private Color highlightColor;
99 private Color highlightContourColor;
100 private boolean highlightEnable;
101 private Object highlightCL;
102 private boolean matchingKeywordsEnable;
103 private boolean matchingOpenersEnable;
104 private boolean overwriteMode;
105 private ScilabLexer lexer;
106 private SciNotes editor;
107 private IndentManager indent;
108 private TabManager tab;
109 private CommentManager com;
110 private HelpOnTypingManager helpOnTyping;
111 private TrailingWhiteManager trailingWhite;
112 private boolean readonly;
113 private boolean binary;
114 private String infoBar = "";
115 private String shortName = "";
116 private String title = "";
118 private Timer selectionTimer;
119 private Timer matcherTimer;
121 private Point mousePoint;
123 private long lastModified;
124 private long lastExternalModification;
126 /* matchLR matches Left to Right ... */
127 private MatchingBlockManager matchLR;
128 private MatchingBlockManager matchRL;
130 private boolean suppressCom = true;
132 private SciNotesLineNumberPanel xln;
133 private ScilabEditorPane rightTextPane;
136 private EditorComponent edComponent;
138 private boolean hand;
139 private boolean infoBarChanged;
140 private boolean ctrlHit;
142 private Color saveHighlightContourColor;
143 private Color saveHighlightColor;
144 private boolean hasBeenSaved;
145 private boolean saveHighlightEnable;
147 private EventListenerList kwListeners = new EventListenerList();
148 private Map<Integer, Object> highlightedWords = new HashMap<Integer, Object>();
150 //private List<Object> highlightedWords = new ArrayList<Object>();
151 //private List<Integer> highlightedWordsBegin = new ArrayList<Integer>();
155 * @param editor which uses this pane
157 public ScilabEditorPane(SciNotes editor) {
159 if (focused == null) {
162 this.editor = editor;
163 this.uuid = UUID.randomUUID();
164 edComponent = new EditorComponent(this);
167 When SciNotes is docked and has two tabs, switching the tabs causes a focus loss.
168 The focus is gave to the other docked component and that generates a toolbar change.
169 The solution is to set FocusCycleRoot to false (set to true by default in JEditorPane).
171 setFocusCycleRoot(false);
173 addCaretListener(this);
174 addMouseMotionListener(this);
175 addMouseListener(this);
176 enableMatchingKeywords(SciNotesOptions.getSciNotesDisplay().highlightKeywords);
177 enableMatchingOpeners(SciNotesOptions.getSciNotesDisplay().highlightBrackets);
179 addFocusListener(new FocusListener() {
180 public void focusGained(FocusEvent e) {
181 updateInfosWhenFocused();
182 if (ScilabEditorPane.this.editor != null) {
183 NavigatorWindow nav = ScilabEditorPane.this.editor.getNavigator();
185 nav.update((ScilabDocument) getDocument());
188 setMustAdjustVisibility(true);
191 public void focusLost(FocusEvent e) {
192 ((ScilabDocument) getDocument()).setFocused(false);
193 if (e.getOppositeComponent() == getOtherPaneInSplit()) {
194 setMustAdjustVisibility(false);
199 addKeywordListener(new KeywordAdapter.MouseOverAdapter() {
200 public void caughtKeyword(KeywordEvent e) {
201 if (ScilabLexerConstants.isClickable(e.getType())) {
203 setCursor(HANDCURSOR);
206 String url = ((ScilabDocument) getDocument()).getText(e.getStart(), e.getLength());
207 if (ScilabLexerConstants.isClickable(e.getType())) {
209 switch (e.getType()) {
210 case ScilabLexerConstants.URL :
211 text = SciNotesMessages.OPENURL;
213 case ScilabLexerConstants.MAIL :
214 text = SciNotesMessages.MAILTO;
216 case ScilabLexerConstants.MACROS :
217 text = SciNotesMessages.OPENSOURCE;
219 case ScilabLexerConstants.MACROINFILE :
220 text = SciNotesMessages.SHOWSOURCE;
226 if (text != null && ScilabEditorPane.this.editor != null) {
227 ScilabEditorPane.this.editor.getInfoBar().setText(text + url);
228 infoBarChanged = true;
231 } catch (BadLocationException ex) { }
232 } else if (ScilabEditorPane.this.editor != null) {
233 ScilabEditorPane.this.editor.getInfoBar().setText(SciNotesMessages.CLICKABLE_URL);
234 infoBarChanged = true;
236 setCursor(TEXTCURSOR);
242 setCursor(TEXTCURSOR);
245 if (infoBarChanged && ScilabEditorPane.this.editor != null) {
246 ScilabEditorPane.this.editor.getInfoBar().setText(infoBar);
247 infoBarChanged = false;
249 if (ScilabLexerConstants.isLaTeX(e.getType())) {
251 int start = e.getStart();
252 int end = start + e.getLength();
253 String exp = ((ScilabDocument) getDocument()).getText(start, e.getLength());
254 ScilabScrollPane ssp = getScrollPane();
257 height = ssp.getHeight() + ssp.getVerticalScrollBar().getValue();
259 height = edComponent.getHeight();
261 ScilabLaTeXViewer.displayExpression(ScilabEditorPane.this, height, exp, start, end);
262 } catch (BadLocationException ex) { }
264 ScilabLaTeXViewer.removeLaTeXViewer(ScilabEditorPane.this);
270 addKeywordListener(new KeywordAdapter.MouseClickedAdapter() {
271 public void caughtKeyword(KeywordEvent e) {
272 if (ctrlHit && ScilabLexerConstants.isClickable(e.getType())) {
276 infoBarChanged = false;
277 setCursor(TEXTCURSOR);
278 if (ScilabEditorPane.this.editor != null) {
279 ScilabEditorPane.this.editor.getInfoBar().setText(infoBar);
281 String url = ((ScilabDocument) getDocument()).getText(e.getStart(), e.getLength());
282 if (ScilabLexerConstants.URL == e.getType() || ScilabLexerConstants.MAIL == e.getType()) {
283 WebBrowser.openUrl(url);
284 } else if (ScilabLexerConstants.isOpenable(e.getType())) {
285 OpenSourceFileOnKeywordAction.openSource(ScilabEditorPane.this, url);
287 } catch (BadLocationException ex) { }
292 getScrollPane().addMouseWheelListener(new MouseWheelListener() {
293 public void mouseWheelMoved(MouseWheelEvent e) {
294 if ((OS.get() == OS.MAC && e.isMetaDown()) || e.isControlDown()) {
295 int n = e.getWheelRotation();
296 SciNotes.updateFontSize(n);
302 addKeyListener(this);
303 setTransferHandler(new CopyAsHTMLAction.HTMLTransferHandler());
305 ((ScilabCaret) getCaret()).setMustAdjustVisibility(false);
308 public void enableColorization(boolean b) {
309 View view = ((ScilabDocument) getDocument()).getView();
311 if (view instanceof ScilabView) {
312 ((ScilabView) view).enable(b);
314 ((ScilabPlainView) view).enable(b);
319 public void configurationChanged(SciNotesConfiguration.Conf conf) {
320 ((ScilabEditorKit) getEditorKit()).getStylePreferences().configurationChanged(conf);
327 enableHighlightedLine(SciNotesOptions.getSciNotesDisplay().highlightCurrentLine);
328 setHighlightedLineColor(SciNotesOptions.getSciNotesDisplay().currentLineColor);
329 enableColorization(SciNotesOptions.getSciNotesDisplay().keywordsColorization);
330 setBackground(SciNotesOptions.getSciNotesDisplay().backgroundColor);
331 setCaretColor(SciNotesOptions.getSciNotesDisplay().caretColor);
333 boolean kw = SciNotesOptions.getSciNotesDisplay().highlightKeywords;
334 boolean op = SciNotesOptions.getSciNotesDisplay().highlightBrackets;
336 if ((kw || op) && (!matchingKeywordsEnable && !matchingOpenersEnable)) {
337 matchLR = new MatchingBlockManager((ScilabDocument) getDocument(), this, true, getHighlighter());
338 matchLR.setDefaults();
339 matchRL = new MatchingBlockManager((ScilabDocument) getDocument(), this, false, getHighlighter());
340 matchRL.setDefaults();
341 enableMatchingKeywords(kw);
342 enableMatchingOpeners(op);
346 if ((kw || op) && (matchingKeywordsEnable || matchingOpenersEnable)) {
347 matchLR.configurationChanged(conf);
348 matchRL.configurationChanged(conf);
352 if ((!kw && !op) && (matchingKeywordsEnable || matchingOpenersEnable)) {
353 matchLR.desactivateMouseOver();
355 matchRL.desactivateMouseOver();
365 public ScilabLexer getLexer() {
371 * When no split and in wrapped view , this method return true and the consequence is
372 * that there is no horizontal scrollbar.
374 public boolean getScrollableTracksViewportWidth() {
375 if (((ScilabDocument) getDocument()).getView() instanceof ScilabView) {
376 return !edComponent.isSplited();
378 return super.getScrollableTracksViewportWidth();
383 * @return true if the pane is in OverWrite mode (insert)
385 public boolean getOverwriteMode() {
386 return this.overwriteMode;
390 * @param overwriteMode true if the pane is in OverWrite mode (insert)
392 public void setOverwriteMode(boolean overwriteMode) {
393 this.overwriteMode = overwriteMode;
394 ((ScilabCaret) getCaret()).setOverwriteMode(overwriteMode);
400 public void replaceSelection(String content) {
401 if (overwriteMode && getSelectionStart() == getSelectionEnd()) {
402 int pos = getCaretPosition();
403 select(pos, pos + content.length());
406 if (((SciNotesCaret) getCaret()).isEmptySelection()) {
407 super.replaceSelection(content);
409 SciNotesCaret caret = (SciNotesCaret) getCaret();
410 int[][] pos = caret.getSelectedPositions();
411 List<Object> sels = caret.getSelections();
412 int len = content.length();
415 ScilabDocument doc = (ScilabDocument) getDocument();
416 doc.getUndoManager().enableOneShot(true);
417 doc.mergeEditsBegin();
418 caret.protectHighlights(true);
419 for (int i = 0; i < pos.length; i++) {
420 if (sels.get(i) != null) {
421 sres = pos[i][0] + res;
423 doc.replace(sres, pos[i][1] - pos[i][0], content, null);
424 } catch (BadLocationException e) { }
425 res = sres + len - pos[i][1];
426 pos[i][0] = sres + len;
427 pos[i][1] = sres + len;
431 doc.getUndoManager().enableOneShot(false);
432 caret.protectHighlights(false);
433 caret.updateHighlights();
441 public void keyPressed(KeyEvent e) {
442 // Workaround for bug 7238
443 if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD
444 && e.getKeyCode() == KeyEvent.VK_DELETE
445 && e.getKeyChar() != KeyEvent.VK_DELETE) {
446 e.setKeyCode(KeyEvent.VK_DECIMAL);
448 } else if (mousePoint != null && e.getKeyCode() == KeyEvent.VK_CONTROL) {
450 preventConcernedKeywordListener(viewToModel(mousePoint), e, KeywordListener.ONMOUSEOVER);
460 public void keyReleased(KeyEvent e) {
461 if (mousePoint != null && e.getKeyCode() == KeyEvent.VK_CONTROL) {
463 preventConcernedKeywordListener(viewToModel(mousePoint), e, KeywordListener.ONMOUSEOVER);
471 public void keyTyped(KeyEvent e) { }
474 * @overload #setDocument
477 public void setDocument(Document doc) {
478 super.setDocument(doc);
479 if (doc instanceof ScilabDocument) {
480 ((ScilabDocument) doc).getUndoManager().discardAllEdits();
481 initialize((ScilabDocument) doc);
488 public void setName(String name) {
489 setNameInSuper(name);
490 setShortNameAndTitle(name);
491 ScilabEditorPane pane = getOtherPaneInSplit();
493 // I don't call pane.setName since we will enter in an infinite loop
494 pane.setNameInSuper(name);
495 pane.setShortNameAndTitle(name);
500 * @param name the name
502 private void setNameInSuper(String name) {
507 * @param name the name
509 private void setShortNameAndTitle(String name) {
511 File f = new File(name);
512 setShortName(f.getName());
513 title = shortName + " (" + f.getAbsolutePath() + ")" + TIRET + SciNotesMessages.SCILAB_EDITOR;
518 * @param title the title
520 public void setTitle(String title) {
521 this.title = title + TIRET + SciNotesMessages.SCILAB_EDITOR;
527 public String getTitle() {
532 * @param name the short name
534 public void setShortName(String name) {
535 this.shortName = name;
539 * @return the short name of the file (without the full path)
541 public String getShortName() {
548 public void init(final int pos) {
549 SwingUtilities.invokeLater(new Runnable() {
552 if (getCaret() != null) {
553 setCaretPosition(pos);
562 public void close() {
563 FocusListener[] l = getFocusListeners();
564 for (int i = 0; i < l.length; i++) {
565 removeFocusListener(l[i]);
567 if (getCaret() instanceof SciNotesCaret) {
568 ((SciNotesCaret) getCaret()).clean();
569 super.setCaret(null);
570 if (selectionTimer != null && selectionTimer.isRunning()) {
571 selectionTimer.stop();
572 selectionTimer = null;
574 if (matcherTimer != null && matcherTimer.isRunning()) {
584 public void updateInfosWhenFocused() {
585 ScilabDocument doc = (ScilabDocument) getDocument();
586 doc.setFocused(true);
587 SciNotes.setEditor(editor);
589 doc.getUndoManager().enableUndoRedoButtons();
591 if (editor != null) {
592 if (checkExternalModif() && lastExternalModification < getLastModification()) {
593 editor.getInfoBar().setText(SciNotesMessages.EXTERNAL_MODIFICATION_INFO);
594 if (ScilabModalDialog.show(editor, String.format(SciNotesMessages.ASK_TO_RELOAD, getShortName()), SciNotesMessages.RELOAD, IconType.QUESTION_ICON, ButtonType.YES_NO) == AnswerOption.YES_OPTION) {
595 editor.reload(getEditor().getTabPane().indexOfComponent(getEditorComponent()));
596 editor.getTextPane().updateInfosWhenFocused();
598 lastExternalModification = getLastModification();
600 editor.getInfoBar().setText(getInfoBarText());
606 * @return true if an external modif occurred
608 public boolean checkExternalModif() {
609 String path = getName();
611 File f = new File(path);
612 if (f != null && f.exists()) {
613 return lastModified < f.lastModified();
622 * Get last modification on file
623 * @return the lastModified value
625 public long getLastModification() {
626 String path = getName();
628 File f = new File(path);
629 if (f != null && f.exists()) {
630 return f.lastModified();
638 * @return the String which must be displayed in the infobar
640 public String getInfoBarText() {
645 * @param text String which must be displayed in the infobar
647 public void setInfoBarText(String text) {
649 if (editor != null) {
650 editor.getInfoBar().setText(getInfoBarText());
655 * @param readonly true to set Read-Only mode
657 public void setReadOnly(boolean readonly) {
658 this.readonly = readonly;
659 setEditable(!readonly);
660 setDragEnabled(!readonly);
662 infoBar = SciNotesMessages.READ_ONLY_MODE;
669 * @param binary true to set binary mode
671 public void setBinary(boolean binary) {
672 this.binary = binary;
673 setEditable(!binary);
674 setDragEnabled(!binary);
676 infoBar = SciNotesMessages.BINARY_FILE_MODE;
684 * Copy the props of this textPane to pane
685 * @param pane the pane which receives the same props as this
687 public void copyProps(ScilabEditorPane pane) {
688 pane.lastModified = lastModified;
689 pane.highlightEnable = highlightEnable;
690 pane.matchingKeywordsEnable = matchingKeywordsEnable;
691 pane.matchingOpenersEnable = matchingOpenersEnable;
692 pane.suppressCom = suppressCom;
693 pane.setName(getName());
694 pane.setShortName(getShortName());
695 pane.setTitle(getTitle().substring(0, getTitle().lastIndexOf(TIRET)));
696 pane.setEditable(isEditable());
700 * Get the UUID associated with the editor pane instance.
701 * @return unique identifier
703 public UUID getUUID() {
708 * Get the time where the file associated with this pane
710 * @return the last modified time or 0
712 public long getLastModified() {
717 * Set the last time where the file associated with this pane
719 * @param time the time
721 public void setLastModified(long time) {
722 this.lastModified = time;
723 ScilabEditorPane pane = getOtherPaneInSplit();
725 pane.lastModified = time;
730 * @return the SciNotesLineNumberPanel used with this pane
732 public SciNotesLineNumberPanel getXln() {
739 public SciNotes getEditor() {
744 * @param editor to set
746 public void setEditor(SciNotes editor) {
747 this.editor = editor;
753 public void disableAll() {
757 trailingWhite = null;
762 rightTextPane = null;
764 enableMatchingKeywords(false);
765 enableMatchingOpeners(false);
766 if (matchLR != null) {
767 matchLR.desactivateMouseOver();
770 if (matchRL != null) {
771 matchRL.desactivateMouseOver();
778 * Destroy this component
780 public void destroy() {
786 * @return the scrollPane or the splitpane associated with this textPane
788 public EditorComponent getEditorComponent() {
793 * @return the scrollPane or the splitpane associated with this textPane
795 public void setEditorComponent(EditorComponent ed) {
796 this.edComponent = ed;
800 * @param split the split used
802 public void setSplitPane(JSplitPane split) {
803 edComponent.setSplitPane(split);
807 * @param split the split used
809 public JSplitPane getSplitPane() {
810 return edComponent.getSplitPane();
814 * Update the title of current tab
816 public void updateTitle() {
817 if (editor != null) {
818 int index = editor.getTextPaneIndex(this);
820 editor.updateTabTitle(index);
825 public void setMustAdjustVisibility(boolean mustAdjustVisibility) {
826 ((ScilabCaret) getCaret()).setMustAdjustVisibility(mustAdjustVisibility);
829 public boolean getMustAdjustVisibility() {
830 return ((ScilabCaret) getCaret()).getMustAdjustVisibility();
834 * Scroll the pane to have the line containing pos on the top of the pane
835 * @param pos the position in the document
837 public void scrollTextToPos(int pos) {
838 scrollTextToPos(pos, true, false);
842 * Scroll the pane to have the line containing pos on the top or centered on the pane
843 * @param pos the position in the document
844 * @param setCaret, if true the caret is set at the given position
845 * @param centered, if true the line is centered
847 public void scrollTextToPos(final int pos, final boolean setCaret, final boolean centered) {
848 SwingUtilities.invokeLater(new Runnable() {
852 setCaretPosition(pos);
854 JScrollBar scrollbar = getScrollPane().getVerticalScrollBar();
855 Rectangle rect = modelToView(pos);
857 int value = scrollbar.getValue();
858 int h = scrollbar.getHeight();
859 if (rect.y < value || rect.y > value + h) {
860 scrollbar.setValue(Math.max(0, rect.y - h / 2));
863 if (rect.y > scrollbar.getMaximum()) {
864 scrollbar.setMaximum(rect.y);
866 scrollbar.setValue(rect.y);
868 } catch (BadLocationException e) { }
874 * Scroll the pane to have the line lineNumber on the top of the pane
875 * @param lineNumber the number of the line
876 * @param highlight true to highlight the line
878 public void scrollTextToLineNumber(int lineNumber, boolean highlight) {
879 scrollTextToLineNumber(lineNumber, highlight, true, false);
883 * Scroll the pane to have the line lineNumber on the top or centered on the pane
884 * @param lineNumber the number of the line
885 * @param highlight true to highlight the line
886 * @param setCaret, if true the caret is set at the given line
887 * @param centered, if true the line is centered
889 public void scrollTextToLineNumber(int lineNumber, final boolean highlight, final boolean setCaret, final boolean centered) {
890 Element root = getDocument().getDefaultRootElement();
891 if (lineNumber >= 1 && lineNumber <= root.getElementCount()) {
892 final int pos = root.getElement(lineNumber - 1).getStartOffset();
893 SwingUtilities.invokeLater(new Runnable() {
895 scrollTextToPos(pos, setCaret, centered);
897 saveHighlightContourColor = highlightContourColor;
898 highlightContourColor = null;
899 saveHighlightColor = highlightColor;
900 highlightColor = Color.YELLOW;
901 saveHighlightEnable = highlightEnable;
903 enableHighlightedLine(false);
904 enableHighlightedLine(true);
912 * Scroll the pane to have the line lineNumber on the top of the pane in whereami mode
913 * The line number is computed regarding the function named funname.
914 * @param lineNumber the number of the line
915 * @param funname the function name
916 * @param highlight true to highlight the line
918 public void scrollTextToLineNumberInWhereami(int lineNumber, String funname, boolean highlight) {
919 scrollTextToLineNumberInWhereami(lineNumber, funname, highlight, true, false);
923 * Scroll the pane to have the line lineNumber on the top of the pane in whereami mode
924 * The line number is computed regarding the function named funname.
925 * @param lineNumber the number of the line
926 * @param funname the function name
927 * @param highlight true to highlight the line
928 * @param setCaret, if true the caret is set at the given line
929 * @param centered, if true the line is centered
931 public void scrollTextToLineNumberInWhereami(int lineNumber, String funname, boolean highlight, boolean setCaret, boolean centered) {
932 if (funname != null) {
933 Element root = getDocument().getDefaultRootElement();
934 int nlines = root.getElementCount();
935 ScilabDocument.ScilabLeafElement elem;
936 for (int i = 0; i < nlines; i++) {
937 elem = (ScilabDocument.ScilabLeafElement) root.getElement(i);
938 if (elem.getFunctionName().equals(funname)) {
944 scrollTextToLineNumber(lineNumber, highlight, setCaret, centered);
948 * @return the width of a white
950 public int getWhiteWidth() {
951 View view = ((ScilabDocument) getDocument()).getView();
953 if (view instanceof ScilabView) {
954 return ((ScilabView) view).getWhiteWidth();
956 return ((ScilabPlainView) view).getWhiteWidth();
963 public void resetFont() {
964 Font font = ((ScilabEditorKit) getEditorKit()).getStylePreferences().getBaseFont();
966 xln.updateFont(font);
971 * @param keyword the type of keyword
972 * @param type an int : -2 to reset italic, -1 to reset bold, 1 to set bold and 2 to set italic
974 public void resetFont(String keyword, int type) {
975 ((ScilabEditorKit) getEditorKit()).getStylePreferences().genFont(keyword, type);
979 * Set a new attribute
980 * @param keyword the type of keyword
981 * @param type an int : 0 nothing, 1 underline, 2 stroke, 3 underline and stroke
983 public void resetAttribute(String keyword, int type) {
984 ((ScilabEditorKit) getEditorKit()).getStylePreferences().genAttribute(keyword, type);
989 * @param keyword the kind of the keyword
990 * @param color the color
992 public void resetColor(String keyword, Color color) {
993 ((ScilabEditorKit) getEditorKit()).getStylePreferences().genColors(keyword, color);
997 * Set to true if the comments must be suppressed when the code is executing in the console
1000 public void suppressCommentsInExecutingCode(boolean b) {
1005 * Execute the code in the console, the code is the selected text if exists
1006 * or the text from beginning to actual position of the caret
1007 * Comments are removed if suppressCom is set to true
1008 * @return the code to be executed in the console.
1010 public String getCodeToExecute() {
1013 start = getSelectionStart();
1014 end = getSelectionEnd();
1016 if (((SciNotesCaret) getCaret()).isEmptySelection()) {
1019 selection = getDocument().getText(0, start);
1022 selection = getSelectedText();
1024 } catch (BadLocationException e) {
1028 selection = getSelectedText();
1032 StringBuffer buf = new StringBuffer(selection.length());
1033 ScilabLexer.ScilabTokens tokens = ScilabLexer.getScilabTokens(selection);
1034 List<Integer> tokType = tokens.getTokenType();
1035 List<Integer> tokPos = tokens.getTokenPos();
1036 List<String> commands = new ArrayList<String>();
1038 for (int i = 0; i < tokType.size(); i++) {
1039 String str = selection.substring(prevPos, tokPos.get(i));
1040 if ("\n".equals(str)) {
1041 commands.add(buf.toString());
1043 } else if (!ScilabLexerConstants.isComment(tokType.get(i))) {
1046 prevPos = tokPos.get(i);
1049 if (buf.length() != 0) {
1050 commands.add(buf.toString());
1054 Pattern pat = Pattern.compile("[ \t]*");
1055 for (String command : commands) {
1056 if (!pat.matcher(command).matches()) {
1057 buf.append(command).append("\n");
1061 return buf.toString();
1068 * Add a new KeywordListener
1069 * @param kw a KeywordListener
1071 public void addKeywordListener(KeywordListener kw) {
1072 kwListeners.add(KeywordListener.class, kw);
1076 * Remove a new KeywordListener
1077 * @param kw a KeywordListener
1079 public void removeKeywordListener(KeywordListener kw) {
1080 kwListeners.remove(KeywordListener.class, kw);
1084 * @return an array of KeywordListener
1086 public KeywordListener[] getKeywordListeners() {
1087 return kwListeners.getListeners(KeywordListener.class);
1091 * Set a new color for the highlighting
1092 * @param c the color, can be null (useful if setHighlightedContourColor is used with a
1095 public void setHighlightedLineColor(Color c) {
1100 * Set a new color for the contour of the highlighting
1101 * @param c the color, if null no contour is drawn
1103 public void setHighlightedContourColor(Color c) {
1104 highlightContourColor = c;
1108 * Activate or desactivate the help on typing
1110 public void activateHelpOnTyping() {
1111 boolean isActive = HelpOnTypingManager.getInstance().isActive();
1112 if (isActive && helpOnTyping == null) {
1113 helpOnTyping = HelpOnTypingManager.getInstance();
1114 addKeyListener(helpOnTyping);
1115 } else if (!isActive && helpOnTyping != null) {
1116 removeKeyListener(helpOnTyping);
1117 helpOnTyping = null;
1122 * Enable (active true) or disable (active false) the line-highlighting.
1123 * @param active true or false
1125 public void enableHighlightedLine(boolean active) {
1126 if (active && !highlightEnable) {
1128 highlightCL = getHighlighter().addHighlight(0, 0, this);
1129 } catch (BadLocationException e) { }
1130 highlightEnable = true;
1133 if (!active && highlightEnable) {
1134 getHighlighter().removeHighlight(highlightCL);
1135 highlightEnable = false;
1142 * Enable (active true) or disable (active false) the matching keywords.
1143 * @param active true or false
1145 public void enableMatchingKeywords(boolean active) {
1146 matchingKeywordsEnable = active;
1150 * Enable (active true) or disable (active false) the matching keywords.
1151 * @param active true or false
1153 public void enableMatchingOpeners(boolean active) {
1154 matchingOpenersEnable = active;
1158 * Get a matching manager
1159 * @param lr true if the LR matcher must be returned
1160 * @return the MatchingBlockManager
1162 public MatchingBlockManager getMatchingBlockManager(boolean lr) {
1171 * This class listens to the caret event
1174 public void caretUpdate(CaretEvent e) {
1176 removeHighlightForLine();
1180 final String str = getSelectedText();
1181 if (str != null && str.length() != 0) {
1182 if (selectionTimer == null) {
1183 selectionTimer = new Timer(1000, new ActionListener() {
1184 public void actionPerformed(ActionEvent e) {
1186 final String str = getSelectedText();
1187 if (str != null && str.length() != 0) {
1188 int tok = lexer.getKeyword(getSelectionStart(), false);
1189 int s = lexer.start + lexer.yychar();
1190 if (ScilabLexerConstants.isSearchable(tok) && getSelectionStart() == s && getSelectionEnd() == s + lexer.yylength()) {
1191 highlightWords(tok, SearchManager.generatePattern(str, false, true, false), false);
1193 highlightWords(str, false);
1196 if (highlightedWords.size() > 1 && editor != null && editor.getInfoBar() != null) {
1197 editor.getInfoBar().setText(String.format(SciNotesMessages.OCCURENCES_FOUND, Integer.toString(highlightedWords.size())));
1199 removeHighlightOnPosition(getSelectionStart());
1201 } catch (Exception ee) { }
1202 selectionTimer = null;
1205 selectionTimer.setRepeats(false);
1206 selectionTimer.start();
1208 selectionTimer.restart();
1211 removeHighlightedWords();
1215 if (highlightEnable) {
1219 if (matchingKeywordsEnable || matchingOpenersEnable) {
1220 if (matcherTimer == null) {
1221 matcherTimer = new Timer(100, new ActionListener() {
1222 public void actionPerformed(ActionEvent e) {
1223 int pos = getCaretPosition();
1224 int tok = lexer.getKeyword(pos, false);
1225 matchLR.searchMatchingBlock(false, tok, lexer.start + lexer.yychar());
1226 tok = lexer.getKeyword(pos, true);
1227 matchRL.searchMatchingBlock(false, tok, lexer.start + lexer.yychar() + lexer.yylength());
1228 matcherTimer = null;
1231 matcherTimer.setRepeats(false);
1232 matcherTimer.start();
1234 matcherTimer.restart();
1240 if (!readonly && !binary && editor != null) {
1241 editor.getInfoBar().setText(((ScilabDocument) getDocument()).getCurrentFunction(getCaretPosition()));
1246 * Used to paint the highlighted line
1247 * @param g graphics to use
1250 * @param bounds the shape representing the area
1251 * @param c this pane
1253 public void paint(Graphics g, int p0, int p1, Shape bounds, JTextComponent c) {
1254 if (highlightEnable) {
1256 Rectangle r = modelToView(getCaretPosition());
1257 if (highlightColor != null) {
1258 g.setColor(highlightColor);
1259 g.fillRect(0, r.y, getWidth(), r.height);
1261 if (highlightContourColor != null) {
1262 g.setColor(highlightContourColor);
1263 g.drawLine(0, r.y - 1, getWidth(), r.y - 1);
1264 g.drawLine(0, r.y + r.height, getWidth(), r.y + r.height);
1267 } catch (BadLocationException e) { }
1272 * @return the current TrailingWhiteManager
1274 public TrailingWhiteManager getTrailingWhiteManager() {
1275 return trailingWhite;
1279 * @return the current IndentManager
1281 public IndentManager getIndentManager() {
1286 * @return the current TabManager
1288 public TabManager getTabManager() {
1293 * @return the current CommentManager
1295 public CommentManager getCommentManager() {
1300 * Get a keyword at a position in the document.
1301 * @param position in the document
1302 * @return the KeywordEvent containing infos about keyword.
1304 public KeywordEvent getKeywordEvent(int position) {
1305 int tok = lexer.getKeyword(position, true);
1306 return new KeywordEvent(this, null, tok, lexer.start + lexer.yychar(), lexer.yylength());
1310 * Get a keyword at the current position in the document.
1311 * @return the KeywordEvent containing infos about keyword.
1313 public KeywordEvent getKeywordEvent() {
1314 return getKeywordEvent(getCaretPosition());
1318 * Get a keyword at the current position in the document.
1319 * @param caret if true the position is the current caret position in the doc else
1320 * the position is the mouse pointer position projected in the document.
1321 * @param strict if true the char just after the caret is ignored
1322 * @return the KeywordEvent containing infos about keyword.
1324 public KeywordEvent getKeywordEvent(boolean caret, boolean strict) {
1327 tok = lexer.getKeyword(getCaretPosition(), strict);
1329 tok = lexer.getKeyword(viewToModel(mousePoint), strict);
1331 return new KeywordEvent(this, null, tok, lexer.start + lexer.yychar(), lexer.yylength());
1335 * Get an helpable keyword at the current position in the document.
1336 * @param caret if true the position is the current caret position in the doc else
1337 * the position is the mouse pointer position projected in the document.
1338 * @return the helpable keyword.
1340 public String getHelpableKeyword(boolean caret) {
1344 pos = getCaretPosition();
1346 pos = viewToModel(mousePoint);
1349 tok = lexer.getKeyword(pos, true);
1350 if (!ScilabLexerConstants.isHelpable(tok)) {
1351 tok = lexer.getKeyword(pos + 1, true);
1354 if (ScilabLexerConstants.isHelpable(tok)) {
1356 return getDocument().getText(lexer.start + lexer.yychar(), lexer.yylength());
1357 } catch (BadLocationException e) { }
1364 * Prevents the different KeywordListener that a MouseEvent occurred
1365 * @param position of the mouse
1366 * @param ev the event which occurred
1367 * @param type of the event : KeywordListener.ONMOUSECLICKED or KeywordListener.ONMOUSEOVER
1369 protected void preventConcernedKeywordListener(int position, EventObject ev, int type) {
1370 KeywordEvent kev = null;
1371 Object[] listeners = kwListeners.getListenerList();
1372 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1373 if (listeners[i] == KeywordListener.class && type == ((KeywordListener) listeners[i + 1]).getType()) {
1375 int tok = lexer.getKeyword(position, true);
1376 kev = new KeywordEvent(this, ev, tok, lexer.start + lexer.yychar(), lexer.yylength());
1379 ((KeywordListener) listeners[i + 1]).caughtKeyword(kev);
1385 * Implements mouseClicked in MouseListener
1388 public void mouseClicked(MouseEvent e) {
1389 preventConcernedKeywordListener(getCaretPosition(), e, KeywordListener.ONMOUSECLICKED);
1393 * Implements mouseEntered in MouseListener
1396 public void mouseEntered(MouseEvent e) {
1397 this.mousePoint = e.getPoint();
1401 * Implements mouseExited in MouseListener
1404 public void mouseExited(MouseEvent e) { }
1407 * Implements mousePressed in MouseListener
1410 public void mousePressed(MouseEvent e) {
1412 removeHighlightForLine();
1415 removeHighlightedWords();
1417 if (highlightEnable) {
1423 * Implements mouseReleseaed in MouseListener
1426 public void mouseReleased(MouseEvent e) { }
1429 * Implements mouseMoved in MouseMotionListener
1432 public void mouseMoved(MouseEvent e) {
1433 this.mousePoint = e.getPoint();
1434 preventConcernedKeywordListener(viewToModel(mousePoint), e, KeywordListener.ONMOUSEOVER);
1438 * Implements mouseDragged in MouseMotionListener
1441 public void mouseDragged(MouseEvent e) {
1443 removeHighlightForLine();
1446 removeHighlightedWords();
1448 if (highlightEnable) {
1454 * @return the current mouse poisition in this pane
1456 public Point getMousePoint() {
1461 * @param pane the EditorPane associated with this EditorPane in a splitted view
1463 public void setOtherPaneInSplit(ScilabEditorPane pane) {
1465 rightTextPane.rightTextPane = null;
1467 rightTextPane = pane;
1471 * @return the EditorPane associated with this EditorPane in a splitted view
1473 public ScilabEditorPane getOtherPaneInSplit() {
1474 return rightTextPane;
1477 public ScilabEditorPane getCurrent() {
1478 if (focused == rightTextPane) {
1479 return rightTextPane;
1487 * @return the scrollPane associated with this EditorPane
1489 public ScilabScrollPane getScrollPane() {
1490 return (ScilabScrollPane) SwingUtilities.getAncestorOfClass(ScilabScrollPane.class, this);
1494 * @return the current focused editorPane
1496 public static ScilabEditorPane getFocusedPane() {
1503 public static void clean() {
1510 public String toString() {
1515 * @return true if something has been copied
1517 public boolean copyColumnSelectionInClipBoard() {
1518 return ((SciNotesCaret) getCaret()).copyPositionsInClipboard();
1522 * @return true if something has been removed
1524 public boolean removeColumnSelection() {
1525 return ((SciNotesCaret) getCaret()).removePositions();
1531 public String getSelectedText() {
1532 if (getCaret() instanceof SciNotesCaret) {
1533 String str = ((SciNotesCaret) getCaret()).getSelectedText();
1535 return super.getSelectedText();
1547 public int getCaretPosition() {
1548 if (getCaret() != null) {
1549 return super.getCaretPosition();
1556 * @param pos the position int the text
1557 * @return null if no column selection on the same line and an array of
1558 * integer (of size 2) containing the position of the selection
1560 public int[] isNearColumnSelection(int pos) {
1561 if (!(getCaret() instanceof SciNotesCaret) || ((SciNotesCaret) getCaret()).isEmptySelection()) {
1565 Element root = getDocument().getDefaultRootElement();
1566 int[][] positions = ((SciNotesCaret) getCaret()).getSelectedPositions();
1567 int line = root.getElementIndex(pos);
1568 int min = root.getElementIndex(positions[0][0]);
1569 int max = root.getElementIndex(positions[positions.length - 1][0]);
1571 if (line >= min && line <= max) {
1572 return positions[line - min];
1581 public void setCaret(Caret c) {
1582 if (!(c instanceof ScilabCaret)) {
1583 final Caret caret = new SciNotesCaret(this);
1584 setCaretColor(getCaretColor());
1585 SwingUtilities.invokeLater(new Runnable() {
1587 int blinkRate = 500;
1588 Object o = UIManager.get("TextComponent.caretBlinkRate");
1589 if ((o != null) && (o instanceof Integer)) {
1590 Integer rate = (Integer) o;
1591 blinkRate = rate.intValue();
1593 caret.setBlinkRate(blinkRate);
1594 caret.setVisible(true);
1597 super.setCaret(caret);
1606 public void select(int start, int end) {
1607 removeHighlightOnPosition(start);
1608 int docLength = getDocument().getLength();
1609 if (start > docLength) {
1612 if (end > docLength) {
1615 super.setCaretPosition(start);
1616 super.moveCaretPosition(end);
1620 * Remove the highlight putted to show the line (for editor('foo',123))
1622 private void removeHighlightForLine() {
1623 highlightContourColor = saveHighlightContourColor;
1624 highlightColor = saveHighlightColor;
1625 enableHighlightedLine(false);
1626 if (saveHighlightEnable) {
1627 enableHighlightedLine(true);
1629 hasBeenSaved = false;
1633 * Remove the highlight at position start
1634 * @param start the beginning of the highlight
1636 public void removeHighlightOnPosition(int start) {
1637 Object h = highlightedWords.get(start);
1639 getHighlighter().removeHighlight(h);
1640 highlightedWords.remove(h);
1645 * Highlight a word in this textpane.
1646 * @param word the word to highlight
1647 * @param exact if true the search is case sensitive
1649 public void highlightWords(String word, boolean exact) {
1650 if (word != null && word.length() != 0) {
1651 highlightWords(SearchManager.generatePattern(word, exact, false, false), false);
1656 * Highlight a word according to a pattern in this textpane.
1657 * @param pattern the pattern to highlight
1658 * @param centered, if true the pane is centered on the first occurence
1660 public void highlightWords(Pattern pattern, boolean centered) {
1661 highlightWords(-1, pattern, centered);
1665 * Highlight a word according to a pattern in this textpane.
1666 * @param pattern the pattern to highlight
1667 * @param centered, if true the pane is centered on the first occurence
1669 public void highlightWords(int tok, Pattern pattern, boolean centered) {
1670 if (pattern != null) {
1671 removeHighlightedWords();
1673 String text = ((ScilabDocument) getDocument()).getText();
1674 Matcher matcher = pattern.matcher(text);
1676 Highlighter highlighter = getHighlighter();
1677 List<Integer[]> positions = SearchManager.findToken((ScilabDocument) getDocument(), tok, lexer, pattern);
1679 if (positions != null) {
1680 List<Rectangle> marks = new ArrayList<Rectangle>();
1682 for (Integer[] position : positions) {
1684 highlightedWords.put(position[0], highlighter.addHighlight(position[0], position[1], HIGHLIGHTER));
1685 Rectangle r = modelToView(position[0]);
1686 if (r != null && (marks.size() == 0 || marks.get(marks.size() - 1).y != r.y)) {
1689 } catch (BadLocationException e) { }
1692 ScilabScrollPane ssp = getScrollPane();
1694 ssp.putMarks(marks);
1697 if (centered && positions.size() != 0) {
1698 scrollTextToPos(positions.get(0)[0], false, true);
1705 * Remove all the highlighted words
1707 public void removeHighlightedWords() {
1708 Highlighter highlighter = getHighlighter();
1709 for (Object obj : highlightedWords.values()) {
1710 highlighter.removeHighlight(obj);
1712 highlightedWords.clear();
1713 ScilabScrollPane ssp = getScrollPane();
1720 * Initialize the pane when the document is loaded
1721 * @param doc used with this pane
1723 private void initialize(ScilabDocument doc) {
1724 indent = new IndentManager(doc);
1725 tab = new TabManager(doc, indent);
1726 tab.setDefaultTabulation();
1727 com = new CommentManager(doc);
1728 trailingWhite = new TrailingWhiteManager(doc);
1730 if (matchingKeywordsEnable || matchingOpenersEnable) {
1731 matchLR = new MatchingBlockManager(doc, this, true, getHighlighter());
1732 matchLR.setDefaults();
1733 matchRL = new MatchingBlockManager(doc, this, false, getHighlighter());
1734 matchRL.setDefaults();
1737 lexer = doc.createLexer();
1738 xln = new SciNotesLineNumberPanel(this);
1740 /* The order of the next two lines is important: the doc
1741 as listener will be called before xln so the resetTypeWhenBroken
1742 will be called before ! */
1743 doc.addDocumentListener(xln);
1744 doc.addDocumentListener(doc);
1746 getScrollPane().setRowHeaderView(xln);
1747 doc.setEditorPane(this);
1749 if (editor != null) {
1750 NavigatorWindow nav = editor.getNavigator();
1752 nav.addEditorPane(this);