blob: 4f7cd7b0d7d7ff4dce3d034a059eb262b8ed6324 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 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:
* Jonas Helming, Dirk Fauth - Bug 410087
******************************************************************************/
package org.eclipse.e4.ui.workbench.renderers.swt;
import javax.inject.Inject;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.e4.core.contexts.IContextFunction;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.services.log.Logger;
import org.eclipse.e4.ui.internal.workbench.RenderedElementUtil;
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.MUIElement;
import org.eclipse.e4.ui.model.application.ui.menu.ItemType;
import org.eclipse.e4.ui.model.application.ui.menu.MItem;
import org.eclipse.e4.ui.model.application.ui.menu.MMenu;
import org.eclipse.e4.ui.model.application.ui.menu.MToolItem;
import org.eclipse.e4.ui.services.help.EHelpService;
import org.eclipse.e4.ui.workbench.IPresentationEngine;
import org.eclipse.e4.ui.workbench.IResourceUtilities;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.e4.ui.workbench.swt.util.ISWTResourceUtilities;
import org.eclipse.emf.common.util.URI;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.resource.DeviceResourceException;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Widget;
/**
* Common super class for HandledContributionItem and DirectContributionItem
*/
public abstract class AbstractContributionItem extends ContributionItem {
protected static final String FORCE_TEXT = "FORCE_TEXT"; //$NON-NLS-1$
protected static final String ICON_URI = "iconURI"; //$NON-NLS-1$
protected static final String DISABLED_URI = "disabledURI"; //$NON-NLS-1$
/**
* Internal key for transient maps to provide a runnable on widget disposal
*/
public static final String DISPOSABLE = "IDisposable"; //$NON-NLS-1$
@Inject
@Optional
protected Logger logger;
@Inject
private EModelService modelService;
@Inject
@Optional
protected EHelpService helpService;
protected Widget widget;
protected Listener menuItemListener;
protected LocalResourceManager localResourceManager;
MItem modelItem;
private ISafeRunnable updateRunner;
private ISWTResourceUtilities resUtils = null;
protected IMenuListener menuListener = manager -> update(null);
/**
* Flag to ensure that an error during updates are only logged once to
* prevent spamming the log. Is set to <code>true</code> after an error on
* update was logged the first time.
*/
private boolean logged = false;
/**
*
*/
public AbstractContributionItem() {
super();
}
/**
* @param id
*/
public AbstractContributionItem(String id) {
super(id);
}
@Override
public void update() {
update(null);
}
@Override
public void update(String id) {
updateIcons();
if (widget instanceof MenuItem) {
updateMenuItem();
} else if (widget instanceof ToolItem) {
updateToolItem();
}
}
protected abstract void updateMenuItem();
protected abstract void updateToolItem();
@Inject
void setResourceUtils(IResourceUtilities<ImageDescriptor> utils) {
resUtils = (ISWTResourceUtilities) utils;
}
protected Image getImage(String iconURI, LocalResourceManager resourceManager) {
Image image = null;
if (iconURI != null && iconURI.length() > 0) {
ImageDescriptor iconDescriptor = resUtils.imageDescriptorFromURI(URI.createURI(iconURI));
if (iconDescriptor != null) {
try {
image = resourceManager.createImage(iconDescriptor);
} catch (DeviceResourceException e) {
iconDescriptor = ImageDescriptor.getMissingImageDescriptor();
image = resourceManager.createImage(iconDescriptor);
// as we replaced the failed icon, log the message once.
if (Policy.DEBUG_MENUS) {
WorkbenchSWTActivator.trace(Policy.DEBUG_MENUS_FLAG, "failed to create image " + iconURI, e); //$NON-NLS-1$
}
}
}
}
return image;
}
protected void updateIcons() {
if (!(widget instanceof Item)) {
return;
}
Item item = (Item) widget;
String iconURI = modelItem.getIconURI() != null ? modelItem.getIconURI() : ""; //$NON-NLS-1$
String disabledURI = getDisabledIconURI(modelItem);
Object disabledData = item.getData(DISABLED_URI);
if (disabledData == null)
disabledData = ""; //$NON-NLS-1$
if (!iconURI.equals(item.getData(ICON_URI)) || !disabledURI.equals(disabledData)) {
LocalResourceManager resourceManager = new LocalResourceManager(JFaceResources.getResources());
Image iconImage = getImage(iconURI, resourceManager);
item.setImage(iconImage);
item.setData(ICON_URI, iconURI);
if (item instanceof ToolItem) {
iconImage = getImage(disabledURI, resourceManager);
((ToolItem) item).setDisabledImage(iconImage);
item.setData(DISABLED_URI, disabledURI);
}
disposeOldImages();
localResourceManager = resourceManager;
}
}
private String getDisabledIconURI(MItem toolItem) {
Object obj = toolItem.getTransientData().get(IPresentationEngine.DISABLED_ICON_IMAGE_KEY);
return obj instanceof String ? (String) obj : ""; //$NON-NLS-1$
}
protected void disposeOldImages() {
if (localResourceManager != null) {
localResourceManager.dispose();
localResourceManager = null;
}
}
/**
* @param item
* the model item
*/
public void setModel(MItem item) {
modelItem = item;
setId(modelItem.getElementId());
updateVisible();
}
/**
* @return the model
*/
public MItem getModel() {
return modelItem;
}
@Override
public void setParent(IContributionManager parent) {
if (getParent() instanceof IMenuManager) {
IMenuManager menuMgr = (IMenuManager) getParent();
menuMgr.removeMenuListener(menuListener);
}
if (parent instanceof IMenuManager) {
IMenuManager menuMgr = (IMenuManager) parent;
menuMgr.addMenuListener(menuListener);
}
super.setParent(parent);
}
@Override
public void fill(Menu menu, int index) {
if (modelItem == null) {
return;
}
if (widget != null) {
return;
}
int style = SWT.PUSH;
if (modelItem.getType() == ItemType.PUSH)
style = SWT.PUSH;
else if (modelItem.getType() == ItemType.CHECK)
style = SWT.CHECK;
else if (modelItem.getType() == ItemType.RADIO)
style = SWT.RADIO;
MenuItem item = null;
if (index >= 0) {
item = new MenuItem(menu, style, index);
} else {
item = new MenuItem(menu, style);
}
item.setData(this);
item.addListener(SWT.Dispose, getItemListener());
item.addListener(SWT.Selection, getItemListener());
item.addListener(SWT.DefaultSelection, getItemListener());
item.addListener(SWT.Help, getItemListener());
widget = item;
modelItem.setWidget(widget);
widget.setData(AbstractPartRenderer.OWNING_ME, modelItem);
update(null);
postMenuFill();
}
/**
* This method is intended to perform actions additionally to the common
* actions in {@link AbstractContributionItem#fill(Menu, int)}
*/
protected void postMenuFill() {
}
@Override
public void fill(ToolBar parent, int index) {
if (modelItem == null) {
return;
}
if (widget != null) {
return;
}
boolean isDropdown = false;
if (modelItem instanceof MToolItem) {
MMenu menu = ((MToolItem) modelItem).getMenu();
isDropdown = menu != null;
}
int style = SWT.PUSH;
if (isDropdown)
style = SWT.DROP_DOWN;
else if (modelItem.getType() == ItemType.CHECK)
style = SWT.CHECK;
else if (modelItem.getType() == ItemType.RADIO)
style = SWT.RADIO;
ToolItem item = null;
if (index >= 0) {
item = new ToolItem(parent, style, index);
} else {
item = new ToolItem(parent, style);
}
item.setData(this);
item.addListener(SWT.Dispose, getItemListener());
item.addListener(SWT.Selection, getItemListener());
item.addListener(SWT.DefaultSelection, getItemListener());
widget = item;
modelItem.setWidget(widget);
widget.setData(AbstractPartRenderer.OWNING_ME, modelItem);
ToolItemUpdater updater = getUpdater();
if (updater != null) {
updater.registerItem(this);
}
update(null);
postToolbarFill();
}
/**
* This method is intended to perform actions additionally to the common
* actions in {@link AbstractContributionItem#fill(ToolBar, int)}
*/
protected void postToolbarFill() {
}
/**
* Return a parent context for this part.
*
* @param element
* the part to start searching from
* @return the parent's closest context, or global context if none in the
* hierarchy
*/
protected IEclipseContext getContextForParent(MUIElement element) {
return modelService.getContainingContext(element);
}
/**
* Return a context for this part.
*
* @param part
* the part to start searching from
* @return the closest context, or global context if none in the hierarchy
*/
protected IEclipseContext getContext(MUIElement part) {
if (part instanceof MContext) {
return ((MContext) part).getContext();
}
return getContextForParent(part);
}
/**
* @return the widgets of the contribution
*/
public Widget getWidget() {
return widget;
}
protected Menu getMenu(final MMenu mmenu, ToolItem toolItem) {
Object obj = mmenu.getWidget();
if (obj instanceof Menu && !((Menu) obj).isDisposed()) {
return (Menu) obj;
}
// this is a temporary passthrough of the IMenuCreator
if (RenderedElementUtil.isRenderedMenu(mmenu)) {
obj = RenderedElementUtil.getContributionManager(mmenu);
if (obj instanceof IContextFunction) {
final IEclipseContext lclContext = getContext(mmenu);
obj = ((IContextFunction) obj).compute(lclContext, null);
RenderedElementUtil.setContributionManager(mmenu, obj);
}
if (obj instanceof IMenuCreator) {
final IMenuCreator creator = (IMenuCreator) obj;
final Menu menu = creator.getMenu(toolItem.getParent().getShell());
if (menu != null) {
toolItem.addDisposeListener(e -> {
if (menu != null && !menu.isDisposed()) {
creator.dispose();
mmenu.setWidget(null);
}
});
menu.setData(AbstractPartRenderer.OWNING_ME, menu);
return menu;
}
}
} else {
final IEclipseContext lclContext = getContext(getModel());
IPresentationEngine engine = lclContext.get(IPresentationEngine.class);
obj = engine.createGui(mmenu, toolItem.getParent(), lclContext);
if (obj instanceof Menu) {
return (Menu) obj;
}
if (logger != null) {
logger.debug("Rendering returned " + obj); //$NON-NLS-1$
}
}
return null;
}
/**
* @param event
* @return whether the event was a drop down on a toolitem
*/
protected boolean dropdownEvent(Event event) {
if (event.detail == SWT.ARROW && modelItem instanceof MToolItem) {
ToolItem ti = (ToolItem) event.widget;
MMenu mmenu = ((MToolItem) modelItem).getMenu();
if (mmenu == null) {
return false;
}
Menu menu = getMenu(mmenu, ti);
if (menu == null || menu.isDisposed()) {
return true;
}
Rectangle itemBounds = ti.getBounds();
Point displayAt = ti.getParent().toDisplay(itemBounds.x, itemBounds.y + itemBounds.height);
menu.setLocation(displayAt);
menu.setVisible(true);
Display display = menu.getDisplay();
while (!menu.isDisposed() && menu.isVisible()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
return true;
}
return false;
}
protected void handleWidgetSelection(Event event) {
if (widget != null && !widget.isDisposed()) {
if (dropdownEvent(event)) {
return;
}
if (modelItem.getType() == ItemType.CHECK || modelItem.getType() == ItemType.RADIO) {
boolean selection = false;
if (widget instanceof MenuItem) {
selection = ((MenuItem) widget).getSelection();
} else if (widget instanceof ToolItem) {
selection = ((ToolItem) widget).getSelection();
}
modelItem.setSelected(selection);
}
if (canExecuteItem(event)) {
executeItem(event);
}
}
}
/**
* @param event
*/
protected abstract void executeItem(Event event);
/**
* @param event
* @return if the item can be executed
*/
protected abstract boolean canExecuteItem(Event event);
protected Listener getItemListener() {
if (menuItemListener == null) {
menuItemListener = event -> {
switch (event.type) {
case SWT.Dispose:
handleWidgetDispose(event);
break;
case SWT.DefaultSelection:
case SWT.Selection:
if (event.widget != null) {
handleWidgetSelection(event);
}
break;
case SWT.Help:
handleHelpRequest();
break;
}
};
}
return menuItemListener;
}
/**
*
*/
protected void handleHelpRequest() {
if (helpService == null)
return;
String helpContextId = getModel().getPersistedState().get(EHelpService.HELP_CONTEXT_ID);
if (helpContextId != null)
helpService.displayHelp(helpContextId);
}
/**
* @param event
*/
protected abstract void handleWidgetDispose(Event event);
protected void updateVisible() {
setVisible((modelItem).isVisible());
final IContributionManager parent = getParent();
if (parent != null) {
parent.markDirty();
}
}
private ISafeRunnable getUpdateRunner() {
if (updateRunner == null) {
updateRunner = new ISafeRunnable() {
@Override
public void run() throws Exception {
boolean shouldEnable = canExecuteItem(null);
if (shouldEnable != modelItem.isEnabled()) {
modelItem.setEnabled(shouldEnable);
update();
}
}
@Override
public void handleException(Throwable exception) {
if (!logged) {
logged = true;
if (logger != null) {
logger.error(exception,
"Internal error during tool item enablement updating, this is only logged once per tool item."); //$NON-NLS-1$
}
}
}
};
}
return updateRunner;
}
protected ToolItemUpdater getUpdater() {
if (modelItem != null) {
Object obj = modelItem.getRenderer();
if (obj instanceof ToolBarManagerRenderer) {
return ((ToolBarManagerRenderer) obj).getUpdater();
}
}
return null;
}
protected void updateItemEnablement() {
if (!(modelItem.getWidget() instanceof ToolItem))
return;
ToolItem widget = (ToolItem) modelItem.getWidget();
if (widget == null || widget.isDisposed())
return;
SafeRunner.run(getUpdateRunner());
}
}