001    package nl.cwi.sen1.util;
002    
003    import java.awt.Component;
004    import java.awt.event.InputEvent;
005    import java.awt.event.KeyEvent;
006    
007    import javax.swing.Action;
008    import javax.swing.JMenu;
009    import javax.swing.JMenuItem;
010    import javax.swing.KeyStroke;
011    
012    import nl.cwi.sen1.configapi.types.ActionDescription;
013    import nl.cwi.sen1.configapi.types.ActionDescriptionList;
014    import nl.cwi.sen1.configapi.types.Event;
015    import nl.cwi.sen1.configapi.types.ItemLabels;
016    import nl.cwi.sen1.configapi.types.KeyModifier;
017    import nl.cwi.sen1.configapi.types.KeyModifierList;
018    import nl.cwi.sen1.configapi.types.ShortCut;
019    import toolbus.AbstractTool;
020    import aterm.ATerm;
021    
022    public class MenuBuilder {
023            private AbstractTool bridge;
024    
025            public MenuBuilder(AbstractTool bridge) {
026                    this.bridge = bridge;
027            }
028    
029            /**
030             * Build a JMenu from a list of menu declarations in ATerm format. This
031             * method may skip the first item the menu path leading up to each menu
032             * item, such that the JMenu can be embedded in a menu bar, as well as in a
033             * popup menu.
034             * 
035             * @param id
036             *            the name of the gui element that owns the menu
037             * @param menuList
038             *            the list of menu options
039             * @param action
040             *            if not null, this action will be executed on clicking the menu
041             * @param skip
042             *            the number of labels to skip
043             * @return
044             */
045            public void fill(JMenu menu, ATerm id, ActionDescriptionList menuList, Action action) {
046                    for (; !menuList.isEmpty(); menuList = menuList.getTail()) {
047                            ActionDescription first = menuList.getHead();
048                            Event event = first.getEvent();
049                            Action exec = action != null ? action : buildMenuAction(id, bridge,
050                                            event);
051                            addMenuPath(menu, event, exec);
052                    }
053            }
054            
055            /**
056             * This extension point implements the default reaction to a menu event,
057             * which is forward the event to the ToolBus
058             * 
059             * @param id
060             * @param bridge
061             * @param event
062             * @return
063             */
064            protected Action buildMenuAction(ATerm id, AbstractTool bridge, Event event) {
065                    return new MenuAction(id, bridge, event);
066            }
067    
068            private void addMenuPath(JMenu menu, Event event, Action action) {
069                    if (!event.hasLabels()) {
070                            return;
071                    }
072                    
073                    ItemLabels items = event.getLabels();
074    
075                    if (items.isEmpty()) {
076                            return;
077                    }
078    
079                    /* traverse the existing menu structure to find a matching path */
080                    for (; items.getLength() > 1; items = items.getTail()) {
081                            String text = items.getHead().getName();
082                            JMenu menuExists = findSubMenu(menu, text);
083                            if (menuExists == null) {
084                                    menuExists = new JMenu(text);
085                                    menu.add(menuExists);
086                            }
087                            menu = menuExists;
088                    }
089    
090                    /* add a new leaf to the deepest node of the found path */
091                    String text = items.getHead().getName();
092                    JMenuItem leaf = new JMenuItem(action);
093                    leaf.setText(text);
094                    if (event.hasShortcut()) {
095                            KeyStroke keyStroke = createKeyStroke(event.getShortcut());
096                            if (keyStroke != null) {
097                                    leaf.setAccelerator(keyStroke);
098                            }
099                    }
100                    leaf.setToolTipText(event.getInfo());
101                    menu.add(leaf);
102            }
103    
104            private KeyStroke createKeyStroke(ShortCut shortcut) {
105                    String key = shortcut.getKey().toString();
106                    KeyModifierList modifiers = shortcut.getList();
107    
108                    int mask = 0;
109                    while (!modifiers.isEmpty()) {
110                            KeyModifier mod = modifiers.getHead();
111                            modifiers = modifiers.getTail();
112    
113                            if (mod.isM_ALT()) {
114                                    mask |= InputEvent.ALT_MASK;
115                            } else if (mod.isM_CTRL()) {
116                                    mask |= InputEvent.CTRL_MASK;
117                            } else {
118                                    mask |= InputEvent.SHIFT_MASK;
119                            }
120                    }
121    
122                    try {
123                            return KeyStroke.getKeyStroke(KeyEvent.class.getField(key).getInt(
124                                            KeyEvent.class), mask);
125                    } catch (IllegalArgumentException e) {
126                            e.printStackTrace();
127                    } catch (SecurityException e) {
128                            e.printStackTrace();
129                    } catch (IllegalAccessException e) {
130                            e.printStackTrace();
131                    } catch (NoSuchFieldException e) {
132                            e.printStackTrace();
133                    }
134    
135                    return null;
136            }
137    
138            private JMenu findSubMenu(JMenu parent, String text) {
139                    Component[] components = parent.getMenuComponents();
140                    for (int i = 0; i < components.length; i++) {
141                            Component cur = components[i];
142                            if (cur instanceof JMenu) {
143                                    JMenu item = (JMenu) cur;
144                                    if (item.getText().equals(text)) {
145                                            return item;
146                                    }
147                            }
148                    }
149                    return null;
150            }
151    }