Preferences: continue to connect SciNotes
[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.nio.charset.Charset;
16
17 import java.util.Vector;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.TreeSet;
21 import java.util.Set;
22 import java.util.HashSet;
23 import java.util.Comparator;
24 import java.util.Iterator;
25
26 import javax.swing.text.GapContent;
27 import javax.swing.text.PlainDocument;
28 import javax.swing.text.BadLocationException;
29 import javax.swing.text.Element;
30 import javax.swing.text.AttributeSet;
31 import javax.swing.text.View;
32
33 import javax.swing.tree.DefaultMutableTreeNode;
34
35 import javax.swing.event.DocumentEvent;
36 import javax.swing.event.DocumentListener;
37
38 import org.scilab.modules.scinotes.utils.ConfigSciNotesManager;
39 import org.scilab.modules.scinotes.utils.SciNotesMessages;
40 import org.scilab.modules.console.utils.ScilabLaTeXViewer;
41
42 /**
43  * The class ScilabDocument is used to render a document .sci or .sce
44  * @author Calixte DENIZET
45  */
46 public class ScilabDocument extends PlainDocument implements DocumentListener {
47
48     private static final long serialVersionUID = -1227880612912063687L;
49
50     /**
51      * The EOL in mac OS
52      */
53     public static final String EOLMAC = "\r";
54
55     /**
56      * The EOL in windows OS
57      */
58     public static final String EOLWIN = "\r\n";
59
60     /**
61      * The EOL in unix OS
62      */
63     public static final String EOLUNIX = "\n";
64
65     private static final int GAPBUFFERCAPACITY = 2;
66     private static final String LINE_SEPARATOR = "line.separator";
67     private static final int INITFUNCTIONSNUMBER = 128;
68
69     private View view;
70     private List<String> saved = new Vector<String>();
71     private FunctionScanner funScanner;
72
73     private Set<String> functions = new HashSet<String>(INITFUNCTIONSNUMBER);
74
75     private boolean contentModified;
76     private boolean alphaOrder;
77
78     // Editor's default encoding is UTF-8
79     private String encoding;
80     private boolean updater = true;
81     private boolean binary;
82     private boolean autoIndent;
83     private boolean shouldMergeEdits;
84     private boolean undoManagerEnabled;
85     private CompoundUndoManager undo;
86
87     private ScilabEditorPane pane;
88     private boolean focused;
89
90     private String eolStyle;
91
92     /**
93      * Constructor
94      */
95     public ScilabDocument() {
96         super(new GapContent(GAPBUFFERCAPACITY));
97         setAsynchronousLoadPriority(2);
98
99         autoIndent = SciNotesOptions.getSciNotesDisplay().automaticIndent;
100         encoding = Charset.forName(SciNotesOptions.getSciNotesPreferences().encoding).toString();
101         eolStyle = SciNotesOptions.getSciNotesPreferences().eol;
102
103         undo = new CompoundUndoManager(this);
104         addUndoableEditListener(undo);
105         undoManagerEnabled = true;
106
107         contentModified = false;
108     }
109
110     /**
111      * Set the ScilabEditorPane associated with this doc
112      * @param pane the ScilabEditorPane
113      */
114     public void setEditorPane(ScilabEditorPane pane) {
115         this.pane = pane;
116     }
117
118     /**
119      * Get the ScilabEditorPane associated with this doc
120      * @return pane the ScilabEditorPane
121      */
122     public ScilabEditorPane getEditorPane() {
123         return pane;
124     }
125
126     /**
127      * Set to true of the document is focused in the EditorPane
128      * @param b the boolean
129      */
130     public void setFocused(boolean b) {
131         focused = b;
132     }
133
134     /**
135      * Create a lexer used to colorize the text
136      * @return ScilabLexer the lexer
137      */
138     public ScilabLexer createLexer() {
139         return new ScilabLexer(this);
140     }
141
142     /**
143      * @return the Set containing the functions name
144      */
145     public Set<String> getFunctionsInDoc() {
146         return functions;
147     }
148
149     /**
150      * Set the current view to render the code
151      * @param view the used view
152      */
153     public void setView(View view) {
154         this.view = view;
155     }
156
157     /**
158      * @return the current used view
159      */
160     public View getView() {
161         return view;
162     }
163
164     /**
165      * Get encoding
166      * @return String encoding
167      */
168     public String getEncoding() {
169         return encoding;
170     }
171
172     /**
173      * set Encoding
174      * @param encoding encoding
175      */
176     public void setEncoding(String encoding) {
177         this.encoding = encoding;
178     }
179
180     /**
181      * Set to true if it's a binary doc
182      * @param binary boolean
183      */
184     public void setBinary(boolean binary) {
185         this.binary = binary;
186     }
187
188     /**
189      * @return true if it's a binary file
190      */
191     public boolean getBinary() {
192         return binary;
193     }
194
195     /**
196      * set end of line value
197      * @param eol String
198      */
199     public void setEOL(String eol) {
200         this.eolStyle = eol;
201     }
202
203     /**
204      * get end of line
205      * @return end of line
206      */
207     public String getEOL() {
208         return this.eolStyle;
209     }
210
211     /**
212      * get end of line
213      * @return end of line
214      */
215     public String getDefaultEOL() {
216         return System.getProperty(LINE_SEPARATOR);
217     }
218
219     /**
220      * isUpdater
221      * @return boolean
222      */
223     public boolean isUpdater() {
224         return updater;
225     }
226
227     /**
228      * getAutoIndent
229      * @return boolean
230      */
231     public boolean getAutoIndent() {
232         return autoIndent;
233     }
234
235     /**
236      * setAutoIndent
237      * @param b boolean
238      */
239     public void setAutoIndent(boolean b) {
240         autoIndent = b;
241     }
242
243     /**
244      * setUpdater
245      * @param updaterDisabled boolean
246      */
247     public void setUpdater(boolean updaterDisabled) {
248         this.updater = updaterDisabled;
249     }
250
251     /**
252      * Get document text
253      * @return String
254      */
255     public String getText() {
256         try {
257             return getText(0, getLength());
258         } catch (BadLocationException e) {
259             return "";
260         }
261     }
262
263     /**
264      * Begins a compound edit (for the undo)
265      */
266     public void mergeEditsBegin() {
267         undo.endCompoundEdit();
268         undo.startCompoundEdit();
269     }
270
271     /**
272      * Ends a compound edit (for the undo)
273      */
274     public void mergeEditsEnd() {
275         undo.endCompoundEdit();
276     }
277
278     /**
279      * getUndoManager
280      * @return CompoundUndoManager
281      */
282     public CompoundUndoManager getUndoManager() {
283         return undo;
284     }
285
286     /**
287      * disableUndoManager
288      */
289     public void disableUndoManager() {
290         if (undoManagerEnabled) {
291             this.removeUndoableEditListener(undo);
292             undoManagerEnabled = false;
293         }
294     }
295
296     /**
297      * enableUndoManager
298      */
299     public void enableUndoManager() {
300         if (!undoManagerEnabled) {
301             this.addUndoableEditListener(undo);
302             undoManagerEnabled = true;
303         }
304     }
305
306     /**
307      * isContentModified
308      * @return boolean
309      */
310     public boolean isContentModified() {
311         return contentModified;
312     }
313
314     /**
315      * setContentModified
316      * @param contentModified boolean
317      */
318     public void setContentModified(boolean contentModified) {
319         this.contentModified = contentModified;
320         if (!contentModified) {
321             undo.setReference();
322             pane.updateTitle();
323         }
324     }
325
326     /**
327      * dump document on stderr with line positions
328      */
329     public void dump() {
330         readLock();
331         try {
332             Element root = getDefaultRootElement();
333             for (int i = 0; i != root.getElementCount(); ++i) {
334                 Element e = root.getElement(i);
335                 int start = e.getStartOffset();
336                 int end = e.getEndOffset();
337                 System.err.println("line " + i + " from: " + start + " to: " + end + ":|" + getText(start, end - start) + "|");
338             }
339         } catch (BadLocationException e) {
340             System.err.println(e);
341         }
342         readUnlock();
343     }
344
345     /**
346      * Search the position of the function name in the Document
347      * @param name the name of the function
348      * @return the position where to go or -1 if not found
349      */
350     public int searchFunctionByName(String name) {
351         Element root = getDefaultRootElement();
352         for (int i = 0; i < root.getElementCount(); i++) {
353             Element e = root.getElement(i);
354             if (e instanceof ScilabLeafElement) {
355                 ScilabLeafElement se = (ScilabLeafElement) e;
356                 if (se.isFunction() && se.getFunctionInfo().functionName.equals(name)) {
357                     return e.getStartOffset();
358                 }
359             }
360         }
361         return -1;
362     }
363
364     /**
365      * @return a list containing all the infos about functions available in this document
366      */
367     public List<FunctionScanner.FunctionInfo> getFunctionInfo() {
368         List<FunctionScanner.FunctionInfo> list = new ArrayList<FunctionScanner.FunctionInfo>();
369         Element root = getDefaultRootElement();
370         for (int i = 0; i < root.getElementCount(); i++) {
371             Element e = root.getElement(i);
372             if (e instanceof ScilabLeafElement) {
373                 ScilabLeafElement se = (ScilabLeafElement) e;
374                 if (se.isFunction()) {
375                     list.add(se.getFunctionInfo());
376                 }
377             }
378         }
379         return list;
380     }
381
382     /**
383      * @param alphaOrder is true if names must be sorted with alphabetic order
384      */
385     public void setAlphaOrderInTree(boolean alphaOrder) {
386         this.alphaOrder = alphaOrder;
387     }
388
389     /**
390      * Fill a tree with function's name according to alphabetic order or not
391      * @param base to fill
392      */
393     public synchronized void fillTreeFuns(DefaultMutableTreeNode base) {
394         Element root = getDefaultRootElement();
395         int nlines = root.getElementCount();
396         if (!alphaOrder) {
397             for (int i = 0; i < nlines; i++) {
398                 Element elem = root.getElement(i);
399                 if (elem instanceof ScilabDocument.ScilabLeafElement) {
400                     int type = ((ScilabDocument.ScilabLeafElement) elem).getType();
401                     switch (type) {
402                         case ScilabDocument.ScilabLeafElement.NOTHING :
403                             break;
404                         case ScilabDocument.ScilabLeafElement.FUN :
405                             base.add(new DefaultMutableTreeNode(elem));
406                             break;
407                         case ScilabDocument.ScilabLeafElement.ENDFUN :
408                             break;
409                         default :
410                             break;
411                     }
412                 }
413             }
414         } else {
415             Set<DefaultMutableTreeNode> set = new TreeSet<DefaultMutableTreeNode>(new Comparator<DefaultMutableTreeNode>() {
416                 public int compare(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
417                     ScilabLeafElement l1 = (ScilabLeafElement) o1.getUserObject();
418                     ScilabLeafElement l2 = (ScilabLeafElement) o2.getUserObject();
419                     int n = l1.getFunctionName().compareTo(l2.getFunctionName());
420                     if (n != 0) {
421                         return n;
422                     }
423                     return l1.getStartOffset() - l2.getStartOffset();
424                 }
425
426                 public boolean equals(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
427                     return false;
428                 }
429             });
430             for (int i = 0; i < nlines; i++) {
431                 Element elem = root.getElement(i);
432                 if (elem instanceof ScilabDocument.ScilabLeafElement) {
433                     int type = ((ScilabDocument.ScilabLeafElement) elem).getType();
434                     switch (type) {
435                         case ScilabDocument.ScilabLeafElement.NOTHING :
436                             break;
437                         case ScilabDocument.ScilabLeafElement.FUN :
438                             set.add(new DefaultMutableTreeNode(elem));
439                             break;
440                         case ScilabDocument.ScilabLeafElement.ENDFUN :
441                             break;
442                         default :
443                             break;
444                     }
445                 }
446             }
447             Iterator<DefaultMutableTreeNode> iter = set.iterator();
448             while (iter.hasNext()) {
449                 base.add(iter.next());
450             }
451         }
452     }
453
454     /**
455      * Fill a tree with anchor's name according to alphabetic order or not
456      * @param base to fill
457      */
458     public synchronized void fillTreeAnchors(DefaultMutableTreeNode base) {
459         Element root = getDefaultRootElement();
460         int nlines = root.getElementCount();
461         if (!alphaOrder) {
462             for (int i = 0; i < nlines; i++) {
463                 ScilabLeafElement elem = (ScilabLeafElement) root.getElement(i);
464                 if (elem.isAnchor()) {
465                     base.add(new DefaultMutableTreeNode(elem));
466                 }
467             }
468         } else {
469             Set<DefaultMutableTreeNode> set = new TreeSet<DefaultMutableTreeNode>(new Comparator<DefaultMutableTreeNode>() {
470                 public int compare(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
471                     ScilabLeafElement l1 = (ScilabLeafElement) o1.getUserObject();
472                     ScilabLeafElement l2 = (ScilabLeafElement) o2.getUserObject();
473                     int n = l1.getAnchorName().compareTo(l2.getAnchorName());
474                     if (n != 0) {
475                         return n;
476                     }
477                     return l1.getStartOffset() - l2.getStartOffset();
478                 }
479
480                 public boolean equals(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
481                     return false;
482                 }
483             });
484             for (int i = 0; i < nlines; i++) {
485                 ScilabLeafElement elem = (ScilabLeafElement) root.getElement(i);
486                 if (elem.isAnchor()) {
487                     set.add(new DefaultMutableTreeNode(elem));
488                 }
489             }
490             Iterator<DefaultMutableTreeNode> iter = set.iterator();
491             while (iter.hasNext()) {
492                 base.add(iter.next());
493             }
494         }
495     }
496
497     /**
498      * @return the first function name which appears in this doc or null
499      */
500     public String getFirstFunctionName() {
501         Element root = getDefaultRootElement();
502         for (int i = 0; i < root.getElementCount(); i++) {
503             Element e = root.getElement(i);
504             if (e instanceof ScilabLeafElement) {
505                 ScilabLeafElement se = (ScilabLeafElement) e;
506                 if (se.isFunction()) {
507                     return se.getFunctionInfo().functionName;
508                 }
509             }
510         }
511         return null;
512     }
513
514     /**
515      * @param line the number of the line where to begin the search
516      * @return the next anchor
517      */
518     public int nextAnchorFrom(int line) {
519         Element root = getDefaultRootElement();
520         for (int i = line + 1; i < root.getElementCount(); i++) {
521             ScilabLeafElement se = (ScilabLeafElement) root.getElement(i);
522             if (se.isAnchor()) {
523                 return i;
524             }
525         }
526         for (int i = 0; i < line; i++) {
527             ScilabLeafElement se = (ScilabLeafElement) root.getElement(i);
528             if (se.isAnchor()) {
529                 return i;
530             }
531         }
532
533         return -1;
534     }
535
536     /**
537      * @param line the number of the line where to begin the search
538      * @return the previous anchor
539      */
540     public int previousAnchorFrom(int line) {
541         Element root = getDefaultRootElement();
542         for (int i = line - 1; i >= 0; i--) {
543             ScilabLeafElement se = (ScilabLeafElement) root.getElement(i);
544             if (se.isAnchor()) {
545                 return i;
546             }
547         }
548         for (int i = root.getElementCount() - 1; i > line; i--) {
549             ScilabLeafElement se = (ScilabLeafElement) root.getElement(i);
550             if (se.isAnchor()) {
551                 return i;
552             }
553         }
554
555         return -1;
556     }
557
558     /**
559      * Get the anchors between two positions
560      * @param start the beginning
561      * @param end the end
562      * @return a list of the anchors
563      */
564     public List<Anchor> getAnchorsBetween(int start, int end) {
565         Element root = getDefaultRootElement();
566         int lineS = root.getElementIndex(start);
567         int lineE = root.getElementIndex(end);
568         List<Anchor> list = new ArrayList<Anchor>();
569         for (int i = lineS; i <= lineE; i++) {
570             final ScilabLeafElement se = (ScilabLeafElement) root.getElement(i);
571             if (se.isAnchor()) {
572                 list.add(new Anchor(i, se.getAnchorName()));
573             }
574         }
575
576         return list;
577     }
578
579     /**
580      * Get the lhs/rhs args used in a function declaration
581      * @param pos the position in the document
582      * @return the two lists containing args and returned values or null if we are not
583      * in a function
584      */
585     public List<String>[] getInOutArgs(int pos) {
586         Element root = getDefaultRootElement();
587         int index = root.getElementIndex(pos);
588         int compt = 0;
589         while (index != -1) {
590             ScilabLeafElement e = (ScilabLeafElement) root.getElement(index--);
591             switch (e.getType()) {
592                 case ScilabLeafElement.NOTHING :
593                     break;
594                 case ScilabLeafElement.FUN :
595                     if (compt == 0) {
596                         FunctionScanner.FunctionInfo info = e.getFunctionInfo();
597                         return new List[] {info.returnValues, info.argsValues};
598                     } else {
599                         compt++;
600                     }
601                     break;
602                 case ScilabLeafElement.ENDFUN :
603                     compt--;
604                     break;
605                 default :
606             }
607         }
608         return null;
609     }
610
611     /**
612      * Get the function name where the caret is
613      * @param pos the position in the document
614      * @return the nearest function name
615      */
616     public String getCurrentFunction(int pos) {
617         Element root = getDefaultRootElement();
618         int index = root.getElementIndex(pos);
619         int line = index;
620         int compt = 0;
621         while (index != -1) {
622             ScilabLeafElement e = (ScilabLeafElement) root.getElement(index--);
623             switch (e.getType()) {
624                 case ScilabLeafElement.NOTHING :
625                     break;
626                 case ScilabLeafElement.FUN :
627                     if (compt == 0) {
628                         String str = e.getFunctionInfo().functionName;
629                         if (str == null) {
630                             str = SciNotesMessages.UNKNOWN_FUNCTION;
631                         }
632                         return String.format(SciNotesMessages.POSFUN_IN_DOC, line + 1, pos - root.getElement(line).getStartOffset(), str, line - index);
633                     } else {
634                         compt++;
635                     }
636                     break;
637                 case ScilabLeafElement.ENDFUN :
638                     compt--;
639                     break;
640                 default :
641             }
642         }
643         return String.format(SciNotesMessages.POS_IN_DOC, line + 1, pos - root.getElement(line).getStartOffset());
644     }
645
646     /**
647      * lock
648      */
649     public void lock() {
650         super.writeLock();
651     }
652
653     /**
654      * unlock
655      */
656     public void unlock() {
657         super.writeUnlock();
658     }
659
660     /**
661      * Nothing !
662      * @param e the event
663      */
664     public void changedUpdate(DocumentEvent e) { }
665
666     /**
667      * Called when an insertion is made in the doc
668      * @param e the event
669      */
670     public void insertUpdate(DocumentEvent e) {
671         handleEvent(e);
672     }
673
674     /**
675      * Called when a remove is made in the doc
676      * @param e the event
677      */
678     public void removeUpdate(DocumentEvent e) {
679         handleEvent(e);
680     }
681
682     /**
683      * {@inheritDoc}
684      */
685     protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) {
686         // Fix bug 8277 in putting attr=null
687         // Java, by default, highlights the chinese chars when entered on keyboard
688         super.insertUpdate(chng, null);
689     }
690
691     /**
692      * @param ev the DocumentEvent to handle
693      */
694     private void handleEvent(DocumentEvent ev) {
695         if (!contentModified && pane != null) {
696             contentModified = true;
697             pane.updateTitle();
698         }
699
700         DocumentEvent.ElementChange chg = ev.getChange(getDefaultRootElement());
701         if (chg != null) {
702             Element[] added = chg.getChildrenAdded();
703             Element[] removed = chg.getChildrenRemoved();
704             if ((added != null && added.length > 0) || (removed != null && removed.length > 0)) {
705                 for (int i = 0; i < removed.length; i++) {
706                     String name = ((ScilabLeafElement) removed[i]).getFunctionName();
707                     if (name != null && name.length() != 0) {
708                         functions.remove(name);
709                     }
710                 }
711                 for (int i = 0; i < added.length; i++) {
712                     ((ScilabLeafElement) added[i]).resetType();
713                     ((ScilabLeafElement) added[i]).resetTypeWhenBroken();
714                     String name = ((ScilabLeafElement) added[i]).getFunctionName();
715                     if (name != null && name.length() != 0) {
716                         functions.add(name);
717                     }
718                 }
719             }
720         } else {
721             // change occured only on one line
722             Element root = getDefaultRootElement();
723             int index = root.getElementIndex(ev.getOffset());
724             ScilabLeafElement line = (ScilabLeafElement) root.getElement(index);
725             boolean broken = line.isBroken();
726             if (line.resetType() == ScilabLeafElement.FUN || broken != line.isBroken()
727                     || (index > 0 && ((ScilabLeafElement) root.getElement(index - 1)).isBroken())) {
728                 pane.repaint();
729             }
730         }
731
732         KeywordEvent e = pane.getKeywordEvent();
733         if (ScilabLexerConstants.isLaTeX(e.getType())) {
734             try {
735                 int start = e.getStart();
736                 int end = start + e.getLength();
737                 String exp = getText(start, e.getLength());
738                 int height = pane.getScrollPane().getHeight() + pane.getScrollPane().getVerticalScrollBar().getValue();
739                 ScilabLaTeXViewer.displayExpressionIfVisible(pane, height, exp, start, end);
740             } catch (BadLocationException ex) { }
741         }
742     }
743
744     /**
745      * @overload #createDefaultRoot
746      * @return the element base
747      */
748     protected AbstractElement createDefaultRoot() {
749         funScanner = new FunctionScanner(this);
750         BranchElement map = (BranchElement) createBranchElement(null, null);
751         Element line = createLeafElement(map, null, 0, 1);
752         map.replace(0, 0, new Element[] {line});
753         return map;
754     }
755
756     /**
757      * @overload #createLeafElement
758      * @param parent the parent Element
759      * @param a an AttributeSet
760      * @param p0 start in the doc
761      * @param p1 end in the doc
762      * @return the created LeafElement
763      */
764     protected Element createLeafElement(Element parent, AttributeSet a, int p0, int p1) {
765         return new ScilabLeafElement(parent, a, p0, p1);
766     }
767
768     /**
769      * Inner class to consider the type of a line :
770      *  - FUN : function y=foo(x)
771      *  - ENDFUN : endfunction
772      *  - NOTHING : bla bla bla
773      * This inner class is useful to make a line numbering compatible with the whereami macro.
774      */
775     public class ScilabLeafElement extends LeafElement {
776
777         private static final long serialVersionUID = 4389590345677765643L;
778
779         /**
780          * Nothing in this line
781          */
782         public static final int NOTHING = 0;
783
784         /**
785          * function ... in this line
786          */
787         public static final int FUN = 1;
788
789         /**
790          * endfunction in this line
791          */
792         public static final int ENDFUN = 2;
793
794         /**
795          * broken line
796          */
797         public static final int BROKEN = 4;
798
799         private boolean visible = true;
800         private int type;
801         private FunctionScanner.FunctionInfo info;
802         private boolean broken;
803         private boolean brokenString;
804
805         private boolean anchor;
806         private String anchorName;
807
808         /**
809          * The same constructor as in LeafElement.
810          * @param parent the parent Element
811          * @param a an AttributeSet
812          * @param p0 start in the doc
813          * @param p1 end in the doc
814          */
815         public ScilabLeafElement(Element parent, AttributeSet a, int p0, int p1) {
816             super(parent, a, p0, p1);
817             type = funScanner.getLineType(p0, p1);
818             if ((type & BROKEN) == BROKEN) {
819                 broken = true;
820                 type -= BROKEN;
821             }
822
823             if (type == FUN) {
824                 info = funScanner.getFunctionInfo();
825             }
826         }
827
828         /**
829          * Reset type (normally called on a change in the document)
830          * @return the new type
831          */
832         public int resetType() {
833             String oldName = "";
834             if (type == FUN) {
835                 oldName = info.functionName;
836             }
837
838             type = funScanner.getLineType(getStartOffset(), getEndOffset());
839
840             if ((type & BROKEN) == BROKEN) {
841                 broken = true;
842                 type -= BROKEN;
843             } else {
844                 broken = false;
845             }
846
847             if (type == FUN) {
848                 info = funScanner.getFunctionInfo();
849                 if (info.functionName != null) {
850                     if (!info.functionName.equals(oldName)) {
851                         functions.remove(oldName);
852                         functions.add(info.functionName);
853                     }
854                 } else {
855                     functions.remove(oldName);
856                 }
857             }
858
859             resetTypeWhenBroken();
860
861             return type;
862         }
863
864         /**
865          * If the previous line is broken, then this line is a part of it
866          * so we need to resetType of the previous.
867          */
868         public void resetTypeWhenBroken() {
869             int p0 = getStartOffset();
870             if (p0 != 0) {
871                 Element parent = getParentElement();
872                 ScilabLeafElement elem = (ScilabLeafElement) parent.getElement(parent.getElementIndex(p0 - 1));
873                 if (elem.broken) {
874                     elem.resetType();
875                 }
876             }
877         }
878
879         /**
880          * @return the type of this line (FUN,...)
881          */
882         public int getType() {
883             return type;
884         }
885
886         /**
887          * @return the info about this line containing a function def
888          */
889         public FunctionScanner.FunctionInfo getFunctionInfo() {
890             return info;
891         }
892
893         /**
894          * @return if this line begins with function
895          */
896         public boolean isFunction() {
897             return type == FUN;
898         }
899
900         /**
901          * @return if this line begins with endfunction
902          */
903         public boolean isEndfunction() {
904             return type == ENDFUN;
905         }
906
907         /**
908          * @return if this line is visible
909          */
910         public boolean isVisible() {
911             return visible;
912         }
913
914         /**
915          * @param b true if this line is visible
916          */
917         public void setVisible(boolean b) {
918             visible = b;
919         }
920
921         /**
922          * @return if this line is broken
923          */
924         public boolean isBroken() {
925             return broken;
926         }
927
928         /**
929          * @param b true if this line is broken
930          */
931         public void setBroken(boolean b) {
932             broken = b;
933         }
934
935         /**
936          * @return if this line is broken
937          */
938         public boolean isBrokenString() {
939             return brokenString;
940         }
941
942         /**
943          * @param b true if this line is broken in a string
944          */
945         public void setBrokenString(boolean b) {
946             brokenString = b;
947             if (b) {
948                 broken = true;
949             }
950         }
951
952         /**
953          * @return the function's name
954          */
955         public String getFunctionName() {
956             if (type == FUN) {
957                 return info.functionName;
958             }
959             return "";
960         }
961
962         /**
963          * @return if this line is an anchor
964          */
965         public boolean isAnchor() {
966             return anchor;
967         }
968
969         /**
970          * @param name the name of the anchor, if null remove
971          * the anchor.
972          */
973         public void setAnchor(String name) {
974             if (name == null) {
975                 anchor = false;
976                 return;
977             }
978
979             anchor = true;
980             anchorName = name;
981         }
982
983         /**
984          * @return the name of the anchor if exists
985          */
986         public String getAnchorName() {
987             if (anchor) {
988                 return anchorName;
989             } else {
990                 return "";
991             }
992         }
993
994         /**
995          * @return String representation
996          */
997         public String toString() {
998             if (anchor) {
999                 if (type == FUN) {
1000                     return "function: " + info.functionName + " & anchor: " + anchorName;
1001                 } else {
1002                     return anchorName;
1003                 }
1004             }
1005             return info.functionName;
1006         }
1007     }
1008
1009     /**
1010      * Inner class to get infos on anchor
1011      */
1012     public class Anchor {
1013
1014         private int line;
1015         private String name;
1016
1017         /**
1018          * Default constructor
1019          * @param line the line where the anchor is
1020          * @param name the anchor's name
1021          */
1022         public Anchor(int line, String name) {
1023             this.line = line;
1024             this.name = name;
1025         }
1026
1027         /**
1028          * {@inheritDoc}
1029          */
1030         public String toString() {
1031             return name;
1032         }
1033
1034         /**
1035          * @return the line number
1036          */
1037         public int getLine() {
1038             return line;
1039         }
1040     }
1041 }