Refactor SwingScilabWindow and SwingScilabPanel
[scilab.git] / scilab / modules / gui / src / java / org / scilab / modules / gui / utils / WindowsConfigurationManager.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 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.gui.utils;
14
15 import java.awt.Dimension;
16 import java.awt.GraphicsDevice;
17 import java.awt.GraphicsEnvironment;
18 import java.awt.Point;
19 import java.awt.Rectangle;
20 import java.io.File;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.LinkedHashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.UUID;
31
32 import javax.swing.SwingUtilities;
33 import javax.xml.xpath.XPath;
34 import javax.xml.xpath.XPathConstants;
35 import javax.xml.xpath.XPathFactory;
36
37 import org.flexdock.docking.Dockable;
38 import org.flexdock.docking.DockingManager;
39 import org.flexdock.docking.activation.ActiveDockableTracker;
40 import org.flexdock.docking.state.LayoutNode;
41 import org.flexdock.perspective.persist.xml.LayoutNodeSerializer;
42 import org.flexdock.perspective.persist.xml.PersistenceConstants;
43 import org.scilab.modules.commons.ScilabCommonsUtils;
44 import org.scilab.modules.commons.ScilabConstants;
45 import org.scilab.modules.commons.xml.ScilabXMLUtilities;
46 import org.scilab.modules.commons.xml.XConfiguration;
47 import org.scilab.modules.commons.xml.XConfigurationEvent;
48 import org.scilab.modules.commons.xml.XConfigurationListener;
49 import static org.scilab.modules.commons.xml.XConfiguration.XConfAttribute;
50 import org.scilab.modules.gui.bridge.tab.SwingScilabDockablePanel;
51 import org.scilab.modules.gui.bridge.window.SwingScilabWindow;
52 import org.scilab.modules.gui.console.ScilabConsole;
53 import org.scilab.modules.gui.tab.Tab;
54 import org.scilab.modules.gui.tabfactory.ScilabTabFactory;
55 import org.w3c.dom.Document;
56 import org.w3c.dom.Element;
57 import org.w3c.dom.NodeList;
58
59 /**
60  *
61  * Save the windows properties.
62  *
63  * @author Calixte DENIZET
64  */
65 public class WindowsConfigurationManager implements XConfigurationListener {
66
67     private static final int DEFAULTX = 0;
68     private static final int DEFAULTY = 0;
69     private static final int DEFAULTHEIGHT = 500;
70     private static final int DEFAULTWIDTH = 500;
71
72     private static final String LAYOUT_PATH = "//general/desktop-layout/body/layouts";
73
74     private static final String SCI = "SCI";
75     private static final String WINDOWS_CONFIG_FILE = System.getenv(SCI) + "/modules/gui/etc/windowsConfiguration.xml";
76     private static final String DEFAULT_WINDOWS_CONFIG_FILE = System.getenv(SCI) + "/modules/gui/etc/integratedConfiguration.xml";
77     private static final String NULLUUID = new UUID(0L, 0L).toString();
78     private static final Map<SwingScilabDockablePanel, EndedRestoration> endedRestoration = new HashMap<SwingScilabDockablePanel, EndedRestoration>();
79     private static final List<String> alreadyRestoredWindows = new ArrayList<String>();
80     private static final Map<String, Object> defaultWinAttributes = new HashMap<String, Object>();
81     private static final List<String> currentlyRestored = new ArrayList<String>();
82
83
84     private static String USER_WINDOWS_CONFIG_FILE = ScilabConstants.SCIHOME.toString() + "/windowsConfiguration.xml";
85
86     private static boolean oneTry;
87     private static Document doc;
88
89     private static boolean mustInvalidate;
90     private static boolean mustSave = true;
91     private static int layoutId;
92
93     static {
94         try {
95             if (ScilabConstants.SCIHOME != null && ScilabConstants.SCIHOME.canRead() && ScilabConstants.SCIHOME.canWrite()) {
96                 USER_WINDOWS_CONFIG_FILE = ScilabConstants.SCIHOME.toString() + "/windowsConfiguration.xml";
97             } else {
98                 USER_WINDOWS_CONFIG_FILE = DEFAULT_WINDOWS_CONFIG_FILE;
99                 mustSave = false;
100             }
101
102             new WindowsConfigurationManager();
103             Runnable runnable = new Runnable() {
104                 public void run() {
105                     if (mustInvalidate) {
106                         File f = new File(USER_WINDOWS_CONFIG_FILE);
107                         if (f.exists() && f.isFile()) {
108                             f.delete();
109                         }
110                     }
111                 }
112             };
113
114             Class scilab = ClassLoader.getSystemClassLoader().loadClass("org.scilab.modules.core.Scilab");
115             Method registerFinalHook = scilab.getDeclaredMethod("registerFinalHook", Runnable.class);
116             registerFinalHook.invoke(null, runnable);
117
118             defaultWinAttributes.put("x", new Integer(DEFAULTX));
119             defaultWinAttributes.put("y", new Integer(DEFAULTY));
120             defaultWinAttributes.put("height", new Integer(DEFAULTHEIGHT));
121             defaultWinAttributes.put("width", new Integer(DEFAULTWIDTH));
122             defaultWinAttributes.put("state", new Integer(SwingScilabWindow.NORMAL));
123             /*
124               Uncomment this code for debugging focus issues
125
126               java.awt.Toolkit.getDefaultToolkit().addAWTEventListener(new java.awt.event.AWTEventListener() {
127               public void eventDispatched(java.awt.AWTEvent e) {
128               System.out.println(e);
129               }
130               }, java.awt.AWTEvent.FOCUS_EVENT_MASK);*/
131         } catch (Exception e) {
132             System.err.println(e);
133         }
134     }
135
136     private WindowsConfigurationManager() {
137         XConfiguration.addXConfigurationListener(this);
138     }
139
140     public void configurationChanged(XConfigurationEvent e) {
141         if (e.getModifiedPaths().contains(LAYOUT_PATH)) {
142             Options options = getOptions();
143             if (options.id != layoutId) {
144                 mustInvalidate = true;
145                 layoutId = options.id;
146             }
147
148             mustSave = options.mustSave;
149         }
150     }
151
152     public static String getLayoutFilePath() {
153         try {
154             Document doc = XConfiguration.getXConfigurationDocument();
155             if (doc != null) {
156                 XPath xp = XPathFactory.newInstance().newXPath();
157                 NodeList nodes = (NodeList) xp.compile(LAYOUT_PATH + "/layout[@id=../@id]/@path").evaluate(doc, XPathConstants.NODESET);
158                 if (nodes != null && nodes.getLength() > 0) {
159                     return nodes.item(0).getNodeValue().replace("$SCI", System.getenv(SCI));
160                 }
161             }
162         } catch (Exception e) { }
163
164         return WINDOWS_CONFIG_FILE;
165     }
166
167     /**
168      * Create a copy of windows configuration file in the user directory
169      */
170     public static void createUserCopy() {
171         if (isCopyNeeded() && mustSave) {
172             ScilabCommonsUtils.copyFile(new File(getLayoutFilePath()), new File(USER_WINDOWS_CONFIG_FILE));
173             doc = null;
174         }
175     }
176
177     /**
178      * Read the file to modify
179      */
180     private static void readDocument() {
181         if (doc == null) {
182             createUserCopy();
183             doc = ScilabXMLUtilities.readDocument(USER_WINDOWS_CONFIG_FILE);
184             Options options = getOptions();
185             if (mustSave) {
186                 mustSave = options.mustSave;
187             }
188             layoutId = options.id;
189         }
190
191         if (doc == null && !oneTry) {
192             System.err.println("Try to reload the default configuration file.");
193             File f = new File(USER_WINDOWS_CONFIG_FILE);
194             if (f.exists() && f.isFile()) {
195                 f.delete();
196             }
197             oneTry = true;
198             readDocument();
199         } else if (doc == null && oneTry) {
200             System.err.println("Serious problem to copy and parse the configuration file.");
201             System.err.println("Please check if you have the rights to write the file: " + USER_WINDOWS_CONFIG_FILE);
202             System.err.println("If the previous file exists, please check if it is a valid XML");
203             System.err.println("and if yes, please report a bug: http://bugzilla.scilab.org");
204         }
205     }
206
207     /**
208      * Write the document
209      */
210     private static void writeDocument() {
211         if (mustSave) {
212             ScilabXMLUtilities.writeDocument(doc, USER_WINDOWS_CONFIG_FILE);
213         }
214     }
215
216     /**
217      * @return true if a copy is needed
218      */
219     private static final boolean isCopyNeeded() {
220         return !new File(USER_WINDOWS_CONFIG_FILE).exists();
221     }
222
223     /**
224      * Register an EndedRestoration, op.finish() will be executed when the tab restoration will be finished.
225      * @param tab the associated tab
226      * @param ended the closing operation
227      */
228     public static void registerEndedRestoration(SwingScilabDockablePanel tab, EndedRestoration ended) {
229         endedRestoration.put(tab, ended);
230     }
231
232     /**
233      * Register an EndedRestoration, op.finish() will be executed when the tab restoration will be finished.
234      * @param tab the associated tab
235      * @param ended the closing operation
236      */
237     public static void registerEndedRestoration(Tab tab, EndedRestoration ended) {
238         registerEndedRestoration((SwingScilabDockablePanel) tab.getAsSimpleTab(), ended);
239     }
240
241     /**
242      * Create a new node with parent element
243      * @param parent the parent element
244      * @param nodeName the node name
245      * @param attr an array containing attribute name followed by its value: "attr1", 1, "attr2", true, ...
246      * @return the created element
247      */
248     public static Element createNode(Element parent, String nodeName, Object[] attr) {
249         readDocument();
250         for (int i = 0; i < attr.length; i += 2) {
251             if (attr[i].equals("uuid")) {
252                 removeNode(parent, nodeName, (String) attr[i + 1]);
253             }
254         }
255
256         return ScilabXMLUtilities.createNode(doc, parent, nodeName, attr);
257     }
258
259     /**
260      * Save the window properties
261      * @param window the window
262      */
263     public static void saveWindowProperties(SwingScilabWindow window) {
264         if (ScilabConstants.isGUI()) {
265             readDocument();
266
267             Element root = doc.getDocumentElement();
268             Element win = createNode(root, "Window", new Object[] {"uuid", window.getUUID(),
269                                      "x", (int) window.getLastPosition().getX(),
270                                      "y", (int) window.getLastPosition().getY(),
271                                      "width", (int) window.getLastDimension().getWidth(),
272                                      "height", (int) window.getLastDimension().getHeight(),
273                                      "state", window.getExtendedState()
274                                                                   });
275             LayoutNode layoutNode = window.getDockingPort().exportLayout();
276             LayoutNodeSerializer serializer = new LayoutNodeSerializer();
277             win.appendChild(serializer.serialize(doc, layoutNode));
278
279             for (Dockable dockable : (Set<Dockable>) window.getDockingPort().getDockables()) {
280                 saveTabProperties((SwingScilabDockablePanel) dockable, false);
281             }
282
283             writeDocument();
284         }
285     }
286
287     /**
288      * Create a window according to the uuid.
289      *
290      * This method can be used to create a reference windows.
291      *
292      * @param uuid
293      *            the reference uuid
294      * @param preserveUUID
295      *            if true the uuid will be used on the new windows, generate a
296      *            new uuid otherwise
297      * @return the window
298      */
299     public static SwingScilabWindow createWindow(final String uuid, final boolean preserveUUID) {
300         readDocument();
301
302         final Element root = doc.getDocumentElement();
303         final boolean nullUUID = uuid.equals(NULLUUID);
304         final Map<String, Object> attrs = new HashMap<String, Object>();
305         Element win = null;
306         boolean containsX = true;
307
308         if (!nullUUID) {
309             win = getElementWithUUID(root, "Window", uuid);
310             if (win == null) {
311                 return null;
312             }
313
314             containsX = !win.getAttribute("x").equals("");
315
316             if (containsX) {
317                 attrs.put("x", int.class);
318                 attrs.put("y", int.class);
319             }
320
321             attrs.put("height", int.class);
322             attrs.put("width", int.class);
323             attrs.put("state", int.class);
324             ScilabXMLUtilities.readNodeAttributes(win, attrs);
325         } else {
326             attrs.putAll(defaultWinAttributes);
327         }
328
329         SwingScilabWindow window = SwingScilabWindow.createWindow(true);
330         window.setVisible(false);
331
332         final String localUUID;
333         if (preserveUUID) {
334             localUUID = uuid;
335         } else {
336             localUUID = UUID.randomUUID().toString();
337         }
338         window.setUUID(localUUID);
339
340         if (containsX) {
341             boolean positionned = false;
342             Point p = new Point(((Integer) attrs.get("x")).intValue(), ((Integer) attrs.get("y")).intValue());
343
344             // We check that the coordinates are valid
345             GraphicsDevice[] gds = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
346             if (gds != null) {
347                 for (GraphicsDevice gd : gds) {
348                     Rectangle r = gd.getDefaultConfiguration().getBounds();
349                     if (r.contains(p)) {
350                         positionned = true;
351                         window.setLocation(p.x, p.y);
352                         break;
353                     }
354                 }
355             }
356
357             if (!positionned) {
358                 window.setLocation(DEFAULTX, DEFAULTY);
359             }
360         }
361
362         window.setSize(((Integer) attrs.get("width")).intValue(), ((Integer) attrs.get("height")).intValue());
363         window.setExtendedState(((Integer) attrs.get("state")).intValue());
364
365         return window;
366     }
367
368     public static SwingScilabWindow restoreWindow(String uuid) {
369         String winuuid = UUID.randomUUID().toString();
370         SwingScilabWindow win = SwingScilabWindow.createWindow(true);
371         win.setUUID(winuuid);
372         win.setIsRestoring(true);
373
374         final SwingScilabDockablePanel tab = ScilabTabFactory.getInstance().getTab(uuid);
375         win.addTab(tab);
376         BarUpdater.forceUpdateBars(tab.getParentWindowId(), tab.getMenuBar(), tab.getToolBar(), tab.getInfoBar(), tab.getName(), tab.getWindowIcon());
377
378         win.pack();
379         win.setVisible(true);
380         win.requestFocus();
381         win.toFront();
382         win.setIsRestoring(false);
383
384         SwingUtilities.invokeLater(new Runnable() {
385             @Override
386             public void run() {
387                 ActiveDockableTracker.requestDockableActivation(tab);
388             }
389         });
390
391         return win;
392     }
393
394     /**
395      * Restore a window with a given uuid
396      *
397      * @param uuid
398      *            the uuid
399      * @param restoreTab
400      *            if true the tab is restored too
401      * @return the corresponding window
402      */
403     public static SwingScilabWindow restoreWindow(String uuid, String defaultTabUuid, boolean restoreTab, boolean requestFocus) {
404         readDocument();
405
406         final boolean nullUUID = uuid.equals(NULLUUID);
407
408         // create the window and preserve the uuid if not null
409         final SwingScilabWindow window = createWindow(uuid, !nullUUID);
410         if (window == null) {
411             return null;
412         }
413
414         window.setIsRestoring(true);
415
416         if (restoreTab) {
417             if (!nullUUID) {
418                 final LayoutNodeSerializer serializer = new LayoutNodeSerializer();
419                 final Element dockingPort = getDockingPort(uuid);
420                 LayoutNode layoutNode = (LayoutNode) serializer.deserialize(dockingPort);
421                 window.getDockingPort().importLayout(layoutNode);
422             } else if (defaultTabUuid != null && !defaultTabUuid.isEmpty()) {
423                 SwingScilabDockablePanel defaultTab = ScilabTabFactory.getInstance().getTab(defaultTabUuid);
424                 defaultTab.setParentWindowId(window.getId());
425                 DockingManager.dock(defaultTab, window.getDockingPort());
426             }
427
428             for (SwingScilabDockablePanel tab : (Set<SwingScilabDockablePanel>) window.getDockingPort().getDockables()) {
429                 tab.setParentWindowId(window.getId());
430             }
431
432             SwingScilabDockablePanel[] tabs = new SwingScilabDockablePanel[window.getNbDockedObjects()];
433             tabs = ((Set<SwingScilabDockablePanel>) window.getDockingPort().getDockables()).toArray(tabs);
434
435             // Be sur that the main tab will have the focus.
436             // Get the elder tab and activate it
437             final SwingScilabDockablePanel mainTab = ClosingOperationsManager.getElderTab(new ArrayList(Arrays.asList(tabs)));
438             BarUpdater.forceUpdateBars(mainTab.getParentWindowId(), mainTab.getMenuBar(), mainTab.getToolBar(), mainTab.getInfoBar(), mainTab.getName(), mainTab.getWindowIcon());
439
440             if (!ScilabConsole.isExistingConsole() && tabs.length == 1 && tabs[0].getPersistentId().equals(NULLUUID)) {
441                 // null uuid is reserved to the console and in NW mode, there is no console.
442                 return null;
443             }
444
445             for (SwingScilabDockablePanel tab : tabs) {
446                 // each tab has now a window so it can be useful for the tab to set an icon window or to center a dialog...
447                 EndedRestoration ended = endedRestoration.get(tab);
448                 if (ended != null) {
449                     ended.finish();
450                     endedRestoration.remove(ended);
451                 }
452             }
453
454             if (tabs.length == 1) {
455                 // we remove undock and close buttons when there is only one View in the DockingPort
456                 SwingScilabDockablePanel.removeActions(tabs[0]);
457             } else {
458                 // we add undock and close buttons
459                 for (SwingScilabDockablePanel tab : tabs) {
460                     SwingScilabDockablePanel.addActions(tab);
461                 }
462             }
463
464             if (requestFocus) {
465                 SwingUtilities.invokeLater(new Runnable() {
466                     @Override
467                     public void run() {
468                         final Thread t = new Thread(new Runnable() {
469                             @Override
470                             public void run() {
471                                 synchronized (currentlyRestored) {
472                                     while (currentlyRestored.size() > 0) {
473                                         try {
474                                             currentlyRestored.wait();
475                                         } catch (InterruptedException e) {
476                                             e.printStackTrace();
477                                         }
478                                     }
479                                 }
480
481                                 SwingUtilities.invokeLater(new Runnable() {
482                                     @Override
483                                     public void run() {
484                                         window.setVisible(true);
485                                         window.requestFocus();
486                                         window.toFront();
487                                         window.setIsRestoring(false);
488
489                                         ActiveDockableTracker.requestDockableActivation(mainTab);
490                                     }
491                                 });
492                             }
493                         });
494                         t.start();
495                     }
496                 });
497             } else {
498                 SwingUtilities.invokeLater(new Runnable() {
499                     @Override
500                     public void run() {
501                         window.setIsRestoring(false);
502                         window.setVisible(true);
503                     }
504                 });
505             }
506         }
507
508         return window;
509     }
510
511     private static final Element getDockingPort(final String winUUID) {
512         readDocument();
513
514         final Element root = doc.getDocumentElement();
515         final Element win = getElementWithUUID(root, "Window", winUUID);
516         if (win == null) {
517             return null;
518         }
519
520         final NodeList children = win.getElementsByTagName(PersistenceConstants.DOCKING_PORT_NODE_ELEMENT_NAME);
521         return (Element) children.item(0);
522     }
523
524
525     /**
526      * Must be called when the restoration is finished
527      * @param tab the tab
528      */
529     public static final void restorationFinished(SwingScilabDockablePanel tab) {
530         synchronized (currentlyRestored) {
531             currentlyRestored.remove(tab.getPersistentId());
532
533             // notify after remove
534             currentlyRestored.notify();
535         }
536     }
537
538     /**
539      * Remove a window from the already restored windows
540      * @param uuid the win uuid
541      */
542     public static final void removeWin(String uuid) {
543         alreadyRestoredWindows.remove(uuid);
544     }
545
546     /**
547      * Find all the dependencies of the given tab. The returned list contains parent before its children.
548      * @param uuid the tab's uuid
549      * @return a list of the elements with the given uuid
550      */
551     public static final Set<Element> getTabDependencies(String uuid) {
552         readDocument();
553         Element root = doc.getDocumentElement();
554
555         // Children
556         List<Element> elements = ScilabXMLUtilities.getElementsWithAttributeEquals(root, "depends", uuid);
557
558         Set<Element> list = new LinkedHashSet<Element>();
559         Element el = getElementWithUUID(doc.getDocumentElement(), uuid);
560         if (el != null) {
561             //We add the parent
562             list.add(el);
563             // We add the children and their own children
564             for (Element e : elements) {
565                 list.addAll(getTabDependencies(e.getAttribute("uuid")));
566             }
567         }
568
569         return list;
570     }
571
572     /**
573      * Create all the tabs depending of the tab with the given uuid.
574      * The creation will respect the convention: parent before children.
575      * @param uuid the tab uuid
576      * @return the list of all the uuids to restore
577      */
578     public static final Set<Element> createDescendantTabs(String uuid) {
579         Set<Element> list = getTabDependencies(uuid);
580         Dimension nullDims = new Dimension(0, 0);
581         for (Element e : list) {
582             // All the tabs created in the factory will be cached so when Flexdock will restore the docking
583             // it will use the same tab as created here.
584             ScilabTabFactory factory = ScilabTabFactory.getInstance();
585             factory.addTabFactory(e.getAttribute("load"), e.getAttribute("factory"));
586             currentlyRestored.add(e.getAttribute("uuid"));
587             SwingScilabDockablePanel tab = factory.getTab(e.getAttribute("uuid"));
588             if (!e.getAttribute("width").isEmpty() && !e.getAttribute("height").isEmpty()) {
589                 tab.setMinimumSize(nullDims);
590                 tab.setPreferredSize(new Dimension(Integer.parseInt(e.getAttribute("width")), Integer.parseInt(e.getAttribute("width"))));
591             }
592         }
593
594         return list;
595     }
596
597     /**
598      * Useful for the following case: you have 3 tabs, A, B and C, C depends of B, A and C are docked in the same window.
599      * You want to restore A, the window containing A needs to restore C too (and finally B).
600      * There is no direct dependency between A and C but the fact that they're in the same dockport
601      * implies a dependency.
602      * @param elems the elems corresponding to the tabs to restore
603      * @return tabs to restore
604      */
605     private static final Set<Element> createAdjacentTabs(Set<Element> elems) {
606         readDocument();
607
608         Element root = doc.getDocumentElement();
609         boolean jobFinished = true;
610         Set<Element> toAdd = new LinkedHashSet<Element>();
611         for (Element e : elems) {
612             String winuuid = e.getAttribute("winuuid");
613             if (!winuuid.isEmpty() && !winuuid.equals(NULLUUID)) {
614                 List<Element> elements = ScilabXMLUtilities.getElementsWithAttributeEquals(root, "winuuid", winuuid);
615                 elements.removeAll(elems);
616                 jobFinished = jobFinished && elements.size() == 0;
617                 toAdd.addAll(elements);
618             }
619         }
620
621         if (jobFinished) {
622             return elems;
623         }
624
625         for (Element ee : toAdd) {
626             elems.addAll(createDescendantTabs(getElderParent(ee).getAttribute("uuid")));
627         }
628
629
630         return createAdjacentTabs(elems);
631     }
632
633     /**
634      * @param e the element
635      * @return the elder parent of this element (elder for the attribute "depends")
636      */
637     private static final Element getElderParent(Element e) {
638         readDocument();
639
640         Element root = doc.getDocumentElement();
641         String dep = e.getAttribute("depends");
642         if (!dep.isEmpty()) {
643             return getElderParent(ScilabXMLUtilities.getElementsWithAttributeEquals(root, "uuid", dep).get(0));
644         }
645
646         return e;
647     }
648
649     /**
650      * Starts the restoration of the tab with the given uuid
651      * @param uuid the tab uuid to restore
652      */
653     private static final void startRestoration(String uuid) {
654         if (!ScilabConstants.isGUI()) {
655             readDocument();
656             Element root = doc.getDocumentElement();
657             Element e = ScilabXMLUtilities.getElementsWithAttributeEquals(root, "uuid", uuid).get(0);
658             if (e != null) {
659                 String winuuid = e.getAttribute("winuuid");
660                 if (!winuuid.isEmpty() && !winuuid.equals(NULLUUID)) {
661                     List<Element> elements = ScilabXMLUtilities.getElementsWithAttributeEquals(root, "winuuid", winuuid);
662                     for (Element ee : elements) {
663                         if (ee.getNodeName().equals("Console")) {
664                             restoreWindow(uuid);
665                             return;
666                         }
667                     }
668                 }
669             }
670         }
671
672         Set<Element> list = createDescendantTabs(uuid);
673         list = createAdjacentTabs(list);
674
675         List<String> wins = new ArrayList<String>();
676         List<String> tabsWithoutWin = new ArrayList<String>();
677         for (Element e : list) {
678             String winuuid = e.getAttribute("winuuid");
679             if (winuuid.equals(NULLUUID) || getElementWithUUID(winuuid) == null || !isDockableIdExisting(winuuid, e.getAttribute("uuid"))) {
680                 tabsWithoutWin.add(e.getAttribute("uuid"));
681             } else if (!wins.contains(winuuid)) {
682                 wins.add(winuuid);
683             }
684         }
685
686         boolean requestFocus = true;
687
688         if (wins.contains(NULLUUID)) {
689             wins.remove(NULLUUID);
690             wins.add(NULLUUID);
691         }
692
693         for (String winuuid : wins) {
694             if (!alreadyRestoredWindows.contains(winuuid)) {
695                 restoreWindow(winuuid, uuid, true, requestFocus);
696                 alreadyRestoredWindows.add(winuuid);
697                 if (requestFocus) {
698                     requestFocus = false;
699                 }
700             }
701         }
702
703         for (String u : tabsWithoutWin) {
704             SwingScilabWindow window = restoreWindow(NULLUUID, u, true, requestFocus);
705             alreadyRestoredWindows.add(window.getUUID());
706             if (requestFocus) {
707                 requestFocus = false;
708             }
709         }
710     }
711
712     /**
713      * Search a node (child of root) with name nodeName and with a given uuid
714      * @param root the root element
715      * @param nodeName the node name
716      * @param uuid the uuid
717      * @return the corresponding element or null if it does not exist
718      */
719     public static final Element getElementWithUUID(Element root, String nodeName, String uuid) {
720         if (uuid == null || uuid.isEmpty()) {
721             return null;
722         }
723         NodeList list = root.getElementsByTagName(nodeName);
724         int length = getNodeListLength(list);
725         for (int i = 0; i < length; i++) {
726             Element elem = (Element) list.item(i);
727             if (elem.getAttribute("uuid").equals(uuid)) {
728                 return elem;
729             }
730         }
731
732         return null;
733     }
734
735     /**
736      * Search a node with a given uuid
737      * @param uuid the uuid
738      * @return the corresponding element or null if it does not exist
739      */
740     public static final Element getElementWithUUID(String uuid) {
741         readDocument();
742
743         return getElementWithUUID(doc.getDocumentElement(), uuid);
744     }
745
746     /**
747      * Search a node (child of root) with name nodeName and with a given uuid
748      * @param root the root element
749      * @param uuid the uuid
750      * @return the corresponding element or null if it does not exist
751      */
752     public static final Element getElementWithUUID(Element root, String uuid) {
753         if (uuid == null || uuid.isEmpty()) {
754             return null;
755         }
756         List<Element> list = ScilabXMLUtilities.getElementsWithAttributeEquals(root, "uuid", uuid);
757         if (list.size() != 0) {
758             return list.get(0);
759         }
760
761         return null;
762     }
763
764     /**
765      * Check if there is a window which has a dockableID equal to the given uuid
766      * @param winuuid the uuid of the window
767      * @param uuid the uuid to test
768      * @return true if a dockableId exists
769      */
770     public static final boolean isDockableIdExisting(String winuuid, String uuid) {
771         if (winuuid == null || winuuid.isEmpty() || uuid == null || uuid.isEmpty()) {
772             return false;
773         }
774
775         Element win = getElementWithUUID(winuuid);
776         if (win != null) {
777             List<Element> list = ScilabXMLUtilities.getElementsWithAttributeEquals(win, "dockableId", uuid);
778             if (list.size() != 0) {
779                 return true;
780             }
781         }
782
783         return false;
784     }
785
786     /**
787      * Validate all the windows in checking if all the dockable nodes are ok.
788      * If a split node contains an invalid uuid, then the invalid dockable is removed
789      * and the split node is replaced by the valid dockable.
790      */
791     private static final void validateWindows() {
792         // We remove all the blanks and carriage return
793         try {
794             XPath xp = XPathFactory.newInstance().newXPath();
795             NodeList nodes = (NodeList) xp.compile("//text()").evaluate(doc, XPathConstants.NODESET);
796             for (int i = 0; i < nodes.getLength(); i++) {
797                 nodes.item(i).getParentNode().removeChild(nodes.item(i));
798             }
799         } catch (Exception e) {
800             e.printStackTrace();
801         }
802
803         Element root = doc.getDocumentElement();
804         NodeList list = root.getElementsByTagName("Window");
805
806         int length = getNodeListLength(list);
807         List<Element> elems = new ArrayList<Element>(length);
808         for (int i = 0; i < length; i++) {
809             elems.add((Element) list.item(i));
810         }
811
812         for (Element e : elems) {
813             validateWindow(e.getAttribute("uuid"));
814         }
815     }
816
817     /**
818      * Validate a window element.
819      * The window element in the DOM is eventually replaced by a valid one.
820      * @param winuuid the window uuid
821      */
822     private static final void validateWindow(final String winuuid) {
823         if (winuuid == null || winuuid.isEmpty()) {
824             return;
825         }
826
827         Element win = getElementWithUUID(winuuid);
828         if (win == null) {
829             return;
830         }
831
832         Element dp = (Element) win.getFirstChild();
833         if (dp == null) {
834             win.getParentNode().removeChild(win);
835             return;
836         }
837
838         Element e = validateDockingPortNode(winuuid, dp);
839         if (e == null) {
840             win.removeChild(dp);
841             return;
842         }
843
844         win.removeChild(dp);
845         win.appendChild(e);
846     }
847
848     /**
849      * Validate a dockingport node element.
850      * @param winuuid the window uuid
851      * @param e the element to validate
852      * @return a valid element
853      */
854     private static final Element validateDockingPortNode(final String winuuid, final Element e) {
855         Element ee = (Element) e.getFirstChild();
856         if (ee == null) {
857             return null;
858         }
859
860         if (ee.getTagName().equals("DockableNode")) {
861             ee = validateDockableNode(winuuid, ee);
862             if (ee == null) {
863                 return null;
864             }
865             return e;
866         } else {
867             ee = validateSplitNode(winuuid, ee);
868             if (ee == null) {
869                 return null;
870             }
871             Element eee = (Element) e.cloneNode(false);
872             eee.appendChild(ee);
873
874             return eee;
875         }
876     }
877
878     /**
879      * Validate a dockable node element.
880      * @param winuuid the window uuid
881      * @param e the element to validate
882      * @return a valid element
883      */
884     private static final Element validateDockableNode(final String winuuid, final Element e) {
885         String id = e.getAttribute("dockableId");
886         Element ee = getElementWithUUID(id);
887         if (ee == null || !ee.getAttribute("winuuid").equals(winuuid)) {
888             return null;
889         }
890
891         return e;
892     }
893
894     /**
895      * Validate a split node element.
896      * @param winuuid the window uuid
897      * @param e the element to validate
898      * @return a valid element
899      */
900     private static final Element validateSplitNode(final String winuuid, final Element e) {
901         NodeList set = e.getChildNodes();
902         Element c1 = validateDockingPortNode(winuuid, (Element) set.item(0));
903         Element c2 = validateDockingPortNode(winuuid, (Element) set.item(1));
904
905         if (c1 != null && c2 != null) {
906             Element ee = (Element) e.cloneNode(false);
907             ee.appendChild(c1);
908             ee.appendChild(c2);
909             return ee;
910         }
911
912         if (c1 == null && c2 == null) {
913             return null;
914         }
915
916         if (c1 == null) {
917             return (Element) c2.getFirstChild().cloneNode(true);
918         }
919
920         return (Element) c1.getFirstChild().cloneNode(true);
921     }
922
923     /**
924      * Remove a node with a given uuid
925      * @param parent the parent element
926      * @param nodeName the node name
927      * @param uuid the uuid
928      */
929     private static final void removeNode(Element parent, String nodeName, String uuid) {
930         if (uuid == null || uuid.isEmpty()) {
931             return;
932         }
933         Element e = getElementWithUUID(parent, nodeName, uuid);
934         if (e != null) {
935             parent.removeChild(e);
936         }
937     }
938
939     /**
940      * Remove a node with a given uuid
941      * @param nodeName the node name
942      * @param uuid the uuid
943      */
944     public static final void removeNode(String uuid) {
945         if (uuid == null || uuid.isEmpty()) {
946             return;
947         }
948         Element e = getElementWithUUID(uuid);
949         if (e != null && e.getParentNode() != null) {
950             e.getParentNode().removeChild(e);
951         }
952     }
953
954     /**
955      * Save the tab properties
956      * @param tab the tab
957      * @param nullWin if true, the winuuid will be set to 0 (the tab is not docked)
958      */
959     public static void saveTabProperties(SwingScilabDockablePanel tab, boolean nullWin) {
960         if (ScilabConstants.isGUI()) {
961             readDocument();
962
963             ScilabTabFactory factory = ScilabTabFactory.getInstance();
964             String uuid = tab.getPersistentId();
965             Element root = doc.getDocumentElement();
966
967             String app = factory.getApplication(uuid);
968             if (app.isEmpty()) {
969                 return;
970             }
971
972             String winuuid;
973             if (nullWin) {
974                 winuuid = NULLUUID;
975             } else {
976                 winuuid = tab.getParentWindowUUID();
977             }
978
979             Dimension dim = tab.getSize();
980
981             createNode(root, app, new Object[] {"winuuid", winuuid,
982                                                 "uuid", uuid,
983                                                 "load", factory.getPackage(uuid),
984                                                 "factory", factory.getClassName(uuid),
985                                                 "width", (int) dim.getWidth(),
986                                                 "height", (int) dim.getHeight()
987                                                });
988             writeDocument();
989         }
990     }
991
992     /**
993      * Clean the document in removing the useless tags
994      * and validate the different windows.
995      */
996     public static void clean() {
997         if (ScilabConstants.isGUI()) {
998             readDocument();
999
1000             validateWindows();
1001
1002             Element root = doc.getDocumentElement();
1003             NodeList list = root.getElementsByTagName("Window");
1004             int len = getNodeListLength(list);
1005             for (int i = 0; i < len; i++) {
1006                 if (list.item(i) instanceof Element) {
1007                     String uuid = ((Element) list.item(i)).getAttribute("uuid");
1008                     List<Element> elements = ScilabXMLUtilities.getElementsWithAttributeEquals(root, "winuuid", uuid);
1009                     if (elements == null || elements.size() == 0) {
1010                         root.removeChild(list.item(i));
1011                         removeWin(uuid);
1012                     }
1013                 }
1014             }
1015             writeDocument();
1016         }
1017     }
1018
1019     /**
1020      * Make a dependency between two tabs
1021      * @param parentUUID the parent tab uuid
1022      * @param childUUID the child tab uuid
1023      */
1024     public static final void makeDependency(String parentUUID, String childUUID) {
1025         if (ScilabConstants.isGUI()) {
1026             readDocument();
1027
1028             Element e = getElementWithUUID(doc.getDocumentElement(), childUUID);
1029             if (e != null) {
1030                 e.setAttribute("depends", parentUUID);
1031             }
1032             writeDocument();
1033         }
1034     }
1035
1036     /**
1037      * Remove a dependency with the parent tab
1038      * @param childUUID the child tab uuid
1039      */
1040     public static void removeDependency(String childUUID) {
1041         if (ScilabConstants.isGUI()) {
1042             readDocument();
1043
1044             Element e = getElementWithUUID(doc.getDocumentElement(), childUUID);
1045             if (e != null) {
1046                 e.removeAttribute("depends");
1047             }
1048             writeDocument();
1049         }
1050     }
1051
1052     /**
1053      * Restore an application by its uuid
1054      * @param uuid the application uuid
1055      * @return true if the operation succeded
1056      */
1057     public static boolean restoreUUID(final String uuid) {
1058         readDocument();
1059         clean();
1060
1061         Element elem = getElementWithUUID(uuid);
1062         if (elem == null) {
1063             return false;
1064         }
1065
1066         if (SwingUtilities.isEventDispatchThread()) {
1067             startRestoration(uuid);
1068         } else {
1069             try {
1070                 SwingUtilities.invokeAndWait(new Runnable() {
1071                     public void run() {
1072                         startRestoration(uuid);
1073                     }
1074                 });
1075             } catch (InvocationTargetException e) {
1076                 System.err.println(e);
1077             } catch (InterruptedException e) {
1078                 System.err.println(e);
1079             }
1080         }
1081
1082         if (ScilabConstants.isGUI()) {
1083             writeDocument();
1084         }
1085
1086         doc = null;
1087
1088         return true;
1089     }
1090
1091     /**
1092      * Get the uuids of an application
1093      * @param name the application anem
1094      * @return the corresponding uuids
1095      */
1096     public static String[] getApplicationUUIDs(String name) {
1097         readDocument();
1098
1099         NodeList list = doc.getDocumentElement().getElementsByTagName(name);
1100         String[] uuids = new String[getNodeListLength(list)];
1101         for (int i = 0; i < uuids.length; i++) {
1102             if (list.item(i) instanceof Element) {
1103                 Element elem = (Element) list.item(i);
1104                 uuids[i] = elem.getAttribute("uuid");
1105             }
1106         }
1107
1108         return uuids;
1109     }
1110
1111     /**
1112      * Get the length of a NodeList (this is a workaround for a f... java bug)
1113      */
1114     private static final int getNodeListLength(NodeList list) {
1115         int length = 0;
1116         try {
1117             length = list.getLength();
1118         } catch (NullPointerException e) { }
1119
1120         return length;
1121     }
1122
1123     private static final Options getOptions() {
1124         return XConfiguration.get(Options.class, XConfiguration.getXConfigurationDocument(), LAYOUT_PATH)[0];
1125     }
1126
1127
1128     /**
1129      * Inner interface used to have something to execute when the restoration is finished
1130      */
1131     public interface EndedRestoration {
1132
1133         /**
1134          * Stuff to do when the restoration is ended
1135          */
1136         public void finish();
1137     }
1138
1139     @XConfAttribute
1140     private static class Options {
1141
1142         public boolean mustSave;
1143         public int id;
1144
1145         @XConfAttribute(tag = "layouts", attributes = {"id", "save-desktop"})
1146         private void set(int id, boolean mustSave) {
1147             this.id = id;
1148             this.mustSave = mustSave;
1149         }
1150     }
1151 }