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