Refactorization of the management of the actions in menus, button, shortcuts
[scilab.git] / scilab / modules / scinotes / src / java / org / scilab / modules / scinotes / ScilabDocument.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - 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-en.txt
10  *
11  */
12
13 package org.scilab.modules.scinotes;
14
15 import java.util.Vector;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.TreeSet;
19 import java.util.Set;
20 import java.util.Comparator;
21 import java.util.Iterator;
22
23 import javax.swing.text.GapContent;
24 import javax.swing.text.PlainDocument;
25 import javax.swing.text.BadLocationException;
26 import javax.swing.text.Element;
27 import javax.swing.text.AttributeSet;
28 import javax.swing.text.View;
29
30 import javax.swing.JTree;
31 import javax.swing.tree.DefaultMutableTreeNode;
32 import javax.swing.tree.DefaultTreeModel;
33
34 import javax.swing.event.DocumentEvent;
35 import javax.swing.event.DocumentListener;
36
37 import org.scilab.modules.scinotes.utils.ConfigSciNotesManager;
38
39 /**
40  * The class ScilabDocument is used to render a document .sci or .sce
41  * @author Calixte DENIZET
42  */
43 public class ScilabDocument extends PlainDocument implements DocumentListener {
44
45     /**
46      * The EOL in mac OS
47      */
48     public static final String EOLMAC = "\r";
49
50     /**
51      * The EOL in windows OS
52      */
53     public static final String EOLWIN = "\r\n";
54
55     /**
56      * The EOL in unix OS
57      */
58     public static final String EOLUNIX = "\n";
59
60     private static final int GAPBUFFERCAPACITY = 2;
61     private static final String LINE_SEPARATOR = "line.separator";
62
63     private View view;
64     private List<String> saved = new Vector();
65     private FunctionScanner funScanner;
66
67     private boolean contentModified;
68
69     // Editor's default encoding is UTF-8
70     private String encoding = "utf-8";
71     private boolean updater = true;
72     private boolean binary;
73     private boolean autoIndent;
74     private boolean autoColorize;
75     private boolean shouldMergeEdits;
76     private boolean undoManagerEnabled;
77     private CompoundUndoManager undo;
78
79     private ScilabEditorPane pane;
80     private boolean focused;
81
82     private String eolStyle = System.getProperty(LINE_SEPARATOR);
83
84     /**
85      * Constructor
86      */
87     public ScilabDocument() {
88         super(new GapContent(GAPBUFFERCAPACITY));
89         setAsynchronousLoadPriority(2);
90
91         autoIndent = ConfigSciNotesManager.getAutoIndent();
92         autoColorize = ConfigSciNotesManager.getAutoColorize();
93         encoding = ConfigSciNotesManager.getDefaultEncoding();
94
95         undo = new CompoundUndoManager(this);
96         addUndoableEditListener(undo);
97         undoManagerEnabled = true;
98         contentModified = false;
99
100         funScanner = new FunctionScanner(this);
101         addDocumentListener(this);
102     }
103
104     /**
105      * Set the ScilabEditorPane associated with this doc
106      * @param pane the ScilabEditorPane
107      */
108     public void setEditorPane(ScilabEditorPane pane) {
109         this.pane = pane;
110     }
111
112     /**
113      * Get the ScilabEditorPane associated with this doc
114      * @return pane the ScilabEditorPane
115      */
116     public ScilabEditorPane getEditorPane() {
117         return pane;
118     }
119
120     /**
121      * Set to true of the document is focused in the EditorPane
122      * @param b the boolean
123      */
124     public void setFocused(boolean b) {
125         focused = b;
126     }
127
128     /**
129      * Create a lexer used to colorize the text
130      * @return ScilabLexer the lexer
131      */
132     public ScilabLexer createLexer() {
133         return new ScilabLexer(this);
134     }
135
136     /**
137      * Set the current view to render the code
138      * @param view the used view
139      */
140     public void setView(View view) {
141         this.view = view;
142     }
143
144     /**
145      * @return the current used view
146      */
147     public View getView() {
148         return view;
149     }
150
151     /**
152      * Get encoding
153      * @return String encoding
154      */
155     public String getEncoding() {
156         return encoding;
157     }
158
159     /**
160      * set Encoding
161      * @param encoding encoding
162      */
163     public void setEncoding(String encoding) {
164         this.encoding = encoding;
165     }
166
167     /**
168      * Set to true if it's a binary doc
169      * @param binary boolean
170      */
171     public void setBinary(boolean binary) {
172         this.binary = binary;
173     }
174
175     /**
176      * @return true if it's a binary file
177      */
178     public boolean getBinary() {
179         return binary;
180     }
181
182     /**
183      * set end of line value
184      * @param eol String
185      */
186     public void setEOL(String eol) {
187         this.eolStyle = eol;
188     }
189
190     /**
191      * get end of line
192      * @return end of line
193      */
194     public String getEOL() {
195         return this.eolStyle;
196     }
197
198     /**
199      * get end of line
200      * @return end of line
201      */
202     public String getDefaultEOL() {
203         return System.getProperty(LINE_SEPARATOR);
204     }
205
206     /**
207      * isUpdater
208      * @return boolean
209      */
210     public boolean isUpdater() {
211         return updater;
212     }
213
214     /**
215      * getAutoIndent
216      * @return boolean
217      */
218     public boolean getAutoIndent() {
219         return autoIndent;
220     }
221
222     /**
223      * setAutoIndent
224      * @param b boolean
225      */
226     public void setAutoIndent(boolean b) {
227         autoIndent = b;
228     }
229
230     /**
231      * setUpdater
232      * @param updaterDisabled boolean
233      */
234     public void setUpdater(boolean updaterDisabled) {
235         this.updater = updaterDisabled;
236     }
237
238
239     /**
240      * Get document text
241      * @return String
242      */
243     public String getText() {
244         try {
245             return getText(0, getLength());
246         } catch (BadLocationException e) {
247             return "";
248         }
249     }
250
251     /**
252      * Begins a compound edit (for the undo)
253      */
254     public void mergeEditsBegin() {
255         undo.endCompoundEdit();
256         undo.startCompoundEdit();
257     }
258
259     /**
260      * Ends a compound edit (for the undo)
261      */
262     public void mergeEditsEnd() {
263         undo.endCompoundEdit();
264     }
265
266     /**
267      * getColorize
268      * @return boolean
269      */
270     public boolean getColorize() {
271         return autoColorize;
272     }
273
274     /**
275      * setColorize
276      * @param b boolean
277      */
278     public void setColorize(boolean b) {
279         autoColorize = b;
280     }
281
282     /**
283      * getUndoManager
284      * @return CompoundUndoManager
285      */
286     public CompoundUndoManager getUndoManager() {
287         return undo;
288     }
289
290     /**
291      * disableUndoManager
292      */
293     public void disableUndoManager() {
294         if (undoManagerEnabled) {
295             this.removeUndoableEditListener(undo);
296             undoManagerEnabled = false;
297         }
298     }
299
300     /**
301      * enableUndoManager
302      */
303     public void enableUndoManager() {
304         if (!undoManagerEnabled) {
305             this.addUndoableEditListener(undo);
306             undoManagerEnabled = true;
307         }
308     }
309
310     /**
311      * isContentModified
312      * @return boolean
313      */
314     public boolean isContentModified() {
315         return contentModified;
316     }
317
318     /**
319      * setContentModified
320      * @param contentModified boolean
321      */
322     public void setContentModified(boolean contentModified) {
323         this.contentModified = contentModified;
324         if (!contentModified) {
325             undo.setReference();
326             pane.updateTitle();
327         }
328     }
329
330     /**
331      * dump document on stderr with line positions
332      */
333     public void dump() {
334         readLock();
335         try {
336             Element root = getDefaultRootElement();
337             for (int i = 0; i != root.getElementCount(); ++i) {
338                 Element e = root.getElement(i);
339                 int start = e.getStartOffset();
340                 int end = e.getEndOffset();
341                 System.err.println("line " + i + " from: " + start + " to: " + end + ":|" + getText(start, end - start) + "|");
342             }
343         } catch (BadLocationException e) {
344             System.err.println(e);
345         }
346         readUnlock();
347     }
348
349     /**
350      * Search the position of the function name in the Document
351      * @param name the name of the function
352      * @return the position where to go or -1 if not found
353      */
354     public int searchFunctionByName(String name) {
355         Element root = getDefaultRootElement();
356         for (int i = 0; i < root.getElementCount(); i++) {
357             Element e = root.getElement(i);
358             if (e instanceof ScilabLeafElement) {
359                 ScilabLeafElement se = (ScilabLeafElement) e;
360                 if (se.isFunction() && se.getFunctionInfo().functionName.equals(name)) {
361                     return e.getStartOffset();
362                 }
363             }
364         }
365         return -1;
366     }
367
368     /**
369      * @return a list containing all the infos about functions available in this document
370      */
371     public List<FunctionScanner.FunctionInfo> getFunctionInfo() {
372         List list = new ArrayList();
373         Element root = getDefaultRootElement();
374         for (int i = 0; i < root.getElementCount(); i++) {
375             Element e = root.getElement(i);
376             if (e instanceof ScilabLeafElement) {
377                 ScilabLeafElement se = (ScilabLeafElement) e;
378                 if (se.isFunction()) {
379                     list.add(se.getFunctionInfo());
380                 }
381             }
382         }
383         return list;
384     }
385
386     /**
387      * Fill a tree with function's name according to alphabetic order or not
388      * @param tree to fill
389      * @param alphaOrder is true if names must be sorted with alphabetic order
390      */
391     public synchronized void fillTree(JTree tree, boolean alphaOrder) {
392         DefaultMutableTreeNode base = new DefaultMutableTreeNode("Functions");
393         Element root = getDefaultRootElement();
394         int nlines = root.getElementCount();
395         if (!alphaOrder) {
396             for (int i = 0; i < nlines; i++) {
397                 Element elem = root.getElement(i);
398                 if (elem instanceof ScilabDocument.ScilabLeafElement) {
399                     int type = ((ScilabDocument.ScilabLeafElement) elem).getType();
400                     switch (type) {
401                     case ScilabDocument.ScilabLeafElement.NOTHING :
402                         break;
403                     case ScilabDocument.ScilabLeafElement.FUN :
404                         base.add(new DefaultMutableTreeNode(elem));
405                         break;
406                     case ScilabDocument.ScilabLeafElement.ENDFUN :
407                         break;
408                     default :
409                         break;
410                     }
411                 }
412             }
413         } else {
414             Set<DefaultMutableTreeNode> set = new TreeSet(new Comparator<DefaultMutableTreeNode>() {
415                     public int compare(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
416                         ScilabLeafElement l1 = (ScilabLeafElement) o1.getUserObject();
417                         ScilabLeafElement l2 = (ScilabLeafElement) o2.getUserObject();
418                         int n = l1.toString().compareTo(l2.toString());
419                         if (n != 0) {
420                             return n;
421                         }
422                         return l1.getStart() - l2.getStart();
423                     }
424
425                     public boolean equals(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
426                         return false;
427                     }
428                 });
429             for (int i = 0; i < nlines; i++) {
430                 Element elem = root.getElement(i);
431                 if (elem instanceof ScilabDocument.ScilabLeafElement) {
432                     int type = ((ScilabDocument.ScilabLeafElement) elem).getType();
433                     switch (type) {
434                     case ScilabDocument.ScilabLeafElement.NOTHING :
435                         break;
436                     case ScilabDocument.ScilabLeafElement.FUN :
437                         set.add(new DefaultMutableTreeNode(elem));
438                         break;
439                     case ScilabDocument.ScilabLeafElement.ENDFUN :
440                         break;
441                     default :
442                         break;
443                     }
444                 }
445             }
446             Iterator<DefaultMutableTreeNode> iter = set.iterator();
447             while (iter.hasNext()) {
448                 base.add(iter.next());
449             }
450         }
451         tree.setModel(new DefaultTreeModel(base));
452     }
453
454     /**
455      * @return the first function name which appears in this doc or null
456      */
457     public String getFirstFunctionName() {
458         Element root = getDefaultRootElement();
459         for (int i = 0; i < root.getElementCount(); i++) {
460             Element e = root.getElement(i);
461             if (e instanceof ScilabLeafElement) {
462                 ScilabLeafElement se = (ScilabLeafElement) e;
463                 if (se.isFunction()) {
464                     return se.getFunctionInfo().functionName;
465                 }
466             }
467         }
468         return null;
469     }
470
471     /**
472      * Get the local variables used as arguments or returned valuesof a function
473      * @param pos the position in the document
474      * @return the two lists containing args and returned values or null if we are not
475      * in a function
476      */
477     public List<String>[] getLocalVariables(int pos) {
478         Element root = getDefaultRootElement();
479         int index = root.getElementIndex(pos);
480         int compt = 0;
481         while (index != -1) {
482             Element e = root.getElement(index--);
483             if (e instanceof ScilabLeafElement) {
484                 ScilabLeafElement se = (ScilabLeafElement) e;
485                 int type = se.getType();
486                 switch (type) {
487                 case ScilabLeafElement.NOTHING :
488                     break;
489                 case ScilabLeafElement.FUN :
490                     if (compt == 0) {
491                         FunctionScanner.FunctionInfo info = se.getFunctionInfo();
492                         return new List[]{info.returnValues, info.argsValues};
493                     } else {
494                         compt++;
495                     }
496                     break;
497                 case ScilabLeafElement.ENDFUN :
498                     compt--;
499                     break;
500                 default :
501                 }
502             }
503         }
504         return null;
505     }
506
507     /**
508      * lock
509      */
510     public void lock() {
511         super.writeLock();
512     }
513
514     /**
515      * unlock
516      */
517     public void unlock() {
518         super.writeUnlock();
519     }
520
521     /**
522      * Implements DocumentListener
523      * @param documentEvent DocumentEvent
524      */
525     public void changedUpdate(DocumentEvent documentEvent) { }
526
527     /**
528      * Implements DocumentListener
529      * @param documentEvent DocumentEvent
530      */
531     public void insertUpdate(DocumentEvent documentEvent) {
532         if (!contentModified && pane != null) {
533             contentModified = true;
534             pane.updateTitle();
535         }
536     }
537
538     /**
539      * Implements DocumentListener
540      * @param documentEvent DocumentEvent
541      */
542     public void removeUpdate(DocumentEvent documentEvent) {
543         if (!contentModified && pane != null) {
544             contentModified = true;
545             pane.updateTitle();
546         }
547     }
548
549     /**
550      * @overload #createDefaultRoot
551      * @return the element base
552      */
553     protected AbstractElement createDefaultRoot() {
554         BranchElement map = (BranchElement) createBranchElement(null, null);
555         Element line = super.createLeafElement(map, null, 0, 1);
556         Element[] lines = new Element[1];
557         lines[0] = line;
558         map.replace(0, 0, lines);
559         return map;
560     }
561
562     /**
563      * @overload #createLeafElement
564      * @param parent the parent Element
565      * @param a an AttributeSet
566      * @param p0 start in the doc
567      * @param p1 end in the doc
568      * @return the created LeafElement
569      */
570     protected Element createLeafElement(Element parent, AttributeSet a, int p0, int p1) {
571         return new ScilabLeafElement(parent, a, p0, p1);
572     }
573
574     /**
575      * Inner class to consider the type of a line :
576      *  - FUN : function y=foo(x)
577      *  - ENDFUN : endfunction
578      *  - NOTHING : bla bla bla
579      * This inner class is useful to make a line numbering compatible with the whereami macro.
580      */
581     public class ScilabLeafElement extends LeafElement {
582
583         /**
584          * Nothing in this line
585          */
586         public static final int NOTHING = 0;
587
588         /**
589          * function ... in this line
590          */
591         public static final int FUN = 1;
592
593         /**
594          * endfunction in this line
595          */
596         public static final int ENDFUN = 2;
597
598         private boolean visible = true;
599         private int type;
600         private FunctionScanner.FunctionInfo info;
601         private int start;
602
603         /**
604          * The same constructor as in LeafElement.
605          * @param parent the parent Element
606          * @param a an AttributeSet
607          * @param p0 start in the doc
608          * @param p1 end in the doc
609          */
610         public ScilabLeafElement(Element parent, AttributeSet a, int p0, int p1) {
611             super(parent, a, p0, p1);
612             start = p0;
613             type = funScanner.getLineType(p0, p1);
614             if (type == FUN) {
615                 info = funScanner.getFunctionInfo();
616             }
617         }
618
619         /**
620          * Reset type (normally called on a change in the document)
621          */
622         public void resetType() {
623             type = funScanner.getLineType(getStartOffset(), getEndOffset());
624             if (type == FUN) {
625                 info = funScanner.getFunctionInfo();
626             }
627         }
628
629         /**
630          * @return the type of this line (FUN,...)
631          */
632         public int getType() {
633             return type;
634         }
635
636         /**
637          * @return the info about this line containing a function def
638          */
639         public FunctionScanner.FunctionInfo getFunctionInfo() {
640             return info;
641         }
642
643         /**
644          * @return if this line begins with function
645          */
646         public boolean isFunction() {
647             return type == FUN;
648         }
649
650         /**
651          * @return if this line begins with endfunction
652          */
653         public boolean isEndfunction() {
654             return type == ENDFUN;
655         }
656
657         /**
658          * @return if this line is visible
659          */
660         public boolean isVisible() {
661             return visible;
662         }
663
664         /**
665          * @param b true if this line is visible
666          */
667         public void setVisible(boolean b) {
668             visible = b;
669         }
670
671         /**
672          * @return the position of the beginning of this element
673          */
674         public int getStart() {
675             return start;
676         }
677
678         /**
679          * @return String representation
680          */
681         public String toString() {
682             return info.functionName;
683         }
684     }
685 }