/******************************************************************************* * 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(); } } }