From ba762b0dee4358c59a878ec94727c0379c77c727 Mon Sep 17 00:00:00 2001
From: Florian Hoelzl <hoelzl@fortiss.org>
Date: Tue, 26 Nov 2019 13:49:24 +0100
Subject: [PATCH] Kernel: Added FX image load utility and elliptic content
 controller.

Issue-Ref: 3873
Issue-Url: https://af3-developer.fortiss.org/issues/3815
Signed-off-by: Florian Hoelzl <hoelzl@fortiss.org>
---
 .../tooling/base/ui/editor/fx/.ratings        |   1 +
 ...llipticResizableContentControllerBase.java | 146 ++++++++++++++++++
 .../fortiss/tooling/base/ui/utils/.ratings    |   2 +-
 .../base/ui/utils/LWFXEditorUtils.java        |  34 ++++
 4 files changed, 182 insertions(+), 1 deletion(-)
 create mode 100644 org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/EObjectBasedEllipticResizableContentControllerBase.java

diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/.ratings b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/.ratings
index e65316624..2af896f26 100644
--- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/.ratings
+++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/.ratings
@@ -1,5 +1,6 @@
 ContextMenuUtil.java 3e04dbd52ee99a59735d39b4087c55b07fb91076 GREEN
 EObjectBasedDiagramControllerBase.java b25bbbaa6eef3878b9aea73ca67d66de9beebb18 GREEN
+EObjectBasedEllipticResizableContentControllerBase.java 7d04a93fdce63e0542e78433cc3da7e112ed1b88 YELLOW
 EObjectBasedRectangularResizableContentControllerBase.java a263a6c123ee96015e0a5c37b6e3c13ebd0e8a75 GREEN
 HierarchicModelElementModelChangeProvider.java 05217577a99d78038763bbcd841a032bb84f0553 GREEN
 KernelServiceBasedModelChangeProviderBase.java 7c30347fb0c7cc4aacc79aeaf85167872afc1958 GREEN
diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/EObjectBasedEllipticResizableContentControllerBase.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/EObjectBasedEllipticResizableContentControllerBase.java
new file mode 100644
index 000000000..7d04a93fd
--- /dev/null
+++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/EObjectBasedEllipticResizableContentControllerBase.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2017, 2018 fortiss GmbH. 
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Florian Hoelzl (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.fortiss.tooling.base.ui.editor.fx;
+
+import static java.lang.Math.max;
+import static org.fortiss.tooling.base.ui.editor.fx.ContextMenuUtil.createElementCompositionContext;
+import static org.fortiss.tooling.base.ui.editor.fx.ContextMenuUtil.createPrototypeMenu;
+
+import java.util.List;
+
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramCoordinate;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerFeatures;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.change.Change;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IClickController;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.base.ClickControllerBase;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.elliptic.EllipticResizableContentControllerBase;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.rectangular.RectangularResizableContentControllerBase;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelChangeProvider;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle;
+import org.fortiss.tooling.base.model.element.ElementPackage;
+import org.fortiss.tooling.base.model.layout.ILayoutedModelElement;
+import org.fortiss.tooling.kernel.extension.data.IElementCompositionContext;
+import org.fortiss.tooling.kernel.service.IElementCompositorService;
+import org.fortiss.tooling.kernel.ui.extension.IModelElementHandler;
+import org.fortiss.tooling.kernel.ui.service.IModelEditorBindingService;
+import org.fortiss.tooling.kernel.ui.service.IModelElementHandlerService;
+
+import javafx.scene.Node;
+import javafx.scene.control.MenuItem;
+import javafx.scene.input.MouseEvent;
+
+/**
+ * {@link RectangularResizableContentControllerBase} with a listener mechanism for the layout of the
+ * connected {@link EObject model element}.
+ * 
+ * @author hoelzl
+ */
+public abstract class EObjectBasedEllipticResizableContentControllerBase
+		extends EllipticResizableContentControllerBase {
+	/** {@link IModelChangeProvider} for this controller */
+	private final LayoutModelElementModelChangeProvider layoutModelChangeProvider;
+	/** The click controller handling double-click to open editor. */
+	private final IClickController openEditorDoubleClickController = new ClickControllerBase() {
+		@Override
+		public Change singleClick(MouseEvent event, Node node, DiagramCoordinate diagramLocation) {
+			return EObjectBasedEllipticResizableContentControllerBase.super.getClickController(node,
+					diagramLocation).singleClick(event, node, diagramLocation);
+		}
+
+		@Override
+		public Change secondaryClick(MouseEvent event, Node node,
+				DiagramCoordinate diagramLocation) {
+			return EObjectBasedEllipticResizableContentControllerBase.super.getClickController(node,
+					diagramLocation).secondaryClick(event, node, diagramLocation);
+		}
+
+		@Override
+		public Change doubleClick(MouseEvent event, Node node, DiagramCoordinate diagramLocation) {
+			EObject eo = getLayoutedME();
+			IModelElementHandler<EObject> handler =
+					IModelElementHandlerService.getInstance().getModelElementHandler(eo);
+			if(handler != null) {
+				eo = handler.handleOpenModelElementRequest(eo);
+			}
+			IModelEditorBindingService.getInstance().openInEditor(eo);
+			return null; // no changes to model
+		}
+	};
+
+	/** Constructor. */
+	public EObjectBasedEllipticResizableContentControllerBase(IContentMVCBundle mvcb) {
+		super(mvcb);
+		if(!(mvcb.getModel() instanceof ILayoutedModelElement)) {
+			throw new IllegalArgumentException(
+					"Model element must be an instance of an ILayoutedModelElement.");
+		}
+		ILayoutedModelElement lme = (ILayoutedModelElement)mvcb.getModel();
+		this.layoutModelChangeProvider = new LayoutModelElementModelChangeProvider(lme) {
+			/** {@inheritDoc} */
+			@Override
+			protected boolean acceptNotification(Notification notification) {
+				if(notification.getNotifier() != getLayoutedME()) {
+					return false;
+				}
+				return isAnchorageFeature(notification.getFeature());
+			}
+		};
+	}
+
+	/** Returns the {@link ILayoutedModelElement} model element. */
+	protected ILayoutedModelElement getLayoutedME() {
+		// wild cast works: see constructor check with exception
+		return (ILayoutedModelElement)getModel();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public IModelChangeProvider getModelChangeProvider() {
+		return layoutModelChangeProvider;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<MenuItem> contextMenuContributions(Node node, DiagramCoordinate diagramLocation) {
+		// outer-most grid areas of the diagram are inaccessible
+		DiagramViewerFeatures features = getViewer().getFeatures();
+		double x = max(features.getHorizontalSpacing(), diagramLocation.getX());
+		double y = max(features.getVerticalSpacing(), diagramLocation.getY());
+
+		// wild cast works: see constructor exception
+		EObject modelParent = (EObject)getModel();
+		IElementCompositionContext edc = createElementCompositionContext(modelParent, x, y, false,
+				getViewer().getFeatures().getCurrentZoomFactor());
+		return createPrototypeMenu(modelParent, edc);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void delete() {
+		IElementCompositorService.getInstance().decompose(getLayoutedME());
+	}
+
+	/** Checks whether the given feature corresponds to an anchorage model element. */
+	protected boolean isAnchorageFeature(Object feature) {
+		return feature == ElementPackage.Literals.IHIERARCHIC_ELEMENT__CONNECTORS;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public IClickController getClickController(Node node, DiagramCoordinate diagramLocation) {
+		return openEditorDoubleClickController;
+	}
+}
diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/utils/.ratings b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/utils/.ratings
index 27fbc6fb9..c0c902300 100644
--- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/utils/.ratings
+++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/utils/.ratings
@@ -5,7 +5,7 @@ DragAndDropBaseUtils.java d375377f9124f6113b2a295e6b0e09ac8966e564 GREEN
 EllipseLayoutUIUtils.java 4dd9dbd96a45e8c455c019caa19e4a50f18336af GREEN
 FontUtils.java a167a05bdaa8da9853705cc5134f30f6d81bc9f2 GREEN
 GCStateManager.java 983973a92376b5c757c1253b32e33d0666ccdf7b GREEN
-LWFXEditorUtils.java c628e79fa43c1be8a7b87ee90db6e7264904eafc GREEN
+LWFXEditorUtils.java c6a08711872a74aa52078bba25a3a4adeddc348c YELLOW
 LayoutDataUIUtils.java c85886ac313a6efb122532218eb134047ffd6631 GREEN
 PropertiesViewUtils.java d345b4501c4092228edf1c98e0189317d53aaf22 GREEN
 RectangleLayoutUIUtils.java ef4b872bb5b4a51174e9a29d9ef05e7cb3bff3a1 GREEN
diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/utils/LWFXEditorUtils.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/utils/LWFXEditorUtils.java
index c628e79fa..c6a087118 100644
--- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/utils/LWFXEditorUtils.java
+++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/utils/LWFXEditorUtils.java
@@ -19,12 +19,18 @@ import static java.lang.Math.atan2;
 import static java.lang.Math.cos;
 import static java.lang.Math.sin;
 
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
+
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramCoordinate;
 import org.fortiss.tooling.base.model.layout.EOrientation;
 
 import javafx.geometry.Point2D;
 import javafx.geometry.Rectangle2D;
 import javafx.geometry.Side;
+import javafx.scene.image.Image;
+import javafx.scene.paint.Color;
 
 /**
  * Utility methods for the JavaFX-based graphical LWFXEF editors.
@@ -99,6 +105,34 @@ public final class LWFXEditorUtils {
 		return new DiagramCoordinate(nx, ny);
 	}
 
+	/** The image cache of loaded images. */
+	private static final HashMap<String, Image> imageCache = new HashMap<>();
+
+	/** Returns the Java FX Image load from the local path. */
+	public static Image getFXImage(String pluginId, String localPath) {
+		String uri = "platform:/plugin/" + pluginId + localPath;
+		Image cacheImage = imageCache.get(uri);
+		if(cacheImage == null) {
+			InputStream in = null;
+			try {
+				in = new URL(uri).openStream();
+			} catch(Exception e) {
+				e.printStackTrace();
+			}
+			if(in != null) {
+				cacheImage = new Image(in);
+			}
+			imageCache.put(uri, cacheImage);
+		}
+		return cacheImage;
+	}
+
+	/** Converts the given SWT color to Java FX color. */
+	public static Color convertColor(org.eclipse.swt.graphics.Color swtColor) {
+		return Color.rgb(swtColor.getRed(), swtColor.getGreen(), swtColor.getBlue(),
+				swtColor.getAlpha() / 255.0);
+	}
+
 	/** Constructor. */
 	private LWFXEditorUtils() {
 		// prevent instantiation
-- 
GitLab