SciNotes: code navigator was broken
[scilab.git] / scilab / modules / scinotes / src / java / org / scilab / modules / scinotes / utils / NavigatorWindow.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.utils;
14
15 import java.awt.Color;
16 import java.awt.Component;
17 import java.awt.Container;
18 import java.awt.Dimension;
19 import java.awt.FocusTraversalPolicy;
20 import java.awt.event.ActionEvent;
21 import java.awt.event.ActionListener;
22 import java.awt.event.FocusEvent;
23 import java.awt.event.FocusListener;
24 import java.awt.event.KeyAdapter;
25 import java.awt.event.KeyEvent;
26 import java.awt.event.KeyListener;
27 import java.awt.event.MouseAdapter;
28 import java.awt.event.MouseEvent;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.UUID;
35
36 import javax.swing.DefaultComboBoxModel;
37 import javax.swing.GroupLayout;
38 import javax.swing.GroupLayout.Alignment;
39 import javax.swing.JComboBox;
40 import javax.swing.JLabel;
41 import javax.swing.JMenuItem;
42 import javax.swing.JPopupMenu;
43 import javax.swing.JScrollPane;
44 import javax.swing.JSeparator;
45 import javax.swing.JTextField;
46 import javax.swing.JTree;
47 import javax.swing.KeyStroke;
48 import javax.swing.LayoutStyle.ComponentPlacement;
49 import javax.swing.SwingUtilities;
50 import javax.swing.event.DocumentEvent;
51 import javax.swing.event.DocumentListener;
52 import javax.swing.event.TreeExpansionEvent;
53 import javax.swing.event.TreeExpansionListener;
54 import javax.swing.text.Element;
55 import javax.swing.tree.DefaultMutableTreeNode;
56 import javax.swing.tree.DefaultTreeModel;
57 import javax.swing.tree.TreePath;
58
59 import org.flexdock.docking.event.DockingEvent;
60 import org.scilab.modules.gui.bridge.menuitem.SwingScilabMenuItem;
61 import org.scilab.modules.gui.bridge.tab.SwingScilabDockablePanel;
62 import org.scilab.modules.gui.bridge.window.SwingScilabWindow;
63 import org.scilab.modules.gui.events.callback.CommonCallBack;
64 import org.scilab.modules.gui.menu.Menu;
65 import org.scilab.modules.gui.menu.ScilabMenu;
66 import org.scilab.modules.gui.menubar.MenuBar;
67 import org.scilab.modules.gui.menubar.ScilabMenuBar;
68 import org.scilab.modules.gui.menuitem.MenuItem;
69 import org.scilab.modules.gui.menuitem.ScilabMenuItem;
70 import org.scilab.modules.gui.tab.SimpleTab;
71 import org.scilab.modules.gui.tabfactory.ScilabTabFactory;
72 import org.scilab.modules.gui.textbox.ScilabTextBox;
73 import org.scilab.modules.gui.textbox.TextBox;
74 import org.scilab.modules.gui.toolbar.ToolBar;
75 import org.scilab.modules.gui.utils.ClosingOperationsManager;
76 import org.scilab.modules.gui.utils.WindowsConfigurationManager;
77 import org.scilab.modules.scinotes.SciNotes;
78 import org.scilab.modules.scinotes.SciNotesGUI;
79 import org.scilab.modules.scinotes.ScilabDocument;
80 import org.scilab.modules.scinotes.ScilabEditorPane;
81 import org.scilab.modules.scinotes.tabfactory.CodeNavigatorTabFactory;
82
83 /**
84  *
85  * @author Calixte DENIZET
86  */
87 @SuppressWarnings(value = { "serial" })
88 public final class NavigatorWindow extends SwingScilabDockablePanel implements DocumentListener,
89     TreeExpansionListener {
90
91     private static final String EMPTY = "";
92
93     private Map<ScilabEditorPane, DefaultMutableTreeNode> mapNode = new HashMap();
94     private Map<ScilabEditorPane, TreePath> mapFunPath = new HashMap();
95     private Map<ScilabEditorPane, TreePath> mapAnchorPath = new HashMap();
96     private List<ScilabEditorPane> panes = new ArrayList<ScilabEditorPane>();
97
98     private SciNotes editor;
99
100     private JTree functionNavigator;
101     private DefaultTreeModel model;
102     private ScilabEditorPane pane;
103     private ScilabDocument doc;
104     private boolean alphaOrder;
105
106     private boolean isAbsolute = true;
107     private boolean lineNumberActive = true;
108     private boolean locked;
109     private boolean init;
110
111     private JSeparator jSeparator1;
112     private JLabel labelGotoLine;
113     private JLabel labelNumerotation;
114     private JTextField lineNumber;
115     private JComboBox numType;
116     private JScrollPane scrollPane;
117
118     private SwingScilabWindow parentWindow;
119
120     static {
121         ScilabTabFactory.getInstance().addTabFactory(CodeNavigatorTabFactory.getInstance());
122     }
123
124     /**
125      * Creates new Navigator Window
126      */
127     public NavigatorWindow(SciNotes editor, String uuid) {
128         super(SciNotesMessages.CODE_NAVIGATOR, uuid == null ? UUID.randomUUID().toString() : uuid);
129         this.editor = editor;
130         editor.addNavigator(this);
131         setContentPane(new javax.swing.JPanel());
132         ConfigSciNotesManager.saveCodeNavigatorState(editor.getPersistentId(), getPersistentId());
133         WindowsConfigurationManager.restorationFinished(this);
134     }
135
136     /**
137      * Creates new Navigator Window
138      */
139     public NavigatorWindow(SciNotes editor) {
140         this(editor, null);
141     }
142
143     /**
144      * @param doc the doc to update
145      */
146     public void update(ScilabDocument doc) {
147         if (!init) {
148             initTree();
149             initComponents();
150             functionNavigator.addTreeExpansionListener(this);
151             init = true;
152         }
153
154         if (doc != null) {
155             if (this.doc != null) {
156                 this.doc.removeDocumentListener(this);
157             }
158
159             this.doc = doc;
160             this.pane = doc.getEditorPane();
161
162             this.doc.addDocumentListener(this);
163
164             if (!mapNode.containsKey(pane)) {
165                 initTree();
166             } else {
167                 updateTree();
168             }
169         }
170     }
171
172     public void addEditorPane(ScilabEditorPane pane) {
173         panes.add(pane);
174         update((ScilabDocument) pane.getDocument());
175     }
176
177     /**
178      * Update the navigator
179      */
180     public void update() {
181         updateTree();
182     }
183
184     /**
185      * @param doc to use
186      */
187     public void activateNavigator(ScilabDocument doc) {
188         initTree();
189         update(doc);
190     }
191
192     /**
193      * Set the parent window
194      */
195     public void setParentWindow() {
196         this.parentWindow = SwingScilabWindow.createWindow(true);
197         parentWindow.addTab(this);
198         parentWindow.setVisible(true);
199     }
200
201     /**
202      * Get the parent window id for this tab
203      * @return the id of the parent window
204      */
205     public SwingScilabWindow getParentWindow() {
206         return this.parentWindow;
207     }
208
209     /**
210      * {@inheritDoc}
211      */
212     public SimpleTab getAsSimpleTab() {
213         return this;
214     }
215
216     /**
217      * {@inheritDoc}
218      */
219     public void addInfoBar(TextBox infoBarToAdd) {
220         setInfoBar(infoBarToAdd);
221     }
222
223     /**
224      * {@inheritDoc}
225      */
226     public void addMenuBar(MenuBar menuBarToAdd) {
227         setMenuBar(menuBarToAdd);
228     }
229
230     /**
231      * {@inheritDoc}
232      */
233     public void addToolBar(ToolBar toolBarToAdd) {
234         setToolBar(toolBarToAdd);
235     }
236
237     /**
238      * Close this Navigator.
239      */
240     public void closeNavigator() {
241         editor.removeNavigator();
242         mapNode.clear();
243         mapFunPath.clear();
244         mapAnchorPath.clear();
245         functionNavigator = null;
246         model = null;
247         pane = null;
248         doc = null;
249         for (ScilabEditorPane p : panes) {
250             ((ScilabDocument) p.getDocument()).removeDocumentListener(this);
251         }
252         panes.clear();
253     }
254
255     /**
256      * Nothing !
257      * @param e the event
258      */
259     public void changedUpdate(DocumentEvent e) { }
260
261     /**
262      * Called when an insertion is made in the doc
263      * @param e the event
264      */
265     public void insertUpdate(DocumentEvent e) {
266         handleEvent(e.getOffset(), e.getLength());
267     }
268
269     /**
270      * Called when a remove is made in the doc
271      * @param e the event
272      */
273     public void removeUpdate(DocumentEvent e) {
274         handleEvent(e.getOffset(), e.getLength());
275     }
276
277     /**
278      * {@inheritDoc}
279      */
280     public void dockingComplete(DockingEvent evt) {
281         super.dockingComplete(evt);
282         changeToolBar();
283     }
284
285     /**
286      * Try to add a SciNotes toolbar
287      */
288     public void changeToolBar() {
289         SwingScilabWindow win = (SwingScilabWindow) SwingUtilities.getAncestorOfClass(SwingScilabWindow.class, this);
290         if (win != null && win.getDockingPort() != null) {
291             Set<SwingScilabDockablePanel> set = (Set<SwingScilabDockablePanel>) win.getDockingPort().getDockables();
292             for (SwingScilabDockablePanel tab : set) {
293                 if (tab == editor) {
294                     addToolBar(editor.getToolBar());
295                     break;
296                 }
297             }
298         }
299     }
300
301     /**
302      * {@inheritDoc}
303      */
304     public void undockingComplete(DockingEvent evt) {
305         super.undockingComplete(evt);
306         addToolBar(null);
307     }
308
309     /**
310      * {@inheritDoc}
311      */
312     public void treeCollapsed(TreeExpansionEvent evt) { }
313
314     /**
315      * {@inheritDoc}
316      */
317     public void treeExpanded(TreeExpansionEvent evt) {
318         if (!locked) {
319             TreePath path = evt.getPath();
320             DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
321             ScilabEditorPane savePane = pane;
322             ScilabDocument saveDoc = doc;
323             boolean update = false;
324             if (node.getUserObject() instanceof String && SciNotesMessages.FUNCTIONS.equals(node.getUserObject())) {
325                 pane = (ScilabEditorPane) ((DefaultMutableTreeNode) node.getParent()).getUserObject();
326                 doc = (ScilabDocument) pane.getDocument();
327                 locked = true;
328                 updateFunctions();
329                 functionNavigator.expandPath(mapFunPath.get(pane));
330                 update = true;
331             } else if (node.getUserObject() instanceof String && SciNotesMessages.ANCHORS.equals(node.getUserObject())) {
332                 pane = (ScilabEditorPane) ((DefaultMutableTreeNode) node.getParent()).getUserObject();
333                 doc = (ScilabDocument) pane.getDocument();
334                 locked = true;
335                 updateAnchors();
336                 functionNavigator.expandPath(mapAnchorPath.get(pane));
337                 update = true;
338             } else if (node.getUserObject() instanceof ScilabEditorPane) {
339                 pane = (ScilabEditorPane) node.getUserObject();
340                 doc = (ScilabDocument) pane.getDocument();
341                 boolean expanded = functionNavigator.isExpanded(mapFunPath.get(pane));
342                 updateFunctions();
343                 if (expanded) {
344                     locked = true;
345                     functionNavigator.expandPath(mapFunPath.get(pane));
346                 }
347                 update = true;
348             }
349             if (update) {
350                 pane = savePane;
351                 doc = saveDoc;
352             }
353         } else {
354             locked = false;
355         }
356     }
357
358     /**
359      * @param pane the pane which has been splitted
360      * @param split the new pane
361      */
362     public void changePaneOnSplit(ScilabEditorPane pane, ScilabEditorPane split) {
363         if (mapNode.containsKey(pane)) {
364             DefaultMutableTreeNode node = mapNode.get(pane);
365             node.setUserObject(split);
366             mapNode.put(split, mapNode.get(pane));
367             mapNode.remove(pane);
368             mapFunPath.put(split, mapFunPath.get(pane));
369             mapFunPath.remove(pane);
370             mapAnchorPath.put(split, mapAnchorPath.get(pane));
371             mapAnchorPath.remove(pane);
372         }
373     }
374
375     /**
376      * @param pane to remove
377      */
378     public void removePane(ScilabEditorPane pane) {
379         if (mapNode.containsKey(pane)) {
380             DefaultMutableTreeNode node = mapNode.get(pane);
381             ((DefaultMutableTreeNode) model.getRoot()).remove(node);
382             model.reload();
383             mapNode.remove(pane);
384             mapFunPath.remove(pane);
385             mapAnchorPath.remove(pane);
386             panes.remove(pane);
387             this.pane = null;
388             this.doc = null;
389         }
390     }
391
392     /**
393      * This method is called from within the constructor to
394      * initialize the form.
395      */
396     private void initComponents() {
397         labelNumerotation = new JLabel();
398         lineNumber = new JTextField();
399         numType = new JComboBox();
400         jSeparator1 = new JSeparator();
401         labelGotoLine = new JLabel();
402         scrollPane = new JScrollPane();
403
404         setTitle(SciNotesMessages.CODE_NAVIGATOR);
405         updateUI();
406
407         labelNumerotation.setText(SciNotesMessages.NUMEROTATION);
408         labelNumerotation.setFocusable(false);
409
410         lineNumber.addKeyListener(new KeyListener() {
411             public void keyTyped(KeyEvent ke) { }
412             public void keyPressed(KeyEvent ke) { }
413
414             public void keyReleased(KeyEvent ke) {
415                 updateCaretPosition();
416             }
417         });
418
419         lineNumber.addFocusListener(new FocusListener() {
420             public void focusGained(FocusEvent e) {
421                 updateCaretPosition();
422             }
423
424             public void focusLost(FocusEvent e) {
425             }
426         });
427
428         numType.setModel(new DefaultComboBoxModel(new String[] {SciNotesMessages.ABSOLUTE, SciNotesMessages.RELATIVE}));
429         numType.addActionListener(new ActionListener() {
430             public void actionPerformed(ActionEvent evt) {
431                 int i = numType.getSelectedIndex();
432                 if (i == 0 && !isAbsolute) {
433                     lineNumber.setText(EMPTY);
434                     isAbsolute = true;
435                 } else if (i == 1 && isAbsolute) {
436                     lineNumber.setText(EMPTY);
437                     isAbsolute = false;
438                 }
439             }
440         });
441
442         labelGotoLine.setText(SciNotesMessages.GO_TO_LINE);
443         labelGotoLine.setFocusable(false);
444
445         functionNavigator.addMouseListener(new MouseAdapter() {
446             public void mousePressed(MouseEvent e) {
447                 int row = functionNavigator.getRowForLocation(e.getX(), e.getY());
448                 if (e.getClickCount() == 2) {
449                     handleSelectedItem(row);
450                 }
451             }
452         });
453
454         functionNavigator.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "toggle");
455         functionNavigator.addKeyListener(new KeyAdapter() {
456             public void keyTyped(KeyEvent e) {
457                 if (e.getKeyChar() == '\n') {
458                     int row = functionNavigator.getMinSelectionRow();
459                     handleSelectedItem(row);
460                 }
461             }
462         });
463         createPopupMenuOnJTree();
464
465         scrollPane.setViewportView(functionNavigator);
466
467         /* Begin NetBeans */
468         GroupLayout layout = new GroupLayout(getContentPane());
469         getContentPane().setLayout(layout);
470         layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING)
471                                   .addGroup(layout.createSequentialGroup()
472                                             .addContainerGap()
473                                             .addGroup(layout.createParallelGroup(Alignment.LEADING)
474                                                     .addComponent(scrollPane, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 200, Short.MAX_VALUE)
475                                                     .addComponent(jSeparator1, GroupLayout.DEFAULT_SIZE, 200, Short.MAX_VALUE)
476                                                     .addGroup(layout.createSequentialGroup()
477                                                             .addGroup(layout.createParallelGroup(Alignment.LEADING)
478                                                                     .addComponent(labelGotoLine)
479                                                                     .addComponent(labelNumerotation))
480                                                             .addGap(18, 18, 18)
481                                                             .addGroup(layout.createParallelGroup(Alignment.LEADING)
482                                                                     .addComponent(lineNumber, GroupLayout.PREFERRED_SIZE, 58, GroupLayout.PREFERRED_SIZE)
483                                                                     .addComponent(numType, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))))
484                                             .addContainerGap())
485                                  );
486         layout.setVerticalGroup(layout.createParallelGroup(Alignment.LEADING)
487                                 .addGroup(Alignment.TRAILING, layout.createSequentialGroup()
488                                           .addContainerGap()
489                                           .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 439, Short.MAX_VALUE)
490                                           .addPreferredGap(ComponentPlacement.RELATED)
491                                           .addComponent(jSeparator1, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
492                                           .addPreferredGap(ComponentPlacement.RELATED)
493                                           .addGroup(layout.createParallelGroup(Alignment.BASELINE)
494                                                   .addComponent(labelGotoLine)
495                                                   .addComponent(lineNumber, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
496                                           .addGap(4, 4, 4)
497                                           .addGroup(layout.createParallelGroup(Alignment.BASELINE)
498                                                   .addComponent(labelNumerotation)
499                                                   .addComponent(numType, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
500                                           .addContainerGap())
501                                );
502         /* End NetBeans */
503
504         List<Component> components = new ArrayList(3);
505         components.add(functionNavigator);
506         components.add(lineNumber);
507         components.add(numType);
508         setFocusable(true);
509         setFocusCycleRoot(true);
510         setFocusTraversalPolicy(new NavigatorFocusTraversalPolicy(components));
511
512         setMinimumSize(new Dimension(224, 0));
513         setPreferredSize(new Dimension(224, 543));
514
515         CommonCallBack callback = new CommonCallBack(null) {
516             public void callBack() {
517                 ClosingOperationsManager.startClosingOperation((SwingScilabDockablePanel) NavigatorWindow.this);
518             }
519
520             public void actionPerformed(ActionEvent e) {
521                 callBack();
522             }
523         };
524
525         MenuBar menubar = ScilabMenuBar.createMenuBar();
526         Menu fileMenu = ScilabMenu.createMenu();
527         fileMenu.setText(SciNotesMessages.FILE);
528         fileMenu.setMnemonic('F');
529         MenuItem menu = ScilabMenuItem.createMenuItem();
530         menu.setCallback(callback);
531         ((SwingScilabMenuItem) menu.getAsSimpleMenuItem()).setAccelerator(SciNotes.getActionKeys().get("scinotes-exit"));
532         menu.setText(SciNotesMessages.EXIT);
533         fileMenu.add(menu);
534         menubar.add(fileMenu);
535
536         Menu orderMenu = ScilabMenu.createMenu();
537         orderMenu.setText(SciNotesMessages.ORDER);
538         orderMenu.setMnemonic('O');
539         menu = ScilabMenuItem.createMenuItem();
540         menu.setCallback(new CommonCallBack(null) {
541             public void callBack() {
542                 doc.setAlphaOrderInTree(true);
543                 updateTree();
544             }
545
546             public void actionPerformed(ActionEvent e) {
547                 callBack();
548             }
549         });
550         menu.setText(SciNotesMessages.ALPHABETIC_ORDER);
551         ((SwingScilabMenuItem) menu.getAsSimpleMenuItem()).setAccelerator(KeyStroke.getKeyStroke("alt A"));
552         orderMenu.add(menu);
553
554         menu = ScilabMenuItem.createMenuItem();
555         menu.setCallback(new CommonCallBack(null) {
556             public void callBack() {
557                 doc.setAlphaOrderInTree(false);
558                 updateTree();
559             }
560
561             public void actionPerformed(ActionEvent e) {
562                 callBack();
563             }
564         });
565         ((SwingScilabMenuItem) menu.getAsSimpleMenuItem()).setAccelerator(KeyStroke.getKeyStroke("alt N"));
566         menu.setText(SciNotesMessages.NATURAL_ORDER);
567         orderMenu.add(menu);
568
569         menubar.add(orderMenu);
570         addMenuBar(menubar);
571
572         TextBox infobar = ScilabTextBox.createTextBox();
573         addInfoBar(infobar);
574     }
575
576     /**
577      * Init the tree
578      */
579     public void initTree() {
580         mapNode.clear();
581         mapFunPath.clear();
582         if (functionNavigator == null) {
583             functionNavigator = new JTree();
584         }
585         DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");
586         model = new DefaultTreeModel(root);
587
588         for (ScilabEditorPane sep : panes) {
589             DefaultMutableTreeNode base = new DefaultMutableTreeNode(sep);
590             mapNode.put(sep, base);
591             DefaultMutableTreeNode funs = new DefaultMutableTreeNode(SciNotesMessages.FUNCTIONS);
592             ScilabDocument scidoc = (ScilabDocument) sep.getDocument();
593             scidoc.fillTreeFuns(funs);
594             DefaultMutableTreeNode anchors = new DefaultMutableTreeNode(SciNotesMessages.ANCHORS);
595             scidoc.fillTreeAnchors(anchors);
596             base.add(funs);
597             base.add(anchors);
598             root.add(base);
599             mapFunPath.put(sep, new TreePath(model.getPathToRoot(funs)));
600             mapAnchorPath.put(sep, new TreePath(model.getPathToRoot(anchors)));
601         }
602
603         functionNavigator.setModel(model);
604         functionNavigator.setRootVisible(false);
605     }
606
607     /**
608      * Update the tree
609      */
610     public void updateTree() {
611         DefaultMutableTreeNode current = mapNode.get(pane);
612         TreePath funPath = mapFunPath.get(pane);
613         TreePath anchorPath = mapAnchorPath.get(pane);
614         DefaultMutableTreeNode funs = (DefaultMutableTreeNode) current.getFirstChild();
615         DefaultMutableTreeNode anchors = (DefaultMutableTreeNode) current.getChildAt(1);
616         boolean expFuns = false;
617         boolean expAnchors = false;
618
619         if (functionNavigator.isExpanded(funPath) || funs.isLeaf()) {
620             current.removeAllChildren();
621             funs.removeAllChildren();
622             doc.fillTreeFuns(funs);
623             current.add(funs);
624             current.add(anchors);
625             model.reload(current);
626             expFuns = true;
627         }
628
629         if (functionNavigator.isExpanded(anchorPath) || anchors.isLeaf()) {
630             current.removeAllChildren();
631             anchors.removeAllChildren();
632             doc.fillTreeAnchors(anchors);
633             current.add(funs);
634             current.add(anchors);
635             model.reload(current);
636             expAnchors = true;
637         }
638
639         if (expFuns) {
640             functionNavigator.expandPath(funPath);
641         }
642
643         if (expAnchors) {
644             functionNavigator.expandPath(anchorPath);
645         }
646     }
647
648     /**
649      * Update a branch Functions in the tree
650      */
651     private void updateFunctions() {
652         DefaultMutableTreeNode current = mapNode.get(pane);
653         DefaultMutableTreeNode funs = (DefaultMutableTreeNode) current.getFirstChild();
654         DefaultMutableTreeNode anchors = (DefaultMutableTreeNode) current.getChildAt(1);
655         current.removeAllChildren();
656         funs.removeAllChildren();
657         doc.fillTreeFuns(funs);
658         current.add(funs);
659         current.add(anchors);
660         model.reload(current);
661     }
662
663     /**
664      * Update a branch Anchors in the tree
665      */
666     private void updateAnchors() {
667         DefaultMutableTreeNode current = mapNode.get(pane);
668         DefaultMutableTreeNode funs = (DefaultMutableTreeNode) current.getFirstChild();
669         DefaultMutableTreeNode anchors = (DefaultMutableTreeNode) current.getChildAt(1);
670         current.removeAllChildren();
671         anchors.removeAllChildren();
672         doc.fillTreeAnchors(anchors);
673         current.add(funs);
674         current.add(anchors);
675         model.reload(current);
676     }
677
678     /**
679      * Creates a popup menu on right click
680      */
681     private void createPopupMenuOnJTree() {
682         final JPopupMenu popup = new JPopupMenu();
683
684         final JMenuItem alpha = new JMenuItem(SciNotesMessages.ALPHABETIC_ORDER);
685         final JMenuItem natural = new JMenuItem(SciNotesMessages.NATURAL_ORDER);
686
687         alpha.addActionListener(new ActionListener() {
688             public void actionPerformed(ActionEvent actionEvent) {
689                 handleOrder(true);
690             }
691         });
692         popup.add(alpha);
693
694         natural.addActionListener(new ActionListener() {
695             public void actionPerformed(ActionEvent actionEvent) {
696                 handleOrder(false);
697             }
698         });
699         popup.add(natural);
700         functionNavigator.setComponentPopupMenu(popup);
701     }
702
703     /**
704      * @param alpha true for alphabetic order
705      */
706     private void handleOrder(boolean alpha) {
707         int row = functionNavigator.getMinSelectionRow();
708         if (row == -1) {
709             functionNavigator.setSelectionRow(0);
710             row = 0;
711         }
712
713         TreePath path = functionNavigator.getPathForRow(row);
714         ScilabDocument saveDoc = doc;
715         ScilabEditorPane savePane = pane;
716         doc = getDocumentInNode((DefaultMutableTreeNode) path.getLastPathComponent());
717         pane = getPaneInNode((DefaultMutableTreeNode) path.getLastPathComponent());
718         doc.setAlphaOrderInTree(alpha);
719         updateTree();
720         doc = saveDoc;
721         pane = savePane;
722     }
723
724     /**
725      * Update the line numbering on a change in the document
726      * @param offset offset where the event occurred
727      * @param length length of inserted or removed text
728      */
729     private void handleEvent(int offset, int length) {
730         Element root = doc.getDefaultRootElement();
731         Element line = root.getElement(root.getElementIndex(offset));
732         if (line instanceof ScilabDocument.ScilabLeafElement) {
733             ((ScilabDocument.ScilabLeafElement) line).resetType();
734             updateTree();
735         }
736     }
737
738     /**
739      * Handle a selected item in the JTree
740      * @param row to handle
741      */
742     private void handleSelectedItem(int row) {
743         TreePath path = functionNavigator.getPathForRow(row);
744         if (row != -1) {
745             DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
746             if (node.isLeaf() && node.getUserObject() instanceof ScilabDocument.ScilabLeafElement) {
747                 ScilabEditorPane sep = (ScilabEditorPane) ((DefaultMutableTreeNode) node.getParent().getParent()).getUserObject();
748                 int pos = ((ScilabDocument.ScilabLeafElement) node.getUserObject()).getStartOffset();
749                 if (pos != -1) {
750                     sep.getEditor().getTabPane().setSelectedComponent(sep.getEditorComponent());
751                     sep.scrollTextToPos(pos);
752                     pos = sep.getDocument().getDefaultRootElement().getElementIndex(pos) + 1;
753                     numType.setSelectedItem(SciNotesMessages.ABSOLUTE);
754                     lineNumber.setText(Integer.toString(pos));
755                 }
756             }
757         }
758     }
759
760     /**
761      * Update the caret position when a number is entered in the field
762      */
763     private void updateCaretPosition() {
764         int line = 0;
765         boolean correct = false;
766         TreePath path = functionNavigator.getSelectionPath();
767         DefaultMutableTreeNode node = null;
768         if (path != null) {
769             node = (DefaultMutableTreeNode) path.getLastPathComponent();
770         }
771
772         try {
773             line = Integer.decode(lineNumber.getText()).intValue();
774             if (isAbsolute) {
775                 correct = true;
776                 line = correctLineNumber(line);
777                 updatePaneDoc(node);
778             } else if (functionNavigator.getRowCount() >= 2) {
779                 if (functionNavigator.isSelectionEmpty()) {
780                     functionNavigator.setSelectionRow(1);
781                 }
782
783                 if (node.isLeaf() && node.getUserObject() instanceof ScilabDocument.ScilabLeafElement) {
784                     int pos = ((ScilabDocument.ScilabLeafElement) node.getUserObject()).getStartOffset();
785                     if (pos != -1) {
786                         updatePaneDoc(node);
787                         line += doc.getDefaultRootElement().getElementIndex(pos);
788                         correct = true;
789                         line = correctLineNumber(line);
790                     }
791                 }
792             }
793         } catch (NumberFormatException e) {
794             correct = false;
795         }
796
797         setLineNumberColor(correct);
798         if (correct) {
799             pane.scrollTextToLineNumber(line, false);
800         }
801     }
802
803     /**
804      * @param node corresponding to the pane to select
805      */
806     private void updatePaneDoc(DefaultMutableTreeNode node) {
807         if (node != null) {
808             ScilabEditorPane sep = getPaneInNode(node);
809             if (sep != pane) {
810                 pane = sep;
811                 doc = (ScilabDocument) pane.getDocument();
812                 pane.getEditor().getTabPane().setSelectedComponent(pane.getEditorComponent());
813             }
814         }
815     }
816
817     /**
818      * @param node corresponding to the document to get
819      * @return the doc
820      */
821     private ScilabDocument getDocumentInNode(DefaultMutableTreeNode node) {
822         return (ScilabDocument) getPaneInNode(node).getDocument();
823     }
824
825     /**
826      * @param node corresponding to the pane to get
827      * @return the pane
828      */
829     private ScilabEditorPane getPaneInNode(DefaultMutableTreeNode node) {
830         if (node.isLeaf()) {
831             DefaultMutableTreeNode node1 = (DefaultMutableTreeNode) node.getParent();
832             if (node1 != null) {
833                 DefaultMutableTreeNode node2 = (DefaultMutableTreeNode) node1.getParent();
834                 if (node2 != null && node2.getUserObject() instanceof ScilabEditorPane) {
835                     return (ScilabEditorPane) node2.getUserObject();
836                 }
837             }
838         } else if (node.getUserObject() instanceof String && SciNotesMessages.FUNCTIONS.equals(node.getUserObject())) {
839             return (ScilabEditorPane) ((DefaultMutableTreeNode) node.getParent()).getUserObject();
840         } else if (node.getUserObject() instanceof ScilabEditorPane) {
841             return (ScilabEditorPane) node.getUserObject();
842         }
843
844         return pane;
845     }
846
847     /**
848      * Checks if a line number is correct or not
849      * @param line the line number
850      * @return a correct line number
851      */
852     private int correctLineNumber(int line) {
853         if (line < 0) {
854             return 0;
855         }
856
857         int maxLine = doc.getDefaultRootElement().getElementCount();
858         if (line >= maxLine) {
859             return maxLine - 1;
860         }
861
862         return line;
863     }
864
865     /**
866      * @param correct true if the lineNumber contains a number
867      */
868     private void setLineNumberColor(boolean correct) {
869         if (correct && !lineNumberActive) {
870             lineNumber.setForeground(Color.BLACK);
871             lineNumberActive = true;
872         } else if (!correct && lineNumberActive) {
873             lineNumber.setForeground(Color.RED);
874             lineNumberActive = false;
875         }
876     }
877
878     /**
879      * {@inheritDoc}
880      */
881     private static class NavigatorFocusTraversalPolicy extends FocusTraversalPolicy {
882
883         private List<Component> cycle;
884
885         /**
886          * Default constructor
887          * @param cycle the componenents where the focus move
888          */
889         public NavigatorFocusTraversalPolicy(List<Component> cycle) {
890             this.cycle = cycle;
891         }
892
893         /**
894          * {@inheritDoc}
895          */
896         public Component getComponentAfter(Container focusCycleRoot, Component aComponent) {
897             int n = (cycle.indexOf(aComponent) + 1) % cycle.size();
898             return cycle.get(n);
899         }
900
901         /**
902          * {@inheritDoc}
903          */
904         public Component getComponentBefore(Container focusCycleRoot, Component aComponent) {
905             int n = (cycle.size() + cycle.indexOf(aComponent) - 1) % cycle.size();
906             return cycle.get(n);
907         }
908
909         /**
910          * {@inheritDoc}
911          */
912         public Component getDefaultComponent(Container focusCycleRoot) {
913             return cycle.get(0);
914         }
915
916         /**
917          * {@inheritDoc}
918          */
919         public Component getLastComponent(Container focusCycleRoot) {
920             return cycle.get(cycle.size() - 1);
921         }
922
923         /**
924          * {@inheritDoc}
925          */
926         public Component getFirstComponent(Container focusCycleRoot) {
927             return cycle.get(0);
928         }
929     }
930 }