/******************************************************************************* * Copyright (c) 2010, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Marco Descher <marco@descher.at> - Bug 389063,398865,398866,403081,403083 * Bruce Skingle <Bruce.Skingle@immutify.com> - Bug 442570 * Lars Vogel <Lars.Vogel@vogella.com> - Bug 472654 *******************************************************************************/ package org.eclipse.e4.ui.workbench.renderers.swt; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.inject.Inject; import org.eclipse.e4.core.contexts.ContextInjectionFactory; import org.eclipse.e4.core.contexts.EclipseContextFactory; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.e4.core.services.contributions.IContributionFactory; import org.eclipse.e4.core.services.log.Logger; import org.eclipse.e4.ui.di.AboutToShow; import org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer; import org.eclipse.e4.ui.internal.workbench.swt.Policy; import org.eclipse.e4.ui.internal.workbench.swt.WorkbenchSWTActivator; import org.eclipse.e4.ui.model.application.ui.MContext; import org.eclipse.e4.ui.model.application.ui.menu.MDynamicMenuContribution; import org.eclipse.e4.ui.model.application.ui.menu.MMenu; import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement; import org.eclipse.e4.ui.model.application.ui.menu.MPopupMenu; import org.eclipse.e4.ui.workbench.modeling.EModelService; import org.eclipse.e4.ui.workbench.swt.factories.IRendererFactory; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IMenuListener2; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.swt.widgets.Menu; /** * <code>MenuManagerShowProcessor</code> provides hooks for renderer processing * before and after the <code>MenuManager</code> calls out to its * <code>IMenuManagerListener2</code> for the <code>menuAboutToShow</code> * events. */ public class MenuManagerShowProcessor implements IMenuListener2 { private static void trace(String msg, MenuManager menuManager, MMenu menuModel) { WorkbenchSWTActivator.trace(Policy.DEBUG_MENUS_FLAG, msg + ": " + menuManager + ": " + menuManager.getMenu() + ": " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + menuModel, null); } @Inject private EModelService modelService; @Inject private IRendererFactory rendererFactory; @Inject private MenuManagerRenderer renderer; @Inject private IContributionFactory contributionFactory; @Inject @Optional private Logger logger; @Override public void menuAboutToShow(IMenuManager manager) { if (!(manager instanceof MenuManager)) { return; } MenuManager menuManager = (MenuManager) manager; final MMenu menuModel = renderer.getMenuModel(menuManager); final Menu menu = menuManager.getMenu(); if (menuModel != null && menuManager != null) { cleanUp(menuModel, menuManager); } if (menuModel instanceof MPopupMenu) { showPopup(menu, (MPopupMenu) menuModel, menuManager); } AbstractPartRenderer obj = rendererFactory.getRenderer(menuModel, menu.getParent()); if (!(obj instanceof MenuManagerRenderer)) { if (Policy.DEBUG_MENUS) { trace("Not the correct renderer: " + obj, menuManager, menuModel); //$NON-NLS-1$ } return; } MenuManagerRenderer renderer = (MenuManagerRenderer) obj; if (menuModel.getWidget() == null) { renderer.bindWidget(menuModel, menuManager.getMenu()); } } @Override public void menuAboutToHide(IMenuManager manager) { if (!(manager instanceof MenuManager)) { return; } MenuManager menuManager = (MenuManager) manager; final MMenu menuModel = renderer.getMenuModel(menuManager); final Menu menu = menuManager.getMenu(); if (menuModel != null) { processDynamicElements(menuModel, menuManager); showMenu(menu, menuModel, menuManager); } } /** * HashMap key for storage of {@link MDynamicMenuContribution} elements used * in {@link #processDynamicElements(MMenu, MenuManager)} */ protected static final String DYNAMIC_ELEMENT_STORAGE_KEY = MenuManagerShowProcessor.class .getSimpleName() + ".dynamicElements"; //$NON-NLS-1$ /** * Process dynamic menu contributions provided by * {@link MDynamicMenuContribution} application model elements * * @param menuModel * @param menuManager * */ private void processDynamicElements(MMenu menuModel, MenuManager menuManager) { MMenuElement[] menuElements = menuModel.getChildren().toArray( new MMenuElement[menuModel.getChildren().size()]); for (MMenuElement currentMenuElement : menuElements) { if (currentMenuElement instanceof MDynamicMenuContribution) { MDynamicMenuContribution dmc = (MDynamicMenuContribution) currentMenuElement; Object contribution = dmc.getObject(); if (contribution == null) { IEclipseContext context = modelService.getContainingContext(menuModel); contribution = contributionFactory.create(dmc.getContributionURI(), context); dmc.setObject(contribution); } IEclipseContext dynamicMenuContext = EclipseContextFactory.create(); ArrayList<MMenuElement> mel = new ArrayList<>(); dynamicMenuContext.set(List.class, mel); dynamicMenuContext.set(MDynamicMenuContribution.class, dmc); IEclipseContext parentContext = modelService.getContainingContext(currentMenuElement); Object rc = ContextInjectionFactory.invoke(contribution, AboutToShow.class, parentContext, dynamicMenuContext, this); dynamicMenuContext.dispose(); if (rc == this) { if (logger != null) { logger.error("Missing @AboutToShow method in " + contribution); //$NON-NLS-1$ } continue; } if (mel.size() > 0) { int position = 0; while (position < menuModel.getChildren().size()) { if (currentMenuElement == menuModel.getChildren().get( position)) { position++; break; } position++; } // ensure that each element of the list has a valid element // id // and set the parent of the entries for (int j = 0; j < mel.size(); j++) { MMenuElement menuElement = mel.get(j); if (menuElement.getElementId() == null || menuElement.getElementId().length() < 1) { menuElement.setElementId(currentMenuElement .getElementId() + "." + j); //$NON-NLS-1$ } menuModel.getChildren().add(position++, menuElement); renderer.modelProcessSwitch(menuManager, menuElement); } currentMenuElement.getTransientData().put(DYNAMIC_ELEMENT_STORAGE_KEY, mel); } } } } /** * Remove all of the items created by any dynamic contributions on the * menuModel. In addition removes all of the items of menuModel in the case * all items of menuManager need removal when the menu is about to show. * This needs to be done or else menu items get added multiple times to * MenuModel which results in incorrect behavior and memory leak - bug * 486474 * * @param menuModel * @param menuManager */ private void cleanUp(MMenu menuModel, MenuManager menuManager) { if (Policy.DEBUG_MENUS) { trace("\nCleaning up the dynamic menu contributions", menuManager, menuModel); //$NON-NLS-1$ } renderer.removeDynamicMenuContributions(menuManager, menuModel); if (menuManager.getRemoveAllWhenShown()) { // remove the items from the model related to contributions defined // with location URIs if (Policy.DEBUG_MENUS) { trace("\nCleaning up all of the menu model items", menuManager, menuModel); //$NON-NLS-1$ } renderer.cleanUp(menuModel); // cleanup any leftovers - opaque items etc for (Iterator<MMenuElement> it = menuModel.getChildren().iterator(); it.hasNext();) { MMenuElement mMenuElement = it.next(); // remove item from the menu model it.remove(); // cleanup the renderer IContributionItem ici = renderer.getContribution(mMenuElement); if (ici == null && mMenuElement instanceof MMenu) { MMenu menuElement = (MMenu) mMenuElement; ici = renderer.getManager(menuElement); renderer.clearModelToManager(menuElement, (MenuManager) ici); } renderer.clearModelToContribution(mMenuElement, ici); } } } private void showPopup(final Menu menu, final MPopupMenu menuModel, MenuManager menuManager) { // System.err.println("showPopup: " + menuModel + "\n\t" + menu); // we need some context foolery here final IEclipseContext popupContext = menuModel.getContext(); final IEclipseContext parentContext = popupContext.getParent(); final IEclipseContext originalChild = parentContext.getActiveChild(); popupContext.activate(); popupContext.set(MenuManagerRendererFilter.TMP_ORIGINAL_CONTEXT, originalChild); } private void showMenu(final Menu menu, final MMenu menuModel, MenuManager menuManager) { final IEclipseContext evalContext; if (menuModel instanceof MContext) { evalContext = ((MContext) menuModel).getContext(); } else { evalContext = modelService.getContainingContext(menuModel); } MenuManagerRendererFilter.updateElementVisibility(menuModel, renderer, menuManager, evalContext, 2, true); } }