Bug 551020: Keyword to register dynamic views

  the keyword 'view' was added
  example: view: MyCategory/View Title

Change-Id: I9828c79ebf6a50e85425367c6fdf7ea5151dcae4
diff --git a/plugins/org.eclipse.ease.modules.platform/META-INF/MANIFEST.MF b/plugins/org.eclipse.ease.modules.platform/META-INF/MANIFEST.MF
index d725fbe..8ee3c9a 100644
--- a/plugins/org.eclipse.ease.modules.platform/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.ease.modules.platform/META-INF/MANIFEST.MF
@@ -1,5 +1,6 @@
 Manifest-Version: 1.0
-Export-Package: org.eclipse.ease.modules.platform
+Export-Package: org.eclipse.ease.modules.platform,
+ org.eclipse.ease.modules.platform.keywords
 Require-Bundle: org.eclipse.ui;bundle-version="[3.7.0,4.0.0)",
  org.eclipse.ease,
  org.eclipse.core.resources;bundle-version="[3.7.101,4.0.0)",
@@ -20,7 +21,8 @@
  org.eclipse.e4.core.services;bundle-version="[1.2.1,3.0.0)",
  org.eclipse.osgi.services;bundle-version="[3.4.0,4.0.0)",
  org.eclipse.e4.ui.model.workbench,
- org.eclipse.e4.ui.workbench
+ org.eclipse.e4.ui.workbench,
+ org.eclipse.ease.ui.scripts;bundle-version="0.8.0"
 Bundle-Version: 0.8.0.qualifier
 Bundle-Name: Platform modules (Incubation)
 Bundle-ManifestVersion: 2
diff --git a/plugins/org.eclipse.ease.modules.platform/plugin.xml b/plugins/org.eclipse.ease.modules.platform/plugin.xml
index b385ce4..5d50d58 100644
--- a/plugins/org.eclipse.ease.modules.platform/plugin.xml
+++ b/plugins/org.eclipse.ease.modules.platform/plugin.xml
@@ -37,6 +37,13 @@
             name="UI Builder"
             visible="true">
       </module>
+      <module
+            category="org.eclipse.ease.category.system"
+            class="org.eclipse.ease.modules.platform.debug.LaunchModule"
+            id="org.eclipse.ease.modules.debug.launch"
+            name="Launch"
+            visible="true">
+      </module>
    </extension>
    <extension
          point="org.eclipse.ease.ui.shell">
@@ -50,22 +57,26 @@
       <codeCompletionProvider
             class="org.eclipse.ease.modules.platform.completion.ResourcesCompletionProvider">
       </codeCompletionProvider>
-   </extension>
-   <extension
-         point="org.eclipse.ease.modules">
-      <module
-            category="org.eclipse.ease.category.system"
-            class="org.eclipse.ease.modules.platform.debug.LaunchModule"
-            id="org.eclipse.ease.modules.debug.launch"
-            name="Launch"
-            visible="true">
-      </module>
-   </extension>
-   <extension
-         point="org.eclipse.ease.ui.codeCompletionProvider">
       <codeCompletionProvider
             class="org.eclipse.ease.modules.platform.debug.LaunchModuleCompletionProvider">
       </codeCompletionProvider>
    </extension>
+   <extension
+         point="org.eclipse.ease.ui.scripts.keyword">
+      <handler
+            class="org.eclipse.ease.modules.platform.keywords.RegisterView"
+            description="Allows to register a script that populates a scripted view that uses the UI Builder module. Your script needs to call &apos;pushComposite(view.getComposite())&apos;"
+            id="org.eclipse.ease.modules.platform.registerViewHandler"
+            keywords="view"
+            name="view">
+      </handler>
+   </extension>
+   <extension
+         point="org.eclipse.ui.views">
+      <category
+            id="org.eclipse.ease.modules.platform.scriptedViewsCategory"
+            name="Scripted Views">
+      </category>
+   </extension>
 
 </plugin>
diff --git a/plugins/org.eclipse.ease.modules.platform/src/org/eclipse/ease/modules/platform/keywords/RegisterView.java b/plugins/org.eclipse.ease.modules.platform/src/org/eclipse/ease/modules/platform/keywords/RegisterView.java
new file mode 100644
index 0000000..a9e3e1b
--- /dev/null
+++ b/plugins/org.eclipse.ease.modules.platform/src/org/eclipse/ease/modules/platform/keywords/RegisterView.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Christian Pontesegger and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * Contributors:
+ *     Christian Pontesegger - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.ease.modules.platform.keywords;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.e4.ui.model.application.MApplication;
+import org.eclipse.e4.ui.model.application.descriptor.basic.MPartDescriptor;
+import org.eclipse.e4.ui.workbench.IWorkbench;
+import org.eclipse.e4.ui.workbench.modeling.EModelService;
+import org.eclipse.ease.modules.platform.PluginConstants;
+import org.eclipse.ease.modules.platform.uibuilder.UIBuilderModule;
+import org.eclipse.ease.ui.scripts.repository.IScript;
+import org.eclipse.ui.PlatformUI;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+
+public class RegisterView implements EventHandler {
+
+	public static MApplication getApplication() {
+		return PlatformUI.getWorkbench().getService(IWorkbench.class).getApplication();
+	}
+
+	private static MPartDescriptor createPartDescriptor() {
+		final EModelService modelService = PlatformUI.getWorkbench().getService(EModelService.class);
+		return modelService.createModelElement(MPartDescriptor.class);
+	}
+
+	private static String getCategory(String viewName) {
+		final IPath viewPath = new Path(viewName);
+		return (viewPath.segmentCount() >= 2) ? viewPath.segment(0) : "Scripted Views";
+	}
+
+	private static String getLabel(String viewName) {
+		final IPath viewPath = new Path(viewName);
+		return viewPath.lastSegment();
+	}
+
+	@Override
+	public void handleEvent(Event event) {
+		final IScript script = (IScript) event.getProperty("script");
+
+		final String value = (String) event.getProperty("value");
+		final String oldValue = (String) event.getProperty("oldValue");
+
+		if ((oldValue != null) && (!oldValue.isEmpty()))
+			removeContribution(script, oldValue);
+
+		if ((value != null) && (!value.isEmpty()))
+			addContribution(script, value);
+
+	}
+
+	private void addContribution(IScript script, String viewName) {
+		String iconUri = script.getKeywords().get("image");
+		iconUri = (iconUri == null) ? "platform:/plugin/" + PluginConstants.PLUGIN_ID + "/icons/eview16/scripted_view.png" : iconUri;
+
+		final MPartDescriptor partDescriptor = createPartDescriptor();
+		partDescriptor.setCategory(getCategory(viewName));
+		partDescriptor.setAllowMultiple(false);
+		partDescriptor.setCloseable(true);
+		partDescriptor.setLabel(getLabel(viewName));
+		partDescriptor.setIconURI(iconUri);
+		partDescriptor.setContributionURI("bundleclass://" + PluginConstants.PLUGIN_ID + "/" + ScriptedView.class.getName());
+		partDescriptor.setElementId(UIBuilderModule.getDynamicViewId());
+		partDescriptor.getProperties().put("script", script.getPath().toString());
+
+		// needed to be displayed in the Show views dialog
+		partDescriptor.getTags().add("View");
+
+		// do not store permanently in the workbench model
+		partDescriptor.getPersistedState().put(IWorkbench.PERSIST_STATE, Boolean.FALSE.toString());
+
+		getApplication().getDescriptors().add(partDescriptor);
+	}
+
+	private void removeContribution(IScript script, String viewName) {
+		final String category = getCategory(viewName);
+		final String label = getLabel(viewName);
+
+		for (final MPartDescriptor descriptor : getApplication().getDescriptors()) {
+			if ((label.equals(descriptor.getLabel())) && (category.equals(descriptor.getCategory()))) {
+				getApplication().getDescriptors().remove(descriptor);
+				break;
+			}
+		}
+	}
+}
diff --git a/plugins/org.eclipse.ease.modules.platform/src/org/eclipse/ease/modules/platform/keywords/ScriptedView.java b/plugins/org.eclipse.ease.modules.platform/src/org/eclipse/ease/modules/platform/keywords/ScriptedView.java
new file mode 100644
index 0000000..54a0e0e
--- /dev/null
+++ b/plugins/org.eclipse.ease.modules.platform/src/org/eclipse/ease/modules/platform/keywords/ScriptedView.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Christian Pontesegger and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * Contributors:
+ *     Christian Pontesegger - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.ease.modules.platform.keywords;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.ease.ui.scripts.repository.IRepositoryService;
+import org.eclipse.ease.ui.scripts.repository.IScript;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IWorkbenchListener;
+import org.eclipse.ui.PlatformUI;
+
+public class ScriptedView {
+
+	private Composite fParent;
+
+	@Inject
+	public ScriptedView(MPart part) {
+		final IRepositoryService repositoryService = PlatformUI.getWorkbench().getService(IRepositoryService.class);
+		final IScript script = repositoryService.getScript(part.getProperties().get("script"));
+
+		createPartControl((Composite) part.getWidget(), script);
+
+		// make sure to close this window before we terminate. Eclipse would store it in its layout actually destroying all layout data.
+		final EPartService partService = PlatformUI.getWorkbench().getService(EPartService.class);
+		PlatformUI.getWorkbench().addWorkbenchListener(new IWorkbenchListener() {
+
+			@Override
+			public boolean preShutdown(org.eclipse.ui.IWorkbench workbench, boolean forced) {
+				partService.hidePart(part, true);
+				return true;
+			}
+
+			@Override
+			public void postShutdown(org.eclipse.ui.IWorkbench workbench) {
+			}
+		});
+	}
+
+	public void createPartControl(Composite parent, IScript script) {
+		fParent = parent;
+
+		final Map<String, Object> parameters = new HashMap<>();
+		parameters.put("view", this);
+		parameters.put("viewComposite", parent);
+
+		script.run(parameters);
+	}
+
+	/**
+	 * Get the root composite for this view.
+	 *
+	 * @return root composite
+	 */
+	public Composite getComposite() {
+		return fParent;
+	}
+}
diff --git a/plugins/org.eclipse.ease.modules.platform/src/org/eclipse/ease/modules/platform/uibuilder/UIBuilderModule.java b/plugins/org.eclipse.ease.modules.platform/src/org/eclipse/ease/modules/platform/uibuilder/UIBuilderModule.java
index ad61df2..317ed43 100644
--- a/plugins/org.eclipse.ease.modules.platform/src/org/eclipse/ease/modules/platform/uibuilder/UIBuilderModule.java
+++ b/plugins/org.eclipse.ease.modules.platform/src/org/eclipse/ease/modules/platform/uibuilder/UIBuilderModule.java
@@ -25,6 +25,7 @@
 import org.eclipse.ease.modules.AbstractScriptModule;
 import org.eclipse.ease.modules.ScriptParameter;
 import org.eclipse.ease.modules.WrapToScript;
+import org.eclipse.ease.modules.platform.PluginConstants;
 import org.eclipse.ease.modules.platform.UIModule;
 import org.eclipse.ease.tools.RunnableWithResult;
 import org.eclipse.ease.ui.tools.LocationImageDescriptor;
@@ -120,6 +121,54 @@
 
 	private static int fCounter = 1;
 
+	public static String getDynamicViewId() {
+		return "org.eclipse.ease.view.dynamic." + fCounter++;
+	}
+
+	public static MPart createDynamicPart(String title, String iconUri) throws Throwable {
+		final RunnableWithResult<MPart> runnable = new RunnableWithResult<MPart>() {
+
+			@Override
+			public MPart runWithTry() throws Throwable {
+				final EPartService partService = PlatformUI.getWorkbench().getService(EPartService.class);
+
+				// create part
+				final MPart part = MBasicFactory.INSTANCE.createPart();
+				part.setLabel(title);
+				if (iconUri != null)
+					part.setIconURI(iconUri);
+				else
+					part.setIconURI("platform:/plugin/" + PluginConstants.PLUGIN_ID + "/icons/eview16/scripted_view.png");
+
+				part.setElementId(getDynamicViewId());
+				part.setCloseable(true);
+				part.getPersistedState().put(IWorkbench.PERSIST_STATE, Boolean.FALSE.toString());
+
+				partService.showPart(part, PartState.VISIBLE);
+
+				// make sure to close this window before we terminate. Eclipse would store it in its layout actually destroying all layout data.
+				PlatformUI.getWorkbench().addWorkbenchListener(new IWorkbenchListener() {
+
+					@Override
+					public boolean preShutdown(org.eclipse.ui.IWorkbench workbench, boolean forced) {
+						partService.hidePart(part, true);
+						return true;
+					}
+
+					@Override
+					public void postShutdown(org.eclipse.ui.IWorkbench workbench) {
+					}
+				});
+
+				return part;
+			}
+		};
+
+		Display.getDefault().syncExec(runnable);
+
+		return runnable.getResultOrThrow();
+	}
+
 	/** Holds viewModel, renderer, and further elements for created composites. */
 	private final List<UICompositor> fUICompositors = new ArrayList<>();
 
@@ -158,53 +207,15 @@
 	public MPart createView(String title, @ScriptParameter(defaultValue = ScriptParameter.NULL) String iconUri,
 			@ScriptParameter(defaultValue = ScriptParameter.NULL) String relativeTo, @ScriptParameter(defaultValue = "x") String position) throws Throwable {
 
-		final RunnableWithResult<MPart> runnable = new RunnableWithResult<MPart>() {
+		final MPart part = createDynamicPart(title, iconUri);
 
-			@Override
-			public MPart runWithTry() throws Throwable {
-				final EPartService partService = PlatformUI.getWorkbench().getService(EPartService.class);
+		if (relativeTo != null)
+			UIModule.moveView(part.getElementId(), relativeTo, position);
 
-				// create part
-				final MPart part = MBasicFactory.INSTANCE.createPart();
-				part.setLabel(title);
-				if (iconUri != null)
-					part.setIconURI(iconUri);
-				else
-					part.setIconURI("platform:/plugin/org.eclipse.ease.modules.platform/icons/eview16/scripted_view.png");
+		fUICompositors.clear();
+		pushComposite((Composite) part.getWidget());
 
-				part.setElementId("org.eclipse.ease.view.dynamic:" + fCounter++);
-				part.setCloseable(true);
-				part.getPersistedState().put(IWorkbench.PERSIST_STATE, Boolean.FALSE.toString());
-
-				partService.showPart(part, PartState.VISIBLE);
-
-				if (relativeTo != null)
-					UIModule.moveView("org.eclipse.ease.view.dynamic:" + (fCounter - 1), relativeTo, position);
-
-				fUICompositors.clear();
-				pushComposite((Composite) part.getWidget());
-
-				// make sure to close this window before we terminate. Eclipse would store it in its layout actually destroying all layout data.
-				PlatformUI.getWorkbench().addWorkbenchListener(new IWorkbenchListener() {
-
-					@Override
-					public boolean preShutdown(org.eclipse.ui.IWorkbench workbench, boolean forced) {
-						partService.hidePart(part, true);
-						return true;
-					}
-
-					@Override
-					public void postShutdown(org.eclipse.ui.IWorkbench workbench) {
-					}
-				});
-
-				return part;
-			}
-		};
-
-		Display.getDefault().syncExec(runnable);
-
-		return runnable.getResultOrThrow();
+		return part;
 	}
 
 	/**