Project: Eclipse_Platform_UI
Bug 2682 [UI] EC: Can't contribute submenu items (1GI3XO6)
Reported: 2001-10-10 22:41:23 | Status: resolved fixed

EC post "Menus and submenus" by GB on Aug 2, 2001: I am trying to create a cascading menu under my own menu. MyMenu Menu1->Submenu1 Submenu2 I cannot get this to work. I can, using the same technique as outlined in the help, add the submenu to the Window menu, and it works. Window Menu1->Submenu1 Submenu2 But when I try to add the submenu to my menu, the menu shows up but it is grayed and none of the Submenu items are available. MyMenu Menu1 (grayed) The log reports 1 org.eclipse.core.resources 4 Invalid Menu Extension (Path is invalid): SubMenu1Id Here is the sample I based the code on. This is from the Eclipse documentation. <extension point = "org.eclipse.ui.actionSets"> <actionSet id="org_eclipse_ui_examples_readmetool_actionSet" label="ReadMe Actions" visible="true"> <menu id="org_eclipse_ui_examples_readmetool" label="Readme &File Editor" path="window/additions"> <separator name="slot1"/> <separator name="slot2"/> <separator name="slot3"/> </menu> <action id="org_eclipse_ui_examples_readmetool_readmeAction" menubarPath="window/org_eclipse_ui_examples_readmetool/slot1" toolbarPath="readme" label="&Open Readme Browser@Ctrl+R" tooltip="Open Readme Browser" helpContextId="org.eclipse.ui.examples.readmetool.open_browser_action_context" icon="icons/basic/ctool16/openbrwsr.gif" class="org.eclipse.ui.examples.readmetool.WindowActionDelegate" enablesFor="1"> <selection class="org.eclipse.core.resources.IFile" name="*.readme"> </selection> Here is a version that I got to work in my own application. <actionSet id="com.rational.test.ft.wswplugin.TestActionSet" visible="true" label="MyMenu"> <menu id = "MyMenuId" label = "&MyMenu"> <separator name = "Menu1Slot" /> </menu> <menu id="Menu1Id" label = "&Menu1" path ="window/additions"> <separator name="slot1" /> <separator name="slot2" /> <separator name="slot3" /> </menu> <action id = "SubMenu1Id" menubarPath = "window/Menu1Id/slot1" label = "&SubMenu1" class = "com.rational.test.ft.wswplugin.x"> </action> Here is a version that fails in my application. The only difference is that instead of window, I use MyMenu and instead of additions, I use MyMenu1Slot. I tried variations on ids and slots, but nothing changed. <actionSet id="com.rational.test.ft.wswplugin.TestActionSet" visible="true" label="MyMenu"> <menu id = "MyMenuId" label = "&MyMenu"> <separator name = "Menu1Slot" /> </menu> <menu id="Menu1Id" label = "&Menu1" path ="MyMenuId/Menu1Slot"> <separator name="slot1" /> <separator name="slot2" /> <separator name="slot3" /> </menu> <action id = "SubMenu1Id" menubarPath = "MyMenuId/Menu1Id/slot1" label = "&SubMenu1" class = "com.rational.test.ft.wswplugin.x"> </action> NOTES: NE (8/7/01 5:25:18 PM) Reproduced both success case and fail case. Problem is that the call to parent.findMenuUsingPath(mpath) in PlugActionSetBuilder.contributeMenuAction() returns null when mpath has multiple segments. This is because SubMenuManager.findUsingPath calls findUsingPath on the parentMgr, and MenuManager.findUsingPath tests whether the parent menu is an IMenuManager, which it is not in this case. It was wrapped in an ActionSetContributionItem (subclass of SubContributionItem). Should change SubMenuManager.findUsingPath to recurse itself, unwrapping at each stage. NE (08/07/01 6:01:14 PM) Suggested fix: Change SubContributionManager.find() to unwrap (it doesn't currently, but should): public IContributionItem find(String id) { IContributionItem item = parentMgr.find(id); if (item instanceof SubContributionItem) { return ((SubContributionItem)item).getInnerItem(); } else { return item; } } Change SubMenuManager.getWrapper(IMenuManager) to always create wrapper (as spec'ed). Also added @param tag: /** * Returns the menu wrapper for a menu manager. * <p> * The sub menus within this menu are wrapped within a <code>SubMenuManager</code> to * monitor additions and removals. If the visibility of this menu is modified * the visibility of the sub menus is also modified. * <p> * * @param menu the menu manager to wrap * @return the menu wrapper */ private IMenuManager getWrapper(IMenuManager menu) { if (mapMenuToWrapper == null) { mapMenuToWrapper = new HashMap(4); } SubMenuManager wrapper = (SubMenuManager) mapMenuToWrapper.get(menu); if (wrapper == null) { wrapper = wrapMenu(menu); mapMenuToWrapper.put(menu, wrapper); } return wrapper; } Change SubMenuManager.findMenuUsingPath to just call getWrapper() rather than getWrapper then putWrapper if null: public IMenuManager findMenuUsingPath(String path) { IContributionItem item = findUsingPath(path); if (item instanceof IMenuManager) { IMenuManager menu = (IMenuManager)item; return getWrapper(menu); } return null; } Change SubMenuManager.findUsingPath to use same algorithm as MenuManager.findUsingPath, but wrapping submenus along the way: public IContributionItem findUsingPath(String path) { String id = path; String rest = null; int separator = path.indexOf('/'); if (separator != -1) { id = path.substring(0, separator); rest = path.substring(separator + 1); } IContributionItem item = find(id); // unwraps item if (rest != null && item instanceof IMenuManager) { IMenuManager menu = (IMenuManager) item; // wrap menu, just like findMenuUsingPath IMenuManager wrapper = getWrapper(menu); return wrapper.findUsingPath(rest); } return item; } Delete SubMenuManager.putWrapper -- it's no longer needed.

Files changed in fix:
bundles/org.eclipse.ui/Eclipse JFace/org/eclipse/jface/action/SubContributionManager.java bundles/org.eclipse.ui/Eclipse JFace/org/eclipse/jface/action/SubMenuManager.java bundles/org.eclipse.ui/Eclipse UI/org/eclipse/ui/internal/ActionSetMenuManager.java