| /******************************************************************************* |
| * Copyright (c) 2008, 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: |
| * IBM Corporation - initial API and implementation |
| * Ragnar Nevries <r.eclipse@nevri.es> - Bug 443514 |
| * Lars Vogel <Lars.Vogel@vogella.com> - Bug 472654 |
| *******************************************************************************/ |
| package org.eclipse.e4.ui.workbench.renderers.swt; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import org.eclipse.e4.core.contexts.IEclipseContext; |
| import org.eclipse.e4.core.services.log.Logger; |
| import org.eclipse.e4.ui.css.core.engine.CSSEngine; |
| import org.eclipse.e4.ui.css.swt.dom.WidgetElement; |
| import org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer; |
| import org.eclipse.e4.ui.internal.workbench.swt.CSSConstants; |
| import org.eclipse.e4.ui.model.application.descriptor.basic.MPartDescriptor; |
| 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.services.IStylingEngine; |
| import org.eclipse.e4.ui.workbench.IPresentationEngine; |
| import org.eclipse.e4.ui.workbench.IResourceUtilities; |
| 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.swt.accessibility.AccessibleAdapter; |
| import org.eclipse.swt.accessibility.AccessibleEvent; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Widget; |
| |
| public abstract class SWTPartRenderer extends AbstractPartRenderer { |
| private static final String ICON_URI_FOR_PART = "IconUriForPart"; //$NON-NLS-1$ |
| |
| private Map<String, Image> imageMap = new HashMap<>(); |
| |
| private String pinURI = "platform:/plugin/org.eclipse.e4.ui.workbench.renderers.swt/icons/full/ovr16/pinned_ovr.png"; //$NON-NLS-1$ |
| private Image pinImage; |
| |
| private ISWTResourceUtilities resUtils; |
| |
| @Override |
| public void processContents(MElementContainer<MUIElement> container) { |
| // EMF gives us null lists if empty |
| if (container == null) |
| return; |
| |
| // Process any contents of the newly created ME |
| List<MUIElement> parts = container.getChildren(); |
| if (parts != null) { |
| // loading a legacy app will add children to the window while it is |
| // being rendered. |
| // this is *not* the correct place for this |
| // hope that the ADD event will pick up the new part. |
| IPresentationEngine renderer = context.get(IPresentationEngine.class); |
| for (int i = 0; i < parts.size(); i++) { |
| MUIElement childME = parts.get(i); |
| renderer.createGui(childME); |
| } |
| } |
| } |
| |
| public void styleElement(MUIElement element, boolean active) { |
| if (!active) |
| element.getTags().remove(CSSConstants.CSS_ACTIVE_CLASS); |
| else |
| element.getTags().add(CSSConstants.CSS_ACTIVE_CLASS); |
| |
| if (element.getWidget() != null) |
| setCSSInfo(element, element.getWidget()); |
| } |
| |
| public void setCSSInfo(MUIElement me, Object widget) { |
| // No SWT widget, nothing to style... |
| if (widget == null) |
| return; |
| |
| // Set up the CSS Styling parameters; id & class |
| IEclipseContext ctxt = getContext(me); |
| if (ctxt == null) { |
| return; |
| } |
| |
| final IStylingEngine engine = (IStylingEngine) ctxt |
| .get(IStylingEngine.SERVICE_NAME); |
| if (engine == null) |
| return; |
| |
| // Put all the tags into the class string |
| EObject eObj = (EObject) me; |
| String cssClassStr = 'M' + eObj.eClass().getName(); |
| for (String tag : me.getTags()) |
| cssClassStr += ' ' + tag; |
| |
| // this will trigger style() |
| String id = me.getElementId(); |
| if (id != null) { |
| id = id.replace('.', '-'); |
| } |
| engine.setClassnameAndId(widget, cssClassStr, id); |
| } |
| |
| @SuppressWarnings("restriction") |
| protected void reapplyStyles(Widget widget) { |
| CSSEngine engine = WidgetElement.getEngine(widget); |
| if (engine != null) { |
| engine.applyStyles(widget, false); |
| } |
| } |
| |
| @Override |
| public void bindWidget(MUIElement me, Object widget) { |
| if (widget instanceof Widget) { |
| ((Widget) widget).setData(OWNING_ME, me); |
| |
| // Set up the CSS Styling parameters; id & class |
| setCSSInfo(me, widget); |
| |
| // Ensure that disposed widgets are unbound form the model |
| Widget swtWidget = (Widget) widget; |
| swtWidget.addDisposeListener(e -> { |
| MUIElement element = (MUIElement) e.widget |
| .getData(OWNING_ME); |
| if (element != null) |
| unbindWidget(element); |
| }); |
| } |
| |
| // Create a bi-directional link between the widget and the model |
| me.setWidget(widget); |
| } |
| |
| public Object unbindWidget(MUIElement me) { |
| Widget widget = (Widget) me.getWidget(); |
| if (widget != null) { |
| me.setWidget(null); |
| if (!widget.isDisposed()) |
| widget.setData(OWNING_ME, null); |
| } |
| |
| // Clear the factory reference |
| me.setRenderer(null); |
| |
| return widget; |
| } |
| |
| @Override |
| protected Widget getParentWidget(MUIElement element) { |
| return (Widget) element.getParent().getWidget(); |
| } |
| |
| @Override |
| public void disposeWidget(MUIElement element) { |
| |
| if (element.getWidget() instanceof Widget) { |
| Widget curWidget = (Widget) element.getWidget(); |
| |
| if (curWidget != null && !curWidget.isDisposed()) { |
| unbindWidget(element); |
| try { |
| curWidget.dispose(); |
| } catch (Exception e) { |
| Logger logService = context.get(Logger.class); |
| if (logService != null) { |
| String msg = "Error disposing widget for : " + element.getClass().getName(); //$NON-NLS-1$ |
| if (element instanceof MUILabel) { |
| msg += ' ' + ((MUILabel) element) |
| .getLocalizedLabel(); |
| } |
| logService.error(e, msg); |
| } |
| } |
| } |
| } |
| element.setWidget(null); |
| } |
| |
| @Override |
| public void hookControllerLogic(final MUIElement me) { |
| Object widget = me.getWidget(); |
| |
| // add an accessibility listener (not sure if this is in the wrong place |
| // (factory?) |
| if (widget instanceof Control && me instanceof MUILabel) { |
| ((Control) widget).getAccessible().addAccessibleListener( |
| new AccessibleAdapter() { |
| @Override |
| public void getName(AccessibleEvent e) { |
| e.result = ((MUILabel) me).getLocalizedLabel(); |
| } |
| }); |
| } |
| } |
| |
| public String getToolTip(MUILabel element) { |
| String overrideTip = (String) ((MUIElement) element).getTransientData() |
| .get(IPresentationEngine.OVERRIDE_TITLE_TOOL_TIP_KEY); |
| return overrideTip == null ? element.getLocalizedTooltip() |
| : overrideTip; |
| } |
| |
| protected Image getImageFromURI(String iconURI) { |
| if (iconURI == null || iconURI.length() == 0) |
| return null; |
| |
| Image image = imageMap.get(iconURI); |
| if (image == null) { |
| image = resUtils.imageDescriptorFromURI(URI.createURI(iconURI)) |
| .createImage(); |
| imageMap.put(iconURI, image); |
| } |
| return image; |
| } |
| |
| @Override |
| public Image getImage(MUILabel element) { |
| Image image = (Image) ((MUIElement) element).getTransientData().get( |
| IPresentationEngine.OVERRIDE_ICON_IMAGE_KEY); |
| if (image == null || image.isDisposed()) { |
| image = getImageFromURI(getIconURI(element)); |
| } |
| |
| if (image != null) { |
| image = adornImage((MUIElement) element, image); |
| } |
| |
| return image; |
| } |
| |
| private String getIconURI(MUILabel element) { |
| if (element instanceof MPart) { |
| MPart part = (MPart) element; |
| String iconURI = (String) part.getTransientData().get( |
| ICON_URI_FOR_PART); |
| if (iconURI != null) { |
| return iconURI; |
| } |
| |
| MPartDescriptor desc = modelService.getPartDescriptor(part |
| .getElementId()); |
| iconURI = desc != null && desc.getIconURI() != null ? desc |
| .getIconURI() : element.getIconURI(); |
| part.getTransientData().put(ICON_URI_FOR_PART, iconURI); |
| |
| return iconURI; |
| } |
| return element.getIconURI(); |
| } |
| |
| /** |
| * @param element |
| * @param image |
| * @return |
| */ |
| private Image adornImage(MUIElement element, Image image) { |
| // Remove and dispose any previous adorned image |
| Image previouslyAdornedImage = (Image) element.getTransientData().get( |
| "previouslyAdorned"); //$NON-NLS-1$ |
| if (previouslyAdornedImage != null |
| && !previouslyAdornedImage.isDisposed()) |
| previouslyAdornedImage.dispose(); |
| element.getTransientData().remove(IPresentationEngine.ADORNMENT_PIN); |
| |
| Image adornedImage = image; |
| if (element.getTags().contains(IPresentationEngine.ADORNMENT_PIN)) { |
| adornedImage = resUtils.adornImage(image, pinImage); |
| if (adornedImage != image) |
| element.getTransientData().put( |
| "previouslyAdorned", adornedImage); //$NON-NLS-1$ |
| } |
| |
| return adornedImage; |
| } |
| |
| /** |
| * Calculates the index of the element in terms of the other <b>rendered</b> |
| * elements. This is useful when 'inserting' elements in the middle of |
| * existing, rendered parents. |
| * |
| * @param element |
| * The element to get the index for |
| * @return The visible index or -1 if the element is not a child of the |
| * parent |
| */ |
| protected int calcVisibleIndex(MUIElement element) { |
| MElementContainer<MUIElement> parent = element.getParent(); |
| |
| int curIndex = 0; |
| for (MUIElement child : parent.getChildren()) { |
| if (child == element) { |
| return curIndex; |
| } |
| |
| if (child.getWidget() != null) |
| curIndex++; |
| } |
| return -1; |
| } |
| |
| protected int calcIndex(MUIElement element) { |
| MElementContainer<MUIElement> parent = element.getParent(); |
| return parent.getChildren().indexOf(element); |
| } |
| |
| @Override |
| public void childRendered(MElementContainer<MUIElement> parentElement, MUIElement element) { |
| } |
| |
| @Override |
| public void init(IEclipseContext context) { |
| super.init(context); |
| |
| resUtils = (ISWTResourceUtilities) context.get(IResourceUtilities.class |
| .getName()); |
| pinImage = getImageFromURI(pinURI); |
| |
| Display.getCurrent().disposeExec(() -> { |
| for (Image image : imageMap.values()) { |
| image.dispose(); |
| } |
| }); |
| } |
| |
| @Override |
| protected boolean requiresFocus(MPart element) { |
| MUIElement focussed = getModelElement(Display.getDefault() |
| .getFocusControl()); |
| if (focussed == null) { |
| return true; |
| } |
| // we ignore menus |
| do { |
| if (focussed == element || focussed == element.getToolbar()) { |
| return false; |
| } |
| focussed = focussed.getParent(); |
| } while (focussed != null); |
| return true; |
| } |
| |
| static protected MUIElement getModelElement(Control ctrl) { |
| if (ctrl == null) |
| return null; |
| |
| MUIElement element = (MUIElement) ctrl |
| .getData(AbstractPartRenderer.OWNING_ME); |
| if (element != null) { |
| return element; |
| // FIXME: DndUtil.getModelElement() has the following check; |
| // do we need this? |
| // if (modelService.getTopLevelWindowFor(element) == topLevelWindow) |
| // { |
| // return element; |
| // } |
| // return null; |
| } |
| |
| return getModelElement(ctrl.getParent()); |
| } |
| |
| @Override |
| public void forceFocus(MUIElement element) { |
| if (element.getWidget() instanceof Control) { |
| // Have SWT set the focus |
| Control ctrl = (Control) element.getWidget(); |
| if (!ctrl.isDisposed()) |
| ctrl.forceFocus(); |
| } |
| } |
| } |