001    // GraphLab Project: http://graphlab.sharif.edu
002    // Copyright (C) 2008 Mathematical Science Department of Sharif University of Technology
003    // Distributed under the terms of the GNU General Public License (GPL): http://www.gnu.org/licenses/
004    package graphlab.ui.xml;
005    
006    import graphlab.platform.core.AbstractAction;
007    import graphlab.platform.core.BlackBoard;
008    import graphlab.platform.extension.ExtensionLoader;
009    import graphlab.platform.lang.Pair;
010    import graphlab.platform.preferences.lastsettings.StorableOnExit;
011    import graphlab.ui.AbstractExtensionAction;
012    import graphlab.ui.UIUtils;
013    import graphlab.ui.actions.UIEventData;
014    import graphlab.ui.components.*;
015    import graphlab.ui.components.gmenu.GMenuBar;
016    import graphlab.ui.components.gmenu.GMenuItem;
017    import graphlab.ui.components.gmenu.KeyBoardShortCut;
018    import graphlab.ui.components.gmenu.KeyBoardShortCutProvider;
019    import graphlab.ui.components.gsidebar.GSidebar;
020    import graphlab.ui.extension.UIActionExtensionAction;
021    import org.xml.sax.Attributes;
022    import org.xml.sax.SAXException;
023    
024    import javax.swing.*;
025    import java.awt.*;
026    import java.lang.reflect.Constructor;
027    import java.lang.reflect.InvocationTargetException;
028    import java.util.HashMap;
029    
030    
031    public class UIHandlerImpl implements UIHandler, StorableOnExit {
032    
033        public static final boolean DEBUG = false;
034        public GToolbar toolbar;
035        public BlackBoard blackboard;
036        Class resourceClass;
037        HashMap<String, graphlab.platform.core.AbstractAction> actions = null;
038        /**
039         * determines the character which if put before a character in the string of the label, that character will be set to it's mnemonics
040         */
041        public static final char menueIndexChar = '_';
042    
043        public UIHandlerImpl(GFrame gFrame, BlackBoard bb
044                , HashMap<String, graphlab.platform.core.AbstractAction> actions, Class resClass) {
045            this.blackboard = bb;
046            this.toolbar = gFrame.getToolbar();
047            this.sidebar = gFrame.getSidebar();
048            this.statusbar = gFrame.getStatusbar();
049            //this.blackboard = gFrame.blackboard;
050            this.menubar = gFrame.getMenu();
051            this.frame = gFrame;
052            this.actions = actions;
053            this.resourceClass = resClass;
054    //        toolbar.add(lastToolbar);
055        }
056    
057        //**********      TOOLBARS handling  ---------------------
058        private JToolBar lastToolbar = new JToolBar();
059        private int lastToolbarPlace;
060    
061        public void start_toolbar(final Attributes meta) throws SAXException {
062    //        lastToolbar = new JToolBar();
063    //        lastToolbar.setFloatable(false);
064            lastToolbar = toolbar.createToolBar();
065            lastToolbarPlace = extractPlace(meta);
066            if (DEBUG) System.err.println("start_toolbar: " + meta);
067        }
068    
069        public void handle_tool(final Attributes meta) throws SAXException {
070            String label = meta.getValue("label");
071            String icon = meta.getValue("image");
072            String action = meta.getValue("action");
073            String _place = meta.getValue("place");
074            int place = -1;
075            if (_place != null)
076                place = Integer.parseInt(_place);
077            GButton b;
078    
079            if (resourceClass != null && icon != null)
080                System.out.println("[handle_tool]" + icon + " : " + resourceClass.getResource(icon));
081    
082            if (icon == null || icon.equals("") || resourceClass == null ||
083                    resourceClass.getResource(icon) == null)
084                b = new GButton(label, icon, blackboard, action);
085            else
086                b = new GButton(label, resourceClass.getResource(icon), blackboard, action);
087            b.setBorderPainted(false);
088    //        b.setBorder(new EmptyBorder(0, 1, 0, 2));
089    
090            lastToolbar.add(b);
091            if (DEBUG) System.err.println("handle_tool: " + meta);
092        }
093    
094        public void end_toolbar() throws SAXException {
095            lastToolbar.add(new JSeparator(JSeparator.VERTICAL));
096            toolbar.addIndexed(lastToolbar, lastToolbarPlace);
097    //        toolbar.add(lastToolbar);
098            if (DEBUG) System.err.println("end_toolbar()");
099        }
100    
101        public void start_toolbars(Attributes meta) throws SAXException {
102    //        lastToolbar = toolbar.getLastToolBar();
103        }
104    
105        public void end_toolbars() throws SAXException {
106        }
107    
108        //***************** status handling --------------------
109        public GStatusBar statusbar;
110    
111        public void handle_bar(Attributes meta) throws SAXException {
112            String clazz = meta.getValue("class");
113            String id = meta.getValue("id");
114            System.out.println("Adding the Bar with id:" + id + " ,class:" + clazz);
115            Component _ = getComponent(clazz);
116            //we put the component in the blackboad and later (in Actions) we can fetch it if we like, i guess that it is the only way to do the loading of side bar and actions dynamically
117            UIUtils.setComponent(blackboard, id, _);
118            statusbar.addComponent(_);
119        }
120    
121        //*****************menu handling------------------------
122        public GMenuBar menubar;
123        private GFrame frame;
124        private JMenu currentMenu;
125    
126        public void start_menues(Attributes meta) throws SAXException {
127        }
128    
129        static HashMap<JMenuItem, Integer> places = new HashMap<JMenuItem, Integer>();
130    
131        private Pair<Integer, String> extractLabelInfo(String label) {
132            int index = Math.max(label.indexOf(menueIndexChar), 0);
133            label = label.replace(menueIndexChar + "", "");
134            return new Pair<Integer, String>(index, label);
135        }
136    
137        int lastMenuPlace;
138    
139        public void start_submenu(final Attributes meta) throws SAXException {
140            String label = meta.getValue("label");
141            String accel = meta.getValue("accelerator");
142            Pair<Integer, String> lInfo = extractLabelInfo(label);
143            lastMenuPlace = extractPlace(meta);
144            int index = lInfo.first;
145            label = lInfo.second;
146            currentMenu = menubar.getUniqueMenu(label, lastMenuPlace);
147    //        currentMenu = new JMenu(label);
148    
149            KeyBoardShortCut shortcut = KeyBoardShortCutProvider.registerKeyBoardShortcut(accel, label, index);
150            if (shortcut != null) {
151                if (!shortcut.isAccelerator()) {
152                    currentMenu.setMnemonic(shortcut.getKeyMnemonic());
153                    currentMenu.setDisplayedMnemonicIndex(shortcut.getKeyWordIndex());
154                }
155            }
156            if (DEBUG) System.err.println("start_submenu: " + meta);
157        }
158    
159        public void handle_menu(final Attributes meta) throws SAXException {
160            String label = meta.getValue("label");
161            String action = meta.getValue("action");
162            String accel = meta.getValue("accelerator");
163            int place = extractPlace(meta);
164            if (label.equals("seperator_menu")) {
165                JSeparator js = new JSeparator(JSeparator.HORIZONTAL);
166                menubar.insert(currentMenu, js, place);
167                return;
168            }
169            Pair<Integer, String> lInfo = extractLabelInfo(label);
170            int index = lInfo.first;
171            label = lInfo.second;
172    
173    
174            GMenuItem item;
175            /**
176             * extension handling part:
177             * if the menu action was an extension removes the menu
178             * that the extension created in UI and set it to this menu.
179             */
180            graphlab.platform.core.AbstractAction targetAction = actions.get(action);
181            if (targetAction instanceof AbstractExtensionAction) {
182                AbstractExtensionAction targetExt = (AbstractExtensionAction) targetAction;
183    //            targetExt.removeCreatedUIComponents();
184    //            targetExt.listen4Event(UI.getUIEvent(action));
185                item = targetExt.menuItem;
186                /**
187                 * set the label properties according to XML
188                 */
189    //            if (accel != null && !accel.equals(""))
190    //                item.setAccelerator(accel);
191    //                if (label != null && !label.equals(""))
192                item.setText(label);
193                //todo: BUG the mnemotic doesn't set
194                item.setMnemonic(index);
195            } else {
196                item = new GMenuItem(label, action, blackboard, accel, index);
197            }
198    
199            menubar.insert(currentMenu, item, place);
200            if (DEBUG) System.err.println("handle_menu: " + meta);
201        }
202    
203        private int extractPlace(Attributes meta) {
204            String _place = meta.getValue("place");
205            int place = -1;     //place -1 means no idea given for place,
206            try {
207                if (_place != null)
208                    place = Integer.parseInt(_place);
209            }
210            catch (NumberFormatException e) {
211                System.err.println("the place given for menu " + meta.getValue("label") + " is not a valid number:" + _place);
212            }
213            return place;
214        }
215    
216        public void end_submenu() throws SAXException {
217    //        currentMenu.add(new JSeparator(JSeparator.VERTICAL));
218    //        GMenuBar.insert(currentMenu, new JSeparator(JSeparator.VERTICAL), -1);
219            //todo: add ability of adding JSeperator to UI
220            if (DEBUG) System.err.println("end_submenu()");
221        }
222    
223        public void end_menues() throws SAXException {
224            //pak kardan e menu e action e ezafe
225            GMenuBar menu = frame.getMenu();
226            for (int i = 0; i < menu.getMenuCount(); i++) {
227                if (menu.getMenu(i).getText().equals("Actions") && menu.getMenu(i).getSubElements().length == 1) {
228                    menu.remove(i);
229                    return;
230                }
231            }
232        }
233    //***************** action handling ----------------------
234    
235    // If action has a group, then its default value for enable will be true,
236    // else it will true if enable property equals to true.
237    
238        public void handle_action(Attributes meta) throws SAXException {
239            String clazz = meta.getValue("class");
240            String id = meta.getValue("id");
241            String group = meta.getValue("group");
242    //todo: is it good to remove the action wich loaded twice, (2 of same action are working together)
243            System.err.println("  Adding action " + clazz + " (" + id + "," + group + ") ...");
244    
245            Class<?> clazzz = null;
246            try {
247                clazzz = Class.forName(clazz);
248            } catch (ClassNotFoundException e) {
249                System.err.println("the given class name can't be loaded: " + clazz);
250                e.printStackTrace();
251                return;
252            }
253            boolean b = false;
254            graphlab.platform.core.AbstractAction x = loadAbstractAction(clazz);
255    
256    /**
257     * handling extensions here
258     * they are not AbstractAction, but their handlers should
259     * return an AbstractAction(if it support the extension)
260     * and after that the program know the
261     * extension by that AbstractAction
262     */
263    //        if (clazzz.isAssignableFrom(Extension.class)) {
264    
265    //        for (ExtensionHandler s : ExtensionLoader.getRegisteredExtensionHandlers()) {
266            if (x == null) {
267                Object e = ExtensionLoader.loadExtension(clazzz);
268    //            SETTINGS.registerSetting(e,"Extention Options");     //Moved to Extension Loader
269                if (e != null)
270                    x = ExtensionLoader.handleExtension(blackboard, e);
271            }
272    //            x = s.handle(blackboard, clazzz);
273    //        b = b | x != null;
274            if (x != null) {
275                if (id == null) {
276                    id = x.getLastListenedEventKey();
277                    id = id.replaceFirst(UIEventData.name(""), "");
278                }
279                if (x instanceof UIActionExtensionAction) {
280                    UIActionExtensionAction _ = (UIActionExtensionAction) x;
281                    _.setUIEvent(id);
282                }
283                addAction(id, x, group);
284    //            }
285    //        }
286    //        }
287    //        if (b) {
288    //            return;
289            }
290            if (x == null) {
291                System.err.println("Error while loading " + clazz + ". skiped.");
292                return; //error, skip it.
293            }
294            addAction(id, x, group);
295        }
296    
297        private void addAction(String id, graphlab.platform.core.AbstractAction x, String group) {
298            if ((id != null) && !id.equals(""))
299                actions.put(id, x);
300    //        if (group != null && !group.equals("")) {
301    //            //configuration age group vojood nadashte bashe khodesh ijadesh mikone. inja be ghole omid "error prone hast"
302    //            //todo: bara hamin be zehnam resid ke biaim bebinim esme gorooha masalan age kamtar az 2harf ekhtelaf daran, pas ehtemalan eshtebahe typi boode , ie jooraii kashf konim eroro :D
303    //            conf.addToGroup(group, x);
304    //        }
305        }
306    
307        //****************** side bar handling -------------------------
308        public GSidebar sidebar;
309    
310        public void start_sidebar(Attributes meta) throws SAXException {
311        }
312    
313        public void handle_sidebar(Attributes meta) throws SAXException {
314            String image = meta.getValue("image") + "";//to getting it not null
315            String clazz = meta.getValue("class");
316            String id = meta.getValue("id");
317            String label = meta.getValue("label");
318    
319            Component component = getComponent(clazz);
320            UIUtils.setComponent(blackboard, id, component);
321            if (resourceClass == null || resourceClass.getResource(image) == null) {
322                sidebar.addButton(image, component, label);
323            } else
324                sidebar.addButton(resourceClass.getResource(image), component, label);
325        }
326    
327        public void end_sidebar() throws SAXException {
328        }
329    
330    //************* body handling ------------------------------------
331    
332        public void handle_body(Attributes meta) throws SAXException {
333            String clazz = meta.getValue("class");
334            String id = meta.getValue("id");
335            Component gci = getComponent(clazz);
336            frame.getBody().setBodyPane(gci);
337            UIUtils.setComponent(blackboard, id, gci);
338        }
339    
340    //************** utilities +++++++++++++++++++++
341    
342        AbstractAction loadAbstractAction(String abstractActionclazz) {
343            String clazz = abstractActionclazz;
344            if (!(clazz == null) && !(clazz.equals(""))) {
345                Class t = clazz2Class(clazz);
346                if (graphlab.platform.core.AbstractAction.class.isAssignableFrom(t)) {
347                    Object[] o = {blackboard};
348                    try {
349                        Constructor c = t.getConstructor(BlackBoard.class);
350                        Object _ = c.newInstance(o);
351                        return (graphlab.platform.core.AbstractAction) _;
352                    }
353                    catch (Exception e) {
354    //                    System.err.println("Error while loading " + clazz);
355                        e.printStackTrace();
356                    }
357                }
358            }
359    //        System.err.println("Error while loading " + clazz);
360            return null;
361        }
362    
363        //todo: it is possible to also get a component from xml by it's direct class name, like javax.swing.JLabel . but i decided not to do it for cleaner codes! i am not sure is it good or not?
364        Component getComponent(String GComponentInterfaceClassName) {
365            String clazz = GComponentInterfaceClassName;
366            if (!(clazz == null) && !(clazz.equals(""))) {
367                Class t = clazz2Class(clazz);
368                Constructor c = null;
369                Object[] o = {blackboard};
370                try {
371                    c = t.getConstructor(BlackBoard.class);
372                } catch (NoSuchMethodException e) {
373                    try {
374                        c = t.getConstructor(new Class[]{});
375                        o = new Object[]{};
376                    } catch (NoSuchMethodException e1) {
377                        System.err.println("the clazz " + clazz + "does not have a constructor(blackboard) or constructor(), how can i load it?");
378                        e1.printStackTrace();
379                    }
380    //                e.printStackTrace();
381                }
382                //if it had a constructor(blackboard)
383                try {
384                    Object _ = c.newInstance(o);
385                    if (_ instanceof GComponentInterface) {
386                        //load was successfull
387                        return ((GComponentInterface) _).getComponent(blackboard);
388                    } else {
389                        System.err.println("the class " + clazz + " doesn't implement the interface GComponentInterface, so it can't be put on the UI.");
390                    }
391                } catch (InstantiationException e) {
392                    System.err.println("There was an error while initializing the class" + clazz + "may be in it's constructor or in one of classes it instantiate in its constructor");
393                    e.printStackTrace();
394                } catch (IllegalAccessException e) {
395                    System.err.println("There was an error while initializing the class" + clazz + "may be in it's constructor or in one of classes it instantiate in its constructor");
396                    e.printStackTrace();
397                } catch (InvocationTargetException e) {
398                    System.err.println("There was an error while initializing the class" + clazz + "may be in it's constructor or in one of classes it instantiate in its constructor");
399                    e.getTargetException().printStackTrace();
400                }
401            }
402            return null;
403        }
404    
405        private Class clazz2Class(String clazz) {
406            Class t = null;
407            try {
408                t = Class.forName(clazz);
409            } catch (ClassNotFoundException e) {
410                System.err.println("the Class" + clazz + "didn't found in class path");
411                e.printStackTrace();
412            }
413            return t;
414        }
415    
416    }