diff --git a/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings b/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings index fae2680ff2e964a0a728896270c4b2ffa749b8b4..fec43d694d3e66c5bcfce8ae3f98d59eb5ce29e7 100644 --- a/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings +++ b/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings @@ -1,4 +1,3 @@ - ConstraintUIBases.java c59835240d43f3bdfb5b5acae69b2941f6b75214 GREEN ContextMenuSubMenuContributorBase.java 9539350f9f72b7cdd9537fea9cb3b42be719ef7d GREEN EObjectActionBase.java 67c307ac15d7f45ad970ab1df42f4729fee09518 GREEN 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 new file mode 100644 index 0000000000000000000000000000000000000000..420a3fd6ec2424b8df98a853535a39881b58648d --- /dev/null +++ b/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/ContextMenuSubMenuContributorBase.java @@ -0,0 +1,323 @@ +/*-------------------------------------------------------------------------+ +| Copyright 2012 fortiss GmbH | +| | +| Licensed under the Apache License, Version 2.0 (the "License"); | +| you may not use this file except in compliance with the License. | +| You may obtain a copy of the License at | +| | +| http://www.apache.org/licenses/LICENSE-2.0 | +| | +| Unless required by applicable law or agreed to in writing, software | +| distributed under the License is distributed on an "AS IS" BASIS, | +| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | +| See the License for the specific language governing permissions and | +| limitations under the License. | ++--------------------------------------------------------------------------*/ +package org.fortiss.tooling.kernel.ui.extension.base; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IContributionItem; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.resource.ImageDescriptor; +import org.fortiss.tooling.kernel.ui.extension.IContextMenuContributor; +import org.fortiss.tooling.kernel.ui.extension.base.EObjectActionBase.EObjectActionFactory; +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()}. + * <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. + * <p> + * A usage example can be found in org.fortiss.af3.time.ui plug-in. + * + * @param <T> + * Indicates the type of selection elements. + * + * @author trachtenherz + */ +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)) { + List<IContributionItem> contributionItems = new ArrayList<IContributionItem>(); + contributionItems.add(createMenu(createActionsForElement((T)selection))); + return contributionItems; + } + return Collections.emptyList(); + } + + /** 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()) { + list.add(factory.createAction(selection)); + } + return list; + } + + /** + * Creates a sub-menu {@link IMenuManager} containing specified actions. + */ + protected IMenuManager createMenu(Collection<Action> actions) { + 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 { + hintItems = new MenuStructureHint[actions.size()]; + Arrays.fill(hintItems, 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(hintItems[i] != null && + (hintItems[i].getHint() == MenuStructureHint.SUBMENU_START_BEFORE || hintItems[i] + .getHint() == MenuStructureHint.SUBMENU_START_BEFORE_SEPARATOR_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); + } + if(hintItems[i].getHint() == MenuStructureHint.SUBMENU_START_BEFORE_SEPARATOR_BEFORE) { + menu.add(new Separator()); + } + 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); + } + + 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; + } + return menu; + } + + /** {@inheritDoc} */ + @Override + public String getMenuSectionID() { + return IContextMenuService.BOTTOM_MOST_MENU_SECTION_ID; + } + + /** + * Returns {@code true} if the selected element is acceptable. This method + * must also ensure that {@code selection} is an instance of {@code T} + */ + protected abstract boolean acceptSelection(EObject selection, + ContextMenuContextProvider contextProvider); + + /** The name of the sub-menu */ + protected abstract String getMenuName(); + + /** 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. */ + 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#getMenuStructureHintItems() + */ + public static enum MenuStructureHint implements MenuStructureHintItem { + /** Insert a separator before current menu item. */ + SEPARATOR_BEFORE, + /** Insert a separator after current menu item. */ + 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, + /** + * Insert separator and begin sub-menu before current. + * + * @see #SEPARATOR_BEFORE + * @see #SUBMENU_START_BEFORE + */ + SUBMENU_START_BEFORE_SEPARATOR_BEFORE; + + /** {@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. + */ + 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; + + /** + * @see #getHint() + */ + private MenuStructureHint menuStructureHint; + + /** + * 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, false); + } + + /** + * Creates a {@link MenuStructureHintSubMenuStart} with given sub-menu label text. + * If {@code separatorBefore} is true than {@link #getHint()} will later return + * {@link MenuStructureHint#SUBMENU_START_BEFORE_SEPARATOR_BEFORE} otherwise + * {@link MenuStructureHint#SUBMENU_START_BEFORE}. + */ + public MenuStructureHintSubMenuStart(String subMenuLabel, boolean separatorBefore) { + this.subMenuLabel = subMenuLabel; + if(separatorBefore) { + menuStructureHint = MenuStructureHint.SUBMENU_START_BEFORE_SEPARATOR_BEFORE; + } else { + menuStructureHint = MenuStructureHint.SUBMENU_START_BEFORE; + } + } + + /** Returns the sub-menu label text. */ + public String getSubMenuLabel() { + return subMenuLabel; + } + + /** + * {@inheritDoc} + * + * @return {@link MenuStructureHint#SUBMENU_START_BEFORE} or + * {@link MenuStructureHint#SUBMENU_START_BEFORE_SEPARATOR_BEFORE}, depending on the + * initialization parameters used for the constructor (see + * {@link #MenuStructureHintSubMenuStart(String, boolean)}. + */ + @Override + public MenuStructureHint getHint() { + return this.menuStructureHint; + } + + /** {@inheritDoc} */ + @Override + public MenuStructureHint[] getHints() { + return new MenuStructureHint[] {getHint()}; + } + } + + /** + * 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 MenuStructureHintItem[] getMenuStructureHintItems() { + return null; + } + + /** The image for the sub-menu */ + protected ImageDescriptor getMenuIcon() { + return null; + } + + /** The Id for the sub-menu */ + protected String getMenuId() { + return null; + } +} diff --git a/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/EObjectActionBase.java b/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/EObjectActionBase.java index 0f784ea462b8a75b2f63829963847ac7ef1e5f67..4ef9f8be59e64d4838acc9e268d418ba5d94fa1a 100644 --- a/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/EObjectActionBase.java +++ b/org.fortiss.tooling.kernel.ui/trunk/src/org/fortiss/tooling/kernel/ui/extension/base/EObjectActionBase.java @@ -35,6 +35,12 @@ import org.fortiss.tooling.kernel.ui.service.ITutorialUIService; */ public abstract class EObjectActionBase<T extends EObject> extends Action { + /** Factory interface for action instances. */ + public static interface EObjectActionFactory<T extends EObject> { + /** Creates and returns an action instance for the given target. */ + EObjectActionBase<T> createAction(T target); + } + /** Stores the target elements. */ private EList<T> targets;