Bug 13702 fixed: Problems with * prefix for modified files in SciNotes
[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             int index = editor.getTextPaneIndex(this);
819             if (index != -1) {
820                 editor.updateTabTitle(index);
821             }
822         }
823     }
824
825     public void setMustAdjustVisibility(boolean mustAdjustVisibility) {
826         ((ScilabCaret) getCaret()).setMustAdjustVisibility(mustAdjustVisibility);
827     }
828
829     public boolean getMustAdjustVisibility() {
830         return ((ScilabCaret) getCaret()).getMustAdjustVisibility();
831     }
832
833     /**
834      * Scroll the pane to have the line containing pos on the top of the pane
835      * @param pos the position in the document
836      */
837     public void scrollTextToPos(int pos) {
838         scrollTextToPos(pos, true, false);
839     }
840
841     /**
842      * Scroll the pane to have the line containing pos on the top or centered on the pane
843      * @param pos the position in the document
844      * @param setCaret, if true the caret is set at the given position
845      * @param centered, if true the line is centered
846      */
847     public void scrollTextToPos(final int pos, final boolean setCaret, final boolean centered) {
848         SwingUtilities.invokeLater(new Runnable() {
849             public void run() {
850                 try {
851                     if (setCaret) {
852                         setCaretPosition(pos);
853                     }
854                     JScrollBar scrollbar = getScrollPane().getVerticalScrollBar();
855                     Rectangle rect = modelToView(pos);
856                     if (centered) {
857                         int value = scrollbar.getValue();
858                         int h = scrollbar.getHeight();
859                         if (rect.y < value || rect.y > value + h) {
860                             scrollbar.setValue(Math.max(0, rect.y - h / 2));
861                         }
862                     } else {
863                         if (rect.y > scrollbar.getMaximum()) {
864                             scrollbar.setMaximum(rect.y);
865                         }
866                         scrollbar.setValue(rect.y);
867                     }
868                 } catch (BadLocationException e) { }
869             }
870         });
871     }
872
873     /**
874      * Scroll the pane to have the line lineNumber on the top of the pane
875      * @param lineNumber the number of the line
876      * @param highlight true to highlight the line
877      */
878     public void scrollTextToLineNumber(int lineNumber, boolean highlight) {
879         scrollTextToLineNumber(lineNumber, highlight, true, false);
880     }
881
882     /**
883      * Scroll the pane to have the line lineNumber on the top or centered on the pane
884      * @param lineNumber the number of the line
885      * @param highlight true to highlight the line
886      * @param setCaret, if true the caret is set at the given line
887      * @param centered, if true the line is centered
888      */
889     public void scrollTextToLineNumber(int lineNumber, final boolean highlight, final boolean setCaret, final boolean centered) {
890         Element root = getDocument().getDefaultRootElement();
891         if (lineNumber >= 1 && lineNumber <= root.getElementCount()) {
892             final int pos = root.getElement(lineNumber - 1).getStartOffset();
893             SwingUtilities.invokeLater(new Runnable() {
894                 public void run() {
895                     scrollTextToPos(pos, setCaret, centered);
896                     if (highlight) {
897                         saveHighlightContourColor = highlightContourColor;
898                         highlightContourColor = null;
899                         saveHighlightColor = highlightColor;
900                         highlightColor = Color.YELLOW;
901                         saveHighlightEnable = highlightEnable;
902                         hasBeenSaved = true;
903                         enableHighlightedLine(false);
904                         enableHighlightedLine(true);
905                     }
906                 }
907             });
908         }
909     }
910
911     /**
912      * Scroll the pane to have the line lineNumber on the top of the pane in whereami mode
913      * The line number is computed regarding the function named funname.
914      * @param lineNumber the number of the line
915      * @param funname the function name
916      * @param highlight true to highlight the line
917      */
918     public void scrollTextToLineNumberInWhereami(int lineNumber, String funname, boolean highlight) {
919         scrollTextToLineNumberInWhereami(lineNumber, funname, highlight, true, false);
920     }
921
922     /**
923      * Scroll the pane to have the line lineNumber on the top of the pane in whereami mode
924      * The line number is computed regarding the function named funname.
925      * @param lineNumber the number of the line
926      * @param funname the function name
927      * @param highlight true to highlight the line
928      * @param setCaret, if true the caret is set at the given line
929      * @param centered, if true the line is centered
930      */
931     public void scrollTextToLineNumberInWhereami(int lineNumber, String funname, boolean highlight, boolean setCaret, boolean centered) {
932         if (funname != null) {
933             Element root = getDocument().getDefaultRootElement();
934             int nlines = root.getElementCount();
935             ScilabDocument.ScilabLeafElement elem;
936             for (int i = 0; i < nlines; i++) {
937                 elem = (ScilabDocument.ScilabLeafElement) root.getElement(i);
938                 if (elem.getFunctionName().equals(funname)) {
939                     lineNumber += i;
940                     break;
941                 }
942             }
943         }
944         scrollTextToLineNumber(lineNumber, highlight, setCaret, centered);
945     }
946
947     /**
948      * @return the width of a white
949      */
950     public int getWhiteWidth() {
951         View view = ((ScilabDocument) getDocument()).getView();
952         if (view != null) {
953             if (view instanceof ScilabView) {
954                 return ((ScilabView) view).getWhiteWidth();
955             } else {
956                 return ((ScilabPlainView) view).getWhiteWidth();
957             }
958         }
959
960         return 0;
961     }
962
963     public void resetFont() {
964         Font font = ((ScilabEditorKit) getEditorKit()).getStylePreferences().getBaseFont();
965         setFont(font);
966         xln.updateFont(font);
967     }
968
969     /**
970      * Set a new font
971      * @param keyword the type of keyword
972      * @param type an int : -2 to reset italic, -1 to reset bold, 1 to set bold and 2 to set italic
973      */
974     public void resetFont(String keyword, int type) {
975         ((ScilabEditorKit) getEditorKit()).getStylePreferences().genFont(keyword, type);
976     }
977
978     /**
979      * Set a new attribute
980      * @param keyword the type of keyword
981      * @param type an int : 0 nothing, 1 underline, 2 stroke, 3 underline and stroke
982      */
983     public void resetAttribute(String keyword, int type) {
984         ((ScilabEditorKit) getEditorKit()).getStylePreferences().genAttribute(keyword, type);
985     }
986
987     /**
988      * Set a new color
989      * @param keyword the kind of the keyword
990      * @param color the color
991      */
992     public void resetColor(String keyword, Color color) {
993         ((ScilabEditorKit) getEditorKit()).getStylePreferences().genColors(keyword, color);
994     }
995
996     /**
997      * Set to true if the comments must be suppressed when the code is executing in the console
998      * @param b boolean
999      */
1000     public void suppressCommentsInExecutingCode(boolean b) {
1001         suppressCom = b;
1002     }
1003
1004     /**
1005      * Execute the code in the console, the code is the selected text if exists
1006      * or the text from beginning to actual position of the caret
1007      * Comments are removed if suppressCom is set to true
1008      * @return the code to be executed in the console.
1009      */
1010     public String getCodeToExecute() {
1011         String selection;
1012         int start, end;
1013         start = getSelectionStart();
1014         end = getSelectionEnd();
1015
1016         if (((SciNotesCaret) getCaret()).isEmptySelection()) {
1017             try {
1018                 if (start == end) {
1019                     selection = getDocument().getText(0, start);
1020                     start = 0;
1021                 } else {
1022                     selection = getSelectedText();
1023                 }
1024             } catch (BadLocationException e) {
1025                 selection = "";
1026             }
1027         } else {
1028             selection = getSelectedText();
1029         }
1030
1031         if (suppressCom) {
1032             StringBuffer buf = new StringBuffer(selection.length());
1033             ScilabLexer.ScilabTokens tokens = ScilabLexer.getScilabTokens(selection);
1034             List<Integer> tokType = tokens.getTokenType();
1035             List<Integer> tokPos = tokens.getTokenPos();
1036             List<String> commands = new ArrayList<String>();
1037             int prevPos = 0;
1038             for (int i = 0; i < tokType.size(); i++) {
1039                 String str = selection.substring(prevPos, tokPos.get(i));
1040                 if ("\n".equals(str)) {
1041                     commands.add(buf.toString());
1042                     buf.setLength(0);
1043                 } else if (!ScilabLexerConstants.isComment(tokType.get(i))) {
1044                     buf.append(str);
1045                 }
1046                 prevPos = tokPos.get(i);
1047             }
1048
1049             if (buf.length() != 0) {
1050                 commands.add(buf.toString());
1051             }
1052
1053             buf.setLength(0);
1054             Pattern pat = Pattern.compile("[ \t]*");
1055             for (String command : commands) {
1056                 if (!pat.matcher(command).matches()) {
1057                     buf.append(command).append("\n");
1058                 }
1059             }
1060
1061             return buf.toString();
1062         }
1063
1064         return selection;
1065     }
1066
1067     /**
1068      * Add a new KeywordListener
1069      * @param kw a KeywordListener
1070      */
1071     public void addKeywordListener(KeywordListener kw) {
1072         kwListeners.add(KeywordListener.class, kw);
1073     }
1074
1075     /**
1076      * Remove a new KeywordListener
1077      * @param kw a KeywordListener
1078      */
1079     public void removeKeywordListener(KeywordListener kw) {
1080         kwListeners.remove(KeywordListener.class, kw);
1081     }
1082
1083     /**
1084      * @return an array of KeywordListener
1085      */
1086     public KeywordListener[] getKeywordListeners() {
1087         return kwListeners.getListeners(KeywordListener.class);
1088     }
1089
1090     /**
1091      * Set a new color for the highlighting
1092      * @param c the color, can be null (useful if setHighlightedContourColor is used with a
1093      * non-null value)
1094      */
1095     public void setHighlightedLineColor(Color c) {
1096         highlightColor = c;
1097     }
1098
1099     /**
1100      * Set a new color for the contour of the highlighting
1101      * @param c the color, if null no contour is drawn
1102      */
1103     public void setHighlightedContourColor(Color c) {
1104         highlightContourColor = c;
1105     }
1106
1107     /**
1108      * Activate or desactivate the help on typing
1109      */
1110     public void activateHelpOnTyping() {
1111         boolean isActive = HelpOnTypingManager.getInstance().isActive();
1112         if (isActive && helpOnTyping == null) {
1113             helpOnTyping = HelpOnTypingManager.getInstance();
1114             addKeyListener(helpOnTyping);
1115         } else if (!isActive && helpOnTyping != null) {
1116             removeKeyListener(helpOnTyping);
1117             helpOnTyping = null;
1118         }
1119     }
1120
1121     /**
1122      * Enable (active true) or disable (active false) the line-highlighting.
1123      * @param active true or false
1124      */
1125     public void enableHighlightedLine(boolean active) {
1126         if (active && !highlightEnable) {
1127             try {
1128                 highlightCL = getHighlighter().addHighlight(0, 0, this);
1129             } catch (BadLocationException e) { }
1130             highlightEnable = true;
1131         }
1132
1133         if (!active && highlightEnable) {
1134             getHighlighter().removeHighlight(highlightCL);
1135             highlightEnable = false;
1136         }
1137
1138         repaint();
1139     }
1140
1141     /**
1142      * Enable (active true) or disable (active false) the matching keywords.
1143      * @param active true or false
1144      */
1145     public void enableMatchingKeywords(boolean active) {
1146         matchingKeywordsEnable = active;
1147     }
1148
1149     /**
1150      * Enable (active true) or disable (active false) the matching keywords.
1151      * @param active true or false
1152      */
1153     public void enableMatchingOpeners(boolean active) {
1154         matchingOpenersEnable = active;
1155     }
1156
1157     /**
1158      * Get a matching manager
1159      * @param lr true if the LR matcher must be returned
1160      * @return the MatchingBlockManager
1161      */
1162     public MatchingBlockManager getMatchingBlockManager(boolean lr) {
1163         if (lr) {
1164             return matchLR;
1165         } else {
1166             return matchRL;
1167         }
1168     }
1169
1170     /**
1171      * This class listens to the caret event
1172      * @param e event
1173      */
1174     public void caretUpdate(CaretEvent e) {
1175         if (hasBeenSaved) {
1176             removeHighlightForLine();
1177         }
1178
1179         if (hasFocus()) {
1180             final String str = getSelectedText();
1181             if (str != null && str.length() != 0) {
1182                 if (selectionTimer == null) {
1183                     selectionTimer = new Timer(1000, new ActionListener() {
1184                         public void actionPerformed(ActionEvent e) {
1185                             try {
1186                                 final String str = getSelectedText();
1187                                 if (str != null && str.length() != 0) {
1188                                     int tok = lexer.getKeyword(getSelectionStart(), false);
1189                                     int s = lexer.start + lexer.yychar();
1190                                     if (ScilabLexerConstants.isSearchable(tok) && getSelectionStart() == s && getSelectionEnd() == s + lexer.yylength()) {
1191                                         highlightWords(tok, SearchManager.generatePattern(str, false, true, false), false);
1192                                     } else {
1193                                         highlightWords(str, false);
1194                                     }
1195
1196                                     if (highlightedWords.size() > 1 && editor != null && editor.getInfoBar() != null) {
1197                                         editor.getInfoBar().setText(String.format(SciNotesMessages.OCCURENCES_FOUND, Integer.toString(highlightedWords.size())));
1198                                     }
1199                                     removeHighlightOnPosition(getSelectionStart());
1200                                 }
1201                             } catch (Exception ee) { }
1202                             selectionTimer = null;
1203                         }
1204                     });
1205                     selectionTimer.setRepeats(false);
1206                     selectionTimer.start();
1207                 } else {
1208                     selectionTimer.restart();
1209                 }
1210             } else {
1211                 removeHighlightedWords();
1212             }
1213         }
1214
1215         if (highlightEnable) {
1216             repaint();
1217         }
1218
1219         if (matchingKeywordsEnable || matchingOpenersEnable) {
1220             if (matcherTimer == null) {
1221                 matcherTimer = new Timer(100, new ActionListener() {
1222                     public void actionPerformed(ActionEvent e) {
1223                         int pos = getCaretPosition();
1224                         int tok = lexer.getKeyword(pos, false);
1225                         matchLR.searchMatchingBlock(false, tok, lexer.start + lexer.yychar());
1226                         tok = lexer.getKeyword(pos, true);
1227                         matchRL.searchMatchingBlock(false, tok, lexer.start + lexer.yychar() + lexer.yylength());
1228                         matcherTimer = null;
1229                     }
1230                 });
1231                 matcherTimer.setRepeats(false);
1232                 matcherTimer.start();
1233             } else {
1234                 matcherTimer.restart();
1235                 matchLR.update();
1236                 matchRL.update();
1237             }
1238         }
1239
1240         if (!readonly && !binary && editor != null) {
1241             editor.getInfoBar().setText(((ScilabDocument) getDocument()).getCurrentFunction(getCaretPosition()));
1242         }
1243     }
1244
1245     /**
1246      * Used to paint the highlighted line
1247      * @param g graphics to use
1248      * @param p0 start
1249      * @param p1 end
1250      * @param bounds the shape representing the area
1251      * @param c this pane
1252      */
1253     public void paint(Graphics g, int p0, int p1, Shape bounds, JTextComponent c) {
1254         if (highlightEnable) {
1255             try {
1256                 Rectangle r = modelToView(getCaretPosition());
1257                 if (highlightColor != null) {
1258                     g.setColor(highlightColor);
1259                     g.fillRect(0, r.y, getWidth(), r.height);
1260                 }
1261                 if (highlightContourColor != null) {
1262                     g.setColor(highlightContourColor);
1263                     g.drawLine(0, r.y - 1, getWidth(), r.y - 1);
1264                     g.drawLine(0, r.y + r.height, getWidth(), r.y + r.height);
1265                 }
1266
1267             } catch (BadLocationException e) { }
1268         }
1269     }
1270
1271     /**
1272      * @return the current TrailingWhiteManager
1273      */
1274     public TrailingWhiteManager getTrailingWhiteManager() {
1275         return trailingWhite;
1276     }
1277
1278     /**
1279      * @return the current IndentManager
1280      */
1281     public IndentManager getIndentManager() {
1282         return indent;
1283     }
1284
1285     /**
1286      * @return the current TabManager
1287      */
1288     public TabManager getTabManager() {
1289         return tab;
1290     }
1291
1292     /**
1293      * @return the current CommentManager
1294      */
1295     public CommentManager getCommentManager() {
1296         return com;
1297     }
1298
1299     /**
1300      * Get a keyword at a position in the document.
1301      * @param position in the document
1302      * @return the KeywordEvent containing infos about keyword.
1303      */
1304     public KeywordEvent getKeywordEvent(int position) {
1305         int tok = lexer.getKeyword(position, true);
1306         return new KeywordEvent(this, null, tok, lexer.start + lexer.yychar(), lexer.yylength());
1307     }
1308
1309     /**
1310      * Get a keyword at the current position in the document.
1311      * @return the KeywordEvent containing infos about keyword.
1312      */
1313     public KeywordEvent getKeywordEvent() {
1314         return getKeywordEvent(getCaretPosition());
1315     }
1316
1317     /**
1318      * Get a keyword at the current position in the document.
1319      * @param caret if true the position is the current caret position in the doc else
1320      * the position is the mouse pointer position projected in the document.
1321      * @param strict if true the char just after the caret is ignored
1322      * @return the KeywordEvent containing infos about keyword.
1323      */
1324     public KeywordEvent getKeywordEvent(boolean caret, boolean strict) {
1325         int tok;
1326         if (caret) {
1327             tok = lexer.getKeyword(getCaretPosition(), strict);
1328         } else {
1329             tok = lexer.getKeyword(viewToModel(mousePoint), strict);
1330         }
1331         return new KeywordEvent(this, null, tok, lexer.start + lexer.yychar(), lexer.yylength());
1332     }
1333
1334     /**
1335      * Get an helpable keyword at the current position in the document.
1336      * @param caret if true the position is the current caret position in the doc else
1337      * the position is the mouse pointer position projected in the document.
1338      * @return the helpable keyword.
1339      */
1340     public String getHelpableKeyword(boolean caret) {
1341         int tok;
1342         int pos;
1343         if (caret) {
1344             pos = getCaretPosition();
1345         } else {
1346             pos = viewToModel(mousePoint);
1347         }
1348
1349         tok = lexer.getKeyword(pos, true);
1350         if (!ScilabLexerConstants.isHelpable(tok)) {
1351             tok = lexer.getKeyword(pos + 1, true);
1352         }
1353
1354         if (ScilabLexerConstants.isHelpable(tok)) {
1355             try {
1356                 return getDocument().getText(lexer.start + lexer.yychar(), lexer.yylength());
1357             } catch (BadLocationException e) { }
1358         }
1359
1360         return null;
1361     }
1362
1363     /**
1364      * Prevents the different KeywordListener that a MouseEvent occurred
1365      * @param position of the mouse
1366      * @param ev the event which occurred
1367      * @param type of the event : KeywordListener.ONMOUSECLICKED or KeywordListener.ONMOUSEOVER
1368      */
1369     protected void preventConcernedKeywordListener(int position, EventObject ev, int type) {
1370         KeywordEvent kev = null;
1371         Object[] listeners = kwListeners.getListenerList();
1372         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1373             if (listeners[i] == KeywordListener.class && type == ((KeywordListener) listeners[i + 1]).getType()) {
1374                 if (kev == null) {
1375                     int tok = lexer.getKeyword(position, true);
1376                     kev = new KeywordEvent(this, ev, tok, lexer.start + lexer.yychar(), lexer.yylength());
1377                 }
1378
1379                 ((KeywordListener) listeners[i + 1]).caughtKeyword(kev);
1380             }
1381         }
1382     }
1383
1384     /**
1385      * Implements mouseClicked in MouseListener
1386      * @param e event
1387      */
1388     public void mouseClicked(MouseEvent e) {
1389         preventConcernedKeywordListener(getCaretPosition(), e, KeywordListener.ONMOUSECLICKED);
1390     }
1391
1392     /**
1393      * Implements mouseEntered in MouseListener
1394      * @param e event
1395      */
1396     public void mouseEntered(MouseEvent e) {
1397         this.mousePoint = e.getPoint();
1398     }
1399
1400     /**
1401      * Implements mouseExited in MouseListener
1402      * @param e event
1403      */
1404     public void mouseExited(MouseEvent e) { }
1405
1406     /**
1407      * Implements mousePressed in MouseListener
1408      * @param e event
1409      */
1410     public void mousePressed(MouseEvent e) {
1411         if (hasBeenSaved) {
1412             removeHighlightForLine();
1413         }
1414
1415         removeHighlightedWords();
1416
1417         if (highlightEnable) {
1418             repaint();
1419         }
1420     }
1421
1422     /**
1423      * Implements mouseReleseaed in MouseListener
1424      * @param e event
1425      */
1426     public void mouseReleased(MouseEvent e) { }
1427
1428     /**
1429      * Implements mouseMoved in MouseMotionListener
1430      * @param e event
1431      */
1432     public void mouseMoved(MouseEvent e) {
1433         this.mousePoint = e.getPoint();
1434         preventConcernedKeywordListener(viewToModel(mousePoint), e, KeywordListener.ONMOUSEOVER);
1435     }
1436
1437     /**
1438      * Implements mouseDragged in MouseMotionListener
1439      * @param e event
1440      */
1441     public void mouseDragged(MouseEvent e) {
1442         if (hasBeenSaved) {
1443             removeHighlightForLine();
1444         }
1445
1446         removeHighlightedWords();
1447
1448         if (highlightEnable) {
1449             repaint();
1450         }
1451     }
1452
1453     /**
1454      * @return the current mouse poisition in this pane
1455      */
1456     public Point getMousePoint() {
1457         return mousePoint;
1458     }
1459
1460     /**
1461      * @param pane the EditorPane associated with this EditorPane in a splitted view
1462      */
1463     public void setOtherPaneInSplit(ScilabEditorPane pane) {
1464         if (pane == null) {
1465             rightTextPane.rightTextPane = null;
1466         }
1467         rightTextPane = pane;
1468     }
1469
1470     /**
1471      * @return the EditorPane associated with this EditorPane in a splitted view
1472      */
1473     public ScilabEditorPane getOtherPaneInSplit() {
1474         return rightTextPane;
1475     }
1476
1477     public ScilabEditorPane getCurrent() {
1478         if (focused == rightTextPane) {
1479             return rightTextPane;
1480         }
1481
1482         return this;
1483     }
1484
1485
1486     /**
1487      * @return the scrollPane associated with this EditorPane
1488      */
1489     public ScilabScrollPane getScrollPane() {
1490         return (ScilabScrollPane) SwingUtilities.getAncestorOfClass(ScilabScrollPane.class, this);
1491     }
1492
1493     /**
1494      * @return the current focused editorPane
1495      */
1496     public static ScilabEditorPane getFocusedPane() {
1497         return focused;
1498     }
1499
1500     /**
1501      * clean
1502      */
1503     public static void clean() {
1504         focused = null;
1505     }
1506
1507     /**
1508      * {@inheritDoc}
1509      */
1510     public String toString() {
1511         return shortName;
1512     }
1513
1514     /**
1515      * @return true if something has been copied
1516      */
1517     public boolean copyColumnSelectionInClipBoard() {
1518         return ((SciNotesCaret) getCaret()).copyPositionsInClipboard();
1519     }
1520
1521     /**
1522      * @return true if something has been removed
1523      */
1524     public boolean removeColumnSelection() {
1525         return ((SciNotesCaret) getCaret()).removePositions();
1526     }
1527
1528     /**
1529      * {@inheritDoc}
1530      */
1531     public String getSelectedText() {
1532         if (getCaret() instanceof SciNotesCaret) {
1533             String str = ((SciNotesCaret) getCaret()).getSelectedText();
1534             if (str == null) {
1535                 return super.getSelectedText();
1536             } else {
1537                 return str;
1538             }
1539         }
1540
1541         return null;
1542     }
1543
1544     /**
1545      * {@inheritDoc}
1546      */
1547     public int getCaretPosition() {
1548         if (getCaret() != null) {
1549             return super.getCaretPosition();
1550         }
1551
1552         return 0;
1553     }
1554
1555     /**
1556      * @param pos the position int the text
1557      * @return null if no column selection on the same line and an array of
1558      * integer (of size 2) containing the position of the selection
1559      */
1560     public int[] isNearColumnSelection(int pos) {
1561         if (!(getCaret() instanceof SciNotesCaret) || ((SciNotesCaret) getCaret()).isEmptySelection()) {
1562             return null;
1563         }
1564
1565         Element root = getDocument().getDefaultRootElement();
1566         int[][] positions = ((SciNotesCaret) getCaret()).getSelectedPositions();
1567         int line = root.getElementIndex(pos);
1568         int min = root.getElementIndex(positions[0][0]);
1569         int max = root.getElementIndex(positions[positions.length - 1][0]);
1570
1571         if (line >= min && line <= max) {
1572             return positions[line - min];
1573         }
1574
1575         return null;
1576     }
1577
1578     /**
1579      * {@inheritDoc}
1580      */
1581     public void setCaret(Caret c) {
1582         if (!(c instanceof ScilabCaret)) {
1583             final Caret caret = new SciNotesCaret(this);
1584             setCaretColor(getCaretColor());
1585             SwingUtilities.invokeLater(new Runnable() {
1586                 public void run() {
1587                     int blinkRate = 500;
1588                     Object o = UIManager.get("TextComponent.caretBlinkRate");
1589                     if ((o != null) && (o instanceof Integer)) {
1590                         Integer rate = (Integer) o;
1591                         blinkRate = rate.intValue();
1592                     }
1593                     caret.setBlinkRate(blinkRate);
1594                     caret.setVisible(true);
1595                 }
1596             });
1597             super.setCaret(caret);
1598         } else {
1599             super.setCaret(c);
1600         }
1601     }
1602
1603     /**
1604      * {@inheritDoc}
1605      */
1606     public void select(int start, int end) {
1607         removeHighlightOnPosition(start);
1608         int docLength = getDocument().getLength();
1609         if (start > docLength) {
1610             start = docLength;
1611         }
1612         if (end > docLength) {
1613             end = docLength;
1614         }
1615         super.setCaretPosition(start);
1616         super.moveCaretPosition(end);
1617     }
1618
1619     /**
1620      * Remove the highlight putted to show the line (for editor('foo',123))
1621      */
1622     private void removeHighlightForLine() {
1623         highlightContourColor = saveHighlightContourColor;
1624         highlightColor = saveHighlightColor;
1625         enableHighlightedLine(false);
1626         if (saveHighlightEnable) {
1627             enableHighlightedLine(true);
1628         }
1629         hasBeenSaved = false;
1630     }
1631
1632     /**
1633      * Remove the highlight at position start
1634      * @param start the beginning of the highlight
1635      */
1636     public void removeHighlightOnPosition(int start) {
1637         Object h = highlightedWords.get(start);
1638         if (h != null) {
1639             getHighlighter().removeHighlight(h);
1640             highlightedWords.remove(h);
1641         }
1642     }
1643
1644     /**
1645      * Highlight a word in this textpane.
1646      * @param word the word to highlight
1647      * @param exact if true the search is case sensitive
1648      */
1649     public void highlightWords(String word, boolean exact) {
1650         if (word != null && word.length() != 0) {
1651             highlightWords(SearchManager.generatePattern(word, exact, false, false), false);
1652         }
1653     }
1654
1655     /**
1656      * Highlight a word according to a pattern in this textpane.
1657      * @param pattern the pattern to highlight
1658      * @param centered, if true the pane is centered on the first occurence
1659      */
1660     public void highlightWords(Pattern pattern, boolean centered) {
1661         highlightWords(-1, pattern, centered);
1662     }
1663
1664     /**
1665      * Highlight a word according to a pattern in this textpane.
1666      * @param pattern the pattern to highlight
1667      * @param centered, if true the pane is centered on the first occurence
1668      */
1669     public void highlightWords(int tok, Pattern pattern, boolean centered) {
1670         if (pattern != null) {
1671             removeHighlightedWords();
1672             int first = -1;
1673             String text = ((ScilabDocument) getDocument()).getText();
1674             Matcher matcher = pattern.matcher(text);
1675
1676             Highlighter highlighter = getHighlighter();
1677             List<Integer[]> positions = SearchManager.findToken((ScilabDocument) getDocument(), tok, lexer, pattern);
1678
1679             if (positions != null) {
1680                 List<Rectangle> marks = new ArrayList<Rectangle>();
1681
1682                 for (Integer[] position : positions) {
1683                     try {
1684                         highlightedWords.put(position[0], highlighter.addHighlight(position[0], position[1], HIGHLIGHTER));
1685                         Rectangle r = modelToView(position[0]);
1686                         if (r != null && (marks.size() == 0 || marks.get(marks.size() - 1).y != r.y)) {
1687                             marks.add(r);
1688                         }
1689                     } catch (BadLocationException e) { }
1690                 }
1691
1692                 ScilabScrollPane ssp = getScrollPane();
1693                 if (ssp != null) {
1694                     ssp.putMarks(marks);
1695                 }
1696
1697                 if (centered && positions.size() != 0) {
1698                     scrollTextToPos(positions.get(0)[0], false, true);
1699                 }
1700             }
1701         }
1702     }
1703
1704     /**
1705      * Remove all the highlighted words
1706      */
1707     public void removeHighlightedWords() {
1708         Highlighter highlighter = getHighlighter();
1709         for (Object obj : highlightedWords.values()) {
1710             highlighter.removeHighlight(obj);
1711         }
1712         highlightedWords.clear();
1713         ScilabScrollPane ssp = getScrollPane();
1714         if (ssp != null) {
1715             ssp.removeMarks();
1716         }
1717     }
1718
1719     /**
1720      * Initialize the pane when the document is loaded
1721      * @param doc used with this pane
1722      */
1723     private void initialize(ScilabDocument doc) {
1724         indent = new IndentManager(doc);
1725         tab = new TabManager(doc, indent);
1726         tab.setDefaultTabulation();
1727         com = new CommentManager(doc);
1728         trailingWhite = new TrailingWhiteManager(doc);
1729
1730         if (matchingKeywordsEnable || matchingOpenersEnable) {
1731             matchLR = new MatchingBlockManager(doc, this, true, getHighlighter());
1732             matchLR.setDefaults();
1733             matchRL = new MatchingBlockManager(doc, this, false, getHighlighter());
1734             matchRL.setDefaults();
1735         }
1736
1737         lexer = doc.createLexer();
1738         xln = new SciNotesLineNumberPanel(this);
1739
1740         /* The order of the next two lines is important: the doc
1741            as listener will be called before xln so the resetTypeWhenBroken
1742            will be called before ! */
1743         doc.addDocumentListener(xln);
1744         doc.addDocumentListener(doc);
1745
1746         getScrollPane().setRowHeaderView(xln);
1747         doc.setEditorPane(this);
1748
1749         if (editor != null) {
1750             NavigatorWindow nav = editor.getNavigator();
1751             if (nav != null) {
1752                 nav.addEditorPane(this);
1753             }
1754         }
1755
1756         resetFont();
1757     }
1758 }