blob: 233528ff4d13453527f24df3a58370d40756c31f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2010 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
*******************************************************************************/
package org.eclipse.e4.ui.workbench.renderers.swt;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.e4.core.commands.ECommandService;
import org.eclipse.e4.core.commands.EHandlerService;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.EclipseContextFactory;
import org.eclipse.e4.core.contexts.IContextFunction;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.core.services.contributions.IContributionFactory;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.core.services.log.Logger;
import org.eclipse.e4.ui.bindings.EBindingService;
import org.eclipse.e4.ui.internal.workbench.Activator;
import org.eclipse.e4.ui.internal.workbench.ContributionsAnalyzer;
import org.eclipse.e4.ui.internal.workbench.Policy;
import org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer;
import org.eclipse.e4.ui.model.application.MContribution;
import org.eclipse.e4.ui.model.application.commands.MParameter;
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.menu.ItemType;
import org.eclipse.e4.ui.model.application.ui.menu.MHandledItem;
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.MRenderedMenu;
import org.eclipse.e4.ui.model.application.ui.menu.MToolItem;
import org.eclipse.e4.ui.workbench.UIEvents;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.bindings.TriggerSequence;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Widget;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
/**
* Create a contribute part.
*/
public class ToolItemRenderer extends SWTPartRenderer {
private static final String TIR_STATIC_CONTEXT = "HCI-staticContext"; //$NON-NLS-1$
@Inject
Logger logger;
@Inject
IEventBroker eventBroker;
private EventHandler itemUpdater = new EventHandler() {
public void handleEvent(Event event) {
// Ensure that this event is for a MToolItem
if (!(event.getProperty(UIEvents.EventTags.ELEMENT) instanceof MToolItem))
return;
MToolItem itemModel = (MToolItem) event
.getProperty(UIEvents.EventTags.ELEMENT);
ToolItem toolItem = (ToolItem) itemModel.getWidget();
// No widget == nothing to update
if (toolItem == null)
return;
String attName = (String) event
.getProperty(UIEvents.EventTags.ATTNAME);
if (UIEvents.UILabel.LABEL.equals(attName)) {
setItemText(itemModel, toolItem);
} else if (UIEvents.UILabel.ICONURI.equals(attName)) {
toolItem.setImage(getImage(itemModel));
} else if (UIEvents.UILabel.TOOLTIP.equals(attName)) {
toolItem.setToolTipText(getToolTipText(itemModel));
toolItem.setImage(getImage(itemModel));
}
}
};
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 MToolItem))
return;
MToolItem itemModel = (MToolItem) event
.getProperty(UIEvents.EventTags.ELEMENT);
ToolItem toolItem = (ToolItem) itemModel.getWidget();
if (toolItem != null) {
toolItem.setSelection(itemModel.isSelected());
}
}
};
private EventHandler enabledUpdater = new EventHandler() {
public void handleEvent(Event event) {
// Ensure that this event is for a MToolItem
if (!(event.getProperty(UIEvents.EventTags.ELEMENT) instanceof MToolItem))
return;
MToolItem itemModel = (MToolItem) event
.getProperty(UIEvents.EventTags.ELEMENT);
ToolItem toolItem = (ToolItem) itemModel.getWidget();
if (toolItem != null) {
toolItem.setEnabled(itemModel.isEnabled());
}
}
};
@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);
}
@PreDestroy
public void contextDisposed() {
eventBroker.unsubscribe(itemUpdater);
eventBroker.unsubscribe(selectionUpdater);
eventBroker.unsubscribe(enabledUpdater);
}
private ParameterizedCommand generateParameterizedCommand(
final MHandledItem item, final IEclipseContext lclContext) {
ECommandService cmdService = (ECommandService) lclContext
.get(ECommandService.class.getName());
Map<String, Object> parameters = null;
List<MParameter> modelParms = item.getParameters();
if (modelParms != null && !modelParms.isEmpty()) {
parameters = new HashMap<String, Object>();
for (MParameter mParm : modelParms) {
parameters.put(mParm.getName(), mParm.getValue());
}
}
ParameterizedCommand cmd = cmdService.createCommand(item.getCommand()
.getElementId(), parameters);
item.setWbCommand(cmd);
return cmd;
}
private void setItemText(MToolItem model, ToolItem item) {
String text = model.getLocalizedLabel();
if (text == null) {
text = ""; //$NON-NLS-1$
}
item.setText(text);
}
private String getToolTipText(MItem item) {
String text = item.getLocalizedTooltip();
if (item instanceof MHandledItem) {
MHandledItem handledItem = (MHandledItem) item;
IEclipseContext context = getContext(item);
EBindingService bs = (EBindingService) context
.get(EBindingService.class.getName());
ParameterizedCommand cmd = handledItem.getWbCommand();
if (cmd == null) {
cmd = generateParameterizedCommand(handledItem, context);
}
TriggerSequence sequence = bs.getBestSequenceFor(handledItem
.getWbCommand());
if (sequence != null) {
if (text == null) {
try {
text = cmd.getName();
} catch (NotDefinedException e) {
return null;
}
}
text = text + " (" + sequence.format() + ')'; //$NON-NLS-1$
}
return text;
}
return text;
}
public Object createWidget(final MUIElement element, Object parent) {
if (!(element instanceof MToolItem) || !(parent instanceof ToolBar))
return null;
MToolItem itemModel = (MToolItem) element;
// determine the index at which we should create the new item
int addIndex = calcVisibleIndex(element);
// OK, it's a real menu item, what kind?
MMenu menu = itemModel.getMenu();
int flags = 0;
if (menu != null) {
flags |= SWT.DROP_DOWN;
} else if (itemModel.getType() == ItemType.PUSH)
flags |= SWT.PUSH;
else if (itemModel.getType() == ItemType.CHECK)
flags |= SWT.CHECK;
else if (itemModel.getType() == ItemType.RADIO)
flags |= SWT.RADIO;
ToolItem newItem = new ToolItem((ToolBar) parent, flags, addIndex);
if (itemModel.getLabel() != null)
newItem.setText(itemModel.getLocalizedLabel());
newItem.setToolTipText(getToolTipText(itemModel));
newItem.setImage(getImage((MUILabel) element));
newItem.setEnabled(itemModel.isEnabled());
newItem.setSelection(itemModel.isSelected());
return newItem;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer#hideChild
* (org.eclipse.e4.ui.model.application.MElementContainer,
* org.eclipse.e4.ui.model.application.MUIElement)
*/
@Override
public void hideChild(MElementContainer<MUIElement> parentElement,
MUIElement child) {
super.hideChild(parentElement, child);
// Since there's no place to 'store' a child that's not in a menu
// we'll blow it away and re-create on an add
Widget widget = (Widget) child.getWidget();
if (widget != null && !widget.isDisposed())
widget.dispose();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer#hookControllerLogic
* (org.eclipse.e4.ui.model.application.MUIElement)
*/
@Override
public void hookControllerLogic(MUIElement me) {
// If the item is a CHECK or RADIO update the model's state to match
if (me instanceof MItem) {
final MItem item = (MItem) me;
if (item.getType() == ItemType.CHECK
|| item.getType() == ItemType.RADIO) {
ToolItem ti = (ToolItem) me.getWidget();
ti.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
item.setSelected(((ToolItem) e.widget).getSelection());
}
public void widgetDefaultSelected(SelectionEvent e) {
item.setSelected(((ToolItem) e.widget).getSelection());
}
});
} else if (me instanceof MToolItem) {
final MMenu mmenu = ((MToolItem) me).getMenu();
if (mmenu != null) {
final ToolItem ti = (ToolItem) me.getWidget();
ti.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (e.detail == SWT.ARROW) {
Menu menu = getMenu(mmenu, ti);
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.isVisible()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
});
}
}
}
// 'Execute' the operation if possible
if (me instanceof MContribution
&& ((MContribution) me).getContributionURI() != null) {
final MToolItem item = (MToolItem) me;
final MContribution contrib = (MContribution) me;
final IEclipseContext lclContext = getContext(me);
ToolItem ti = (ToolItem) me.getWidget();
ti.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
if (contrib.getObject() == null) {
IContributionFactory cf = (IContributionFactory) lclContext
.get(IContributionFactory.class.getName());
contrib.setObject(cf.create(
contrib.getContributionURI(), lclContext));
}
lclContext.set(MItem.class.getName(), item);
ContextInjectionFactory.invoke(contrib.getObject(),
Execute.class, lclContext);
lclContext.remove(MItem.class.getName());
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
} else if (me instanceof MHandledItem) {
final MHandledItem item = (MHandledItem) me;
final IEclipseContext lclContext = getContext(me);
final ToolItem ti = (ToolItem) me.getWidget();
final Display display = ti.getDisplay();
display.timerExec(500, new Runnable() {
boolean logged = false;
public void run() {
if (ti.isDisposed()) {
return;
}
SafeRunner.run(new ISafeRunnable() {
public void run() throws Exception {
EHandlerService service = lclContext
.get(EHandlerService.class);
if (service == null) {
// no localized handler service
return;
}
ParameterizedCommand cmd = item.getWbCommand();
if (cmd == null) {
cmd = generateParameterizedCommand(item,
lclContext);
}
if (cmd == null) {
return;
}
final IEclipseContext staticContext = EclipseContextFactory
.create(TIR_STATIC_CONTEXT);
ContributionsAnalyzer.populateModelInterfaces(item,
staticContext, item.getClass()
.getInterfaces());
try {
item.setEnabled(service.canExecute(cmd,
staticContext));
} finally {
staticContext.dispose();
}
}
public void handleException(Throwable exception) {
if (!logged) {
logged = true;
logger.error(
exception,
"Internal error during tool item enablement updating, this is only logged once per tool item."); //$NON-NLS-1$
}
}
});
// repeat until disposed
display.timerExec(500, this);
}
});
ti.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
if (e.detail != SWT.ARROW) {
EHandlerService service = (EHandlerService) lclContext
.get(EHandlerService.class.getName());
ParameterizedCommand cmd = item.getWbCommand();
if (cmd == null) {
cmd = generateParameterizedCommand(item, lclContext);
}
if (cmd == null) {
Activator.trace(Policy.DEBUG_MENUS,
"Failed to execute: " + item.getCommand(), //$NON-NLS-1$
null);
return;
}
final IEclipseContext staticContext = EclipseContextFactory
.create(TIR_STATIC_CONTEXT);
ContributionsAnalyzer.populateModelInterfaces(item,
staticContext, item.getClass().getInterfaces());
service.executeHandler(cmd, staticContext);
}
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
}
}
/**
* @param mmenu
* @param shell
* @return
*/
protected Menu getMenu(final MMenu mmenu, ToolItem toolItem) {
Object obj = mmenu.getWidget();
if (obj instanceof Menu) {
return (Menu) obj;
}
// this is a temporary passthrough of the IMenuCreator
if (mmenu instanceof MRenderedMenu) {
obj = ((MRenderedMenu) mmenu).getContributionManager();
if (obj instanceof IContextFunction) {
final IEclipseContext lclContext = getContext(mmenu);
obj = ((IContextFunction) obj).compute(lclContext);
((MRenderedMenu) mmenu).setContributionManager(obj);
}
if (obj instanceof IMenuCreator) {
final IMenuCreator creator = (IMenuCreator) obj;
final Menu menu = creator.getMenu(toolItem.getParent()
.getShell());
if (menu != null) {
toolItem.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
if (menu != null && !menu.isDisposed()) {
creator.dispose();
((MRenderedMenu) mmenu).setWidget(null);
}
}
});
mmenu.setWidget(menu);
menu.setData(AbstractPartRenderer.OWNING_ME, menu);
return menu;
}
}
}
return null;
}
}