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 }