4f0ce4645d64bf106d4bfd668a7883002023bcbd
[scilab.git] / scilab / modules / scinotes / src / java / org / scilab / modules / scinotes / ScilabEditorPane.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - 2011 - Calixte DENIZET
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.1-en.txt
10  *
11  */
12
13 package org.scilab.modules.scinotes;
14
15 import java.awt.Color;
16 import java.awt.Cursor;
17 import java.awt.Font;
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;
33 import java.io.File;
34 import java.util.ArrayList;
35 import java.util.EventObject;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.UUID;
40 import java.util.Vector;
41 import java.util.regex.Matcher;
42 import java.util.regex.Pattern;
43
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;
62
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;
78
79 /**
80  * Class ScilabEditorPane
81  * @author Calixte DENIZET
82  *
83  */
84 public class ScilabEditorPane extends JEditorPane implements Highlighter.HighlightPainter,
85     CaretListener, MouseListener,
86     MouseMotionListener, Cloneable,
87     KeyListener {
88
89     private static final long serialVersionUID = 4322071415211939097L;
90
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);
95
96     private static ScilabEditorPane focused;
97
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 = "";
117
118     private Timer selectionTimer;
119     private Timer matcherTimer;
120
121     private Point mousePoint;
122
123     private long lastModified;
124     private long lastExternalModification;
125
126     /* matchLR matches Left to Right ... */
127     private MatchingBlockManager matchLR;
128     private MatchingBlockManager matchRL;
129
130     private boolean suppressCom = true;
131
132     private SciNotesLineNumberPanel xln;
133     private ScilabEditorPane rightTextPane;
134     private UUID uuid;
135
136     private EditorComponent edComponent;
137
138     private boolean hand;
139     private boolean infoBarChanged;
140     private boolean ctrlHit;
141
142     private Color saveHighlightContourColor;
143     private Color saveHighlightColor;
144     private boolean hasBeenSaved;
145     private boolean saveHighlightEnable;
146
147     private EventListenerList kwListeners = new EventListenerList();
148     private Map<Integer, Object> highlightedWords = new HashMap<Integer, Object>();
149
150     //private List<Object> highlightedWords = new ArrayList<Object>();
151     //private List<Integer> highlightedWordsBegin = new ArrayList<Integer>();
152
153     /**
154      * Constructor
155      * @param editor which uses this pane
156      */
157     public ScilabEditorPane(SciNotes editor) {
158         super();
159         if (focused == null) {
160             focused = this;
161         }
162         this.editor = editor;
163         this.uuid = UUID.randomUUID();
164         edComponent = new EditorComponent(this);
165
166         /*
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).
170         */
171         setFocusCycleRoot(false);
172
173         addCaretListener(this);
174         addMouseMotionListener(this);
175         addMouseListener(this);
176         enableMatchingKeywords(SciNotesOptions.getSciNotesDisplay().highlightKeywords);
177         enableMatchingOpeners(SciNotesOptions.getSciNotesDisplay().highlightBrackets);
178         setFocusable(true);
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();
184                     if (nav != null) {
185                         nav.update((ScilabDocument) getDocument());
186                     }
187                 }
188                 setMustAdjustVisibility(true);
189             }
190
191             public void focusLost(FocusEvent e) {
192                 ((ScilabDocument) getDocument()).setFocused(false);
193                 if (e.getOppositeComponent() == getOtherPaneInSplit()) {
194                     setMustAdjustVisibility(false);
195                 }
196             }
197         });
198
199         addKeywordListener(new KeywordAdapter.MouseOverAdapter() {
200             public void caughtKeyword(KeywordEvent e) {
201                 if (ScilabLexerConstants.isClickable(e.getType())) {
202                     if (ctrlHit) {
203                         setCursor(HANDCURSOR);
204                         hand = true;
205                         try {
206                             String url = ((ScilabDocument) getDocument()).getText(e.getStart(), e.getLength());
207                             if (ScilabLexerConstants.isClickable(e.getType())) {
208                                 String text;
209                                 switch (e.getType()) {
210                                     case ScilabLexerConstants.URL :
211                                         text = SciNotesMessages.OPENURL;
212                                         break;
213                                     case ScilabLexerConstants.MAIL :
214                                         text = SciNotesMessages.MAILTO;
215                                         break;
216                                     case ScilabLexerConstants.MACROS :
217                                         text = SciNotesMessages.OPENSOURCE;
218                                         break;
219                                     case ScilabLexerConstants.MACROINFILE :
220                                         text = SciNotesMessages.SHOWSOURCE;
221                                         break;
222                                     default :
223                                         text = null;
224                                 }
225
226                                 if (text != null && ScilabEditorPane.this.editor != null) {
227                                     ScilabEditorPane.this.editor.getInfoBar().setText(text + url);
228                                     infoBarChanged = true;
229                                 }
230                             }
231                         } catch (BadLocationException ex) { }
232                     } else if (ScilabEditorPane.this.editor != null) {
233                         ScilabEditorPane.this.editor.getInfoBar().setText(SciNotesMessages.CLICKABLE_URL);
234                         infoBarChanged = true;
235                         if (hand) {
236                             setCursor(TEXTCURSOR);
237                             hand = false;
238                         }
239                     }
240                 } else {
241                     if (hand) {
242                         setCursor(TEXTCURSOR);
243                         hand = false;
244                     }
245                     if (infoBarChanged && ScilabEditorPane.this.editor != null) {
246                         ScilabEditorPane.this.editor.getInfoBar().setText(infoBar);
247                         infoBarChanged = false;
248                     }
249                     if (ScilabLexerConstants.isLaTeX(e.getType())) {
250                         try {
251                             int start = e.getStart();
252                             int end = start + e.getLength();
253                             String exp = ((ScilabDocument) getDocument()).getText(start, e.getLength());
254                             ScilabScrollPane ssp = getScrollPane();
255                             int height;
256                             if (ssp != null) {
257                                 height = ssp.getHeight() + ssp.getVerticalScrollBar().getValue();
258                             } else {
259                                 height = edComponent.getHeight();
260                             }
261                             ScilabLaTeXViewer.displayExpression(ScilabEditorPane.this, height, exp, start, end);
262                         } catch (BadLocationException ex) { }
263                     } else {
264                         ScilabLaTeXViewer.removeLaTeXViewer(ScilabEditorPane.this);
265                     }
266                 }
267             }
268         });
269
270         addKeywordListener(new KeywordAdapter.MouseClickedAdapter() {
271             public void caughtKeyword(KeywordEvent e) {
272                 if (ctrlHit && ScilabLexerConstants.isClickable(e.getType())) {
273                     try {
274                         hand = false;
275                         ctrlHit = false;
276                         infoBarChanged = false;
277                         setCursor(TEXTCURSOR);
278                         if (ScilabEditorPane.this.editor != null) {
279                             ScilabEditorPane.this.editor.getInfoBar().setText(infoBar);
280                         }
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);
286                         }
287                     } catch (BadLocationException ex) { }
288                 }
289             }
290         });
291
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);
297                     e.consume();
298                 }
299             }
300         });
301
302         addKeyListener(this);
303         setTransferHandler(new CopyAsHTMLAction.HTMLTransferHandler());
304
305         ((ScilabCaret) getCaret()).setMustAdjustVisibility(false);
306     }
307
308     public void enableColorization(boolean b) {
309         View view = ((ScilabDocument) getDocument()).getView();
310         if (view != null) {
311             if (view instanceof ScilabView) {
312                 ((ScilabView) view).enable(b);
313             } else {
314                 ((ScilabPlainView) view).enable(b);
315             }
316         }
317     }
318
319     public void configurationChanged(SciNotesConfiguration.Conf conf) {
320         ((ScilabEditorKit) getEditorKit()).getStylePreferences().configurationChanged(conf);
321
322         if (conf.font) {
323             resetFont();
324         }
325
326         if (conf.display) {
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);
332
333             boolean kw = SciNotesOptions.getSciNotesDisplay().highlightKeywords;
334             boolean op = SciNotesOptions.getSciNotesDisplay().highlightBrackets;
335
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);
343                 return;
344             }
345
346             if ((kw || op) && (matchingKeywordsEnable || matchingOpenersEnable)) {
347                 matchLR.configurationChanged(conf);
348                 matchRL.configurationChanged(conf);
349                 return;
350             }
351
352             if ((!kw && !op) && (matchingKeywordsEnable || matchingOpenersEnable)) {
353                 matchLR.desactivateMouseOver();
354                 matchLR = null;
355                 matchRL.desactivateMouseOver();
356                 matchRL = null;
357                 return;
358             }
359         }
360     }
361
362     /**
363      * @return the lexer
364      */
365     public ScilabLexer getLexer() {
366         return lexer;
367     }
368
369     /**
370      * {@inheritDoc}
371      * When no split and in wrapped view , this method return true and the consequence is
372      * that there is no horizontal scrollbar.
373      */
374     public boolean getScrollableTracksViewportWidth() {
375         return ((ScilabDocument) getDocument()).getView() instanceof ScilabView && !edComponent.isSplited();
376     }
377
378     /**
379      * @return true if the pane is in OverWrite mode (insert)
380      */
381     public boolean getOverwriteMode() {
382         return this.overwriteMode;
383     }
384
385     /**
386      * @param overwriteMode true if the pane is in OverWrite mode (insert)
387      */
388     public void setOverwriteMode(boolean overwriteMode) {
389         this.overwriteMode = overwriteMode;
390         ((ScilabCaret) getCaret()).setOverwriteMode(overwriteMode);
391     }
392
393     /**
394      * {@inheritDoc}
395      */
396     public void replaceSelection(String content) {
397         if (overwriteMode && getSelectionStart() == getSelectionEnd()) {
398             int pos = getCaretPosition();
399             select(pos, pos + content.length());
400         }
401
402         if (((SciNotesCaret) getCaret()).isEmptySelection()) {
403             super.replaceSelection(content);
404         } else {
405             SciNotesCaret caret = (SciNotesCaret) getCaret();
406             int[][] pos = caret.getSelectedPositions();
407             List<Object> sels = caret.getSelections();
408             int len = content.length();
409             int res = 0;
410             int sres;
411             ScilabDocument doc = (ScilabDocument) getDocument();
412             doc.getUndoManager().enableOneShot(true);
413             doc.mergeEditsBegin();
414             caret.protectHighlights(true);
415             for (int i = 0; i < pos.length; i++) {
416                 if (sels.get(i) != null) {
417                     sres = pos[i][0] + res;
418                     try {
419                         doc.replace(sres, pos[i][1] - pos[i][0], content, null);
420                     } catch (BadLocationException e) { }
421                     res = sres + len - pos[i][1];
422                     pos[i][0] = sres + len;
423                     pos[i][1] = sres + len;
424                 }
425             }
426             doc.mergeEditsEnd();
427             doc.getUndoManager().enableOneShot(false);
428             caret.protectHighlights(false);
429             caret.updateHighlights();
430         }
431     }
432
433     /**
434      * Nothing !
435      * @param e the event
436      */
437     public void keyPressed(KeyEvent e) {
438         // Workaround for bug 7238
439         if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD
440                 && e.getKeyCode() == KeyEvent.VK_DELETE
441                 && e.getKeyChar() != KeyEvent.VK_DELETE) {
442             e.setKeyCode(KeyEvent.VK_DECIMAL);
443             ctrlHit = false;
444         } else if (mousePoint != null && e.getKeyCode() == KeyEvent.VK_CONTROL) {
445             ctrlHit = true;
446             preventConcernedKeywordListener(viewToModel(mousePoint), e, KeywordListener.ONMOUSEOVER);
447         } else {
448             ctrlHit = false;
449         }
450     }
451
452     /**
453      * Nothing !
454      * @param e the event
455      */
456     public void keyReleased(KeyEvent e) {
457         if (mousePoint != null && e.getKeyCode() == KeyEvent.VK_CONTROL) {
458             ctrlHit = false;
459             preventConcernedKeywordListener(viewToModel(mousePoint), e, KeywordListener.ONMOUSEOVER);
460         }
461     }
462
463     /**
464      * Nothing !
465      * @param e the event
466      */
467     public void keyTyped(KeyEvent e) { }
468
469     /**
470      * @overload #setDocument
471      * @param doc to set
472      */
473     public void setDocument(Document doc) {
474         super.setDocument(doc);
475         if (doc instanceof ScilabDocument) {
476             ((ScilabDocument) doc).getUndoManager().discardAllEdits();
477             initialize((ScilabDocument) doc);
478         }
479     }
480
481     /**
482      * {@inheritDoc}
483      */
484     public void setName(String name) {
485         setNameInSuper(name);
486         setShortNameAndTitle(name);
487         ScilabEditorPane pane = getOtherPaneInSplit();
488         if (pane != null) {
489             // I don't call pane.setName since we will enter in an infinite loop
490             pane.setNameInSuper(name);
491             pane.setShortNameAndTitle(name);
492         }
493     }
494
495     /**
496      * @param name the name
497      */
498     private void setNameInSuper(String name) {
499         super.setName(name);
500     }
501
502     /**
503      * @param name the name
504      */
505     private void setShortNameAndTitle(String name) {
506         if (name != null) {
507             File f = new File(name);
508             setShortName(f.getName());
509             title =  shortName + " (" + f.getAbsolutePath() + ")" + TIRET + SciNotesMessages.SCILAB_EDITOR;
510         }
511     }
512
513     /**
514      * @param title the title
515      */
516     public void setTitle(String title) {
517         this.title = title + TIRET + SciNotesMessages.SCILAB_EDITOR;
518     }
519
520     /**
521      * @return the title
522      */
523     public String getTitle() {
524         return title;
525     }
526
527     /**
528      * @param name the short name
529      */
530     public void setShortName(String name) {
531         this.shortName = name;
532     }
533
534     /**
535      * @return the short name of the file (without the full path)
536      */
537     public String getShortName() {
538         return shortName;
539     }
540
541     /**
542      * Init the pane
543      */
544     public void init(final int pos) {
545         SwingUtilities.invokeLater(new Runnable() {
546             public void run() {
547                 requestFocus();
548                 if (getCaret() != null) {
549                     setCaretPosition(pos);
550                 }
551             }
552         });
553     }
554
555     /**
556      * Close this pane
557      */
558     public void close() {
559         FocusListener[] l = getFocusListeners();
560         for (int i = 0; i < l.length; i++) {
561             removeFocusListener(l[i]);
562         }
563         if (getCaret() instanceof SciNotesCaret) {
564             ((SciNotesCaret) getCaret()).clean();
565             super.setCaret(null);
566             if (selectionTimer != null && selectionTimer.isRunning()) {
567                 selectionTimer.stop();
568                 selectionTimer = null;
569             }
570             if (matcherTimer != null && matcherTimer.isRunning()) {
571                 matcherTimer.stop();
572                 matcherTimer = null;
573             }
574         }
575     }
576
577     /**
578      * Update infos
579      */
580     public void updateInfosWhenFocused() {
581         ScilabDocument doc = (ScilabDocument) getDocument();
582         doc.setFocused(true);
583         SciNotes.setEditor(editor);
584         focused = this;
585         doc.getUndoManager().enableUndoRedoButtons();
586
587         if (editor != null) {
588             if (checkExternalModif() && lastExternalModification < getLastModification()) {
589                 editor.getInfoBar().setText(SciNotesMessages.EXTERNAL_MODIFICATION_INFO);
590                 if (ScilabModalDialog.show(editor, String.format(SciNotesMessages.ASK_TO_RELOAD, getShortName()), SciNotesMessages.RELOAD, IconType.QUESTION_ICON, ButtonType.YES_NO) == AnswerOption.YES_OPTION) {
591                     editor.reload(getEditor().getTabPane().indexOfComponent(getEditorComponent()));
592                     editor.getTextPane().updateInfosWhenFocused();
593                 }
594                 lastExternalModification = getLastModification();
595             } else {
596                 editor.getInfoBar().setText(getInfoBarText());
597             }
598         }
599     }
600
601     /**
602      * @return true if an external modif occurred
603      */
604     public boolean checkExternalModif() {
605         String path = getName();
606         if (path != null) {
607             File f = new File(path);
608             if (f != null && f.exists()) {
609                 return lastModified < f.lastModified();
610             } else {
611                 return true;
612             }
613         }
614         return false;
615     }
616
617     /**
618      * Get last modification on file
619      * @return the lastModified value
620      */
621     public long getLastModification() {
622         String path = getName();
623         if (path != null) {
624             File f = new File(path);
625             if (f != null && f.exists()) {
626                 return f.lastModified();
627             }
628         }
629
630         return -1;
631     }
632
633     /**
634      * @return the String which must be displayed in the infobar
635      */
636     public String getInfoBarText() {
637         return infoBar;
638     }
639
640     /**
641      * @param text String which must be displayed in the infobar
642      */
643     public void setInfoBarText(String text) {
644         this.infoBar = text;
645         if (editor != null) {
646             editor.getInfoBar().setText(getInfoBarText());
647         }
648     }
649
650     /**
651      * @param readonly true to set Read-Only mode
652      */
653     public void setReadOnly(boolean readonly) {
654         this.readonly = readonly;
655         setEditable(!readonly);
656         setDragEnabled(!readonly);
657         if (readonly) {
658             infoBar = SciNotesMessages.READ_ONLY_MODE;
659         } else {
660             infoBar = "";
661         }
662     }
663
664     /**
665      * @param binary true to set binary mode
666      */
667     public void setBinary(boolean binary) {
668         this.binary = binary;
669         setEditable(!binary);
670         setDragEnabled(!binary);
671         if (binary) {
672             infoBar = SciNotesMessages.BINARY_FILE_MODE;
673             disableAll();
674         } else {
675             infoBar = "";
676         }
677     }
678
679     /**
680      * Copy the props of this textPane to pane
681      * @param pane the pane which receives the same props as this
682      */
683     public void copyProps(ScilabEditorPane pane) {
684         pane.lastModified = lastModified;
685         pane.highlightEnable = highlightEnable;
686         pane.matchingKeywordsEnable = matchingKeywordsEnable;
687         pane.matchingOpenersEnable = matchingOpenersEnable;
688         pane.suppressCom = suppressCom;
689         pane.setName(getName());
690         pane.setShortName(getShortName());
691         pane.setTitle(getTitle().substring(0, getTitle().lastIndexOf(TIRET)));
692         pane.setEditable(isEditable());
693     }
694
695     /**
696      * Get the UUID associated with the editor pane instance.
697      * @return unique identifier
698      */
699     public UUID getUUID() {
700         return uuid;
701     }
702
703     /**
704      * Get the time where the file associated with this pane
705      * has been modified.
706      * @return the last modified time or 0
707      */
708     public long getLastModified() {
709         return lastModified;
710     }
711
712     /**
713      * Set the last time where the file associated with this pane
714      * has been modified.
715      * @param time the time
716      */
717     public void setLastModified(long time) {
718         this.lastModified = time;
719         ScilabEditorPane pane = getOtherPaneInSplit();
720         if (pane != null) {
721             pane.lastModified = time;
722         }
723     }
724
725     /**
726      * @return the SciNotesLineNumberPanel used with this pane
727      */
728     public SciNotesLineNumberPanel getXln() {
729         return xln;
730     }
731
732     /**
733      * @return the editor
734      */
735     public SciNotes getEditor() {
736         return editor;
737     }
738
739     /**
740      * @param editor to set
741      */
742     public void setEditor(SciNotes editor) {
743         this.editor = editor;
744     }
745
746     /**
747      * Disable all
748      */
749     public void disableAll() {
750         indent = null;
751         tab = null;
752         com = null;
753         trailingWhite = null;
754         editor = null;
755         lexer = null;
756         helpOnTyping = null;
757         xln = null;
758         rightTextPane = null;
759         edComponent = null;
760         enableMatchingKeywords(false);
761         enableMatchingOpeners(false);
762         if (matchLR != null) {
763             matchLR.desactivateMouseOver();
764             matchLR = null;
765         }
766         if (matchRL != null) {
767             matchRL.desactivateMouseOver();
768             matchRL = null;
769         }
770         kwListeners = null;
771     }
772
773     /**
774      * Destroy this component
775      */
776     public void destroy() {
777         close();
778         disableAll();
779     }
780
781     /**
782      * @return the scrollPane or the splitpane associated with this textPane
783      */
784     public EditorComponent getEditorComponent() {
785         return edComponent;
786     }
787
788     /**
789      * @return the scrollPane or the splitpane associated with this textPane
790      */
791     public void setEditorComponent(EditorComponent ed) {
792         this.edComponent = ed;
793     }
794
795     /**
796      * @param split the split used
797      */
798     public void setSplitPane(JSplitPane split) {
799         edComponent.setSplitPane(split);
800     }
801
802     /**
803      * @param split the split used
804      */
805     public JSplitPane getSplitPane() {
806         return edComponent.getSplitPane();
807     }
808
809     /**
810      * Update the title of current tab
811      */
812     public void updateTitle() {
813         if (editor != null) {
814             editor.updateTabTitle();
815         }
816     }
817
818     public void setMustAdjustVisibility(boolean mustAdjustVisibility) {
819         ((ScilabCaret) getCaret()).setMustAdjustVisibility(mustAdjustVisibility);
820     }
821
822     public boolean getMustAdjustVisibility() {
823         return ((ScilabCaret) getCaret()).getMustAdjustVisibility();
824     }
825
826     /**
827      * Scroll the pane to have the line containing pos on the top of the pane
828      * @param pos the position in the document
829      */
830     public void scrollTextToPos(int pos) {
831         scrollTextToPos(pos, true, false);
832     }
833
834     /**
835      * Scroll the pane to have the line containing pos on the top or centered on the pane
836      * @param pos the position in the document
837      * @param setCaret, if true the caret is set at the given position
838      * @param centered, if true the line is centered
839      */
840     public void scrollTextToPos(final int pos, final boolean setCaret, final boolean centered) {
841         SwingUtilities.invokeLater(new Runnable() {
842             public void run() {
843                 try {
844                     if (setCaret) {
845                         setCaretPosition(pos);
846                     }
847                     JScrollBar scrollbar = getScrollPane().getVerticalScrollBar();
848                     Rectangle rect = modelToView(pos);
849                     if (centered) {
850                         int value = scrollbar.getValue();
851                         int h = scrollbar.getHeight();
852                         if (rect.y < value || rect.y > value + h) {
853                             scrollbar.setValue(Math.max(0, rect.y - h / 2));
854                         }
855                     } else {
856                         if (rect.y > scrollbar.getMaximum()) {
857                             scrollbar.setMaximum(rect.y);
858                         }
859                         scrollbar.setValue(rect.y);
860                     }
861                 } catch (BadLocationException e) { }
862             }
863         });
864     }
865
866     /**
867      * Scroll the pane to have the line lineNumber on the top of the pane
868      * @param lineNumber the number of the line
869      * @param highlight true to highlight the line
870      */
871     public void scrollTextToLineNumber(int lineNumber, boolean highlight) {
872         scrollTextToLineNumber(lineNumber, highlight, true, false);
873     }
874
875     /**
876      * Scroll the pane to have the line lineNumber on the top or centered on the pane
877      * @param lineNumber the number of the line
878      * @param highlight true to highlight the line
879      * @param setCaret, if true the caret is set at the given line
880      * @param centered, if true the line is centered
881      */
882     public void scrollTextToLineNumber(int lineNumber, final boolean highlight, final boolean setCaret, final boolean centered) {
883         Element root = getDocument().getDefaultRootElement();
884         if (lineNumber >= 1 && lineNumber <= root.getElementCount()) {
885             final int pos = root.getElement(lineNumber - 1).getStartOffset();
886             SwingUtilities.invokeLater(new Runnable() {
887                 public void run() {
888                     scrollTextToPos(pos, setCaret, centered);
889                     if (highlight) {
890                         saveHighlightContourColor = highlightContourColor;
891                         highlightContourColor = null;
892                         saveHighlightColor = highlightColor;
893                         highlightColor = Color.YELLOW;
894                         saveHighlightEnable = highlightEnable;
895                         hasBeenSaved = true;
896                         enableHighlightedLine(false);
897                         enableHighlightedLine(true);
898                     }
899                 }
900             });
901         }
902     }
903
904     /**
905      * Scroll the pane to have the line lineNumber on the top of the pane in whereami mode
906      * The line number is computed regarding the function named funname.
907      * @param lineNumber the number of the line
908      * @param funname the function name
909      * @param highlight true to highlight the line
910      */
911     public void scrollTextToLineNumberInWhereami(int lineNumber, String funname, boolean highlight) {
912         scrollTextToLineNumberInWhereami(lineNumber, funname, highlight, true, false);
913     }
914
915     /**
916      * Scroll the pane to have the line lineNumber on the top of the pane in whereami mode
917      * The line number is computed regarding the function named funname.
918      * @param lineNumber the number of the line
919      * @param funname the function name
920      * @param highlight true to highlight the line
921      * @param setCaret, if true the caret is set at the given line
922      * @param centered, if true the line is centered
923      */
924     public void scrollTextToLineNumberInWhereami(int lineNumber, String funname, boolean highlight, boolean setCaret, boolean centered) {
925         if (funname != null) {
926             Element root = getDocument().getDefaultRootElement();
927             int nlines = root.getElementCount();
928             ScilabDocument.ScilabLeafElement elem;
929             for (int i = 0; i < nlines; i++) {
930                 elem = (ScilabDocument.ScilabLeafElement) root.getElement(i);
931                 if (elem.getFunctionName().equals(funname)) {
932                     lineNumber += i;
933                     break;
934                 }
935             }
936         }
937         scrollTextToLineNumber(lineNumber, highlight, setCaret, centered);
938     }
939
940     /**
941      * @return the width of a white
942      */
943     public int getWhiteWidth() {
944         View view = ((ScilabDocument) getDocument()).getView();
945         if (view != null) {
946             if (view instanceof ScilabView) {
947                 return ((ScilabView) view).getWhiteWidth();
948             } else {
949                 return ((ScilabPlainView) view).getWhiteWidth();
950             }
951         }
952
953         return 0;
954     }
955
956     public void resetFont() {
957         Font font = ((ScilabEditorKit) getEditorKit()).getStylePreferences().getBaseFont();
958         setFont(font);
959         xln.updateFont(font);
960     }
961
962     /**
963      * Set a new font
964      * @param keyword the type of keyword
965      * @param type an int : -2 to reset italic, -1 to reset bold, 1 to set bold and 2 to set italic
966      */
967     public void resetFont(String keyword, int type) {
968         ((ScilabEditorKit) getEditorKit()).getStylePreferences().genFont(keyword, type);
969     }
970
971     /**
972      * Set a new attribute
973      * @param keyword the type of keyword
974      * @param type an int : 0 nothing, 1 underline, 2 stroke, 3 underline and stroke
975      */
976     public void resetAttribute(String keyword, int type) {
977         ((ScilabEditorKit) getEditorKit()).getStylePreferences().genAttribute(keyword, type);
978     }
979
980     /**
981      * Set a new color
982      * @param keyword the kind of the keyword
983      * @param color the color
984      */
985     public void resetColor(String keyword, Color color) {
986         ((ScilabEditorKit) getEditorKit()).getStylePreferences().genColors(keyword, color);
987     }
988
989     /**
990      * Set to true if the comments must be suppressed when the code is executing in the console
991      * @param b boolean
992      */
993     public void suppressCommentsInExecutingCode(boolean b) {
994         suppressCom = b;
995     }
996
997     /**
998      * Execute the code in the console, the code is the selected text if exists
999      * or the text from beginning to actual position of the caret
1000      * Comments are removed if suppressCom is set to true
1001      * @return the code to be executed in the console.
1002      */
1003     public String getCodeToExecute() {
1004         String selection;
1005         int start, end;
1006         start = getSelectionStart();
1007         end = getSelectionEnd();
1008
1009         if (((SciNotesCaret) getCaret()).isEmptySelection()) {
1010             try {
1011                 if (start == end) {
1012                     selection = getDocument().getText(0, start);
1013                     start = 0;
1014                 } else {
1015                     selection = getSelectedText();
1016                 }
1017             } catch (BadLocationException e) {
1018                 selection = "";
1019             }
1020         } else {
1021             selection = getSelectedText();
1022         }
1023
1024         if (suppressCom) {
1025             StringBuffer buf = new StringBuffer(selection.length());
1026             ScilabLexer.ScilabTokens tokens = ScilabLexer.getScilabTokens(selection);
1027             List<Integer> tokType = tokens.getTokenType();
1028             List<Integer> tokPos = tokens.getTokenPos();
1029             List<String> commands = new ArrayList<String>();
1030             int prevPos = 0;
1031             for (int i = 0; i < tokType.size(); i++) {
1032                 String str = selection.substring(prevPos, tokPos.get(i));
1033                 if ("\n".equals(str)) {
1034                     commands.add(buf.toString());
1035                     buf.setLength(0);
1036                 } else if (!ScilabLexerConstants.isComment(tokType.get(i))) {
1037                     buf.append(str);
1038                 }
1039                 prevPos = tokPos.get(i);
1040             }
1041
1042             if (buf.length() != 0) {
1043                 commands.add(buf.toString());
1044             }
1045
1046             buf.setLength(0);
1047             Pattern pat = Pattern.compile("[ \t]*");
1048             for (String command : commands) {
1049                 if (!pat.matcher(command).matches()) {
1050                     buf.append(command).append("\n");
1051                 }
1052             }
1053
1054             return buf.toString();
1055         }
1056
1057         return selection;
1058     }
1059
1060     /**
1061      * Add a new KeywordListener
1062      * @param kw a KeywordListener
1063      */
1064     public void addKeywordListener(KeywordListener kw) {
1065         kwListeners.add(KeywordListener.class, kw);
1066     }
1067
1068     /**
1069      * Remove a new KeywordListener
1070      * @param kw a KeywordListener
1071      */
1072     public void removeKeywordListener(KeywordListener kw) {
1073         kwListeners.remove(KeywordListener.class, kw);
1074     }
1075
1076     /**
1077      * @return an array of KeywordListener
1078      */
1079     public KeywordListener[] getKeywordListeners() {
1080         return kwListeners.getListeners(KeywordListener.class);
1081     }
1082
1083     /**
1084      * Set a new color for the highlighting
1085      * @param c the color, can be null (useful if setHighlightedContourColor is used with a
1086      * non-null value)
1087      */
1088     public void setHighlightedLineColor(Color c) {
1089         highlightColor = c;
1090     }
1091
1092     /**
1093      * Set a new color for the contour of the highlighting
1094      * @param c the color, if null no contour is drawn
1095      */
1096     public void setHighlightedContourColor(Color c) {
1097         highlightContourColor = c;
1098     }
1099
1100     /**
1101      * Activate or desactivate the help on typing
1102      */
1103     public void activateHelpOnTyping() {
1104         boolean isActive = HelpOnTypingManager.isActive();
1105         if (isActive && helpOnTyping == null) {
1106             helpOnTyping = HelpOnTypingManager.getInstance();
1107             addKeyListener(helpOnTyping);
1108         } else if (!isActive && helpOnTyping != null) {
1109             removeKeyListener(helpOnTyping);
1110             helpOnTyping = null;
1111         }
1112     }
1113
1114     /**
1115      * Enable (active true) or disable (active false) the line-highlighting.
1116      * @param active true or false
1117      */
1118     public void enableHighlightedLine(boolean active) {
1119         if (active && !highlightEnable) {
1120             try {
1121                 highlightCL = getHighlighter().addHighlight(0, 0, this);
1122             } catch (BadLocationException e) { }
1123             highlightEnable = true;
1124         }
1125
1126         if (!active && highlightEnable) {
1127             getHighlighter().removeHighlight(highlightCL);
1128             highlightEnable = false;
1129         }
1130
1131         repaint();
1132     }
1133
1134     /**
1135      * Enable (active true) or disable (active false) the matching keywords.
1136      * @param active true or false
1137      */
1138     public void enableMatchingKeywords(boolean active) {
1139         matchingKeywordsEnable = active;
1140     }
1141
1142     /**
1143      * Enable (active true) or disable (active false) the matching keywords.
1144      * @param active true or false
1145      */
1146     public void enableMatchingOpeners(boolean active) {
1147         matchingOpenersEnable = active;
1148     }
1149
1150     /**
1151      * Get a matching manager
1152      * @param lr true if the LR matcher must be returned
1153      * @return the MatchingBlockManager
1154      */
1155     public MatchingBlockManager getMatchingBlockManager(boolean lr) {
1156         if (lr) {
1157             return matchLR;
1158         } else {
1159             return matchRL;
1160         }
1161     }
1162
1163     /**
1164      * This class listens to the caret event
1165      * @param e event
1166      */
1167     public void caretUpdate(CaretEvent e) {
1168         if (hasBeenSaved) {
1169             removeHighlightForLine();
1170         }
1171
1172         if (hasFocus()) {
1173             final String str = getSelectedText();
1174             if (str != null && str.length() != 0) {
1175                 if (selectionTimer == null) {
1176                     selectionTimer = new Timer(1000, new ActionListener() {
1177                         public void actionPerformed(ActionEvent e) {
1178                             try {
1179                                 final String str = getSelectedText();
1180                                 if (str != null && str.length() != 0) {
1181                                     int tok = lexer.getKeyword(getSelectionStart(), false);
1182                                     int s = lexer.start + lexer.yychar();
1183                                     if (ScilabLexerConstants.isSearchable(tok) && getSelectionStart() == s && getSelectionEnd() == s + lexer.yylength()) {
1184                                         highlightWords(tok, SearchManager.generatePattern(str, false, true, false), false);
1185                                     } else {
1186                                         highlightWords(str, false);
1187                                     }
1188
1189                                     if (highlightedWords.size() > 1 && editor != null && editor.getInfoBar() != null) {
1190                                         editor.getInfoBar().setText(String.format(SciNotesMessages.OCCURENCES_FOUND, Integer.toString(highlightedWords.size())));
1191                                     }
1192                                     removeHighlightOnPosition(getSelectionStart());
1193                                 }
1194                             } catch (Exception ee) { }
1195                             selectionTimer = null;
1196                         }
1197                     });
1198                     selectionTimer.setRepeats(false);
1199                     selectionTimer.start();
1200                 } else {
1201                     selectionTimer.restart();
1202                 }
1203             } else {
1204                 removeHighlightedWords();
1205             }
1206         }
1207
1208         if (highlightEnable) {
1209             repaint();
1210         }
1211
1212         if (matchingKeywordsEnable || matchingOpenersEnable) {
1213             if (matcherTimer == null) {
1214                 matcherTimer = new Timer(100, new ActionListener() {
1215                     public void actionPerformed(ActionEvent e) {
1216                         int pos = getCaretPosition();
1217                         int tok = lexer.getKeyword(pos, false);
1218                         matchLR.searchMatchingBlock(false, tok, lexer.start + lexer.yychar());
1219                         tok = lexer.getKeyword(pos, true);
1220                         matchRL.searchMatchingBlock(false, tok, lexer.start + lexer.yychar() + lexer.yylength());
1221                         matcherTimer = null;
1222                     }
1223                 });
1224                 matcherTimer.setRepeats(false);
1225                 matcherTimer.start();
1226             } else {
1227                 matcherTimer.restart();
1228                 matchLR.update();
1229                 matchRL.update();
1230             }
1231         }
1232
1233         if (!readonly && !binary && editor != null) {
1234             editor.getInfoBar().setText(((ScilabDocument) getDocument()).getCurrentFunction(getCaretPosition()));
1235         }
1236     }
1237
1238     /**
1239      * Used to paint the highlighted line
1240      * @param g graphics to use
1241      * @param p0 start
1242      * @param p1 end
1243      * @param bounds the shape representing the area
1244      * @param c this pane
1245      */
1246     public void paint(Graphics g, int p0, int p1, Shape bounds, JTextComponent c) {
1247         if (highlightEnable) {
1248             try {
1249                 Rectangle r = modelToView(getCaretPosition());
1250                 if (highlightColor != null) {
1251                     g.setColor(highlightColor);
1252                     g.fillRect(0, r.y, getWidth(), r.height);
1253                 }
1254                 if (highlightContourColor != null) {
1255                     g.setColor(highlightContourColor);
1256                     g.drawLine(0, r.y - 1, getWidth(), r.y - 1);
1257                     g.drawLine(0, r.y + r.height, getWidth(), r.y + r.height);
1258                 }
1259
1260             } catch (BadLocationException e) { }
1261         }
1262     }
1263
1264     /**
1265      * @return the current TrailingWhiteManager
1266      */
1267     public TrailingWhiteManager getTrailingWhiteManager() {
1268         return trailingWhite;
1269     }
1270
1271     /**
1272      * @return the current IndentManager
1273      */
1274     public IndentManager getIndentManager() {
1275         return indent;
1276     }
1277
1278     /**
1279      * @return the current TabManager
1280      */
1281     public TabManager getTabManager() {
1282         return tab;
1283     }
1284
1285     /**
1286      * @return the current CommentManager
1287      */
1288     public CommentManager getCommentManager() {
1289         return com;
1290     }
1291
1292     /**
1293      * Get a keyword at a position in the document.
1294      * @param position in the document
1295      * @return the KeywordEvent containing infos about keyword.
1296      */
1297     public KeywordEvent getKeywordEvent(int position) {
1298         int tok = lexer.getKeyword(position, true);
1299         return new KeywordEvent(this, null, tok, lexer.start + lexer.yychar(), lexer.yylength());
1300     }
1301
1302     /**
1303      * Get a keyword at the current position in the document.
1304      * @return the KeywordEvent containing infos about keyword.
1305      */
1306     public KeywordEvent getKeywordEvent() {
1307         return getKeywordEvent(getCaretPosition());
1308     }
1309
1310     /**
1311      * Get a keyword at the current position in the document.
1312      * @param caret if true the position is the current caret position in the doc else
1313      * the position is the mouse pointer position projected in the document.
1314      * @param strict if true the char just after the caret is ignored
1315      * @return the KeywordEvent containing infos about keyword.
1316      */
1317     public KeywordEvent getKeywordEvent(boolean caret, boolean strict) {
1318         int tok;
1319         if (caret) {
1320             tok = lexer.getKeyword(getCaretPosition(), strict);
1321         } else {
1322             tok = lexer.getKeyword(viewToModel(mousePoint), strict);
1323         }
1324         return new KeywordEvent(this, null, tok, lexer.start + lexer.yychar(), lexer.yylength());
1325     }
1326
1327     /**
1328      * Get an helpable keyword at the current position in the document.
1329      * @param caret if true the position is the current caret position in the doc else
1330      * the position is the mouse pointer position projected in the document.
1331      * @return the helpable keyword.
1332      */
1333     public String getHelpableKeyword(boolean caret) {
1334         int tok;
1335         int pos;
1336         if (caret) {
1337             pos = getCaretPosition();
1338         } else {
1339             pos = viewToModel(mousePoint);
1340         }
1341
1342         tok = lexer.getKeyword(pos, true);
1343         if (!ScilabLexerConstants.isHelpable(tok)) {
1344             tok = lexer.getKeyword(pos + 1, true);
1345         }
1346
1347         if (ScilabLexerConstants.isHelpable(tok)) {
1348             try {
1349                 return getDocument().getText(lexer.start + lexer.yychar(), lexer.yylength());
1350             } catch (BadLocationException e) { }
1351         }
1352
1353         return null;
1354     }
1355
1356     /**
1357      * Prevents the different KeywordListener that a MouseEvent occurred
1358      * @param position of the mouse
1359      * @param ev the event which occurred
1360      * @param type of the event : KeywordListener.ONMOUSECLICKED or KeywordListener.ONMOUSEOVER
1361      */
1362     protected void preventConcernedKeywordListener(int position, EventObject ev, int type) {
1363         KeywordEvent kev = null;
1364         Object[] listeners = kwListeners.getListenerList();
1365         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1366             if (listeners[i] == KeywordListener.class && type == ((KeywordListener) listeners[i + 1]).getType()) {
1367                 if (kev == null) {
1368                     int tok = lexer.getKeyword(position, true);
1369                     kev = new KeywordEvent(this, ev, tok, lexer.start + lexer.yychar(), lexer.yylength());
1370                 }
1371
1372                 ((KeywordListener) listeners[i + 1]).caughtKeyword(kev);
1373             }
1374         }
1375     }
1376
1377     /**
1378      * Implements mouseClicked in MouseListener
1379      * @param e event
1380      */
1381     public void mouseClicked(MouseEvent e) {
1382         preventConcernedKeywordListener(getCaretPosition(), e, KeywordListener.ONMOUSECLICKED);
1383     }
1384
1385     /**
1386      * Implements mouseEntered in MouseListener
1387      * @param e event
1388      */
1389     public void mouseEntered(MouseEvent e) {
1390         this.mousePoint = e.getPoint();
1391     }
1392
1393     /**
1394      * Implements mouseExited in MouseListener
1395      * @param e event
1396      */
1397     public void mouseExited(MouseEvent e) { }
1398
1399     /**
1400      * Implements mousePressed in MouseListener
1401      * @param e event
1402      */
1403     public void mousePressed(MouseEvent e) {
1404         if (hasBeenSaved) {
1405             removeHighlightForLine();
1406         }
1407
1408         removeHighlightedWords();
1409
1410         if (highlightEnable) {
1411             repaint();
1412         }
1413     }
1414
1415     /**
1416      * Implements mouseReleseaed in MouseListener
1417      * @param e event
1418      */
1419     public void mouseReleased(MouseEvent e) { }
1420
1421     /**
1422      * Implements mouseMoved in MouseMotionListener
1423      * @param e event
1424      */
1425     public void mouseMoved(MouseEvent e) {
1426         this.mousePoint = e.getPoint();
1427         preventConcernedKeywordListener(viewToModel(mousePoint), e, KeywordListener.ONMOUSEOVER);
1428     }
1429
1430     /**
1431      * Implements mouseDragged in MouseMotionListener
1432      * @param e event
1433      */
1434     public void mouseDragged(MouseEvent e) {
1435         if (hasBeenSaved) {
1436             removeHighlightForLine();
1437         }
1438
1439         removeHighlightedWords();
1440
1441         if (highlightEnable) {
1442             repaint();
1443         }
1444     }
1445
1446     /**
1447      * @return the current mouse poisition in this pane
1448      */
1449     public Point getMousePoint() {
1450         return mousePoint;
1451     }
1452
1453     /**
1454      * @param pane the EditorPane associated with this EditorPane in a splitted view
1455      */
1456     public void setOtherPaneInSplit(ScilabEditorPane pane) {
1457         if (pane == null) {
1458             rightTextPane.rightTextPane = null;
1459         }
1460         rightTextPane = pane;
1461     }
1462
1463     /**
1464      * @return the EditorPane associated with this EditorPane in a splitted view
1465      */
1466     public ScilabEditorPane getOtherPaneInSplit() {
1467         return rightTextPane;
1468     }
1469
1470     public ScilabEditorPane getCurrent() {
1471         if (focused == rightTextPane) {
1472             return rightTextPane;
1473         }
1474
1475         return this;
1476     }
1477
1478
1479     /**
1480      * @return the scrollPane associated with this EditorPane
1481      */
1482     public ScilabScrollPane getScrollPane() {
1483         return (ScilabScrollPane) SwingUtilities.getAncestorOfClass(ScilabScrollPane.class, this);
1484     }
1485
1486     /**
1487      * @return the current focused editorPane
1488      */
1489     public static ScilabEditorPane getFocusedPane() {
1490         return focused;
1491     }
1492
1493     /**
1494      * clean
1495      */
1496     public static void clean() {
1497         focused = null;
1498     }
1499
1500     /**
1501      * {@inheritDoc}
1502      */
1503     public String toString() {
1504         return shortName;
1505     }
1506
1507     /**
1508      * @return true if something has been copied
1509      */
1510     public boolean copyColumnSelectionInClipBoard() {
1511         return ((SciNotesCaret) getCaret()).copyPositionsInClipboard();
1512     }
1513
1514     /**
1515      * @return true if something has been removed
1516      */
1517     public boolean removeColumnSelection() {
1518         return ((SciNotesCaret) getCaret()).removePositions();
1519     }
1520
1521     /**
1522      * {@inheritDoc}
1523      */
1524     public String getSelectedText() {
1525         if (getCaret() instanceof SciNotesCaret) {
1526             String str = ((SciNotesCaret) getCaret()).getSelectedText();
1527             if (str == null) {
1528                 return super.getSelectedText();
1529             } else {
1530                 return str;
1531             }
1532         }
1533
1534         return null;
1535     }
1536
1537     /**
1538      * {@inheritDoc}
1539      */
1540     public int getCaretPosition() {
1541         if (getCaret() != null) {
1542             return super.getCaretPosition();
1543         }
1544
1545         return 0;
1546     }
1547
1548     /**
1549      * @param pos the position int the text
1550      * @return null if no column selection on the same line and an array of
1551      * integer (of size 2) containing the position of the selection
1552      */
1553     public int[] isNearColumnSelection(int pos) {
1554         if (!(getCaret() instanceof SciNotesCaret) || ((SciNotesCaret) getCaret()).isEmptySelection()) {
1555             return null;
1556         }
1557
1558         Element root = getDocument().getDefaultRootElement();
1559         int[][] positions = ((SciNotesCaret) getCaret()).getSelectedPositions();
1560         int line = root.getElementIndex(pos);
1561         int min = root.getElementIndex(positions[0][0]);
1562         int max = root.getElementIndex(positions[positions.length - 1][0]);
1563
1564         if (line >= min && line <= max) {
1565             return positions[line - min];
1566         }
1567
1568         return null;
1569     }
1570
1571     /**
1572      * {@inheritDoc}
1573      */
1574     public void setCaret(Caret c) {
1575         if (!(c instanceof ScilabCaret)) {
1576             final Caret caret = new SciNotesCaret(this);
1577             setCaretColor(getCaretColor());
1578             SwingUtilities.invokeLater(new Runnable() {
1579                 public void run() {
1580                     int blinkRate = 500;
1581                     Object o = UIManager.get("TextComponent.caretBlinkRate");
1582                     if ((o != null) && (o instanceof Integer)) {
1583                         Integer rate = (Integer) o;
1584                         blinkRate = rate.intValue();
1585                     }
1586                     caret.setBlinkRate(blinkRate);
1587                     caret.setVisible(true);
1588                 }
1589             });
1590             super.setCaret(caret);
1591         } else {
1592             super.setCaret(c);
1593         }
1594     }
1595
1596     /**
1597      * {@inheritDoc}
1598      */
1599     public void select(int start, int end) {
1600         removeHighlightOnPosition(start);
1601         int docLength = getDocument().getLength();
1602         if (start > docLength) {
1603             start = docLength;
1604         }
1605         if (end > docLength) {
1606             end = docLength;
1607         }
1608         super.setCaretPosition(start);
1609         super.moveCaretPosition(end);
1610     }
1611
1612     /**
1613      * Remove the highlight putted to show the line (for editor('foo',123))
1614      */
1615     private void removeHighlightForLine() {
1616         highlightContourColor = saveHighlightContourColor;
1617         highlightColor = saveHighlightColor;
1618         enableHighlightedLine(false);
1619         if (saveHighlightEnable) {
1620             enableHighlightedLine(true);
1621         }
1622         hasBeenSaved = false;
1623     }
1624
1625     /**
1626      * Remove the highlight at position start
1627      * @param start the beginning of the highlight
1628      */
1629     public void removeHighlightOnPosition(int start) {
1630         Object h = highlightedWords.get(start);
1631         if (h != null) {
1632             getHighlighter().removeHighlight(h);
1633             highlightedWords.remove(h);
1634         }
1635     }
1636
1637     /**
1638      * Highlight a word in this textpane.
1639      * @param word the word to highlight
1640      * @param exact if true the search is case sensitive
1641      */
1642     public void highlightWords(String word, boolean exact) {
1643         if (word != null && word.length() != 0) {
1644             highlightWords(SearchManager.generatePattern(word, exact, false, false), false);
1645         }
1646     }
1647
1648     /**
1649      * Highlight a word according to a pattern in this textpane.
1650      * @param pattern the pattern to highlight
1651      * @param centered, if true the pane is centered on the first occurence
1652      */
1653     public void highlightWords(Pattern pattern, boolean centered) {
1654         highlightWords(-1, pattern, centered);
1655     }
1656
1657     /**
1658      * Highlight a word according to a pattern in this textpane.
1659      * @param pattern the pattern to highlight
1660      * @param centered, if true the pane is centered on the first occurence
1661      */
1662     public void highlightWords(int tok, Pattern pattern, boolean centered) {
1663         if (pattern != null) {
1664             removeHighlightedWords();
1665             int first = -1;
1666             String text = ((ScilabDocument) getDocument()).getText();
1667             Matcher matcher = pattern.matcher(text);
1668
1669             Highlighter highlighter = getHighlighter();
1670             List<Integer[]> positions = SearchManager.findToken((ScilabDocument) getDocument(), tok, lexer, pattern);
1671
1672             if (positions != null) {
1673                 List<Rectangle> marks = new ArrayList<Rectangle>();
1674
1675                 for (Integer[] position : positions) {
1676                     try {
1677                         highlightedWords.put(position[0], highlighter.addHighlight(position[0], position[1], HIGHLIGHTER));
1678                         Rectangle r = modelToView(position[0]);
1679                         if (r != null && (marks.size() == 0 || marks.get(marks.size() - 1).y != r.y)) {
1680                             marks.add(r);
1681                         }
1682                     } catch (BadLocationException e) { }
1683                 }
1684
1685                 ScilabScrollPane ssp = getScrollPane();
1686                 if (ssp != null) {
1687                     ssp.putMarks(marks);
1688                 }
1689
1690                 if (centered && positions.size() != 0) {
1691                     scrollTextToPos(positions.get(0)[0], false, true);
1692                 }
1693             }
1694         }
1695     }
1696
1697     /**
1698      * Remove all the highlighted words
1699      */
1700     public void removeHighlightedWords() {
1701         Highlighter highlighter = getHighlighter();
1702         for (Object obj : highlightedWords.values()) {
1703             highlighter.removeHighlight(obj);
1704         }
1705         highlightedWords.clear();
1706         ScilabScrollPane ssp = getScrollPane();
1707         if (ssp != null) {
1708             ssp.removeMarks();
1709         }
1710     }
1711
1712     /**
1713      * Initialize the pane when the document is loaded
1714      * @param doc used with this pane
1715      */
1716     private void initialize(ScilabDocument doc) {
1717         indent = new IndentManager(doc);
1718         tab = new TabManager(doc, indent);
1719         tab.setDefaultTabulation();
1720         com = new CommentManager(doc);
1721         trailingWhite = new TrailingWhiteManager(doc);
1722
1723         if (matchingKeywordsEnable || matchingOpenersEnable) {
1724             matchLR = new MatchingBlockManager(doc, this, true, getHighlighter());
1725             matchLR.setDefaults();
1726             matchRL = new MatchingBlockManager(doc, this, false, getHighlighter());
1727             matchRL.setDefaults();
1728         }
1729
1730         lexer = doc.createLexer();
1731         xln = new SciNotesLineNumberPanel(this);
1732
1733         /* The order of the next two lines is important: the doc
1734            as listener will be called before xln so the resetTypeWhenBroken
1735            will be called before ! */
1736         doc.addDocumentListener(xln);
1737         doc.addDocumentListener(doc);
1738
1739         getScrollPane().setRowHeaderView(xln);
1740         doc.setEditorPane(this);
1741
1742         if (editor != null) {
1743             NavigatorWindow nav = editor.getNavigator();
1744             if (nav != null) {
1745                 nav.addEditorPane(this);
1746             }
1747         }
1748
1749         resetFont();
1750     }
1751 }