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 }