bc22fe05e845b4d79a30498b5b47409a8a12dd33
[scilab.git] / scilab / modules / graphic_objects / src / java / org / scilab / modules / graphic_objects / utils / MenuBarBuilder.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2011 - DIGITEO - Vincent Couvert
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.graphic_objects.utils;
14
15 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_CALLBACKTYPE__;
16 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_CALLBACK__;
17 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_HIDDEN__;
18 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_ACCELERATOR__;
19 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_ENABLE__;
20 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_ICON__;
21 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_LABEL__;
22 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_MNEMONIC__;
23 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_SEPARATOR__;
24 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UIMENU__;
25 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_VISIBLE__;
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.lang.reflect.InvocationHandler;
30 import java.lang.reflect.InvocationTargetException;
31 import java.lang.reflect.Method;
32 import java.lang.reflect.Proxy;
33 import java.util.Collection;
34 import java.util.TreeSet;
35
36 import javax.xml.parsers.ParserConfigurationException;
37
38 import org.scilab.modules.commons.OS;
39 import org.scilab.modules.commons.ScilabConstants;
40 import org.scilab.modules.commons.xml.ScilabDocumentBuilderFactory;
41 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
42 import org.scilab.modules.graphic_objects.graphicObject.CallBack;
43 import org.scilab.modules.graphic_objects.graphicObject.GraphicObject.Type;
44 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
45 import org.scilab.modules.localization.Messages;
46 import org.w3c.dom.Document;
47 import org.w3c.dom.NamedNodeMap;
48 import org.w3c.dom.Node;
49 import org.w3c.dom.NodeList;
50 import org.xml.sax.SAXException;
51
52
53 /**
54  * Create a menuBar from an XML file
55  * @author Vincent COUVERT
56  */
57 public final class MenuBarBuilder {
58
59     private static final String MAINMENUBARXMLFILE = ScilabConstants.SCI + "/modules/gui/etc/main_menubar.xml";
60     private static final String GRAPHICSMENUBARXMLFILE = ScilabConstants.SCI + "/modules/gui/etc/graphics_menubar.xml";
61
62     private static final String FILE_NOT_FOUND = "Could not find file: ";
63
64     private static final String CANNOT_CREATE_MENUBAR = "Cannot create MenuBar.\n"
65             + "Check if file *_menubar.xml is available and valid.";
66
67     private static boolean isParentValid = true;;
68
69     /**
70      * Default constructor
71      */
72     private MenuBarBuilder() {
73         throw new UnsupportedOperationException();
74     }
75
76     /**
77      * Create a proxy to access a XML file
78      * @param resultClass class for the return value
79      * @param fileToLoad XML file to load
80      * @return a proxy used to read the XML file
81      * @throws SAXException can be thrown when an error occurs while reading the file
82      * @throws IOException can be thrown when an error occurs while accessing the file
83      * @throws ParserConfigurationException can be thrown when an error occurs while parsing the file
84      */
85     public static Object buildMenuBar(Class[] resultClass, String fileToLoad) throws SAXException, IOException, ParserConfigurationException {
86
87         InvocationHandler invocationHandler = new MenuBarConfigurationHandler(fileToLoad);
88
89         return Proxy.newProxyInstance(invocationHandler.getClass().getClassLoader(), resultClass, invocationHandler);
90     }
91
92     /**
93      * Create console menubar from data in a XML file
94      * @param consoleId the console
95      */
96     public static void buildConsoleMenuBar(String consoleId) {
97         buildMenuBar(MAINMENUBARXMLFILE, consoleId);
98     }
99
100     /**
101      * Create graphic figure menubar from data in a XML file
102      * @param figureId the figure
103      */
104     public static void buildFigureMenuBar(String figureId) {
105         MenuBarBuilder.isParentValid = false;
106         buildMenuBar(GRAPHICSMENUBARXMLFILE, figureId);
107     }
108
109     /**
110      * Create children used in the menubar from data in a XML file
111      * @param fileToLoad XML file to load
112      * @param parentId the menubar parent
113      */
114     public static void buildMenuBar(String fileToLoad, String parentId) {
115
116         try {
117             MenuBarConfiguration menuBarConfig =
118                 (MenuBarConfiguration) buildMenuBar(new Class[] {MenuBarConfiguration.class}, fileToLoad);
119             menuBarConfig.addMenus(parentId);
120         } catch (IllegalArgumentException e) {
121             System.err.println(CANNOT_CREATE_MENUBAR);
122             System.err.println(FILE_NOT_FOUND + e.getLocalizedMessage());
123         } catch (SAXException e) {
124             System.err.println(CANNOT_CREATE_MENUBAR);
125             System.err.println(FILE_NOT_FOUND + e.getLocalizedMessage());
126         } catch (IOException e) {
127             System.err.println(CANNOT_CREATE_MENUBAR);
128             System.err.println(FILE_NOT_FOUND + e.getLocalizedMessage());
129         } catch (ParserConfigurationException e) {
130             System.err.println(CANNOT_CREATE_MENUBAR);
131             System.err.println(FILE_NOT_FOUND + e.getLocalizedMessage());
132         }
133     }
134
135     /**
136      * Class used to read the XMl file
137      */
138     private static class MenuBarConfigurationHandler implements InvocationHandler {
139         protected static final String LABEL = "label";
140         protected static final String MENU = "menu";
141         protected static final String MNEMONIC = "mnemonic";
142         protected static final String SUBMENU = "submenu";
143         protected static final String SEPARATOR = "separator";
144         protected static final String ENABLED = "enabled";
145         protected static final String ACCELERATOR = "accelerator";
146         protected static final String CALLBACK = "callback";
147         protected static final String TYPE = "type";
148         protected static final String INSTRUCTION = "instruction";
149         protected static final String TRUE = "true";
150         protected static final String ICON = "icon";
151         protected static final String MACOSX = "macosx";
152
153         private Document dom;
154         private Collection<String> internalMethodNames;
155
156
157         /**
158          * Constructor
159          * @param xmlFile XML file to load
160          * @throws SAXException can be thrown when an error occurs while reading the file
161          * @throws IOException can be thrown when an error occurs while accessing the file
162          * @throws ParserConfigurationException can be thrown when an error occurs while parsing the file
163          */
164         public MenuBarConfigurationHandler(String xmlFile) throws SAXException, IOException, ParserConfigurationException {
165
166             if (!new File(xmlFile).exists()) {
167                 throw new java.io.IOException();
168             }
169
170             // Build dictionary for internal method
171             internalMethodNames = new TreeSet<String>();
172             Method[] internalMethodes = this.getClass().getMethods();
173             for (Method method : internalMethodes) {
174                 internalMethodNames.add(method.getName());
175             }
176
177             // Build xml document for request
178             dom = ScilabDocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(xmlFile));
179         }
180
181         /**
182          * Invoke a proxy to get data
183          * @param proxy the proxy to call
184          * @param method the method to call
185          * @param args the arguments for the method
186          * @return the object read
187          * @throws IllegalAccessException thrown when the method called is inaccessible
188          * @throws InvocationTargetException thorwn when the method called threw an exception
189          * @throws NoSuchMethodException thrown when invoking a non-existing method
190          * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
191          */
192         public Object invoke(Object proxy, Method method, Object[] args)
193         throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
194             if (internalMethodNames.contains(method.getName())) {
195                 return getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(this, args);
196             } else {
197                 return null;
198             }
199         }
200
201         /**
202          * Add menus to a menubar using Scilab MVC
203          * @param parentId the tab ID to which the menus will be added to
204          * @see org.scilab.modules.MenuBarConfiguration.utils.MenuBarConfiguration#addMenus(org.scilab.modules.gui.menubar.MenuBar)
205          */
206         public void addMenus(String parentId) {
207
208             // delete old menus
209             for (String childId : (String []) GraphicController.getController().getProperty(parentId, GraphicObjectProperties.__GO_CHILDREN__)) {
210                 if (GraphicController.getController().getProperty(childId, GraphicObjectProperties.__GO_TYPE__).equals(__GO_UIMENU__))
211                     GraphicController.getController().removeRelationShipAndDelete(childId);
212             }
213
214             NodeList menus = dom.getElementsByTagName(MENU);
215
216             for (int i = 0; i < menus.getLength(); i++) {
217                 // Create the menu
218                 String menuId = null;
219                 if (isParentValid) {
220                     menuId = GraphicController.getController().askObject(Type.UIMENU);
221                 } else {
222                     menuId = GraphicController.getController().askObject(Type.UIMENUMODEL);
223                 }
224
225                 // The menu is not visible in Scilab view by default
226                 GraphicController.getController().setProperty(menuId, __GO_HIDDEN__, true);
227
228                 // Set the label
229                 String menuLabel = Messages.gettext(menus.item(i).getAttributes().getNamedItem(LABEL).getNodeValue());
230                 GraphicController.getController().setProperty(menuId, __GO_UI_LABEL__, menuLabel);
231
232                 // Set the mnemonic if given
233                 if (menus.item(i).getAttributes().getNamedItem(MNEMONIC) != null) {
234                     String mnemonicString = menus.item(i).getAttributes().getNamedItem(MNEMONIC).getNodeValue();
235                     GraphicController.getController().setProperty(menuId, __GO_UI_MNEMONIC__, mnemonicString);
236                 }
237
238                 // Set the icon if given
239                 if (menus.item(i).getAttributes().getNamedItem(ICON) != null) {
240                     String iconName = menus.item(i).getNodeValue();
241                     GraphicController.getController().setProperty(menuId, __GO_UI_ICON__, iconName);
242                 }
243
244                 // Set the enable status if given
245                 if (menus.item(i).getAttributes().getNamedItem(ENABLED) != null) {
246                     boolean enabled = menus.item(i).getAttributes().getNamedItem(ENABLED).getNodeValue().equals(TRUE);
247                     GraphicController.getController().setProperty(menuId, __GO_UI_ENABLE__, enabled);
248                 }
249                 // Set the menu parent
250                 GraphicController.getController().setGraphicObjectRelationship(parentId, menuId);
251                 addSubMenus(menuId, i);
252             }
253         }
254
255         /**
256          * Read submenus data in the XML file and create them using Scilab MVC
257          * @param parentMenuId the parent menu UID for submenus
258          * @param index the index of the parent in menu list
259          */
260         public void addSubMenus(String parentMenuId, int index) {
261             Node submenu = dom.getElementsByTagName(MENU).item(index).getFirstChild();
262
263             boolean separator = false;
264             boolean macosx = true;
265
266             while (submenu != null) {
267                 if (submenu.getNodeName() == SEPARATOR) {
268                     // Add a separator
269                     separator = true;
270                 } else if (submenu.getNodeName() == SUBMENU) {
271                     // Create the menu
272                     String menuId = null;
273                     if (isParentValid) {
274                         menuId = GraphicController.getController().askObject(Type.UIMENU);
275                     } else {
276                         menuId = GraphicController.getController().askObject(Type.UIMENUMODEL);
277                     }
278
279                     // The menu is not visible in Scilab view by default
280                     GraphicController.getController().setProperty(menuId, __GO_HIDDEN__, true);
281
282                     // Set the menu parent
283                     GraphicController.getController().setGraphicObjectRelationship(parentMenuId, menuId);
284
285                     // First we have to read its attributes
286                     NamedNodeMap attributes = submenu.getAttributes();
287
288                     for (int i = 0; i < attributes.getLength(); i++) {
289                         if (attributes.item(i).getNodeName() == LABEL) {
290                             // Set the label
291                             String menuLabel = Messages.gettext(attributes.item(i).getNodeValue());
292                             GraphicController.getController().setProperty(menuId, __GO_UI_LABEL__, menuLabel);
293                         } else if (attributes.item(i).getNodeName() == MNEMONIC) {
294                             // Set the mnemonic
295                             String mnemonicString = attributes.item(i).getNodeValue();
296                             GraphicController.getController().setProperty(menuId, __GO_UI_MNEMONIC__, mnemonicString);
297                         } else if (attributes.item(i).getNodeName() == ENABLED) {
298                             // Set the enable status
299                             boolean enabled = attributes.item(i).getNodeValue().equals(TRUE);
300                             GraphicController.getController().setProperty(menuId, __GO_UI_ENABLE__, enabled);
301                         } else if (attributes.item(i).getNodeName() == ICON) {
302                             // Set the icon
303                             String iconName = attributes.item(i).getNodeValue();
304                             GraphicController.getController().setProperty(menuId, __GO_UI_ICON__, iconName);
305                         } else if (attributes.item(i).getNodeName() == ACCELERATOR) {
306                             // Set the accelerator
307                             String acceleratorString = attributes.item(i).getNodeValue();
308                             GraphicController.getController().setProperty(menuId, __GO_UI_ACCELERATOR__, acceleratorString);
309                         } else if (attributes.item(i).getNodeName() == MACOSX) {
310                             macosx = attributes.item(i).getNodeValue().equals(TRUE);
311                             if (!macosx && OS.get() == OS.MAC) {
312                                 GraphicController.getController().setProperty(menuId, __GO_VISIBLE__, false);
313                                 separator = false;
314                             }
315                         }
316                     }
317
318                     // Then we get its callback (if exists)
319                     Node callback = submenu.getFirstChild();
320                     while (callback != null) {
321                         if (callback.getNodeName() == CALLBACK) {
322                             NamedNodeMap cbAttributes = callback.getAttributes();
323                             String command = null;
324                             int commandType = CallBack.UNTYPED;
325                             for (int j = 0; j < cbAttributes.getLength(); j++) {
326                                 if (cbAttributes.item(j).getNodeName() == INSTRUCTION) {
327                                     command = cbAttributes.item(j).getNodeValue();
328                                 } else if (cbAttributes.item(j).getNodeName() == TYPE) {
329                                     commandType = Integer.parseInt(cbAttributes.item(j).getNodeValue());
330                                 }
331                             }
332                             if (command != null && commandType != CallBack.UNTYPED) {
333                                 GraphicController.getController().setProperty(menuId, __GO_CALLBACK__, command);
334                                 GraphicController.getController().setProperty(menuId, __GO_CALLBACKTYPE__, commandType);
335                             }
336                         } else if (callback.getNodeName() == SUBMENU) {
337                             addSubMenuItem(menuId, callback);
338                         }
339                         // Read next child
340                         callback = callback.getNextSibling();
341                     }
342                     // Manage separators
343                     if (separator) {
344                         GraphicController.getController().setProperty(menuId, __GO_UI_SEPARATOR__, true);
345                         separator = false;
346                     }
347                 }
348                 // Read next child
349                 submenu = submenu.getNextSibling();
350             }
351         }
352
353         /**
354          * Add submenu for menu
355          * @param parentMenuItemId object with this id will become a menu with subMenuItems
356          * @param node to get attributs of the menu
357          */
358         public void addSubMenuItem(String parentMenuItemId, Node node) {
359
360             NamedNodeMap attributes = node.getAttributes();
361
362             // Create the menu
363             String subMenuItemId = null;
364             if (isParentValid) {
365                 subMenuItemId = GraphicController.getController().askObject(Type.UIMENU);
366             } else {
367                 subMenuItemId = GraphicController.getController().askObject(Type.UIMENUMODEL);
368             }
369
370             // The menu is not visible in Scilab view by default
371             GraphicController.getController().setProperty(subMenuItemId, __GO_HIDDEN__, true);
372
373             // Set the menu parent
374             GraphicController.getController().setGraphicObjectRelationship(parentMenuItemId, subMenuItemId);
375
376             for (int i = 0; i < attributes.getLength(); i++) {
377                 if (attributes.item(i).getNodeName() == LABEL) {
378                     // Set the label
379                     String menuLabel = Messages.gettext(attributes.item(i).getNodeValue());
380                     GraphicController.getController().setProperty(subMenuItemId, __GO_UI_LABEL__, menuLabel);
381                 } else if (attributes.item(i).getNodeName() == MNEMONIC) {
382                     // Set the mnemonic
383                     String mnemonicString = attributes.item(i).getNodeValue();
384                     GraphicController.getController().setProperty(subMenuItemId, __GO_UI_MNEMONIC__, mnemonicString);
385                 } else if (attributes.item(i).getNodeName() == ICON) {
386                     String iconName = attributes.item(i).getNodeValue();
387                     GraphicController.getController().setProperty(subMenuItemId, __GO_UI_ICON__, iconName);
388                 } else if (attributes.item(i).getNodeName() == ENABLED) {
389                     // Set the enable status
390                     boolean enabled = attributes.item(i).getNodeValue().equals(TRUE);
391                     GraphicController.getController().setProperty(subMenuItemId, __GO_UI_ENABLE__, enabled);
392                 }
393             }
394
395             // Then we get its callback (if exists)
396             Node callback = node.getFirstChild();
397             while (callback != null) {
398                 if (callback.getNodeName() == CALLBACK) {
399                     NamedNodeMap cbAttributes = callback.getAttributes();
400                     String command = null;
401                     int commandType = CallBack.UNTYPED;
402                     for (int j = 0; j < cbAttributes.getLength(); j++) {
403                         if (cbAttributes.item(j).getNodeName() == INSTRUCTION) {
404                             command = cbAttributes.item(j).getNodeValue();
405                         } else if (cbAttributes.item(j).getNodeName() == TYPE) {
406                             commandType = Integer.parseInt(cbAttributes.item(j).getNodeValue());
407                         }
408                     }
409                     if (command != null && commandType != CallBack.UNTYPED) {
410                         GraphicController.getController().setProperty(subMenuItemId, __GO_CALLBACK__, command);
411                         GraphicController.getController().setProperty(subMenuItemId, __GO_CALLBACKTYPE__, commandType);
412                     }
413                 } else if (callback.getNodeName() == SUBMENU) {
414                     addSubMenuItem(subMenuItemId, callback);
415                 }
416                 // Read next child
417                 callback = callback.getNextSibling();
418             }
419         }
420     }
421 }