Jbpm designer and context menu extensibility
krzogr Sep 29, 2005 5:09 AMHi,
I am working on a product which uses internally Jbpm designer plugin and adds additional functionality on top of it. I was thinking about extending context (popup) menus in the workflow designer and I thought it would be good to discuss it with you. I think that the following requirements regarding context menus can be specified from my point of view:
1. It would be good if I could extend (define) context menus without needing to change original JBPM designer code. Currently it is rather impossible:
- menu in the source page is set to null (cvs head)
- context menu in the diagram page requires changes in designer code.
2. It would be good if I could define the context menus declaratively (plugin xml and extension points).
I have checked that even using the declarative approach I can specify that particular item should be displayed only when a certain graph element (e.g. task node) is selected.
3. Sometimes it may be required that I want to alter the menu structure dynamically (using IMenuListener). IMHO if I want to add to the context menu the standard actions (e.g. Cut) the best way is to use the dynamic approach.
4. It shouldn't be a problem if default context menus already contain sensible elements (e.g. in the the source page "cut", "copy", ect.). But all items which don't make sense shouldn?t be visible by default (e.g. 'Run' in the source page).
In the worst case I could deal with the unwanted menu options using eclipse activities (I have checked it and it works). The only requirement is that all actions in the menu must be properly contributed (defninitionId must be specified).
What do you think about these issues?
Here is my suggestion about the implementation. It basically consists of additional classes and methods which would be added to the original designer code:
1. Declarative extension mechanism: each page in the designer editor (or at least 'diagram' and 'source') would create the context menu (may be empty) and register it for extensions. Each menu would use a different registration name (identifier) so that adding elements to these menus can be controlled.
package org.jbpm.ui.editor; public class DesignerEditor { ? private void initSourcePage() { int pageCount = getPageCount(); for (int i = 0; i < pageCount; i++) { if (getEditor(i) instanceof StructuredTextEditorXML) { sourcePage = (StructuredTextEditorXML)getEditor(i); processDefinition = getProcessDefinition(sourcePage); } } if (sourcePage != null) { initSourcePageContextMenu(sourcePage.getTextViewer().getTextWidget()); } } private void initSourcePageContextMenu(final Control control) { sourceContextMenuManager = new MenuManager("#PopupMenu"); sourceContextMenuManager.setRemoveAllWhenShown(true); sourceContextMenuManager.addMenuListener(new IMenuListener(){ public void menuAboutToShow(IMenuManager m) { fillContextMenu(m); } }); Menu menu = sourceContextMenuManager.createContextMenu(control); getSite().registerContextMenu("org.jbpm.ui.editor.DesignerEditor.SourcePopupMenu", sourceContextMenuManager, getSite().getSelectionProvider()); control.setMenu(menu); } private void fillContextMenu(final IMenuManager menuManager) { menuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } }
Similar code in org.jbpm.ui.editor.DesignerModelViewer and maybe in other pages (swimlane, design?).
2. Dynamic extension mechanism: menu listeners:
package org.jbpm.ui.editor; /** * Identifies individual page in the Jbpm editor window. */ public enum DesignerEditorPageId { DIAGRAM, SWIMLANE, DESIGN, SOURCE, } package org.jbpm.ui.editor; public class DesignerEditor { ? public final void addContextMenuListener(final DesignerEditorPageId pageId, final IMenuListener listener) { switch(pageId) { case DIAGRAM: graphPage.getDesignerModelViewer().addContextMenuListener(listener); break; case SWIMLANE: throw new UnsupportedOperationException("Not implemented"); case DESIGN: throw new UnsupportedOperationException("Not implemented"); case SOURCE: sourceContextMenuManager.addMenuListener(listener); break; default: throw new RuntimeException("Unknown page id: " + pageId); } } public final void removeContextMenuListener(final DesignerEditorPageId pageId, final IMenuListener listener) { switch(pageId) { case DIAGRAM: graphPage.getDesignerModelViewer().removeContextMenuListener(listener); break; case SWIMLANE: throw new UnsupportedOperationException("Not implemented"); case DESIGN: throw new UnsupportedOperationException("Not implemented"); case SOURCE: sourceContextMenuManager.removeMenuListener(listener); break; default: throw new RuntimeException("Unknown page id: " + pageId); } } /** * Additional utility method. */ public final IEditorPart getEditorPage(final DesignerEditorPageId pageId) { switch(pageId) { case DIAGRAM: return getEditor(0); case SWIMLANE: return getEditor(1); case DESIGN: return getEditor(2); case SOURCE: return getEditor(3); default: throw new RuntimeException("Unknown page id: " + pageId); } } }
What do you think of such changes? Is it possible to have some of the features described above implemented in standard workflow designer plugin?
Kind regards,
Krzysztof