package at.bestsolution.efxclipse.runtime.workbench.internal; import java.util.List; import java.util.Map; import javafx.scene.Group; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.inject.Named; import org.eclipse.e4.core.contexts.ContextInjectionFactory; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.annotations.Optional; 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.internal.workbench.Activator; import org.eclipse.e4.ui.internal.workbench.E4Workbench; import org.eclipse.e4.ui.internal.workbench.Policy; import org.eclipse.e4.ui.model.application.MApplication; import org.eclipse.e4.ui.model.application.MApplicationElement; import org.eclipse.e4.ui.model.application.MContribution; import org.eclipse.e4.ui.model.application.ui.MContext; import org.eclipse.e4.ui.model.application.ui.MElementContainer; import org.eclipse.e4.ui.model.application.ui.MGenericStack; import org.eclipse.e4.ui.model.application.ui.MUIElement; import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective; import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder; import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow; import org.eclipse.e4.ui.model.application.ui.basic.MWindow; import org.eclipse.e4.ui.model.application.ui.menu.MMenu; import org.eclipse.e4.ui.workbench.IPresentationEngine; import org.eclipse.e4.ui.workbench.UIEvents; import org.eclipse.e4.ui.workbench.modeling.EModelService; import org.eclipse.emf.ecore.EObject; import org.osgi.service.event.Event; import org.osgi.service.event.EventHandler; import at.bestsolution.efxclipse.runtime.services.theme.ThemeManager; import at.bestsolution.efxclipse.runtime.workbench.AbstractPartRenderer; import at.bestsolution.efxclipse.runtime.workbench.IRendererFactory; @SuppressWarnings({ "restriction" }) public class PartRenderingEngine implements IPresentationEngine { public static final String engineURI = "bundleclass://at.bestsolution.efxclipse.runtime.workbench/" + "at.bestsolution.efxclipse.runtime.workbench.internal.PartRenderingEngine"; private static final String defaultFactoryUrl = "bundleclass://at.bestsolution.efxclipse.runtime.workbench.renderers/" + "at.bestsolution.efxclipse.runtime.workbench.renderers.WorkbenchRendererFactory"; private final String factoryUrl; private IRendererFactory curFactory = null; private IEclipseContext appContext; @Inject protected Logger logger; @Inject @Optional protected IEventBroker eventBroker; @Inject @Optional EModelService modelService; private MApplication theApp; private Group limboContainer; @Inject @Optional ThemeManager themeManager; // Life Cycle handlers private EventHandler toBeRenderedHandler = new EventHandler() { public void handleEvent(Event event) { MUIElement changedElement = (MUIElement) event .getProperty(UIEvents.EventTags.ELEMENT); MElementContainer<?> parent = changedElement.getParent(); // Handle Detached Windows if (parent == null) { parent = (MElementContainer<?>) ((EObject) changedElement) .eContainer(); } boolean menuChild = parent instanceof MMenu; // If the parent isn't displayed who cares? if (!(parent instanceof MApplication) && (parent == null || parent.getWidget() == null || menuChild)) return; if (changedElement.isToBeRendered()) { Activator.trace(Policy.DEBUG_RENDERER, "visible -> true", null); //$NON-NLS-1$ // Note that the 'createGui' protocol calls 'childAdded' // Object w = createGui(changedElement); // if (w instanceof Control && !(w instanceof Shell)) { // fixZOrder(changedElement); // } } else { Activator .trace(Policy.DEBUG_RENDERER, "visible -> false", null); //$NON-NLS-1$ // Ensure that the element about to be removed is not the // selected element if (parent.getSelectedElement() == changedElement) parent.setSelectedElement(null); // Un-maximize the element before tearing it down if (changedElement.getTags().contains(MAXIMIZED)) changedElement.getTags().remove(MAXIMIZED); // Note that the 'removeGui' protocol calls 'childRemoved' removeGui(changedElement); } } }; private EventHandler childrenHandler = new EventHandler() { public void handleEvent(Event event) { Object changedObj = event.getProperty(UIEvents.EventTags.ELEMENT); if (!(changedObj instanceof MElementContainer<?>)) return; MElementContainer<MUIElement> changedElement = (MElementContainer<MUIElement>) changedObj; boolean isApplication = changedObj instanceof MApplication; boolean menuChild = changedObj instanceof MMenu; // If the parent isn't in the UI then who cares? AbstractPartRenderer renderer = getRendererFor(changedElement); if ((!isApplication && renderer == null) || menuChild) return; String eventType = (String) event .getProperty(UIEvents.EventTags.TYPE); if (UIEvents.EventTypes.ADD.equals(eventType)) { Activator.trace(Policy.DEBUG_RENDERER, "Child Added", null); //$NON-NLS-1$ MUIElement added = (MUIElement) event .getProperty(UIEvents.EventTags.NEW_VALUE); // OK, we have a new -visible- part we either have to create // it or host it under the correct parent. Note that we // explicitly do *not* render non-selected elements in // stacks (to support lazy loading). boolean isStack = changedObj instanceof MGenericStack<?>; boolean hasWidget = added.getWidget() != null; boolean isSelected = added == changedElement .getSelectedElement(); boolean renderIt = !isStack || hasWidget || isSelected; if (renderIt) { // NOTE: createGui will call 'childAdded' if successful Object w = createGui(added); // if (w instanceof Control && !(w instanceof Shell)) { // final Control ctrl = (Control) w; // fixZOrder(added); // if (!ctrl.isDisposed()) { // ctrl.getShell().layout(new Control[] { ctrl }, // SWT.DEFER); // } // } } else { if (renderer != null && added.isToBeRendered()) renderer.childRendered(changedElement, added); } // If the element being added is a placeholder, check to see if // it's 'globally visible' and, if so, remove all other // 'local' placeholders referencing the same element. int newLocation = modelService.getElementLocation(added); if (newLocation == EModelService.IN_SHARED_AREA || newLocation == EModelService.OUTSIDE_PERSPECTIVE) { MWindow topWin = modelService.getTopLevelWindowFor(added); modelService.hideLocalPlaceholders(topWin, null); } } else if (UIEvents.EventTypes.REMOVE.equals(eventType)) { Activator.trace(Policy.DEBUG_RENDERER, "Child Removed", null); //$NON-NLS-1$ MUIElement removed = (MUIElement) event .getProperty(UIEvents.EventTags.OLD_VALUE); // Removing invisible elements is a NO-OP as far as the // renderer is concerned if (!removed.isToBeRendered()) return; // if (removed.getWidget() instanceof Control) { // Control ctrl = (Control) removed.getWidget(); // ctrl.setLayoutData(null); // ctrl.getParent().layout(new Control[] { ctrl }, // SWT.CHANGED | SWT.DEFER); // } // Ensure that the element about to be removed is not the // selected element if (changedElement.getSelectedElement() == removed) changedElement.setSelectedElement(null); if (renderer != null) renderer.hideChild(changedElement, removed); } } }; @Inject public PartRenderingEngine(@Named(E4Workbench.RENDERER_FACTORY_URI) @Optional String factoryUrl) { if (factoryUrl == null) { factoryUrl = defaultFactoryUrl; } this.factoryUrl = factoryUrl; } @PostConstruct void initialize(IEclipseContext context) { this.appContext = context; String cssTheme = (String) appContext.get(E4Application.THEME_ID); if (themeManager != null && cssTheme != null) { themeManager.setCurrentThemeId(cssTheme); } // Add the renderer to the context context.set(IPresentationEngine.class.getName(), this); IRendererFactory factory = null; IContributionFactory contribFactory = context.get(IContributionFactory.class); try { factory = (IRendererFactory) contribFactory.create(factoryUrl, context); } catch (Exception e) { logger.warn(e, "Could not create rendering factory"); } // Try to load the default one if (factory == null) { try { factory = (IRendererFactory) contribFactory.create(defaultFactoryUrl, context); } catch (Exception e) { logger.error(e, "Could not create default rendering factory"); } } if (factory == null) { throw new IllegalStateException("Could not create any rendering factory. Aborting ..."); } curFactory = factory; context.set(IRendererFactory.class, curFactory); eventBroker.subscribe(UIEvents.UIElement.TOPIC_TOBERENDERED, toBeRenderedHandler); eventBroker.subscribe(UIEvents.ElementContainer.TOPIC_CHILDREN, childrenHandler); } @PreDestroy void contextDisposed() { if (eventBroker == null) { return; } } @SuppressWarnings("unchecked") public Object createGui(MUIElement element, Object parentWidget, IEclipseContext parentContext) { System.err.println("creating new element"); if (!element.isToBeRendered()) { return null; } Object currentWidget = element.getWidget(); if(currentWidget != null) { //TODO Port SWT code return currentWidget; } if (element instanceof MContext) { MContext ctxt = (MContext) element; // Assert.isTrue(ctxt.getContext() == null, // "Before rendering Context should be null"); if (ctxt.getContext() == null) { IEclipseContext lclContext = parentContext.createChild(getContextName(element)); populateModelInterfaces(ctxt, lclContext, element.getClass().getInterfaces()); ctxt.setContext(lclContext); // System.out.println("New Context: " + lclContext.toString() // + " parent: " + parentContext.toString()); // make sure the context knows about these variables that have // been defined in the model for (String variable : ctxt.getVariables()) { lclContext.declareModifiable(variable); } Map<String, String> props = ctxt.getProperties(); for (String key : props.keySet()) { lclContext.set(key, props.get(key)); } E4Workbench.processHierarchy(element); } } Object newWidget = createWidget(element, parentWidget); if (newWidget != null) { AbstractPartRenderer renderer = getRendererFor(element); // Have the renderer hook up any widget specific listeners renderer.hookControllerLogic(element); // Process its internal structure through the renderer that created // it if (element instanceof MElementContainer) { renderer.processContents((MElementContainer<MUIElement>) element); } // Allow a final chance to set up renderer.postProcess(element); // Now that we have a widget let the parent (if any) know if (element.getParent() instanceof MUIElement) { MElementContainer<MUIElement> parentElement = element.getParent(); AbstractPartRenderer parentRenderer = getRendererFor(parentElement); if (parentRenderer != null) { parentRenderer.childRendered(parentElement, element); } } } else { // failed to create the widget, dispose its context if necessary if (element instanceof MContext) { MContext ctxt = (MContext) element; IEclipseContext lclContext = ctxt.getContext(); if (lclContext != null) { lclContext.dispose(); ctxt.setContext(null); } } } return newWidget; } private static void populateModelInterfaces(MContext contextModel, IEclipseContext context, Class<?>[] interfaces) { for (Class<?> intf : interfaces) { // Activator.trace(Policy.DEBUG_CONTEXTS, // "Adding " + intf.getName() + " for " //$NON-NLS-1$ //$NON-NLS-2$ // + contextModel.getClass().getName(), null); context.set(intf.getName(), contextModel); populateModelInterfaces(contextModel, context, intf.getInterfaces()); } } private IEclipseContext getContext(MUIElement parent) { if (parent instanceof MContext) { return ((MContext) parent).getContext(); } return modelService.getContainingContext(parent); } protected AbstractPartRenderer getRendererFor(MUIElement element) { return (AbstractPartRenderer) element.getRenderer(); } public Object createGui(final MUIElement element) { // Obtain the necessary parent widget Object parent = null; MUIElement parentME = element.getParent(); if (parentME == null) { parentME = (MUIElement) ((EObject) element).eContainer(); } if (parentME != null) { AbstractPartRenderer renderer = getRendererFor(parentME); if (renderer != null) { if (!element.isVisible()) { parent = getLimboContainer(); } else { parent = renderer.getUIContainer(element); } } } // Obtain the necessary parent context IEclipseContext parentContext = null; if (element.getCurSharedRef() != null) { MPlaceholder ph = element.getCurSharedRef(); parentContext = getContext(ph.getParent()); } else if (parentContext == null && element.getParent() != null) { parentContext = getContext(element.getParent()); } else if (parentContext == null && element.getParent() == null) { parentContext = getContext((MUIElement) ((EObject) element).eContainer()); } return createGui(element, parent, parentContext); } private Object getLimboContainer() { if (limboContainer != null) { limboContainer = new Group(); } return limboContainer; } private String getContextName(MUIElement element) { StringBuilder builder = new StringBuilder(element.getClass().getSimpleName()); String elementId = element.getElementId(); if (elementId != null && elementId.length() != 0) { builder.append(" (").append(elementId).append(") "); } builder.append("Context"); return builder.toString(); } protected Object createWidget(MUIElement element, Object parent) { AbstractPartRenderer renderer = getRenderer(element, parent); if (renderer != null) { // Remember which renderer is responsible for this widget element.setRenderer(renderer); Object newWidget = renderer.createWidget(element, parent); if (newWidget != null) { renderer.bindWidget(element, newWidget); return newWidget; } } return null; } private AbstractPartRenderer getRenderer(MUIElement uiElement, Object parent) { return curFactory.getRenderer(uiElement, parent); } public void removeGui(MUIElement element) { // if (removeRoot == null) // removeRoot = element; // renderedElements.remove(element); // We call 'hideChild' *before* checking if the actual element // has been rendered in order to pick up cases of 'lazy loading' MUIElement parent = element.getParent(); AbstractPartRenderer parentRenderer = parent != null ? getRendererFor(parent) : null; if (parentRenderer != null) { parentRenderer.hideChild(element.getParent(), element); } AbstractPartRenderer renderer = getRendererFor(element); // If the element hasn't been rendered then this is a NO-OP if (renderer != null) { if (element instanceof MElementContainer<?>) { MElementContainer<MUIElement> container = (MElementContainer<MUIElement>) element; MUIElement selectedElement = container.getSelectedElement(); List<MUIElement> children = container.getChildren(); for (MUIElement child : children) { // remove stuff in the "back" first if (child != selectedElement) { removeGui(child); } } if (selectedElement != null && children.contains(selectedElement)) { // now remove the selected element removeGui(selectedElement); } } if (element instanceof MPerspective) { MPerspective perspective = (MPerspective) element; for (MWindow subWindow : perspective.getWindows()) { removeGui(subWindow); } } else if (element instanceof MWindow) { MWindow window = (MWindow) element; for (MWindow subWindow : window.getWindows()) { removeGui(subWindow); } if (window instanceof MTrimmedWindow) { MTrimmedWindow trimmedWindow = (MTrimmedWindow) window; for (MUIElement trimBar : trimmedWindow.getTrimBars()) { removeGui(trimBar); } } } renderer.disposeWidget(element); // unset the client object if (element instanceof MContribution) { MContribution contribution = (MContribution) element; Object client = contribution.getObject(); IEclipseContext parentContext = renderer.getContext(element); if (parentContext != null && client != null) { try { ContextInjectionFactory.uninject(client, parentContext); } catch (Exception e) { if (logger != null) { logger.error(e); } } } contribution.setObject(null); } // dispose the context if (element instanceof MContext) { clearContext((MContext) element); } } // if (removeRoot == element) // removeRoot = null; } private void clearContext(MContext contextME) { MContext ctxt = (MContext) contextME; IEclipseContext lclContext = ctxt.getContext(); if (lclContext != null) { IEclipseContext parentContext = lclContext.getParent(); IEclipseContext child = parentContext.getActiveChild(); if (child == lclContext) { child.deactivate(); } ctxt.setContext(null); lclContext.dispose(); } } public Object run(MApplicationElement uiRoot, IEclipseContext appContext) { if (uiRoot instanceof MApplication) { theApp = (MApplication) uiRoot; MWindow selected = theApp.getSelectedElement(); if (selected == null) { for (MWindow window : theApp.getChildren()) { createGui(window); } } else { // render the selected one first createGui(selected); for (MWindow window : theApp.getChildren()) { if (selected != window) { createGui(window); } } } } return null; } public void stop() { // TODO Auto-generated method stub } }