blob: 702fa021078daa55dc5cd8b56a9a2a68ec6066eb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2012 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 <descher@medevit.at> - Bug 389063 Dynamic Menu Contribution
*******************************************************************************/
package org.eclipse.e4.ui.workbench.renderers.swt;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.IContextFunction;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.contexts.RunAndTrack;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.core.services.log.Logger;
import org.eclipse.e4.ui.internal.workbench.ContributionsAnalyzer;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.ui.MCoreExpression;
import org.eclipse.e4.ui.model.application.ui.MElementContainer;
import org.eclipse.e4.ui.model.application.ui.MUIElement;
import org.eclipse.e4.ui.model.application.ui.MUILabel;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem;
import org.eclipse.e4.ui.model.application.ui.menu.MDynamicMenuContribution;
import org.eclipse.e4.ui.model.application.ui.menu.MHandledMenuItem;
import org.eclipse.e4.ui.model.application.ui.menu.MMenu;
import org.eclipse.e4.ui.model.application.ui.menu.MMenuContribution;
import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement;
import org.eclipse.e4.ui.model.application.ui.menu.MMenuItem;
import org.eclipse.e4.ui.model.application.ui.menu.MMenuSeparator;
import org.eclipse.e4.ui.model.application.ui.menu.MOpaqueMenu;
import org.eclipse.e4.ui.model.application.ui.menu.MOpaqueMenuItem;
import org.eclipse.e4.ui.model.application.ui.menu.MOpaqueMenuSeparator;
import org.eclipse.e4.ui.model.application.ui.menu.MPopupMenu;
import org.eclipse.e4.ui.model.application.ui.menu.MRenderedMenuItem;
import org.eclipse.e4.ui.model.application.ui.menu.impl.MenuFactoryImpl;
import org.eclipse.e4.ui.workbench.IResourceUtilities;
import org.eclipse.e4.ui.workbench.UIEvents;
import org.eclipse.e4.ui.workbench.UIEvents.ElementContainer;
import org.eclipse.e4.ui.workbench.modeling.ExpressionContext;
import org.eclipse.e4.ui.workbench.swt.util.ISWTResourceUtilities;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.action.AbstractGroupMarker;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.internal.MenuManagerEventHelper;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Decorations;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
/**
* Create a contribute part.
*/
public class MenuManagerRenderer extends SWTPartRenderer {
private static final String NO_LABEL = "UnLabled"; //$NON-NLS-1$
public static final String GROUP_MARKER = "org.eclipse.jface.action.GroupMarker.GroupMarker(String)"; //$NON-NLS-1$
private Map<MMenu, MenuManager> modelToManager = new HashMap<MMenu, MenuManager>();
private Map<MenuManager, MMenu> managerToModel = new HashMap<MenuManager, MMenu>();
private Map<MMenuElement, IContributionItem> modelToContribution = new HashMap<MMenuElement, IContributionItem>();
private Map<IContributionItem, MMenuElement> contributionToModel = new HashMap<IContributionItem, MMenuElement>();
private Map<MMenuElement, ContributionRecord> modelContributionToRecord = new HashMap<MMenuElement, ContributionRecord>();
private Map<MMenuElement, ArrayList<ContributionRecord>> sharedElementToRecord = new HashMap<MMenuElement, ArrayList<ContributionRecord>>();
@Inject
private Logger logger;
@Inject
private MApplication application;
@Inject
IEventBroker eventBroker;
private EventHandler itemUpdater = new EventHandler() {
public void handleEvent(Event event) {
// Ensure that this event is for a MMenuItem
if (!(event.getProperty(UIEvents.EventTags.ELEMENT) instanceof MMenuItem))
return;
MMenuItem itemModel = (MMenuItem) event
.getProperty(UIEvents.EventTags.ELEMENT);
IContributionItem ici = getContribution(itemModel);
if (ici == null) {
return;
}
String attName = (String) event
.getProperty(UIEvents.EventTags.ATTNAME);
if (UIEvents.UILabel.LABEL.equals(attName)) {
ici.update();
} else if (UIEvents.UILabel.ICONURI.equals(attName)) {
ici.update();
}
}
};
private EventHandler toBeRenderedUpdater = new EventHandler() {
public void handleEvent(Event event) {
Object element = event.getProperty(UIEvents.EventTags.ELEMENT);
String attName = (String) event
.getProperty(UIEvents.EventTags.ATTNAME);
if (element instanceof MMenuItem) {
MMenuItem itemModel = (MMenuItem) element;
if (UIEvents.UIElement.TOBERENDERED.equals(attName)) {
Object obj = itemModel.getParent();
if (!(obj instanceof MMenu)) {
return;
}
MenuManager parent = getManager((MMenu) obj);
if (itemModel.isToBeRendered()) {
if (parent != null) {
modelProcessSwitch(parent, itemModel);
}
} else {
IContributionItem ici = getContribution(itemModel);
clearModelToContribution(itemModel, ici);
if (ici != null && parent != null) {
parent.remove(ici);
}
if (ici != null) {
ici.dispose();
}
}
}
}
if (UIEvents.UIElement.VISIBLE.equals(attName)) {
if (element instanceof MMenu) {
MMenu menuModel = (MMenu) element;
MenuManager manager = getManager(menuModel);
if (manager == null) {
return;
}
manager.setVisible(menuModel.isVisible());
if (manager.getParent() != null) {
manager.getParent().markDirty();
}
} else if (element instanceof MMenuElement) {
MMenuElement itemModel = (MMenuElement) element;
Object obj = getContribution(itemModel);
if (!(obj instanceof ContributionItem)) {
return;
}
ContributionItem item = (ContributionItem) obj;
item.setVisible(itemModel.isVisible());
if (item.getParent() != null) {
item.getParent().markDirty();
}
}
}
}
};
private EventHandler selectionUpdater = new EventHandler() {
public void handleEvent(Event event) {
// Ensure that this event is for a MToolItem
if (!(event.getProperty(UIEvents.EventTags.ELEMENT) instanceof MMenuItem))
return;
MMenuItem itemModel = (MMenuItem) event
.getProperty(UIEvents.EventTags.ELEMENT);
IContributionItem ici = getContribution(itemModel);
if (ici != null) {
ici.update();
}
}
};
private EventHandler enabledUpdater = new EventHandler() {
public void handleEvent(Event event) {
// Ensure that this event is for a MMenuItem
if (!(event.getProperty(UIEvents.EventTags.ELEMENT) instanceof MMenuItem))
return;
MMenuItem itemModel = (MMenuItem) event
.getProperty(UIEvents.EventTags.ELEMENT);
IContributionItem ici = getContribution(itemModel);
if (ici != null) {
ici.update();
}
}
};
private EventHandler childUpdater = new EventHandler() {
public void handleEvent(Event event) {
// Ensure that this event is for a MMenuItem
if (!(event.getProperty(UIEvents.EventTags.ELEMENT) instanceof MMenu))
return;
Object menuModel = event.getProperty(UIEvents.EventTags.ELEMENT);
if (UIEvents.isADD(event)) {
processContents((MElementContainer<MUIElement>) menuModel);
} else if (UIEvents.isREMOVE(event)) {
MenuManager parentManager = getManager((MMenu) menuModel);
if (parentManager == null) {
return;
}
Object oldValue = event
.getProperty(UIEvents.EventTags.OLD_VALUE);
if (oldValue instanceof MMenu) {
disposeMenuManager(getManager((MMenu) oldValue),
parentManager, (MMenu) oldValue);
} else if (oldValue instanceof MMenuElement) {
disposeContributionItem(
getContribution((MMenuElement) oldValue),
parentManager, (MMenuElement) oldValue);
parentManager.update(false);
} else if (oldValue instanceof List) {
for (Object object : (List) oldValue) {
if (object instanceof MMenu) {
disposeMenuManager(getManager((MMenu) object),
parentManager, (MMenu) object);
} else if (object instanceof MMenuElement) {
disposeContributionItem(
getContribution((MMenuElement) object),
parentManager, (MMenuElement) object);
}
}
parentManager.update(false);
}
}
}
};
private MenuManagerRendererFilter rendererFilter;
@PostConstruct
public void init() {
eventBroker.subscribe(UIEvents.UILabel.TOPIC_ALL, itemUpdater);
eventBroker.subscribe(UIEvents.Item.TOPIC_SELECTED, selectionUpdater);
eventBroker.subscribe(UIEvents.Item.TOPIC_ENABLED, enabledUpdater);
eventBroker
.subscribe(UIEvents.UIElement.TOPIC_ALL, toBeRenderedUpdater);
eventBroker.subscribe(ElementContainer.TOPIC_CHILDREN, childUpdater);
context.set(MenuManagerRenderer.class, this);
Display display = context.get(Display.class);
rendererFilter = ContextInjectionFactory.make(
MenuManagerRendererFilter.class, context);
display.addFilter(SWT.Show, rendererFilter);
display.addFilter(SWT.Hide, rendererFilter);
display.addFilter(SWT.Dispose, rendererFilter);
context.set(MenuManagerRendererFilter.class, rendererFilter);
MenuManagerEventHelper.showHelper = ContextInjectionFactory.make(
MenuManagerShowProcessor.class, context);
MenuManagerEventHelper.hideHelper = ContextInjectionFactory.make(
MenuManagerHideProcessor.class, context);
}
@PreDestroy
public void contextDisposed() {
eventBroker.unsubscribe(itemUpdater);
eventBroker.unsubscribe(selectionUpdater);
eventBroker.unsubscribe(enabledUpdater);
eventBroker.unsubscribe(toBeRenderedUpdater);
eventBroker.unsubscribe(childUpdater);
ContextInjectionFactory.uninject(MenuManagerEventHelper.showHelper,
context);
MenuManagerEventHelper.showHelper = null;
ContextInjectionFactory.uninject(MenuManagerEventHelper.hideHelper,
context);
MenuManagerEventHelper.hideHelper = null;
context.remove(MenuManagerRendererFilter.class);
Display display = context.get(Display.class);
if (display != null && !display.isDisposed() && rendererFilter != null) {
display.removeFilter(SWT.Show, rendererFilter);
display.removeFilter(SWT.Hide, rendererFilter);
display.removeFilter(SWT.Dispose, rendererFilter);
}
if (rendererFilter != null) {
ContextInjectionFactory.uninject(rendererFilter, context);
rendererFilter = null;
}
context.remove(MenuManagerRenderer.class);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer#createWidget
* (org.eclipse.e4.ui.model.application.ui.MUIElement, java.lang.Object)
*/
@Override
public Object createWidget(MUIElement element, Object parent) {
if (!(element instanceof MMenu))
return null;
final MMenu menuModel = (MMenu) element;
Menu newMenu = null;
MenuManager menuManager = null;
boolean menuBar = false;
if (parent instanceof Decorations) {
MUIElement container = (MUIElement) ((EObject) element)
.eContainer();
if (container instanceof MWindow) {
menuManager = getManager(menuModel);
if (menuManager == null) {
menuManager = new MenuManager(NO_LABEL,
menuModel.getElementId());
linkModelToManager(menuModel, menuManager);
}
newMenu = menuManager.createMenuBar((Decorations) parent);
((Decorations) parent).setMenuBar(newMenu);
newMenu.setData(menuManager);
menuBar = true;
} else {
menuManager = getManager(menuModel);
if (menuManager == null) {
menuManager = new MenuManager(NO_LABEL,
menuModel.getElementId());
linkModelToManager(menuModel, menuManager);
}
newMenu = menuManager.createContextMenu((Control) parent);
// we can't be sure this is the correct parent.
// ((Control) parent).setMenu(newMenu);
newMenu.setData(menuManager);
}
} else if (parent instanceof Menu) {
// Object data = ((Menu) parent).getData();
logger.debug(new Exception(), "Trying to render a sub menu " //$NON-NLS-1$
+ menuModel + "\n\t" + parent); //$NON-NLS-1$
return null;
} else if (parent instanceof Control) {
menuManager = getManager(menuModel);
if (menuManager == null) {
menuManager = new MenuManager(NO_LABEL,
menuModel.getElementId());
linkModelToManager(menuModel, menuManager);
}
newMenu = menuManager.createContextMenu((Control) parent);
// we can't be sure this is the correct parent.
// ((Control) parent).setMenu(newMenu);
newMenu.setData(menuManager);
}
if (!menuManager.getRemoveAllWhenShown()) {
processContributions(menuModel, menuBar,
menuModel instanceof MPopupMenu);
}
if (newMenu != null) {
newMenu.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
cleanUp(menuModel);
}
});
}
return newMenu;
}
/**
* @param menuModel
*/
public void cleanUp(MMenu menuModel) {
Collection<ContributionRecord> vals = modelContributionToRecord
.values();
List<ContributionRecord> disposedRecords = new ArrayList<ContributionRecord>();
for (ContributionRecord record : vals
.toArray(new ContributionRecord[vals.size()])) {
if (record.menuModel == menuModel) {
record.dispose();
for (MMenuElement copy : record.generatedElements) {
cleanUpCopy(record, copy);
}
for (MMenuElement copy : record.sharedElements) {
cleanUpCopy(record, copy);
}
record.generatedElements.clear();
record.sharedElements.clear();
disposedRecords.add(record);
}
}
// The cleanup() is called recursively via cleanUpCopy(), hence
// the need to do a separate pass to remove disposed records:
Iterator<Entry<MMenuElement, ContributionRecord>> iterator = modelContributionToRecord
.entrySet().iterator();
for (; iterator.hasNext();) {
Entry<MMenuElement, ContributionRecord> entry = iterator.next();
ContributionRecord record = entry.getValue();
if (disposedRecords.contains(record))
iterator.remove();
}
}
public void cleanUpCopy(ContributionRecord record, MMenuElement copy) {
modelContributionToRecord.remove(copy);
if (copy instanceof MMenu) {
MMenu menuCopy = (MMenu) copy;
cleanUp(menuCopy);
MenuManager copyManager = getManager(menuCopy);
clearModelToManager(menuCopy, copyManager);
if (copyManager != null) {
record.getManagerForModel().remove(copyManager);
copyManager.dispose();
}
} else {
IContributionItem ici = getContribution(copy);
clearModelToContribution(copy, ici);
if (ici != null) {
record.getManagerForModel().remove(ici);
}
}
}
/**
* @param menuModel
* @param isMenuBar
* @param isPopup
*/
public void processContributions(MMenu menuModel, boolean isMenuBar,
boolean isPopup) {
if (menuModel.getElementId() == null) {
return;
}
final ArrayList<MMenuContribution> toContribute = new ArrayList<MMenuContribution>();
ContributionsAnalyzer.XXXgatherMenuContributions(menuModel,
application.getMenuContributions(), menuModel.getElementId(),
toContribute, null, isPopup);
generateContributions(menuModel, toContribute, isMenuBar);
for (MMenuElement element : menuModel.getChildren()) {
if (element instanceof MMenu) {
processContributions((MMenu) element, false, isPopup);
}
}
}
/**
* @param menuModel
* @param toContribute
*/
private void generateContributions(MMenu menuModel,
ArrayList<MMenuContribution> toContribute, boolean menuBar) {
HashSet<String> existingMenuIds = new HashSet<String>();
HashSet<String> existingSeparatorNames = new HashSet<String>();
for (MMenuElement child : menuModel.getChildren()) {
String elementId = child.getElementId();
if (child instanceof MMenu && elementId != null) {
existingMenuIds.add(elementId);
} else if (child instanceof MMenuSeparator && elementId != null) {
existingSeparatorNames.add(elementId);
}
}
MenuManager manager = getManager(menuModel);
boolean done = toContribute.size() == 0;
while (!done) {
ArrayList<MMenuContribution> curList = new ArrayList<MMenuContribution>(
toContribute);
int retryCount = toContribute.size();
toContribute.clear();
for (MMenuContribution menuContribution : curList) {
if (!processAddition(menuModel, manager, menuContribution,
existingMenuIds, existingSeparatorNames, menuBar)) {
toContribute.add(menuContribution);
}
}
// We're done if the retryList is now empty (everything done) or
// if the list hasn't changed at all (no hope)
done = (toContribute.size() == 0)
|| (toContribute.size() == retryCount);
}
}
/**
* @param menuModel
* @param manager
* @param menuContribution
* @return true if the menuContribution was processed
*/
private boolean processAddition(MMenu menuModel, final MenuManager manager,
MMenuContribution menuContribution,
final HashSet<String> existingMenuIds,
HashSet<String> existingSeparatorNames, boolean menuBar) {
final ContributionRecord record = new ContributionRecord(menuModel,
menuContribution, this);
if (!record.mergeIntoModel()) {
return false;
}
if (menuBar || isPartMenu(menuModel)) {
final IEclipseContext parentContext = modelService
.getContainingContext(menuModel);
parentContext.runAndTrack(new RunAndTrack() {
@Override
public boolean changed(IEclipseContext context) {
record.updateVisibility(parentContext.getActiveLeaf());
manager.update(false);
return true;
}
});
}
return true;
}
private boolean isPartMenu(MMenu menuModel) {
// don't want popup menus as their visibility does not need to be
// tracked by a separate RunAndTrack
return !(menuModel instanceof MPopupMenu)
&& ((EObject) menuModel).eContainer() instanceof MPart;
}
public ArrayList<ContributionRecord> getList(MMenuElement item) {
ArrayList<ContributionRecord> tmp = sharedElementToRecord.get(item);
if (tmp == null) {
tmp = new ArrayList<ContributionRecord>();
sharedElementToRecord.put(item, tmp);
}
return tmp;
}
void removeMenuContributions(final MMenu menuModel,
final ArrayList<MMenuElement> menuContributionsToRemove) {
for (MMenuElement item : menuContributionsToRemove) {
menuModel.getChildren().remove(item);
}
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer#processContents
* (org.eclipse.e4.ui.model.application.ui.MElementContainer)
*/
@Override
public void processContents(MElementContainer<MUIElement> container) {
// I can either simply stop processing, or we can walk the model
// ourselves like the "old" days
// EMF gives us null lists if empty
if (container == null)
return;
// this is in direct violation of good programming
MenuManager parentManager = getManager((MMenu) ((Object) container));
if (parentManager == null) {
return;
}
// Process any contents of the newly created ME
List<MUIElement> parts = container.getChildren();
if (parts != null) {
MUIElement[] plist = parts.toArray(new MUIElement[parts.size()]);
for (int i = 0; i < plist.length; i++) {
MUIElement childME = plist[i];
modelProcessSwitch(parentManager, (MMenuElement) childME);
}
}
parentManager.update(false);
}
// Disposes the menuManager and its children
private void disposeMenuManager(MenuManager menuManager,
MenuManager parentManager, MMenu menuModel) {
// Cleanup all contributions in menuManager since the parent is being
// disposed.
for (IContributionItem contributionItem : menuManager.getItems()) {
if (contributionItem instanceof MenuManager) {
disposeMenuManager((MenuManager) contributionItem, menuManager,
getMenuModel((MenuManager) contributionItem));
} else {
disposeContributionItem(contributionItem, parentManager,
getMenuElement(contributionItem));
}
}
clearModelToManager(menuModel, menuManager);
parentManager.remove(menuManager);
menuManager.dispose();
}
private void disposeContributionItem(IContributionItem contributionItem,
MenuManager parentManager, MMenuElement menuElement) {
clearModelToContribution(menuElement, contributionItem);
parentManager.remove(contributionItem);
contributionItem.dispose();
}
private void addToManager(MenuManager parentManager, MMenuElement model,
IContributionItem menuManager) {
MElementContainer<MUIElement> parent = model.getParent();
// technically this shouldn't happen
if (parent == null) {
parentManager.add(menuManager);
} else {
int index = parent.getChildren().indexOf(model);
// shouldn't be -1, but better safe than sorry
if (index > parentManager.getSize() || index == -1) {
parentManager.add(menuManager);
} else {
parentManager.insert(index, menuManager);
}
}
}
/**
* @param parentManager
* @param menuModel
*/
private void processMenu(MenuManager parentManager, MMenu menuModel) {
MenuManager menuManager = getManager(menuModel);
if (menuManager == null) {
String menuText = getText(menuModel);
ImageDescriptor desc = getImageDescriptor(menuModel);
menuManager = new MenuManager(menuText, desc,
menuModel.getElementId());
linkModelToManager(menuModel, menuManager);
menuManager.setVisible(menuModel.isVisible());
addToManager(parentManager, menuModel, menuManager);
}
// processContributions(menuModel, false);
List<MMenuElement> parts = menuModel.getChildren();
if (parts != null) {
MMenuElement[] plist = parts
.toArray(new MMenuElement[parts.size()]);
for (int i = 0; i < plist.length; i++) {
MMenuElement childME = plist[i];
modelProcessSwitch(menuManager, childME);
}
}
}
/**
* @param menuManager
* @param childME
*/
void modelProcessSwitch(MenuManager menuManager, MMenuElement childME) {
if (!childME.isToBeRendered()) {
return;
}
if (childME instanceof MRenderedMenuItem) {
MRenderedMenuItem itemModel = (MRenderedMenuItem) childME;
processRenderedItem(menuManager, itemModel);
} else if (childME instanceof MOpaqueMenuItem) {
MOpaqueMenuItem itemModel = (MOpaqueMenuItem) childME;
processOpaqueItem(menuManager, itemModel);
} else if (childME instanceof MHandledMenuItem) {
MHandledMenuItem itemModel = (MHandledMenuItem) childME;
processHandledItem(menuManager, itemModel);
} else if (childME instanceof MDirectMenuItem) {
MDirectMenuItem itemModel = (MDirectMenuItem) childME;
processDirectItem(menuManager, itemModel, null);
} else if (childME instanceof MMenuSeparator) {
MMenuSeparator sep = (MMenuSeparator) childME;
processSeparator(menuManager, sep);
// } else if (childME instanceof MOpaqueMenu) {
// I'm not sure what to do here
// so I'll just take it out of the running
} else if (childME instanceof MMenu) {
MMenu itemModel = (MMenu) childME;
processMenu(menuManager, itemModel);
}
}
/**
* @param parentManager
* @param itemModel
*/
void processRenderedItem(MenuManager parentManager,
MRenderedMenuItem itemModel) {
IContributionItem ici = getContribution(itemModel);
if (ici != null) {
return;
}
Object obj = itemModel.getContributionItem();
if (obj instanceof IContextFunction) {
final IEclipseContext lclContext = getContext(itemModel);
ici = (IContributionItem) ((IContextFunction) obj)
.compute(lclContext);
itemModel.setContributionItem(ici);
} else if (obj instanceof IContributionItem) {
ici = (IContributionItem) obj;
} else {
// TODO potentially log the state, we've got something we're not
// happy with
return;
}
ici.setVisible(itemModel.isVisible());
addToManager(parentManager, itemModel, ici);
linkModelToContribution(itemModel, ici);
}
void processOpaqueItem(MenuManager parentManager, MOpaqueMenuItem itemModel) {
IContributionItem ici = getContribution(itemModel);
if (ici != null) {
return;
}
Object obj = itemModel.getOpaqueItem();
if (obj instanceof IContributionItem) {
ici = (IContributionItem) obj;
} else {
return;
}
ici.setVisible(itemModel.isVisible());
addToManager(parentManager, itemModel, ici);
linkModelToContribution(itemModel, ici);
}
/**
* @param menuManager
* @param itemModel
*/
private void processSeparator(MenuManager menuManager,
MMenuSeparator itemModel) {
IContributionItem ici = getContribution(itemModel);
if (ici != null) {
return;
}
AbstractGroupMarker marker = null;
if (itemModel.getTags().contains(GROUP_MARKER)
|| !itemModel.isVisible()) {
if (itemModel.getElementId() != null) {
marker = new GroupMarker(itemModel.getElementId());
}
} else {
marker = new Separator();
marker.setId(itemModel.getElementId());
}
if (marker == null) {
return;
}
addToManager(menuManager, itemModel, marker);
linkModelToContribution(itemModel, marker);
}
/**
* @param parentManager
* @param itemModel
* @param id
*/
void processDirectItem(MenuManager parentManager,
MDirectMenuItem itemModel, String id) {
IContributionItem ici = getContribution(itemModel);
if (ici != null) {
return;
}
final IEclipseContext lclContext = getContext(itemModel);
DirectContributionItem ci = ContextInjectionFactory.make(
DirectContributionItem.class, lclContext);
ci.setModel(itemModel);
ci.setVisible(itemModel.isVisible());
addToManager(parentManager, itemModel, ci);
linkModelToContribution(itemModel, ci);
}
/**
* @param parentManager
* @param itemModel
*/
void processHandledItem(MenuManager parentManager,
MHandledMenuItem itemModel) {
IContributionItem ici = getContribution(itemModel);
if (ici != null) {
return;
}
final IEclipseContext lclContext = getContext(itemModel);
HandledContributionItem ci = ContextInjectionFactory.make(
HandledContributionItem.class, lclContext);
ci.setModel(itemModel);
ci.setVisible(itemModel.isVisible());
addToManager(parentManager, itemModel, ci);
linkModelToContribution(itemModel, ci);
}
private String getText(MMenu menuModel) {
String text = menuModel.getLocalizedLabel();
if (text == null || text.length() == 0) {
return NO_LABEL;
}
return text;
}
private ImageDescriptor getImageDescriptor(MUILabel element) {
IEclipseContext localContext = context;
String iconURI = element.getIconURI();
if (iconURI != null && iconURI.length() > 0) {
ISWTResourceUtilities resUtils = (ISWTResourceUtilities) localContext
.get(IResourceUtilities.class.getName());
return resUtils.imageDescriptorFromURI(URI.createURI(iconURI));
}
return null;
}
public MenuManager getManager(MMenu model) {
return modelToManager.get(model);
}
public MMenu getMenuModel(MenuManager manager) {
return managerToModel.get(manager);
}
public void linkModelToManager(MMenu model, MenuManager manager) {
modelToManager.put(model, manager);
managerToModel.put(manager, model);
}
public void clearModelToManager(MMenu model, MenuManager manager) {
modelToManager.remove(model);
managerToModel.remove(manager);
}
public IContributionItem getContribution(MMenuElement model) {
return modelToContribution.get(model);
}
public MMenuElement getMenuElement(IContributionItem item) {
return contributionToModel.get(item);
}
public void linkModelToContribution(MMenuElement model,
IContributionItem item) {
modelToContribution.put(model, item);
contributionToModel.put(item, model);
}
public void clearModelToContribution(MMenuElement model,
IContributionItem item) {
modelToContribution.remove(model);
contributionToModel.remove(item);
}
public ContributionRecord getContributionRecord(MMenuElement element) {
return modelContributionToRecord.get(element);
}
public void linkElementToContributionRecord(MMenuElement element,
ContributionRecord record) {
modelContributionToRecord.put(element, record);
}
/**
* Search the records for testing. Look, but don't touch!
*
* @return the array of active ContributionRecords.
*/
public ContributionRecord[] getContributionRecords() {
HashSet<ContributionRecord> records = new HashSet<ContributionRecord>(
modelContributionToRecord.values());
return records.toArray(new ContributionRecord[records.size()]);
}
public IEclipseContext getContext(MUIElement el) {
return super.getContext(el);
}
/**
* @param menuManager
* @param menuModel
*/
public void reconcileManagerToModel(MenuManager menuManager, MMenu menuModel) {
List<MMenuElement> modelChildren = menuModel.getChildren();
HashSet<MOpaqueMenuItem> oldModelItems = new HashSet<MOpaqueMenuItem>();
HashSet<MOpaqueMenu> oldMenus = new HashSet<MOpaqueMenu>();
HashSet<MOpaqueMenuSeparator> oldSeps = new HashSet<MOpaqueMenuSeparator>();
for (MMenuElement itemModel : modelChildren) {
if (itemModel instanceof MOpaqueMenuSeparator) {
oldSeps.add((MOpaqueMenuSeparator) itemModel);
} else if (itemModel instanceof MOpaqueMenuItem) {
oldModelItems.add((MOpaqueMenuItem) itemModel);
} else if (itemModel instanceof MOpaqueMenu) {
oldMenus.add((MOpaqueMenu) itemModel);
}
}
IContributionItem[] items = menuManager.getItems();
for (int src = 0, dest = 0; src < items.length; src++, dest++) {
IContributionItem item = items[src];
if (item instanceof MenuManager) {
MenuManager childManager = (MenuManager) item;
MMenu childModel = getMenuModel(childManager);
if (childModel == null) {
MMenu legacyModel = MenuFactoryImpl.eINSTANCE
.createOpaqueMenu();
legacyModel.setElementId(childManager.getId());
legacyModel.setVisible(childManager.isVisible());
linkModelToManager(legacyModel, childManager);
modelChildren.add(dest, legacyModel);
reconcileManagerToModel(childManager, legacyModel);
} else {
if (childModel instanceof MOpaqueMenu) {
oldMenus.remove(childModel);
}
if (modelChildren.size() > dest) {
if (modelChildren.get(dest) != childModel) {
modelChildren.remove(childModel);
modelChildren.add(dest, childModel);
}
} else {
modelChildren.add(childModel);
}
if (childModel instanceof MPopupMenu) {
if (((MPopupMenu) childModel).getContext() == null) {
IEclipseContext lclContext = getContext(menuModel);
if (lclContext != null) {
((MPopupMenu) childModel)
.setContext(lclContext
.createChild(childModel
.getElementId()));
}
}
}
}
} else if (item.isSeparator() || item.isGroupMarker()) {
MMenuElement menuElement = getMenuElement(item);
if (menuElement == null) {
MOpaqueMenuSeparator legacySep = MenuFactoryImpl.eINSTANCE
.createOpaqueMenuSeparator();
legacySep.setElementId(item.getId());
legacySep.setVisible(item.isVisible());
legacySep.setOpaqueItem(item);
linkModelToContribution(legacySep, item);
modelChildren.add(dest, legacySep);
} else if (menuElement instanceof MOpaqueMenuSeparator) {
MOpaqueMenuSeparator legacySep = (MOpaqueMenuSeparator) menuElement;
oldSeps.remove(legacySep);
if (modelChildren.size() > dest) {
if (modelChildren.get(dest) != legacySep) {
modelChildren.remove(legacySep);
modelChildren.add(dest, legacySep);
}
} else {
modelChildren.add(legacySep);
}
}
} else {
MMenuElement menuElement = getMenuElement(item);
if (menuElement == null) {
MOpaqueMenuItem legacyItem = MenuFactoryImpl.eINSTANCE
.createOpaqueMenuItem();
legacyItem.setElementId(item.getId());
legacyItem.setVisible(item.isVisible());
legacyItem.setOpaqueItem(item);
linkModelToContribution(legacyItem, item);
modelChildren.add(dest, legacyItem);
} else if (menuElement instanceof MOpaqueMenuItem) {
MOpaqueMenuItem legacyItem = (MOpaqueMenuItem) menuElement;
oldModelItems.remove(legacyItem);
if (modelChildren.size() > dest) {
if (modelChildren.get(dest) != legacyItem) {
modelChildren.remove(legacyItem);
modelChildren.add(dest, legacyItem);
}
} else {
modelChildren.add(legacyItem);
}
}
}
}
if (!oldModelItems.isEmpty()) {
modelChildren.removeAll(oldModelItems);
for (MOpaqueMenuItem model : oldModelItems) {
clearModelToContribution(model,
(IContributionItem) model.getOpaqueItem());
}
}
if (!oldMenus.isEmpty()) {
modelChildren.removeAll(oldMenus);
for (MOpaqueMenu oldMenu : oldMenus) {
MenuManager oldManager = getManager(oldMenu);
clearModelToManager(oldMenu, oldManager);
}
}
if (!oldSeps.isEmpty()) {
modelChildren.removeAll(oldSeps);
for (MOpaqueMenuSeparator model : oldSeps) {
clearModelToContribution(model,
(IContributionItem) model.getOpaqueItem());
}
}
}
/**
* @param menuManager
* @param element
* @param evalContext
*/
public static void updateVisibility(MenuManager menuManager,
MMenuElement element, ExpressionContext evalContext) {
if (!(element.getVisibleWhen() instanceof MCoreExpression)) {
return;
}
boolean val = ContributionsAnalyzer.isVisible(
(MCoreExpression) element.getVisibleWhen(), evalContext);
if (val != element.isVisible()) {
element.setVisible(val);
menuManager.markDirty();
}
}
/**
* Clean dynamic menu contributions provided by
* {@link MDynamicMenuContribution} application model elements
*
* @param menuManager
* @param menuModel
* @param dump
*/
public void removeDynamicMenuContributions(MenuManager menuManager,
MMenu menuModel, ArrayList<MMenuElement> dump) {
removeMenuContributions(menuModel, dump);
for (MMenuElement mMenuElement : dump) {
IContributionItem ici = getContribution(mMenuElement);
menuManager.remove(ici);
clearModelToContribution(menuModel, ici);
}
}
}