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