diff --git a/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/ContextMenuSubMenuContributorBase.java b/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/ContextMenuSubMenuContributorBase.java index de715e091138e8427cf29e2b15ae8ddb411d7cd2..418b877ff540acb08e5ad504c693378f93a0918c 100644 --- a/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/ContextMenuSubMenuContributorBase.java +++ b/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/ContextMenuSubMenuContributorBase.java @@ -36,13 +36,15 @@ import org.fortiss.tooling.kernel.ui.extension.data.ContextMenuContextProvider; import org.fortiss.tooling.kernel.ui.service.IContextMenuService; /** - * Base class for context menu contributors creating a sub-menu - * {@link IMenuManager} with actions provided by actions factories - * {@link EObjectActionFactory} returned by {@link #getActionFactories()}. - * - * The class implements interface methods {@link #getMenuIcon()} and - * {@link #getMenuId()} returning {@code null} by default. Classes providing an - * own icon or id should override them. + * Base class for context menu contributors creating a sub-menu {@link IMenuManager} with actions + * provided by actions factories {@link EObjectActionFactory} returned by + * {@link #getActionFactories()}. + * <p> + * The menu can be additionally structured using {@link #getMenuStructureHintItems()}: currently + * menu separators and one-level sub-menus supported. + * <p> + * The class implements interface methods {@link #getMenuIcon()} and {@link #getMenuId()} returning + * {@code null} by default. Classes providing an own icon or id should override them. * * @param <T> * Indicates the type of selection elements. @@ -50,20 +52,19 @@ import org.fortiss.tooling.kernel.ui.service.IContextMenuService; * @author trachtenherz * @author $Author: hoelzl $ * @version $Rev: 18709 $ - * @ConQAT.Rating GREEN Hash: 8573587F690FC94EF1300F9200C6793A + * @ConQAT.Rating YELLOW Hash: 38206E76C5D298208F2901B878324FF5 */ -public abstract class ContextMenuSubMenuContributorBase<T extends EObject> - implements IContextMenuContributor { +public abstract class ContextMenuSubMenuContributorBase<T extends EObject> implements + IContextMenuContributor { /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public final List<IContributionItem> getContributedItems(EObject selection, ContextMenuContextProvider contextProvider) { - if (acceptSelection(selection, contextProvider)) { + if(acceptSelection(selection, contextProvider)) { List<IContributionItem> contributionItems = new ArrayList<IContributionItem>(); - contributionItems - .add(createMenu(createActionsForElement((T) selection))); + contributionItems.add(createMenu(createActionsForElement((T)selection))); return contributionItems; } return Collections.emptyList(); @@ -72,7 +73,7 @@ public abstract class ContextMenuSubMenuContributorBase<T extends EObject> /** Creates action instances for the given selection. */ protected Collection<Action> createActionsForElement(T selection) { ArrayList<Action> list = new ArrayList<Action>(); - for (EObjectActionFactory<? super T> factory : getActionFactories()) { + for(EObjectActionFactory<? super T> factory : getActionFactories()) { list.add(factory.createAction(selection)); } return list; @@ -82,30 +83,61 @@ public abstract class ContextMenuSubMenuContributorBase<T extends EObject> * Creates a sub-menu {@link IMenuManager} containing specified actions. */ protected IMenuManager createMenu(Collection<Action> actions) { - IMenuManager menu = new MenuManager(getMenuName(), getMenuIcon(), - getMenuId()); - - // Prepare menu structure hints - MenuStructureHints[] hints; - if (getMenuStructureHints() != null) { - hints = Arrays.copyOf(getMenuStructureHints(), actions.size()); + IMenuManager menu = new MenuManager(getMenuName(), getMenuIcon(), getMenuId()); + IMenuManager subMenu = null; + // Points to the currently used menu, which is either {@code menu} or {@code subMenu} + IMenuManager currentMenu = menu; + // Prepare menu structure hint items + MenuStructureHintItem[] hintItems; + if(getMenuStructureHintItems() != null) { + hintItems = Arrays.copyOf(getMenuStructureHintItems(), actions.size()); } else { - hints = new MenuStructureHints[actions.size()]; - Arrays.fill(hints, null); + hintItems = new MenuStructureHint[actions.size()]; + Arrays.fill(hintItems, null); } - // Now the hints array contains all elements from - // getMenuStructureHints(). It - // also has at least as many elements as the actions collection. If - // getMenuStructureHints() has less elements (or is null), - // than all other elements of the hints array are null. + /* + * Now the hint items array contains all elements from getMenuStructureHintItems(). It also + * has at least as many elements as the actions collection. If getMenuStructureHintItems() + * has less elements (or is null), than all other elements of the hint items array are null. + */ int i = 0; - for (Action a : actions) { - if (hints[i] == MenuStructureHints.SEPARATOR_BEFORE) { - menu.add(new Separator()); + for(Action a : actions) { + if(hintItems[i] != null && + hintItems[i].getHint() == MenuStructureHint.SUBMENU_START_BEFORE) { + if(subMenu != null) { + // If there is a previously opened sub-menu then finish processing it because we + // use one-level sub-menus. + menu.add(subMenu); + } + final String subMenuLabel; + if(hintItems[i] instanceof MenuStructureHintSubMenuStart) { + subMenuLabel = ((MenuStructureHintSubMenuStart)hintItems[i]).getSubMenuLabel(); + } else { + subMenuLabel = MenuStructureHintSubMenuStart.DEFAULT_SUB_MENU_LABEL; + } + // Open new sub-menu + currentMenu = subMenu = new MenuManager(subMenuLabel); } - menu.add(a); - if (hints[i] == MenuStructureHints.SEPARATOR_AFTER) { - menu.add(new Separator()); + + if(hintItems[i] != null && hintItems[i].getHint() == MenuStructureHint.SEPARATOR_BEFORE) { + currentMenu.add(new Separator()); + } + + // Add the action to menu + currentMenu.add(a); + + if(hintItems[i] != null && + hintItems[i].getHint() == MenuStructureHint.SUBMENU_END_AFTER) { + if(subMenu != null) { + menu.add(subMenu); + subMenu = null; + // Return to top-level menu + currentMenu = menu; + } + } + + if(hintItems[i] != null && hintItems[i].getHint() == MenuStructureHint.SEPARATOR_AFTER) { + currentMenu.add(new Separator()); } ++i; @@ -132,28 +164,129 @@ public abstract class ContextMenuSubMenuContributorBase<T extends EObject> /** Returns the action factories for the actions to be shown in the menu */ protected abstract Collection<EObjectActionFactory<? super T>> getActionFactories(); + /** + * Interface for menu structure hints. + * + * @author trachtenherz + * @version $Rev: 18709 $ + * @ConQAT.Rating RED Hash: + */ + public static interface MenuStructureHintItem { + /** Returns the hint represented by this item. */ + MenuStructureHint getHint(); + + /** + * Returns hints represented by this item. If it represents one hint, than a one-element + * array is returned containing the result of {@link #getHint()}. + */ + MenuStructureHint[] getHints(); + } + /** * Constants for menu structure hints. * - * @see ContextMenuSubMenuContributorBase#getMenuStructureHints() + * @see ContextMenuSubMenuContributorBase#getMenuStructureHintItems() */ - protected static enum MenuStructureHints { + public static enum MenuStructureHint implements MenuStructureHintItem { /** Insert a separator before current menu item. */ SEPARATOR_BEFORE, /** Insert a separator after current menu item. */ - SEPARATOR_AFTER; - // Further future hints may be e.g.: Begin new sub-menu, End new - // sub-menu + SEPARATOR_AFTER, + /** + * Begin sub-menu before current item. + * <p> + * NOTE: Only one-level sub-menus are supported, so if there was a sub-menu previously + * started, then starting another will automatically end the previous one. + */ + SUBMENU_START_BEFORE, + /** End sub-menu after current item. */ + SUBMENU_END_AFTER; + + /** {@inheritDoc} */ + @Override + public MenuStructureHint getHint() { + return this; + } + + /** {@inheritDoc} */ + @Override + public MenuStructureHint[] getHints() { + return new MenuStructureHint[] {getHint()}; + } + } + + /** + * {@link MenuStructureHintItem} wrapping a {@link MenuStructureHint#SUBMENU_START_BEFORE} hint + * and providing additional information for the sub-menu. + * + * @author trachtenherz + * @version $Rev: 18709 $ + * @ConQAT.Rating RED Hash: + */ + public static class MenuStructureHintSubMenuStart implements MenuStructureHintItem { + /** Default sub-menu item label. */ + public static final String DEFAULT_SUB_MENU_LABEL = "Sub menu"; + /** @see #getSubMenuLabel() */ + private String subMenuLabel; + + /** + * Creates a {@link MenuStructureHintSubMenuStart} with default sub-menu label text. + * + * @see #DEFAULT_SUB_MENU_LABEL + */ + public MenuStructureHintSubMenuStart() { + this(DEFAULT_SUB_MENU_LABEL); + } + + /** + * Creates a {@link MenuStructureHintSubMenuStart} with given sub-menu label text. + */ + public MenuStructureHintSubMenuStart(String subMenuLabel) { + this.subMenuLabel = subMenuLabel; + } + + /** Returns the sub-menu label text. */ + public String getSubMenuLabel() { + return subMenuLabel; + } + + /** + * {@inheritDoc} + * + * @return MenuStructureHint.SUBMENU_START_BEFORE + */ + @Override + public MenuStructureHint getHint() { + return MenuStructureHint.SUBMENU_START_BEFORE; + } + + /** {@inheritDoc} */ + @Override + public MenuStructureHint[] getHints() { + return new MenuStructureHint[] {getHint()}; + } } /** - * Returns additional informations about the structure of the menu. If - * {@code null}, the actions are linearly shown in a menu. + * Returns additional informations about the structure of the menu to be used in + * {@link #createMenu(Collection)}. Each array element at position {@code i} corresponds to the + * menu action at position @{@code i} in {@link #getActionFactories()}. + * <p> + * A {@code null} element means that there is no additional information for the corresponding + * action. If the array itself is {@code null} then this is equivalent to an array filled with + * {@code null} elements: in this case all actions are linearly shown in the menu. + * <p> + * If the array is smaller than the number of menu actions returned by + * {@link #getActionFactories()} then missing structure hints are assumed {@code null} (which is + * equivalent to padding the array with {@code null} till the length of + * {@link #getActionFactories()}). * + * @see MenuStructureHintItem + * @see MenuStructureHint * @see #getActionFactories() * @see #createMenu(Collection) */ - protected MenuStructureHints[] getMenuStructureHints() { + protected MenuStructureHintItem[] getMenuStructureHintItems() { return null; } @@ -167,4 +300,4 @@ public abstract class ContextMenuSubMenuContributorBase<T extends EObject> return null; } -} \ No newline at end of file +}