diff --git a/org.fortiss.tooling.base.ui/META-INF/MANIFEST.MF b/org.fortiss.tooling.base.ui/META-INF/MANIFEST.MF index 2492dbdeffc8235f398f3633019d7c9d9dcbe6f0..6b37b9d9578f382a1d425e57f9fe1f65a5b6ae75 100644 --- a/org.fortiss.tooling.base.ui/META-INF/MANIFEST.MF +++ b/org.fortiss.tooling.base.ui/META-INF/MANIFEST.MF @@ -23,7 +23,6 @@ Export-Package: org.fortiss.tooling.base.ui, org.fortiss.tooling.base.ui.dnd.jface, org.fortiss.tooling.base.ui.editor, org.fortiss.tooling.base.ui.editor.annotations, - org.fortiss.tooling.base.ui.editor.fx, org.fortiss.tooling.base.ui.editor.fx.controller, org.fortiss.tooling.base.ui.editor.fx.model, org.fortiss.tooling.base.ui.editor.fx.visual, 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 deleted file mode 100644 index 21108b1edd907c85abeb1a45d9b92e0fcc5e7941..0000000000000000000000000000000000000000 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/.ratings +++ /dev/null @@ -1,9 +0,0 @@ -ContextMenuUtil.java 3e04dbd52ee99a59735d39b4087c55b07fb91076 GREEN -EObjectBasedDiagramControllerBase.java 3564ece9b3cac29caaf9201e0be3451a6a3c2b21 GREEN -EObjectBasedRectangularResizableContentControllerBase.java a263a6c123ee96015e0a5c37b6e3c13ebd0e8a75 GREEN -EObjectModelChangeProvider.java 664e4db2e32124832682aff8235ac380ed4db1c0 GREEN -KernelServiceBasedModelChangeProviderBase.java 7c30347fb0c7cc4aacc79aeaf85167872afc1958 GREEN -LayoutModelElementModelChangeProvider.java 70c50b789c0088a7e5363bf662bc707a5b5ddf10 GREEN -LayoutedModelElementBasedContentAnchorageController.java c4dfb9dbcd16078c63321d5dec13a902e4088949 GREEN -LayoutedModelElementBasedDiagramAnchorageController.java 8e66f7d8207d2b6a03abaa151f94b51449ad66c7 GREEN -LayoutedModelElementBasedLinkBendPointController.java eb00b6119c02199b53b1a8e3cee2687f835b727c GREEN diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/.ratings b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/.ratings index 4811e9c2991d2e451beef74b3519eb1eeb70d8b0..1261d17460077cfb5a3faca399aa9f9321a5e0ac 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/.ratings +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/.ratings @@ -1,10 +1,10 @@ -ContextMenuUtil.java 7be87ce47b775d90c533078e22d4d445d9864caf GREEN -EObjectDiagramController.java 66b14c1c77953cad7caecae1e04f455958943f28 GREEN -EObjectModelChangeProvider.java f4b60cebb088a5c81ca92a41614e1a5d40030502 GREEN -EObjectRectangularResizableContentControllerBase.java 787a540213f29e8daaecd9afe98af8b3f4088db7 GREEN -KernelServiceBasedModelChangeProviderBase.java 18e48f17ea8dfba90de024a8959fc5a4b0d05d45 GREEN -LayoutModelChangeProvider.java b5449d02eaf39086909720c43e21bd061005fc9e GREEN -LayoutedContentAnchorageController.java 3794b41d76e9ce14ead0bd812cde5c1a6d348d5c GREEN -LayoutedDiagramAnchorageController.java 1e1ac7c5fa26c632736f5023e90f05d09bc0710d GREEN -LayoutedLinkBendPointController.java a8372485ae96f2abf773d1baeb1f8c7b2b25985f GREEN -LayoutedRectangularResizableContentController.java 1e18af3ee10dd3754325ed389fed664da65a0b61 GREEN +ContextMenuUtil.java 36184cc3f383d294463336a8318c61e98fd7d2d4 YELLOW +EObjectDiagramController.java 9af59e8e586c8251d174108a2ce2fcdee5e75782 YELLOW +EObjectModelChangeProvider.java f4b60cebb088a5c81ca92a41614e1a5d40030502 YELLOW +EObjectRectangularResizableContentControllerBase.java f4a967591a60fadb20550ec3eaabccf240c9ec0d YELLOW +KernelServiceBasedModelChangeProviderBase.java 8d1f8ef79ecd383ff74e5a2bbcf24345aabe70af YELLOW +LayoutModelChangeProvider.java b5449d02eaf39086909720c43e21bd061005fc9e YELLOW +LayoutedContentAnchorageController.java 73b103c06edcbf1762654e71a3e524f43c0c50c0 YELLOW +LayoutedDiagramAnchorageController.java 32d7d77daf252d021458c39ebcfe502f26f29a98 YELLOW +LayoutedLinkBendPointController.java 6d5de856f513ca82eab805a0ad9cda8194be011d YELLOW +LayoutedRectangularResizableContentController.java 3232d423572924363702898cf8ba240ce7042b65 YELLOW diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/ContextMenuUtil.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/ContextMenuUtil.java index 7be87ce47b775d90c533078e22d4d445d9864caf..36184cc3f383d294463336a8318c61e98fd7d2d4 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/ContextMenuUtil.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/ContextMenuUtil.java @@ -21,8 +21,8 @@ import java.util.ArrayList; import java.util.List; import org.eclipse.emf.ecore.EObject; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewer; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerFeatures; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures; import org.fortiss.tooling.base.dnd.ElementDropContext; import org.fortiss.tooling.base.model.layout.Point; import org.fortiss.tooling.kernel.extension.data.IElementCompositionContext; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/EObjectDiagramController.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/EObjectDiagramController.java index 66b14c1c77953cad7caecae1e04f455958943f28..9af59e8e586c8251d174108a2ce2fcdee5e75782 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/EObjectDiagramController.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/EObjectDiagramController.java @@ -22,11 +22,11 @@ import static org.fortiss.tooling.base.ui.editor.fx.controller.ContextMenuUtil.c import java.util.List; 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.controller.base.ControllerBase; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelChangeProvider; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.ControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelChangeProvider; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; import org.fortiss.tooling.kernel.extension.data.IElementCompositionContext; import org.fortiss.tooling.kernel.service.IElementCompositorService; import org.fortiss.tooling.kernel.ui.service.IContextMenuService; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/EObjectRectangularResizableContentControllerBase.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/EObjectRectangularResizableContentControllerBase.java index 787a540213f29e8daaecd9afe98af8b3f4088db7..f4a967591a60fadb20550ec3eaabccf240c9ec0d 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/EObjectRectangularResizableContentControllerBase.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/EObjectRectangularResizableContentControllerBase.java @@ -24,19 +24,19 @@ 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.IDragController; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.base.ClickControllerBase; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.rectangular.RectangularContentAnchorageMoveController; -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.IContentAnchorageMVCBundle; -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.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IClickController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.ClickControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.rectangular.RectangularContentAnchorageMoveController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.rectangular.RectangularResizableContentControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelChangeProvider; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; import org.fortiss.tooling.kernel.extension.data.IElementCompositionContext; import org.fortiss.tooling.kernel.service.IElementCompositorService; import org.fortiss.tooling.kernel.ui.extension.IModelElementHandler; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/KernelServiceBasedModelChangeProviderBase.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/KernelServiceBasedModelChangeProviderBase.java index 18e48f17ea8dfba90de024a8959fc5a4b0d05d45..8d1f8ef79ecd383ff74e5a2bbcf24345aabe70af 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/KernelServiceBasedModelChangeProviderBase.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/KernelServiceBasedModelChangeProviderBase.java @@ -23,8 +23,8 @@ import org.eclipse.emf.common.command.CommandStackListener; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EObject; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelChangeProvider; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.ModelChangeProviderBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelChangeProvider; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.ModelChangeProviderBase; import org.fortiss.tooling.kernel.extension.data.ITopLevelElement; import org.fortiss.tooling.kernel.service.ICommandStackService; import org.fortiss.tooling.kernel.service.IPersistencyService; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedContentAnchorageController.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedContentAnchorageController.java index 3794b41d76e9ce14ead0bd812cde5c1a6d348d5c..73b103c06edcbf1762654e71a3e524f43c0c50c0 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedContentAnchorageController.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedContentAnchorageController.java @@ -14,16 +14,16 @@ package org.fortiss.tooling.base.ui.editor.fx.controller; import static java.util.Objects.requireNonNull; -import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_ALLOWED_TAG; -import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_DENIED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_ALLOWED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_DENIED_TAG; 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.controller.base.DelegatingContentAnchorageController; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelChangeProvider; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.MVCBundleTag; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.DelegatingContentAnchorageController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelChangeProvider; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.MVCBundleTag; import org.fortiss.tooling.base.model.element.IConnector; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; import org.fortiss.tooling.kernel.service.IConnectionCompositorService; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedDiagramAnchorageController.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedDiagramAnchorageController.java index 1e1ac7c5fa26c632736f5023e90f05d09bc0710d..32d7d77daf252d021458c39ebcfe502f26f29a98 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedDiagramAnchorageController.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedDiagramAnchorageController.java @@ -14,17 +14,17 @@ package org.fortiss.tooling.base.ui.editor.fx.controller; import static java.util.Objects.requireNonNull; -import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_ALLOWED_TAG; -import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_DENIED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_ALLOWED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_DENIED_TAG; import static org.fortiss.tooling.base.utils.LayoutDataUtils.moveNode; 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.FeedbackChange; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.base.MoveControllerBase; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelChangeProvider; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.MVCBundleTag; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.MoveControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelChangeProvider; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.MVCBundleTag; import org.fortiss.tooling.base.model.element.IConnector; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; import org.fortiss.tooling.kernel.service.IConnectionCompositorService; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedLinkBendPointController.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedLinkBendPointController.java index a8372485ae96f2abf773d1baeb1f8c7b2b25985f..6d5de856f513ca82eab805a0ad9cda8194be011d 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedLinkBendPointController.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedLinkBendPointController.java @@ -21,13 +21,13 @@ import static org.fortiss.tooling.base.ui.utils.LayoutDataUIUtils.removeConnecti import java.util.List; import org.eclipse.emf.common.util.EList; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramCoordinate; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewer; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IController; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.base.LinkControllerBase; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelChangeProvider; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.LinkControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelChangeProvider; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; import org.fortiss.tooling.base.model.layout.Point; import org.fortiss.tooling.kernel.service.IConnectionCompositorService; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedRectangularResizableContentController.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedRectangularResizableContentController.java index 1e18af3ee10dd3754325ed389fed664da65a0b61..3232d423572924363702898cf8ba240ce7042b65 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedRectangularResizableContentController.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/controller/LayoutedRectangularResizableContentController.java @@ -22,10 +22,10 @@ import static org.fortiss.tooling.base.utils.LayoutDataUtils.moveNode; import static org.fortiss.tooling.base.utils.LayoutDataUtils.setNodeSize; import static org.fortiss.tooling.base.utils.LayoutDataUtils.setStickyConnectorLayoutData; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.FeedbackChange; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IController; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; import org.fortiss.tooling.base.model.element.IConnector; import org.fortiss.tooling.base.model.layout.Dimension; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/model/.ratings b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/model/.ratings index 1f94f2987b843eba33765b057f6d210e4bbbcbb4..e486bea097082450923a8577024abfdd76030929 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/model/.ratings +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/model/.ratings @@ -1 +1 @@ -HierarchicElementModelFactoryBase.java 9996bfc1402c27424f0ae69e64560acce0ef81f4 GREEN +HierarchicElementModelFactoryBase.java 10741fec431df8c05038e73b6b40ba9c7c35fa57 YELLOW diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/model/HierarchicElementModelFactoryBase.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/model/HierarchicElementModelFactoryBase.java index 9996bfc1402c27424f0ae69e64560acce0ef81f4..10741fec431df8c05038e73b6b40ba9c7c35fa57 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/model/HierarchicElementModelFactoryBase.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/model/HierarchicElementModelFactoryBase.java @@ -21,7 +21,7 @@ import static org.fortiss.tooling.common.util.LambdaUtils.filterList; import java.util.List; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelFactory; import org.fortiss.tooling.base.model.element.IConnection; import org.fortiss.tooling.base.model.element.IHierarchicElement; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/.ratings b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/.ratings index 5a82836cd656dbc37a323c788161b6ffab643988..a7dd2ab254be3d36a5e8d5b555f759f9f3f9568e 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/.ratings +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/.ratings @@ -1,9 +1,9 @@ -CoordinateCorrections.java c5cc475b45de38c56fc2e888a2d3093cd2efb52a GREEN -LayoutedCircularAnchorageContentVisualBase.java 2aa292444671bf644e37bc923b877c92de0235b1 GREEN -LayoutedCircularAnchorageDiagramVisualBase.java f06e3b908020408e3cd268a3c63edcd75ef74f8b GREEN -LayoutedLineLinkVisual.java feea85e0bd288590fbe06c152a8a8b138ea85ca2 GREEN -LayoutedRectangularContentVisualBase.java c2a3937b99284713e0bbcd3ce458874567b25ac5 GREEN -NamedLayoutedCircularAnchorageContentVisual.java c680002469ce897679fa5a3f4af51d1b19cb53d6 GREEN -NamedLayoutedCircularAnchorageDiagramVisual.java 714a176a0569a2049efb4009f710ca66bf3a57fb GREEN -NamedLayoutedLineLinkVisual.java e66e5b2aaa40fe8b22a292e175bb8f3af4539b9d GREEN -NamedLayoutedRectangularContentVisual.java 122e193ac587857d1dad23b42583a0bcf465f0d0 GREEN +CoordinateCorrections.java 018bf229e5686afcb8540b61dd9d05b6e4a23e93 YELLOW +LayoutedCircularAnchorageContentVisualBase.java dba8ae04ecd5813aae9b29eaccea520caa3cf237 YELLOW +LayoutedCircularAnchorageDiagramVisualBase.java 7634416bcb88a014d985143bf00a8d29ff1e3ff5 YELLOW +LayoutedLineLinkVisual.java ff1291c57d4ce111d5543d7381a616cd2e096290 YELLOW +LayoutedRectangularContentVisualBase.java 61698ffd771ee2ad798025df8195d1bc09c2c765 YELLOW +NamedLayoutedCircularAnchorageContentVisual.java bf06ac6e93d78e98b0359e0f879dccaefc920aa0 YELLOW +NamedLayoutedCircularAnchorageDiagramVisual.java 53b9d739587d658f65dc03517257d6e29f4ba1fa YELLOW +NamedLayoutedLineLinkVisual.java 60f5d21f0723dc5d08b2ad43bc6361fca83a16aa YELLOW +NamedLayoutedRectangularContentVisual.java 8cdc55b306c1db60074fa8c5c240f95d892e1e32 YELLOW diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/CoordinateCorrections.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/CoordinateCorrections.java index c5cc475b45de38c56fc2e888a2d3093cd2efb52a..018bf229e5686afcb8540b61dd9d05b6e4a23e93 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/CoordinateCorrections.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/CoordinateCorrections.java @@ -17,8 +17,8 @@ package org.fortiss.tooling.base.ui.editor.fx.visual; import static org.fortiss.tooling.base.layout.DefaultLayoutConstants.DEFAULT_CONNECTOR_SIZE; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base.ContentAnchorageVisualBase; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular.RectangularContentVisualBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.ContentAnchorageVisualBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular.RectangularContentVisualBase; import org.fortiss.tooling.base.layout.DefaultLayoutConstants; import javafx.geometry.Dimension2D; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedCircularAnchorageContentVisualBase.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedCircularAnchorageContentVisualBase.java index 2aa292444671bf644e37bc923b877c92de0235b1..dba8ae04ecd5813aae9b29eaccea520caa3cf237 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedCircularAnchorageContentVisualBase.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedCircularAnchorageContentVisualBase.java @@ -17,18 +17,18 @@ package org.fortiss.tooling.base.ui.editor.fx.visual; import static javafx.scene.paint.Color.BLACK; import static javafx.scene.paint.Color.rgb; -import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_ALLOWED_TAG; -import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_DENIED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_ALLOWED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_DENIED_TAG; import static org.fortiss.tooling.base.ui.editor.fx.visual.CoordinateCorrections.ANCHOR_DIMENSION; import static org.fortiss.tooling.base.ui.editor.fx.visual.CoordinateCorrections.ANCHOR_INSET; import static org.fortiss.tooling.base.ui.utils.LWFXEditorUtils.convertEOrientationToSide; import static org.fortiss.tooling.base.ui.utils.LayoutDataUIUtils.getConnectorOffsetOrientation; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.layout.ILayout; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.layout.IOffsetLayout; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.layout.ISideLayout; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.elliptic.CircularContentAnchorageVisualBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.ILayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.IOffsetLayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.ISideLayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic.CircularContentAnchorageVisualBase; import org.fortiss.tooling.base.model.layout.EOrientation; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; import org.fortiss.tooling.base.model.layout.OffsetOrientation; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedCircularAnchorageDiagramVisualBase.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedCircularAnchorageDiagramVisualBase.java index f06e3b908020408e3cd268a3c63edcd75ef74f8b..7634416bcb88a014d985143bf00a8d29ff1e3ff5 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedCircularAnchorageDiagramVisualBase.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedCircularAnchorageDiagramVisualBase.java @@ -17,14 +17,14 @@ package org.fortiss.tooling.base.ui.editor.fx.visual; import static javafx.scene.paint.Color.BLACK; import static javafx.scene.paint.Color.rgb; -import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_ALLOWED_TAG; -import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_DENIED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_ALLOWED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_DENIED_TAG; import static org.fortiss.tooling.base.ui.editor.fx.visual.CoordinateCorrections.ANCHOR_DIMENSION; import static org.fortiss.tooling.base.ui.editor.fx.visual.CoordinateCorrections.ANCHOR_INSET; import static org.fortiss.tooling.base.utils.LayoutDataUtils.getNodePosition; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.elliptic.CircularDiagramAnchorageVisualBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic.CircularDiagramAnchorageVisualBase; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; import org.fortiss.tooling.base.model.layout.Point; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedLineLinkVisual.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedLineLinkVisual.java index feea85e0bd288590fbe06c152a8a8b138ea85ca2..ff1291c57d4ce111d5543d7381a616cd2e096290 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedLineLinkVisual.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedLineLinkVisual.java @@ -21,9 +21,9 @@ import static org.fortiss.tooling.base.ui.utils.LayoutDataUIUtils.getConnectionP import java.util.List; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramCoordinate; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular.LineLinkVisualBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular.LineLinkVisualBase; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; import org.fortiss.tooling.base.model.layout.Point; import org.fortiss.tooling.base.model.layout.Points; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedRectangularContentVisualBase.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedRectangularContentVisualBase.java index c2a3937b99284713e0bbcd3ce458874567b25ac5..61698ffd771ee2ad798025df8195d1bc09c2c765 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedRectangularContentVisualBase.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/LayoutedRectangularContentVisualBase.java @@ -19,13 +19,13 @@ import static java.lang.Math.min; import static org.fortiss.tooling.base.ui.editor.fx.visual.CoordinateCorrections.RECTANGLE_INSETS; import static org.fortiss.tooling.base.utils.LayoutDataUtils.getNodeBounds; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramCoordinate; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.layout.IOffsetLayout; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.layout.ISideLayout; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular.RectangularBorderLocation; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular.RectangularContentVisualBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.IOffsetLayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.ISideLayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular.RectangularBorderLocation; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular.RectangularContentVisualBase; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; import org.fortiss.tooling.base.model.layout.Rectangle; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedCircularAnchorageContentVisual.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedCircularAnchorageContentVisual.java index c680002469ce897679fa5a3f4af51d1b19cb53d6..bf06ac6e93d78e98b0359e0f879dccaefc920aa0 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedCircularAnchorageContentVisual.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedCircularAnchorageContentVisual.java @@ -15,8 +15,8 @@ +--------------------------------------------------------------------------*/ package org.fortiss.tooling.base.ui.editor.fx.visual; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; import org.fortiss.tooling.base.model.element.IConnector; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; import org.fortiss.tooling.kernel.model.INamedElement; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedCircularAnchorageDiagramVisual.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedCircularAnchorageDiagramVisual.java index 714a176a0569a2049efb4009f710ca66bf3a57fb..53b9d739587d658f65dc03517257d6e29f4ba1fa 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedCircularAnchorageDiagramVisual.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedCircularAnchorageDiagramVisual.java @@ -15,8 +15,8 @@ +--------------------------------------------------------------------------*/ package org.fortiss.tooling.base.ui.editor.fx.visual; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; import org.fortiss.tooling.base.model.element.IConnector; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; import org.fortiss.tooling.kernel.model.INamedElement; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedLineLinkVisual.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedLineLinkVisual.java index e66e5b2aaa40fe8b22a292e175bb8f3af4539b9d..60f5d21f0723dc5d08b2ad43bc6361fca83a16aa 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedLineLinkVisual.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedLineLinkVisual.java @@ -15,8 +15,8 @@ +--------------------------------------------------------------------------*/ package org.fortiss.tooling.base.ui.editor.fx.visual; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; import org.fortiss.tooling.base.model.element.IConnection; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; import org.fortiss.tooling.kernel.model.INamedElement; diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedRectangularContentVisual.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedRectangularContentVisual.java index 122e193ac587857d1dad23b42583a0bcf465f0d0..8cdc55b306c1db60074fa8c5c240f95d892e1e32 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedRectangularContentVisual.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/editor/fx/visual/NamedLayoutedRectangularContentVisual.java @@ -15,7 +15,7 @@ +--------------------------------------------------------------------------*/ package org.fortiss.tooling.base.ui.editor.fx.visual; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; import org.fortiss.tooling.base.model.layout.ILayoutedModelElement; import org.fortiss.tooling.kernel.model.INamedElement; 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 27fbc6fb9f974296b2e91d4738b5ba7289852c08..d2fc13e9d443fbc1e3005b3723a68a1c72519d3a 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 @@ -1,11 +1,10 @@ AbstractNameEditingSupport.java c57336a0e0da18711a1610ca667dfea76728807f GREEN ActionUtils.java 322f43d4f92f992daef8ac88eb0f9197c840c89b GREEN -ConstraintsBaseUIUtils.java 713512da44cc0ebd9bcd5c11abee87c86c94088d GREEN DragAndDropBaseUtils.java d375377f9124f6113b2a295e6b0e09ac8966e564 GREEN EllipseLayoutUIUtils.java 4dd9dbd96a45e8c455c019caa19e4a50f18336af GREEN FontUtils.java a167a05bdaa8da9853705cc5134f30f6d81bc9f2 GREEN GCStateManager.java 983973a92376b5c757c1253b32e33d0666ccdf7b GREEN -LWFXEditorUtils.java c628e79fa43c1be8a7b87ee90db6e7264904eafc GREEN +LWFXEditorUtils.java c624d3f0f6487b6d426b168dad048b2c39e71114 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 c628e79fa43c1be8a7b87ee90db6e7264904eafc..c624d3f0f6487b6d426b168dad048b2c39e71114 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,7 +19,7 @@ import static java.lang.Math.atan2; import static java.lang.Math.cos; import static java.lang.Math.sin; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; import org.fortiss.tooling.base.model.layout.EOrientation; import javafx.geometry.Point2D; diff --git a/org.fortiss.tooling.common.ui/.classpath b/org.fortiss.tooling.common.ui/.classpath index d9ed18f5cea4deb235213896eef764feded19dee..0920a6fed0bb5a3313c4f46b95ea1fcb99101429 100644 --- a/org.fortiss.tooling.common.ui/.classpath +++ b/org.fortiss.tooling.common.ui/.classpath @@ -3,8 +3,6 @@ <classpathentry kind="src" path="src"/> <classpathentry kind="src" path="external-src"/> <classpathentry kind="src" path="res"/> - <classpathentry exported="true" kind="lib" path="lib/org.eclipse.systemfocus.kernel.common_0.0.5.20191219.jar"/> - <classpathentry exported="true" kind="lib" path="lib/org.eclipse.systemfocus.kernel.common.ui_0.0.5.20191219.jar"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"> <attributes> <attribute name="module" value="true"/> diff --git a/org.fortiss.tooling.common.ui/META-INF/MANIFEST.MF b/org.fortiss.tooling.common.ui/META-INF/MANIFEST.MF index f158eb873beefe27aacb70e4e70c12c3efcf333b..d94aac1c47acf866e4d32c46ffa14022a84a7b3e 100644 --- a/org.fortiss.tooling.common.ui/META-INF/MANIFEST.MF +++ b/org.fortiss.tooling.common.ui/META-INF/MANIFEST.MF @@ -24,9 +24,7 @@ Require-Bundle: org.fortiss.tooling.common;visibility:=reexport, Bundle-ClassPath: ., lib/org.conqat.ide.commons.gef.jar, lib/org.conqat.ide.commons.ui.jar, - lib/swt-grouplayout.jar, - lib/org.eclipse.systemfocus.kernel.common_0.0.5.20191219.jar, - lib/org.eclipse.systemfocus.kernel.common.ui_0.0.5.20191219.jar + lib/swt-grouplayout.jar Export-Package: aerofx, aquafx, jfxtras, @@ -74,25 +72,6 @@ Export-Package: aerofx, org.eclipse.gmf.runtime.draw2d.ui.figures, org.eclipse.gmf.runtime.draw2d.ui.geometry, org.eclipse.jface.viewers, - org.eclipse.systemfocus.kernel.common.ui.javafx.control.treetableview, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.change, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.base, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.elliptic, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.rectangular, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.layout, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.elliptic, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular, - org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.widgets, - org.eclipse.systemfocus.kernel.common.ui.javafx.mesh, - org.eclipse.systemfocus.kernel.common.ui.javafx.style, - org.eclipse.systemfocus.kernel.common.ui.javafx.util, org.eclipse.ui.actions, org.eclipse.wb.swt, org.eclipse.wb.swt.layout.grouplayout, @@ -101,6 +80,21 @@ Export-Package: aerofx, org.fortiss.tooling.common.ui.javafx, org.fortiss.tooling.common.ui.javafx.control.treetableview, org.fortiss.tooling.common.ui.javafx.layout, + org.fortiss.tooling.common.ui.javafx.lwfxef, + org.fortiss.tooling.common.ui.javafx.lwfxef.change, + org.fortiss.tooling.common.ui.javafx.lwfxef.controller, + org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base, + org.fortiss.tooling.common.ui.javafx.lwfxef.controller.elliptic, + org.fortiss.tooling.common.ui.javafx.lwfxef.controller.rectangular, + org.fortiss.tooling.common.ui.javafx.lwfxef.model, + org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout, + org.fortiss.tooling.common.ui.javafx.lwfxef.mvc, + org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl, + org.fortiss.tooling.common.ui.javafx.lwfxef.visual, + org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base, + org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic, + org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular, + org.fortiss.tooling.common.ui.javafx.lwfxef.visual.widgets, org.fortiss.tooling.common.ui.javafx.util Bundle-Vendor: fortiss GmbH Bundle-Activator: org.fortiss.tooling.common.ui.ToolingCommonUIActivator diff --git a/org.fortiss.tooling.common.ui/build.properties b/org.fortiss.tooling.common.ui/build.properties index 047e606ce9d79664f75aab85d5fb16ee0ae09b08..ab1e37fb1bbf7fdb879ccf70079b428aacb1e8ff 100644 --- a/org.fortiss.tooling.common.ui/build.properties +++ b/org.fortiss.tooling.common.ui/build.properties @@ -5,9 +5,7 @@ bin.includes = .,\ lib/org.conqat.ide.commons.gef.jar,\ lib/org.conqat.ide.commons.ui.jar,\ lib/swt-grouplayout.jar,\ - res/,\ - lib/org.eclipse.systemfocus.kernel.common_0.0.5.20191219.jar,\ - lib/org.eclipse.systemfocus.kernel.common.ui_0.0.5.20191219.jar + res/ jars.compile.order = . source.. = src/,\ res/,\ diff --git a/org.fortiss.tooling.common.ui/lib/org.eclipse.systemfocus.kernel.common.ui_0.0.5.20191219.jar b/org.fortiss.tooling.common.ui/lib/org.eclipse.systemfocus.kernel.common.ui_0.0.5.20191219.jar deleted file mode 100644 index 395895b74e4f88edcd5118c917fd2ab6b413e057..0000000000000000000000000000000000000000 Binary files a/org.fortiss.tooling.common.ui/lib/org.eclipse.systemfocus.kernel.common.ui_0.0.5.20191219.jar and /dev/null differ diff --git a/org.fortiss.tooling.common.ui/lib/org.eclipse.systemfocus.kernel.common_0.0.5.20191219.jar b/org.fortiss.tooling.common.ui/lib/org.eclipse.systemfocus.kernel.common_0.0.5.20191219.jar deleted file mode 100644 index b27db9f8c0e0c56fd798ea2b54927bc768b5ce64..0000000000000000000000000000000000000000 Binary files a/org.fortiss.tooling.common.ui/lib/org.eclipse.systemfocus.kernel.common_0.0.5.20191219.jar and /dev/null differ diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..daf51f54143284826e353c054984aacc6c4ed77a --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/.ratings @@ -0,0 +1,12 @@ +DiagramCoordinate.java 6b00aec99054d4cd19003a72bd4e5e774ac6a641 YELLOW +DiagramLayers.java 155cbb47a5f0aaa0025320ae607e6777f3a2d2e8 YELLOW +DiagramViewer.java d5ba3f45ddc619132434f9cbefd36f65cce18b65 YELLOW +DiagramViewerDefaultTags.java 6230763252409c60009ab8887b4ef582cf883229 YELLOW +DiagramViewerFeatures.java 31e3fb61f915b0d8695005b083c47ce1c5be0b05 YELLOW +DiagramViewerSelection.java e833f592543bc97077907d980a39b123fc4044e6 YELLOW +EDragGesture.java 5cfa098d3877db11981c2750e5e103156d62fc5e YELLOW +FeedbackChange.java b088fa89af648f1674f2f9c1f7f99d585ce801ca YELLOW +GridCanvasVisual.java 734027d56af342cd01ff445ba9347b8dbb6c83c2 YELLOW +MVCBundleManager.java 2b4ab114c55b30a3d98d7135458f8f3ddd244d58 YELLOW +MouseState.java ff90af6d1cca427ef6f3fded76367b535120a5df YELLOW +SVGExporter.java 2211f06d81c7b0523ae52dc832410a76875a9e07 YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramCoordinate.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramCoordinate.java new file mode 100644 index 0000000000000000000000000000000000000000..6b00aec99054d4cd19003a72bd4e5e774ac6a641 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramCoordinate.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +import javafx.geometry.Bounds; +import javafx.geometry.Point2D; +import javafx.scene.Node; + +/** Extension of Point2D to improve code understandability when working with coordinates. */ +public final class DiagramCoordinate extends Point2D { + /** Constructor. */ + public DiagramCoordinate(double x, double y) { + super(x, y); + } + + /** Returns the location of this diagram coordinate relative to the given node. */ + public Point2D getLocal(Node node) { + Bounds boundsInLocal = node.getBoundsInLocal(); + return new Point2D(getX() - boundsInLocal.getMinX(), getY() - boundsInLocal.getMinY()); + } + + /** Returns the location of this diagram X coordinate relative to the given node. */ + public double getLocalX(Node node) { + return getX() - node.getBoundsInLocal().getMinX(); + } + + /** Returns the location of this diagram Y coordinate relative to the given node. */ + public double getLocalY(Node node) { + return getY() - node.getBoundsInLocal().getMinY(); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate add(double dx, double dy) { + return new DiagramCoordinate(getX() + dx, getY() + dy); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate subtract(double dx, double dy) { + return add(-dx, -dy); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate subtract(Point2D point) { + return subtract(point.getX(), point.getY()); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramLayers.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramLayers.java new file mode 100644 index 0000000000000000000000000000000000000000..155cbb47a5f0aaa0025320ae607e6777f3a2d2e8 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramLayers.java @@ -0,0 +1,216 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; + +import javafx.collections.ObservableList; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.transform.Scale; + +/** {@link Group} of layers for the {@link DiagramViewer}. */ +public final class DiagramLayers extends Group { + /** The diagram viewer of this layer group. */ + private final DiagramViewer viewer; + /** The bottom layer. */ + private final Layer bottomLayer = new Layer(); + /** The content node layer. */ + private final Layer contentLayer = new Layer(); + /** The link node layer. */ + private final Layer linkLayer = new Layer(); + /** The anchorage node layer. */ + private final Layer anchorageLayer = new Layer(); + /** The text node layer. */ + private final Layer textLayer = new Layer(); + /** The feedback layer for visual feedback. */ + private final Layer visualFeedbackLayer = new Layer(); + /** The feedback layer for interaction with content objects. */ + private final Layer contentInteractionLayer = new Layer(); + /** The feedback layer for interaction with anchorage objects. */ + private final Layer anchorageInteractionLayer = new Layer(); + /** The feedback layer for interaction with link objects. */ + private final Layer linkInteractionLayer = new Layer(); + /** The top layer. */ + private final Layer topLayer = new Layer(); + /** The current mouse drag state associated with this bundle's visual. */ + private final MouseState mouseState; + /** The scale to be applied for zooming the content of the layers. */ + private Scale scale = new Scale(); + + /** Constructor. */ + public DiagramLayers(DiagramViewer viewer) { + this.viewer = viewer; + ObservableList<Node> c = getChildren(); + c.add(bottomLayer); + c.add(contentLayer); + c.add(anchorageLayer); + c.add(linkLayer); + c.add(visualFeedbackLayer); + c.add(textLayer); + c.add(contentInteractionLayer); + c.add(anchorageInteractionLayer); + c.add(linkInteractionLayer); + c.add(topLayer); + mouseState = new MouseState(viewer); + } + + /** Returns mouseState. */ + public MouseState getMouseState() { + return mouseState; + } + + /** Returns the bottom layer. */ + public ILayer getBottomLayer() { + return bottomLayer; + } + + /** Returns the content layer. */ + public ILayer getContentLayer() { + return contentLayer; + } + + /** Returns the anchorage lLayer. */ + public ILayer getAnchorageLayer() { + return anchorageLayer; + } + + /** Returns the link layer. */ + public ILayer getLinkLayer() { + return linkLayer; + } + + /** Returns the visual feedback layer. */ + public ILayer getVisualFeedbackLayer() { + return visualFeedbackLayer; + } + + /** Returns the text layer. */ + public ILayer getTextLayer() { + return textLayer; + } + + /** + * Returns the layer for (usually invisible) nodes used for user interactions with content + * nodes. + */ + public ILayer getContentInteractionLayer() { + return contentInteractionLayer; + } + + /** + * Returns the layer for (usually invisible) nodes used for user interactions with anchorage + * nodes. + */ + public ILayer getAnchorageInteractionLayer() { + return anchorageInteractionLayer; + } + + /** + * Returns the layer for (usually invisible) nodes used for user interactions with link nodes. + */ + public ILayer getLinkInteractionLayer() { + return linkInteractionLayer; + } + + /** Returns the top layer. */ + public ILayer getTopLayer() { + return topLayer; + } + + /** Returns whether the given node is an interactive feedback node. */ + public boolean isInteractionFeedback(Node node) { + return node.getParent() == contentInteractionLayer; + } + + /** Clears all nodes from the layers. */ + public void clearLayers() { + bottomLayer.clear(); + contentLayer.clear(); + anchorageLayer.clear(); + linkLayer.clear(); + visualFeedbackLayer.clear(); + textLayer.clear(); + contentInteractionLayer.clear(); + anchorageInteractionLayer.clear(); + linkInteractionLayer.clear(); + topLayer.clear(); + } + + /** Registers the scroll listener and the zoom scale transformation. */ + private void registerScrollListenerAndZoomScale(Node node) { + node.setOnScroll(viewer.getScrollingHandler()); + node.getTransforms().add(scale); + } + + /** Unregisters the scroll listener and the zoom scale transformation. */ + private void unregisterScrollListenerAndZoomScale(Node node) { + node.setOnScroll(null); + node.getTransforms().remove(scale); + } + + /** Scales all nodes in each layer. */ + /* package */ void setScale(double factor) { + scale.setX(factor); + scale.setY(factor); + scale.setZ(factor); + } + + /** Interface for layers. */ + public static interface ILayer { + /** + * Adds the given bundle's node to this layer. Note that the given node's parent must be + * null. In other words, a node must first be explicitly removed from its former layer and + * then added to the new one. + */ + void add(Node node, IMVCBundle bundle); + + /** Removes the given node from this layer. */ + void remove(Node node); + + /** Clears this layer removing all nodes. */ + void clear(); + } + + /** Implementation of layers. */ + private class Layer extends Group implements ILayer { + /** {@inheritDoc} */ + @Override + public void add(Node node, IMVCBundle bundle) { + if(node == null || bundle == null || node.getParent() != null) { + return; + } + getChildren().add(node); + registerScrollListenerAndZoomScale(node); + mouseState.registerMouseListeners(node, bundle); + } + + /** {@inheritDoc} */ + @Override + public void remove(Node node) { + if(node == null || node.getParent() != this) { + return; + } + mouseState.unregisterMouseListeners(node); + unregisterScrollListenerAndZoomScale(node); + getChildren().remove(node); + } + + /** {@inheritDoc} */ + @Override + public void clear() { + for(Node node : getChildren()) { + mouseState.unregisterMouseListeners(node); + unregisterScrollListenerAndZoomScale(node); + } + getChildren().clear(); + } + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewer.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..d5ba3f45ddc619132434f9cbefd36f65cce18b65 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewer.java @@ -0,0 +1,797 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +import static java.lang.Math.ceil; +import static java.lang.Math.max; +import static java.lang.Math.min; +import static java.util.Objects.requireNonNull; +import static javafx.scene.layout.GridPane.setHgrow; +import static javafx.scene.layout.GridPane.setVgrow; +import static javafx.scene.layout.Priority.ALWAYS; +import static javafx.scene.paint.Color.ORANGERED; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_ALLOWED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_DENIED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_MAYBE_TAG; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.ChangeSet; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.DefaultModelModifier; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IControllerFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IKeyPressController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.MVCBundleTag; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisualFactory; + +import javafx.event.EventHandler; +import javafx.geometry.Bounds; +import javafx.geometry.Orientation; +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.Label; +import javafx.scene.control.MenuItem; +import javafx.scene.control.ScrollBar; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.ScrollEvent; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Pane; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Line; +import javafx.scene.shape.Rectangle; + +/** + * This class represents the diagram viewer node, which manages the grid + * background and the content visuals as well as scrolling, zooming, selection, + * context menus and link creation. Due to the number of features and functions, + * part of this class delegates to a {@link MVCBundleManager} object used solely + * for the purpose of source code size reduction. + */ +public class DiagramViewer { + /** The bundle manager helper class reference. */ + private final MVCBundleManager viewerManager; + /** The feature state helper class reference. */ + private final DiagramViewerFeatures features; + /** The grid pane for the main pane and scrollbars. */ + private final GridPane scrolledPane; + /** The horizontal scrollbar. */ + private final ScrollBar horizontalScrollbar; + /** The vertical scrollbar. */ + private final ScrollBar verticalScrollbar; + /** The group for layered nodes. */ + private final DiagramLayers layers; + /** The viewer main pane. */ + private final Pane viewerPane; + /** The mouse drag multi-selection feedback rectangle. */ + private final Rectangle mouseDragRectangle = new Rectangle(0, 0); + /** The initial location where the selection rectangle was started. */ + private Point2D mouseDragRectangleStartLocation = new Point2D(0, 0); + /** The line used during link creation gesture. */ + private final Line linkLineFeedback = new Line(); + /** The current context menu. */ + private ContextMenu contextMenu = null; + + /** A debug label. */ + private Label debugLabel = new Label(""); + + /** Constructor. */ + public DiagramViewer(IModelFactory modelFactory, IVisualFactory visualFactory, + IControllerFactory controllerFactory, + Consumer<DiagramViewerSelection> selectionChangedCallback) { + this(modelFactory, visualFactory, controllerFactory, selectionChangedCallback, + new DefaultModelModifier()); + } + + /** Constructor. */ + public DiagramViewer(IModelFactory modelFactory, IVisualFactory visualFactory, + IControllerFactory controllerFactory, + Consumer<DiagramViewerSelection> selectionChangedCallback, + Consumer<Change> modelModifier) { + features = new DiagramViewerFeatures(this); + layers = new DiagramLayers(this); + viewerManager = new MVCBundleManager(this, modelFactory, visualFactory, controllerFactory, + layers, selectionChangedCallback, requireNonNull(modelModifier)); + + // selection feedback rectangle + mouseDragRectangle.setFill(Color.TRANSPARENT); + mouseDragRectangle.setStroke(Color.ORANGERED); + mouseDragRectangle.getStrokeDashArray().addAll(15.0, 5.0); + mouseDragRectangle.setMouseTransparent(true); + // viewer pane + viewerPane = new Pane(); + layers.boundsInLocalProperty().addListener((obs, ov, nv) -> { + updateHorizontalScrollbar(); + updateVerticalScrollbar(); + }); + viewerPane.getChildren().add(layers); + viewerPane.widthProperty().addListener(evt -> { + updateGridCanvasSize(); + updateHorizontalScrollbar(); + }); + viewerPane.heightProperty().addListener(evt -> { + updateGridCanvasSize(); + updateVerticalScrollbar(); + }); + // focus event handling + viewerPane.focusedProperty().addListener((obs, ov, nv) -> { + viewerManager.handleFocus(nv, getLayers()); + }); + + // grid pane for viewer and scroll bars + scrolledPane = new GridPane(); + scrolledPane.add(viewerPane, 1, 1); + setHgrow(viewerPane, ALWAYS); + setVgrow(viewerPane, ALWAYS); + + // vertical scrollbar + verticalScrollbar = new ScrollBar(); + verticalScrollbar.setOrientation(Orientation.VERTICAL); + scrolledPane.add(verticalScrollbar, 2, 1); + verticalScrollbar.valueProperty().addListener((obs, ov, nv) -> { + hideContextMenu(); + double zf = features.getCurrentZoomFactor(); + double vszf = features.getVerticalSpacing() * zf; + double deltaY = nv.doubleValue() * vszf; + layers.setTranslateY(-deltaY); + viewerManager.gridCanvasVisual.setScrollY(nv.doubleValue()); + }); + + // horizontal scrollbar + horizontalScrollbar = new ScrollBar(); + scrolledPane.add(horizontalScrollbar, 1, 2); + horizontalScrollbar.valueProperty().addListener((obs, ov, nv) -> { + hideContextMenu(); + double zf = features.getCurrentZoomFactor(); + double hszf = features.getHorizontalSpacing() * zf; + double deltaX = nv.doubleValue() * hszf; + layers.setTranslateX(-deltaX); + viewerManager.gridCanvasVisual.setScrollX(nv.doubleValue()); + }); + + viewerPane.getChildren().add(debugLabel); + debugLabel.setLayoutX(20); + debugLabel.setLayoutY(20); + debugLabel.setTextFill(ORANGERED); + + // clipping rectangle for the viewer pane + Rectangle viewerClip = new Rectangle(); + viewerPane.setClip(viewerClip); + viewerPane.layoutBoundsProperty().addListener((obs, ov, nv) -> { + viewerClip.setWidth(nv.getWidth()); + viewerClip.setHeight(nv.getHeight()); + }); + + // update the viewer content + viewerManager.updateContentObjects(); + } + + /** Returns viewerPane. */ + /* package */ Pane getViewerPane() { + return viewerPane; + } + + /** Returns the {@link IDiagramMVCBundle}. */ + /* package */ IDiagramMVCBundle getDiagramBundle() { + return viewerManager.diagramBundle; + } + + /** Shows the context menu when the mouse is pressed. */ + /* package */ void hideContextMenu() { + if(contextMenu != null && contextMenu.isShowing()) { + contextMenu.hide(); + contextMenu = null; + } + } + + /** Shows the context menu when the mouse is released. */ + public void showContextMenu(Node node, DiagramCoordinate diagramLocation, IMVCBundle mvcb) { + List<MenuItem> items = mvcb.getController().contextMenuContributions(node, diagramLocation); + if(items == null || items.isEmpty()) { + return; + } + contextMenu = new ContextMenu(); + contextMenu.getItems().addAll(items); + contextMenu.setAutoHide(true); + Point2D locationOnNode = diagramLocation.getLocal(node); + Point2D screenLocation; + double zf = features.getCurrentZoomFactor(); + if(viewerManager.gridCanvasVisual.isGridCanvas(node)) { + double hsd = features.getHorizontalSpacing() * getHorizontalScrollBarValue() * zf; + double vsd = features.getVerticalSpacing() * getVerticalScrollBarValue() * zf; + Point2D correction = locationOnNode.multiply(zf).subtract(hsd, vsd); + screenLocation = node.localToScreen(correction); + } else { + Bounds b = node.getBoundsInLocal(); + screenLocation = node.localToScreen(locationOnNode.getX() + b.getMinX(), + locationOnNode.getY() + b.getMinY()); + } + contextMenu.show(node, screenLocation.getX(), screenLocation.getY()); + } + + /** Returns the root model element displayed by this diagram viewer. */ + public Object getRootElement() { + return viewerManager.getRootModel(); + } + + /** Returns the features instance. */ + public DiagramViewerFeatures getFeatures() { + return features; + } + + /** + * Returns the content of a SVG representation of this viewers content. + * <P> + * Note that the background canvas is not exported. + */ + public String getSVGExport() { + SVGExporter exporter = new SVGExporter(this); + return exporter.export(); + } + + /** Updates the vertical scrollbar. */ + private void updateVerticalScrollbar() { + Bounds vpb = viewerPane.getLayoutBounds(); + Bounds cb = layers.getLayoutBounds(); + double vs = features.getVerticalSpacing(); + double contentHeight = max(ceil(cb.getMaxY() / vs), 2); + double viewportHeight = ceil(vpb.getHeight() / vs); + double zf = features.getCurrentZoomFactor(); + // this allows scrolling the content nearly out of sight (two grid spaces) + double max = contentHeight - 2 * zf; + verticalScrollbar.setMax(max); + double visi = max * (viewportHeight / (viewportHeight + contentHeight - 2)); + verticalScrollbar.setVisibleAmount(visi); + verticalScrollbar.setUnitIncrement(1); + verticalScrollbar.setBlockIncrement(5); + if(verticalScrollbar.getValue() > max) { + verticalScrollbar.setValue(max); + } + } + + /** Updates the horizontal scrollbar. */ + private void updateHorizontalScrollbar() { + Bounds vpb = viewerPane.getLayoutBounds(); + Bounds cb = layers.getLayoutBounds(); + double hs = features.getHorizontalSpacing(); + double contentWidth = max(ceil(cb.getMaxX() / hs), 2); + double viewportWidth = ceil(vpb.getWidth() / hs); + double zf = features.getCurrentZoomFactor(); + + // this allows scrolling the content nearly out of sight (two grid spaces) + double max = contentWidth - 2 * zf; + horizontalScrollbar.setMax(max); + double visi = max * (viewportWidth / (viewportWidth + contentWidth - 2)); + horizontalScrollbar.setVisibleAmount(visi); + horizontalScrollbar.setUnitIncrement(1); + horizontalScrollbar.setBlockIncrement(5); + if(horizontalScrollbar.getValue() > max) { + horizontalScrollbar.setValue(max); + } + } + + /** Update the size of the grid canvas. */ + /* package */ void updateGridCanvasSize() { + Bounds viewer = viewerPane.getBoundsInLocal(); + viewerManager.gridCanvasVisual.setWidth(viewer.getWidth()); + viewerManager.gridCanvasVisual.setHeight(viewer.getHeight()); + } + + /** Returns the viewer's visual node. */ + public Pane getVisualNode() { + return scrolledPane; + } + + /** Returns the current horizontal scroll bar value. */ + public double getHorizontalScrollBarValue() { + return horizontalScrollbar.getValue(); + } + + /** Returns the current vertical scroll bar value. */ + public double getVerticalScrollBarValue() { + return verticalScrollbar.getValue(); + } + + /** Returns the model modifier. */ + public Consumer<Change> getModelModifier() { + return viewerManager.getModelModifier(); + } + + /** + * Calls the delete method of all controllers of currently selected elements and returns a + * corresponding {@link Change}. + */ + public Change deleteAllSelected() { + final DiagramViewerSelection sel = getSelection(); + return () -> { + sel.getPrimarySelection().getController().delete(); + for(IMVCBundle sec : sel.getSecondarySelections()) { + sec.getController().delete(); + } + }; + } + + /** Returns the current selection. */ + public DiagramViewerSelection getSelection() { + return viewerManager.getSelection(); + } + + /** Sets the selected {@link IMVCBundle} as single primary selection. */ + public void setSingleSelectedMVCBundle(IMVCBundle sel) { + viewerManager.setSingleSelectedMVCBundle(sel); + } + + /** Returns whether this bundle is selected as primary selection. */ + public boolean isPrimarySelected(IMVCBundle bundle) { + return viewerManager.isPrimarySelected(bundle); + } + + /** Returns whether this bundle is selected as secondary selection. */ + public boolean isSecondarySelected(IMVCBundle bundle) { + return viewerManager.isSecondarySelected(bundle); + } + + /** + * Returns whether this bundle is selected (either primary or secondary + * selection). + */ + public boolean isSelected(IMVCBundle bundle) { + return isPrimarySelected(bundle) || isSecondarySelected(bundle); + } + + /** + * Adds the given bundle as primary selection and moves the current selection to + * the secondary selections list. + */ + public void addSelectedMVCBundle(IMVCBundle sel) { + viewerManager.addSelectedMVCBundle(sel); + } + + /** Selects all content, diagram anchorage and link {@link IMVCBundle}s of this viewer. */ + public void selectAll() { + viewerManager.selectAll(); + } + + /** Interprets the effect of shift-clicking the given {@link IMVCBundle}. */ + public void handleShiftSelectionOf(IMVCBundle sel) { + viewerManager.handleShiftSelectionOf(sel); + } + + /** + * Removes the given bundle from either the primary selection (selecting next + * the secondary list element) or from the secondary selection list. + */ + public void removeSelectedMVCBundle(IMVCBundle sel) { + viewerManager.removeSelectedMVCBundle(sel); + } + + /** Returns the visual factory. */ + public IVisualFactory getVisualFactory() { + return viewerManager.visualFactory; + } + + /** Returns the controller factory. */ + public IControllerFactory getControllerFactory() { + return viewerManager.controllerFactory; + } + + /** Returns the model factory. */ + public IModelFactory getModelFactory() { + return viewerManager.modelFactory; + } + + /** Returns the scroll event handler. */ + /* package */ EventHandler<? super ScrollEvent> getScrollingHandler() { + return evt -> { + if(evt.isControlDown()) { + if(features.getZoomFactorIndex() == -1) { + evt.consume(); + return; + } + if(evt.getDeltaY() > 0) { + features.zoomIn(); + evt.consume(); + } else if(evt.getDeltaY() < 0) { + features.zoomOut(); + evt.consume(); + } + } else if(evt.isShiftDown()) { + double hmax = horizontalScrollbar.getMax(); + double hmin = horizontalScrollbar.getMin(); + double nval = horizontalScrollbar.getValue() - + evt.getDeltaY() / features.getHorizontalSpacing(); + horizontalScrollbar.setValue(max(hmin, min(hmax, nval))); + } else { + double vmax = verticalScrollbar.getMax(); + double vmin = verticalScrollbar.getMin(); + double nval = verticalScrollbar.getValue() - + evt.getDeltaY() / features.getVerticalSpacing(); + verticalScrollbar.setValue(max(vmin, min(vmax, nval))); + } + }; + } + + /** Updates every visual (and only its appearance, not the structure). */ + public void updateAllVisuals() { + viewerManager.updateAllVisuals(); + } + + /** + * Updates the nodes selectively adhering to the relation given by the + * {@link IMVCBundle}s. + */ + public void updateVisual(IMVCBundle bundle) { + viewerManager.updateVisual(bundle); + } + + /** + * Updates the nodes selectively adhering to the relation given by the + * {@link IMVCBundle}s. + */ + public void updateModelVisual(Object model) { + viewerManager.updateModelVisual(model); + } + + /** + * Updates the content of the viewer creating, deleting and updating any visuals + * using the underlying model. + */ + public void updateFromModel() { + double vscroll = verticalScrollbar.getValue(); + double hscroll = horizontalScrollbar.getValue(); + viewerManager.updateContentObjects(); + verticalScrollbar.setValue(vscroll); + horizontalScrollbar.setValue(hscroll); + } + + /** + * Sets the focus to the viewer pane in order to capture key events. This method + * must be called by the container when it receives the focus, e.g. via + * {@code @Focus} annotation. + */ + public void requestFocus() { + viewerPane.requestFocus(); + } + + /** Returns whether the diagram viewer has the focus or not. */ + public boolean hasFocus() { + return viewerPane.isFocused(); + } + + /** Sets the debug message. */ + public void setDebugMessage(String msg) { + debugLabel.setText(msg); + } + + /** Returns the layer group. */ + public DiagramLayers getLayers() { + return layers; + } + + /** Stars the feedback for selection rectangle. */ + public void startSelectionFeedback(Node node, DiagramCoordinate locationInDiagram) { + if(node == null || locationInDiagram == null) { + return; + } + mouseDragRectangleStartLocation = locationInDiagram; + mouseDragRectangle.setX(mouseDragRectangleStartLocation.getX()); + mouseDragRectangle.setY(mouseDragRectangleStartLocation.getY()); + mouseDragRectangle.setWidth(0); + mouseDragRectangle.setHeight(0); + if(mouseDragRectangle.getParent() == null) { + layers.getVisualFeedbackLayer().add(mouseDragRectangle, viewerManager.diagramBundle); + } + setSingleSelectedMVCBundle(null); + } + + /** + * Converts the given absolute diagram coordinates to node-local coordinates. + */ + /* package */ DiagramCoordinate convertEventToDiagramCoordinates(Point2D absoluteLocation, + Node node) { + return convertEventToDiagramCoordinates(absoluteLocation.getX(), absoluteLocation.getY(), + node); + } + + /** Converts the given mouse event coordinates to node-local coordinates. */ + /* package */ DiagramCoordinate convertEventToDiagramCoordinates(double evtX, double evtY, + Node node) { + if(node == viewerManager.gridCanvasVisual.getGridCanvas()) { + return convertGridCanvasCoordinate(evtX, evtY); + } + return new DiagramCoordinate(evtX, evtY); + } + + /** Converts the grid canvas coordinate to the diagram coordinate space. */ + public DiagramCoordinate convertGridCanvasCoordinate(double xOnCanvas, double yOnCanvas) { + double zf = features.getCurrentZoomFactor(); + double x = xOnCanvas / zf + features.getHorizontalSpacing() * getHorizontalScrollBarValue(); + double y = yOnCanvas / zf + features.getVerticalSpacing() * getVerticalScrollBarValue(); + return new DiagramCoordinate(x, y); + } + + /** Scrolls the diagram to center at the given coordinates. */ + public void scrollToCenter(DiagramCoordinate center) { + double zf = features.getCurrentZoomFactor(); + Bounds bounds = viewerPane.getLayoutBounds(); + double upperLeftX = center.getX() - bounds.getMinX() / zf; + if(upperLeftX < 0) { + upperLeftX = 0; + } + double newHSBValue = upperLeftX / features.getHorizontalSpacing(); + double upperLeftY = center.getY() - bounds.getMinY() / zf; + if(upperLeftY < 0) { + upperLeftY = 0; + } + double newVSBValue = upperLeftY - features.getVerticalSpacing(); + + horizontalScrollbar.setValue(newHSBValue); + verticalScrollbar.setValue(newVSBValue); + } + + /** Updates the feedback for selection rectangle. */ + public void updateSelectionFeedback(Node node, Point2D locationInDiagram) { + if(node == null || locationInDiagram == null) { + return; + } + Point2D location = locationInDiagram; + double x = Math.min(mouseDragRectangleStartLocation.getX(), location.getX()); + mouseDragRectangle.setX(x); + double w = Math.abs(mouseDragRectangleStartLocation.getX() - location.getX()); + mouseDragRectangle.setWidth(w); + double y = Math.min(mouseDragRectangleStartLocation.getY(), location.getY()); + mouseDragRectangle.setY(y); + double h = Math.abs(mouseDragRectangleStartLocation.getY() - location.getY()); + mouseDragRectangle.setHeight(h); + } + + /** Terminates the feedback for selection rectangle. */ + public void terminateSelectionFeedback(Node node, Point2D locationInDiagram) { + if(node == null || locationInDiagram == null) { + return; + } + Point2D location = locationInDiagram; + double x = Math.min(mouseDragRectangleStartLocation.getX(), location.getX()); + double w = Math.abs(mouseDragRectangleStartLocation.getX() - location.getX()); + double y = Math.min(mouseDragRectangleStartLocation.getY(), location.getY()); + double h = Math.abs(mouseDragRectangleStartLocation.getY() - location.getY()); + layers.getVisualFeedbackLayer().remove(mouseDragRectangle); + Rectangle2D selectionRect = new Rectangle2D(x, y, w, h); + // remove old selection + setSingleSelectedMVCBundle(null); + for(IContentMVCBundle cmvcb : viewerManager.contentBundles.values()) { + Rectangle2D contentRect = cmvcb.getVisual().getCurrentBounds(); + if(contentRect != null && selectionRect.contains(contentRect)) { + addSelectedMVCBundle(cmvcb); + } + } + for(IDiagramAnchorageMVCBundle damvcb : viewerManager.diagramAnchorageBundles.values()) { + Rectangle2D daRect = damvcb.getVisual().getCurrentBounds(); + if(daRect != null && selectionRect.contains(daRect)) { + addSelectedMVCBundle(damvcb); + } + } + } + + /** + * Starts the feedback of the link creation line and shows the possible target + * feedbacks. + */ + public void startNewLinkLineFeedback(IMVCBundle linkStartBundle, Node node, + Point2D locationInDiagram) { + if(node == null || locationInDiagram == null) { + return; + } + Point2D location = locationInDiagram; + linkLineFeedback.setStartX(location.getX()); + linkLineFeedback.setStartY(location.getY()); + linkLineFeedback.setEndX(location.getX()); + linkLineFeedback.setEndY(location.getY()); + if(linkLineFeedback.getParent() == null) { + layers.getVisualFeedbackLayer().add(linkLineFeedback, viewerManager.diagramBundle); + } + for(IMVCBundle possibleTarget : viewerManager.possibleLinkTargets) { + IController controller = possibleTarget.getController(); + MVCBundleTag effect = controller.getLinkTargetEffectForNewLinkFrom(linkStartBundle); + if(effect != null) { + possibleTarget.addTag(effect); + possibleTarget.getVisual().updateNodes(layers); + } + } + } + + /** Updates the end of the link creation line. */ + public void updateNewLinkLineFeedback(Point2D locationInDiagram, Paint color) { + linkLineFeedback.setEndX(locationInDiagram.getX()); + linkLineFeedback.setEndY(locationInDiagram.getY()); + linkLineFeedback.setStroke(color); + } + + /** Terminates the link creation line and possible target feedbacks. */ + public void terminateNewLinkLineFeedback() { + layers.getVisualFeedbackLayer().remove(linkLineFeedback); + clearLinkTargetFeedback(); + } + + /** Clears the link target feedback fronm all possible link targets. */ + private void clearLinkTargetFeedback() { + for(IMVCBundle possibleTarget : viewerManager.possibleLinkTargets) { + possibleTarget.removeTag(LINK_TARGET_ALLOWED_TAG); + possibleTarget.removeTag(LINK_TARGET_DENIED_TAG); + possibleTarget.removeTag(LINK_TARGET_MAYBE_TAG); + possibleTarget.getVisual().updateNodes(layers); + } + } + + /** Starts the feedback for relocating a link. */ + public void startMoveLinkFeedback(ILinkMVCBundle linkBundle, + IAnchorageMVCBundle movedStartOrEnd) { + for(IMVCBundle possibleTarget : viewerManager.possibleLinkTargets) { + IController controller = possibleTarget.getController(); + MVCBundleTag effect = + controller.getLinkTargetEffectForMoveLink(linkBundle, movedStartOrEnd); + if(effect != null) { + possibleTarget.addTag(effect); + possibleTarget.getVisual().updateNodes(layers); + } + } + } + + /** Terminates the link relocation feedbacks. */ + public void terminateMoveLinkFeedback() { + clearLinkTargetFeedback(); + } + + /** Returns the last known mouse location. */ + public DiagramCoordinate getLastMouseLocation() { + return getLayers().getMouseState().getLastMouseLocation(); + } + + /** Returns the current drag extent. */ + public DiagramCoordinate getDragExtent() { + return getLayers().getMouseState().getDragExtent(); + } + + /** + * Creates a drag controller for the given bundle, gesture, mouse location and source node also + * considering the current selection. + */ + public IDragController createDragController(IMVCBundle bundle, EDragGesture gesture, + Node source, DiagramCoordinate lastMouseLocation) { + DiagramViewerSelection sel = getSelection(); + if(!sel.isMultiSelection()) { + // single selection delegates to the underlying controller + return bundle.getController().getDragController(gesture, source, lastMouseLocation); + } + // return a composite controller + return new CompositeDragController(sel, gesture, source, lastMouseLocation); + } + + /** + * Creates a key press controller for the given bundle also considering the current selection. + */ + public IKeyPressController createKeyPressController(IMVCBundle bundle, Node source, + DiagramCoordinate lastMouseLocation) { + final DiagramViewerSelection sel = getSelection(); + if(sel.isMultiSelection()) { + return new IKeyPressController() { + /** {@inheritDoc} */ + @Override + public Change keyEvent(KeyEvent event, IMVCBundle bundle, Node source, + DiagramCoordinate mouseLocation) { + ChangeSet changeSet = new ChangeSet(); + // handle primary selection + IMVCBundle primaryBundle = sel.getPrimarySelection(); + IKeyPressController primaryKPC = primaryBundle.getController() + .getKeyPressController(source, lastMouseLocation); + Change primaryChg = + primaryKPC.keyEvent(event, primaryBundle, source, lastMouseLocation); + if(primaryChg != null) { + changeSet.add(primaryChg); + } + // handle secondary selection + for(IMVCBundle secondaryBundle : sel.getSecondarySelections()) { + IKeyPressController secondaryKPC = secondaryBundle.getController() + .getKeyPressController(source, lastMouseLocation); + Change secondaryChg = secondaryKPC.keyEvent(event, primaryBundle, source, + lastMouseLocation); + if(secondaryChg != null) { + changeSet.add(secondaryChg); + } + } + if(!changeSet.isEmpty()) { + return changeSet; + } + return null; + } + }; + } + return bundle.getController().getKeyPressController(source, lastMouseLocation); + } + + /** Composite drag controller delegates to a set of {@link IDragController}s. */ + private static final class CompositeDragController implements IDragController { + /** The contained controllers. */ + private final Map<IDragController, IMVCBundle> controllers = new HashMap<>(); + + /** Constructor. */ + public CompositeDragController(DiagramViewerSelection selection, EDragGesture gesture, + Node source, DiagramCoordinate lastMouseLocation) { + addDragController(selection.getPrimarySelection(), gesture, source, lastMouseLocation); + for(IMVCBundle sec : selection.getSecondarySelections()) { + addDragController(sec, gesture, source, lastMouseLocation); + } + } + + /** Creates a drag controller from the bundle's controller and adds it. */ + private void addDragController(IMVCBundle bundle, EDragGesture gesture, Node source, + DiagramCoordinate lastMouseLocation) { + if(bundle == null || bundle.getController() == null) { + return; + } + IDragController dc = + bundle.getController().getDragController(gesture, source, lastMouseLocation); + if(dc != null) { + controllers.put(dc, bundle); + } + } + + /** {@inheritDoc} */ + @Override + public void dragStarted(IMVCBundle startBundle, Node startNode, + DiagramCoordinate lastMouseLocation) { + for(IDragController dc : controllers.keySet()) { + IMVCBundle bundle = controllers.get(dc); + dc.dragStarted(bundle, startNode, lastMouseLocation); + } + } + + /** {@inheritDoc} */ + @Override + public void dragInProgress(IMVCBundle currentBundle, Node currentNode, + DiagramCoordinate lastMouseLocation) { + for(IDragController dc : controllers.keySet()) { + IMVCBundle bundle = controllers.get(dc); + dc.dragInProgress(bundle, currentNode, lastMouseLocation); + } + } + + /** {@inheritDoc} */ + @Override + public Change dragCompleted(IMVCBundle endBundle, Node endNode, + DiagramCoordinate lastMouseLocation) { + ChangeSet cset = new ChangeSet(); + for(IDragController dc : controllers.keySet()) { + Change chg = dc.dragCompleted(endBundle, endNode, lastMouseLocation); + if(chg != null) { + cset.add(chg); + } + } + if(!cset.isEmpty()) { + return cset; + } + return null; + } + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewerDefaultTags.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewerDefaultTags.java new file mode 100644 index 0000000000000000000000000000000000000000..6230763252409c60009ab8887b4ef582cf883229 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewerDefaultTags.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.MVCBundleTag; + +/** + * Predefined {@link MVCBundleTag}s for focusing, selecting, hovering, link targeting, and + * highlighting feedback. + */ +public final class DiagramViewerDefaultTags { + /** The tagged bundle has the focus. */ + public static final MVCBundleTag FOCUS_TAG = makeTag("_FOCUS"); + /** The tagged bundle is the primary selection of the diagram viewer. */ + public static final MVCBundleTag PRIMARY_SELECTION_TAG = makeTag("_PRIMARY"); + /** The tagged bundle belongs to the secondary selections of the diagram viewer. */ + public static final MVCBundleTag SECONDARY_SELECTION_TAG = makeTag("_SECONDARY"); + /** The tagged bundle is currently hover over by the mouse pointer. */ + public static final MVCBundleTag HOVER_TAG = makeTag("_HOVER"); + /** The tagged bundle is a current link target. */ + public static final MVCBundleTag LINK_TARGET_ALLOWED_TAG = makeTag("_LINKTARGET_ALLOWED"); + /** The tagged bundle is not a current link target. */ + public static final MVCBundleTag LINK_TARGET_DENIED_TAG = makeTag("_LINKTARGET_DENIED"); + /** The tagged bundle might be a current link target. */ + public static final MVCBundleTag LINK_TARGET_MAYBE_TAG = makeTag("_LINKTARGET_MAYBE"); + /** The tagged bundle is highlighted as incoming link */ + public static final MVCBundleTag HIGHLIGHT_INCOMING_LINK_TAG = + makeTag("_HIGHLIGHT_INCOMING_LINK"); + /** The tagged bundle is highlighted as outgoing link */ + public static final MVCBundleTag HIGHLIGHT_OUTGOING_LINK_TAG = + makeTag("_HIGHLIGHT_OUTGOING_LINK"); + + /** Creates the tag. */ + private static MVCBundleTag makeTag(String tagId) { + return new MVCBundleTag(DiagramViewerDefaultTags.class, tagId); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewerFeatures.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewerFeatures.java new file mode 100644 index 0000000000000000000000000000000000000000..31e3fb61f915b0d8695005b083c47ce1c5be0b05 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewerFeatures.java @@ -0,0 +1,283 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +import static java.lang.Math.floor; +import static java.lang.Math.max; +import static java.util.Objects.requireNonNull; +import static javafx.scene.paint.Color.LIGHTGRAY; + +import javafx.geometry.Dimension2D; +import javafx.geometry.Point2D; +import javafx.scene.paint.Color; + +/** + * This class handles the state of all features of the {@link DiagramViewer}, which can be toggled + * after instantiating the diagram viewer. + */ +public final class DiagramViewerFeatures { + /** The diagram viewer. */ + private final DiagramViewer viewer; + /** The zoom factors. */ + private double[] zoomFactors = new double[] {0.5, 0.75, 1, 1.5, 2}; + /** The current zoom factor array index. */ + private int zoomFactorIndex = 2; + /** Flag indicating whether link highlighting is enabled. */ + private boolean linkHighlightingEnabled = false; + /** Flag if interaction area shading is active. */ + private boolean interactionAreaShadingEnabled = false; + /** The horizontal and vertical spacing of the grid indicators. */ + private Dimension2D indicatorSpacing = new Dimension2D(20, 20); + /** The flag indicating that this grid is used to snap elements when moving or resizing. */ + private boolean useSnapToGrid = true; + /** The size of the grid indicators. */ + private double indicatorSize = 2; + /** The color of the grid indicator. */ + private Color indicatorColor = LIGHTGRAY.darker(); + /** The flag for drawing the outer border, which occupies the first grid row and lane. */ + private boolean drawOuterBorder = true; + /** The indicator type. */ + private IndicatorType indicatorType = IndicatorType.CROSS; + /** The background color. */ + private Color backgroundColor = LIGHTGRAY; + + /** Constructor. */ + public DiagramViewerFeatures(DiagramViewer diagramViewer) { + this.viewer = diagramViewer; + } + + /** Returns the zoom factors. */ + public double[] getZoomFactors() { + return zoomFactors; + } + + /** Sets the zoom factors. */ + public void setZoomFactors(double[] zoomFactors) { + this.zoomFactors = zoomFactors; + if(zoomFactorIndex >= zoomFactors.length) { + zoomFactorIndex = zoomFactors.length - 1; + } + viewer.updateAllVisuals(); + } + + /** Returns the zoom factor index. */ + public int getZoomFactorIndex() { + return zoomFactorIndex; + } + + /** Returns the current zoom factor. */ + public double getCurrentZoomFactor() { + return zoomFactorIndex == -1 ? 1.0 : zoomFactors[zoomFactorIndex]; + } + + /** Sets the zoom factor index. */ + public void setZoomFactorIndex(int zoomFactorIndex) { + this.zoomFactorIndex = zoomFactorIndex; + viewer.updateAllVisuals(); + } + + /** Increases zoom factor index by one up to the maximum. */ + public void zoomIn() { + if(zoomFactorIndex < zoomFactors.length - 1) { + zoomFactorIndex++; + viewer.updateAllVisuals(); + } + } + + /** Decreases zoom factor index by one down to the minimum. */ + public void zoomOut() { + if(zoomFactorIndex > 0) { + zoomFactorIndex--; + viewer.updateAllVisuals(); + } + } + + /** Returns the link highlighting enabled flag. */ + public boolean isLinkHighlightingEnabled() { + return linkHighlightingEnabled; + } + + /** Sets the link highlighting enabled. */ + public void setLinkHighlightingEnabled(boolean linkHighlightingEnabled) { + this.linkHighlightingEnabled = linkHighlightingEnabled; + viewer.updateAllVisuals(); + } + + /** Returns the interaction area shading enabled flag. */ + public boolean isInteractionAreaShadingEnabled() { + return interactionAreaShadingEnabled; + } + + /** Sets the interaction area shading enabled flag. */ + public void setInteractionAreaShadingEnabled(boolean interactionAreaShadingEnabled) { + this.interactionAreaShadingEnabled = interactionAreaShadingEnabled; + viewer.updateAllVisuals(); + } + + /** Returns the indicator type. */ + public IndicatorType getIndicatorType() { + return indicatorType; + } + + /** Sets the indicator type. */ + public void setIndicatorType(IndicatorType indicatorType) { + this.indicatorType = indicatorType; + viewer.updateAllVisuals(); + } + + /** Returns the background color. */ + public Color getBackgroundColor() { + return backgroundColor; + } + + /** Sets the background color. */ + public void setBackgroundColor(Color backgroundColor) { + this.backgroundColor = backgroundColor; + viewer.updateAllVisuals(); + } + + /** Returns the indicator color. */ + public Color getIndicatorColor() { + return indicatorColor; + } + + /** Sets the indicator color. */ + public void setIndicatorColor(Color indicatorColor) { + this.indicatorColor = indicatorColor; + viewer.updateAllVisuals(); + } + + /** Returns the indicator size. */ + public double getIndicatorSize() { + return indicatorSize; + } + + /** Sets the indicator size. */ + public void setIndicatorSize(double indicatorSize) { + this.indicatorSize = indicatorSize; + viewer.updateAllVisuals(); + } + + /** Returns the indicator spacing. */ + public Dimension2D getIndicatorSpacing() { + return indicatorSpacing; + } + + /** Returns the horizontal spacing. */ + public double getHorizontalSpacing() { + return indicatorSpacing.getWidth(); + } + + /** Returns the vertical spacing. */ + public double getVerticalSpacing() { + return indicatorSpacing.getHeight(); + } + + /** Sets the indicator spacing. */ + public void setIndicatorSpacing(Dimension2D indicatorSpacing) { + this.indicatorSpacing = requireNonNull(indicatorSpacing); + viewer.updateAllVisuals(); + } + + /** Returns whether to use snap to grid. */ + public boolean useSnapToGrid() { + return useSnapToGrid; + } + + /** Sets the use snap to grid flag. */ + public void setUseSnapToGrid(boolean useSnapToGrid) { + this.useSnapToGrid = useSnapToGrid; + viewer.updateAllVisuals(); + } + + /** Computes the closest horizontal grid coordinate of the given point. */ + public double snapToGridX(double x) { + if(useSnapToGrid) { + double width = indicatorSpacing.getWidth(); + return floor(x / width) * width; + } + return x; + } + + /** Computes the closest vertical grid coordinate of the given point. */ + public double snapToGridY(double y) { + if(useSnapToGrid) { + double height = indicatorSpacing.getHeight(); + return floor(y / height) * height; + } + return y; + } + + /** Computes the closest grid location of the given point. */ + public Point2D snapToGrid(Point2D point) { + return snapToGrid(point.getX(), point.getY()); + } + + /** Computes the closest grid location of the given point. */ + public Point2D snapToGrid(double x, double y) { + return new Point2D(snapToGridX(x), snapToGridY(y)); + } + + /** + * Computes the closest horizontal coordinate of the given point that lies in the center between + * the grid points. + */ + public double snapToCenterX(double x) { + double hs2 = indicatorSpacing.getWidth() / 2; + return snapToGridX(x - hs2) + hs2; + } + + /** + * Computes the closest vertical coordinate of the given point that lies in the center between + * the grid points. + */ + public double snapToCenterY(double y) { + double vs2 = indicatorSpacing.getHeight() / 2; + return snapToGridY(y - vs2) + vs2; + } + + /** Computes the closest center location of the given point. */ + public DiagramCoordinate snapToCenter(DiagramCoordinate point) { + return snapToCenter(point.getX(), point.getY()); + } + + /** Computes the closest center location of the given point. */ + public DiagramCoordinate snapToCenter(double x, double y) { + return new DiagramCoordinate(snapToCenterX(x), snapToCenterY(y)); + } + + /** Returns the draw outer border flag. */ + public boolean isDrawOuterBorder() { + return drawOuterBorder; + } + + /** Sets the draw outer border. */ + public void setDrawOuterBorder(boolean drawOuterBorder) { + this.drawOuterBorder = drawOuterBorder; + viewer.updateAllVisuals(); + } + + /** The indicator type used for the grid points. */ + public static enum IndicatorType { + /** Invisible indicator. */ + INVISIBLE, + /** Circle indicator. */ + CIRCLE, + /** Cross indicator. */ + CROSS, + /** Square indicator. */ + SQUARE; + } + + /** Returns the maximum of the horizontal and vertical spacing. */ + public double getMaximumSpacing() { + return max(getVerticalSpacing(), getHorizontalSpacing()); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewerSelection.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewerSelection.java new file mode 100644 index 0000000000000000000000000000000000000000..e833f592543bc97077907d980a39b123fc4044e6 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/DiagramViewerSelection.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; + +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; + +/** + * A {@link DiagramViewerSelection} wraps {@link IMVCBundle}s, which are currently selected. Each + * selection consists of a primary selection and zero or more secondary selections. + */ +public final class DiagramViewerSelection { + /** The primary selection. */ + private final IMVCBundle primarySelection; + /** The list of secondary selections. */ + private final List<IMVCBundle> secondarySelections; + + /** Constructor. */ + public DiagramViewerSelection(IMVCBundle primary) { + this(primary, emptyList()); + } + + /** Constructor. */ + public DiagramViewerSelection(IMVCBundle primary, List<IMVCBundle> secondary) { + this.primarySelection = primary; + this.secondarySelections = unmodifiableList(secondary); + } + + /** Returns the primary selection. */ + public IMVCBundle getPrimarySelection() { + return primarySelection; + } + + /** Returns the secondary selections. */ + public List<IMVCBundle> getSecondarySelections() { + return secondarySelections; + } + + /** Returns whether nothing is selected. */ + public boolean isEmpty() { + return primarySelection == null && secondarySelections.isEmpty(); + } + + /** Returns whether multiple elements are currently selected. */ + public boolean isMultiSelection() { + return primarySelection != null && !secondarySelections.isEmpty(); + } + + /** Returns whether the given bundle is selected or not. */ + public boolean isSelected(IMVCBundle bundle) { + return primarySelection == bundle || secondarySelections.contains(bundle); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/EDragGesture.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/EDragGesture.java new file mode 100644 index 0000000000000000000000000000000000000000..5cfa098d3877db11981c2750e5e103156d62fc5e --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/EDragGesture.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +/** Enumeration of gestures provided when the mouse is dragged at certain locations of visuals. */ +public enum EDragGesture { + /** No dragging initiated. */ + NONE, + /** Move gesture for diagram content and anchorage visuals. */ + MOVE, + /** Resize vertically gesture. */ + RESIZE_V, + /** Resize horizontally gesture. */ + RESIZE_H, + /** Resize vertical and horizontal gesture. */ + RESIZE_VH, + /** Create a link gesture. */ + NEW_LINK, + /** Moves the start or end point of a link. */ + RECONNECT_LINK, + /** Create a bend point gesture. */ + NEW_BENDPOINT, + /** Move drag gesture for an existing bend point. */ + MOVE_BENDPOINT, + /** Selection drag gesture. */ + SELECTION; +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/FeedbackChange.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/FeedbackChange.java new file mode 100644 index 0000000000000000000000000000000000000000..b088fa89af648f1674f2f9c1f7f99d585ce801ca --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/FeedbackChange.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; + +/** + * This class encapsulates the relative position and size changes occurring during feedback + * operations, e.g., when the user is dragging a visual to move or resize it with a mouse gesture. + */ +public final class FeedbackChange { + /** The empty feedback change. */ + public static final FeedbackChange EMPTY = new FeedbackChange(); + + /** The relative X position change of this feedback change. */ + private final double deltaX; + /** The relative Y position change of this feedback change. */ + private final double deltaY; + /** The relative width change of this feedback change. */ + private final double deltaW; + /** The relative height change of this feedback change. */ + private final double deltaH; + + /** Constructor. */ + public FeedbackChange(double dx, double dy, double dw, double dh) { + this.deltaX = dx; + this.deltaY = dy; + this.deltaW = dw; + this.deltaH = dh; + } + + /** Constructor. */ + public FeedbackChange() { + this(0, 0, 0, 0); + } + + /** Returns the X position change. */ + public double getDeltaX() { + return deltaX; + } + + /** Returns Y position change. */ + public double getDeltaY() { + return deltaY; + } + + /** Returns width change. */ + public double getDeltaW() { + return deltaW; + } + + /** Returns the height change. */ + public double getDeltaH() { + return deltaH; + } + + /** Returns whether this feedback change is empty. */ + public boolean isEmpty() { + return deltaX == 0 && deltaY == 0 && deltaW == 0 && deltaH == 0; + } + + /** Applies this change to the given rectangle. */ + public Rectangle2D applyTo(Rectangle2D b) { + return new Rectangle2D(b.getMinX() + deltaX, b.getMinY() + deltaY, b.getWidth() + deltaW, + b.getHeight() + deltaH); + } + + /** Applies the x and y change to the given point. */ + public Point2D applyToPoint(Point2D p) { + return new Point2D(p.getX() + deltaX, p.getY() + deltaY); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[").append(deltaX); + sb.append(",").append(deltaY); + sb.append(",").append(deltaW); + sb.append(",").append(deltaH).append("]"); + return sb.toString(); + } + + /** Returns a {@link FeedbackChange} with a location change. */ + public static FeedbackChange locationFeedbackChange(double dx, double dy) { + return new FeedbackChange(dx, dy, 0, 0); + } + + /** Returns a {@link FeedbackChange} with a size change. */ + public static FeedbackChange sizeFeedbackChange(double dw, double dh) { + return new FeedbackChange(0, 0, dw, dh); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/GridCanvasVisual.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/GridCanvasVisual.java new file mode 100644 index 0000000000000000000000000000000000000000..734027d56af342cd01ff445ba9347b8dbb6c83c2 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/GridCanvasVisual.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures.IndicatorType; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; + +import javafx.geometry.Bounds; +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; +import javafx.scene.canvas.Canvas; +import javafx.scene.canvas.GraphicsContext; +import javafx.scene.paint.Color; + +/** + * {@link IVisual} for the diagram background. Note that the {@link Canvas} used here is always as + * big as the diagram viewer's content area and the grid is painted with an according displacement. + * Having the canvas as big as the diagram will most certainly result in video memory problems, + * which requires to implement the background in this special way. + */ +final class GridCanvasVisual implements IVisual { + /** The diagram bundle. */ + private final DiagramViewer viewer; + /** The FX canvas to be drawn on. */ + private final Canvas gridCanvas; + /** The displacement caused by scroll bars. */ + private DiagramCoordinate displacement = new DiagramCoordinate(0, 0); + + /** Constructor. */ + public GridCanvasVisual(DiagramViewer viewer) { + this.viewer = viewer; + // the background grid canvas + this.gridCanvas = new Canvas(100, 100); + gridCanvas.widthProperty().addListener(evt -> { + paintGrid(); + }); + gridCanvas.heightProperty().addListener(evt -> { + paintGrid(); + }); + } + + /** Returns gridCanvas. */ + /* package */Canvas getGridCanvas() { + return gridCanvas; + } + + /** {@inheritDoc} */ + @Override + public boolean enableInteraction() { + return false; + } + + /** {@inheritDoc} */ + @Override + public boolean enableVisual() { + return true; + } + + /** {@inheritDoc} */ + @Override + public IMVCBundle getMVCBundle() { + return viewer.getDiagramBundle(); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + if(gridCanvas.getParent() == null) { + // grid canvas is added below the diagram layers + viewer.getViewerPane().getChildren().add(0, gridCanvas); + viewer.getLayers().getMouseState().registerMouseListeners(gridCanvas, getMVCBundle()); + gridCanvas.setOnScroll(viewer.getScrollingHandler()); + } + paintGrid(); + } + + /** {@inheritDoc} */ + @Override + public void removeAllVisuals(DiagramLayers layers) { + viewer.getLayers().getMouseState().unregisterMouseListeners(gridCanvas); + viewer.getViewerPane().getChildren().remove(gridCanvas); + gridCanvas.setOnScroll(null); + } + + /** {@inheritDoc} */ + @Override + public Rectangle2D getModelBounds() { + Bounds b = gridCanvas.getBoundsInParent(); + return new Rectangle2D(b.getMinX(), b.getMinY(), b.getWidth(), b.getHeight()); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getLinkAnchorage(DiagramCoordinate indication) { + DiagramViewerFeatures features = viewer.getFeatures(); + return indication.add(features.getHorizontalSpacing() / 2, + features.getVerticalSpacing() / 2); + } + + /** {@inheritDoc} */ + @Override + public Rectangle2D getCurrentBounds() { + return getModelBounds(); + } + + /** {@inheritDoc} */ + @Override + public void requestFocus() { + gridCanvas.requestFocus(); + } + + /** Paints the grid. */ + public void paintGrid() { + DiagramViewerFeatures features = viewer.getFeatures(); + double zf = features.getCurrentZoomFactor(); + double width = gridCanvas.getWidth(); + double height = gridCanvas.getHeight(); + GraphicsContext gc = gridCanvas.getGraphicsContext2D(); + // background + Color backgroundColor = features.getBackgroundColor(); + gc.setFill(backgroundColor); + gc.setStroke(backgroundColor); + gc.fillRect(0, 0, width, height); + // outer border and indicators + Color indicatorColor = features.getIndicatorColor(); + gc.setStroke(indicatorColor); + gc.setFill(indicatorColor); + double hSpacing = features.getHorizontalSpacing(); + double vSpacing = features.getVerticalSpacing(); + double dx = displacement.getX() % 1.0; + double dy = displacement.getY() % 1.0; + double zfhs = zf * hSpacing; + double zfvs = zf * vSpacing; + if(features.isDrawOuterBorder()) { + if(displacement.getX() < 1.0) { + gc.fillRect(0, 0, zfhs * (1.0 - dx), height); + } + if(displacement.getY() < 1.0) { + gc.fillRect(0, 0, width, zfvs * (1.0 - dy)); + } + } + // abort if no indicators should be drawn + IndicatorType indicatorType = features.getIndicatorType(); + if(indicatorType == IndicatorType.INVISIBLE) { + return; + } + // draw grid indicators + double isize = viewer.getFeatures().getIndicatorSize() * zf; + for(double x = zfhs * (1.0 - dx); x < width; x += zfhs) { + for(double y = zfvs * (1.0 - dy); y < height; y += zfvs) { + drawIndicator(gc, x, y, isize, indicatorType); + } + } + } + + /** Draws the indicators. */ + private void drawIndicator(GraphicsContext gc, double x, double y, double size, + IndicatorType indicatorType) { + switch(indicatorType) { + case CIRCLE: + drawCircle(gc, x, y, size); + break; + case CROSS: + drawCross(gc, x, y, size); + break; + case SQUARE: + drawSquare(gc, x, y, size); + break; + default: + break; + } + } + + /** Draws a small cross at the given point. */ + private void drawCircle(GraphicsContext gc, double x, double y, double size) { + gc.fillOval(x - size, y - size, 2 * size, 2 * size); + } + + /** Draws a small cross at the given point. */ + private void drawCross(GraphicsContext gc, double x, double y, double size) { + gc.strokeLine(x - size, y, x + size, y); + gc.strokeLine(x, y - size, x, y + size); + } + + /** Draws a small square at the given point. */ + private void drawSquare(GraphicsContext gc, double x, double y, double size) { + gc.fillRect(x - size, y - size, 2 * size, 2 * size); + } + + /** {@inheritDoc} */ + @Override + public EDragGesture getDragGesture(Node node, DiagramCoordinate diagramLocation) { + if(node == gridCanvas) { + return EDragGesture.SELECTION; + } + return EDragGesture.NONE; + } + + /** Sets horizontal scroll displacement. */ + public void setScrollX(double canvasDeltaX) { + displacement = new DiagramCoordinate(canvasDeltaX, displacement.getY()); + paintGrid(); + } + + /** Sets vertical scroll displacement. */ + public void setScrollY(double canvasDeltaY) { + displacement = new DiagramCoordinate(displacement.getX(), canvasDeltaY); + paintGrid(); + } + + /** Sets the width of the grid canvas. */ + public void setWidth(double w) { + gridCanvas.setWidth(w); + } + + /** Sets the height of the grid canvas. */ + public void setHeight(double h) { + gridCanvas.setHeight(h); + } + + /** Relocates the given scene point to local coordinates. */ + public Point2D sceneToLocal(Point2D locationInScene) { + return gridCanvas.sceneToLocal(locationInScene); + } + + /** Returns whether the given node is the grid canvas. */ + public boolean isGridCanvas(Node source) { + return source == gridCanvas; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/MVCBundleManager.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/MVCBundleManager.java new file mode 100644 index 0000000000000000000000000000000000000000..2b4ab114c55b30a3d98d7135458f8f3ddd244d58 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/MVCBundleManager.java @@ -0,0 +1,638 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.FOCUS_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.PRIMARY_SELECTION_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.SECONDARY_SELECTION_TAG; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IControllerFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundleWithParent; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.ContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.ContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.DiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.DiagramMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.LinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.MVCBundleBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IDiagramAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.ILinkVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisualFactory; + +/** + * This class encapsulates all methods for handling {@link IMVCBundle}s (including selection) + * management and is solely used by {@link DiagramViewer} (i.e. its purpose is to reduce the size of + * the {@link DiagramViewer} class). + */ +/* package */final class MVCBundleManager { + /** The diagram viewer. */ + private final DiagramViewer viewer; + /** The diagram layers. */ + private final DiagramLayers layers; + /** The selection changed callback used to notify about selection changes. */ + private final Consumer<DiagramViewerSelection> selectionChangedCallback; + /** + * The model modifier is called once a change (or a set of changes) needs to be applied to the + * underlying data model. If this reference is {@code null}, the framework simply calls the + * controller implementations. + */ + private final Consumer<Change> modelModifier; + /** The factory for model elements displayed within this viewer. */ + /* package */ final IModelFactory modelFactory; + /** The factory for visuals of this viewer. */ + /* package */ final IVisualFactory visualFactory; + /** The factory for controllers of this viewer. */ + /* package */ final IControllerFactory controllerFactory; + /** The grid canvas visual. */ + /* package */ GridCanvasVisual gridCanvasVisual; + /** The {@link IMVCBundle}s for content elements. */ + /* package */ Map<Object, IContentMVCBundle> contentBundles = new HashMap<>(); + /** The {@link IMVCBundle}s for diagram anchorage elements. */ + /* package */ Map<Object, IDiagramAnchorageMVCBundle> diagramAnchorageBundles = new HashMap<>(); + /** The {@link IMVCBundle}s for content anchorage elements. */ + /* package */ Map<Object, IContentAnchorageMVCBundle> contentAnchorageBundles = new HashMap<>(); + /** The {@link IMVCBundle}s for link elements. */ + /* package */ Map<Object, ILinkMVCBundle> linkBundles = new HashMap<>(); + /** The list of possible link targets. */ + /* package */ List<IMVCBundle> possibleLinkTargets = new ArrayList<IMVCBundle>(); + /** The {@link IDiagramMVCBundle} of this viewer. */ + /* package */ IDiagramMVCBundle diagramBundle; + /** The selected {@link IMVCBundle}. */ + /* package */ IMVCBundle primarySelectedBundle; + /** The list of secondary selected {@link IMVCBundle}. */ + /* package */ List<IMVCBundle> secondarySelectedBundles = new LinkedList<>(); + + /** Constructor. */ + /* package */ MVCBundleManager(DiagramViewer viewer, IModelFactory modelFactory, + IVisualFactory visualFactory, IControllerFactory controllerFactory, + DiagramLayers layers, Consumer<DiagramViewerSelection> selectionChangedCallback, + Consumer<Change> modelModifier) { + this.viewer = viewer; + this.gridCanvasVisual = new GridCanvasVisual(viewer); + this.modelFactory = modelFactory; + this.visualFactory = visualFactory; + this.controllerFactory = controllerFactory; + this.layers = layers; + this.selectionChangedCallback = selectionChangedCallback; + this.modelModifier = modelModifier; + } + + /** Returns the model modifier. */ + public Consumer<Change> getModelModifier() { + return modelModifier; + } + + /** Sets the selected {@link IMVCBundle} as single primary selection. */ + /* package */ void setSingleSelectedMVCBundle(IMVCBundle sel) { + if(primarySelectedBundle == sel) { + return; + } + // single selection on other than primary clears multi-selection + for(IMVCBundle mvcb : secondarySelectedBundles) { + mvcb.removeTag(SECONDARY_SELECTION_TAG); + mvcb.getVisual().updateNodes(layers); + } + secondarySelectedBundles.clear(); + // single selection clears old selection + IMVCBundle oldSelection = primarySelectedBundle; + if(oldSelection != null) { + oldSelection.removeTag(FOCUS_TAG); + oldSelection.removeTag(PRIMARY_SELECTION_TAG); + oldSelection.getVisual().updateNodes(layers); + } + primarySelectedBundle = sel; + if(primarySelectedBundle != null) { + primarySelectedBundle.getVisual().requestFocus(); + primarySelectedBundle.addTag(FOCUS_TAG); + primarySelectedBundle.addTag(PRIMARY_SELECTION_TAG); + primarySelectedBundle.getVisual().updateNodes(layers); + } + fireSelectionChanged(); + } + + /** + * Adds the given bundle as primary selection and moves the current selection to the secondary + * selections list. + */ + /* package */ void addSelectedMVCBundle(IMVCBundle sel) { + if(primarySelectedBundle == sel || secondarySelectedBundles.contains(sel)) { + return; + } + // make current primary selection into a secondary selection + if(primarySelectedBundle != null) { + primarySelectedBundle.removeTag(PRIMARY_SELECTION_TAG); + primarySelectedBundle.addTag(SECONDARY_SELECTION_TAG); + secondarySelectedBundles.add(primarySelectedBundle); + primarySelectedBundle.getVisual().updateNodes(layers); + } + // set new primary selection + primarySelectedBundle = sel; + if(primarySelectedBundle != null) { + primarySelectedBundle.addTag(PRIMARY_SELECTION_TAG); + primarySelectedBundle.getVisual().updateNodes(layers); + } + fireSelectionChanged(); + } + + /** + * Removes the given bundle from either the primary selection (selecting next the secondary list + * element) or from the secondary selection list. + */ + /* package */ void removeSelectedMVCBundle(IMVCBundle sel) { + if(primarySelectedBundle == sel) { + sel.removeTag(PRIMARY_SELECTION_TAG); + if(secondarySelectedBundles.isEmpty()) { + primarySelectedBundle = null; + } else { + primarySelectedBundle = + secondarySelectedBundles.remove(secondarySelectedBundles.size() - 1); + primarySelectedBundle.removeTag(SECONDARY_SELECTION_TAG); + primarySelectedBundle.addTag(PRIMARY_SELECTION_TAG); + primarySelectedBundle.getVisual().updateNodes(layers); + } + } else { + sel.removeTag(SECONDARY_SELECTION_TAG); + secondarySelectedBundles.remove(sel); + } + sel.getVisual().updateNodes(layers); + fireSelectionChanged(); + } + + /** Calls the {@link #selectionChangedCallback} to inform it about a selection change. */ + private void fireSelectionChanged() { + if(selectionChangedCallback != null) { + selectionChangedCallback.accept(getSelection()); + } + } + + /** Initializes the viewer content. */ + /* package */ void updateContentObjects() { + possibleLinkTargets.clear(); + // remember old selection + Object oldSelection = + primarySelectedBundle != null ? primarySelectedBundle.getModel() : null; + if(oldSelection == null) { + setSingleSelectedMVCBundle(null); + } + // before creating any content, allow model factory to update + modelFactory.update(); + // initialize the new hash maps + Map<Object, IContentMVCBundle> newContentBundles = new HashMap<>(); + Map<Object, IDiagramAnchorageMVCBundle> newDiagramAnchorageBundles = new HashMap<>(); + Map<Object, IContentAnchorageMVCBundle> newContentAnchorageBundles = new HashMap<>(); + Map<Object, ILinkMVCBundle> newLinkBundles = new HashMap<>(); + // construct model by moving MVCBs from the old maps to the new ones + updateDiagramBundle(); + oldSelection = updateContentAndContentAnchorageBundles(oldSelection, newContentBundles, + newContentAnchorageBundles); + oldSelection = updateDiagramAnchorages(oldSelection, newDiagramAnchorageBundles); + oldSelection = updateLinkBundles(oldSelection, newDiagramAnchorageBundles, + newContentAnchorageBundles, newLinkBundles); + // select the diagram if the old selection belongs to a bundle which is not + // contained in the new bundle maps + if(oldSelection != null) { + setSingleSelectedMVCBundle(diagramBundle); + } + // iterate through all old maps and remove MVCB references + detachAndRemoveRemainingMVCB(); + // set new maps active + this.contentBundles = newContentBundles; + this.diagramAnchorageBundles = newDiagramAnchorageBundles; + this.contentAnchorageBundles = newContentAnchorageBundles; + this.linkBundles = newLinkBundles; + // update visuals + updateAllVisuals(); + } + + /** Performs an update of the link bundles. */ + private Object updateLinkBundles(Object oldSelection, + Map<Object, IDiagramAnchorageMVCBundle> newDiagramAnchorageBundles, + Map<Object, IContentAnchorageMVCBundle> newContentAnchorageBundles, + Map<Object, ILinkMVCBundle> newLinkBundles) { + for(Object link : modelFactory.getLinkModels()) { + ILinkMVCBundle bundle = linkBundles.remove(link); + if(bundle == null) { + bundle = initLinks(link, newDiagramAnchorageBundles, newContentAnchorageBundles); + } else { + reinitLinks(bundle, newDiagramAnchorageBundles, newContentAnchorageBundles); + } + newLinkBundles.put(link, bundle); + if(link == oldSelection) { + setSingleSelectedMVCBundle(bundle); + oldSelection = null; + } + } + return oldSelection; + } + + /** Performs an update of the diagram anchorage bundles. */ + private Object updateDiagramAnchorages(Object oldSelection, + Map<Object, IDiagramAnchorageMVCBundle> newDiagramAnchorageBundles) { + for(Object m : modelFactory.getDiagramAnchorageModels()) { + IDiagramAnchorageMVCBundle bundle = diagramAnchorageBundles.remove(m); + if(bundle == null) { + bundle = initDiagramAnchorages(m); + } + newDiagramAnchorageBundles.put(m, bundle); + if(m == oldSelection) { + setSingleSelectedMVCBundle(bundle); + oldSelection = null; + } + if(bundle.getController().isPossibleLinkTarget()) { + possibleLinkTargets.add(bundle); + } + } + return oldSelection; + } + + /** Performs an update of the parent relationship of content bundles. */ + private void updateParentRelationship(Map<Object, IContentMVCBundle> newContentBundles, + List<?> contentModels) { + for(Object m : contentModels) { + Object parentModel = modelFactory.getParent(m); + if(parentModel != null) { + IContentMVCBundle parentBundle = newContentBundles.get(parentModel); + IContentMVCBundleWithParent elementBundle = + (IContentMVCBundleWithParent)newContentBundles.get(m); + if(elementBundle != null && parentBundle != null) { + elementBundle.setParentBundle(parentBundle); + } + } + } + } + + /** Performs an update of the content bundles and content anchorage bundles. */ + private Object updateContentAndContentAnchorageBundles(Object oldSelection, + Map<Object, IContentMVCBundle> newContentBundles, + Map<Object, IContentAnchorageMVCBundle> newContentAnchorageBundles) { + List<?> contentModels = modelFactory.getContentModels(); + for(Object m : contentModels) { + IContentMVCBundle bundle = contentBundles.remove(m); + if(bundle == null) { + bundle = initContentBundle(m); + } + newContentBundles.put(m, bundle); + if(m == oldSelection) { + setSingleSelectedMVCBundle(bundle); + oldSelection = null; + } + if(bundle.getController().isPossibleLinkTarget()) { + possibleLinkTargets.add(bundle); + } + // initialize content anchorages + for(Object cElement : modelFactory.getContentAnchorageModels(m)) { + IContentAnchorageMVCBundle cBundle = contentAnchorageBundles.remove(cElement); + if(cBundle == null) { + cBundle = initSingleAttachedContentAnchorage(bundle, cElement); + } + newContentAnchorageBundles.put(cElement, cBundle); + if(cElement == oldSelection) { + setSingleSelectedMVCBundle(cBundle); + oldSelection = null; + } + if(cBundle.getController().isPossibleLinkTarget()) { + possibleLinkTargets.add(cBundle); + } + } + // clear child bundles (will be setup again in the next loop if child has not + // disappeared) + bundle.getChildren().clear(); + } + // setup parent relationship between content + updateParentRelationship(newContentBundles, contentModels); + + return oldSelection; + } + + /** Performs an update of the diagram bundle. */ + private void updateDiagramBundle() { + boolean attached = true; + if(diagramBundle != null && diagramBundle.getModel() != modelFactory.getRootModel()) { + gridCanvasVisual.removeAllVisuals(layers); + diagramBundle.detach(); + diagramBundle.remove(); + gridCanvasVisual = null; + diagramBundle = null; + } + if(diagramBundle == null) { + diagramBundle = new DiagramMVCBundle(modelFactory.getRootModel(), viewer); + gridCanvasVisual = new GridCanvasVisual(viewer); + diagramBundle.setVisual(gridCanvasVisual); + attached = false; + } + IController diagramController = controllerFactory.createDiagramController(diagramBundle); + diagramBundle.setController(diagramController); + if(!attached) { + diagramBundle.attach(); + } + if(diagramController.isPossibleLinkTarget()) { + possibleLinkTargets.add(diagramBundle); + } + } + + /** Detaches and removes all remaining {@link IMVCBundle}s. */ + private void detachAndRemoveRemainingMVCB() { + for(ILinkMVCBundle b : linkBundles.values()) { + b.getVisual().removeAllVisuals(layers); + b.detach(); + b.remove(); + } + for(IContentAnchorageMVCBundle b : contentAnchorageBundles.values()) { + b.getVisual().removeAllVisuals(layers); + b.detach(); + b.remove(); + } + for(IDiagramAnchorageMVCBundle b : diagramAnchorageBundles.values()) { + b.getVisual().removeAllVisuals(layers); + b.detach(); + b.remove(); + } + for(IContentMVCBundle b : contentBundles.values()) { + b.getVisual().removeAllVisuals(layers); + b.detach(); + b.remove(); + } + } + + /** + * Initializes the {@link MVCBundleBase} for the given element and adds it to the + * visuals. + */ + private IContentMVCBundle initContentBundle(Object m) { + IContentMVCBundle mvcb = new ContentMVCBundle(m, diagramBundle, viewer); + IContentVisual visual = visualFactory.createContentVisual(mvcb); + mvcb.setVisual(visual); + IController contentController = controllerFactory.createContentController(mvcb); + mvcb.setController(contentController); + mvcb.attach(); + return mvcb; + } + + /** + * Initializes the {@link MVCBundleBase} for the given element and adds it to the + * visuals. + */ + private DiagramAnchorageMVCBundle initDiagramAnchorages(Object m) { + DiagramAnchorageMVCBundle mvcb = new DiagramAnchorageMVCBundle(m, diagramBundle, viewer); + IDiagramAnchorageVisual visual = visualFactory.createDiagramAnchorageVisual(mvcb); + mvcb.setVisual(visual); + IController diagramAnchorageController = + controllerFactory.createDiagramAnchorageController(mvcb); + mvcb.setController(diagramAnchorageController); + mvcb.attach(); + return mvcb; + } + + /** Handles the focus reaction when the focus property is set to {@code focus}. */ + /* package */ void handleFocus(boolean focus, DiagramLayers layers) { + if(primarySelectedBundle == null) { + return; + } + if(focus) { + primarySelectedBundle.addTag(FOCUS_TAG); + } else { + primarySelectedBundle.removeTag(FOCUS_TAG); + } + primarySelectedBundle.getVisual().updateNodes(layers); + } + + /** Adds a content anchorage for the given element and content bundle. */ + private IContentAnchorageMVCBundle initSingleAttachedContentAnchorage(IContentMVCBundle pBundle, + Object cElement) { + IContentAnchorageMVCBundle cBundle = + new ContentAnchorageMVCBundle(cElement, pBundle, viewer); + IContentAnchorageVisual cVisual = visualFactory.createContentAnchorageVisual(cBundle); + cBundle.setVisual(cVisual); + IController contentAnchorageController = + controllerFactory.createContentAnchorageController(cBundle); + cBundle.setController(contentAnchorageController); + cBundle.attach(); + return cBundle; + } + + /** Initializes the link {@link ILinkMVCBundle}s. */ + private ILinkMVCBundle initLinks(Object link, + Map<Object, IDiagramAnchorageMVCBundle> diagramAnchorageBundles, + Map<Object, IContentAnchorageMVCBundle> contentAnchorageBundles) { + Object startModel = modelFactory.getLinkStart(link); + if(startModel == null) { + return null; + } + IAnchorageMVCBundle startBundle = getBundleForAnchorageModel(startModel, + diagramAnchorageBundles, contentAnchorageBundles); + Object endModel = modelFactory.getLinkEnd(link); + if(endModel == null) { + return null; + } + IAnchorageMVCBundle endBundle = getBundleForAnchorageModel(endModel, + diagramAnchorageBundles, contentAnchorageBundles); + LinkMVCBundle lBundle = + new LinkMVCBundle(link, diagramBundle, startBundle, endBundle, viewer); + ILinkVisual visual = visualFactory.createLinkVisual(lBundle); + lBundle.setVisual(visual); + IController linkController = controllerFactory.createLinkController(lBundle); + lBundle.setController(linkController); + lBundle.attach(); + if(linkController.isPossibleLinkTarget()) { + possibleLinkTargets.add(lBundle); + } + return lBundle; + } + + /** Updates source and target of the link {@link ILinkMVCBundle}s. */ + private void reinitLinks(ILinkMVCBundle linkBundle, + Map<Object, IDiagramAnchorageMVCBundle> diagramAnchorageBundles, + Map<Object, IContentAnchorageMVCBundle> contentAnchorageBundles) { + Object link = linkBundle.getModel(); + Object startModel = modelFactory.getLinkStart(link); + if(startModel == null) { + throw new IllegalArgumentException("Link start model element cannot be null!"); + } + IAnchorageMVCBundle startBundle = getBundleForAnchorageModel(startModel, + diagramAnchorageBundles, contentAnchorageBundles); + Object endModel = modelFactory.getLinkEnd(link); + if(endModel == null) { + throw new IllegalArgumentException("Link target model element cannot be null!"); + } + IAnchorageMVCBundle endBundle = getBundleForAnchorageModel(endModel, + diagramAnchorageBundles, contentAnchorageBundles); + linkBundle.updateLinkBundle(startBundle, endBundle); + } + + /** Returns the {@link MVCBundleBase} of the given anchorage node's model. */ + private IAnchorageMVCBundle getBundleForAnchorageModel(Object m, + Map<Object, IDiagramAnchorageMVCBundle> diagramAnchorageBundles, + Map<Object, IContentAnchorageMVCBundle> contentAnchorageBundles) { + IAnchorageMVCBundle result = diagramAnchorageBundles.get(m); + if(result != null) { + return result; + } + result = contentAnchorageBundles.get(m); + if(result != null) { + return result; + } + throw new IllegalArgumentException( + "Anchorage node model is neither a diagram nor a content anchorage."); + } + + /** Updates every visual (and only its appearance, not the structure). */ + /* package */ void updateAllVisuals() { + layers.setScale(viewer.getFeatures().getCurrentZoomFactor()); + viewer.updateGridCanvasSize(); + gridCanvasVisual.updateNodes(layers); + for(IContentMVCBundle b : contentBundles.values()) { + if(b instanceof IContentMVCBundleWithParent) { + IContentMVCBundleWithParent wp = (IContentMVCBundleWithParent)b; + if(wp.getParentBundle() == null) { + updateChildContentVisualsRecursively(b); + } + } else { + updateChildContentVisualsRecursively(b); + } + } + for(IDiagramAnchorageMVCBundle b : diagramAnchorageBundles.values()) { + b.getVisual().updateNodes(layers); + } + for(IContentAnchorageMVCBundle b : contentAnchorageBundles.values()) { + b.getVisual().updateNodes(layers); + } + for(ILinkMVCBundle b : linkBundles.values()) { + b.getVisual().updateNodes(layers); + } + } + + /** Updates all child content visuals recursively (but does not update anchorages and links). */ + private void updateChildContentVisualsRecursively(IContentMVCBundle bundle) { + bundle.getVisual().updateNodes(layers); + for(IContentMVCBundle cb : bundle.getChildren()) { + updateChildContentVisualsRecursively(cb); + } + } + + /** Updates the nodes selectively adhering to the relation given by the {@link IMVCBundle}s. */ + /* package */ void updateVisual(IMVCBundle bundle) { + if(bundle instanceof IContentMVCBundle) { + updateContentVisual((IContentMVCBundle)bundle); + } else if(bundle instanceof IAnchorageMVCBundle) { + updateAnchorage((IAnchorageMVCBundle)bundle); + } else if(bundle instanceof ILinkMVCBundle) { + bundle.getVisual().updateNodes(layers); + } else if(bundle instanceof IDiagramMVCBundle) { + updateAllVisuals(); + } + } + + /** Updates the nodes selectively adhering to the relation given by the {@link IMVCBundle}s. */ + /* package */ void updateModelVisual(Object model) { + if(contentBundles.containsKey(model)) { + updateContentVisual(contentBundles.get(model)); + } else if(contentAnchorageBundles.containsKey(model)) { + updateAnchorage(contentAnchorageBundles.get(model)); + } else if(diagramAnchorageBundles.containsKey(model)) { + updateAnchorage(diagramAnchorageBundles.get(model)); + } else if(linkBundles.containsKey(model)) { + linkBundles.get(model).getVisual().updateNodes(layers); + } else if(diagramBundle.getModel() == model) { + updateAllVisuals(); + } + } + + /** Updates the given {@link IContentMVCBundle}. */ + private void updateContentVisual(IContentMVCBundle bundle) { + bundle.getVisual().updateNodes(layers); + for(IContentAnchorageMVCBundle cab : bundle.getAnchorages()) { + updateAnchorage(cab); + } + for(IContentMVCBundle cb : bundle.getChildren()) { + updateContentVisual(cb); + } + } + + /** Updates the given {@link IContentAnchorageMVCBundle}. */ + private void updateAnchorage(IAnchorageMVCBundle bundle) { + bundle.getVisual().updateNodes(layers); + for(ILinkMVCBundle link : bundle.getIncomingLinks()) { + link.getVisual().updateNodes(layers); + } + for(ILinkMVCBundle link : bundle.getOutgoingLinks()) { + link.getVisual().updateNodes(layers); + } + } + + /** Returns the root model element of this diagram. */ + /* package */ Object getRootModel() { + return modelFactory.getRootModel(); + } + + /** Returns the current selection. */ + public DiagramViewerSelection getSelection() { + return new DiagramViewerSelection(primarySelectedBundle, secondarySelectedBundles); + } + + /** Returns whether the given bundle is selected as primary. */ + public boolean isPrimarySelected(IMVCBundle bundle) { + return primarySelectedBundle == bundle; + } + + /** Returns whether the given bundle is selected as secondary. */ + public boolean isSecondarySelected(IMVCBundle bundle) { + return secondarySelectedBundles.contains(bundle); + } + + /** Performs shift selection of the given bundle. */ + public void handleShiftSelectionOf(IMVCBundle sel) { + if(primarySelectedBundle == sel || secondarySelectedBundles.contains(sel)) { + removeSelectedMVCBundle(sel); + } else { + addSelectedMVCBundle(sel); + } + } + + /** Selects all content, diagram anchorage and link {@link IMVCBundle}s. */ + public void selectAll() { + secondarySelectedBundles.addAll(contentBundles.values()); + secondarySelectedBundles.addAll(diagramAnchorageBundles.values()); + secondarySelectedBundles.addAll(linkBundles.values()); + if(secondarySelectedBundles.isEmpty()) { + if(primarySelectedBundle != null) { + primarySelectedBundle.removeTag(PRIMARY_SELECTION_TAG); + } + primarySelectedBundle = null; + } else { + primarySelectedBundle = secondarySelectedBundles.remove(0); + primarySelectedBundle.removeTag(SECONDARY_SELECTION_TAG); + primarySelectedBundle.addTag(PRIMARY_SELECTION_TAG); + } + for(IMVCBundle s : secondarySelectedBundles) { + s.removeTag(PRIMARY_SELECTION_TAG); + s.addTag(SECONDARY_SELECTION_TAG); + } + updateAllVisuals(); + fireSelectionChanged(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/MouseState.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/MouseState.java new file mode 100644 index 0000000000000000000000000000000000000000..ff90af6d1cca427ef6f3fded76367b535120a5df --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/MouseState.java @@ -0,0 +1,379 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +import static javafx.scene.input.MouseEvent.MOUSE_ENTERED; +import static javafx.scene.input.MouseEvent.MOUSE_EXITED; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HOVER_TAG; + +import java.util.HashMap; +import java.util.Map; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IClickController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IKeyPressController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; + +import javafx.event.EventHandler; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; + +/** + * Class for monitoring mouse operations including clicking, dragging and linking + * gestures. The listeners are installed on every node that is added to the diagram layers. + * This class stores a mapping from these nodes to {@link IMVCBundle}s in order to route + * mouse gestures, clicks and key typing to the respective controller of the respective bundle. + */ +public final class MouseState { + /** The diagram viewer. */ + private final DiagramViewer viewer; + /** The inverse map for {@link IMVCBundle} lookup with nodes. */ + private final Map<Node, IMVCBundle> nodesToBundleMap = new HashMap<Node, IMVCBundle>(); + /** + * The last mouse down event, which is used for location correction during drag gestures. See + * in-code comment of {@link #mouseDragDetectedHandler}. + */ + private MouseEvent lastMousePressedEvent = null; + /** Stores the last mouse location. */ + private DiagramCoordinate lastMouseLocation = new DiagramCoordinate(0, 0); + /** Stores the drag start or {@code null} if there is no drag currently. */ + private DiagramCoordinate dragStartLocation = null; + + /** Constructor. */ + public MouseState(DiagramViewer viewer) { + this.viewer = viewer; + } + + /** The mouse clicked handler. */ + private EventHandler<MouseEvent> mouseClickedHandler = new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + if(!nodesToBundleMap.containsKey(event.getSource())) { + return; + } + // wild cast works, since source is in nodesMap + Node source = (Node)event.getSource(); + IMVCBundle bundle = nodesToBundleMap.get(source); + lastMouseLocation = + viewer.convertEventToDiagramCoordinates(event.getX(), event.getY(), source); + IClickController cc = + bundle.getController().getClickController(source, lastMouseLocation); + if(cc != null) { + Change chg = null; + if(event.getButton() == MouseButton.SECONDARY) { + if(event.getClickCount() == 1) { + chg = cc.secondaryClick(event, source, lastMouseLocation); + } + } else if(event.getButton() == MouseButton.PRIMARY) { + if(event.getClickCount() == 1) { + chg = cc.singleClick(event, source, lastMouseLocation); + } else if(event.getClickCount() == 2) { + chg = cc.doubleClick(event, source, lastMouseLocation); + } + } + event.consume(); + if(chg != null) { + viewer.getModelModifier().accept(chg); + } + } + } + }; + + /** The current drag controller is non-null while a drag gesture is in progress. */ + private IDragController dragController = null; + + /** The drag detected handler deciding about link drag and normal drag gestures. */ + private EventHandler<MouseEvent> mouseDragDetectedHandler = new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + if(!nodesToBundleMap.containsKey(event.getSource())) { + return; + } + event.consume(); + // the drag detected event occurs a view pixels away from the mouse press location + // (which is required to detect the drag gesture at all). However, we have to take + // the mouse pressed location, since that location's cursor might imply a different + // operation when the mouse is dragged the one or the other location (e.g. move cursor + // is only a view pixels away from resize cursor). If we do not correct the location to + // the initial mouse pressed location the drag controller initiates the wrong operation + // (e.g. a resize operation although the user wanted a move operation). + double x, y; + Node source; + if(lastMousePressedEvent != null) { + x = lastMousePressedEvent.getX(); + y = lastMousePressedEvent.getY(); + source = (Node)lastMousePressedEvent.getSource(); + } else { + x = event.getX(); + y = event.getY(); + source = (Node)event.getSource(); + } + // wild cast works, since source is in nodesToBundleMap + IMVCBundle bundle = nodesToBundleMap.get(source); + IVisual visual = bundle.getVisual(); + lastMouseLocation = viewer.convertEventToDiagramCoordinates(x, y, source); + EDragGesture edg = visual.getDragGesture(source, lastMouseLocation); + if(edg == EDragGesture.NONE) { + return; + } + dragController = viewer.createDragController(bundle, edg, source, lastMouseLocation); + if(dragController != null) { + dragController.dragStarted(bundle, source, lastMouseLocation); + source.startFullDrag(); + } + } + }; + /** The drag completed handler for move, resize, and link gestures. */ + private EventHandler<MouseEvent> mouseDragCompletedHandler = new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + if(!nodesToBundleMap.containsKey(event.getSource())) { + return; + } + if(dragController == null) { + return; + } + event.consume(); + // wild cast works, since source is in nodesToBundleMap + Node source = (Node)event.getSource(); + IMVCBundle bundle = nodesToBundleMap.get(source); + lastMouseLocation = + viewer.convertEventToDiagramCoordinates(event.getX(), event.getY(), source); + Change chg = dragController.dragCompleted(bundle, source, lastMouseLocation); + dragController = null; + if(chg != null) { + viewer.getModelModifier().accept(chg); + } + } + }; + + /** The mouse dragged handler. */ + private EventHandler<MouseEvent> mouseDraggedHandler = new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + if(!nodesToBundleMap.containsKey(event.getSource())) { + return; + } + if(dragController == null) { + // there are drag events even before the drag detected event fired + // ignore those phantoms + return; + } + event.consume(); + // wild cast works, since source is in nodesToBundleMap + Node source = (Node)event.getSource(); + IMVCBundle bundle = nodesToBundleMap.get(source); + lastMouseLocation = + viewer.convertEventToDiagramCoordinates(event.getX(), event.getY(), source); + dragController.dragInProgress(bundle, source, lastMouseLocation); + } + }; + + /** The mouse release handler. */ + private EventHandler<MouseEvent> mouseReleasedHandler = new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + if(lastMousePressedEvent != null && + event.getButton() == lastMousePressedEvent.getButton()) { + lastMousePressedEvent = null; + } + if(!nodesToBundleMap.containsKey(event.getSource())) { + return; + } + if(dragController == null) { + return; + } + event.consume(); + // wild cast works, since source is in nodesToBundleMap + Node source = (Node)event.getSource(); + IMVCBundle bundle = nodesToBundleMap.get(source); + lastMouseLocation = + viewer.convertEventToDiagramCoordinates(event.getX(), event.getY(), source); + Change chg = dragController.dragCompleted(bundle, source, lastMouseLocation); + dragController = null; + if(chg != null) { + viewer.getModelModifier().accept(chg); + } + } + }; + + /** The mouse enter/exit handler. */ + private EventHandler<MouseEvent> mouseEnterExitHandler = new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + if(!nodesToBundleMap.containsKey(event.getSource())) { + return; + } + event.consume(); + // wild cast works, since source is in nodesMap + Node source = (Node)event.getSource(); + IMVCBundle bundle = nodesToBundleMap.get(source); + if(event.getEventType() == MOUSE_ENTERED) { + bundle.addTag(HOVER_TAG); + updateVisualsAfterHoverChange(bundle); + } else if(event.getEventType() == MOUSE_EXITED) { + bundle.removeTag(HOVER_TAG); + updateVisualsAfterHoverChange(bundle); + } + // prevent cursor from changing during drag operations + if(dragController != null) { + return; + } + lastMouseLocation = + viewer.convertEventToDiagramCoordinates(event.getX(), event.getY(), source); + IController ctrl = bundle.getController(); + if(ctrl != null) { + Cursor cursor = ctrl.getCurrentCursor(source, lastMouseLocation); + source.setCursor(cursor); + } + } + + /** Updates visuals after the hover tag has changed. */ + private void updateVisualsAfterHoverChange(IMVCBundle bundle) { + viewer.updateVisual(bundle); + // the following two sections allow hidden anchorages, which are + // only shown once the mouse hovers over their parent visual. + if(bundle instanceof IContentMVCBundle) { + IContentMVCBundle contentBundle = (IContentMVCBundle)bundle; + IContentVisual contentVisual = contentBundle.getVisual(); + if(contentVisual.updateAttachedAnchorageVisualsOnHover()) { + // update anchorage visuals + contentBundle.getAnchorages().stream() + .forEach(anchorageBundle -> viewer.updateVisual(anchorageBundle)); + } + } else if(bundle instanceof IContentAnchorageMVCBundle) { + IContentAnchorageMVCBundle anchorageBundle = (IContentAnchorageMVCBundle)bundle; + IContentAnchorageVisual anchorageVisual = anchorageBundle.getVisual(); + if(anchorageVisual.updateSiblingAnchorageVisualsOnHover()) { + IContentMVCBundle contentBundle = anchorageBundle.getAttachedTo(); + // update sibling visuals + contentBundle.getAnchorages().stream().filter(ab -> ab != anchorageBundle) + .forEach(ab -> viewer.updateVisual(ab)); + } + } + } + }; + + /** The mouse motion handler used for updating the cursor. */ + private EventHandler<MouseEvent> mouseMotionHandler = new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + if(!nodesToBundleMap.containsKey(event.getSource()) || dragController != null) { + return; + } + event.consume(); + // wild cast works, since source is in nodesMap + Node source = (Node)event.getSource(); + IMVCBundle bundle = nodesToBundleMap.get(source); + IController ctrl = bundle.getController(); + lastMouseLocation = + viewer.convertEventToDiagramCoordinates(event.getX(), event.getY(), source); + if(ctrl != null) { + Cursor cursor = ctrl.getCurrentCursor(source, lastMouseLocation); + source.setCursor(cursor); + } + } + }; + + /** The mouse pressed handler for selecting the node under the cursor. */ + private EventHandler<MouseEvent> mousePressedHandler = new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + // see mouseDragDetectedHandler for explanation of storing the last mouse press event + lastMousePressedEvent = event; + viewer.hideContextMenu(); + event.consume(); + } + }; + + /** The key event handler. */ + private final EventHandler<KeyEvent> keyEventHandler = new EventHandler<KeyEvent>() { + @Override + public void handle(KeyEvent event) { + if(!nodesToBundleMap.containsKey(event.getSource())) { + return; + } + // wild cast works, since source is in nodesMap + Node source = (Node)event.getSource(); + IMVCBundle bundle = nodesToBundleMap.get(source); + IKeyPressController kpc = + viewer.createKeyPressController(bundle, source, lastMouseLocation); + if(kpc != null) { + Change change = kpc.keyEvent(event, bundle, source, lastMouseLocation); + if(change != null) { + viewer.getModelModifier().accept(change); + } + } + } + }; + + /** Registers the mouse listeners on the given node. */ + /* package */ void registerMouseListeners(Node node, IMVCBundle mvcb) { + node.setOnMousePressed(mousePressedHandler); + node.setOnMouseEntered(mouseEnterExitHandler); + node.setOnMouseExited(mouseEnterExitHandler); + node.setOnMouseClicked(mouseClickedHandler); + node.setOnDragDetected(mouseDragDetectedHandler); + node.setOnMouseDragged(mouseDraggedHandler); + node.setOnMouseDragOver(mouseDraggedHandler); + node.setOnMouseReleased(mouseReleasedHandler); + node.setOnMouseDragReleased(mouseDragCompletedHandler); + node.setOnMouseMoved(mouseMotionHandler); + node.setOnKeyReleased(keyEventHandler); + nodesToBundleMap.put(node, mvcb); + } + + /** Unregisters the mouse listeners from the given node. */ + /* package */ void unregisterMouseListeners(Node node) { + node.setOnKeyReleased(null); + node.setOnMouseMoved(null); + node.setOnMouseDragReleased(null); + node.setOnMouseReleased(null); + node.setOnMouseDragOver(null); + node.setOnMouseDragged(null); + node.setOnDragDetected(null); + node.setOnMouseClicked(null); + node.setOnMouseExited(null); + node.setOnMouseEntered(null); + node.setOnMousePressed(null); + nodesToBundleMap.remove(node); + } + + /** Returns the last known mouse location. */ + public DiagramCoordinate getLastMouseLocation() { + return lastMouseLocation; + } + + /** Returns the drag start location or {@code null} if no drag is in progress. */ + public DiagramCoordinate getDragStartLocation() { + return dragStartLocation; + } + + /** + * Returns the current drag extent, i.e., last mouse position minus drag start location, or + * {@code null} if no drag is in progress. + */ + public DiagramCoordinate getDragExtent() { + if(dragStartLocation == null || lastMouseLocation == null) { + return null; + } + return lastMouseLocation.subtract(dragStartLocation); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/SVGExporter.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/SVGExporter.java new file mode 100644 index 0000000000000000000000000000000000000000..2211f06d81c7b0523ae52dc832410a76875a9e07 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/SVGExporter.java @@ -0,0 +1,368 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef; + +import java.util.List; + +import javafx.geometry.Bounds; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Circle; +import javafx.scene.shape.ClosePath; +import javafx.scene.shape.CubicCurve; +import javafx.scene.shape.CubicCurveTo; +import javafx.scene.shape.Ellipse; +import javafx.scene.shape.Line; +import javafx.scene.shape.LineTo; +import javafx.scene.shape.MoveTo; +import javafx.scene.shape.Path; +import javafx.scene.shape.PathElement; +import javafx.scene.shape.QuadCurve; +import javafx.scene.shape.QuadCurveTo; +import javafx.scene.shape.Rectangle; +import javafx.scene.shape.Shape; +import javafx.scene.text.Text; + +/** Export the {@link DiagramViewer} to a SVG file. */ +final class SVGExporter { + /** The diagram viewer node to be exported. */ + private final Pane diagramNode; + /** The width of the diagram computed from the nodes. */ + private double width = 0; + /** The height of the diagram computed from the nodes. */ + private double height = 0; + + /** Constructor. */ + public SVGExporter(DiagramViewer viewer) { + // first element of the diagram root scrolled pane is the content pane, see DiagramViewer + this.diagramNode = (Pane)viewer.getVisualNode().getChildren().get(0); + } + + /** Exports the diagram to the target file. */ + public String export() { + width = height = 0; + // first convert the nodes in order to compute width and height + StringBuilder content = new StringBuilder(); + for(Node n : diagramNode.getChildrenUnmodifiable()) { + transform(n, content); + } + + // then build an SVG XML file + StringBuilder sb = new StringBuilder(); + sb.append("<?xml version=\"1.0\" standalone=\"no\"?>\n"); + + sb.append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "); + sb.append("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); + + sb.append("<svg "); + setProp("width", width, sb); + setProp("height", height, sb); + setProp("version", "1.1", sb); + setProp("xmlns", "http://www.w3.org/2000/svg", sb); + setProp("xmlns:xlink", "http://www.w3.org/1999/xlink", sb); + sb.append(">\n"); + + sb.append(content); + sb.append("</svg>\n"); + return sb.toString(); + } + + /** Transforms from {@link Node} to a SVG element. */ + private void transform(Node node, StringBuilder sb) { + Bounds b = node.getBoundsInParent(); + if(b.getMaxX() > width) { + width = b.getMaxX(); + } + if(b.getMaxY() > height) { + height = b.getMaxY(); + } + if(node instanceof Region) { + transform((Region)node, sb); + } else if(node instanceof Group) { + transform((Group)node, sb); + } else if(node instanceof Rectangle) { + transform((Rectangle)node, sb); + } else if(node instanceof Ellipse) { + transform((Ellipse)node, sb); + } else if(node instanceof Circle) { + transform((Circle)node, sb); + } else if(node instanceof QuadCurve) { + transform((QuadCurve)node, sb); + } else if(node instanceof CubicCurve) { + transform((CubicCurve)node, sb); + } else if(node instanceof Line) { + transform((Line)node, sb); + } else if(node instanceof Text) { + transform((Text)node, sb); + } else if(node instanceof Path) { + transform((Path)node, sb); + } + // silently ignore the rest + } + + /** Writes a SVG path element. */ + private static void writePath(StringBuilder sb, String op, double... coords) { + sb.append(op).append(' '); + for(double d : coords) { + sb.append(d).append(' '); + } + } + + /** Transforms from {@link QuadCurve} to SVG Path element. */ + private void transform(CubicCurve curve, StringBuilder sb) { + StyleHelper st = new StyleHelper(curve); + if(st.isTransparent()) { + return; + } + StringBuilder db = new StringBuilder(); + db.append("M ").append(curve.getStartX()).append(' ').append(curve.getStartY()).append(' '); + db.append("C ").append(curve.getControlX1()).append(' ').append(curve.getControlY1()) + .append(' '); + db.append(curve.getControlX2()).append(' ').append(curve.getControlY2()).append(' '); + db.append(curve.getEndX()).append(' ').append(curve.getEndY()); + sb.append("<path "); + setProp("d", db.toString(), sb); + st.applySVGStyle(sb); + sb.append("/>\n"); + } + + /** Transforms from {@link QuadCurve} to SVG Path element. */ + private void transform(QuadCurve curve, StringBuilder sb) { + StyleHelper st = new StyleHelper(curve); + if(st.isTransparent()) { + return; + } + StringBuilder db = new StringBuilder(); + db.append("M ").append(curve.getStartX()).append(' ').append(curve.getStartY()).append(' '); + db.append("Q ").append(curve.getControlX()).append(' ').append(curve.getControlY()) + .append(' '); + db.append(curve.getEndX()).append(' ').append(curve.getEndY()); + sb.append("<path "); + setProp("d", db.toString(), sb); + st.applySVGStyle(sb); + sb.append("/>\n"); + } + + /** Converts {@link PathElement}s to SVG D specification. */ + private static String convertPathToD(Path path) { + StringBuilder db = new StringBuilder(); + for(PathElement pe : path.getElements()) { + if(pe instanceof MoveTo) { + MoveTo mt = (MoveTo)pe; + writePath(db, "M", mt.getX(), mt.getY()); + } else if(pe instanceof LineTo) { + LineTo lt = (LineTo)pe; + writePath(db, "L", lt.getX(), lt.getY()); + } else if(pe instanceof QuadCurveTo) { + QuadCurveTo qc = (QuadCurveTo)pe; + writePath(db, "Q", qc.getControlX(), qc.getControlY(), qc.getX(), qc.getY()); + } else if(pe instanceof CubicCurveTo) { + CubicCurveTo cc = (CubicCurveTo)pe; + writePath(db, "C", cc.getControlX1(), cc.getControlY1(), cc.getControlX2(), + cc.getControlY2(), cc.getX(), cc.getY()); + } else if(pe instanceof ClosePath) { + writePath(db, "Z"); + } + } + return db.toString(); + } + + /** Transforms from {@link Path} to SVG Path element. */ + private void transform(Path path, StringBuilder sb) { + StyleHelper st = new StyleHelper(path); + if(st.isTransparent()) { + return; + } + sb.append("<path "); + setProp("d", convertPathToD(path), sb); + st.applySVGStyle(sb); + sb.append("/>\n"); + } + + /** Transforms from {@link Text} to SVG Path element. */ + private void transform(Text text, StringBuilder sb) { + StyleHelper st = new StyleHelper(text); + if(st.isTransparent()) { + return; + } + sb.append("<text "); + setProp("x", text.getX(), sb); + setProp("y", text.getY(), sb); + setProp("font-family", "Arial, Helvetica, sans-serif", sb); + st.applySVGStyle(sb); + sb.append(">\n"); + sb.append(text.getText()); + sb.append("</text>\n"); + } + + /** Transforms from {@link Line} to SVG element. */ + private void transform(Line line, StringBuilder sb) { + StyleHelper st = new StyleHelper(line); + if(st.isTransparent()) { + return; + } + sb.append("<line "); + setProp("x1", line.getStartX(), sb); + setProp("y1", line.getStartY(), sb); + setProp("x2", line.getEndX(), sb); + setProp("y2", line.getEndY(), sb); + st.applySVGStyle(sb); + sb.append("/>\n"); + } + + /** Transforms from {@link Circle} to SVG element. */ + private void transform(Circle circ, StringBuilder sb) { + StyleHelper st = new StyleHelper(circ); + if(st.isTransparent()) { + return; + } + sb.append("<circle "); + setProp("cx", circ.getCenterX(), sb); + setProp("cy", circ.getCenterY(), sb); + setProp("r", circ.getRadius(), sb); + st.applySVGStyle(sb); + sb.append("/>\n"); + } + + /** Transforms from {@link Ellipse} to SVG element. */ + private void transform(Ellipse ell, StringBuilder sb) { + StyleHelper st = new StyleHelper(ell); + if(st.isTransparent()) { + return; + } + sb.append("<ellipse "); + setProp("cx", ell.getCenterX(), sb); + setProp("cy", ell.getCenterY(), sb); + setProp("rx", ell.getRadiusX(), sb); + setProp("ry", ell.getRadiusY(), sb); + st.applySVGStyle(sb); + sb.append("/>\n"); + } + + /** Transforms from {@link Rectangle} to SVG Rect element. */ + private void transform(Rectangle rect, StringBuilder sb) { + StyleHelper st = new StyleHelper(rect); + if(st.isTransparent()) { + return; + } + sb.append("<rect "); + setProp("x", rect.getX(), sb); + setProp("y", rect.getY(), sb); + setProp("rx", rect.getArcWidth(), sb); + setProp("ry", rect.getArcHeight(), sb); + setProp("width", rect.getWidth(), sb); + setProp("height", rect.getHeight(), sb); + st.applySVGStyle(sb); + sb.append("/>\n"); + } + + /** Transforms from {@link Group} to SVG G element. */ + private void transform(Group g, StringBuilder sb) { + transformToG(null, g.getChildrenUnmodifiable(), sb); + } + + /** Write a SVG G element. */ + private void transformToG(String transform, List<Node> children, StringBuilder sb) { + sb.append("<g "); + if(transform != null) { + sb.append("transform=\"").append(transform).append("\" "); + } + sb.append(">\n"); + for(Node n : children) { + if(n.isVisible()) { + transform(n, sb); + } + } + sb.append("</g>\n"); + } + + /** Transforms from {@link Region} to SVG G element. */ + private void transform(Region r, StringBuilder sb) { + Bounds b = r.getBoundsInParent(); + String transform = "translate(" + b.getMinX() + "," + b.getMinY() + ")"; + transformToG(transform, r.getChildrenUnmodifiable(), sb); + } + + /** Writes the given property and value to the string builder. */ + private static void setProp(String prop, String value, StringBuilder sb) { + sb.append(prop).append("=\"").append(value).append("\" "); + } + + /** Writes the given property and value to the string builder. */ + private static void setProp(String prop, double value, StringBuilder sb) { + sb.append(prop).append("=\"").append(value).append("\" "); + } + + /** Helper class for handling fill and stroke styles. */ + private static class StyleHelper { + /** RGB hex of the fill color. */ + private String fill; + /** Opacity of the fill color. */ + private double fillAlpha; + /** RGB hex of the stroke color. */ + private String stroke; + /** Opacity of the stroke color. */ + private double strokeAlpha; + /** Width of the stroke. */ + private double strokeWidth; + + /** Constructor. */ + public StyleHelper(Paint back, Paint fore, double sw) { + fill = rgbHex(back); + fillAlpha = alpha(back); + stroke = rgbHex(fore); + strokeAlpha = alpha(fore); + strokeWidth = sw; + } + + /** Constructor. */ + public StyleHelper(Shape shape) { + this(shape.getFill(), shape.getStroke(), shape.getStrokeWidth()); + } + + /** Returns the SVG string for this style. */ + public void applySVGStyle(StringBuilder sb) { + setProp("fill", fill != null ? fill : "none", sb); + setProp("fill-opacity", fillAlpha, sb); + setProp("stroke", stroke != null ? stroke : "none", sb); + setProp("stroke-width", strokeWidth, sb); + setProp("stroke-opacity", strokeAlpha, sb); + } + + /** Returns whether the style has transparent colors. */ + public boolean isTransparent() { + return (fill == null && stroke == null) || (fillAlpha == 0 && strokeAlpha == 0); + } + + /** Converts from {@link Paint} to SVG hex-decimal color specification. */ + private static String rgbHex(Paint p) { + if(p instanceof Color) { + Color c = (Color)p; + int r = (int)(c.getRed() * 255); + int g = (int)(c.getGreen() * 255); + int b = (int)(c.getBlue() * 255); + return String.format("#%02X%02X%02X", r, g, b); + } + return null; + } + + /** Converts from Paint to alpha value. */ + private static double alpha(Paint p) { + if(p instanceof Color) { + return ((Color)p).getOpacity(); + } + return 0.0; + } + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..1758ea0a359923dd644757977226edcf4ad6064a --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/.ratings @@ -0,0 +1,3 @@ +Change.java e907a516f1369013cbb3d5d002b1ddb69cd5cc25 YELLOW +ChangeSet.java 363c4fcdad1709e6f7ccad4205a53b22c976b50d YELLOW +DefaultModelModifier.java bfa32a76ce226ec84a173201553b87d6317b84b1 YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/Change.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/Change.java new file mode 100644 index 0000000000000000000000000000000000000000..e907a516f1369013cbb3d5d002b1ddb69cd5cc25 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/Change.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.change; + +/** This interface encapsulates a changes to be applied to the underlying data model. */ +@FunctionalInterface +public interface Change { + /** Applies this change to the underlying model. */ + public void applyChange(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/ChangeSet.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/ChangeSet.java new file mode 100644 index 0000000000000000000000000000000000000000..363c4fcdad1709e6f7ccad4205a53b22c976b50d --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/ChangeSet.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.change; + +import java.util.ArrayList; +import java.util.List; + +/** This class encapsulates a set of {@link Change}s, which need to be performed atomically. */ +public final class ChangeSet implements Change { + /** The contained changes of this change set. */ + private final List<Change> changeSet = new ArrayList<>(); + + /** Adds the given {@link Change} to this change set. */ + public void add(Change chg) { + changeSet.add(chg); + } + + /** {@inheritDoc} */ + @Override + public void applyChange() { + for(Change c : changeSet) { + c.applyChange(); + } + } + + /** Returns whether this change set contains any changes. **/ + public boolean isEmpty() { + return changeSet.isEmpty(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/DefaultModelModifier.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/DefaultModelModifier.java new file mode 100644 index 0000000000000000000000000000000000000000..bfa32a76ce226ec84a173201553b87d6317b84b1 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/change/DefaultModelModifier.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.change; + +import java.util.function.Consumer; + +/** The default model modifier simply applies all changes without any transaction mechanism. */ +public final class DefaultModelModifier implements Consumer<Change> { + /** {@inheritDoc} */ + @Override + public void accept(Change change) { + change.applyChange(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..b36ac7444c8875c8a6f059a120545746e2acdf95 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/.ratings @@ -0,0 +1,5 @@ +IClickController.java c0270e99d918aef14612d46f3e84905d3a6bdd8c YELLOW +IController.java 6ba069977e7588f97187916c23a0e37f7cb91059 YELLOW +IControllerFactory.java 85b86eda643489f2a03454eae5383915ecf27f83 YELLOW +IDragController.java c1f311d2ae9ed684c8a7cd85e9ed1f85e79658d1 YELLOW +IKeyPressController.java dc8fe2a7c441866122e8c7b3114fd12d17f0b051 YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IClickController.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IClickController.java new file mode 100644 index 0000000000000000000000000000000000000000..c0270e99d918aef14612d46f3e84905d3a6bdd8c --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IClickController.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; + +import javafx.scene.Node; +import javafx.scene.input.MouseEvent; + +/** + * Interface for controllers, which handle click gestures provided by the visuals. Each operation + * may result in {@link Change} to the underlying model. + */ +public interface IClickController { + /** Handles a single-click at the given mouse location. */ + Change singleClick(MouseEvent event, Node node, DiagramCoordinate lastMouseLocation); + + /** Handles a double-click at the given mouse location. */ + Change doubleClick(MouseEvent event, Node node, DiagramCoordinate lastMouseLocation); + + /** + * Handles a single click at the given mouse location when the secondary mouse button is used. + */ + Change secondaryClick(MouseEvent event, Node node, DiagramCoordinate lastMouseLocation); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IController.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IController.java new file mode 100644 index 0000000000000000000000000000000000000000..6ba069977e7588f97187916c23a0e37f7cb91059 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IController.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller; + +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelChangeProvider; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundlePart; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.MVCBundleTag; + +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.control.MenuItem; + +/** + * Interface for controllers which are informed about gestures or clicks and provide context menus + * and cursor shapes. + */ +public interface IController extends IMVCBundlePart { + /** + * Returns whether the {@link IMVCBundle} should be selected if the mouse is pressed on one of + * its nodes. + */ + boolean selectOnMousePress(); + + /** + * Returns whether the {@link IMVCBundle} should be added to the selection if the mouse is + * pressed on one of its nodes while the shift key is down. + */ + boolean selectOnShiftMousePress(); + + /** Returns an {@link IDragController} for the given node and last mouse location. */ + IDragController getDragController(EDragGesture gesture, Node node, + DiagramCoordinate lastMouseLocation); + + /** Returns an {@link IClickController} for the given node and last mouse location. */ + IClickController getClickController(Node node, DiagramCoordinate lastMouseLocation); + + /** Returns an {@link IKeyPressController} for the given node and last mouse location. */ + IKeyPressController getKeyPressController(Node node, DiagramCoordinate lastMouseLocation); + + /** + * Returns the menu items when the underlying visual is requesting a context menu. May return + * {@code null} to avoid showing any context menu. The given location is relative to the + * diagram origin. + */ + List<MenuItem> contextMenuContributions(Node node, DiagramCoordinate diagramLocation); + + /** + * Returns whether this bundle represents a possible link target. If this method returns + * {@code false} this bundle is considered neither when a new link gesture is started nor + * when a move-link gesture is started. If {@code true} is returned + * {@link #getLinkTargetEffectForNewLinkFrom(IMVCBundle)} and + * {@link #getLinkTargetEffectForMoveLink(ILinkMVCBundle, IAnchorageMVCBundle)} are called to + * determine the user feedback for the concrete situation. The default is {@code false}. + */ + boolean isPossibleLinkTarget(); + + /** Returns the link target effect tag for a new link gesture starting at the given bundle. */ + MVCBundleTag getLinkTargetEffectForNewLinkFrom(IMVCBundle linkStartBundle); + + /** + * Returns the link target effect tag for a move link gesture of the given link and the given + * previous bundle, from which the link is currently removed. + */ + MVCBundleTag getLinkTargetEffectForMoveLink(ILinkMVCBundle linkBundle, + IAnchorageMVCBundle movedAnchorageBundle); + + /** + * Returns the mouse cursor to be used when the pointer is over the given node at the given + * diagram location. + */ + Cursor getCurrentCursor(Node node, DiagramCoordinate diagramLocation); + + /** Called when the delete operation of the underlying model should be performed. */ + void delete(); + + /** + * Allows the controller to provide a helper object instead of implementing + * {@link IModelChangeProvider} directly. + */ + IModelChangeProvider getModelChangeProvider(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IControllerFactory.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IControllerFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..85b86eda643489f2a03454eae5383915ecf27f83 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IControllerFactory.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; + +/** + * Interface for factories which create controllers for model elements in a {@link DiagramViewer}. + */ +public interface IControllerFactory { + /** Returns the controller instance for the given content model. */ + IController createContentController(IContentMVCBundle model); + + /** Returns the controller instance for the given diagram anchorage model. */ + IController createDiagramAnchorageController(IDiagramAnchorageMVCBundle modelBundle); + + /** + * Returns the controller instance for the given content anchorage model. Sub-classes may assume + * that {@code model.getAttachedTo()} points to the parent {@link IContentMVCBundle}. + */ + IController createContentAnchorageController(IContentAnchorageMVCBundle modelBundle); + + /** + * Returns the controller instance for the given link model. Sub-classes may assume that + * {@code modelBundle.getAttachedTo()} points to the parent {@link IDiagramMVCBundle}. + */ + IController createLinkController(ILinkMVCBundle modelBundle); + + /** + * Returns the controller instance for the given diagram bundle. This method is called only once + * when the diagram is initialized. + */ + IController createDiagramController(IDiagramMVCBundle diagramBundle); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IDragController.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IDragController.java new file mode 100644 index 0000000000000000000000000000000000000000..c1f311d2ae9ed684c8a7cd85e9ed1f85e79658d1 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IDragController.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; + +import javafx.scene.Node; + +/** Interface for controllers, which handle standard drag gestures provided by the visuals. */ +public interface IDragController { + /** + * Informs the drag controller that the drag gesture has started on the given bundle, node and + * diagram location. + */ + void dragStarted(IMVCBundle startBundle, Node startNode, DiagramCoordinate lastMouseLocation); + + /** Updates the drag controller with the new drag bundle, node, and diagram location. */ + void dragInProgress(IMVCBundle currentBundle, Node currentNode, + DiagramCoordinate lastMouseLocation); + + /** + * Informs the drag controller about the final result: the target bundle, target node and + * diagram location. This operation may result in a {@link Change} to the underlying model. + */ + Change dragCompleted(IMVCBundle endBundle, Node endNode, DiagramCoordinate lastMouseLocation); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IKeyPressController.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IKeyPressController.java new file mode 100644 index 0000000000000000000000000000000000000000..dc8fe2a7c441866122e8c7b3114fd12d17f0b051 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/IKeyPressController.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; + +import javafx.scene.Node; +import javafx.scene.input.KeyEvent; + +/** Interface for controllers that handle key interactions on the selected node. */ +public interface IKeyPressController { + /** + * The given key event occurred on the bundle, source node and at the given location. Its effect + * may result in a {@link Change} to the underlying model. + */ + Change keyEvent(KeyEvent event, IMVCBundle bundle, Node source, + DiagramCoordinate mouseLocation); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..2fb55495d45c3c21a244d8e23cb04ab51a9f5314 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/.ratings @@ -0,0 +1,11 @@ +AnchorageContentControllerBase.java da56b10cbf2711b5da69f0b59f43eacbe54f4eea YELLOW +ClickControllerBase.java 8e5861ed5f9318008ad0fdd5497ed320cd5bd647 YELLOW +ContentAnchorageMoveControllerBase.java c18e7915ce23e124757f5b736086ecc46694800a YELLOW +ControllerBase.java 309e9ee3f3a255b5a06fed8f1b4d4ec8bf88f101 YELLOW +DefaultDiagramController.java 0e083b89a08f63967102a384d66ebc1d64d203af YELLOW +DelegatingContentAnchorageController.java 2e3b1b4e14402a3503233f816b21ef3e4aa09edc YELLOW +DragControllerBase.java b15ff874304f679fe494d85f57cc8cbe4d0d1d15 YELLOW +DraggingUtils.java 95117e2ea4c36b6c6a31f8088bb95b484e0e6612 YELLOW +LinkControllerBase.java 392cb79cb42e9f878c665d47053b0795c3768603 YELLOW +MoveControllerBase.java 38d632e31f5e27d112ecdd4933e3a331378180d0 YELLOW +ResizableContentControllerBase.java 898500d389b035f8138308d496d2d24be501c719 YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/AnchorageContentControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/AnchorageContentControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..da56b10cbf2711b5da69f0b59f43eacbe54f4eea --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/AnchorageContentControllerBase.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base; + +import java.util.HashMap; +import java.util.Map; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; + +/** + * Base class providing dragging implementation for visuals which move and resize in the + * {@link DiagramViewer} and which have movable anchorages attached. Sub-classes must implement + * {@link #resize(FeedbackChange)} and {@link #move(FeedbackChange)} in order to change the + * underlying model with the respective coordinate changes. Furthermore, sub-classes must implement + * {@link #createAnchorageMoveController(IContentAnchorageMVCBundle)} and create + * {@link IDragController}s for the given anchorages. + */ +public abstract class AnchorageContentControllerBase extends ResizableContentControllerBase { + /** Constructor. */ + public AnchorageContentControllerBase(IContentMVCBundle mvcb) { + super(mvcb); + } + + /** Stores the anchorage move controllers. */ + private final Map<IContentAnchorageMVCBundle, IDragController> moveAnchorageControllerMap = + new HashMap<IContentAnchorageMVCBundle, IDragController>(); + + /** + * Returns an {@link IDragController} responsible for positioning the anchorage visual during + * the move feedback and for committing the new position to the model. + */ + protected final IDragController + getAnchorageMoveController(IContentAnchorageMVCBundle anchorage) { + if(!canMoveAnchorage(anchorage)) { + return null; + } + IDragController ctrl = moveAnchorageControllerMap.get(anchorage); + if(ctrl == null) { + ctrl = createAnchorageMoveController(anchorage); + moveAnchorageControllerMap.put(anchorage, ctrl); + } + return ctrl; + } + + /** + * Returns whether the given content anchorage should be movable. + * + * @param anchorage + * the {@link IMVCBundle} of the anchorage to be moved + * @return whether the anchorage can be moved + */ + protected boolean canMoveAnchorage(IContentAnchorageMVCBundle anchorage) { + return true; + } + + /** + * Creates the move controller for the given anchorage. + * + * @param anchorage + * the {@link IMVCBundle} of the anchorage to be moved + * @return a drag controller for moving the anchorage + */ + protected abstract IDragController + createAnchorageMoveController(IContentAnchorageMVCBundle anchorage); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ClickControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ClickControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..8e5861ed5f9318008ad0fdd5497ed320cd5bd647 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ClickControllerBase.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IClickController; + +import javafx.scene.Node; +import javafx.scene.input.MouseEvent; + +/** Base class implementing {@link IClickController}. */ +public abstract class ClickControllerBase implements IClickController { + /** {@inheritDoc} */ + @Override + public Change singleClick(MouseEvent event, Node node, DiagramCoordinate diagramLocation) { + // ignore by default + return null; + } + + /** {@inheritDoc} */ + @Override + public Change doubleClick(MouseEvent event, Node node, DiagramCoordinate diagramLocation) { + // ignore by default + return null; + } + + /** {@inheritDoc} */ + @Override + public Change secondaryClick(MouseEvent event, Node node, DiagramCoordinate diagramLocation) { + // ignore by default + return null; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ContentAnchorageMoveControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ContentAnchorageMoveControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..c18e7915ce23e124757f5b736086ecc46694800a --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ContentAnchorageMoveControllerBase.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; + +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; + +/** Base class for move controllers of content anchorages. */ +public abstract class ContentAnchorageMoveControllerBase extends DragControllerBase { + /** The anchorage. */ + protected final IContentAnchorageMVCBundle anchorage; + + /** Constructor. */ + public ContentAnchorageMoveControllerBase(IContentAnchorageMVCBundle anchorage) { + this.anchorage = anchorage; + } + + /** Returns the location of the anchorage when dragged by the given delta. */ + protected abstract Point2D getAnchorageFeedbackLocation(DiagramCoordinate delta); + + /** Sets the angle of the given anchorage when move operation has completed. */ + protected abstract void moveAnchorage(DiagramCoordinate delta); + + /** {@inheritDoc} */ + @Override + public final void safeDragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + DiagramCoordinate delta = diagramLocation.subtract(startDiagramLocation); + Point2D c = getAnchorageFeedbackLocation(delta); + Rectangle2D anchorageBounds = anchorage.getVisual().getModelBounds(); + FeedbackChange change = new FeedbackChange(c.getX() - anchorageBounds.getMinX(), + c.getY() - anchorageBounds.getMinY(), 0, 0); + anchorage.setFeedbackChange(change); + } + + /** {@inheritDoc} */ + @Override + public final Change safeDragCompleted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + DiagramCoordinate delta = diagramLocation.subtract(startDiagramLocation); + anchorage.setFeedbackChange(FeedbackChange.EMPTY); + bundle.getViewer().updateVisual(bundle); + return () -> moveAnchorage(delta); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..309e9ee3f3a255b5a06fed8f1b4d4ec8bf88f101 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ControllerBase.java @@ -0,0 +1,282 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base; + +import static java.util.Collections.emptyList; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture.NEW_LINK; + +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IClickController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IKeyPressController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelChangeProvider; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.MVCBundleTag; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.MVCBundlePartBase; + +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.control.MenuItem; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseEvent; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; + +/** Base class for {@link IController} implementations with move and link operation. */ +public abstract class ControllerBase extends MVCBundlePartBase implements IController { + /** Constructor. */ + public ControllerBase(IMVCBundle mvcb) { + super(mvcb); + } + + /** {@inheritDoc} */ + @Override + public boolean selectOnMousePress() { + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean selectOnShiftMousePress() { + return true; + } + + /** {@inheritDoc} */ + @Override + public List<MenuItem> contextMenuContributions(Node node, DiagramCoordinate diagramLocation) { + return emptyList(); + } + + /** {@inheritDoc} */ + @Override + public IClickController getClickController(Node node, DiagramCoordinate diagramLocation) { + return new ClickControllerBase() { + /** {@inheritDoc} */ + @Override + public Change singleClick(MouseEvent event, Node node, + DiagramCoordinate diagramLocation) { + if(!selectOnClick()) { + return null; + } + if(event.isShiftDown()) { + getViewer().handleShiftSelectionOf(getMVCBundle()); + } else { + getViewer().setSingleSelectedMVCBundle(getMVCBundle()); + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Change secondaryClick(MouseEvent event, Node node, + DiagramCoordinate diagramLocation) { + DiagramViewer viewer = getViewer(); + if(selectOnClick()) { + viewer.setSingleSelectedMVCBundle(getMVCBundle()); + } + if(event.isControlDown()) { + return () -> delete(); + } + viewer.showContextMenu(node, diagramLocation, getMVCBundle()); + return null; + } + }; + } + + /** Returns whether the visual should be selected on single click. */ + protected boolean selectOnClick() { + return true; + } + + /** {@inheritDoc} */ + @Override + public IDragController getDragController(EDragGesture gesture, Node node, + DiagramCoordinate diagramLocation) { + if(allowLink() && gesture == NEW_LINK) { + return getLinkCreationController(); + } + return null; + } + + /** Returns whether this visual can be linked. */ + protected boolean allowLink() { + return true; + } + + /** Returns the default {@link IDragController} for creating links. */ + protected IDragController getLinkCreationController() { + return defaultLinkController; + } + + /** The default {@link IDragController} for link creation. */ + private final IDragController defaultLinkController = new DragControllerBase() { + @Override + public void safeDragStarted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + diagramLocation = getVisual().getLinkAnchorage(diagramLocation); + getViewer().startNewLinkLineFeedback(bundle, node, diagramLocation); + } + + @Override + protected void safeDragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + diagramLocation = bundle.getVisual().getLinkAnchorage(diagramLocation); + getViewer().updateNewLinkLineFeedback(diagramLocation, + newLinkFeedback(getMVCBundle(), startDiagramLocation, bundle, diagramLocation)); + } + + @Override + protected Change safeDragCompleted(IMVCBundle endBundle, Node node, + DiagramCoordinate endDiagramLocation) { + IMVCBundle startBundle = getMVCBundle(); + getViewer().terminateNewLinkLineFeedback(); + if(canLink(startBundle, startDiagramLocation, endBundle, endDiagramLocation)) { + return () -> link(startBundle, startDiagramLocation, endBundle, endDiagramLocation); + } + return null; + } + }; + + /** + * Returns whether a new link can be created between the given bundles and locations. + * + * @param startBundle + * the {@link IMVCBundle} of the start node of the link + * @param startLocation + * the location of the start of the link + * @param endBundle + * the {@link IMVCBundle} of the end node of the link + * @param endLocation + * the location of the end of the link + * @return if the link can be created; default is true + */ + protected boolean canLink(IMVCBundle startBundle, DiagramCoordinate startLocation, + IMVCBundle endBundle, DiagramCoordinate endLocation) { + return true; + } + + /** + * Creates a link between the bundles at the given, respective locations. + * + * @param startBundle + * the {@link IMVCBundle} of the start node of the link + * @param startLocation + * the location of the start of the link + * @param endBundle + * the {@link IMVCBundle} of the end node of the link + * @param endLocation + * the location of the end of the link + */ + protected void link(IMVCBundle startBundle, DiagramCoordinate startLocation, + IMVCBundle endBundle, DiagramCoordinate endLocation) { + // the default does nothing + } + + /** + * Determines the feedback color of for creating new links between the given bundles. + * + * @param startBundle + * the {@link IMVCBundle} of the start node of the link + * @param startLocation + * the location of the start of the link + * @param endBundle + * the {@link IMVCBundle} of the end node of the link + * @param endLocation + * the location of the end of the link + * @return the feedback line color + */ + protected Paint newLinkFeedback(IMVCBundle startBundle, DiagramCoordinate startLocation, + IMVCBundle endBundle, DiagramCoordinate endLocation) { + return Color.BLACK; + } + + /** {@inheritDoc} */ + @Override + public boolean isPossibleLinkTarget() { + return false; + } + + /** {@inheritDoc} */ + @Override + public MVCBundleTag getLinkTargetEffectForNewLinkFrom(IMVCBundle linkStartBundle) { + return null; + } + + /** {@inheritDoc} */ + @Override + public MVCBundleTag getLinkTargetEffectForMoveLink(ILinkMVCBundle linkBundle, + IAnchorageMVCBundle movedAnchorageBundle) { + return null; + } + + /** {@inheritDoc} */ + @Override + public void delete() { + // the default does nothing + } + + /** + * The default implementation for key press controller allowing move with cursor keys and delete + * with the delete key. + */ + private final IKeyPressController defaultKeyPressController = new IKeyPressController() { + @Override + public Change keyEvent(KeyEvent event, IMVCBundle bundle, Node source, + DiagramCoordinate mouseLocation) { + DiagramViewer viewer = getViewer(); + KeyCode code = event.getCode(); + if(code == KeyCode.DELETE) { + return new Change() { + @Override + public void applyChange() { + delete(); + } + }; + } else if(code == KeyCode.A && event.isControlDown()) { + viewer.selectAll(); + } + return null; + } + }; + + /** {@inheritDoc} */ + @Override + public IKeyPressController getKeyPressController(Node node, + DiagramCoordinate lastMouseLocation) { + return defaultKeyPressController; + } + + /** {@inheritDoc} */ + @Override + public Cursor getCurrentCursor(Node node, DiagramCoordinate diagramLocation) { + EDragGesture edg = getVisual().getDragGesture(node, diagramLocation); + switch(edg) { + case NEW_LINK: + return allowLink() ? Cursor.CROSSHAIR : Cursor.DEFAULT; + default: + return Cursor.DEFAULT; + } + } + + /** {@inheritDoc} */ + @Override + public IModelChangeProvider getModelChangeProvider() { + return null; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DefaultDiagramController.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DefaultDiagramController.java new file mode 100644 index 0000000000000000000000000000000000000000..0e083b89a08f63967102a384d66ebc1d64d203af --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DefaultDiagramController.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base; + +import java.util.LinkedList; +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IClickController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; + +import javafx.scene.Node; +import javafx.scene.control.Menu; +import javafx.scene.control.MenuItem; + +/** + * Default implementation for diagram controller, which provides a {@code Display...} context menu + * with the options to enable and disable link highlighting and interactive area shading. + */ +public class DefaultDiagramController extends ControllerBase { + /** Flag for ignoring mouse click event after dragging gesture. */ + private boolean ignoreNextMouseClick = false; + + /** Constructor. */ + public DefaultDiagramController(IMVCBundle mvcb) { + super(mvcb); + } + + /** {@inheritDoc} */ + @Override + public List<MenuItem> contextMenuContributions(Node node, DiagramCoordinate diagramLocation) { + Menu menu = new Menu("Display ..."); + DiagramViewerFeatures features = getViewer().getFeatures(); + // link highlighting + if(features.isLinkHighlightingEnabled()) { + MenuItem disableLinkHighlighting = new MenuItem("Disable Link Highlighting"); + disableLinkHighlighting.setOnAction(evt -> { + features.setLinkHighlightingEnabled(false); + }); + menu.getItems().add(disableLinkHighlighting); + } else { + MenuItem enableLinkHighlighting = new MenuItem("Enable Link Highlighting"); + enableLinkHighlighting.setOnAction(evt -> { + features.setLinkHighlightingEnabled(true); + }); + menu.getItems().add(enableLinkHighlighting); + } + // interaction area shading + if(features.isInteractionAreaShadingEnabled()) { + MenuItem disableInteractionShading = new MenuItem("Disable Interactive Area Shading"); + disableInteractionShading.setOnAction(evt -> { + features.setInteractionAreaShadingEnabled(false); + }); + menu.getItems().add(disableInteractionShading); + } else { + MenuItem enableInteractionShading = new MenuItem("Enable Interactive Area Shading"); + enableInteractionShading.setOnAction(evt -> { + features.setInteractionAreaShadingEnabled(true); + }); + menu.getItems().add(enableInteractionShading); + } + + List<MenuItem> menuList = new LinkedList<>(); + menuList.add(menu); + return menuList; + } + + /** {@inheritDoc} */ + @Override + public IDragController getDragController(EDragGesture gesture, Node node, + DiagramCoordinate diagramLocation) { + if(gesture == EDragGesture.SELECTION) { + return dragSelectionRectangleController; + } + return super.getDragController(gesture, node, diagramLocation); + } + + /** {@link IDragController} for the selection rectangle. */ + private final IDragController dragSelectionRectangleController = new DragControllerBase() { + @Override + protected void safeDragStarted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + getViewer().startSelectionFeedback(node, diagramLocation); + ignoreNextMouseClick = true; + } + + @Override + protected void safeDragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + getViewer().updateSelectionFeedback(node, diagramLocation); + } + + @Override + protected Change safeDragCompleted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + getViewer().terminateSelectionFeedback(node, diagramLocation); + return null; + } + }; + + /** {@inheritDoc} */ + @Override + public IClickController getClickController(Node node, DiagramCoordinate diagramLocation) { + if(ignoreNextMouseClick) { + ignoreNextMouseClick = false; + return null; + } + return super.getClickController(node, diagramLocation); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DelegatingContentAnchorageController.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DelegatingContentAnchorageController.java new file mode 100644 index 0000000000000000000000000000000000000000..2e3b1b4e14402a3503233f816b21ef3e4aa09edc --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DelegatingContentAnchorageController.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture.MOVE; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; + +import javafx.scene.Cursor; +import javafx.scene.Node; + +/** + * Default {@link IController} implementation for moving anchorage visuals attached to a parent + * visual. This controller delegates to the parent controller and tries to acquire an anchorage move + * controller from it, because in most cases the shape of the parent determines the possible + * locations of the anchorages. + */ +public class DelegatingContentAnchorageController extends ControllerBase { + /** Constructor. */ + public DelegatingContentAnchorageController(IContentAnchorageMVCBundle mvcb) { + super(mvcb); + } + + /** Returns the {@link IContentAnchorageMVCBundle}. */ + private IContentAnchorageMVCBundle getContentAnchorageBundle() { + // Wild cast works: see constructor + return (IContentAnchorageMVCBundle)getMVCBundle(); + } + + /** {@inheritDoc} */ + @Override + public IDragController getDragController(EDragGesture gesture, Node node, + DiagramCoordinate diagramLocation) { + if(gesture == MOVE && allowMove()) { + IContentAnchorageMVCBundle contentAnchorageBundle = getContentAnchorageBundle(); + IContentMVCBundle parent = contentAnchorageBundle.getAttachedTo(); + if(parent != null && parent.getController() instanceof AnchorageContentControllerBase) { + AnchorageContentControllerBase parentController = + (AnchorageContentControllerBase)parent.getController(); + return parentController.getAnchorageMoveController(contentAnchorageBundle); + } + return null; + } + return super.getDragController(gesture, node, diagramLocation); + } + + /** {@inheritDoc} */ + @Override + public Cursor getCurrentCursor(Node node, DiagramCoordinate diagramLocation) { + EDragGesture edg = getVisual().getDragGesture(node, diagramLocation); + if(edg == EDragGesture.MOVE) { + return allowMove() ? Cursor.MOVE : Cursor.DEFAULT; + } + return super.getCurrentCursor(node, diagramLocation); + } + + /** Returns whether the model element can be moved. */ + protected boolean allowMove() { + return true; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DragControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DragControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..b15ff874304f679fe494d85f57cc8cbe4d0d1d15 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DragControllerBase.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange.locationFeedbackChange; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; + +import javafx.scene.Node; + +/** Base implementation of {@link IDragController}. */ +public abstract class DragControllerBase implements IDragController { + /** Stores the start bundle of the drag gesture. */ + protected IMVCBundle startBundle = null; + /** Stores the start node of the drag gesture. */ + protected Node startNode; + /** Stores the start location of the drag gesturein diagram coordinates. */ + protected DiagramCoordinate startDiagramLocation = null; + + /** {@inheritDoc} */ + @Override + public final void dragStarted(IMVCBundle bundle, Node node, DiagramCoordinate diagramLocation) { + this.startBundle = bundle; + this.startNode = node; + this.startDiagramLocation = diagramLocation; + safeDragStarted(bundle, node, startDiagramLocation); + } + + /** + * Informs the drag controller that a drag has started. + * + * @param bundle + * the final {@link IMVCBundle} + * @param node + * the final node + * @param diagramLocation + * the current absolute mouse location + */ + protected void safeDragStarted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + // the default does nothing + } + + /** {@inheritDoc} */ + @Override + public final void dragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + // filter drag events that arrive before drag started was called. + if(isDragInProgress()) { + safeDragInProgress(bundle, node, diagramLocation); + } + } + + /** + * Updates the drag controller with the new drag location guaranteeing that its only called + * after {@link #dragStarted(IMVCBundle, Node, DiagramCoordinate)} was called and before + * {@link #dragCompleted(IMVCBundle, Node, DiagramCoordinate)} is + * called. + * + * @param bundle + * the current {@link IMVCBundle} + * @param node + * the current node under the drag gesture pointer + * @param diagramLocation + * the current diagram location of the drag gesture + */ + protected void safeDragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + // the default does nothing + } + + /** {@inheritDoc} */ + @Override + public final Change dragCompleted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + Change result = null; + if(isDragInProgress()) { + result = safeDragCompleted(bundle, node, diagramLocation); + } + startBundle = null; + startNode = null; + startDiagramLocation = null; + return result; + } + + /** + * Updates the drag controller with the final drag location guaranteeing that its only called + * after {@link #dragStarted(IMVCBundle, Node, DiagramCoordinate)} was called and any number of + * {@link #safeDragInProgress(IMVCBundle, Node, DiagramCoordinate)} occurred. + * + * @param bundle + * the final {@link IMVCBundle} + * @param node + * the final node under the gesture pointer + * @param diagramLocation + * the final diagram location of the drag gesture + * @return the model change which was caused by this drag operation. + */ + protected Change safeDragCompleted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + // the default does nothing + return null; + } + + /** + * Returns whether a drag gesture is in progress, i.e., dragStarted() was called and + * dragCompleted() was not yet called. + */ + protected final boolean isDragInProgress() { + return startBundle != null; + } + + /** Returns the current drag delta. */ + protected final FeedbackChange getDragDelta(DiagramCoordinate endDiagramLocation) { + double dx = endDiagramLocation.getX() - startDiagramLocation.getX(); + double dy = endDiagramLocation.getY() - startDiagramLocation.getY(); + return locationFeedbackChange(dx, dy); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DraggingUtils.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DraggingUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..95117e2ea4c36b6c6a31f8088bb95b484e0e6612 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/DraggingUtils.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange.locationFeedbackChange; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; + +/** Utility methods used for computation of locations of mouse dragging operations. */ +/* package */ final class DraggingUtils { + /** Clamps the given location and snaps it to the closest grid point. */ + public static FeedbackChange clampAndSnapMoveLocationToGridPoint(double x, double y, double dx, + double dy, DiagramViewerFeatures features) { + return clampAndSnapMoveLocation(x, y, dx, dy, features, false); + } + + /** Clamps the given location and snaps it to the closest center between four grid points. */ + public static FeedbackChange clampAndSnapMoveLocationToGridCenterPoint(double x, double y, + double dx, double dy, DiagramViewerFeatures features) { + return clampAndSnapMoveLocation(x, y, dx, dy, features, true); + } + + /** Clamps the given location and snaps it to the grid (possibly centered). */ + private static FeedbackChange clampAndSnapMoveLocation(double x, double y, double dx, double dy, + DiagramViewerFeatures features, boolean snapToCenter) { + // add 10 to compensate the mouse cursor hit point + // and make the anchorage stick closer to the cursor + double newx = x + dx + 10; + double newy = y + dy + 10; + // clamp + if(features.isDrawOuterBorder()) { + // clamp one space from origin + newx = Math.max(newx, features.getHorizontalSpacing()); + newy = Math.max(newy, features.getVerticalSpacing()); + } else { + // clamp negativ coordinates + newx = Math.max(newx, 0); + newy = Math.max(newy, 0); + } + // snap to grid + if(features.useSnapToGrid()) { + newx = snapToCenter ? features.snapToCenterX(newx) : features.snapToGridX(newx); + newy = snapToCenter ? features.snapToCenterY(newy) : features.snapToGridY(newy); + } + dx = newx - x; + dy = newy - y; + return locationFeedbackChange(dx, dy); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/LinkControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/LinkControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..392cb79cb42e9f878c665d47053b0795c3768603 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/LinkControllerBase.java @@ -0,0 +1,470 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base; + +import static java.util.Collections.emptyList; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange.locationFeedbackChange; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.DraggingUtils.clampAndSnapMoveLocationToGridCenterPoint; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.visual.ILinkVisual.END_OF_LINK_BEND_POINT_INDEX; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.visual.ILinkVisual.NO_BEND_POINT_INDEX; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.visual.ILinkVisual.SPECIAL_BEND_POINT_INDEX_CAP; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.visual.ILinkVisual.START_OF_LINK_BEND_POINT_INDEX; + +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerSelection; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IClickController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IKeyPressController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelChangeProvider; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.MVCBundleTag; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.MVCBundlePartBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.ILinkVisual; + +import javafx.geometry.Point2D; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.control.MenuItem; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseEvent; + +/** + * Base class implementing {@link IController} for links. It provides dragging implementation for + * visuals of bend-points and of the start or end of the link. It also handles creation and removal + * of bend-points (including keyboard interaction). + * <P> + * Sub-classes must implement the abstract methods, which are called once the respective operation + * is completed, and write the results back into the underlying data model. + */ +public abstract class LinkControllerBase extends MVCBundlePartBase implements IController { + /** Stores the index of the currently moving bend-point. */ + private int currentBendPointIndex = 0; + + /** Constructor. */ + public LinkControllerBase(ILinkMVCBundle mvcb) { + super(mvcb); + } + + /** {@inheritDoc} */ + @Override + public IModelChangeProvider getModelChangeProvider() { + return null; + } + + /** Create a new bend point with the given mouse click coordinates. */ + protected abstract void createBendPointAt(int bpIndex, DiagramCoordinate location); + + /** + * Moves the model element. + * + * @param bpIndex + * the index of moved bend-point + * @param dx + * the x delta + * @param dy + * the y delta + */ + protected abstract void updateModelAfterBendPointMove(int bpIndex, double dx, double dy); + + /** Deletes the current bend-point. */ + protected abstract void deleteBendPoint(int bpIndex); + + /** + * Moves the given end of the link to the new anchorage. The given bend-point + */ + protected abstract void updateModelAfterLinkMove(ILinkMVCBundle linkBundle, + IMVCBundle startBundle, IMVCBundle endBundle); + + /** Removes the given bend point. */ + protected abstract void deleteLink(); + + /** Sets the current bend-point index. */ + protected final void setCurrentBendPointIndex(int bendPointIndex) { + currentBendPointIndex = bendPointIndex; + } + + /** {@inheritDoc} */ + @Override + public ILinkMVCBundle getMVCBundle() { + // wild cast works: see constructor + return (ILinkMVCBundle)super.getMVCBundle(); + } + + /** Returns the link visual. */ + protected ILinkVisual getLinkVisual() { + return getMVCBundle().getVisual(); + } + + /** Returns the {@link IClickController} for creating bend-points and selecting the link. */ + protected IClickController getBendPointCreateAndLinkSelectController() { + return new ClickControllerBase() { + @Override + public Change singleClick(MouseEvent event, Node node, + DiagramCoordinate diagramLocation) { + DiagramViewer viewer = getViewer(); + if(event.isControlDown()) { + // link must be primary selected + if(!viewer.isPrimarySelected(getMVCBundle())) { + return null; + } + // control+click on visible or selection line creates a bend point + DiagramViewerFeatures features = viewer.getFeatures(); + DiagramCoordinate snappedLocation = features.snapToCenter(diagramLocation); + return () -> createBendPointAt(currentBendPointIndex, snappedLocation); + } + if(event.isShiftDown()) { + viewer.handleShiftSelectionOf(getMVCBundle()); + } else { + viewer.setSingleSelectedMVCBundle(getMVCBundle()); + } + return null; + } + + @Override + public Change secondaryClick(MouseEvent event, Node node, + DiagramCoordinate locationOnNode) { + if(event.isControlDown()) { + // link must be primary selected + DiagramViewer viewer = getViewer(); + if(viewer.isPrimarySelected(getMVCBundle())) { + // control+right-click deletes the link + return () -> deleteLink(); + } + } + return null; + } + }; + } + + /** Returns the {@link IDragController} for moving bend-points. */ + protected IDragController getBendPointMoveController() { + return new DragControllerBase() { + @Override + public void safeDragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + Point2D delta = diagramLocation.subtract(startDiagramLocation); + moveBendPointVisually(currentBendPointIndex, delta); + getViewer().updateVisual(getMVCBundle()); + } + + @Override + public Change safeDragCompleted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + Point2D delta = diagramLocation.subtract(startDiagramLocation); + return () -> moveBendPointInModel(currentBendPointIndex, delta); + } + }; + } + + /** + * Updates the bend-point visual(s) during the dragging phase of the move operation. Sub-classes + * may override to move other bend-points than only the current bend-point. + */ + protected void moveBendPointVisually(int bpIndex, Point2D delta) { + FeedbackChange chg = getMoveDeltaFeedback(bpIndex, delta.getX(), delta.getY()); + getLinkVisual().setFeedbackChangeForBendPoint(bpIndex, chg); + } + + /** + * Updates the bend-point visual(s) during the dragging phase of the move operation. Sub-classes + * may override to move other bend-points than only the current bend-point. + */ + protected void moveBendPointVisually(int bpIndex, FeedbackChange change) { + FeedbackChange chg = getMoveDeltaFeedback(bpIndex, change.getDeltaX(), change.getDeltaY()); + getLinkVisual().setFeedbackChangeForBendPoint(bpIndex, chg); + } + + /** Returns the clamped and snapped move delta feedback change for the given bend point. */ + protected final FeedbackChange getMoveDeltaFeedback(int bpIndex, Point2D delta) { + return getMoveDeltaFeedback(bpIndex, delta.getX(), delta.getY()); + } + + /** Returns the clamped and snapped move delta feedback change for the given bend point. */ + protected final FeedbackChange getMoveDeltaFeedback(int bpIndex, double dx, double dy) { + DiagramCoordinate bpModelLocation = getLinkVisual().getBendPointLocation(bpIndex); + DiagramViewerFeatures features = getViewer().getFeatures(); + if(clampAndSnapBendPointToGridCenter()) { + return clampAndSnapMoveLocationToGridCenterPoint(bpModelLocation.getX(), + bpModelLocation.getY(), dx, dy, features); + } + if(clampAndSnapBendPointToGrid()) { + return DraggingUtils.clampAndSnapMoveLocationToGridPoint(bpModelLocation.getX(), + bpModelLocation.getY(), dx, dy, features); + } + return locationFeedbackChange(dx, dy); + } + + /** + * Returns whether bend point positions should be snapped to the center of four grid points. + * This mode is the default and has priority over {@link #clampAndSnapBendPointToGrid()}. + */ + protected boolean clampAndSnapBendPointToGridCenter() { + return true; + } + + /** Returns whether bend point positions should be snapped to grid points. */ + protected boolean clampAndSnapBendPointToGrid() { + return false; + } + + /** + * Updates the bend-point model element(s) after the dragging of the move operation is complete. + */ + protected void moveBendPointInModel(int bpIndex, Point2D delta) { + FeedbackChange chg = getMoveDeltaFeedback(bpIndex, delta.getX(), delta.getY()); + getLinkVisual().setFeedbackChangeForBendPoint(bpIndex, null); + updateModelAfterBendPointMove(bpIndex, chg.getDeltaX(), chg.getDeltaY()); + } + + /** + * Updates the bend-point model element(s) after the dragging of the move operation is complete. + */ + protected void moveBendPointInModel(int bpIndex, FeedbackChange delta) { + FeedbackChange chg = getMoveDeltaFeedback(bpIndex, delta.getDeltaX(), delta.getDeltaY()); + getLinkVisual().setFeedbackChangeForBendPoint(bpIndex, null); + updateModelAfterBendPointMove(bpIndex, chg.getDeltaX(), chg.getDeltaY()); + } + + /** Returns the {@link IClickController} for deleting bend-points. */ + protected IClickController getBendPointDeleteController() { + return new ClickControllerBase() { + @Override + public Change secondaryClick(MouseEvent event, Node node, + DiagramCoordinate diagramLocation) { + if(event.isControlDown()) { + // bundle must be primary selection + DiagramViewer viewer = getViewer(); + if(viewer.isPrimarySelected(getMVCBundle())) { + // control+right-click on selection handle of bend point removes it + return () -> deleteBendPoint(currentBendPointIndex); + } + } + return null; + } + }; + } + + /** {@inheritDoc} */ + @Override + public IDragController getDragController(EDragGesture gesture, Node node, + DiagramCoordinate diagramLocation) { + DiagramViewerSelection selection = getViewer().getSelection(); + ILinkMVCBundle linkBundle = getMVCBundle(); + if(selection.getPrimarySelection() == linkBundle) { + // links with primary selection show move handles + ILinkVisual linkVisual = getLinkVisual(); + int bpIndex = linkVisual.getBendPointIndex(node); + if(bpIndex == START_OF_LINK_BEND_POINT_INDEX || + bpIndex == END_OF_LINK_BEND_POINT_INDEX) { + return getLinkMoveController(bpIndex); + } + if(bpIndex == NO_BEND_POINT_INDEX) { + return null; + } + setCurrentBendPointIndex(bpIndex); + return getBendPointMoveController(); + } + if(selection.isSelected(linkBundle)) { + // selected links may be moved by group move operation + return new DragControllerBase() { + /** {@inheritDoc} */ + @Override + protected void safeDragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + FeedbackChange fc = getDragDelta(diagramLocation); + int numerOfBendPoints = getNumerOfBendPoints(); + for(int i = 0; i < numerOfBendPoints; i++) { + moveBendPointVisually(i, fc); + } + } + + /** {@inheritDoc} */ + @Override + protected Change safeDragCompleted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + FeedbackChange fc = getDragDelta(diagramLocation); + return () -> { + int numerOfBendPoints = getNumerOfBendPoints(); + for(int i = 0; i < numerOfBendPoints; i++) { + moveBendPointInModel(i, fc); + } + }; + } + }; + } + return null; + } + + /** + * Returns an {@link IDragController} for relocating the end of this link. {@code bpIndex} is + * either {@link ILinkVisual#START_OF_LINK_BEND_POINT_INDEX} or + * {@link ILinkVisual#END_OF_LINK_BEND_POINT_INDEX} depending on which part of the link is + * moved. + */ + protected IDragController getLinkMoveController(final int bpIndex) { + return new DragControllerBase() { + @Override + protected void safeDragStarted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + ILinkMVCBundle linkBundle = getMVCBundle(); + IAnchorageMVCBundle anchorage; + if(bpIndex == START_OF_LINK_BEND_POINT_INDEX) { + anchorage = linkBundle.getStartAnchorage(); + } else { + anchorage = linkBundle.getEndAnchorage(); + } + getLinkVisual().disableFeedback(); + getViewer().startMoveLinkFeedback(linkBundle, anchorage); + } + + @Override + public void safeDragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + Point2D linkAnchorageLocation = + bundle.getVisual().getLinkAnchorage(diagramLocation); + Point2D delta; + if(linkAnchorageLocation != null) { + delta = linkAnchorageLocation.subtract(startDiagramLocation); + } else { + delta = diagramLocation.subtract(startDiagramLocation); + } + FeedbackChange chg = locationFeedbackChange(delta.getX(), delta.getY()); + getLinkVisual().setFeedbackChangeForBendPoint(bpIndex, chg); + getViewer().updateVisual(getMVCBundle()); + } + + @Override + public Change safeDragCompleted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + getLinkVisual().enableFeedback(); + getLinkVisual().setFeedbackChangeForBendPoint(bpIndex, null); + DiagramViewer viewer = getViewer(); + viewer.terminateMoveLinkFeedback(); + ILinkMVCBundle linkBundle = getMVCBundle(); + if(bpIndex == START_OF_LINK_BEND_POINT_INDEX) { + return () -> updateModelAfterLinkMove(linkBundle, bundle, + linkBundle.getEndAnchorage()); + } + return () -> updateModelAfterLinkMove(linkBundle, linkBundle.getStartAnchorage(), + bundle); + } + }; + } + + /** {@inheritDoc} */ + @Override + public IClickController getClickController(Node node, DiagramCoordinate diagramLocation) { + int bpIndex = getLinkVisual().getBendPointIndex(node); + if(bpIndex < SPECIAL_BEND_POINT_INDEX_CAP) { + return null; + } + setCurrentBendPointIndex(bpIndex); + if(getLinkVisual().isBendPointHandle(node)) { + return getBendPointDeleteController(); + } + return getBendPointCreateAndLinkSelectController(); + } + + /** {@inheritDoc} */ + @Override + public boolean selectOnMousePress() { + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean selectOnShiftMousePress() { + return true; + } + + /** {@inheritDoc} */ + @Override + public IKeyPressController getKeyPressController(Node node, + DiagramCoordinate lastMouseLocation) { + return new IKeyPressController() { + @Override + public Change keyEvent(KeyEvent event, IMVCBundle bundle, Node source, + DiagramCoordinate mouseLocation) { + KeyCode code = event.getCode(); + if(code == KeyCode.DELETE) { + return () -> deleteLink(); + } else if(code == KeyCode.A && event.isControlDown()) { + getViewer().selectAll(); + } + return null; + } + }; + } + + /** {@inheritDoc} */ + @Override + public List<MenuItem> contextMenuContributions(Node node, DiagramCoordinate diagramLocation) { + return emptyList(); + } + + /** {@inheritDoc} */ + @Override + public boolean isPossibleLinkTarget() { + return false; + } + + /** {@inheritDoc} */ + @Override + public MVCBundleTag getLinkTargetEffectForNewLinkFrom(IMVCBundle linkStartBundle) { + return null; + } + + /** {@inheritDoc} */ + @Override + public MVCBundleTag getLinkTargetEffectForMoveLink(ILinkMVCBundle linkBundle, + IAnchorageMVCBundle movedAnchorageBundle) { + return null; + } + + /** {@inheritDoc} */ + @Override + public Cursor getCurrentCursor(Node node, DiagramCoordinate diagramLocation) { + EDragGesture edg = getVisual().getDragGesture(node, diagramLocation); + switch(edg) { + case MOVE_BENDPOINT: + return allowBendPointMove() ? Cursor.MOVE : Cursor.DEFAULT; + case NEW_BENDPOINT: + return Cursor.CROSSHAIR; + case RECONNECT_LINK: + return allowLinkMove() ? Cursor.MOVE : Cursor.DEFAULT; + default: + return Cursor.DEFAULT; + } + } + + /** Determines whether bend points can be moved. */ + protected boolean allowBendPointMove() { + return true; + } + + /** Determines whether link start and end can be relocated. */ + protected boolean allowLinkMove() { + return true; + } + + /** Returns the number of bend-points in the model. */ + protected abstract int getNumerOfBendPoints(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/MoveControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/MoveControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..38d632e31f5e27d112ecdd4933e3a331378180d0 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/MoveControllerBase.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture.MOVE; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange.locationFeedbackChange; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.DraggingUtils.clampAndSnapMoveLocationToGridPoint; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IKeyPressController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; + +import javafx.geometry.Rectangle2D; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; + +/** + * Controller implementation, which allows to move the underlying model element. Sub-classes must + * implement {@link #move(FeedbackChange)} in order to change the underlying model with the + * respective coordinate changes. + */ +public abstract class MoveControllerBase extends ControllerBase { + /** Constructor. */ + public MoveControllerBase(IMVCBundle mvcb) { + super(mvcb); + } + + /** {@inheritDoc} */ + @Override + public IDragController getDragController(EDragGesture gesture, Node node, + DiagramCoordinate diagramLocation) { + if(allowMove() && gesture == MOVE) { + return moveController; + } + return super.getDragController(gesture, node, diagramLocation); + } + + /** {@inheritDoc} */ + @Override + public Cursor getCurrentCursor(Node node, DiagramCoordinate diagramLocation) { + EDragGesture edg = getVisual().getDragGesture(node, diagramLocation); + if(edg == EDragGesture.MOVE) { + return allowMove() ? Cursor.MOVE : Cursor.DEFAULT; + } + return super.getCurrentCursor(node, diagramLocation); + } + + /** {@inheritDoc} */ + @Override + public IKeyPressController getKeyPressController(Node node, + DiagramCoordinate lastMouseLocation) { + if(allowMove() && allowKeyboardMove()) { + return new IKeyPressController() { + @Override + public Change keyEvent(KeyEvent event, IMVCBundle bundle, Node source, + DiagramCoordinate mouseLocation) { + DiagramViewerFeatures features = getViewer().getFeatures(); + double hSpacing = features.getHorizontalSpacing(); + double vSpacing = features.getVerticalSpacing(); + FeedbackChange fbChange = null; + KeyCode code = event.getCode(); + if(code == KeyCode.RIGHT) { + fbChange = locationFeedbackChange(hSpacing, 0); + } else if(code == KeyCode.LEFT) { + fbChange = locationFeedbackChange(-hSpacing, 0); + } else if(code == KeyCode.UP) { + fbChange = locationFeedbackChange(0, -vSpacing); + } else if(code == KeyCode.DOWN) { + fbChange = locationFeedbackChange(0, vSpacing); + } else { + return MoveControllerBase.super.getKeyPressController(node, + lastMouseLocation).keyEvent(event, bundle, source, mouseLocation); + } + if(fbChange != null && !fbChange.isEmpty()) { + return new MoveChange(fbChange); + } + return null; + } + }; + } + return super.getKeyPressController(node, lastMouseLocation); + } + + /** + * Perform the move operation by altering the underlying model. The default does nothing. + * + * @param deltaChange + * the (possibly altered) delta the model should be moved. + */ + protected void move(FeedbackChange deltaChange) { + // default does nothing + } + + /** Returns whether the model element can be moved. */ + protected boolean allowMove() { + return true; + } + + /** Returns whether the model element can be moved by keyboard commands. */ + protected boolean allowKeyboardMove() { + return true; + } + + /** + * Returns whether move operations should be clamped to positive diagram area and snapped to the + * grid. + */ + protected boolean clampAndSnapMove() { + return true; + } + + /** Returns whether child elements should be moved also. */ + protected boolean moveChildren() { + return true; + } + + /** Sets the current feedback change for the given bundle and possibly its children. */ + private void setMoveFeedbackChange(FeedbackChange change, IMVCBundle bundle) { + bundle.setFeedbackChange(change); + if(moveChildren() && bundle instanceof IContentMVCBundle) { + IContentMVCBundle cb = (IContentMVCBundle)bundle; + for(IMVCBundle ch : cb.getChildren()) { + setMoveFeedbackChange(change, ch); + } + } + } + + /** + * Alters the move feedback to apply some constraints to it (e.g. no negative result location). + */ + protected FeedbackChange alterMoveFeedback(FeedbackChange change) { + if(!clampAndSnapMove()) { + return change; + } + Rectangle2D modelBounds = getVisual().getModelBounds(); + return clampAndSnapMoveLocationToGridPoint(modelBounds.getMinX(), modelBounds.getMinY(), + change.getDeltaX(), change.getDeltaY(), getViewer().getFeatures()); + } + + /** The drag controller managing the move operation. */ + private final IDragController moveController = new DragControllerBase() { + @Override + protected final void safeDragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + FeedbackChange altered = alterMoveFeedback(getDragDelta(diagramLocation)); + setMoveFeedbackChange(altered, startBundle); + } + + @Override + protected final Change safeDragCompleted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + setMoveFeedbackChange(FeedbackChange.EMPTY, startBundle); + final FeedbackChange change = alterMoveFeedback(getDragDelta(diagramLocation)); + if(change.isEmpty()) { + return null; + } + return new MoveChange(change); + } + }; + + /** A {@link Change} that calls {@link #move(FeedbackChange)}. */ + private final class MoveChange implements Change { + /** The feedback change to be applied. */ + private final FeedbackChange change; + + /** Constructor. */ + public MoveChange(FeedbackChange change) { + this.change = change; + } + + /** {@inheritDoc} */ + @Override + public void applyChange() { + move(change); + } + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ResizableContentControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ResizableContentControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..898500d389b035f8138308d496d2d24be501c719 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/base/ResizableContentControllerBase.java @@ -0,0 +1,269 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base; + +import static java.lang.Math.max; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture.RESIZE_H; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture.RESIZE_V; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange.sizeFeedbackChange; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IClickController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentVisual; + +import javafx.geometry.Dimension2D; +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.input.MouseEvent; + +/** + * Base class providing dragging implementation for visuals which move and resize in the + * {@link DiagramViewer}. Sub-classes must implement {@link #resize(FeedbackChange)} and + * {@link #move(FeedbackChange)} in order to change the underlying model with the respective + * coordinate changes. + */ +public abstract class ResizableContentControllerBase extends MoveControllerBase { + /** Constructor. */ + public ResizableContentControllerBase(IContentMVCBundle mvcb) { + super(mvcb); + } + + /** {@inheritDoc} */ + @Override + public IClickController getClickController(Node node, DiagramCoordinate diagramLocation) { + // wild cast works: see constructor + IContentVisual visual = (IContentVisual)getVisual(); + if(allowExpandCollapse() && visual.isExpandCollapseWidget(node)) { + return new ClickControllerBase() { + /** {@inheritDoc} */ + @Override + public Change singleClick(MouseEvent event, Node node, + DiagramCoordinate diagramLocation) { + return () -> expandCollapse(); + } + }; + } + return super.getClickController(node, diagramLocation); + } + + /** Returns whether the expand/collapse feature is active. */ + public boolean allowExpandCollapse() { + return false; + } + + /** Triggers the expand/collapse operation. */ + public void expandCollapse() { + // the default does nothing + } + + /** Returns whether the horizontal resize is available. */ + public boolean allowHorizontalResize() { + return true; + } + + /** Returns whether the vertical resize is available. */ + public boolean allowVerticalResize() { + return true; + } + + /** Returns whether child elements should be resized also. */ + protected boolean resizeChildren() { + return true; + } + + /** Sets the current feedback change for the given bundle and possibly its children. */ + private void setResizeFeedbackChange(FeedbackChange change, IMVCBundle bundle) { + bundle.setFeedbackChange(change); + if(resizeChildren() && bundle instanceof IContentMVCBundle) { + IContentMVCBundle cb = (IContentMVCBundle)bundle; + for(IMVCBundle ch : cb.getChildren()) { + setResizeFeedbackChange(change, ch); + } + } + } + + /** + * The minimum dimension of the diagram element in model coordinates. It will not be resizable + * below this value. The default value is a single grid spacing. + */ + protected Dimension2D getMinimumSize() { + DiagramViewerFeatures features = getViewer().getFeatures(); + return new Dimension2D(features.getHorizontalSpacing(), features.getVerticalSpacing()); + } + + /** {@inheritDoc} */ + @Override + public IDragController getDragController(EDragGesture gesture, Node n, + DiagramCoordinate diagramLocation) { + if(gesture == RESIZE_H && allowHorizontalResize()) { + return getHorizontalResizeDragController(); + } + if(gesture == RESIZE_V && allowVerticalResize()) { + return getVerticalResizeDragController(); + } + if(gesture == EDragGesture.RESIZE_VH && allowHorizontalResize() && allowVerticalResize()) { + return getHorizontalVerticalResizeDragController(); + } + return super.getDragController(gesture, n, diagramLocation); + } + + /** Returns the {@link IDragController} for the horizontal resize. */ + public IDragController getHorizontalResizeDragController() { + return defaultHorizontalResizeDragController; + } + + /** The default controller for horizontal resize. */ + private IDragController defaultHorizontalResizeDragController = new DragControllerBase() { + @Override + public void safeDragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + Point2D delta = diagramLocation.subtract(startDiagramLocation); + // use startBundle since bundle and node might differ + handleResizeDragInProgress(startBundle, delta.getX(), 0); + } + + @Override + public Change safeDragCompleted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + Point2D delta = diagramLocation.subtract(startDiagramLocation); + // use startBundle since bundle and node might differ + return handleResizeCompleted(startBundle, delta.getX(), 0); + } + }; + + /** Applies the feedback change for the given delta. */ + private void handleResizeDragInProgress(IMVCBundle bundle, double dw, double dh) { + FeedbackChange altered = alterResizeFeedback(sizeFeedbackChange(dw, dh)); + setResizeFeedbackChange(altered, bundle); + } + + /** Handles the completion of the resize operation. */ + private Change handleResizeCompleted(IMVCBundle bundle, double dw, double dh) { + bundle.setFeedbackChange(FeedbackChange.EMPTY); + final FeedbackChange altered = alterResizeFeedback(sizeFeedbackChange(dw, dh)); + if(altered.isEmpty()) { + return null; + } + return () -> resize(altered); + } + + /** + * Returns whether resize operations should be clamped to positive diagram area and snapped to + * the grid. + */ + protected boolean clampAndSnapResize() { + return true; + } + + /** Alters the given resize feedback (e.g. to uphold minimum size. */ + protected FeedbackChange alterResizeFeedback(FeedbackChange change) { + IContentVisual visual = (IContentVisual)getMVCBundle().getVisual(); + Rectangle2D orig = visual.getModelBounds(); + double dw = change.getDeltaW(); + double dh = change.getDeltaH(); + Dimension2D minDim = getMinimumSize(); + double newMaxX = orig.getMinX() + max(minDim.getWidth(), orig.getWidth() + dw); + double newMaxY = orig.getMinY() + max(minDim.getHeight(), orig.getHeight() + dh); + if(clampAndSnapResize()) { + DiagramViewerFeatures features = getViewer().getFeatures(); + if(features.useSnapToGrid()) { + newMaxX = features.snapToGridX(newMaxX); + newMaxY = features.snapToGridY(newMaxY); + } + } + return sizeFeedbackChange(newMaxX - orig.getMaxX(), newMaxY - orig.getMaxY()); + } + + /** Returns the {@link IDragController} for the vertical resize. */ + public IDragController getVerticalResizeDragController() { + return defaultVerticalResizeController; + } + + /** The default controller for vertical resize. */ + private IDragController defaultVerticalResizeController = new DragControllerBase() { + @Override + public void safeDragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + Point2D delta = diagramLocation.subtract(startDiagramLocation); + // use startBundle since bundle and node might differ + handleResizeDragInProgress(startBundle, 0, delta.getY()); + } + + @Override + public Change safeDragCompleted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + Point2D delta = diagramLocation.subtract(startDiagramLocation); + // use startBundle since bundle and node might differ + return handleResizeCompleted(startBundle, 0, delta.getY()); + } + }; + + /** Returns the {@link IDragController} for the resize. */ + public IDragController getHorizontalVerticalResizeDragController() { + return defaultHorizontalVerticalResizeDragController; + } + + /** The default controller for horizontal and vertical resize. */ + private IDragController defaultHorizontalVerticalResizeDragController = + new DragControllerBase() { + @Override + public void safeDragInProgress(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + Point2D delta = diagramLocation.subtract(startDiagramLocation); + // use startBundle since bundle and node might differ + handleResizeDragInProgress(startBundle, delta.getX(), delta.getY()); + } + + @Override + public Change safeDragCompleted(IMVCBundle bundle, Node node, + DiagramCoordinate diagramLocation) { + Point2D delta = diagramLocation.subtract(startDiagramLocation); + // use startBundle since bundle and node might differ + return handleResizeCompleted(startBundle, delta.getX(), delta.getY()); + } + }; + + /** + * Resizes the model element. + * + * @param delta + * the delta width and height to be applied to the model element + */ + protected void resize(FeedbackChange delta) { + // default does nothing + } + + /** {@inheritDoc} */ + @Override + public Cursor getCurrentCursor(Node node, DiagramCoordinate diagramLocation) { + EDragGesture edg = getVisual().getDragGesture(node, diagramLocation); + switch(edg) { + case RESIZE_H: + return allowHorizontalResize() ? Cursor.H_RESIZE : Cursor.DEFAULT; + case RESIZE_V: + return allowVerticalResize() ? Cursor.V_RESIZE : Cursor.DEFAULT; + case RESIZE_VH: + return allowHorizontalResize() && allowVerticalResize() ? Cursor.SE_RESIZE + : Cursor.DEFAULT; + default: + } + return super.getCurrentCursor(node, diagramLocation); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..ca7cd95560997655fec8af345c644cec0bccbc37 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/.ratings @@ -0,0 +1,3 @@ +CircularResizableContentControllerBase.java daf05a58eac298462a5f092503e506575b31dff1 YELLOW +CurveLinkBendPointControllerBase.java 4d119cd640b864f2193ea5c1a7f816310b7a57a4 YELLOW +EllipticResizableContentControllerBase.java 42bcbdbf29c1cf2b71177e55f74358d1517d623f YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/CircularResizableContentControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/CircularResizableContentControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..daf05a58eac298462a5f092503e506575b31dff1 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/CircularResizableContentControllerBase.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.elliptic; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic.EllipticBorderLocation.getClosestLocationOnBounds; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.AnchorageContentControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.ContentAnchorageMoveControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic.EllipticBorderLocation; + +import javafx.geometry.Dimension2D; +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; + +/** Base class for controllers with content visuals represented by ellipses. */ +public abstract class CircularResizableContentControllerBase + extends AnchorageContentControllerBase { + /** Constructor. */ + public CircularResizableContentControllerBase(IContentMVCBundle mvcb) { + super(mvcb); + } + + /** {@inheritDoc} */ + @Override + protected IDragController createAnchorageMoveController(IContentAnchorageMVCBundle anchorage) { + return new CircularAnchorageMoveController(anchorage); + } + + /** Move controller for anchorages moving on the ellipse border. */ + private final class CircularAnchorageMoveController extends ContentAnchorageMoveControllerBase { + /** Constructor. */ + public CircularAnchorageMoveController(IContentAnchorageMVCBundle anchorage) { + super(anchorage); + } + + /** Returns the border location on the ellipse shape. */ + private EllipticBorderLocation getBorderLocation(DiagramCoordinate delta) { + Rectangle2D contentBounds = getVisual().getModelBounds(); + Rectangle2D anchorageBounds = anchorage.getVisual().getModelBounds(); + Dimension2D span = + new Dimension2D(anchorageBounds.getWidth(), anchorageBounds.getHeight()); + Point2D p = new Point2D(anchorageBounds.getMinX() + delta.getX(), + anchorageBounds.getMinY() + delta.getY()); + EllipticBorderLocation rbl = getClosestLocationOnBounds(p, contentBounds, span); + return rbl; + } + + /** {@inheritDoc} */ + @Override + protected Point2D getAnchorageFeedbackLocation(DiagramCoordinate delta) { + EllipticBorderLocation rbl = getBorderLocation(delta); + return rbl.getLocation(); + } + + /** {@inheritDoc} */ + @Override + protected void moveAnchorage(DiagramCoordinate delta) { + EllipticBorderLocation rbl = getBorderLocation(delta); + moveAnchorageToAngle(anchorage, rbl.getAngleInDegree()); + } + } + + /** Sets the angle of the given anchorage when move operation has completed. */ + protected abstract void moveAnchorageToAngle(IContentAnchorageMVCBundle anchorage, + double angleInDegree); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/CurveLinkBendPointControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/CurveLinkBendPointControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..4d119cd640b864f2193ea5c1a7f816310b7a57a4 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/CurveLinkBendPointControllerBase.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.elliptic; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange.locationFeedbackChange; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.LinkControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.ILinkVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic.CurveLinkVisualBase; + +import javafx.geometry.Point2D; + +/** Base class for {@link CurveLinkVisualBase} dragging implementation. */ +public abstract class CurveLinkBendPointControllerBase extends LinkControllerBase { + /** Constructor. */ + public CurveLinkBendPointControllerBase(ILinkMVCBundle mvcb) { + super(mvcb); + } + + /** {@inheritDoc} */ + @Override + protected final void moveBendPointVisually(int bpIndex, Point2D delta) { + super.moveBendPointVisually(bpIndex, delta); + if(bpIndex <= 0 || bpIndex >= getNumerOfBendPoints() - 1) { + // first and last bend-point move unconnected + return; + } + ILinkVisual linkVisual = getLinkVisual(); + FeedbackChange centerPointChange = getMoveDeltaFeedback(bpIndex, delta); + int indexMod3 = bpIndex % 3; + if(indexMod3 == 2) { + // bend-points between curve segments also drag the previous and next point + linkVisual.setFeedbackChangeForBendPoint(bpIndex - 1, centerPointChange); + linkVisual.setFeedbackChangeForBendPoint(bpIndex + 1, centerPointChange); + } else { + // otherwise the connected bend-point is moved in the opposite direction + FeedbackChange inv = locationFeedbackChange(-centerPointChange.getDeltaX(), + -centerPointChange.getDeltaY()); + int otherIndex = (indexMod3 == 1) ? bpIndex + 2 : bpIndex - 2; + linkVisual.setFeedbackChangeForBendPoint(otherIndex, inv); + } + // linkVisual.update(); + return; + } + + /** {@inheritDoc} */ + @Override + protected final void moveBendPointInModel(int bpIndex, Point2D delta) { + FeedbackChange chg = getMoveDeltaFeedback(bpIndex, delta); + getLinkVisual().setFeedbackChangeForBendPoint(bpIndex, null); + updateModelAfterBendPointMove(bpIndex, chg.getDeltaX(), chg.getDeltaY()); + if(bpIndex <= 0 || bpIndex >= getNumerOfBendPoints() - 1) { + // first and last bend-point move unconnected + return; + } + ILinkVisual linkVisual = getLinkVisual(); + int indexMod3 = bpIndex % 3; + if(indexMod3 == 2) { + // bend-points between curve segments also drag the previous and next point + linkVisual.setFeedbackChangeForBendPoint(bpIndex - 1, null); + updateModelAfterBendPointMove(bpIndex - 1, chg.getDeltaX(), chg.getDeltaY()); + linkVisual.setFeedbackChangeForBendPoint(bpIndex + 1, null); + updateModelAfterBendPointMove(bpIndex + 1, chg.getDeltaX(), chg.getDeltaY()); + } else { + // otherwise the connected bend-point is moved in the opposite direction + int otherIndex = (indexMod3 == 1) ? bpIndex + 2 : bpIndex - 2; + linkVisual.setFeedbackChangeForBendPoint(otherIndex, null); + updateModelAfterBendPointMove(otherIndex, -chg.getDeltaX(), -chg.getDeltaY()); + } + } + + /** {@inheritDoc} */ + @Override + protected final void deleteBendPoint(int bpIndex) { + if(bpIndex <= 0 || bpIndex >= getNumerOfBendPoints() - 1) { + // never delete first and last bend-points (i.e. control points) + return; + } + int indexMod3 = bpIndex % 3; + if(indexMod3 == 0) { + deleteFromCurve(bpIndex - 2, bpIndex - 1, bpIndex); + } else if(indexMod3 == 1) { + deleteFromCurve(bpIndex, bpIndex + 1, bpIndex + 2); + } else { + deleteFromCurve(bpIndex - 1, bpIndex, bpIndex + 1); + } + } + + /** Deletes the given three bend-points from the model. */ + protected abstract void deleteFromCurve(int ctrlPoint2Index, int segmentPointIndex, + int ctrlPoint1Index); + + /** {@inheritDoc} */ + @Override + protected final void createBendPointAt(int bpIndex, DiagramCoordinate location) { + DiagramViewerFeatures features = getViewer().getFeatures(); + DiagramCoordinate c2 = new DiagramCoordinate( + location.getX() - 2 * features.getHorizontalSpacing(), location.getY()); + DiagramCoordinate c1 = new DiagramCoordinate( + location.getX() + 2 * features.getHorizontalSpacing(), location.getY()); + createCurveSegment(bpIndex - 1, c2, location, c1); + } + + /** Creates the new curve segment with the given locations after the given index. */ + protected abstract void createCurveSegment(int afterIndex, DiagramCoordinate newCtrlPt2, + DiagramCoordinate newSegmentPoint, DiagramCoordinate newCtrlPt1); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/EllipticResizableContentControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/EllipticResizableContentControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..42bcbdbf29c1cf2b71177e55f74358d1517d623f --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/elliptic/EllipticResizableContentControllerBase.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.elliptic; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic.EllipticBorderLocation.getClosestLocationOnBounds; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.AnchorageContentControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.ContentAnchorageMoveControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic.EllipticBorderLocation; + +import javafx.geometry.Dimension2D; +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; + +/** Base class for controllers with content visuals represented by ellipses. */ +public abstract class EllipticResizableContentControllerBase + extends AnchorageContentControllerBase { + /** Constructor. */ + public EllipticResizableContentControllerBase(IContentMVCBundle mvcb) { + super(mvcb); + } + + /** {@inheritDoc} */ + @Override + protected final IDragController + createAnchorageMoveController(IContentAnchorageMVCBundle anchorage) { + return new EllipticAnchorageMoveController(anchorage); + } + + /** Move controller for anchorages moving on the ellipse border. */ + private final class EllipticAnchorageMoveController extends ContentAnchorageMoveControllerBase { + /** Constructor. */ + public EllipticAnchorageMoveController(IContentAnchorageMVCBundle anchorage) { + super(anchorage); + } + + /** Returns the border location on the ellipse shape. */ + private EllipticBorderLocation getBorderLocation(DiagramCoordinate delta) { + Rectangle2D contentBounds = getVisual().getModelBounds(); + Rectangle2D anchorageBounds = anchorage.getVisual().getModelBounds(); + Dimension2D span = + new Dimension2D(anchorageBounds.getWidth(), anchorageBounds.getHeight()); + Point2D p = new Point2D(anchorageBounds.getMinX() + delta.getX(), + anchorageBounds.getMinY() + delta.getY()); + EllipticBorderLocation rbl = getClosestLocationOnBounds(p, contentBounds, span); + return rbl; + } + + /** {@inheritDoc} */ + @Override + protected Point2D getAnchorageFeedbackLocation(DiagramCoordinate delta) { + EllipticBorderLocation rbl = getBorderLocation(delta); + return rbl.getLocation(); + } + + /** {@inheritDoc} */ + @Override + protected void moveAnchorage(DiagramCoordinate delta) { + EllipticBorderLocation rbl = getBorderLocation(delta); + moveAnchorageToAngle(anchorage, rbl.getAngleInDegree()); + } + } + + /** Sets the angle of the given anchorage when move operation has completed. */ + protected abstract void moveAnchorageToAngle(IContentAnchorageMVCBundle anchorage, + double angleInDegree); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..47f0dc98d33d9c0b1565094a64e13befc63d6696 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/.ratings @@ -0,0 +1,5 @@ +DiamondResizableContentControllerBase.java 31f227e88205bd2e131326bdddd8421991b47d79 YELLOW +RectangularContentAnchorageMoveController.java c119b011f5a38933cee07b8082fcd44c6c3b0037 YELLOW +RectangularResizableContentControllerBase.java 262b083e4e0ce842b0bb8fbb594585e9f0d2f678 YELLOW +RhomboidContentAnchorageMoveController.java fbd40ce483f99c18cfa94076374f572bb72c2646 YELLOW +RhomboidResizableContentControllerBase.java 863b9eae49bad18f71b20c8cde4b0f350348aa27 YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/DiamondResizableContentControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/DiamondResizableContentControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..31f227e88205bd2e131326bdddd8421991b47d79 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/DiamondResizableContentControllerBase.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.rectangular; + +import static java.lang.Math.hypot; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.AnchorageContentControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.ContentAnchorageMoveControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; + +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.geometry.Side; + +/** Base class for controllers with content visuals represented by diamonds. */ +public abstract class DiamondResizableContentControllerBase extends AnchorageContentControllerBase { + /** Relative X coordinates to be checked for anchorage locations. */ + private static final double[] XCOORDS = {0.5, 0.5, 0, 1}; + /** Relative Y coordinates to be checked for anchorage locations. */ + private static final double[] YCOORDS = {0, 1, 0.5, 0.5}; + + /** Constructor. */ + public DiamondResizableContentControllerBase(IContentMVCBundle mvcb) { + super(mvcb); + } + + /** Finds the closest corner of the diamond shape relative to the given indicator position. */ + private Side getClosestBorderLocation(double indicatorX, double indicatorY, + Rectangle2D bounds) { + Side resultSide = Side.values()[0]; + double hypo = Double.MAX_VALUE; + double boundsX = bounds.getMinX(); + double boundsY = bounds.getMinY(); + for(int cur = 0; cur < XCOORDS.length; cur++) { + double anchorageX = boundsX + getXCoord(cur, bounds); + double anchorageY = boundsY + getYCoord(cur, bounds); + double curHypo = hypot(indicatorX - anchorageX, indicatorY - anchorageY); + if(curHypo < hypo) { + hypo = curHypo; + resultSide = Side.values()[cur]; + } + } + return resultSide; + } + + /** Computes X coordinate. */ + private double getXCoord(int cur, Rectangle2D b) { + return b.getWidth() * XCOORDS[cur]; + } + + /** Computes Y coordinate. */ + private double getYCoord(int cur, Rectangle2D b) { + return b.getHeight() * YCOORDS[cur]; + } + + /** {@inheritDoc} */ + @Override + protected IDragController createAnchorageMoveController(IContentAnchorageMVCBundle anchorage) { + return new ContentAnchorageMoveControllerBase(anchorage) { + @Override + protected Point2D getAnchorageFeedbackLocation(DiagramCoordinate delta) { + Rectangle2D cBounds = getVisual().getModelBounds(); + Rectangle2D aBounds = anchorage.getVisual().getModelBounds(); + double indicatorX = cBounds.getMinX() + aBounds.getMinX() + delta.getX(); + double indicatorY = cBounds.getMinY() + aBounds.getMinY() + delta.getY(); + Side closest = getClosestBorderLocation(indicatorX, indicatorY, cBounds); + int idx = closest.ordinal(); + double x = cBounds.getWidth() * XCOORDS[idx] - aBounds.getWidth() / 2; + double y = cBounds.getHeight() * YCOORDS[idx] - aBounds.getHeight() / 2; + return new Point2D(x, y); + } + + @Override + protected void moveAnchorage(DiagramCoordinate delta) { + Rectangle2D cBounds = getVisual().getModelBounds(); + Rectangle2D aBounds = anchorage.getVisual().getModelBounds(); + double indicatorX = cBounds.getMinX() + aBounds.getMinX() + delta.getX(); + double indicatorY = cBounds.getMinY() + aBounds.getMinY() + delta.getY(); + moveAnchorageToSideOffset(anchorage, + getClosestBorderLocation(indicatorX, indicatorY, cBounds), 0); + } + }; + } + + /** Sets the side and offset of the given anchorage when move operation has completed. */ + protected abstract void moveAnchorageToSideOffset(IContentAnchorageMVCBundle anchorage, + Side side, double offset); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RectangularContentAnchorageMoveController.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RectangularContentAnchorageMoveController.java new file mode 100644 index 0000000000000000000000000000000000000000..c119b011f5a38933cee07b8082fcd44c6c3b0037 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RectangularContentAnchorageMoveController.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.rectangular; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular.RectangularBorderLocation.getClosestLocationOnBounds; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.ContentAnchorageMoveControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular.RectangularBorderLocation; + +import javafx.geometry.Dimension2D; +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; + +/** + * Default implementation for moving content anchorages on the border of a rectangular content + * model element visual. + */ +public class RectangularContentAnchorageMoveController extends ContentAnchorageMoveControllerBase { + /** The controller of the rectangular content visual. */ + private final RectangularResizableContentControllerBase contentController; + + /** Constructor. */ + public RectangularContentAnchorageMoveController( + RectangularResizableContentControllerBase controller, + IContentAnchorageMVCBundle anchorage) { + super(anchorage); + this.contentController = controller; + } + + /** Returns the location clamped to the border of the parent visual. */ + private RectangularBorderLocation getLocationOnBorder(IContentAnchorageMVCBundle anchorage, + Point2D delta) { + Rectangle2D contentBounds = contentController.getVisual().getModelBounds(); + Rectangle2D anchorageBounds = anchorage.getVisual().getModelBounds(); + Dimension2D anchorageSize = + new Dimension2D(anchorageBounds.getWidth(), anchorageBounds.getHeight()); + Dimension2D borderSnap = overrideBorderSnap(anchorageSize); + DiagramCoordinate p = new DiagramCoordinate(anchorageBounds.getMinX() + delta.getX(), + anchorageBounds.getMinY() + delta.getY()); + return getClosestLocationOnBounds(p, contentBounds, anchorageSize, borderSnap); + } + + /** Allows sub-classes to override the default span calculation. */ + protected Dimension2D overrideBorderSnap(Dimension2D anchorageBounds) { + return anchorageBounds; + } + + /** {@inheritDoc} */ + @Override + protected Point2D getAnchorageFeedbackLocation(DiagramCoordinate delta) { + RectangularBorderLocation rbl = getLocationOnBorder(anchorage, delta); + return rbl.getLocation(); + } + + /** {@inheritDoc} */ + @Override + protected void moveAnchorage(DiagramCoordinate delta) { + RectangularBorderLocation rbl = getLocationOnBorder(anchorage, delta); + contentController.moveAnchorageToSideOffset(anchorage, rbl.getSide(), rbl.getOffset()); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RectangularResizableContentControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RectangularResizableContentControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..262b083e4e0ce842b0bb8fbb594585e9f0d2f678 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RectangularResizableContentControllerBase.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.rectangular; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.AnchorageContentControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; + +import javafx.geometry.Side; + +/** Base class for controllers with content visuals represented by rectangles. */ +public abstract class RectangularResizableContentControllerBase + extends AnchorageContentControllerBase { + /** Constructor. */ + public RectangularResizableContentControllerBase(IContentMVCBundle mvcb) { + super(mvcb); + } + + /** {@inheritDoc} */ + @Override + protected IDragController createAnchorageMoveController(IContentAnchorageMVCBundle anchorage) { + return new RectangularContentAnchorageMoveController(this, anchorage); + } + + /** Sets the side and offset of the given anchorage when move operation has completed. */ + protected abstract void moveAnchorageToSideOffset(IContentAnchorageMVCBundle anchorage, + Side side, double offset); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RhomboidContentAnchorageMoveController.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RhomboidContentAnchorageMoveController.java new file mode 100644 index 0000000000000000000000000000000000000000..fbd40ce483f99c18cfa94076374f572bb72c2646 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RhomboidContentAnchorageMoveController.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.rectangular; + +import static java.lang.Math.hypot; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.ContentAnchorageMoveControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular.RectangularBorderLocation; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular.RhomboidContentVisualBase; + +import javafx.geometry.Dimension2D; +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.geometry.Side; + +/** + * Default implementation for moving content anchorages on the border of a rhomboid content + * model element visual. + */ +public class RhomboidContentAnchorageMoveController extends ContentAnchorageMoveControllerBase { + /** The controller of the rhomboid content visual. */ + private final RhomboidResizableContentControllerBase contentController; + + /** Constructor. */ + public RhomboidContentAnchorageMoveController(RhomboidResizableContentControllerBase controller, + IContentAnchorageMVCBundle anchorage) { + super(anchorage); + this.contentController = controller; + if(!(contentController.getVisual() instanceof RhomboidContentVisualBase)) { + throw new IllegalArgumentException("Expected RhomboidContentVisualBase."); + } + } + + /** Returns the location clamped to the border of the parent visual. */ + private Pair<Side, Double> getLocationOnBorder(IContentAnchorageMVCBundle anchorage, + Point2D delta) { + IContentAnchorageVisual anchorageVisual = anchorage.getVisual(); + Rectangle2D anchorageBounds = anchorageVisual.getModelBounds(); + double awidth = anchorageBounds.getWidth(); + double aheight = anchorageBounds.getHeight(); + + IVisual contentVisual = contentController.getVisual(); + Rectangle2D contentBounds = contentVisual.getModelBounds(); + double rhomboidOffset = ((RhomboidContentVisualBase)contentVisual).getOffset(); + + double topX = contentBounds.getMinX(); + double indicatorX = topX + rhomboidOffset + delta.getX() + awidth / 2; + double widthLessOffset = contentBounds.getWidth() - rhomboidOffset; + double topY = contentBounds.getMinY(); + double indicatorY = topY + delta.getY() + aheight / 2; + double botY = contentBounds.getMaxY(); + + Side resultSide = Side.TOP; + double resultOffset = rhomboidOffset; + double hypo = Double.MAX_VALUE; // hypot(indicatorX - topX, indicatorY - topY); + + // search TOP and BOTTOM locations + for(double fx = 0; fx <= 1; fx += 0.25) { + double tx = topX + rhomboidOffset + fx * widthLessOffset; + double topHypo = hypot(indicatorX - tx, indicatorY - topY); + if(topHypo < hypo) { + hypo = topHypo; + resultOffset = tx - topX; + resultSide = Side.TOP; + } + double bx = topX + fx * widthLessOffset; + double botHypo = hypot(indicatorX - bx, indicatorY - botY); + if(botHypo < hypo) { + hypo = botHypo; + resultOffset = bx - topX; + resultSide = Side.BOTTOM; + } + } + + double height2 = contentBounds.getHeight() / 2; + double halfHeightY = topY + height2; + // search LEFT center + double lx = topX + rhomboidOffset / 2; + double leftHypo = hypot(indicatorX - lx, indicatorY - halfHeightY); + if(leftHypo < hypo) { + hypo = leftHypo; + resultOffset = height2; + resultSide = Side.LEFT; + } + // search RIGHT center + double rx = topX + contentBounds.getWidth() - rhomboidOffset / 2; + double rightHypo = hypot(indicatorX - rx, indicatorY - halfHeightY); + if(rightHypo < hypo) { + resultOffset = height2; + resultSide = Side.RIGHT; + } + // return closest test point + return new ImmutablePair<Side, Double>(resultSide, resultOffset - awidth / 2); + } + + /** Allows sub-classes to override the default span calculation. */ + protected Dimension2D overrideBorderSnap(Dimension2D anchorageBounds) { + return anchorageBounds; + } + + /** {@inheritDoc} */ + @Override + protected Point2D getAnchorageFeedbackLocation(DiagramCoordinate delta) { + RhomboidContentVisualBase contentVisual = + (RhomboidContentVisualBase)contentController.getVisual(); + Rectangle2D contentBounds = contentVisual.getModelBounds(); + Rectangle2D anchorageBounds = anchorage.getVisual().getModelBounds(); + Dimension2D anchorageSize = + new Dimension2D(anchorageBounds.getWidth(), anchorageBounds.getHeight()); + double rhomboidOffset = contentVisual.getOffset(); + + Pair<Side, Double> lob = getLocationOnBorder(anchorage, delta); + Side side = lob.getLeft(); + if(side == Side.LEFT) { + double lx = rhomboidOffset / 2 - anchorageSize.getWidth() / 2; + double ly = (contentBounds.getHeight() - anchorageSize.getHeight()) / 2; + return new DiagramCoordinate(lx, ly); + } else if(side == Side.RIGHT) { + double lx = contentBounds.getWidth() - (rhomboidOffset + anchorageSize.getWidth()) / 2; + double ly = (contentBounds.getHeight() - anchorageSize.getHeight()) / 2; + return new DiagramCoordinate(lx, ly); + } + RectangularBorderLocation rbl = + new RectangularBorderLocation(side, lob.getRight(), contentBounds, anchorageSize); + return rbl.getLocation(); + } + + /** {@inheritDoc} */ + @Override + protected void moveAnchorage(DiagramCoordinate delta) { + Pair<Side, Double> lob = getLocationOnBorder(anchorage, delta); + contentController.moveAnchorageToSideOffset(anchorage, lob.getLeft(), lob.getRight()); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RhomboidResizableContentControllerBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RhomboidResizableContentControllerBase.java new file mode 100644 index 0000000000000000000000000000000000000000..863b9eae49bad18f71b20c8cde4b0f350348aa27 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/controller/rectangular/RhomboidResizableContentControllerBase.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.controller.rectangular; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IDragController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.base.AnchorageContentControllerBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; + +import javafx.geometry.Side; + +/** Base class for controllers with content visuals represented by rhomboids. */ +public abstract class RhomboidResizableContentControllerBase + extends AnchorageContentControllerBase { + /** Constructor. */ + public RhomboidResizableContentControllerBase(IContentMVCBundle mvcb) { + super(mvcb); + } + + /** {@inheritDoc} */ + @Override + protected IDragController createAnchorageMoveController(IContentAnchorageMVCBundle anchorage) { + return new RhomboidContentAnchorageMoveController(this, anchorage); + } + + /** Sets the side and offset of the given anchorage when move operation has completed. */ + protected abstract void moveAnchorageToSideOffset(IContentAnchorageMVCBundle anchorage, + Side side, double offset); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..64f28f835bfdcd13cc22512f260cf8d9d693a7ca --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/.ratings @@ -0,0 +1,5 @@ +IModelChangeListener.java 241a583a3517ad504ad0a2d5a8f4d98afee8fd8b YELLOW +IModelChangeProvider.java bc7622c4af211e3ee470d12b80d04a92880f0d38 YELLOW +IModelFactory.java 3b6a1cb2779af0215a2637c40428b42600ef4ffb YELLOW +ModelChangeProviderBase.java eb5ad0363e30f727a70c42edb3d781acb6e6add6 YELLOW +ModelFactoryBase.java 6d0e1e5658c592f3f98d3bcf68bdbb54a506c489 YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/IModelChangeListener.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/IModelChangeListener.java new file mode 100644 index 0000000000000000000000000000000000000000..241a583a3517ad504ad0a2d5a8f4d98afee8fd8b --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/IModelChangeListener.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.model; + +/** Functional interface for listeners of model changes. */ +@FunctionalInterface +public interface IModelChangeListener { + /** Called when the change occurred. */ + public void changed(Object element); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/IModelChangeProvider.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/IModelChangeProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..bc7622c4af211e3ee470d12b80d04a92880f0d38 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/IModelChangeProvider.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.model; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.MVCBundleBase; + +/** Interface for classes providing model changes to the {@link MVCBundleBase}s. */ +public interface IModelChangeProvider { + /** Adds the given listener. */ + public void addListener(IModelChangeListener l); + + /** Removes the given listener. */ + public void removeListener(IModelChangeListener l); + + /** Fires the change event to all listeners. */ + public void fireUpdateComplete(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/IModelFactory.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/IModelFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3b6a1cb2779af0215a2637c40428b42600ef4ffb --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/IModelFactory.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.model; + +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; + +/** Factory class for providing model data to the {@link DiagramViewer}. */ +public interface IModelFactory { + /** The root model element, which is presented as the background of the diagram. */ + Object getRootModel(); + + /** + * The content model elements, which are represented by their visuals. Usually, the parent model + * element of these is the root element. + */ + List<?> getContentModels(); + + /** + * The anchorage model elements of the root element, which are presented as freely positioned + * visuals. The parent of these elements is usually the root element. + */ + List<?> getDiagramAnchorageModels(); + + /** + * The anchorage model elements of the given content model elements, which are presented as + * visuals sticking to their content parent visual's border. + */ + List<?> getContentAnchorageModels(Object parent); + + /** + * The link model elements of the root element, which connect between interface model + * elements of both the root and/or content model elements. + */ + List<?> getLinkModels(); + + /** + * Returns the model element, which is the start of the given link element. The returned + * element must be contained in either {@link #getDiagramAnchorageModels()} or + * {@link #getContentAnchorageModels(Object)}. + */ + Object getLinkStart(Object link); + + /** + * Returns the model element, which is the end of the given link element. The returned + * element must be contained in either {@link #getDiagramAnchorageModels()} or + * {@link #getContentAnchorageModels(Object)}. + */ + Object getLinkEnd(Object link); + + /** + * Returns the model element, which is considered the parent content model of the given model + * element. + */ + Object getParent(Object element); + + /** Update the model factory's auxiliary data before calling any of the other methods. */ + void update(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/ModelChangeProviderBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/ModelChangeProviderBase.java new file mode 100644 index 0000000000000000000000000000000000000000..eb5ad0363e30f727a70c42edb3d781acb6e6add6 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/ModelChangeProviderBase.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.model; + +import java.util.ArrayList; +import java.util.List; + +/** Base class for model change listeners. */ +public abstract class ModelChangeProviderBase implements IModelChangeProvider { + /** The registered listeners. */ + private final List<IModelChangeListener> listeners = new ArrayList<>(); + + /** {@inheritDoc} */ + @Override + public void addListener(IModelChangeListener l) { + if(!listeners.contains(l)) { + listeners.add(l); + connectToModel(); + } + } + + /** Called when the first listener is added. */ + protected void connectToModel() { + // does nothing + } + + /** {@inheritDoc} */ + @Override + public void removeListener(IModelChangeListener l) { + listeners.remove(l); + if(listeners.isEmpty()) { + disconnectFromModel(); + } + } + + /** Called when the last listener is removed. */ + protected void disconnectFromModel() { + // does nothing + } + + /** {@inheritDoc} */ + @Override + public void fireUpdateComplete() { + // use a copy to avoid concurrent modification exception + ArrayList<IModelChangeListener> iterationSafeCopy = new ArrayList<>(listeners); + for(IModelChangeListener l : iterationSafeCopy) { + l.changed(this); + } + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/ModelFactoryBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/ModelFactoryBase.java new file mode 100644 index 0000000000000000000000000000000000000000..6d0e1e5658c592f3f98d3bcf68bdbb54a506c489 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/ModelFactoryBase.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.model; + +import static java.util.Collections.emptyList; + +import java.util.List; + +/** Base implementation for {@link IModelFactory}. */ +public abstract class ModelFactoryBase implements IModelFactory { + /** {@inheritDoc} */ + @Override + public List<?> getContentModels() { + return emptyList(); + } + + /** {@inheritDoc} */ + @Override + public List<?> getDiagramAnchorageModels() { + return emptyList(); + } + + /** {@inheritDoc} */ + @Override + public List<?> getContentAnchorageModels(Object parent) { + return emptyList(); + } + + /** {@inheritDoc} */ + @Override + public List<?> getLinkModels() { + return emptyList(); + } + + /** {@inheritDoc} */ + @Override + public Object getLinkStart(Object link) { + return null; + } + + /** {@inheritDoc} */ + @Override + public Object getLinkEnd(Object link) { + return null; + } + + /** {@inheritDoc} */ + @Override + public Object getParent(Object element) { + return null; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..e16ac9b565554bdc3997e7fda8cf824f4f77e9aa --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/.ratings @@ -0,0 +1,6 @@ +IAngleLayout.java 4993d53870c2a71dc63e8a99ceb47df6cfec3801 YELLOW +ILayout.java e6fd2a1ac953a18c8ea8951e594e44a1c0fc8d92 YELLOW +IOffsetLayout.java 440551f07a2af2ceb000b14177cc2351cb1b7f52 YELLOW +IPointsLayout.java be1c706ae6e782d6ac960e02e6855f8d2118914b YELLOW +ISideLayout.java 20b14640a7424a013338e6683b9f4cf6e62f01e1 YELLOW +IXYLayout.java e3e007db33846621bf6844621360b5881f7f6023 YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IAngleLayout.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IAngleLayout.java new file mode 100644 index 0000000000000000000000000000000000000000..4993d53870c2a71dc63e8a99ceb47df6cfec3801 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IAngleLayout.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout; + +/** {@link ILayout} defined by an angle in degree. */ +public interface IAngleLayout extends ILayout { + /** Returns the angle in degree. */ + double getAngleInDegree(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/ILayout.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/ILayout.java new file mode 100644 index 0000000000000000000000000000000000000000..e6fd2a1ac953a18c8ea8951e594e44a1c0fc8d92 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/ILayout.java @@ -0,0 +1,15 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout; + +/** Base interface for all pieces of layout information of model elements. */ +public interface ILayout { + // nothing to do here +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IOffsetLayout.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IOffsetLayout.java new file mode 100644 index 0000000000000000000000000000000000000000..440551f07a2af2ceb000b14177cc2351cb1b7f52 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IOffsetLayout.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout; + +/** {@link ILayout} defined by an offset. */ +public interface IOffsetLayout extends ILayout { + /** Returns the offset. */ + double getOffset(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IPointsLayout.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IPointsLayout.java new file mode 100644 index 0000000000000000000000000000000000000000..be1c706ae6e782d6ac960e02e6855f8d2118914b --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IPointsLayout.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout; + +/** {@link ILayout} defined by a sequence of X and Y coordinates. */ +public interface IPointsLayout extends ILayout { + /** Returns the number of points in the layout data. */ + int getNumberOfPoints(); + + /** + * Returns the X coordinate of the point at the given index [0 <= index < getNumberOfPoints()]. + */ + double getX(int index); + + /** + * Returns the Y coordinate of the point at the given index [0 <= index < getNumberOfPoints()]. + */ + double getY(int index); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/ISideLayout.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/ISideLayout.java new file mode 100644 index 0000000000000000000000000000000000000000..20b14640a7424a013338e6683b9f4cf6e62f01e1 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/ISideLayout.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout; + +import javafx.geometry.Side; + +/** {@link ILayout} defined by a {@link Side}. */ +public interface ISideLayout extends ILayout { + /** Returns the side. */ + Side getSide(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IXYLayout.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IXYLayout.java new file mode 100644 index 0000000000000000000000000000000000000000..e3e007db33846621bf6844621360b5881f7f6023 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/model/layout/IXYLayout.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout; + +/** {@link ILayout} defined by X and Y. */ +public interface IXYLayout extends ILayout { + /** Returns the X coordinate. */ + double getX(); + + /** Returns the Y coordinate. */ + double getY(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..2cbebcd4f0eb0c2a87624fda33091a00b464d1d6 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/.ratings @@ -0,0 +1,10 @@ +IAnchorageMVCBundle.java 17671d1ef6ecc4d1b4b98cf9d83016b31455346d YELLOW +IContentAnchorageMVCBundle.java c3bd9eb5266d0f87b16cdd16aeaadbfa6eeb49de YELLOW +IContentMVCBundle.java f84f8e59af85d022c2019d271d957821c5c55d3f YELLOW +IContentMVCBundleWithParent.java 890ffcacb493fda5ead5b7f74dc7ae1637077526 YELLOW +IDiagramAnchorageMVCBundle.java c49ef7ca7457ea9d4557251263779b403f79e69c YELLOW +IDiagramMVCBundle.java 354facfc00887726a6d7553d40824b7171865330 YELLOW +ILinkMVCBundle.java a2b1527f98ed642def2346190f6a71016ca674af YELLOW +IMVCBundle.java 19a6cc6bc96c42dd98dca24288a050ff0ba04734 YELLOW +IMVCBundlePart.java 6ddc90167a6ee7410876150e1785840f6ad32912 YELLOW +MVCBundleTag.java cf428833f24caccd74945119cf906b19daa3d63b YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IAnchorageMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IAnchorageMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..17671d1ef6ecc4d1b4b98cf9d83016b31455346d --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IAnchorageMVCBundle.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc; + +import java.util.List; + +/** + * Interface for {@link IMVCBundle}s, which represent anchorages and thus can have directed links + * attached to them. + */ +public interface IAnchorageMVCBundle extends IMVCBundle { + /** Returns the {@link IMVCBundle} this anchorage is attached to. */ + IMVCBundle getAttachedTo(); + + /** Returns the list of incoming links. */ + List<ILinkMVCBundle> getIncomingLinks(); + + /** Returns the list of outgoing links. */ + List<ILinkMVCBundle> getOutgoingLinks(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IContentAnchorageMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IContentAnchorageMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..c3bd9eb5266d0f87b16cdd16aeaadbfa6eeb49de --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IContentAnchorageMVCBundle.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; + +/** Interface for anchorage {@link IMVCBundle}s attached to {@link IContentMVCBundle}s. */ +public interface IContentAnchorageMVCBundle extends IAnchorageMVCBundle { + /** Returns the {@link IContentMVCBundle} this anchorage is attached to. */ + @Override + IContentMVCBundle getAttachedTo(); + + /** {@inheritDoc} */ + @Override + IContentAnchorageVisual getVisual(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IContentMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IContentMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..f84f8e59af85d022c2019d271d957821c5c55d3f --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IContentMVCBundle.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc; + +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentVisual; + +/** Interface for content elements of the diagram viewer. */ +public interface IContentMVCBundle extends IMVCBundle { + /** Returns the {@link IDiagramMVCBundle} this content is attached to. */ + IDiagramMVCBundle getDiagramBundle(); + + /** Returns the list of attached anchorages. */ + List<IContentAnchorageMVCBundle> getAnchorages(); + + /** Returns the children of this content bundle. */ + List<IContentMVCBundleWithParent> getChildren(); + + /** {@inheritDoc} */ + @Override + IContentVisual getVisual(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IContentMVCBundleWithParent.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IContentMVCBundleWithParent.java new file mode 100644 index 0000000000000000000000000000000000000000..890ffcacb493fda5ead5b7f74dc7ae1637077526 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IContentMVCBundleWithParent.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc; + +/** + * Interface for content elements of diagrams, which have a reference to another + * {@link IContentMVCBundle}. + */ +public interface IContentMVCBundleWithParent extends IContentMVCBundle { + /** Returns the parent {@link IContentMVCBundle}. */ + IContentMVCBundle getParentBundle(); + + /** Sets the parent of this {@link IContentMVCBundleWithParent}. */ + void setParentBundle(IContentMVCBundle parent); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IDiagramAnchorageMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IDiagramAnchorageMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..c49ef7ca7457ea9d4557251263779b403f79e69c --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IDiagramAnchorageMVCBundle.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IDiagramAnchorageVisual; + +/** Interface for {@link IAnchorageMVCBundle}s attached to the diagram background. */ +public interface IDiagramAnchorageMVCBundle extends IAnchorageMVCBundle { + /** Returns the {@link IDiagramMVCBundle} this anchorage is attached to. */ + @Override + IDiagramMVCBundle getAttachedTo(); + + /** {@inheritDoc} */ + @Override + IDiagramAnchorageVisual getVisual(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IDiagramMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IDiagramMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..354facfc00887726a6d7553d40824b7171865330 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IDiagramMVCBundle.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc; + +import java.util.List; + +/** Interface for the {@link IMVCBundle} representing the diagram (i.e. its background). */ +public interface IDiagramMVCBundle extends IMVCBundle { + /** Returns the list of content elements. */ + List<IContentMVCBundle> getContents(); + + /** Returns the list of links. */ + List<ILinkMVCBundle> getLinks(); + + /** Returns the list of attached diagram anchorages. */ + List<IDiagramAnchorageMVCBundle> getAnchorages(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/ILinkMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/ILinkMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..a2b1527f98ed642def2346190f6a71016ca674af --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/ILinkMVCBundle.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.ILinkVisual; + +/** Interface for directed links anchored to {@link IAnchorageMVCBundle}s. */ +public interface ILinkMVCBundle extends IMVCBundle { + /** Returns the parent {@link IDiagramMVCBundle}. */ + IDiagramMVCBundle getDiagramMVCBundle(); + + /** Returns the start anchorage of this link. */ + IAnchorageMVCBundle getStartAnchorage(); + + /** Returns the end anchorage of this link. */ + IAnchorageMVCBundle getEndAnchorage(); + + /** Returns the visual. */ + @Override + ILinkVisual getVisual(); + + /** Updates the {@link ILinkMVCBundle}'s start and end anchorages. */ + void updateLinkBundle(IAnchorageMVCBundle start, IAnchorageMVCBundle end); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..19a6cc6bc96c42dd98dca24288a050ff0ba04734 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IMVCBundle.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; + +/** Interface for bundles composed of a model, a view, and a controller. */ +public interface IMVCBundle { + /** Returns the model. */ + Object getModel(); + + /** Returns the visual. */ + IVisual getVisual(); + + /** Sets the visual. */ + void setVisual(IVisual v); + + /** Returns the controller. */ + IController getController(); + + /** Sets the controller. */ + void setController(IController c); + + /** Returns the diagram viewer. */ + DiagramViewer getViewer(); + + /** Returns the feedback change (temporary location or size changes during user gestures). */ + FeedbackChange getFeedbackChange(); + + /** Sets the feedback change (temporary location or size changes during user gestures). */ + void setFeedbackChange(FeedbackChange fc); + + /** Attaches listeners to the visual and the underlying model. */ + void attach(); + + /** Detaches listeners from the visual and from the underlying model. */ + void detach(); + + /** Adds the given tag to this bundle. */ + void addTag(MVCBundleTag tag); + + /** Returns the tag of this bundle matching the given tags identifier. */ + MVCBundleTag getTag(MVCBundleTag tag); + + /** Returns whether this bundle is tagged with the given tag (using equals()). */ + boolean hasTag(MVCBundleTag tag); + + /** Removes the tag from this bundle. */ + void removeTag(MVCBundleTag tag); + + /** Removes the bundle from any other bundle it is related to. */ + void remove(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IMVCBundlePart.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IMVCBundlePart.java new file mode 100644 index 0000000000000000000000000000000000000000..6ddc90167a6ee7410876150e1785840f6ad32912 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/IMVCBundlePart.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.MVCBundleBase; + +/** + * Interface for classes that take part in the model-visual-controller setup and reference an + * {@link IMVCBundle}. + */ +public interface IMVCBundlePart { + /** Returns the {@link MVCBundleBase}. */ + IMVCBundle getMVCBundle(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/MVCBundleTag.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/MVCBundleTag.java new file mode 100644 index 0000000000000000000000000000000000000000..cf428833f24caccd74945119cf906b19daa3d63b --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/MVCBundleTag.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc; + +/** Class encapsulating a String tag and, optionally, a user object. */ +public final class MVCBundleTag { + /** The tag identifier. */ + private final String tagIdentifier; + /** The tag user object. */ + private final Object userObject; + + /** Constructor. */ + private MVCBundleTag(String id, Object userObject) { + this.tagIdentifier = id; + this.userObject = userObject; + } + + /** Constructor. */ + private MVCBundleTag(String id) { + this(id, null); + } + + /** Constructor. */ + public MVCBundleTag(Class<?> clazz, String id) { + this(clazz.getName() + "_" + id); + } + + /** Returns the tag identifier. */ + public String getTagIdentifier() { + return tagIdentifier; + } + + /** Returns the user object. */ + public Object getUserObject() { + return userObject; + } + + /** Creates a tag object with the same identifier and given user object. */ + public MVCBundleTag getCopy(Object userObject) { + return new MVCBundleTag(tagIdentifier, userObject); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..75dd39150629f014e42f0ffb5515d36228037152 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/.ratings @@ -0,0 +1,8 @@ +AnchorageMVCBundleBase.java 63dc967da3342d402603adcc005344ce89386ea8 YELLOW +ContentAnchorageMVCBundle.java 1a517903c6400203cb7850502671e01d715f2453 YELLOW +ContentMVCBundle.java ee8a2c7780b64d61cf3e196437ce5ab07ad29e7c YELLOW +DiagramAnchorageMVCBundle.java b0f12259bbb8ccbf68b0774eb76c060188f4fea2 YELLOW +DiagramMVCBundle.java 46860e820c67f985cd75704b9084948df1939ee8 YELLOW +LinkMVCBundle.java b8246cd0e6f6ff53b377a9a0c4d1c8e4aa6371c4 YELLOW +MVCBundleBase.java 0a1b561e6ed5a5b4a3d2fdf52e2e5839c770193b YELLOW +MVCBundlePartBase.java 9e56b3799f14fe63649a09130edbdef083b87fdc YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/AnchorageMVCBundleBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/AnchorageMVCBundleBase.java new file mode 100644 index 0000000000000000000000000000000000000000..63dc967da3342d402603adcc005344ce89386ea8 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/AnchorageMVCBundleBase.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl; + +import static java.util.Objects.requireNonNull; + +import java.util.LinkedList; +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; + +/** Base class for {@link IAnchorageMVCBundle}s. */ +abstract class AnchorageMVCBundleBase extends MVCBundleBase implements IAnchorageMVCBundle { + /** The parent bundle this bundle is attached to. */ + private IMVCBundle attachedTo; + /** The list of incoming links. */ + private final List<ILinkMVCBundle> incomingLinks = new LinkedList<>(); + /** The list of outgoing links. */ + private final List<ILinkMVCBundle> outgoingLinks = new LinkedList<>(); + + /** Constructor. */ + public AnchorageMVCBundleBase(Object m, IMVCBundle attachedTo, DiagramViewer v) { + super(m, v); + this.attachedTo = requireNonNull(attachedTo); + } + + /** {@inheritDoc} */ + @Override + public IMVCBundle getAttachedTo() { + return attachedTo; + } + + /** {@inheritDoc} */ + @Override + public List<ILinkMVCBundle> getIncomingLinks() { + return incomingLinks; + } + + /** {@inheritDoc} */ + @Override + public List<ILinkMVCBundle> getOutgoingLinks() { + return outgoingLinks; + } + + /** {@inheritDoc} */ + @Override + public void changed(Object element) { + // Anchorages have no children, so only the properties can have changed. + // Therefore, update just the view and the connected links. + getViewer().updateVisual(this); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/ContentAnchorageMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/ContentAnchorageMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..1a517903c6400203cb7850502671e01d715f2453 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/ContentAnchorageMVCBundle.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; + +/** Implementation of {@link IContentAnchorageMVCBundle}. */ +public final class ContentAnchorageMVCBundle extends AnchorageMVCBundleBase + implements IContentAnchorageMVCBundle { + /** Constructor. */ + public ContentAnchorageMVCBundle(Object m, IContentMVCBundle attachedTo, DiagramViewer v) { + super(m, attachedTo, v); + attachedTo.getAnchorages().add(this); + } + + /** {@inheritDoc} */ + @Override + public void remove() { + getAttachedTo().getAnchorages().remove(this); + } + + /** {@inheritDoc} */ + @Override + public IContentMVCBundle getAttachedTo() { + // wild cast works: see constructor + return (IContentMVCBundle)super.getAttachedTo(); + } + + /** {@inheritDoc} */ + @Override + public IContentAnchorageVisual getVisual() { + return (IContentAnchorageVisual)super.getVisual(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/ContentMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/ContentMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..ee8a2c7780b64d61cf3e196437ce5ab07ad29e7c --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/ContentMVCBundle.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundleWithParent; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentVisual; + +/** Implementation of {@link IContentMVCBundle}. */ +public final class ContentMVCBundle extends MVCBundleBase implements IContentMVCBundleWithParent { + /** The diagram bundle this content is attached to. */ + private final IDiagramMVCBundle diagramBundle; + /** The list of attached anchorages. */ + private final List<IContentAnchorageMVCBundle> anchorages = new ArrayList<>(); + /** The parent bundle. */ + private IContentMVCBundle parent; + /** The child bundles. */ + private final List<IContentMVCBundleWithParent> children = + new LinkedList<IContentMVCBundleWithParent>(); + + /** Constructor. */ + public ContentMVCBundle(Object m, IDiagramMVCBundle diagramBundle, DiagramViewer v) { + super(m, v); + this.diagramBundle = diagramBundle; + } + + /** {@inheritDoc} */ + @Override + public void remove() { + // nothing to do here + } + + /** {@inheritDoc} */ + @Override + public IDiagramMVCBundle getDiagramBundle() { + return diagramBundle; + } + + /** {@inheritDoc} */ + @Override + public List<IContentAnchorageMVCBundle> getAnchorages() { + return anchorages; + } + + /** {@inheritDoc} */ + @Override + public IContentMVCBundle getParentBundle() { + return parent; + } + + /** {@inheritDoc} */ + @Override + public IContentVisual getVisual() { + return (IContentVisual)super.getVisual(); + } + + /** {@inheritDoc} */ + @Override + public void setParentBundle(IContentMVCBundle p) { + if(parent != null) { + parent.getChildren().remove(this); + } + this.parent = p; + if(parent != null) { + parent.getChildren().add(this); + } + } + + /** {@inheritDoc} */ + @Override + public List<IContentMVCBundleWithParent> getChildren() { + return children; + } + + /** {@inheritDoc} */ + @Override + public void changed(Object element) { + // Check if the anchorages have changed. + boolean anchoragesChanged = false; + List<Object> modelAnchorages = new ArrayList<>( + getViewer().getModelFactory().getContentAnchorageModels(getModel())); + for(IContentAnchorageMVCBundle a : getAnchorages()) { + anchoragesChanged |= !modelAnchorages.remove(a.getModel()); + } + anchoragesChanged |= modelAnchorages.size() > 0; + + // If the anchorages have changed, invoke the generic case from the superclass + if(anchoragesChanged) { + super.changed(element); + return; + } + + // Otherwise, only the properties of the element can have changed. + // Update the view of the element, its anchorages and the connected links. + getViewer().updateVisual(this); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/DiagramAnchorageMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/DiagramAnchorageMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..b0f12259bbb8ccbf68b0774eb76c060188f4fea2 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/DiagramAnchorageMVCBundle.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IDiagramAnchorageVisual; + +/** Implementation of {@link IContentAnchorageMVCBundle}. */ +public final class DiagramAnchorageMVCBundle extends AnchorageMVCBundleBase + implements IDiagramAnchorageMVCBundle { + + /** Constructor. */ + public DiagramAnchorageMVCBundle(Object m, IDiagramMVCBundle attachedTo, DiagramViewer v) { + super(m, attachedTo, v); + attachedTo.getAnchorages().add(this); + } + + /** {@inheritDoc} */ + @Override + public void remove() { + getAttachedTo().getAnchorages().remove(this); + } + + /** {@inheritDoc} */ + @Override + public IDiagramMVCBundle getAttachedTo() { + // wild cast works: see constructor + return (IDiagramMVCBundle)super.getAttachedTo(); + } + + /** {@inheritDoc} */ + @Override + public IDiagramAnchorageVisual getVisual() { + return (IDiagramAnchorageVisual)super.getVisual(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/DiagramMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/DiagramMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..46860e820c67f985cd75704b9084948df1939ee8 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/DiagramMVCBundle.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; + +/** Implementation of {@link IDiagramMVCBundle}. */ +public final class DiagramMVCBundle extends MVCBundleBase implements IDiagramMVCBundle { + /** The list of content elements. */ + private final List<IContentMVCBundle> contents = new ArrayList<>(); + /** The list of link elements. */ + private final List<ILinkMVCBundle> links = new ArrayList<>(); + /** The list of attached anchorages. */ + private final List<IDiagramAnchorageMVCBundle> anchorages = new ArrayList<>(); + + /** Constructor. */ + public DiagramMVCBundle(Object m, DiagramViewer v) { + super(m, v); + } + + /** {@inheritDoc} */ + @Override + public void remove() { + // nothing to do here + } + + /** {@inheritDoc} */ + @Override + public List<IContentMVCBundle> getContents() { + return contents; + } + + /** {@inheritDoc} */ + @Override + public List<ILinkMVCBundle> getLinks() { + return links; + } + + /** {@inheritDoc} */ + @Override + public List<IDiagramAnchorageMVCBundle> getAnchorages() { + return anchorages; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/LinkMVCBundle.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/LinkMVCBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..b8246cd0e6f6ff53b377a9a0c4d1c8e4aa6371c4 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/LinkMVCBundle.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.ILinkVisual; + +/** + * {@link MVCBundleBase} for connections providing access to both end-point {@link MVCBundleBase}s. + */ +public class LinkMVCBundle extends MVCBundleBase implements ILinkMVCBundle { + /** The {@link IDiagramMVCBundle}, which is the parent of all links. */ + private final IDiagramMVCBundle parent; + /** The start-point {@link IAnchorageMVCBundle}. */ + private IAnchorageMVCBundle start; + /** The end-point {@link IAnchorageMVCBundle}. */ + private IAnchorageMVCBundle end; + + /** Constructor. */ + public LinkMVCBundle(Object m, IDiagramMVCBundle parent, IAnchorageMVCBundle start, + IAnchorageMVCBundle end, DiagramViewer v) { + super(m, v); + this.parent = parent; + parent.getLinks().add(this); + this.start = start; + start.getOutgoingLinks().add(this); + this.end = end; + end.getIncomingLinks().add(this); + } + + /** {@inheritDoc} */ + @Override + public final void remove() { + parent.getLinks().remove(this); + start.getOutgoingLinks().remove(this); + end.getIncomingLinks().remove(this); + } + + /** {@inheritDoc} */ + @Override + public IAnchorageMVCBundle getStartAnchorage() { + return start; + } + + /** Sets the start bundle. */ + public void setStartAnchorage(IAnchorageMVCBundle start) { + this.start = start; + } + + /** {@inheritDoc} */ + @Override + public IAnchorageMVCBundle getEndAnchorage() { + return end; + } + + /** Sets the end bundle. */ + public void setEndAnchorage(IAnchorageMVCBundle end) { + this.end = end; + } + + /** {@inheritDoc} */ + @Override + public IDiagramMVCBundle getDiagramMVCBundle() { + return parent; + } + + /** {@inheritDoc} */ + @Override + public ILinkVisual getVisual() { + return (ILinkVisual)super.getVisual(); + } + + /** {@inheritDoc} */ + @Override + public final void updateLinkBundle(IAnchorageMVCBundle newStart, IAnchorageMVCBundle newEnd) { + if(start != null && start != newStart) { + start.getOutgoingLinks().remove(this); + start = newStart; + start.getOutgoingLinks().add(this); + } + if(end != null && end != newEnd) { + end.getIncomingLinks().remove(this); + end = newEnd; + end.getIncomingLinks().add(this); + } + } + + /** {@inheritDoc} */ + @Override + public void changed(Object element) { + getViewer().updateVisual(this); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/MVCBundleBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/MVCBundleBase.java new file mode 100644 index 0000000000000000000000000000000000000000..0a1b561e6ed5a5b4a3d2fdf52e2e5839c770193b --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/MVCBundleBase.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl; + +import static java.util.Objects.requireNonNull; + +import java.util.HashMap; +import java.util.Map; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelChangeListener; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelChangeProvider; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.MVCBundleTag; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; + +/** + * This class bundles all objects used by the model-view-controller paradigm. + * <p> + * It is also responsible for propagating model changes to changes of the visual. + * Model changes may be received directly from the model element (if it implements + * {@link IModelChangeProvider}) or from the assigned controller (if that one + * implements {@link IModelChangeProvider}). + */ +public abstract class MVCBundleBase implements IModelChangeListener, IMVCBundle { + /** The model. */ + private final Object model; + /** The diagram viewer to be used for selection handling. */ + private final DiagramViewer viewer; + /** The visual. */ + private IVisual visual; + /** The controller. */ + private IController controller; + /** The current feedback change applied to this bundle's visual. */ + private FeedbackChange feedbackChange = new FeedbackChange(); + /** The set of {@link MVCBundleTag}s. */ + private final Map<String, MVCBundleTag> tagMap = new HashMap<>(); + + /** Constructor. */ + public MVCBundleBase(Object m, DiagramViewer v) { + model = requireNonNull(m, "Model must not be null."); + viewer = requireNonNull(v, "Viewer must not be null."); + } + + /** {@inheritDoc} */ + @Override + public final void setVisual(IVisual visual) { + this.visual = requireNonNull(visual, "Visual must not be null."); + } + + /** {@inheritDoc} */ + @Override + public void attach() { + if(model instanceof IModelChangeProvider) { + IModelChangeProvider mcp = (IModelChangeProvider)model; + mcp.addListener(this); + } + if(controller instanceof IModelChangeProvider) { + IModelChangeProvider mcp = (IModelChangeProvider)controller; + mcp.addListener(this); + } + IModelChangeProvider adapted = controller.getModelChangeProvider(); + if(adapted != null) { + adapted.addListener(this); + } + } + + /** {@inheritDoc} */ + @Override + public final void detach() { + if(model instanceof IModelChangeProvider) { + IModelChangeProvider mcp = (IModelChangeProvider)model; + mcp.removeListener(this); + } + if(controller instanceof IModelChangeProvider) { + IModelChangeProvider mcp = (IModelChangeProvider)controller; + mcp.removeListener(this); + } + IModelChangeProvider adapted = controller.getModelChangeProvider(); + if(adapted != null) { + adapted.removeListener(this); + } + } + + /** {@inheritDoc} */ + @Override + public final void setController(IController controller) { + this.controller = requireNonNull(controller, "Controller must not be null."); + } + + /** {@inheritDoc} */ + @Override + public Object getModel() { + return model; + } + + /** {@inheritDoc} */ + @Override + public IVisual getVisual() { + return visual; + } + + /** {@inheritDoc} */ + @Override + public IController getController() { + return controller; + } + + /** {@inheritDoc} */ + @Override + public DiagramViewer getViewer() { + return viewer; + } + + /** {@inheritDoc} */ + @Override + public void changed(Object element) { + viewer.updateFromModel(); + } + + /** + * {@inheritDoc} + * <p> + * Updates the visual after setting the feedback change. + */ + @Override + public final void setFeedbackChange(FeedbackChange feedbackChange) { + this.feedbackChange = requireNonNull(feedbackChange); + viewer.updateVisual(this); + } + + /** {@inheritDoc} */ + @Override + public final FeedbackChange getFeedbackChange() { + return feedbackChange; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return getClass().getSimpleName() + " MVC bundle of " + getModel().toString(); + } + + /** {@inheritDoc} */ + @Override + public void addTag(MVCBundleTag tag) { + tagMap.put(tag.getTagIdentifier(), tag); + } + + /** {@inheritDoc} */ + @Override + public MVCBundleTag getTag(MVCBundleTag tag) { + return tagMap.get(tag.getTagIdentifier()); + } + + /** {@inheritDoc} */ + @Override + public boolean hasTag(MVCBundleTag tag) { + if(tag == null) { + return false; + } + return tagMap.get(tag.getTagIdentifier()) != null; + } + + /** {@inheritDoc} */ + @Override + public void removeTag(MVCBundleTag tag) { + tagMap.remove(tag.getTagIdentifier()); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/MVCBundlePartBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/MVCBundlePartBase.java new file mode 100644 index 0000000000000000000000000000000000000000..9e56b3799f14fe63649a09130edbdef083b87fdc --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/mvc/impl/MVCBundlePartBase.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundlePart; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; + +/** Base class implementing {@link IMVCBundlePart}. */ +public class MVCBundlePartBase implements IMVCBundlePart { + /** The {@link MVCBundleBase}. */ + private IMVCBundle mvcb; + + /** Constructor. */ + public MVCBundlePartBase(IMVCBundle mvcb) { + this.mvcb = mvcb; + } + + /** Returns the model object. */ + public final Object getModel() { + return mvcb.getModel(); + } + + /** Returns the visual. */ + public final IVisual getVisual() { + return mvcb.getVisual(); + } + + /** Returns the controller. */ + public final IController getController() { + return mvcb.getController(); + } + + /** Returns the diagram viewer. */ + public final DiagramViewer getViewer() { + return mvcb.getViewer(); + } + + /** Returns the feedback change. */ + public final FeedbackChange getFeedbackChange() { + return mvcb.getFeedbackChange(); + } + + /** {@inheritDoc} */ + @Override + public IMVCBundle getMVCBundle() { + return mvcb; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("MVCB part ").append(getClass().getSimpleName()); + sb.append(" of ").append(mvcb.toString()); + return sb.toString(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..eac3ce8aa6f5915c3c45dd6ddaee84beae2392df --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/.ratings @@ -0,0 +1,7 @@ +IAnchorageVisual.java e742212c132db1e2309650f5d6e40bc79c2ff85a YELLOW +IContentAnchorageVisual.java 3a3ceb675820f151aa678b6caa0f1ae833da1b7f YELLOW +IContentVisual.java 1052ece1f6172799204b1d7524398766c3b7a12e YELLOW +IDiagramAnchorageVisual.java ae0895912fe735147ccd11b7f466a7a5b61c3ca5 YELLOW +ILinkVisual.java 8f9bd6cf7c423a78d67fab0a20ba2dad2dfbd55e YELLOW +IVisual.java 400569502788bf2efff3de84f2c53b65256b3fc4 YELLOW +IVisualFactory.java 39253dc705f7f73ddbb83d050232a19219d54e71 YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IAnchorageVisual.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IAnchorageVisual.java new file mode 100644 index 0000000000000000000000000000000000000000..e742212c132db1e2309650f5d6e40bc79c2ff85a --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IAnchorageVisual.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual; + +import javafx.geometry.Dimension2D; + +/** {@link IVisual} interface for anchorages. */ +public interface IAnchorageVisual extends IVisual { + /** Returns the dimension of the visual. */ + Dimension2D getDimensions(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IContentAnchorageVisual.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IContentAnchorageVisual.java new file mode 100644 index 0000000000000000000000000000000000000000..3a3ceb675820f151aa678b6caa0f1ae833da1b7f --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IContentAnchorageVisual.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.ILayout; + +/** Interface for anchorage visuals which stick to a content node in the diagram. */ +public interface IContentAnchorageVisual extends IAnchorageVisual { + /** + * Returns the layout information for this content anchorage. Callers must provide an actual + * type of the requested layout information. + */ + <T extends ILayout> T getLayout(Class<T> type); + + /** + * Returns whether sibling anchorage visuals (i.e. those attached to the same content visual as + * this anchorage visual) should be updated when this visual is hovered over by the mouse + * pointer. The default is {@code false}. + * + * @see IContentVisual#updateAttachedAnchorageVisualsOnHover() + */ + boolean updateSiblingAnchorageVisualsOnHover(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IContentVisual.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IContentVisual.java new file mode 100644 index 0000000000000000000000000000000000000000..1052ece1f6172799204b1d7524398766c3b7a12e --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IContentVisual.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; + +import javafx.scene.Node; + +/** + * {@link IVisual} for content nodes moving freely on the diagram background. + * Content nodes can normally be resized and have anchorage visuals. + */ +public interface IContentVisual extends IVisual { + /** + * Returns the location on the border of this content visual for the given + * {@link IContentAnchorageVisual}. + */ + DiagramCoordinate getAnchorageLocation(IContentAnchorageVisual visual); + + /** Returns whether the given node is the expand/collapse widget. */ + boolean isExpandCollapseWidget(Node node); + + /** + * Returns whether anchorage visuals attached to this visual should be updated when this content + * visual is hovered over by the mouse pointer. The default is {@code false}. + * + * @see IContentAnchorageVisual#updateSiblingAnchorageVisualsOnHover() + */ + boolean updateAttachedAnchorageVisualsOnHover(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IDiagramAnchorageVisual.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IDiagramAnchorageVisual.java new file mode 100644 index 0000000000000000000000000000000000000000..ae0895912fe735147ccd11b7f466a7a5b61c3ca5 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IDiagramAnchorageVisual.java @@ -0,0 +1,15 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual; + +/** {@link IVisual} for anchorages moving freely on the diagram background. */ +public interface IDiagramAnchorageVisual extends IAnchorageVisual { + // marker interface +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/ILinkVisual.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/ILinkVisual.java new file mode 100644 index 0000000000000000000000000000000000000000..8f9bd6cf7c423a78d67fab0a20ba2dad2dfbd55e --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/ILinkVisual.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; + +import javafx.scene.Node; + +/** + * Interface for visuals encapsulating a {@link Node}, which usually displays a link or connection, + * e.g. a Bezier curve. + */ +public interface ILinkVisual extends IVisual { + /** Bend-point index indicating the start of the link point. */ + public static final int START_OF_LINK_BEND_POINT_INDEX = Integer.MIN_VALUE; + /** Bend-point index indicating the end of the link point. */ + public static final int END_OF_LINK_BEND_POINT_INDEX = Integer.MIN_VALUE + 1; + /** Bend-point index indicating that no bend-point is hit. */ + public static final int NO_BEND_POINT_INDEX = Integer.MIN_VALUE + 2; + /** Index cap for special bend-point indices. */ + public static final int SPECIAL_BEND_POINT_INDEX_CAP = Integer.MIN_VALUE + 3; + + /** Returns the location of the start anchor point. */ + DiagramCoordinate getStartAnchorPoint(); + + /** Returns the location of the end anchor point. */ + DiagramCoordinate getEndAnchorPoint(); + + /** Sets the feedback change for the bend-point at the given index. */ + void setFeedbackChangeForBendPoint(int index, FeedbackChange change); + + /** + * Returns the index of the bend-point represented by the given node or + * {@link #START_OF_LINK_BEND_POINT_INDEX}, if the node represents the start of the link, or + * {@link #END_OF_LINK_BEND_POINT_INDEX}, if the node represents the end of the link, or + * {@link #NO_BEND_POINT_INDEX}, if the node does not represent any bend point. + */ + int getBendPointIndex(Node node); + + /** Returns whether the node represents a bend-point handle or the link. */ + boolean isBendPointHandle(Node node); + + /** + * Returns the location of the bend point in the diagram coordinate space derived from the + * current model state. + */ + DiagramCoordinate getBendPointLocation(int bpIndex); + + /** Enable all feedback nodes (at least make them mouse opaque). */ + void enableFeedback(); + + /** Disable all feedback nodes (at least make them mouse transparent). */ + void disableFeedback(); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IVisual.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IVisual.java new file mode 100644 index 0000000000000000000000000000000000000000..400569502788bf2efff3de84f2c53b65256b3fc4 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IVisual.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundlePart; + +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; + +/** + * Interface for visuals (encapsulating a {@link Node}), which are created by + * {@link IVisualFactory} and usually represent a single model element. + */ +public interface IVisual extends IMVCBundlePart { + /** + * Updates the visual's nodes possibly adding or removing nodes from the scene graph using the + * diagram layers. + */ + void updateNodes(DiagramLayers layers); + + /** + * Removes any nodes from the layers before the {@link IMVCBundle} is removed from the diagram, + * e.g., because the model element was deleted. + */ + void removeAllVisuals(DiagramLayers layers); + + /** Returns whether this visual is currently visible. */ + boolean enableVisual(); + + /** Returns whether this visual is currently receiving interactions. */ + boolean enableInteraction(); + + /** + * Returns the current bounds computed from model and feedback coordinates relative to the + * diagram origin. + */ + Rectangle2D getCurrentBounds(); + + /** + * Returns the bounds of the visual in the diagram coordinate space derived from the current + * model state. + */ + Rectangle2D getModelBounds(); + + /** + * Returns the point used to start or end the link creation line feedback using the given + * location as an indication. + */ + DiagramCoordinate getLinkAnchorage(DiagramCoordinate indication); + + /** Requests the focus on a specific node determined by this visual. */ + void requestFocus(); + + /** + * Returns the {@link EDragGesture} for the given node and diagram location. + * + * @param node + * the node to be checked + * @param diagramLocation + * the location on the diagram + * @return the drag gesture that can be initiated at this location + */ + EDragGesture getDragGesture(Node node, DiagramCoordinate diagramLocation); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IVisualFactory.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IVisualFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..39253dc705f7f73ddbb83d050232a19219d54e71 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/IVisualFactory.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; + +/** + * Interface for factories which create {@link IVisual}s from model elements for the + * {@link DiagramViewer}. The factory creates four types of visuals: diagram anchorage, content, + * content anchorage, and link visuals. + */ +public interface IVisualFactory { + /** Returns a visual node for the given content model element bundle. */ + IContentVisual createContentVisual(IContentMVCBundle modelBundle); + + /** Returns a visual node for the given diagram anchorage model element bundle. */ + IDiagramAnchorageVisual createDiagramAnchorageVisual(IDiagramAnchorageMVCBundle modelBundle); + + /** + * Returns a visual node for the given content anchorage model element bundle. Sub-classes may + * assume that {@code modelBundle.getAttachedTo()} points to the parent bundle. + */ + IContentAnchorageVisual createContentAnchorageVisual(IContentAnchorageMVCBundle modelBundle); + + /** + * Returns a visual node for the given link model element bundle. Sub-classes may assume + * that {@code modelBundle.getDiagramMVCBundle()} points to the parent bundle. + */ + ILinkVisual createLinkVisual(ILinkMVCBundle modelBundle); +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..46f793ff95f84ccfb94b335d55d982ac19faa129 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/.ratings @@ -0,0 +1,6 @@ +ContentAnchorageVisualBase.java 6722629a940e9f8d973d2176bc3855932d7fa35a YELLOW +ContentVisualBase.java b061d42fc27e980023980d070961d0814702237d YELLOW +DiagramAnchorageVisualBase.java 05c235152bc79187f0fc9b041435da7968654a78 YELLOW +LinkVisualBase.java 909b933b38b7651cac901d767115e173983bef26 YELLOW +MVCBundlePartWithEffectsBase.java 6f6fbbb065950ad3acd4dc1fbfdd1348874e51d2 YELLOW +VisualBase.java a2daf2d8f8450559450ddf5bbc93d3f948c189dd YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/ContentAnchorageVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/ContentAnchorageVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..6722629a940e9f8d973d2176bc3855932d7fa35a --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/ContentAnchorageVisualBase.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers.ILayer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentVisual; + +import javafx.geometry.Dimension2D; +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Shape; + +/** Base implementation of {@link IContentAnchorageVisual}. */ +public abstract class ContentAnchorageVisualBase extends VisualBase + implements IContentAnchorageVisual { + /** Constructor. */ + public ContentAnchorageVisualBase(IContentAnchorageMVCBundle mvcb, Shape visual, Shape hit) { + super(mvcb, visual, hit); + } + + /** {@inheritDoc} */ + @Override + public IContentAnchorageMVCBundle getMVCBundle() { + // wild cast works: see constructor + return (IContentAnchorageMVCBundle)super.getMVCBundle(); + } + + /** {@inheritDoc} */ + @Override + protected ILayer getLayerForVisualShape(DiagramLayers layers) { + return layers.getAnchorageLayer(); + } + + /** {@inheritDoc} */ + @Override + protected ILayer getLayerForHitShape(DiagramLayers layers) { + return layers.getAnchorageInteractionLayer(); + } + + /** Returns the location of the visual in its parent coordinate system. */ + protected Point2D getLocationOnParent() { + IContentVisual cv = getMVCBundle().getAttachedTo().getVisual(); + return cv.getAnchorageLocation(this); + } + + /** {@inheritDoc} */ + @Override + public Rectangle2D getCurrentBounds() { + Rectangle2D parentBounds = getMVCBundle().getAttachedTo().getVisual().getCurrentBounds(); + Point2D modelLocationOnParent = getLocationOnParent(); + Dimension2D size = getDimensions(); + FeedbackChange chg = getFeedbackChange(); + double x = parentBounds.getMinX() + modelLocationOnParent.getX() + chg.getDeltaX(); + double y = parentBounds.getMinY() + modelLocationOnParent.getY() + chg.getDeltaY(); + return new Rectangle2D(x, y, size.getWidth(), size.getHeight()); + } + + /** {@inheritDoc} */ + @Override + public Dimension2D getDimensions() { + DiagramViewerFeatures features = getViewer().getFeatures(); + return new Dimension2D(features.getHorizontalSpacing(), features.getVerticalSpacing()); + } + + /** Returns the color to be used when the port is highlighted as incoming port. */ + protected Paint getHighlightIncomingLinkColor() { + return Color.GREENYELLOW; + } + + /** Returns the color to be used when the port is highlighted as outgoing port. */ + protected Paint getHighlightOutgoingLinkColor() { + return Color.TOMATO; + } + + /** Returns the color of the border to be used when the port is highlighted as incoming port. */ + protected Paint getHighlightIncomingLinkBorderColor() { + return Color.GREENYELLOW.darker(); + } + + /** Returns the color of the border to be used when the port is highlighted as outgoing port. */ + protected Paint getHighlightOutgoingLinkBorderColor() { + return Color.TOMATO.darker(); + } + + /** {@inheritDoc} */ + @Override + public boolean updateSiblingAnchorageVisualsOnHover() { + return false; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/ContentVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/ContentVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..b061d42fc27e980023980d070961d0814702237d --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/ContentVisualBase.java @@ -0,0 +1,280 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG; + +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.MVCBundleTag; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.widgets.ExpandCollapseWidget; + +import javafx.geometry.Insets; +import javafx.geometry.Rectangle2D; +import javafx.geometry.VPos; +import javafx.scene.Node; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.shape.Shape; +import javafx.scene.text.Text; +import javafx.scene.text.TextAlignment; + +/** Base implementation for {@link IContentVisual}. */ +public abstract class ContentVisualBase extends VisualBase implements IContentVisual { + /** The text of this visual. */ + protected final Text text = new Text(); + /** The icon of this visual. */ + protected final ImageView icon = new ImageView(); + /** The expanded / collapsed indicator widget. */ + protected final ExpandCollapseWidget expandCollapseWidget = + new ExpandCollapseWidget(0, 0, 20, 20, 3); + + /** Constructor. */ + public ContentVisualBase(IContentMVCBundle mvcb, Shape visual, Shape hit) { + super(mvcb, visual, hit); + } + + /** Determines whether the icon is enabled or not. */ + protected boolean enableIcon() { + return true; + } + + /** Determines whether the text of this visual is enabled or not. */ + protected boolean enableText() { + return true; + } + + /** Determines whether the expand/collapse widget of this visual is enabled or not. */ + protected boolean enableExpandCollapseWidget() { + return false; + } + + /** Returns the name of the content. */ + protected String getName() { + return ""; + } + + /** Returns the anchor location relative to the parent bounds. */ + protected DiagramCoordinate getTextAnchorLocation() { + Insets i = getTextInsets(); + double croppedHeight = getCurrentBounds().getHeight() - i.getTop() - i.getBottom(); + return new DiagramCoordinate(0, croppedHeight / 2); + } + + /** Returns the insets of the text label. */ + protected Insets getTextInsets() { + return new Insets(10, 10, 10, 10); + } + + /** Returns the vertical alignment of the text label. */ + protected VPos getVerticalTextAlignment() { + return VPos.CENTER; + } + + /** Returns the horizontal alignment of the text label. */ + protected TextAlignment getHorizontalTextAlignment() { + return TextAlignment.LEFT; + } + + /** + * Returns the icon image for the content. Sub-classes should use an image cache instead of + * creating a new image every time this method is called. + */ + protected Image getIcon() { + return null; + } + + /** {@inheritDoc} */ + @Override + public final void applySelectedFocusedEffect(DiagramLayers layers) { + super.applySelectedFocusedEffect(layers); + + if(getViewer().getFeatures().isLinkHighlightingEnabled()) { + IContentMVCBundle bundle = getMVCBundle(); + List<IContentAnchorageMVCBundle> anchoragesBundles = bundle.getAnchorages(); + for(IContentAnchorageMVCBundle aBundle : anchoragesBundles) { + List<ILinkMVCBundle> incomingLinksBundles = aBundle.getIncomingLinks(); + for(ILinkMVCBundle ilBundle : incomingLinksBundles) { + addTag(ilBundle, HIGHLIGHT_INCOMING_LINK_TAG, layers); + addTag(ilBundle.getStartAnchorage(), HIGHLIGHT_INCOMING_LINK_TAG, layers); + addTag(ilBundle.getEndAnchorage(), HIGHLIGHT_INCOMING_LINK_TAG, layers); + } + List<ILinkMVCBundle> outgoingLinksBundles = aBundle.getOutgoingLinks(); + for(ILinkMVCBundle olBundle : outgoingLinksBundles) { + addTag(olBundle, HIGHLIGHT_OUTGOING_LINK_TAG, layers); + addTag(olBundle.getEndAnchorage(), HIGHLIGHT_OUTGOING_LINK_TAG, layers); + addTag(olBundle.getStartAnchorage(), HIGHLIGHT_OUTGOING_LINK_TAG, layers); + } + } + } + } + + /** {@inheritDoc} */ + @Override + protected void applyNotSelectedEffect(DiagramLayers layers) { + super.applyNotSelectedEffect(layers); + + IContentMVCBundle bundle = getMVCBundle(); + List<IContentAnchorageMVCBundle> anchoragesBundles = bundle.getAnchorages(); + for(IContentAnchorageMVCBundle aBundle : anchoragesBundles) { + List<ILinkMVCBundle> incomingLinksBundles = aBundle.getIncomingLinks(); + for(ILinkMVCBundle ilBundle : incomingLinksBundles) { + removeTag(ilBundle, HIGHLIGHT_INCOMING_LINK_TAG, layers); + removeTag(ilBundle.getStartAnchorage(), HIGHLIGHT_INCOMING_LINK_TAG, layers); + removeTag(ilBundle.getEndAnchorage(), HIGHLIGHT_INCOMING_LINK_TAG, layers); + } + List<ILinkMVCBundle> outgoingLinksBundles = aBundle.getOutgoingLinks(); + for(ILinkMVCBundle olBundle : outgoingLinksBundles) { + removeTag(olBundle, HIGHLIGHT_OUTGOING_LINK_TAG, layers); + removeTag(olBundle.getEndAnchorage(), HIGHLIGHT_OUTGOING_LINK_TAG, layers); + removeTag(olBundle.getStartAnchorage(), HIGHLIGHT_OUTGOING_LINK_TAG, layers); + } + } + } + + /** {@inheritDoc} */ + @Override + public Rectangle2D getCurrentBounds() { + FeedbackChange fb = getFeedbackChange(); + Rectangle2D mb = getModelBounds(); + return fb.applyTo(mb); + } + + /** Adds a tag for the given bundle and updates the visual. */ + private void addTag(IMVCBundle bundle, MVCBundleTag tag, DiagramLayers layers) { + bundle.addTag(tag); + bundle.getVisual().updateNodes(layers); + } + + /** Removes a tag from the given bundle and updates the visual. */ + private void removeTag(IMVCBundle bundle, MVCBundleTag tag, DiagramLayers layers) { + bundle.removeTag(tag); + bundle.getVisual().updateNodes(layers); + } + + /** + * {@inheritDoc} + * <p> + * Sub-classes may override, but must call super implementation to update children properly. + */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + + IContentMVCBundle bundle = getMVCBundle(); + Rectangle2D bounds = getCurrentBounds(); + double leftX = bounds.getMinX(); + double rightX = bounds.getMaxX(); + double upperY = bounds.getMinY(); + + Insets i = getTextInsets(); + DiagramCoordinate anchorLocation = getTextAnchorLocation(); + double anchorX = leftX + anchorLocation.getX() + i.getLeft(); + double anchorY = upperY + anchorLocation.getY() + i.getTop(); + + if(enableIcon()) { + if(icon.getParent() == null) { + layers.getTextLayer().add(icon, bundle); + } + Image img = getIcon(); + icon.setImage(img); + if(img != null) { + icon.setX(anchorX); + anchorX += img.getWidth() + i.getLeft(); + icon.setY(anchorY - img.getHeight() / 2); + } + } else { + layers.getTextLayer().remove(icon); + } + + if(enableText()) { + if(text.getParent() == null) { + layers.getTextLayer().add(text, bundle); + } + text.setX(anchorX); + text.setY(anchorY); + text.setText(getName()); + text.setWrappingWidth(rightX - anchorX - i.getRight()); + text.setTextAlignment(getHorizontalTextAlignment()); + text.setTextOrigin(getVerticalTextAlignment()); + } else { + layers.getTextLayer().remove(text); + } + + if(enableExpandCollapseWidget()) { + if(expandCollapseWidget.getParent() == null) { + layers.getContentInteractionLayer().add(expandCollapseWidget, bundle); + } + Rectangle2D ecWidgetLocation = getExpandCollapseWidgetRectangle(); + double inset = getExpandCollapseWidgetInset(); + double x = leftX + ecWidgetLocation.getMinX(); + double y = upperY + ecWidgetLocation.getMinY(); + expandCollapseWidget.update(x, y, ecWidgetLocation.getWidth(), + ecWidgetLocation.getHeight(), inset); + expandCollapseWidget.setState(getExpandCollapseWidgetState()); + } else { + layers.getContentInteractionLayer().remove(expandCollapseWidget); + } + } + + /** {@inheritDoc} */ + @Override + public void removeAllVisuals(DiagramLayers layers) { + super.removeAllVisuals(layers); + + layers.getContentInteractionLayer().remove(expandCollapseWidget); + layers.getTextLayer().remove(text); + layers.getTextLayer().remove(icon); + } + + /** Returns the current state of the expand/collapse widget. */ + protected boolean getExpandCollapseWidgetState() { + return true; + } + + /** Returns the inset for the plus/minus sign of the expand/collapse widget. */ + protected double getExpandCollapseWidgetInset() { + return 3; + } + + /** Returns the location for the expand/collapse widget. */ + protected Rectangle2D getExpandCollapseWidgetRectangle() { + Rectangle2D b = getCurrentBounds(); + return new Rectangle2D(b.getWidth() - 20, 6, 14, 14); + } + + /** {@inheritDoc} */ + @Override + public IContentMVCBundle getMVCBundle() { + // wild cast works: see constructor + return (IContentMVCBundle)super.getMVCBundle(); + } + + /** {@inheritDoc} */ + @Override + public boolean isExpandCollapseWidget(Node widget) { + return expandCollapseWidget.isHit(widget); + } + + /** {@inheritDoc} */ + @Override + public boolean updateAttachedAnchorageVisualsOnHover() { + return false; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/DiagramAnchorageVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/DiagramAnchorageVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..05c235152bc79187f0fc9b041435da7968654a78 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/DiagramAnchorageVisualBase.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IDiagramAnchorageVisual; + +import javafx.geometry.Rectangle2D; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Shape; + +/** Base implementation of {@link IDiagramAnchorageVisual}. */ +public abstract class DiagramAnchorageVisualBase extends VisualBase + implements IDiagramAnchorageVisual { + /** Constructor. */ + public DiagramAnchorageVisualBase(IDiagramAnchorageMVCBundle mvcb, Shape visual, Shape hit) { + super(mvcb, visual, hit); + } + + /** {@inheritDoc} */ + @Override + public IDiagramAnchorageMVCBundle getMVCBundle() { + // wild cast works: see constructor + return (IDiagramAnchorageMVCBundle)super.getMVCBundle(); + } + + /** {@inheritDoc} */ + @Override + public Rectangle2D getCurrentBounds() { + FeedbackChange fb = getMVCBundle().getFeedbackChange(); + Rectangle2D mb = getModelBounds(); + return fb.applyTo(mb); + } + + /** Returns the color to be used when the port is highlighted as incoming port. */ + protected Paint getHighlightIncomingLinkColor() { + return Color.GREENYELLOW; + } + + /** Returns the color to be used when the port is highlighted as outgoing port. */ + protected Paint getHighlightOutgoingLinkColor() { + return Color.TOMATO; + } + + /** Returns the color of the border to be used when the port is highlighted as incoming port. */ + protected Paint getHighlightIncomingLinkBorderColor() { + return Color.GREENYELLOW.darker(); + } + + /** Returns the color of the border to be used when the port is highlighted as outgoing port. */ + protected Paint getHighlightOutgoingLinkBorderColor() { + return Color.TOMATO.darker(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/LinkVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/LinkVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..909b933b38b7651cac901d767115e173983bef26 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/LinkVisualBase.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base; + +import static java.lang.Math.abs; +import static java.lang.Math.atan2; +import static java.lang.Math.cos; +import static java.lang.Math.sin; +import static javafx.scene.paint.Color.RED; +import static javafx.scene.paint.Color.TRANSPARENT; +import static javafx.scene.paint.Color.color; + +import java.util.HashMap; +import java.util.Map; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.ILinkVisual; + +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Rectangle; + +/** Base implementation for {@link ILinkVisual}. */ +public abstract class LinkVisualBase extends MVCBundlePartWithEffectsBase implements ILinkVisual { + /** Stores the feedback change for each bend point. */ + protected final Map<Integer, FeedbackChange> feedbackChanges = new HashMap<>(); + /** The selection feedback handle rectangle for the start of the line link. */ + protected final Rectangle startFeedbackHandle; + /** The selection feedback handle rectangle for the end of the line link. */ + protected final Rectangle endFeedbackHandle; + + /** Constructor. */ + public LinkVisualBase(ILinkMVCBundle mvcb) { + super(mvcb); + double feedbackSize = getFeedbackMarkerSize(); + double fs2 = feedbackSize / 2; + Point2D startAnchorPoint = getStartAnchorPoint(); + startFeedbackHandle = new Rectangle(startAnchorPoint.getX() - fs2, + startAnchorPoint.getY() - fs2, feedbackSize, feedbackSize); + startFeedbackHandle.setFill(TRANSPARENT); + startFeedbackHandle.setStroke(RED); + Point2D endAnchorPoint = getEndAnchorPoint(); + endFeedbackHandle = new Rectangle(endAnchorPoint.getX() - fs2, endAnchorPoint.getY() - fs2, + feedbackSize, feedbackSize); + endFeedbackHandle.setFill(TRANSPARENT); + endFeedbackHandle.setStroke(RED); + } + + /** {@inheritDoc} */ + @Override + public final Rectangle2D getModelBounds() { + // links do not need model bounds + return null; + } + + /** {@inheritDoc} */ + @Override + public void setFeedbackChangeForBendPoint(int index, FeedbackChange change) { + if(change == null) { + feedbackChanges.remove(index); + } else { + feedbackChanges.put(index, change); + } + getViewer().updateVisual(getMVCBundle()); + } + + /** + * Returns the current {@link FeedbackChange} for the bend-point at the given index. If there is + * no entry at the given index then {@link FeedbackChange#EMPTY} is returned. + */ + protected FeedbackChange getFeedbackChangeForBendPoint(int index) { + FeedbackChange result = feedbackChanges.get(index); + if(result == null) { + return FeedbackChange.EMPTY; + } + return result; + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getLinkAnchorage(DiagramCoordinate indication) { + return indication; + } + + /** Returns the color to be used when the connection is highlighted as incoming link. */ + protected Paint getHighlightIncomingLinkColor() { + return Color.GREENYELLOW; + } + + /** Returns the color to be used when the connection is highlighted as outgoing link. */ + protected Paint getHighlightOutgoingLinkColor() { + return Color.TOMATO; + } + + /** + * Returns the point on a circle centered in the current bounds intersecting with a line between + * the center point and the given point. + */ + protected DiagramCoordinate getCircleLocation(Rectangle2D bounds, Point2D target) { + double sx = bounds.getMinX() + bounds.getWidth() / 2; + double sy = bounds.getMinY() + bounds.getHeight() / 2; + + double a = atan2(target.getY() - sy, target.getX() - sx); + double nx = sx + cos(a) * (bounds.getWidth() / 2 - 1); + double ny = sy + sin(a) * (bounds.getHeight() / 2 - 1); + return new DiagramCoordinate(nx, ny); + } + + /** Returns the middle point of the rectangle border closest to the target point. */ + protected final DiagramCoordinate getRectangleLocation(Rectangle2D ib, Point2D target) { + double cx = ib.getMinX() + ib.getWidth() / 2; + double cy = ib.getMinY() + ib.getHeight() / 2; + double dx = cx - target.getX(); + double dy = cy - target.getY(); + if(abs(dx) > abs(dy)) { + if(cx < target.getX()) { + return new DiagramCoordinate(ib.getMaxX(), cy); + } + return new DiagramCoordinate(ib.getMinX(), cy); + } + if(cy < target.getY()) { + return new DiagramCoordinate(cx, ib.getMaxY()); + } + return new DiagramCoordinate(cx, ib.getMinY()); + } + + /** The interaction shading color. */ + private static final Color INTERACTION_SHADING = color(0, 0, 0, .1); + + /** Returns the color for the interaction area. */ + protected final Paint getInteractionColor() { + return getViewer().getFeatures().isInteractionAreaShadingEnabled() ? INTERACTION_SHADING + : Color.TRANSPARENT; + } + + /** Returns the size of the feedback markers. */ + protected double getFeedbackMarkerSize() { + return getMVCBundle().getViewer().getFeatures().getHorizontalSpacing() / 2; + } + + /** {@inheritDoc} */ + @Override + public int getBendPointIndex(Node node) { + if(node == startFeedbackHandle) { + return START_OF_LINK_BEND_POINT_INDEX; + } + if(node == endFeedbackHandle) { + return END_OF_LINK_BEND_POINT_INDEX; + } + return NO_BEND_POINT_INDEX; + } + + /** {@inheritDoc} */ + @Override + public boolean isBendPointHandle(Node node) { + return node == startFeedbackHandle || node == endFeedbackHandle; + } + + /** {@inheritDoc} */ + @Override + public void disableFeedback() { + startFeedbackHandle.setMouseTransparent(true); + endFeedbackHandle.setMouseTransparent(true); + } + + /** {@inheritDoc} */ + @Override + public void enableFeedback() { + startFeedbackHandle.setMouseTransparent(false); + endFeedbackHandle.setMouseTransparent(false); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/MVCBundlePartWithEffectsBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/MVCBundlePartWithEffectsBase.java new file mode 100644 index 0000000000000000000000000000000000000000..6f6fbbb065950ad3acd4dc1fbfdd1348874e51d2 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/MVCBundlePartWithEffectsBase.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.FOCUS_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HOVER_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_ALLOWED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_DENIED_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.LINK_TARGET_MAYBE_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.PRIMARY_SELECTION_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.SECONDARY_SELECTION_TAG; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.MVCBundlePartBase; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; + +/** {@link MVCBundlePartBase} base class extended with effect methods from {@link IVisual}. */ +public abstract class MVCBundlePartWithEffectsBase extends MVCBundlePartBase implements IVisual { + /** Constructor. */ + public MVCBundlePartWithEffectsBase(IMVCBundle mvcb) { + super(mvcb); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + IMVCBundle bundle = getMVCBundle(); + // selection and focus effects + if(bundle.hasTag(PRIMARY_SELECTION_TAG)) { + if(bundle.hasTag(FOCUS_TAG)) { + applySelectedFocusedEffect(layers); + } else { + applySelectedNotFocusedEffect(layers); + } + } else if(bundle.hasTag(SECONDARY_SELECTION_TAG)) { + applySecondarySelectedEffect(layers); + } else { + applyNotSelectedEffect(layers); + } + // hover effects + if(bundle.hasTag(HOVER_TAG)) { + createHoverEffect(layers); + } else { + removeHoverEffect(layers); + } + // link target effects + if(bundle.hasTag(LINK_TARGET_ALLOWED_TAG)) { + createLinkTargetAllowedEffect(layers); + } else if(bundle.hasTag(LINK_TARGET_DENIED_TAG)) { + createLinkTargetDeniedEffect(layers); + } else if(bundle.hasTag(LINK_TARGET_MAYBE_TAG)) { + createLinkTargetMaybeEffect(layers); + } else { + createLinkTargetDisabledEffect(layers); + } + } + + /** {@inheritDoc} */ + @Override + public void removeAllVisuals(DiagramLayers layers) { + applyNotSelectedEffect(layers); + removeHoverEffect(layers); + createLinkTargetDisabledEffect(layers); + } + + /** + * Applies the effect when not selected and not focused. + * + * @param layers + * the layers of the diagram + */ + protected void applyNotSelectedEffect(DiagramLayers layers) { + // the default does nothing + } + + /** + * Applies the effect when selected and focused. + * + * @param layers + * the layers of the diagram + */ + protected void applySelectedFocusedEffect(DiagramLayers layers) { + // the default does nothing + } + + /** + * Applies the effect when selected, but not focused. + * + * @param layers + * the layers of the diagram + */ + protected void applySelectedNotFocusedEffect(DiagramLayers layers) { + // the default does nothing + } + + /** + * Applies the effect when secondary selected. + * + * @param layers + * the layers of the diagram + */ + protected void applySecondarySelectedEffect(DiagramLayers layers) { + // the default does nothing + } + + /** + * Creates the hover effect. + * + * @param layers + * the layers of the diagram + */ + protected void createHoverEffect(DiagramLayers layers) { + // the default does nothing + } + + /** + * Removes the hover effect. + * + * @param layers + * the layers of the diagram + */ + protected void removeHoverEffect(DiagramLayers layers) { + // the default does nothing + } + + /** + * Creates the link target allowed effect. + * + * @param layers + * the layers of the diagram + */ + protected void createLinkTargetAllowedEffect(DiagramLayers layers) { + // do nothing + } + + /** + * Creates the link target denied effect. + * + * @param layers + * the layers of the diagram + */ + protected void createLinkTargetDeniedEffect(DiagramLayers layers) { + // do nothing + } + + /** + * Creates the link target maybe effect. + * + * @param layers + * the layers of the diagram + */ + protected void createLinkTargetMaybeEffect(DiagramLayers layers) { + // do nothing + } + + /** + * Creates the link target disabled effect. + * + * @param layers + * the layers of the diagram + */ + protected void createLinkTargetDisabledEffect(DiagramLayers layers) { + // do nothing + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/VisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/VisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..a2daf2d8f8450559450ddf5bbc93d3f948c189dd --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/base/VisualBase.java @@ -0,0 +1,369 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base; + +import static javafx.scene.paint.Color.CORNFLOWERBLUE; +import static javafx.scene.paint.Color.DEEPSKYBLUE; +import static javafx.scene.paint.Color.ORANGE; +import static javafx.scene.paint.Color.RED; +import static javafx.scene.paint.Color.TRANSPARENT; +import static javafx.scene.paint.Color.color; +import static javafx.scene.shape.StrokeType.INSIDE; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture.MOVE; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture.NEW_LINK; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture.NONE; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture.RESIZE_H; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture.RESIZE_V; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture.RESIZE_VH; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers.ILayer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; + +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.geometry.VPos; +import javafx.scene.Node; +import javafx.scene.effect.DropShadow; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Rectangle; +import javafx.scene.shape.Shape; +import javafx.scene.shape.StrokeType; +import javafx.scene.text.Text; +import javafx.scene.text.TextAlignment; + +/** + * Base class implementing the {@link IVisual} interface. + * <P> + * This implementation provides a simple {@link DropShadow} hover effect and a hover label. + */ +public abstract class VisualBase extends MVCBundlePartWithEffectsBase implements IVisual { + /** The visible shape of this visual. */ + private final Shape visualShape; + /** The invisible hit area of this visual. */ + private final Shape hitAreaShape; + /** The selection rectangle. */ + protected final Rectangle selectionRectangle = new Rectangle(); + /** The hover text label. */ + private final Text hoverText = new Text(""); + + /** Constructor. */ + public VisualBase(IMVCBundle mvcb, Shape visual, Shape hit) { + super(mvcb); + this.visualShape = visual; + this.hitAreaShape = hit; + hitAreaShape.setFill(TRANSPARENT); + hitAreaShape.setStroke(TRANSPARENT); + selectionRectangle.setFill(TRANSPARENT); + selectionRectangle.setStrokeWidth(2); + hoverText.setMouseTransparent(true); + } + + /** Returns the hit area shape. */ + protected final Shape getHitAreaShape() { + return hitAreaShape; + } + + /** Returns visual shape. */ + protected final Shape getVisualShape() { + return visualShape; + } + + /** {@inheritDoc} */ + @Override + protected void createHoverEffect(DiagramLayers layers) { + Color shadow = getHoverShadowColor(); + if(shadow != null && visualShape != null) { + visualShape.setEffect(new DropShadow(10, 3, 3, shadow)); + } + // handle hover text + String text = getHoverText(); + if(text == null) { + return; + } + hoverText.setText(text); + Point2D loc = getHoverTextLocation(); + Rectangle2D bounds = getCurrentBounds(); + double tx = bounds.getMinX() + loc.getX(); + hoverText.setX(tx); + double ty = bounds.getMinY() + loc.getY(); + hoverText.setY(ty); + hoverText.setTextAlignment(TextAlignment.LEFT); + hoverText.setTextOrigin(VPos.TOP); + hoverText.setMouseTransparent(true); + layers.getVisualFeedbackLayer().add(hoverText, getMVCBundle()); + } + + /** {@inheritDoc} */ + @Override + protected void removeHoverEffect(DiagramLayers layers) { + if(visualShape != null) { + visualShape.setEffect(null); + } + layers.getVisualFeedbackLayer().remove(hoverText); + } + + /** {@inheritDoc} */ + @Override + public void removeAllVisuals(DiagramLayers layers) { + super.removeAllVisuals(layers); + + getLayerForHitShape(layers).remove(hitAreaShape); + getLayerForVisualShape(layers).remove(visualShape); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + + IMVCBundle bundle = getMVCBundle(); + if(visualShape != null) { + if(enableVisual()) { + if(visualShape.getParent() == null) { + getLayerForVisualShape(layers).add(visualShape, bundle); + } + } else { + getLayerForVisualShape(layers).remove(visualShape); + } + } + if(hitAreaShape != null) { + if(enableInteraction()) { + if(hitAreaShape.getParent() == null) { + getLayerForHitShape(layers).add(hitAreaShape, bundle); + } + } else { + getLayerForHitShape(layers).remove(hitAreaShape); + } + } + } + + /** + * Returns the layer to be used for the {@link #getVisualShape() visual shape}. The default is + * {@link DiagramLayers#getContentLayer()} + */ + protected ILayer getLayerForVisualShape(DiagramLayers layers) { + return layers.getContentLayer(); + } + + /** + * Returns the layer to be used for the {@link #getHitAreaShape() hit area shape}. The default + * is {@link DiagramLayers#getContentInteractionLayer()} + */ + protected ILayer getLayerForHitShape(DiagramLayers layers) { + return layers.getContentInteractionLayer(); + } + + /** {@inheritDoc} */ + @Override + public boolean enableVisual() { + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean enableInteraction() { + return true; + } + + /** Updates and adds the selection feedback rectangle. */ + protected void updateSelectionFeedbackRectangle(Paint color, DiagramLayers layers) { + Rectangle2D bounds = getCurrentBounds(); + if(bounds == null) { + return; + } + selectionRectangle.setX(bounds.getMinX()); + selectionRectangle.setY(bounds.getMinY()); + selectionRectangle.setWidth(bounds.getWidth()); + selectionRectangle.setHeight(bounds.getHeight()); + selectionRectangle.setStroke(color); + layers.getVisualFeedbackLayer().add(selectionRectangle, getMVCBundle()); + } + + /** {@inheritDoc} */ + @Override + protected void applySelectedNotFocusedEffect(DiagramLayers layers) { + updateSelectionFeedbackRectangle(getSelectedNotFocusedColor(), layers); + } + + /** {@inheritDoc} */ + @Override + protected void applySecondarySelectedEffect(DiagramLayers layers) { + updateSelectionFeedbackRectangle(getSecondarySelectedColor(), layers); + } + + /** {@inheritDoc} */ + @Override + protected void applySelectedFocusedEffect(DiagramLayers layers) { + updateSelectionFeedbackRectangle(getSelectedFocusedColor(), layers); + } + + /** {@inheritDoc} */ + @Override + protected void applyNotSelectedEffect(DiagramLayers layers) { + super.applyNotSelectedEffect(layers); + layers.getVisualFeedbackLayer().remove(selectionRectangle); + } + + /** {@inheritDoc} */ + @Override + public EDragGesture getDragGesture(Node node, DiagramCoordinate diagramLocation) { + boolean notSelected = !getViewer().isPrimarySelected(getMVCBundle()); + EDragGesture gesture = dragGestureHitTest(node, diagramLocation); + if(gesture == NEW_LINK && requireSelectionForNewLinkGesture() && notSelected) { + // override when not selected + return NONE; + } + if(gesture == MOVE && requireSelectionForMoveGesture() && notSelected) { + // override when not selected + return NONE; + } + if(gesture == RESIZE_H || gesture == RESIZE_V || gesture == RESIZE_VH) { + if(requireSelectionForResizeGesture() && notSelected) { + // override when not selected + return NONE; + } + } + return gesture; + } + + /** + * Performs a hit test on the given node returning the expected gesture. Note that the result + * may be overridden if the node is not selected and + * {@link #requireSelectionForNewLinkGesture()} or {@link #requireSelectionForMoveGesture()} + * return {@code true}, respectively. The default returns {@link EDragGesture#NONE}. + * + * @param node + * the node currently under the mouse pointer + * @param diagramLocation + * the location in diagram coordinates + * @return the drag gesture + */ + protected EDragGesture dragGestureHitTest(Node node, DiagramCoordinate diagramLocation) { + return NONE; + } + + /** + * Returns whether the visual must be selected when creating links is allowed. The default is + * {@code false}. + */ + protected boolean requireSelectionForNewLinkGesture() { + return false; + } + + /** + * Returns whether the visual must be selected when moving is allowed. The default is + * {@code true}. + */ + protected boolean requireSelectionForMoveGesture() { + return true; + } + + /** + * Returns whether the visual must be selected when resizing is allowed. The default is + * {@code true}. + */ + protected boolean requireSelectionForResizeGesture() { + return true; + } + + /** Returns the opacity of the content visual. */ + protected double getOpacity() { + return 0.8; + } + + /** Returns the color used to paint the border of the visual. */ + protected Paint getBorderColor() { + return CORNFLOWERBLUE; + } + + /** Returns the width of the border. */ + protected double getBorderWidth() { + return 2; + } + + /** Returns the type of the border. */ + protected StrokeType getBorderType() { + return INSIDE; + } + + /** Returns the color for the interior of the visual. */ + protected Paint getFillColor() { + return DEEPSKYBLUE; + } + + /** Returns the color of the selection rectangle if it also has focus. */ + protected Color getSelectedFocusedColor() { + return RED; + } + + /** Returns the color of the selection rectangle if it part of a secondary selection. */ + protected Color getSecondarySelectedColor() { + return ORANGE; + } + + /** Returns the color of the selection rectangle if it does not have focus. */ + protected Color getSelectedNotFocusedColor() { + return RED; + } + + /** Returns the size of the hit area as an outset of the visible rectangle. */ + protected double getHitAreaOutset() { + return 10; + } + + /** The interaction shading color. */ + protected static final Color INTERACTION_SHADING = color(0, 0, 0, .1); + + /** Returns the color for the interaction area. */ + protected Color getInteractionColor() { + return getViewer().getFeatures().isInteractionAreaShadingEnabled() ? INTERACTION_SHADING + : Color.TRANSPARENT; + } + + /** Returns the color of the hover drop shadow. */ + protected Color getHoverShadowColor() { + return Color.DARKGRAY.darker(); + } + + /** Returns the hover text. */ + protected String getHoverText() { + return null; + } + + /** Returns the hover text location . */ + protected Point2D getHoverTextLocation() { + return new Point2D(0, getCurrentBounds().getHeight()); + } + + /** Returns the center point of the current bounds. */ + protected final DiagramCoordinate getCurrentBoundsCenter() { + Rectangle2D b = getVisual().getCurrentBounds(); + double x = b.getMinX() + b.getWidth() / 2; + double y = b.getMinY() + b.getHeight() / 2; + return new DiagramCoordinate(x, y); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getLinkAnchorage(DiagramCoordinate indication) { + return getCurrentBoundsCenter(); + } + + /** {@inheritDoc} */ + @Override + public void requestFocus() { + getVisualShape().requestFocus(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..c2c5235bd3b9deb31fdbf82757057360aa46d36b --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/.ratings @@ -0,0 +1,7 @@ +CircularContentAnchorageVisualBase.java de227ce8a2a14eb4df7bdcf43b82d98f6ff17045 YELLOW +CircularContentVisualBase.java cc3caea328e36e90069b915e413c8e7e9522a939 YELLOW +CircularDiagramAnchorageVisualBase.java 7a3b92fb1b135c218b9a5e16506acfc74a6b5468 YELLOW +CurveLinkVisualBase.java 0b8706214320d715966c86a5242ad21c8bf5a315 YELLOW +CurveSegment.java 445bc2607cb70ae1c788c27ba9fc637dd7df4956 YELLOW +EllipticBorderLocation.java 1e9b3d42c7fcd5495004fb30b0c499096a839967 YELLOW +EllipticContentVisualBase.java dc2fddc9cfe5605bc8a5d09dd862845e360b23f5 YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CircularContentAnchorageVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CircularContentAnchorageVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..de227ce8a2a14eb4df7bdcf43b82d98f6ff17045 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CircularContentAnchorageVisualBase.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.ContentAnchorageVisualBase; + +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; + +/** Base class for {@link IContentAnchorageVisual}s depicted as circles. */ +public abstract class CircularContentAnchorageVisualBase extends ContentAnchorageVisualBase { + /** Constructor. */ + public CircularContentAnchorageVisualBase(IContentAnchorageMVCBundle mvcb) { + super(mvcb, new Circle(), new Circle()); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + updateCircleProperties(); + } + + /** Sets all properties of the main node. */ + private void updateCircleProperties() { + Rectangle2D b = getCurrentBounds(); + if(enableVisual()) { + Circle circle = getVisualCircle(); + circle.setCenterX(b.getMinX() + b.getWidth() / 2); + circle.setCenterY(b.getMinY() + b.getHeight() / 2); + circle.setRadius(getInnerRadius(b)); + circle.setOpacity(getOpacity()); + circle.setStrokeWidth(getBorderWidth()); + circle.setStrokeType(getBorderType()); + if(getMVCBundle().hasTag(HIGHLIGHT_INCOMING_LINK_TAG)) { + circle.setFill(getHighlightIncomingLinkColor()); + circle.setStroke(getHighlightIncomingLinkBorderColor()); + } else if(getMVCBundle().hasTag(HIGHLIGHT_OUTGOING_LINK_TAG)) { + circle.setFill(getHighlightOutgoingLinkColor()); + circle.setStroke(getHighlightOutgoingLinkBorderColor()); + } else { + circle.setFill(getFillColor()); + circle.setStroke(getBorderColor()); + } + } + + if(enableInteraction()) { + Circle ha = getHitAreaCircle(); + ha.setCenterX(b.getMinX() + b.getWidth() / 2); + ha.setCenterY(b.getMinY() + b.getHeight() / 2); + ha.setRadius(getOuterRadius(b)); + Color interactionColor = getInteractionColor(); + ha.setStroke(interactionColor); + ha.setFill(interactionColor); + } + } + + /** Helper function to compute outer radius. */ + private double getOuterRadius(Rectangle2D b) { + return getInnerRadius(b) + getHitAreaOutset(); + } + + /** Helper function to compute inner radius. */ + private double getInnerRadius(Rectangle2D b) { + return b.getWidth() / 2 - getInset(); + } + + /** Returns the insets of the filled circle subtracted from {@link #getDimensions()}. */ + protected double getInset() { + return 2; + } + + /** {@inheritDoc} */ + @Override + protected EDragGesture dragGestureHitTest(Node n, DiagramCoordinate diagramLocation) { + if(n == getHitAreaCircle()) { + Circle ha = getHitAreaCircle(); + double dx = Math.abs(diagramLocation.getX() - ha.getCenterX()); + double dy = Math.abs(diagramLocation.getY() - ha.getCenterY()); + Rectangle2D bounds = getCurrentBounds(); + double innerRadius = getInnerRadius(bounds); + double outerRadius = getOuterRadius(bounds); + double squaredLocation = dx * dx + dy * dy; + if(squaredLocation > innerRadius * innerRadius && + squaredLocation <= outerRadius * outerRadius) { + return EDragGesture.NEW_LINK; + } + return EDragGesture.MOVE; + } + if(n == getVisualShape()) { + return EDragGesture.MOVE; + } + return EDragGesture.NONE; + } + + /** Returns the visual circle. */ + protected final Circle getVisualCircle() { + // wild cast works: see constructor + return (Circle)getVisualShape(); + } + + /** Returns the hit area circle. */ + protected final Circle getHitAreaCircle() { + // wild cast works: see constructor + return (Circle)getHitAreaShape(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CircularContentVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CircularContentVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..cc3caea328e36e90069b915e413c8e7e9522a939 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CircularContentVisualBase.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic; + +import static java.lang.Math.cos; +import static java.lang.Math.hypot; +import static java.lang.Math.max; +import static java.lang.Math.sin; +import static java.lang.Math.toRadians; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.IAngleLayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.ContentVisualBase; + +import javafx.geometry.Bounds; +import javafx.geometry.Dimension2D; +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; + +/** Base class for {@link ContentVisualBase content visuals} depicted by rectangles. */ +public abstract class CircularContentVisualBase extends ContentVisualBase { + /** Constructor. */ + public CircularContentVisualBase(IContentMVCBundle mvcb) { + super(mvcb, new Circle(), new Circle()); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + + Rectangle2D bounds = getCurrentBounds(); + double leftX = bounds.getMinX(); + double upperY = bounds.getMinY(); + double width = bounds.getWidth(); + double height = bounds.getHeight(); + double w2 = width / 2.0; + double cx = leftX + w2; + double h2 = height / 2.0; + double cy = upperY + h2; + double radius = max(w2, h2); + if(enableVisual()) { + Circle circle = getVisualCircleShape(); + circle.setCenterX(cx); + circle.setCenterY(cy); + circle.setRadius(radius); + circle.setOpacity(getOpacity()); + circle.setStroke(getBorderColor()); + circle.setStrokeWidth(getBorderWidth()); + circle.setStrokeType(getBorderType()); + circle.setFill(getFillColor()); + } + + if(enableInteraction()) { + Circle ha = getHitAreaCircleShape(); + double lnk = getHitAreaStartLinkSize(); + double resize = getHitAreaResizeSize(); + radius = lnk + resize + radius; + ha.setCenterX(cx); + ha.setCenterY(cy); + ha.setRadius(radius); + + Color interactionColor = getInteractionColor(); + ha.setStroke(interactionColor); + ha.setFill(interactionColor); + } + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getAnchorageLocation(IContentAnchorageVisual visual) { + Rectangle2D pb = getCurrentBounds(); + double w2 = pb.getWidth() / 2.0; + double h2 = pb.getHeight() / 2.0; + double radius = max(w2, h2); + double angleDeg = visual.getLayout(IAngleLayout.class).getAngleInDegree(); + double angleRad = toRadians(angleDeg); + double nx = cos(angleRad); + double ny = sin(angleRad); + double x = pb.getMinX() + w2 + radius * nx; + double y = pb.getMinY() + h2 + radius * ny; + return new DiagramCoordinate(x, y); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getLinkAnchorage(DiagramCoordinate indication) { + return getLocationOnCircle(indication); + } + + /** Returns the location on the circle closest to the given indication coordinate. */ + private DiagramCoordinate getLocationOnCircle(DiagramCoordinate indication) { + Circle circ = getVisualCircleShape(); + double radius = getRadius(circ); + double cx = circ.getCenterX(); + double dx = indication.getX() - cx; + double cy = circ.getCenterY(); + double dy = indication.getY() - cy; + double len = hypot(dx, dy); + double nx = radius * dx / len; + double ny = radius * dy / len; + return new DiagramCoordinate(cx + nx, cy + ny); + } + + /** Returns the dimensions of the corner arcs. */ + protected Dimension2D getCornerArcDimensions() { + return new Dimension2D(15, 15); + } + + /** {@inheritDoc} */ + @Override + protected EDragGesture dragGestureHitTest(Node node, DiagramCoordinate diagramLocation) { + Circle circ = getHitAreaCircleShape(); + if(node == circ) { + double dx = diagramLocation.getX() - circ.getCenterX(); + double dy = diagramLocation.getY() - circ.getCenterY(); + double radius = getRadius(circ); + double hypot = hypot(dx, dy); + if(hypot < radius - getHitAreaResizeSize() - getHitAreaStartLinkSize()) { + return EDragGesture.MOVE; + } + if(hypot < radius - getHitAreaStartLinkSize()) { + return EDragGesture.RESIZE_VH; + } + if(hypot < radius) { + return EDragGesture.NEW_LINK; + } + } + if(node == getVisualShape() || node == text) { + return EDragGesture.MOVE; + } + return EDragGesture.NONE; + } + + /** Returns the radius of the given circle computed from node bounds. */ + private double getRadius(Circle circ) { + Bounds bounds = circ.getBoundsInLocal(); + double w2 = bounds.getWidth() / 2.0; + double h2 = bounds.getHeight() / 2.0; + double radius = max(w2, h2); + return radius; + } + + /** Returns the visual circle shape. */ + protected final Circle getVisualCircleShape() { + // wild cast works: see constructor + return (Circle)getVisualShape(); + } + + /** Returns the hit area circle shape. */ + protected final Circle getHitAreaCircleShape() { + // wild cast works: see constructor + return (Circle)getHitAreaShape(); + } + + /** + * Returns the extension of the hit area in pixel used to trigger link creation. This number + * specifies how many pixels beyond the visible border are used to trigger link creation by a + * mouse drag gesture. + */ + protected double getHitAreaStartLinkSize() { + return getViewer().getFeatures().getMaximumSpacing(); + } + + /** Returns the size of the hit area in pixel used to resize the content visual. */ + protected double getHitAreaResizeSize() { + return getViewer().getFeatures().getMaximumSpacing(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CircularDiagramAnchorageVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CircularDiagramAnchorageVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..7a3b92fb1b135c218b9a5e16506acfc74a6b5468 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CircularDiagramAnchorageVisualBase.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.DiagramAnchorageVisualBase; + +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; + +/** Base class for {@link DiagramAnchorageVisualBase free interface visuals} depicted as circles. */ +public abstract class CircularDiagramAnchorageVisualBase extends DiagramAnchorageVisualBase { + /** Constructor. */ + public CircularDiagramAnchorageVisualBase(IDiagramAnchorageMVCBundle mvcb) { + super(mvcb, new Circle(), new Circle()); + } + + /** Returns the visual circle. */ + protected final Circle getVisualCircle() { + // wild cast works: see constructor + return (Circle)getVisualShape(); + } + + /** Returns the hit area circle. */ + protected final Circle getHitAreaCircle() { + // wild cast works: see constructor + return (Circle)getHitAreaShape(); + } + + /** Sets the properties of the circle node. */ + private void updateCircleProperties() { + Rectangle2D b = getCurrentBounds(); + double cx = b.getMinX() + b.getWidth() / 2; + double cy = b.getMinY() + b.getHeight() / 2; + + if(enableVisual()) { + Circle circle = getVisualCircle(); + circle.setCenterX(cx); + circle.setCenterY(cy); + circle.setRadius(getInnerRadius(b)); + circle.setOpacity(getOpacity()); + circle.setStrokeWidth(getBorderWidth()); + circle.setStrokeType(getBorderType()); + if(getMVCBundle().hasTag(HIGHLIGHT_INCOMING_LINK_TAG)) { + circle.setFill(getHighlightIncomingLinkColor()); + circle.setStroke(getHighlightIncomingLinkBorderColor()); + } else if(getMVCBundle().hasTag(HIGHLIGHT_OUTGOING_LINK_TAG)) { + circle.setFill(getHighlightOutgoingLinkColor()); + circle.setStroke(getHighlightOutgoingLinkBorderColor()); + } else { + circle.setFill(getFillColor()); + circle.setStroke(getBorderColor()); + } + } + + if(enableInteraction()) { + Circle ha = getHitAreaCircle(); + ha.setCenterX(cx); + ha.setCenterY(cy); + ha.setRadius(getOuterRadius(b)); + Color interactionColor = getInteractionColor(); + ha.setStroke(interactionColor); + ha.setFill(interactionColor); + } + } + + /** Helper function to compute outer radius. */ + private double getOuterRadius(Rectangle2D b) { + return getInnerRadius(b) + getHitAreaOutset(); + } + + /** Helper function to compute inner radius. */ + private double getInnerRadius(Rectangle2D b) { + return b.getWidth() / 2 - getInset(); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + updateCircleProperties(); + } + + /** Returns the insets of the filled circle subtracted from {@link #getDimensions()}. */ + protected double getInset() { + return 2; + } + + /** {@inheritDoc} */ + @Override + protected EDragGesture dragGestureHitTest(Node n, DiagramCoordinate diagramLocation) { + if(n == getHitAreaCircle()) { + Circle ha = getHitAreaCircle(); + double dx = Math.abs(diagramLocation.getX() - ha.getCenterX()); + double dy = Math.abs(diagramLocation.getY() - ha.getCenterY()); + Rectangle2D bounds = getCurrentBounds(); + double innerRadius = getInnerRadius(bounds); + double outerRadius = getOuterRadius(bounds); + double squaredLocation = dx * dx + dy * dy; + if(squaredLocation > innerRadius * innerRadius && + squaredLocation <= outerRadius * outerRadius) { + return EDragGesture.NEW_LINK; + } + return EDragGesture.MOVE; + } + if(n == getVisualCircle()) { + return EDragGesture.MOVE; + } + return EDragGesture.NONE; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CurveLinkVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CurveLinkVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..0b8706214320d715966c86a5242ad21c8bf5a315 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CurveLinkVisualBase.java @@ -0,0 +1,451 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.FOCUS_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.PRIMARY_SELECTION_TAG; + +import java.util.LinkedList; +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers.ILayer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.LinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.LinkVisualBase; + +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; + +/** Base class for {@link LinkVisualBase link visuals} with Bezier curve lines. */ +public abstract class CurveLinkVisualBase extends LinkVisualBase { + /** Stores the visual segments. */ + private final List<CurveSegment> segments = new LinkedList<>(); + /** Flag storing if the lines have been added. */ + private boolean curvesAddedToSceneGraph = false; + + /** Constructor. */ + public CurveLinkVisualBase(ILinkMVCBundle mvcb) { + super(mvcb); + } + + /** Adds the curve segments to the scene graph. */ + private void addCurveSegments(DiagramLayers layers) { + LinkMVCBundle linkBundle = getLinkBundle(); + double markerSize = getFeedbackMarkerSize(); + double ms2 = markerSize / 2; + // start point + Point2D sp = getFeedbackChangeForBendPoint(START_OF_LINK_BEND_POINT_INDEX) + .applyToPoint(getStartAnchorPoint()); + Point2D cp1 = getBendPointLocation(0); + Point2D cp2 = getBendPointLocation(1); + startFeedbackHandle.setX(sp.getX() - ms2); + startFeedbackHandle.setY(sp.getY() - ms2); + startFeedbackHandle.setWidth(markerSize); + startFeedbackHandle.setHeight(markerSize); + // handle bend points + int pts = getNumberOfBendPoints(); + for(int i = 2; i < pts; i += 3) { + Point2D ep = getBendPointLocation(i); + makeCurve(sp, cp1, cp2, ep, getInvisibleSelectionLineWidth(), markerSize, markerSize, + false, true, 0); + sp = ep; + cp1 = getBendPointLocation(i + 1); + cp2 = getBendPointLocation(i + 2); + // bpModel = getBendPointModel(); + } + // end point + Point2D ep = getFeedbackChangeForBendPoint(END_OF_LINK_BEND_POINT_INDEX) + .applyToPoint(getEndAnchorPoint()); + // Last bend point is not selectable => set its feedback rectangle size to 0 + makeCurve(sp, cp1, cp2, ep, getInvisibleSelectionLineWidth(), 0, markerSize, + showArrowOnLastSegment(), useLineArrow(), getArrowLength()); + endFeedbackHandle.setX(ep.getX() - ms2); + endFeedbackHandle.setY(ep.getY() - ms2); + endFeedbackHandle.setWidth(markerSize); + endFeedbackHandle.setHeight(markerSize); + + for(CurveSegment cs : segments) { + cs.addLinkNodes(layers, linkBundle); + } + curvesAddedToSceneGraph = true; + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + // if no longer enabled => remove + if(!enableVisual()) { + if(curvesAddedToSceneGraph) { + removeCurveSegments(layers); + } + return; + } + // check for model updates (e.g new bend-point) + int pts = getNumberOfBendPoints(); + int expectedSegments = 1 + (pts - 2) / 3; + if(!curvesAddedToSceneGraph) { + addCurveSegments(layers); + } + if(segments.size() != expectedSegments) { + removeCurveSegments(layers); + addCurveSegments(layers); + if(getViewer().getSelection().getPrimarySelection() == getLinkBundle()) { + if(getViewer().hasFocus()) { + applySelectedFocusedEffect(layers); + } else { + applySelectedNotFocusedEffect(layers); + } + } + return; + } + + int segIndex = 0; + double markerSize = getFeedbackMarkerSize(); + double ms2 = markerSize / 2; + // start point + Point2D sp = getFeedbackChangeForBendPoint(START_OF_LINK_BEND_POINT_INDEX) + .applyToPoint(getStartAnchorPoint()); + startFeedbackHandle.setX(sp.getX() - ms2); + startFeedbackHandle.setY(sp.getY() - ms2); + startFeedbackHandle.setWidth(markerSize); + startFeedbackHandle.setHeight(markerSize); + Point2D cp1 = getFeedbackChangeForBendPoint(0).applyToPoint(getBendPointLocation(0)); + Point2D cp2 = getFeedbackChangeForBendPoint(1).applyToPoint(getBendPointLocation(1)); + // handle bend points + for(int i = 2; i < pts; i += 3) { + Point2D ep = getFeedbackChangeForBendPoint(i).applyToPoint(getBendPointLocation(i)); + segments.get(segIndex).update(sp, cp1, cp2, ep, markerSize, markerSize, 0); + sp = ep; + cp1 = getFeedbackChangeForBendPoint(i + 1).applyToPoint(getBendPointLocation(i + 1)); + cp2 = getFeedbackChangeForBendPoint(i + 2).applyToPoint(getBendPointLocation(i + 2)); + segIndex++; + } + // end point + Point2D ep = getFeedbackChangeForBendPoint(END_OF_LINK_BEND_POINT_INDEX) + .applyToPoint(getEndAnchorPoint()); + segments.get(segIndex).update(sp, cp1, cp2, ep, 0, markerSize, getArrowLength()); + endFeedbackHandle.setX(ep.getX() - ms2); + endFeedbackHandle.setY(ep.getY() - ms2); + endFeedbackHandle.setWidth(markerSize); + endFeedbackHandle.setHeight(markerSize); + + // coloring + Paint color = getLineColor(); + IMVCBundle mvcBundle = getMVCBundle(); + if(mvcBundle.hasTag(HIGHLIGHT_INCOMING_LINK_TAG)) { + color = getHighlightIncomingLinkColor(); + } else if(mvcBundle.hasTag(HIGHLIGHT_OUTGOING_LINK_TAG)) { + color = getHighlightOutgoingLinkColor(); + } else if(mvcBundle.hasTag(PRIMARY_SELECTION_TAG)) { + if(mvcBundle.hasTag(FOCUS_TAG)) { + color = getSelectionFocusedColor(); + } else { + color = getSelectionNotFocusedColor(); + } + } + for(CurveSegment seg : segments) { + seg.setStrokeColor(color); + } + } + + /** {@inheritDoc} */ + @Override + public void removeAllVisuals(DiagramLayers layers) { + super.removeAllVisuals(layers); + removeCurveSegments(layers); + } + + /** Removes the curve segments from the scene graph. */ + private void removeCurveSegments(DiagramLayers layers) { + for(CurveSegment cs : segments) { + cs.removeLinkNodes(layers); + cs.removeFeedbackNodes(layers); + } + ILayer l = layers.getLinkInteractionLayer(); + l.remove(startFeedbackHandle); + l.remove(endFeedbackHandle); + segments.clear(); + curvesAddedToSceneGraph = false; + } + + /** {@inheritDoc} */ + @Override + public boolean enableVisual() { + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean enableInteraction() { + return true; + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getStartAnchorPoint() { + IAnchorageMVCBundle start = getLinkBundle().getStartAnchorage(); + Rectangle2D sb = start.getVisual().getCurrentBounds(); + + Point2D target; + int pts = getNumberOfBendPoints(); + if(pts == 0) { + IAnchorageMVCBundle end = getLinkBundle().getEndAnchorage(); + Rectangle2D eb = end.getVisual().getCurrentBounds(); + target = new Point2D(eb.getMinX() + eb.getWidth() / 2, + eb.getMinY() + eb.getHeight() / 2); + } else { + target = getCurrentBendPointLocation(0); + } + return getStartAnchorLocation(sb, target); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getEndAnchorPoint() { + IAnchorageMVCBundle end = getLinkBundle().getEndAnchorage(); + Rectangle2D eb = end.getVisual().getCurrentBounds(); + + Point2D target; + int pts = getNumberOfBendPoints(); + if(pts == 0) { + IAnchorageMVCBundle start = getLinkBundle().getStartAnchorage(); + Rectangle2D sb = start.getVisual().getCurrentBounds(); + target = new Point2D(sb.getMinX() + sb.getWidth() / 2, + sb.getMinY() + sb.getHeight() / 2); + } else { + target = getCurrentBendPointLocation(pts - 1); + } + return getEndAnchorLocation(eb, target); + } + + /** {@inheritDoc} */ + @Override + public void applySelectedFocusedEffect(DiagramLayers layers) { + ILinkMVCBundle bundle = getLinkBundle(); + Paint selectionFocusedColor = getSelectionFocusedColor(); + Paint interactionColor = getInteractionColor(); + for(CurveSegment seg : segments) { + seg.setStrokeColor(selectionFocusedColor); + seg.setClickableLinesColor(interactionColor); + seg.addFeedbackNodes(layers, bundle); + } + ILayer linkInteractionLayer = layers.getLinkInteractionLayer(); + linkInteractionLayer.add(startFeedbackHandle, bundle); + linkInteractionLayer.add(endFeedbackHandle, bundle); + } + + /** {@inheritDoc} */ + @Override + public void applySelectedNotFocusedEffect(DiagramLayers layers) { + removeFeedback(layers, getSelectionNotFocusedColor()); + } + + /** {@inheritDoc} */ + @Override + protected void applyNotSelectedEffect(DiagramLayers layers) { + removeFeedback(layers, getInteractionColor()); + } + + /** Removes the feedback handles and sets the link to the given color. */ + private void removeFeedback(DiagramLayers layers, Paint lineColor) { + Paint selectionNotFocusedColor = lineColor; + Paint interactionColor = getInteractionColor(); + for(CurveSegment seg : segments) { + seg.setStrokeColor(selectionNotFocusedColor); + seg.setClickableLinesColor(interactionColor); + seg.removeFeedbackNodes(layers); + } + ILayer linkInteractionLayer = layers.getLinkInteractionLayer(); + linkInteractionLayer.remove(startFeedbackHandle); + linkInteractionLayer.remove(endFeedbackHandle); + } + + /** + * Returns the anchor location w.r.t. the interface node bonds and the given target location + * (e.g. the first bend-point or the end interface node center point). + */ + protected abstract DiagramCoordinate getStartAnchorLocation(Rectangle2D ifcBounds, + Point2D target); + + /** + * Returns the anchor location w.r.t. the interface node bonds and the given target location + * (e.g. the last bend-point or the start interface node center point). + */ + protected abstract DiagramCoordinate getEndAnchorLocation(Rectangle2D ifcBounds, + Point2D target); + + /** Returns the color to be used when the connection is selected. */ + protected Paint getSelectionFocusedColor() { + return Color.RED; + } + + /** Returns the color to be used when the connection is selected, but not focused. */ + protected Paint getSelectionNotFocusedColor() { + return Color.ORANGE; + } + + /** Returns the color to be used when the connection is not selected. */ + protected Paint getLineColor() { + return Color.BLACK; + } + + /** + * Returns the point of the bend-point at the given index altered by its current feedback + * change. + */ + private Point2D getCurrentBendPointLocation(int bendPointIndex) { + if(bendPointIndex >= 0 && bendPointIndex < getNumberOfBendPoints()) { + Point2D bendPoint = getBendPointLocation(bendPointIndex); + FeedbackChange chg = getFeedbackChangeForBendPoint(bendPointIndex); + double x = bendPoint.getX() + chg.getDeltaX(); + double y = bendPoint.getY() + chg.getDeltaY(); + return new Point2D(x, y); + } + throw new IndexOutOfBoundsException("No bend-point exists at index " + bendPointIndex); + } + + /** + * Returns the model of the bend-point at the given index. Each bend-point must be identified by + * a unique object, which must not change as long as the bend-point exists. + */ + protected abstract Object getBendPointModel(int i); + + /** Returns the number of bend-points. */ + protected abstract int getNumberOfBendPoints(); + + /** Returns the width of the invisible selection line. */ + protected abstract double getInvisibleSelectionLineWidth(); + + /** Creates the curve segment. */ + private void makeCurve(Point2D sp, Point2D cp1, Point2D cp2, Point2D ep, double selWidth, + double bpMarkerSize, double cpMarkerSize, boolean showArrow, boolean useLineArrow, + double arrowLength) { + CurveSegment segVis = new CurveSegment(sp.getX(), sp.getY(), cp1.getX(), cp1.getY(), + cp2.getX(), cp2.getY(), ep.getX(), ep.getY(), selWidth, bpMarkerSize, cpMarkerSize, + showArrow, useLineArrow, arrowLength); + segments.add(segVis); + } + + /** Returns the {@link LinkMVCBundle}. */ + private LinkMVCBundle getLinkBundle() { + return (LinkMVCBundle)getMVCBundle(); + } + + /** {@inheritDoc} */ + @Override + public Rectangle2D getCurrentBounds() { + // connections do not need bounds + return Rectangle2D.EMPTY; + } + + /** {@inheritDoc} */ + @Override + public int getBendPointIndex(Node node) { + int superResult = super.getBendPointIndex(node); + if(superResult != NO_BEND_POINT_INDEX) { + return superResult; + } + for(int i = 0; i < segments.size(); i++) { + CurveSegment cs = segments.get(i); + if(node == cs.getFirstControlPointHandle()) { + return i * 3; + } else if(node == cs.getSecondControlPointHandle()) { + return 1 + i * 3; + } else if(node == cs.getBendPointHandle()) { + return 2 + i * 3; + } else if(node == cs.getVisibleCurve() || node == cs.getClickableCurve()) { + return 2 + i * 3; + } + } + return NO_BEND_POINT_INDEX; + } + + /** {@inheritDoc} */ + @Override + public boolean isBendPointHandle(Node node) { + for(CurveSegment cs : segments) { + if(node == cs.getBendPointHandle() || node == cs.getFirstControlPointHandle() || + node == cs.getSecondControlPointHandle()) { + return true; + } + } + return super.isBendPointHandle(node); + } + + /** {@inheritDoc} */ + @Override + public void requestFocus() { + if(!segments.isEmpty()) { + // segment list was tested for emptiness => get(0) works + segments.get(0).getVisibleCurve().requestFocus(); + } + } + + /** Returns whether the last line segment should show an arrow head marker. */ + protected abstract boolean showArrowOnLastSegment(); + + /** Returns the type of the arrow to be used (line vs. filled). The default is filled. */ + protected boolean useLineArrow() { + return false; + } + + /** Returns the length of the arrow. */ + protected double getArrowLength() { + return 10; + } + + /** {@inheritDoc} */ + @Override + public EDragGesture getDragGesture(Node node, DiagramCoordinate diagramLocation) { + if(node == startFeedbackHandle || node == endFeedbackHandle) { + return EDragGesture.RECONNECT_LINK; + } + for(CurveSegment cs : segments) { + if(node == cs.getBendPointHandle() || node == cs.getFirstControlPointHandle() || + node == cs.getSecondControlPointHandle()) { + return EDragGesture.MOVE_BENDPOINT; + } + if(node == cs.getClickableCurve()) { + return EDragGesture.NEW_BENDPOINT; + } + } + return EDragGesture.NONE; + } + + /** {@inheritDoc} */ + @Override + public void enableFeedback() { + super.enableFeedback(); + for(CurveSegment cs : segments) { + cs.setMouseTransparent(false); + } + } + + /** {@inheritDoc} */ + @Override + public void disableFeedback() { + super.disableFeedback(); + for(CurveSegment cs : segments) { + cs.setMouseTransparent(true); + } + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CurveSegment.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CurveSegment.java new file mode 100644 index 0000000000000000000000000000000000000000..445bc2607cb70ae1c788c27ba9fc637dd7df4956 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/CurveSegment.java @@ -0,0 +1,250 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic; + +import static javafx.scene.paint.Color.BLACK; +import static javafx.scene.paint.Color.GRAY; +import static javafx.scene.paint.Color.RED; +import static javafx.scene.paint.Color.TRANSPARENT; +import static javafx.scene.shape.StrokeLineCap.BUTT; +import static javafx.scene.shape.StrokeLineJoin.ROUND; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers.ILayer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.widgets.LinkArrowWidget; + +import javafx.geometry.Point2D; +import javafx.scene.paint.Paint; +import javafx.scene.shape.CubicCurve; +import javafx.scene.shape.Line; +import javafx.scene.shape.Rectangle; + +/** + * This class encapsulates {@link CubicCurve}s usually needed for every segment: a visible one, a + * bigger and invisible one for mouse pointer hit tests. + */ +final class CurveSegment { + /** The visible curve. */ + private final CubicCurve visibleCurve; + /** The clickable curve. */ + private final CubicCurve clickableCurve; + /** The selection feedback handle rectangle for the first control point. */ + private final Rectangle firstControlPointHandle; + /** The selection feedback handle rectangle for the second control point. */ + private final Rectangle secondControlPointHandle; + /** The selection feedback handle rectangle for the bend point. */ + private final Rectangle bendPointHandle; + /** The visual helper line from the start to the first control point. */ + private final Line helperStart; + /** The visual helper line from the second control point to the end point. */ + private final Line helperEnd; + /** The arrow widget for curved links. */ + private final LinkArrowWidget arrowWidget; + + /** Constructor. */ + public CurveSegment(double sx, double sy, double c1x, double c1y, double c2x, double c2y, + double ex, double ey, double selectionStrokeWidth, double bendPointFeedbackSize, + double controlPointFeedbackSize, boolean showArrow, boolean useLineArrow, + double arrowLength) { + visibleCurve = new CubicCurve(sx, sy, c1x, c1y, c2x, c2y, ex, ey); + visibleCurve.setStroke(BLACK); + visibleCurve.setFill(null); + + clickableCurve = new CubicCurve(sx, sy, c1x, c1y, c2x, c2y, ex, ey); + clickableCurve.setStrokeWidth(selectionStrokeWidth); + clickableCurve.setFill(null); + clickableCurve.setStrokeLineJoin(ROUND); + clickableCurve.setStrokeLineCap(BUTT); + clickableCurve.setStroke(TRANSPARENT); + + double fs2 = bendPointFeedbackSize / 2; + bendPointHandle = + new Rectangle(ex - fs2, ey - fs2, bendPointFeedbackSize, bendPointFeedbackSize); + bendPointHandle.setFill(TRANSPARENT); + bendPointHandle.setStroke(RED); + + double cps2 = controlPointFeedbackSize / 2; + firstControlPointHandle = new Rectangle(c1x - cps2, c1y - cps2, controlPointFeedbackSize, + controlPointFeedbackSize); + firstControlPointHandle.setFill(TRANSPARENT); + firstControlPointHandle.setStroke(GRAY); + + secondControlPointHandle = new Rectangle(c2x - cps2, c2y - cps2, controlPointFeedbackSize, + controlPointFeedbackSize); + secondControlPointHandle.setFill(TRANSPARENT); + secondControlPointHandle.setStroke(GRAY); + + helperStart = new Line(sx, sy, c1x, c1y); + helperStart.setStroke(GRAY); + + helperEnd = new Line(c2x, c2y, ex, ey); + helperEnd.setStroke(GRAY); + + if(showArrow) { + arrowWidget = new LinkArrowWidget(useLineArrow, ex, ey, c2x, c2y, arrowLength); + } else { + arrowWidget = null; + } + } + + /** Returns the visible curve. */ + public CubicCurve getVisibleCurve() { + return visibleCurve; + } + + /** Returns the clickable curve. */ + public CubicCurve getClickableCurve() { + return clickableCurve; + } + + /** Returns the first control point feedback handle. */ + public Rectangle getFirstControlPointHandle() { + return firstControlPointHandle; + } + + /** Returns the second control point feedback handle. */ + public Rectangle getSecondControlPointHandle() { + return secondControlPointHandle; + } + + /** Returns the bend-point feedback handle. */ + public Rectangle getBendPointHandle() { + return bendPointHandle; + } + + /** Returns the helper line of the start. */ + public Line getHelperStart() { + return helperStart; + } + + /** Returns the helper line of the end. */ + public Line getHelperEnd() { + return helperEnd; + } + + /** Sets the color of the visible lines. */ + public void setStrokeColor(Paint color) { + visibleCurve.setStroke(color); + if(arrowWidget != null) { + arrowWidget.setColor(color); + } + } + + /** Sets the color of the invisible, clickable curves. */ + public void setClickableLinesColor(Paint color) { + clickableCurve.setStroke(color); + } + + /** Updates the segment coordinates. */ + public void update(Point2D sp, Point2D cp1, Point2D cp2, Point2D ep, + double bendPointFeedbackSize, double controlPointFeedbackSize, double arrowLength) { + double sx = sp.getX(); + double sy = sp.getY(); + visibleCurve.setStartX(sx); + visibleCurve.setStartY(sy); + double c1x = cp1.getX(); + double c1y = cp1.getY(); + visibleCurve.setControlX1(c1x); + visibleCurve.setControlY1(c1y); + double c2x = cp2.getX(); + double c2y = cp2.getY(); + visibleCurve.setControlX2(c2x); + visibleCurve.setControlY2(c2y); + double ex = ep.getX(); + double ey = ep.getY(); + visibleCurve.setEndX(ex); + visibleCurve.setEndY(ey); + + clickableCurve.setStartX(sx); + clickableCurve.setStartY(sy); + clickableCurve.setControlX1(c1x); + clickableCurve.setControlY1(c1y); + clickableCurve.setControlX2(c2x); + clickableCurve.setControlY2(c2y); + clickableCurve.setEndX(ex); + clickableCurve.setEndY(ey); + + double cps2 = controlPointFeedbackSize / 2; + firstControlPointHandle.setX(c1x - cps2); + firstControlPointHandle.setY(c1y - cps2); + + secondControlPointHandle.setX(c2x - cps2); + secondControlPointHandle.setY(c2y - cps2); + + double bps2 = bendPointFeedbackSize / 2; + bendPointHandle.setX(ex - bps2); + bendPointHandle.setY(ey - bps2); + + helperStart.setStartX(sx); + helperStart.setStartY(sy); + helperStart.setEndX(c1x); + helperStart.setEndY(c1y); + + helperEnd.setStartX(c2x); + helperEnd.setStartY(c2y); + helperEnd.setEndX(ex); + helperEnd.setEndY(ey); + + if(arrowWidget != null) { + arrowWidget.update(ex, ey, c2x, c2y, arrowLength); + } + } + + /** Adds the link nodes of this segment to the link layer node. */ + public void addLinkNodes(DiagramLayers layers, ILinkMVCBundle bundle) { + ILayer linkLayer = layers.getLinkLayer(); + linkLayer.add(visibleCurve, bundle); + linkLayer.add(clickableCurve, bundle); + if(arrowWidget != null) { + linkLayer.add(arrowWidget, bundle); + } + } + + /** Removes the link nodes of this segment from the link layer node. */ + public void removeLinkNodes(DiagramLayers layers) { + ILayer linkLayer = layers.getLinkLayer(); + linkLayer.remove(visibleCurve); + linkLayer.remove(clickableCurve); + if(arrowWidget != null) { + linkLayer.remove(arrowWidget); + } + } + + /** Adds the feedback nodes of this segment to the feedback layer node. */ + public void addFeedbackNodes(DiagramLayers layers, ILinkMVCBundle bundle) { + layers.getVisualFeedbackLayer().add(helperStart, bundle); + layers.getVisualFeedbackLayer().add(helperEnd, bundle); + layers.getLinkInteractionLayer().add(firstControlPointHandle, bundle); + layers.getLinkInteractionLayer().add(bendPointHandle, bundle); + layers.getLinkInteractionLayer().add(secondControlPointHandle, bundle); + } + + /** Removes the feedback nodes of this segment from the feedback layer node. */ + public void removeFeedbackNodes(DiagramLayers layers) { + layers.getVisualFeedbackLayer().remove(helperStart); + layers.getVisualFeedbackLayer().remove(helperEnd); + layers.getLinkInteractionLayer().remove(firstControlPointHandle); + layers.getLinkInteractionLayer().remove(bendPointHandle); + layers.getLinkInteractionLayer().remove(secondControlPointHandle); + } + + /** Sets all elements to the given mouse transparency. */ + public void setMouseTransparent(boolean transparent) { + visibleCurve.setMouseTransparent(transparent); + clickableCurve.setMouseTransparent(transparent); + bendPointHandle.setMouseTransparent(transparent); + firstControlPointHandle.setMouseTransparent(transparent); + secondControlPointHandle.setMouseTransparent(transparent); + if(arrowWidget != null) { + arrowWidget.setMouseTransparent(transparent); + } + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/EllipticBorderLocation.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/EllipticBorderLocation.java new file mode 100644 index 0000000000000000000000000000000000000000..1e9b3d42c7fcd5495004fb30b0c499096a839967 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/EllipticBorderLocation.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic; + +import static java.lang.Math.atan2; +import static java.lang.Math.cos; +import static java.lang.Math.sin; +import static java.util.Objects.requireNonNull; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; + +import javafx.geometry.Dimension2D; +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; + +/** This class encapsulates locations on the border of an ellipse defined by the angular value. */ +public final class EllipticBorderLocation { + /** The angle in degrees. */ + private final double angleInDegree; + /** The radiuses of the ellipse. */ + private final Dimension2D radiuses; + /** + * The correction values used to compute the final location (usually half the size of the object + * located on the border so that it looks centered. + */ + private final Dimension2D correction; + + /** Constructor. */ + public EllipticBorderLocation(double angleInDegree, Dimension2D radiuses, + Dimension2D correction) { + this.angleInDegree = angleInDegree; + this.radiuses = requireNonNull(radiuses); + this.correction = requireNonNull(correction); + } + + /** Returns the angle in degree. */ + public double getAngleInDegree() { + return angleInDegree; + } + + /** Returns the radiuses. */ + public Dimension2D getRadiuses() { + return radiuses; + } + + /** + * Returns the location computed from this {@link EllipticBorderLocation} relative to the + * ellipse center. + */ + public DiagramCoordinate getLocation() { + double rw = radiuses.getWidth(); + double rh = radiuses.getHeight(); + double angle = Math.PI * angleInDegree / 180; + double x = rw - correction.getWidth() / 2 + rw * cos(angle); + double y = rh - correction.getHeight() / 2 - rh * sin(angle); + return new DiagramCoordinate(x, y); + } + + /** + * Returns the closest point on the ellipse bounds inside the given rectangle when pointing at + * the given location. + */ + public static final EllipticBorderLocation getClosestLocationOnBounds(Point2D point, + Rectangle2D ellipse, Dimension2D size) { + double w2 = ellipse.getWidth() / 2; + double h2 = ellipse.getHeight() / 2; + double dx = point.getX() - w2; + double dy = h2 - point.getY(); + + double angle = atan2(dy, dx) * 180.0 / Math.PI; + Dimension2D radiuses = new Dimension2D(w2, h2); + return new EllipticBorderLocation(angle, radiuses, size); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/EllipticContentVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/EllipticContentVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..dc2fddc9cfe5605bc8a5d09dd862845e360b23f5 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/elliptic/EllipticContentVisualBase.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic; + +import static java.lang.Math.atan2; +import static java.lang.Math.toDegrees; +import static javafx.scene.paint.Color.DARKORANGE; +import static javafx.scene.paint.Color.ORANGE; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.visual.elliptic.EllipticBorderLocation.getClosestLocationOnBounds; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.IAngleLayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.ContentVisualBase; + +import javafx.geometry.Bounds; +import javafx.geometry.Dimension2D; +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Ellipse; + +/** Base class for {@link ContentVisualBase content visuals} depicted by ellipses. */ +public abstract class EllipticContentVisualBase extends ContentVisualBase { + /** Constructor. */ + public EllipticContentVisualBase(IContentMVCBundle mvcb) { + super(mvcb, new Ellipse(), new Ellipse()); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + updateNodeProperties(); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getAnchorageLocation(IContentAnchorageVisual visual) { + Rectangle2D pb = getCurrentBounds(); + Dimension2D ell = new Dimension2D(pb.getWidth() / 2, pb.getHeight() / 2); + Dimension2D dim = visual.getDimensions(); + double angleDeg = visual.getLayout(IAngleLayout.class).getAngleInDegree(); + EllipticBorderLocation ebl = new EllipticBorderLocation(angleDeg, ell, dim); + return ebl.getLocation(); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getLinkAnchorage(DiagramCoordinate indication) { + Rectangle2D pb = getCurrentBounds(); + EllipticBorderLocation ebl = getClosestLocationOnBounds( + indication.add(-pb.getMinX(), -pb.getMinY()), pb, new Dimension2D(1, 1)); + return ebl.getLocation().add(pb.getMinX(), pb.getMinY()); + } + + /** {@inheritDoc} */ + @Override + protected Paint getBorderColor() { + return DARKORANGE.darker(); + } + + /** {@inheritDoc} */ + @Override + protected Paint getFillColor() { + return ORANGE.brighter(); + } + + /** Updates the properties of the visual nodes. */ + private void updateNodeProperties() { + Rectangle2D bounds = getCurrentBounds(); + double w2 = bounds.getWidth() / 2; + double h2 = bounds.getHeight() / 2; + Ellipse ellipse = getVisualEllipse(); + ellipse.setCenterX(bounds.getMinX() + w2); + ellipse.setCenterY(bounds.getMinY() + h2); + ellipse.setRadiusX(w2); + ellipse.setRadiusY(h2); + ellipse.setOpacity(getOpacity()); + ellipse.setStroke(getBorderColor()); + ellipse.setStrokeWidth(getBorderWidth()); + ellipse.setStrokeType(getBorderType()); + ellipse.setFill(getFillColor()); + + double lnk = getHitAreaStartLinkSize(); + double res = getHitAreaResizeSize(); + // wild cast works: see constructor + Ellipse hitShape = getHitAreaEllipse(); + hitShape.setCenterX(bounds.getMinX() + w2); + hitShape.setCenterY(bounds.getMinY() + h2); + hitShape.setRadiusX(w2 + lnk + res / 2); + hitShape.setRadiusY(h2 + lnk + res / 2); + Paint interactionColor = getInteractionColor(); + hitShape.setStroke(interactionColor); + hitShape.setFill(interactionColor); + } + + /** {@inheritDoc} */ + @Override + protected EDragGesture dragGestureHitTest(Node node, DiagramCoordinate diagramLocation) { + if(node == getHitAreaShape()) { + Bounds bounds = node.getBoundsInLocal(); + double w = bounds.getWidth(); + double h = bounds.getHeight(); + + double x = diagramLocation.getLocalX(node) - w / 2; + double y = diagramLocation.getLocalY(node) - h / 2; + double res = getHitAreaResizeSize(); + double lnk = getHitAreaStartLinkSize(); + // check for move area + double a = w / 2 - res - lnk; + double b = h / 2 - res - lnk; + double hitMoveArea = (x * x / (a * a) + y * y / (b * b)); + if(hitMoveArea <= 1.0) { + return EDragGesture.MOVE; + } + // check for resize area + a = w / 2 - lnk; + b = h / 2 - lnk; + double hitResizeArea = (x * x / (a * a) + y * y / (b * b)); + if(hitResizeArea <= 1.0) { + // check angle + double angle = (360.0 + toDegrees(atan2(-y, x))) % 360.0; + if(angle <= 45.0 || angle >= 345.0) { + return EDragGesture.RESIZE_H; + } + if(angle >= 300.0) { + return EDragGesture.RESIZE_VH; + } + if(angle >= 225.0) { + return EDragGesture.RESIZE_V; + } + return EDragGesture.NONE; + } + // else startlink + return EDragGesture.NEW_LINK; + } + if(node == getVisualEllipse() || node == text) { + return EDragGesture.MOVE; + } + return EDragGesture.NONE; + } + + /** + * Returns the extension of the hit area in pixel used to trigger link creation. This number + * specifies how many pixels beyond the visible border are used to trigger link creation by a + * mouse drag gesture. + */ + protected double getHitAreaStartLinkSize() { + return getViewer().getFeatures().getMaximumSpacing(); + } + + /** Returns the size of the hit area in pixel used to resize the content visual. */ + protected double getHitAreaResizeSize() { + return getViewer().getFeatures().getMaximumSpacing(); + } + + /** Returns the ellipse visual. */ + protected final Ellipse getVisualEllipse() { + // wild cast works: see constructor + return (Ellipse)getVisualShape(); + } + + /** Returns the ellipse of the hit area. */ + protected final Ellipse getHitAreaEllipse() { + return (Ellipse)getHitAreaShape(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..1f339261e0cee18516e6bb05f07035ad012a2ef4 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/.ratings @@ -0,0 +1,9 @@ +DiamondContentVisualBase.java 214dc886dba3f26a6404db5c521715466ce85522 YELLOW +LineLinkGraph.java 85a06a553f88f7b9fb4bd9c06411725d9fb160fc YELLOW +LineLinkVisualBase.java 41cee7c8258cb65080bfce2a5d785772305a8119 YELLOW +LineSegment.java a8658ec5bcd930d417a148861831b9ebb70bb37d YELLOW +RectangularBorderLocation.java 824472c353534d1094ae4f735a30a231b885f050 YELLOW +RectangularContentAnchorageVisualBase.java 39981dc29cac42d77c6ffe855ecc8ccad1689230 YELLOW +RectangularContentVisualBase.java ccd23400a7ac47573127a39bc68c55c82baa44ad YELLOW +RectangularDiagramAnchorageVisualBase.java 1259d6d110becca9ae02c754036c6693f00de683 YELLOW +RhomboidContentVisualBase.java 7a91e401034acc629179fb1a45416cd519f617d7 YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/DiamondContentVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/DiamondContentVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..214dc886dba3f26a6404db5c521715466ce85522 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/DiamondContentVisualBase.java @@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular.RectangularBorderLocation.getClosestLocationOnBounds; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.ISideLayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.ContentVisualBase; + +import javafx.collections.ObservableList; +import javafx.geometry.Bounds; +import javafx.geometry.Dimension2D; +import javafx.geometry.Rectangle2D; +import javafx.geometry.Side; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.shape.ClosePath; +import javafx.scene.shape.LineTo; +import javafx.scene.shape.MoveTo; +import javafx.scene.shape.Path; +import javafx.scene.shape.PathElement; + +/** Base class for {@link ContentVisualBase content visuals} depicted by a diamond shape. */ +public abstract class DiamondContentVisualBase extends ContentVisualBase { + /** Constructor. */ + public DiamondContentVisualBase(IContentMVCBundle mvcb) { + super(mvcb, new Path(), new Path()); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + + Rectangle2D bounds = getCurrentBounds(); + double leftX = bounds.getMinX(); + double upperY = bounds.getMinY(); + double width = bounds.getWidth(); + double height = bounds.getHeight(); + if(enableVisual()) { + Path path = getVisualPathShape(); + path.setOpacity(getOpacity()); + path.setStroke(getBorderColor()); + path.setStrokeWidth(getBorderWidth()); + path.setStrokeType(getBorderType()); + path.setFill(getFillColor()); + makePath(leftX, upperY, width, height, path); + } + + if(enableInteraction()) { + Path ha = getHitAreaPathShape(); + double lnk = getHitAreaStartLinkSize(); + double resize = getHitAreaResizeSize(); + double lr = lnk + resize; + makePath(leftX - lr, upperY - lr, width + 2 * lr, height + 2 * lr, ha); + Color interactionColor = getInteractionColor(); + ha.setStroke(interactionColor); + ha.setFill(interactionColor); + } + } + + /** Creates the rhomboid shape. */ + private void makePath(double x, double y, double w, double h, Path p) { + double w2 = w / 2; + double h2 = h / 2; + ObservableList<PathElement> pes = p.getElements(); + pes.clear(); + pes.add(new MoveTo(x, y + h2)); + pes.add(new LineTo(x + w2, y + h)); + pes.add(new LineTo(x + w, y + h2)); + pes.add(new LineTo(x + w2, y)); + pes.add(new ClosePath()); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getAnchorageLocation(IContentAnchorageVisual visual) { + Rectangle2D pb = getCurrentBounds(); + double pbw2 = pb.getWidth() / 2; + double pbh2 = pb.getHeight() / 2; + Dimension2D anchorageSize = visual.getDimensions(); + double aw2 = anchorageSize.getWidth() / 2; + double ah2 = anchorageSize.getHeight() / 2; + Side side = visual.getLayout(ISideLayout.class).getSide(); + // Side.TOP + double x = pbw2 - aw2; + double y = -ah2; + if(side == Side.BOTTOM) { + y = pb.getHeight() - ah2; + } else if(side == Side.LEFT) { + x = -aw2; + y = pbh2 - ah2; + } else if(side == Side.RIGHT) { + x = pb.getWidth() - aw2; + y = pbh2 - ah2; + } + return new DiagramCoordinate(x, y); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getLinkAnchorage(DiagramCoordinate indication) { + Rectangle2D b = getCurrentBounds(); + Dimension2D dummy = new Dimension2D(1, 1); + RectangularBorderLocation loc = getClosestLocationOnBounds( + indication.add(-b.getMinX(), -b.getMinY()), b, dummy, dummy); + return loc.getLocation().add(b.getMinX(), b.getMinY()); + } + + /** Returns the dimensions of the corner arcs. */ + protected Dimension2D getCornerArcDimensions() { + return new Dimension2D(15, 15); + } + + /** {@inheritDoc} */ + @Override + protected EDragGesture dragGestureHitTest(Node node, DiagramCoordinate diagramLocation) { + if(node == getHitAreaShape()) { + double x = diagramLocation.getLocalX(node); + double y = diagramLocation.getLocalY(node); + double l = getHitAreaStartLinkSize(); + double r = getHitAreaResizeSize(); + Bounds bounds = node.getBoundsInLocal(); + // check for move area + double inset = l + r; + Rectangle2D moveArea = new Rectangle2D(inset, inset, bounds.getWidth() - 2 * inset, + bounds.getHeight() - 2 * inset); + if(moveArea.contains(x, y)) { + return EDragGesture.MOVE; + } + if(x < l || y < l || x > bounds.getWidth() - l || y > bounds.getHeight() - l) { + return EDragGesture.NEW_LINK; + } + if(x > bounds.getWidth() - inset && y >= l && y <= bounds.getHeight() - inset) { + return EDragGesture.RESIZE_H; + } + if(y > bounds.getHeight() - inset && x >= l && x <= bounds.getWidth() - inset) { + return EDragGesture.RESIZE_V; + } + if(x > bounds.getWidth() - inset && y > bounds.getHeight() - inset) { + return EDragGesture.RESIZE_VH; + } + return EDragGesture.NONE; + } + if(node == getVisualShape() || node == text) { + return EDragGesture.MOVE; + } + return EDragGesture.NONE; + } + + /** Returns the visual path shape. */ + protected final Path getVisualPathShape() { + // wild cast works: see constructor + return (Path)getVisualShape(); + } + + /** Returns the hit area path shape. */ + protected final Path getHitAreaPathShape() { + // wild cast works: see constructor + return (Path)getHitAreaShape(); + } + + /** + * Returns the extension of the hit area in pixel used to trigger link creation. This number + * specifies how many pixels beyond the visible border are used to trigger link creation by a + * mouse drag gesture. + */ + protected double getHitAreaStartLinkSize() { + return getViewer().getFeatures().getMaximumSpacing(); + } + + /** Returns the size of the hit area in pixel used to resize the content visual. */ + protected double getHitAreaResizeSize() { + return getViewer().getFeatures().getMaximumSpacing(); + } + + /** {@inheritDoc} */ + @Override + protected DiagramCoordinate getTextAnchorLocation() { + DiagramCoordinate textAnchorLocation = super.getTextAnchorLocation(); + return textAnchorLocation.add(getCurrentBounds().getWidth() / 5, 0); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/LineLinkGraph.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/LineLinkGraph.java new file mode 100644 index 0000000000000000000000000000000000000000..85a06a553f88f7b9fb4bd9c06411725d9fb160fc --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/LineLinkGraph.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javafx.scene.shape.Line; +import javafx.util.Pair; + +/** This class represents all rectangular lines. */ +public class LineLinkGraph { + /** Maps the LineSegment to its Lines. */ + private Map<LineSegment, List<Line>> segment2lines = new HashMap<>(); + /** Maps the Line to its LineSegment. */ + private Map<Line, LineSegment> line2segment = new HashMap<>(); + /** Maps the Line to every Line Pair that is interrupted by it. */ + private Map<Line, Set<Pair<Line, Line>>> line2interruptedLine = new HashMap<>(); + + /** Adds a new LineSegment to the graph. */ + public void addSegment(LineSegment seg, List<Line> lines) { + segment2lines.put(seg, lines); + for(Line l : lines) { + line2segment.put(l, seg); + } + } + + /** Removes the LineSegment from the graph. */ + public void removeSegment(LineSegment seg, List<Line> lines) { + segment2lines.remove(seg); + for(Line l : lines) { + line2segment.remove(l); + } + } + + /** Adds a new interrupted line. */ + public void addInterruptedLine(Line line, Line interruptedLine1, Line interruptedLine2) { + Set<Pair<Line, Line>> set = line2interruptedLine.get(line); + if(set == null) + set = new HashSet<>(); + set.add(new Pair<>(interruptedLine1, interruptedLine2)); + line2interruptedLine.put(line, set); + } + + /** Adds new interrupted lines. */ + public void addInterruptedLines(Map<Line, Pair<Line, Line>> lines) { + for(Line line : lines.keySet()) { + Pair<Line, Line> pair = lines.get(line); + addInterruptedLine(line, pair.getKey(), pair.getValue()); + } + } + + /** Returns line2segment. */ + public Map<Line, LineSegment> getLine2segment() { + return line2segment; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/LineLinkVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/LineLinkVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..41cee7c8258cb65080bfce2a5d785772305a8119 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/LineLinkVisualBase.java @@ -0,0 +1,447 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.FOCUS_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.PRIMARY_SELECTION_TAG; + +import java.util.LinkedList; +import java.util.List; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers.ILayer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.FeedbackChange; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.impl.LinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.LinkVisualBase; + +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; + +/** Base class for {@link LinkVisualBase link visuals} with straight lines. */ +public abstract class LineLinkVisualBase extends LinkVisualBase { + /** Stores the visual segments. */ + private final List<LineSegment> segments = new LinkedList<>(); + /** Flag storing if the lines have been added. */ + private boolean linesAddedToSceneGraph = false; + + /** Constructor. */ + public LineLinkVisualBase(ILinkMVCBundle mvcb) { + super(mvcb); + } + + /** Creates the line segments and adds them to the scene graph. */ + private void addLineSegments(DiagramLayers layers) { + Object bpModel = null; + double feedbackSize = getFeedbackMarkerSize(); + double fs2 = feedbackSize / 2; + // start point + Point2D startAnchorPoint = getFeedbackChangeForBendPoint(START_OF_LINK_BEND_POINT_INDEX) + .applyToPoint(getStartAnchorPoint()); + double sx = startAnchorPoint.getX(); + double sy = startAnchorPoint.getY(); + startFeedbackHandle.setX(sx - fs2); + startFeedbackHandle.setY(sy - fs2); + startFeedbackHandle.setWidth(feedbackSize); + startFeedbackHandle.setHeight(feedbackSize); + // handle bend points + int pts = getNumberOfBendPoints(); + for(int i = 0; i < pts; i++) { + Point2D bp = getBendPointLocation(i); + FeedbackChange chg = getFeedbackChangeForBendPoint(i); + double nx = bp.getX() + chg.getDeltaX(); + double ny = bp.getY() + chg.getDeltaY(); + segments.add(new LineSegment(bpModel, sx, sy, nx, ny, getInvisibleSelectionLineWidth(), + feedbackSize, false, false, 0, getLabelText(i, pts + 1))); + sx = nx; + sy = ny; + bpModel = getBendPointModel(i); + } + // end point + Point2D endPoint = getFeedbackChangeForBendPoint(END_OF_LINK_BEND_POINT_INDEX) + .applyToPoint(getEndAnchorPoint()); + segments.add(new LineSegment(bpModel, sx, sy, endPoint.getX(), endPoint.getY(), + getInvisibleSelectionLineWidth(), 0, showArrowOnLastSegment(), useLineArrow(), + getArrowLength(), getLabelText(pts, pts + 1))); + endFeedbackHandle.setX(endPoint.getX() - fs2); + endFeedbackHandle.setY(endPoint.getY() - fs2); + endFeedbackHandle.setWidth(feedbackSize); + endFeedbackHandle.setHeight(feedbackSize); + + ILinkMVCBundle bundle = getLinkBundle(); + int numberOfSegs = segments.size(); + for(int i = 0; i < numberOfSegs; i++) { + LineSegment ls = segments.get(i); + ls.addLinkNodes(layers, bundle, getLabelText(i, numberOfSegs)); + } + linesAddedToSceneGraph = true; + } + + /** {@inheritDoc} */ + @Override + public void removeAllVisuals(DiagramLayers layers) { + super.removeAllVisuals(layers); + removeLineSegments(layers); + } + + /** Removes the line segments from the scene graph. */ + private void removeLineSegments(DiagramLayers layers) { + for(LineSegment ls : segments) { + ls.removeLinkNodes(layers); + ls.removeFeedbackNodes(layers); + } + ILayer linkInteractionLayer = layers.getLinkInteractionLayer(); + linkInteractionLayer.remove(startFeedbackHandle); + linkInteractionLayer.remove(endFeedbackHandle); + segments.clear(); + linesAddedToSceneGraph = false; + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + if(!enableVisual()) { + if(linesAddedToSceneGraph) { + removeLineSegments(layers); + } + return; + } + + int pts = getNumberOfBendPoints(); + int expectedSegments = 1 + pts; + if(!linesAddedToSceneGraph) { + addLineSegments(layers); + } + if(segments.size() != expectedSegments) { + removeLineSegments(layers); + addLineSegments(layers); + if(getViewer().getSelection().getPrimarySelection() == getLinkBundle()) { + if(getViewer().hasFocus()) { + applySelectedFocusedEffect(layers); + } else { + applySelectedNotFocusedEffect(layers); + } + } + return; + } + int segIndex = 0; + double feedbackSize = getFeedbackMarkerSize(); + double fs2 = feedbackSize / 2; + // start point + Point2D sp = getFeedbackChangeForBendPoint(START_OF_LINK_BEND_POINT_INDEX) + .applyToPoint(getStartAnchorPoint()); + startFeedbackHandle.setX(sp.getX() - fs2); + startFeedbackHandle.setY(sp.getY() - fs2); + startFeedbackHandle.setWidth(feedbackSize); + startFeedbackHandle.setHeight(feedbackSize); + // handle bend points + for(int i = 0; i < pts; i++) { + Point2D ep = getFeedbackChangeForBendPoint(i).applyToPoint(getBendPointLocation(i)); + segments.get(segIndex).update(sp, ep, feedbackSize, 0, getLabelText(i, pts)); + sp = ep; + segIndex++; + } + // end point + Point2D ep = getFeedbackChangeForBendPoint(END_OF_LINK_BEND_POINT_INDEX) + .applyToPoint(getEndAnchorPoint()); + segments.get(segIndex).update(sp, ep, 0, getArrowLength(), getLabelText(pts, pts)); + endFeedbackHandle.setX(ep.getX() - fs2); + endFeedbackHandle.setY(ep.getY() - fs2); + endFeedbackHandle.setWidth(feedbackSize); + endFeedbackHandle.setHeight(feedbackSize); + + Paint color = getLineColor(); + IMVCBundle mvcBundle = getMVCBundle(); + if(mvcBundle.hasTag(HIGHLIGHT_INCOMING_LINK_TAG)) { + color = getHighlightIncomingLinkColor(); + } else if(mvcBundle.hasTag(HIGHLIGHT_OUTGOING_LINK_TAG)) { + color = getHighlightOutgoingLinkColor(); + } else if(mvcBundle.hasTag(PRIMARY_SELECTION_TAG)) { + if(mvcBundle.hasTag(FOCUS_TAG)) { + color = getSelectionFocusedColor(); + } else { + color = getSelectionNotFocusedColor(); + } + } + for(LineSegment seg : segments) { + seg.setStrokeColor(color); + } + } + + /** {@inheritDoc} */ + @Override + public boolean enableVisual() { + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean enableInteraction() { + return true; + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getStartAnchorPoint() { + IAnchorageMVCBundle start = getLinkBundle().getStartAnchorage(); + Rectangle2D sb = start.getVisual().getCurrentBounds(); + + DiagramCoordinate target; + int pts = getNumberOfBendPoints(); + if(pts == 0) { + IAnchorageMVCBundle end = getLinkBundle().getEndAnchorage(); + Rectangle2D eb = end.getVisual().getCurrentBounds(); + target = new DiagramCoordinate(eb.getMinX() + eb.getWidth() / 2, + eb.getMinY() + eb.getHeight() / 2); + } else { + target = getCurrentBendPointLocation(0); + } + return getStartAnchorLocation(sb, target); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getEndAnchorPoint() { + IAnchorageMVCBundle end = getLinkBundle().getEndAnchorage(); + Rectangle2D eb = end.getVisual().getCurrentBounds(); + + DiagramCoordinate target; + int pts = getNumberOfBendPoints(); + if(pts == 0) { + IAnchorageMVCBundle start = getLinkBundle().getStartAnchorage(); + Rectangle2D sb = start.getVisual().getCurrentBounds(); + target = new DiagramCoordinate(sb.getMinX() + sb.getWidth() / 2, + sb.getMinY() + sb.getHeight() / 2); + } else { + target = getCurrentBendPointLocation(pts - 1); + } + return getEndAnchorLocation(eb, target); + } + + /** {@inheritDoc} */ + @Override + public void applySelectedFocusedEffect(DiagramLayers layers) { + ILinkMVCBundle bundle = getLinkBundle(); + Paint interactionColor = getInteractionColor(); + Paint selectionFocusedColor = getSelectionFocusedColor(); + for(LineSegment seg : segments) { + seg.setStrokeColor(selectionFocusedColor); + seg.setClickableLinesColor(interactionColor); + seg.addFeedbackNodes(layers, bundle); + } + ILayer linkInteractionLayer = layers.getLinkInteractionLayer(); + linkInteractionLayer.add(startFeedbackHandle, bundle); + linkInteractionLayer.add(endFeedbackHandle, bundle); + } + + /** {@inheritDoc} */ + @Override + public void applySelectedNotFocusedEffect(DiagramLayers layers) { + removeFeedback(layers, getSelectionNotFocusedColor()); + } + + /** {@inheritDoc} */ + @Override + protected void applyNotSelectedEffect(DiagramLayers layers) { + removeFeedback(layers, getLineColor()); + } + + /** Removes the feedback handles and sets the link to the given color. */ + private void removeFeedback(DiagramLayers layers, Paint lineColor) { + Paint interactionColor = getInteractionColor(); + for(LineSegment seg : segments) { + seg.setStrokeColor(lineColor); + seg.setClickableLinesColor(interactionColor); + seg.removeFeedbackNodes(layers); + } + ILayer linkInteractionLayer = layers.getLinkInteractionLayer(); + linkInteractionLayer.remove(startFeedbackHandle); + linkInteractionLayer.remove(endFeedbackHandle); + } + + /** + * Returns the anchor location w.r.t. the interface node bonds and the given target location + * (e.g. the first bend-point or the end interface node center point). + */ + protected abstract DiagramCoordinate getStartAnchorLocation(Rectangle2D ifcBounds, + DiagramCoordinate target); + + /** + * Returns the anchor location w.r.t. the interface node bonds and the given target location + * (e.g. the last bend-point or the start interface node center point). + */ + protected abstract DiagramCoordinate getEndAnchorLocation(Rectangle2D ifcBounds, + DiagramCoordinate target); + + /** Returns the color to be used when the connection is selected and focused. */ + protected Paint getSelectionFocusedColor() { + return Color.RED; + } + + /** Returns the color to be used when the connection is selected, but not focused. */ + protected Paint getSelectionNotFocusedColor() { + return Color.ORANGE; + } + + /** Returns the color to be used when the connection is not selected. */ + protected Paint getLineColor() { + return Color.BLACK; + } + + /** + * Returns the point of the bend-point at the given index altered by its current feedback + * change. + */ + private DiagramCoordinate getCurrentBendPointLocation(int bendPointIndex) { + if(bendPointIndex >= 0 && bendPointIndex < getNumberOfBendPoints()) { + Point2D bendPoint = getBendPointLocation(bendPointIndex); + FeedbackChange chg = getFeedbackChangeForBendPoint(bendPointIndex); + double x = bendPoint.getX() + chg.getDeltaX(); + double y = bendPoint.getY() + chg.getDeltaY(); + return new DiagramCoordinate(x, y); + } + throw new IndexOutOfBoundsException("No bend-point exists at index " + bendPointIndex); + } + + /** + * Returns the model of the bend-point at the given index. Each bend-point must be identified by + * a unique object, which must not change as long as the bend-point exists. + */ + protected abstract Object getBendPointModel(int i); + + /** Returns the number of bend-points. */ + protected abstract int getNumberOfBendPoints(); + + /** Returns whether the last line segment should show an arrow head marker. */ + protected abstract boolean showArrowOnLastSegment(); + + /** Returns the type of the arrow to be used (line vs. filled). The default is filled. */ + protected boolean useLineArrow() { + return false; + } + + /** Returns the length of the arrow. */ + protected double getArrowLength() { + return 10; + } + + /** Returns the width of the invisible selection line. */ + protected abstract double getInvisibleSelectionLineWidth(); + + /** + * Returns the label text. + * + * @param currentSegment + * the current segment + * @param lastSegment + * the last index of a segment + * @return the label text for the current segment or null for no label + */ + protected String getLabelText(int currentSegment, int lastSegment) { + return null; + } + + /** Returns the {@link LinkMVCBundle}. */ + private LinkMVCBundle getLinkBundle() { + return (LinkMVCBundle)getMVCBundle(); + } + + /** {@inheritDoc} */ + @Override + public Rectangle2D getCurrentBounds() { + // connections do not need bounds + return Rectangle2D.EMPTY; + } + + /** {@inheritDoc} */ + @Override + public int getBendPointIndex(Node node) { + int superResult = super.getBendPointIndex(node); + if(superResult != NO_BEND_POINT_INDEX) { + return superResult; + } + for(int i = 0; i < segments.size(); i++) { + LineSegment ls = segments.get(i); + if(node == ls.getFeedbackHandle() || node == ls.getClickableLine() || + node == ls.getVisibleLine()) { + return i; + } + } + return NO_BEND_POINT_INDEX; + } + + /** {@inheritDoc} */ + @Override + public boolean isBendPointHandle(Node node) { + for(LineSegment ls : segments) { + if(node == ls.getFeedbackHandle()) { + return true; + } + if(node == ls.getClickableLine() || node == ls.getVisibleLine()) { + return false; + } + } + return super.isBendPointHandle(node); + } + + /** {@inheritDoc} */ + @Override + public void requestFocus() { + if(!segments.isEmpty()) { + // segments list tested for emptiness => get(0) works + segments.get(0).getVisibleLine().requestFocus(); + } + } + + /** {@inheritDoc} */ + @Override + public EDragGesture getDragGesture(Node node, DiagramCoordinate diagramLocation) { + if(node == startFeedbackHandle || node == endFeedbackHandle) { + return EDragGesture.RECONNECT_LINK; + } + for(LineSegment ls : segments) { + if(node == ls.getFeedbackHandle()) { + return EDragGesture.MOVE_BENDPOINT; + } + if(node == ls.getClickableLine()) { + return EDragGesture.NEW_BENDPOINT; + } + } + return EDragGesture.NONE; + } + + /** {@inheritDoc} */ + @Override + public void enableFeedback() { + super.enableFeedback(); + for(LineSegment ls : segments) { + ls.setMouseTransparent(false); + } + } + + /** {@inheritDoc} */ + @Override + public void disableFeedback() { + super.disableFeedback(); + for(LineSegment ls : segments) { + ls.setMouseTransparent(true); + } + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/LineSegment.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/LineSegment.java new file mode 100644 index 0000000000000000000000000000000000000000..a8658ec5bcd930d417a148861831b9ebb70bb37d --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/LineSegment.java @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular; + +import static javafx.scene.paint.Color.TRANSPARENT; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers.ILayer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.widgets.LinkArrowWidget; + +import javafx.geometry.Point2D; +import javafx.geometry.VPos; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Line; +import javafx.scene.shape.Rectangle; +import javafx.scene.text.Text; +import javafx.scene.text.TextAlignment; + +/** + * This class encapsulates {@link Line}s usually needed for every segment: a visible one, a + * bigger and invisible one for mouse pointer hit tests. It also provides access to the + * selection feedback handle of the segment, which is {@code null} for the last segment, since + * this one ends in an interface node. + */ +final class LineSegment { + /** + * The bend-point model this segments originates from or {@code null} if it is the first + * segment. + */ + private final Object bendPointModel; + /** The visible line. */ + private final Line visibleLine; + /** The invisible line. */ + private final Line clickableLine; + /** The selection feedback handle rectangle. */ + private final Rectangle feedbackHandle; + /** The arrow head widget. */ + private final LinkArrowWidget arrowWidget; + /** Stores the label. */ + private Text label; + + /** Constructor. */ + public LineSegment(double sx, double sy, double ex, double ey, double clickableWidth, + double markerSize, boolean showArrow, boolean useLineArrow, double arrowLength, + String labelText) { + this(null, sx, sy, ex, ey, clickableWidth, markerSize, showArrow, useLineArrow, arrowLength, + labelText); + } + + /** Constructor. */ + public LineSegment(Object bendPointModel, double sx, double sy, double ex, double ey, + double clickableWidth, double feedbackSize, boolean showArrow, boolean useLineArrow, + double arrowLength, String labelText) { + this.bendPointModel = bendPointModel; + + this.visibleLine = new Line(sx, sy, ex, ey); + + this.clickableLine = new Line(sx, sy, ex, ey); + clickableLine.setStroke(TRANSPARENT); + clickableLine.setStrokeWidth(clickableWidth); + + double fs2 = feedbackSize / 2; + this.feedbackHandle = new Rectangle(ex - fs2, ey - fs2, feedbackSize, feedbackSize); + feedbackHandle.setFill(TRANSPARENT); + feedbackHandle.setStroke(Color.RED); + + if(showArrow) { + arrowWidget = new LinkArrowWidget(useLineArrow, ex, ey, sx, sy, arrowLength); + } else { + arrowWidget = null; + } + if(labelText != null) { + label = new Text(labelText); + label.setTextAlignment(TextAlignment.CENTER); + label.setTextOrigin(VPos.TOP); + } else { + label = null; + } + } + + /** Returns the bend point model. */ + public Object getBendPointModel() { + return bendPointModel; + } + + /** Returns the visible line. */ + public Line getVisibleLine() { + return visibleLine; + } + + /** Returns the invisible, clickable line. */ + public Line getClickableLine() { + return clickableLine; + } + + /** Returns the feedback handle. */ + public Rectangle getFeedbackHandle() { + return feedbackHandle; + } + + /** Adds the link nodes of this segment to the link layer node. */ + public void addLinkNodes(DiagramLayers layers, ILinkMVCBundle bundle, String labelText) { + ILayer visualLayer = layers.getLinkLayer(); + visualLayer.add(visibleLine, bundle); + if(arrowWidget != null) { + visualLayer.add(arrowWidget, bundle); + } + if(label != null) { + label.setText(labelText); + double lx = (visibleLine.getStartX() + visibleLine.getEndX() - + label.getBoundsInLocal().getWidth()) / 2; + double ly = (visibleLine.getStartY() + visibleLine.getEndY()) / 2; + label.setX(lx); + label.setY(ly); + layers.getTextLayer().add(label, bundle); + } + ILayer interactionLayer = layers.getLinkInteractionLayer(); + interactionLayer.add(clickableLine, bundle); + } + + /** Removes the link nodes of this segment from the link layer node. */ + public void removeLinkNodes(DiagramLayers layers) { + ILayer visualLayer = layers.getLinkLayer(); + visualLayer.remove(visibleLine); + if(arrowWidget != null) { + visualLayer.remove(arrowWidget); + } + if(label != null) { + layers.getTextLayer().remove(label); + } + ILayer interactionLayer = layers.getLinkInteractionLayer(); + interactionLayer.remove(clickableLine); + } + + /** Adds the feedback nodes of this segment to the feedback layer node. */ + public void addFeedbackNodes(DiagramLayers layers, ILinkMVCBundle bundle) { + layers.getLinkInteractionLayer().add(feedbackHandle, bundle); + } + + /** Removes the feedback nodes of this segment from the feedback layer node. */ + public void removeFeedbackNodes(DiagramLayers layers) { + layers.getLinkInteractionLayer().remove(feedbackHandle); + } + + /** Sets the color of the visible lines. */ + public void setStrokeColor(Paint color) { + visibleLine.setStroke(color); + if(arrowWidget != null) { + arrowWidget.setColor(color); + } + } + + /** Sets the color of the invisible, clickable lines. */ + public void setClickableLinesColor(Paint color) { + clickableLine.setStroke(color); + } + + /** Updates the line segment. */ + public void update(Point2D sp, Point2D ep, double feedbackSize, double arrowLength, + String labelText) { + double sx = sp.getX(); + double sy = sp.getY(); + visibleLine.setStartX(sx); + visibleLine.setStartY(sy); + clickableLine.setStartX(sx); + clickableLine.setStartY(sy); + + double ex = ep.getX(); + double ey = ep.getY(); + visibleLine.setEndX(ex); + visibleLine.setEndY(ey); + clickableLine.setEndX(ex); + clickableLine.setEndY(ey); + + double fs2 = feedbackSize / 2; + feedbackHandle.setX(ex - fs2); + feedbackHandle.setY(ey - fs2); + feedbackHandle.setWidth(feedbackSize); + feedbackHandle.setHeight(feedbackSize); + + if(arrowWidget != null) { + arrowWidget.update(ex, ey, sx, sy, arrowLength); + } + if(label != null) { + label.setText(labelText); + double lx = (sx + ex - label.getBoundsInLocal().getWidth()) / 2; + double ly = (sy + ey) / 2; + label.setX(lx); + label.setY(ly); + } + } + + /** Sets all elements to the given mouse transparency. */ + public void setMouseTransparent(boolean transparent) { + visibleLine.setMouseTransparent(transparent); + clickableLine.setMouseTransparent(transparent); + feedbackHandle.setMouseTransparent(transparent); + if(arrowWidget != null) { + arrowWidget.setMouseTransparent(transparent); + } + if(label != null) { + label.setMouseTransparent(transparent); + } + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularBorderLocation.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularBorderLocation.java new file mode 100644 index 0000000000000000000000000000000000000000..824472c353534d1094ae4f735a30a231b885f050 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularBorderLocation.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular; + +import static java.lang.Math.hypot; +import static java.util.Objects.requireNonNull; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; + +import javafx.geometry.Dimension2D; +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; +import javafx.geometry.Side; + +/** + * This class encapsulates locations on the border of a rectangle defined by the side and offset + * value. It can be used to compute x-y-locations relative to the upper-left corner of the + * rectangle. + * <P> + * The {@link #getClosestLocationOnBounds(Point2D, Rectangle2D, Dimension2D, Dimension2D)} method + * allows to construct a {@link RectangularBorderLocation} given a rectangle and a location. The + * computed location will be on the rectangle and it will be the closest of those locations to the + * given point. + */ +public final class RectangularBorderLocation { + /** The side value. */ + private final Side side; + /** The offset value. */ + private final double offset; + /** The rectangle used for the computation. */ + private final Rectangle2D rectangle; + /** + * The correction values used to compute the final location (usually half the size of the object + * located on the border so that it looks centered. + */ + private final Dimension2D anchorageSize; + + /** Constructor. */ + public RectangularBorderLocation(Side side, double offset, Rectangle2D rectangle, + Dimension2D anchorageSize) { + this.side = requireNonNull(side); + this.offset = offset; + this.rectangle = requireNonNull(rectangle); + this.anchorageSize = requireNonNull(anchorageSize); + } + + /** Returns the side. */ + public Side getSide() { + return side; + } + + /** Returns the offset. */ + public double getOffset() { + return offset; + } + + /** + * Returns the location computed from this {@link RectangularBorderLocation} relative to the + * upper-left corner of the {@link #rectangle}. + */ + public DiagramCoordinate getLocation() { + if(side == Side.TOP) { + return new DiagramCoordinate(offset, -anchorageSize.getHeight() / 2); + } + if(side == Side.LEFT) { + return new DiagramCoordinate(-anchorageSize.getWidth() / 2, offset); + } + if(side == Side.BOTTOM) { + return new DiagramCoordinate(offset, + rectangle.getHeight() - anchorageSize.getHeight() / 2); + } + // Side.RIGHT + return new DiagramCoordinate(rectangle.getWidth() - anchorageSize.getWidth() / 2, offset); + } + + /** + * Returns the closest point on the rectangle bounds when pointing at the given location. Only + * locations with distances {@code horizontalSpan} and {@code verticalSpan} are considered. + */ + @Deprecated + public static final RectangularBorderLocation getClosestLocationOnBounds(Point2D p, + Rectangle2D rectangle, Dimension2D span) { + LocationOnBorderComputation comp = + new LocationOnBorderComputation(p, rectangle, span, span); + RectangularBorderLocation result = new RectangularBorderLocation(comp.getResultSide(), + comp.getResultOffset(), rectangle, span); + return result; + } + + /** + * Returns the closest point on the rectangle bounds when pointing at the given location. + */ + public static final RectangularBorderLocation getClosestLocationOnBounds(Point2D p, + Rectangle2D rectangle, Dimension2D anchorageSize, Dimension2D span) { + LocationOnBorderComputation comp = + new LocationOnBorderComputation(p, rectangle, anchorageSize, span); + RectangularBorderLocation result = new RectangularBorderLocation(comp.getResultSide(), + comp.getResultOffset(), rectangle, anchorageSize); + return result; + } + + /** Class for efficiently computing the closest viable position on the parent bounds. */ + private static class LocationOnBorderComputation { + /** The left-right result offset. */ + private double leftRightOffset; + /** The left-right result side. */ + private Side leftRightSide; + /** The left-right distance. */ + private double leftRightDistance; + + /** The top-bottom result offset. */ + private double topBottomOffset; + /** The top-bottom result side. */ + private Side topBottomSide; + /** The top-bottom distance. */ + private double topBottomDistance; + + /** + * Constructor, which computes the closest location on the given border to the given point. + */ + public LocationOnBorderComputation(Point2D pt, Rectangle2D rectangle, + Dimension2D anchorageSize, Dimension2D snap) { + // divide the problem by reducing to horizontal and vertical sub-problems + computeLeftRight(pt, rectangle, anchorageSize.getHeight(), snap.getHeight()); + computeTopBottom(pt, rectangle, anchorageSize.getWidth(), snap.getWidth()); + } + + /** Computes the minimal location on the top or bottom border. */ + private void computeTopBottom(Point2D pt, Rectangle2D rect, double anchorageWidth, + double snapWidth) { + // determine top or bottom + topBottomSide = Side.TOP; + double borderY = 0; + if(pt.getY() > rect.getHeight() / 2) { + topBottomSide = Side.BOTTOM; + borderY = rect.getHeight(); + } + // setup helper variables + double borderWidth = rect.getWidth(); + double correctedX = pt.getX() - anchorageWidth / 2; + double distanceY = pt.getY() - borderY; + + // scan for closest border location + double xOffset = 0; + double currentDistance = hypot(correctedX, distanceY); + for(double nextOffset = snapWidth; nextOffset < borderWidth; nextOffset += snapWidth) { + double nextDistance = hypot(correctedX - nextOffset, distanceY); + if(nextDistance > currentDistance) { + // distance increased => solution found + break; + } + xOffset = nextOffset; + currentDistance = nextDistance; + } + // record result + topBottomOffset = xOffset; + topBottomDistance = currentDistance; + } + + /** Computes the minimal location on the left or right border. */ + private void computeLeftRight(Point2D pt, Rectangle2D rect, double anchorageHeight, + double snapHeight) { + // determine left or right + leftRightSide = Side.LEFT; + double borderX = 0; + if(pt.getX() > rect.getWidth() / 2) { + leftRightSide = Side.RIGHT; + borderX = rect.getWidth(); + } + // setup helper variables + double borderHeight = rect.getHeight(); + double correctedY = pt.getY() - anchorageHeight / 2; + double distanceX = pt.getX() - borderX; + // scan for closest location + double yOffset = 0; + double currentDistance = hypot(distanceX, correctedY); + for(double nextOffset = snapHeight; nextOffset < borderHeight; nextOffset += + snapHeight) { + double nextDistance = hypot(distanceX, correctedY - nextOffset); + if(nextDistance > currentDistance) { + // distance increased => solution found + break; + } + yOffset = nextOffset; + currentDistance = nextDistance; + } + // record result + leftRightOffset = yOffset; + leftRightDistance = currentDistance; + } + + /** Returns the side of the result. */ + public Side getResultSide() { + if(leftRightDistance <= topBottomDistance) { + return leftRightSide; + } + return topBottomSide; + } + + /** Returns the offset of the result. */ + public double getResultOffset() { + if(leftRightDistance <= topBottomDistance) { + return leftRightOffset; + } + return topBottomOffset; + } + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return side.toString() + ":" + offset; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentAnchorageVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentAnchorageVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..39981dc29cac42d77c6ffe855ecc8ccad1689230 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentAnchorageVisualBase.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.ContentAnchorageVisualBase; + +import javafx.geometry.Insets; +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; + +/** Base class for {@link IContentAnchorageVisual}s depicted as rectangles (or squares). */ +public abstract class RectangularContentAnchorageVisualBase extends ContentAnchorageVisualBase { + /** Constructor. */ + public RectangularContentAnchorageVisualBase(IContentAnchorageMVCBundle mvcb) { + super(mvcb, new Rectangle(), new Rectangle()); + } + + /** Returns the visual rectangle. */ + protected final Rectangle getVisualRectangle() { + // wild cast works: see constructor + return (Rectangle)getVisualShape(); + } + + /** Returns the hit area rectangle. */ + protected final Rectangle getHitAreaRectangle() { + // wild cast works: see constructor + return (Rectangle)getHitAreaShape(); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + + Rectangle2D bounds = getCurrentBounds(); + if(enableVisual()) { + Insets i = getInsets(); + Rectangle rectangle = getVisualRectangle(); + rectangle.setX(bounds.getMinX() + i.getLeft()); + rectangle.setY(bounds.getMinY() + i.getTop()); + double w = bounds.getWidth() - i.getLeft() - i.getRight(); + rectangle.setWidth(w); + double h = bounds.getHeight() - i.getTop() - i.getBottom(); + rectangle.setHeight(h); + rectangle.setOpacity(getOpacity()); + rectangle.setStrokeWidth(getBorderWidth()); + rectangle.setStrokeType(getBorderType()); + if(getMVCBundle().hasTag(HIGHLIGHT_INCOMING_LINK_TAG)) { + rectangle.setFill(getHighlightIncomingLinkColor()); + rectangle.setStroke(getHighlightIncomingLinkBorderColor()); + } else if(getMVCBundle().hasTag(HIGHLIGHT_OUTGOING_LINK_TAG)) { + rectangle.setFill(getHighlightOutgoingLinkColor()); + rectangle.setStroke(getHighlightOutgoingLinkBorderColor()); + } else { + rectangle.setFill(getFillColor()); + rectangle.setStroke(getBorderColor()); + } + } + + if(enableInteraction()) { + // wild cast works: see addNodes(...) + Rectangle ha = getHitAreaRectangle(); + double hitAreaOutSet = getHitAreaOutset(); + ha.setX(bounds.getMinX() - hitAreaOutSet); + ha.setY(bounds.getMinY() - hitAreaOutSet); + ha.setWidth(bounds.getWidth() + 2 * hitAreaOutSet); + ha.setHeight(bounds.getHeight() + 2 * hitAreaOutSet); + Color interactionColor = getInteractionColor(); + ha.setStroke(interactionColor); + ha.setFill(interactionColor); + } + } + + /** Returns the insets for the filled rectangle subtracted from {@link #getDimensions()}. */ + protected Insets getInsets() { + return new Insets(2); + } + + /** {@inheritDoc} */ + @Override + protected EDragGesture dragGestureHitTest(Node n, DiagramCoordinate diagramLocation) { + if(n == getHitAreaRectangle()) { + // wild cast works: see addNodes(...) + Rectangle ha = getHitAreaRectangle(); + double x = diagramLocation.getLocalX(ha); + double y = diagramLocation.getLocalY(ha); + double outset = getHitAreaOutset(); + if(x < outset || x > ha.getWidth() - outset || y < outset || + y > ha.getHeight() - outset) { + return EDragGesture.NEW_LINK; + } + return EDragGesture.MOVE; + } + if(n == getVisualRectangle()) { + return EDragGesture.MOVE; + } + return EDragGesture.NONE; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..ccd23400a7ac47573127a39bc68c55c82baa44ad --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentVisualBase.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular.RectangularBorderLocation.getClosestLocationOnBounds; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.IOffsetLayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.ISideLayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.ContentVisualBase; + +import javafx.geometry.Bounds; +import javafx.geometry.Dimension2D; +import javafx.geometry.Rectangle2D; +import javafx.geometry.Side; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; + +/** Base class for {@link ContentVisualBase content visuals} depicted by rectangles. */ +public abstract class RectangularContentVisualBase extends ContentVisualBase { + /** Constructor. */ + public RectangularContentVisualBase(IContentMVCBundle mvcb) { + super(mvcb, new Rectangle(), new Rectangle()); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + + Rectangle2D bounds = getCurrentBounds(); + double leftX = bounds.getMinX(); + double upperY = bounds.getMinY(); + double width = bounds.getWidth(); + double height = bounds.getHeight(); + if(enableVisual()) { + Rectangle rectangle = getVisualRectangleShape(); + rectangle.setX(leftX); + rectangle.setY(upperY); + rectangle.setWidth(width); + rectangle.setHeight(height); + Dimension2D cornerArc = getCornerArcDimensions(); + rectangle.setArcWidth(cornerArc.getWidth()); + rectangle.setArcHeight(cornerArc.getHeight()); + rectangle.setOpacity(getOpacity()); + rectangle.setStroke(getBorderColor()); + rectangle.setStrokeWidth(getBorderWidth()); + rectangle.setStrokeType(getBorderType()); + rectangle.setFill(getFillColor()); + } + + if(enableInteraction()) { + Rectangle ha = getHitAreaRectangleShape(); + double lnk = getHitAreaStartLinkSize(); + double resize = getHitAreaResizeSize(); + ha.setX(leftX - lnk - resize / 2); + ha.setY(upperY - lnk - resize / 2); + ha.setWidth(lnk + resize + width + lnk); + ha.setHeight(lnk + resize + height + lnk); + Color interactionColor = getInteractionColor(); + ha.setStroke(interactionColor); + ha.setFill(interactionColor); + } + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getAnchorageLocation(IContentAnchorageVisual visual) { + Rectangle2D pb = getCurrentBounds(); + Side side = visual.getLayout(ISideLayout.class).getSide(); + double offset = visual.getLayout(IOffsetLayout.class).getOffset(); + RectangularBorderLocation rbl = + new RectangularBorderLocation(side, offset, pb, visual.getDimensions()); + return rbl.getLocation(); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getLinkAnchorage(DiagramCoordinate indication) { + Rectangle2D b = getCurrentBounds(); + Dimension2D dummy = new Dimension2D(1, 1); + RectangularBorderLocation loc = getClosestLocationOnBounds( + indication.add(-b.getMinX(), -b.getMinY()), b, dummy, dummy); + return loc.getLocation().add(b.getMinX(), b.getMinY()); + } + + /** Returns the dimensions of the corner arcs. */ + protected Dimension2D getCornerArcDimensions() { + return new Dimension2D(15, 15); + } + + /** {@inheritDoc} */ + @Override + protected EDragGesture dragGestureHitTest(Node node, DiagramCoordinate diagramLocation) { + if(node == getHitAreaShape()) { + double x = diagramLocation.getLocalX(node); + double y = diagramLocation.getLocalY(node); + double l = getHitAreaStartLinkSize(); + double r = getHitAreaResizeSize(); + Bounds bounds = node.getBoundsInLocal(); + // check for move area + double inset = l + r; + Rectangle2D moveArea = new Rectangle2D(inset, inset, bounds.getWidth() - 2 * inset, + bounds.getHeight() - 2 * inset); + if(moveArea.contains(x, y)) { + return EDragGesture.MOVE; + } + if(x < l || y < l || x > bounds.getWidth() - l || y > bounds.getHeight() - l) { + return EDragGesture.NEW_LINK; + } + if(x > bounds.getWidth() - inset && y >= l && y <= bounds.getHeight() - inset) { + return EDragGesture.RESIZE_H; + } + if(y > bounds.getHeight() - inset && x >= l && x <= bounds.getWidth() - inset) { + return EDragGesture.RESIZE_V; + } + if(x > bounds.getWidth() - inset && y > bounds.getHeight() - inset) { + return EDragGesture.RESIZE_VH; + } + return EDragGesture.NONE; + } + if(node == getVisualShape() || node == text) { + return EDragGesture.MOVE; + } + return EDragGesture.NONE; + } + + /** Returns the visual rectangle shape. */ + protected final Rectangle getVisualRectangleShape() { + // wild cast works: see constructor + return (Rectangle)getVisualShape(); + } + + /** Returns the hit area rectangle shape. */ + protected final Rectangle getHitAreaRectangleShape() { + // wild cast works: see constructor + return (Rectangle)getHitAreaShape(); + } + + /** + * Returns the extension of the hit area in pixel used to trigger link creation. This number + * specifies how many pixels beyond the visible border are used to trigger link creation by a + * mouse drag gesture. + */ + protected double getHitAreaStartLinkSize() { + return getViewer().getFeatures().getMaximumSpacing(); + } + + /** Returns the size of the hit area in pixel used to resize the content visual. */ + protected double getHitAreaResizeSize() { + return getViewer().getFeatures().getMaximumSpacing(); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularDiagramAnchorageVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularDiagramAnchorageVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..1259d6d110becca9ae02c754036c6693f00de683 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RectangularDiagramAnchorageVisualBase.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG; +import static org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.DiagramAnchorageVisualBase; + +import javafx.geometry.Insets; +import javafx.geometry.Rectangle2D; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; + +/** + * Base class for {@link DiagramAnchorageVisualBase free interface visuals} depicted as rectangles. + */ +public abstract class RectangularDiagramAnchorageVisualBase extends DiagramAnchorageVisualBase { + /** Constructor. */ + public RectangularDiagramAnchorageVisualBase(IDiagramAnchorageMVCBundle mvcb) { + super(mvcb, new Rectangle(), new Rectangle()); + } + + /** Returns the visual rectangle. */ + protected final Rectangle getVisualRectangle() { + // wild cast works: see constructor + return (Rectangle)getVisualShape(); + } + + /** Returns the hit area rectangle. */ + protected final Rectangle getHitAreaRectangle() { + // wild cast works: see constructor + return (Rectangle)getHitAreaShape(); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + + Rectangle2D bounds = getCurrentBounds(); + if(enableVisual()) { + Rectangle visual = getVisualRectangle(); + Insets i = getInsets(); + visual.setX(bounds.getMinX() + i.getLeft()); + visual.setY(bounds.getMinY() + i.getTop()); + visual.setWidth(bounds.getWidth() - i.getLeft() - i.getRight()); + visual.setHeight(bounds.getHeight() - i.getTop() - i.getBottom()); + visual.setOpacity(getOpacity()); + visual.setStrokeWidth(getBorderWidth()); + visual.setStrokeType(getBorderType()); + + if(getMVCBundle().hasTag(HIGHLIGHT_INCOMING_LINK_TAG)) { + visual.setFill(getHighlightIncomingLinkColor()); + visual.setStroke(getHighlightIncomingLinkBorderColor()); + } else if(getMVCBundle().hasTag(HIGHLIGHT_OUTGOING_LINK_TAG)) { + visual.setFill(getHighlightOutgoingLinkColor()); + visual.setStroke(getHighlightOutgoingLinkBorderColor()); + } else { + visual.setFill(getFillColor()); + visual.setStroke(getBorderColor()); + } + } + + if(enableInteraction()) { + Rectangle ha = getHitAreaRectangle(); + double hitAreaOutSet = getHitAreaOutset(); + ha.setX(bounds.getMinX() - hitAreaOutSet); + ha.setY(bounds.getMinY() - hitAreaOutSet); + ha.setWidth(bounds.getWidth() + 2 * hitAreaOutSet); + ha.setHeight(bounds.getHeight() + 2 * hitAreaOutSet); + Color interactionColor = getInteractionColor(); + ha.setStroke(interactionColor); + ha.setFill(interactionColor); + } + } + + /** Returns the insets for the filled rectangle subtracted from {@link #getDimensions()}. */ + protected Insets getInsets() { + return new Insets(2); + } + + /** {@inheritDoc} */ + @Override + protected EDragGesture dragGestureHitTest(Node n, DiagramCoordinate diagramLocation) { + if(n == getHitAreaRectangle()) { + Rectangle ha = getHitAreaRectangle(); + double x = diagramLocation.getLocalX(n); + double y = diagramLocation.getLocalY(n); + double outset = getHitAreaOutset(); + if(x < outset || x > ha.getWidth() - outset || y < outset || + y > ha.getHeight() - outset) { + return EDragGesture.NEW_LINK; + } + return EDragGesture.MOVE; + } + if(n == getVisualRectangle()) { + return EDragGesture.MOVE; + } + return EDragGesture.NONE; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RhomboidContentVisualBase.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RhomboidContentVisualBase.java new file mode 100644 index 0000000000000000000000000000000000000000..7a91e401034acc629179fb1a45416cd519f617d7 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/rectangular/RhomboidContentVisualBase.java @@ -0,0 +1,197 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular; + +import static org.fortiss.tooling.common.ui.javafx.lwfxef.visual.rectangular.RectangularBorderLocation.getClosestLocationOnBounds; + +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramCoordinate; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramLayers; +import org.fortiss.tooling.common.ui.javafx.lwfxef.EDragGesture; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.IOffsetLayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.layout.ISideLayout; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.base.ContentVisualBase; + +import javafx.collections.ObservableList; +import javafx.geometry.Bounds; +import javafx.geometry.Dimension2D; +import javafx.geometry.Rectangle2D; +import javafx.geometry.Side; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.shape.ClosePath; +import javafx.scene.shape.LineTo; +import javafx.scene.shape.MoveTo; +import javafx.scene.shape.Path; +import javafx.scene.shape.PathElement; + +/** Base class for {@link ContentVisualBase content visuals} depicted by a rhomboid. */ +public abstract class RhomboidContentVisualBase extends ContentVisualBase { + /** Constructor. */ + public RhomboidContentVisualBase(IContentMVCBundle mvcb) { + super(mvcb, new Path(), new Path()); + } + + /** {@inheritDoc} */ + @Override + public void updateNodes(DiagramLayers layers) { + super.updateNodes(layers); + + Rectangle2D bounds = getCurrentBounds(); + double leftX = bounds.getMinX(); + double upperY = bounds.getMinY(); + double width = bounds.getWidth(); + double height = bounds.getHeight(); + if(enableVisual()) { + Path path = getVisualPathShape(); + path.setOpacity(getOpacity()); + path.setStroke(getBorderColor()); + path.setStrokeWidth(getBorderWidth()); + path.setStrokeType(getBorderType()); + path.setFill(getFillColor()); + makePath(leftX, upperY, width, height, path); + } + + if(enableInteraction()) { + Path ha = getHitAreaPathShape(); + double lnk = getHitAreaStartLinkSize(); + double resize = getHitAreaResizeSize(); + double lr = lnk + resize; + makePath(leftX - lr, upperY - lr, width + 2 * lr, height + 2 * lr, ha); + Color interactionColor = getInteractionColor(); + ha.setStroke(interactionColor); + ha.setFill(interactionColor); + } + } + + /** Creates the rhomboid shape. */ + private void makePath(double x, double y, double w, double h, Path p) { + double offset = getOffset(); + ObservableList<PathElement> pes = p.getElements(); + pes.clear(); + pes.add(new MoveTo(x, y + h)); + pes.add(new LineTo(x + w - offset, y + h)); + pes.add(new LineTo(x + w, y)); + pes.add(new LineTo(x + offset, y)); + pes.add(new ClosePath()); + } + + /** Returns the offset of the upper and lower rhomboid line (0 = rectangle). */ + public double getOffset() { + return 3.0 * getViewer().getFeatures().getHorizontalSpacing(); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getAnchorageLocation(IContentAnchorageVisual visual) { + Rectangle2D pb = getCurrentBounds(); + double rhomboidOffset = getOffset(); + Dimension2D anchorageSize = visual.getDimensions(); + Side side = visual.getLayout(ISideLayout.class).getSide(); + double anchorageOffset = visual.getLayout(IOffsetLayout.class).getOffset(); + if(side == Side.LEFT) { + double lx = rhomboidOffset / 2 - anchorageSize.getWidth() / 2; + double ly = pb.getHeight() / 2 - anchorageSize.getHeight() / 2; + return new DiagramCoordinate(lx, ly); + } else if(side == Side.RIGHT) { + double lx = pb.getWidth() - rhomboidOffset / 2 - anchorageSize.getWidth() / 2; + double ly = pb.getHeight() / 2 - anchorageSize.getHeight() / 2; + return new DiagramCoordinate(lx, ly); + } + RectangularBorderLocation rbl = + new RectangularBorderLocation(side, anchorageOffset, pb, anchorageSize); + return rbl.getLocation(); + } + + /** {@inheritDoc} */ + @Override + public DiagramCoordinate getLinkAnchorage(DiagramCoordinate indication) { + Rectangle2D b = getCurrentBounds(); + Dimension2D dummy = new Dimension2D(1, 1); + RectangularBorderLocation loc = getClosestLocationOnBounds( + indication.add(-b.getMinX(), -b.getMinY()), b, dummy, dummy); + return loc.getLocation().add(b.getMinX(), b.getMinY()); + } + + /** Returns the dimensions of the corner arcs. */ + protected Dimension2D getCornerArcDimensions() { + return new Dimension2D(15, 15); + } + + /** {@inheritDoc} */ + @Override + protected EDragGesture dragGestureHitTest(Node node, DiagramCoordinate diagramLocation) { + if(node == getHitAreaShape()) { + double x = diagramLocation.getLocalX(node); + double y = diagramLocation.getLocalY(node); + double l = getHitAreaStartLinkSize(); + double r = getHitAreaResizeSize(); + Bounds bounds = node.getBoundsInLocal(); + // check for move area + double inset = l + r; + Rectangle2D moveArea = new Rectangle2D(inset, inset, bounds.getWidth() - 2 * inset, + bounds.getHeight() - 2 * inset); + if(moveArea.contains(x, y)) { + return EDragGesture.MOVE; + } + if(x < l || y < l || x > bounds.getWidth() - l || y > bounds.getHeight() - l) { + return EDragGesture.NEW_LINK; + } + if(x > bounds.getWidth() - inset && y >= l && y <= bounds.getHeight() - inset) { + return EDragGesture.RESIZE_H; + } + if(y > bounds.getHeight() - inset && x >= l && x <= bounds.getWidth() - inset) { + return EDragGesture.RESIZE_V; + } + if(x > bounds.getWidth() - inset && y > bounds.getHeight() - inset) { + return EDragGesture.RESIZE_VH; + } + return EDragGesture.NONE; + } + if(node == getVisualShape() || node == text) { + return EDragGesture.MOVE; + } + return EDragGesture.NONE; + } + + /** Returns the visual path shape. */ + protected final Path getVisualPathShape() { + // wild cast works: see constructor + return (Path)getVisualShape(); + } + + /** Returns the hit area path shape. */ + protected final Path getHitAreaPathShape() { + // wild cast works: see constructor + return (Path)getHitAreaShape(); + } + + /** + * Returns the extension of the hit area in pixel used to trigger link creation. This number + * specifies how many pixels beyond the visible border are used to trigger link creation by a + * mouse drag gesture. + */ + protected double getHitAreaStartLinkSize() { + return getViewer().getFeatures().getMaximumSpacing(); + } + + /** Returns the size of the hit area in pixel used to resize the content visual. */ + protected double getHitAreaResizeSize() { + return getViewer().getFeatures().getMaximumSpacing(); + } + + /** {@inheritDoc} */ + @Override + protected DiagramCoordinate getTextAnchorLocation() { + DiagramCoordinate textAnchorLocation = super.getTextAnchorLocation(); + return textAnchorLocation.add(getOffset() / 2, 0); + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/widgets/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/widgets/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..23567ab4141161407f8d322123ed4eb8e732c7f4 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/widgets/.ratings @@ -0,0 +1,2 @@ +ExpandCollapseWidget.java f431b6a86f3ce1794c55b90f39a15cda4f92ea06 YELLOW +LinkArrowWidget.java 5354f14ca9d53cc3df88afb1867a266dfde65199 YELLOW diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/widgets/ExpandCollapseWidget.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/widgets/ExpandCollapseWidget.java new file mode 100644 index 0000000000000000000000000000000000000000..f431b6a86f3ce1794c55b90f39a15cda4f92ea06 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/widgets/ExpandCollapseWidget.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.widgets; + +import static javafx.scene.paint.Color.BLACK; +import static javafx.scene.paint.Color.TRANSPARENT; + +import javafx.collections.ObservableList; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Line; +import javafx.scene.shape.Rectangle; + +/** Widget showing a plus / minus indicator used for hierarchical content nodes. */ +public final class ExpandCollapseWidget extends Group { + /** The rectangle of the indicator. */ + private final Rectangle borderRect = new Rectangle(); + /** The horizontal line used for both plus and minus state. */ + private final Line horizontalLine = new Line(); + /** The vertical line used for plus state. */ + private final Line verticalLine = new Line(); + + /** Constructor. */ + public ExpandCollapseWidget(double x, double y, double w, double h, double inset) { + update(x, y, w, h, inset); + + ObservableList<Node> c = getChildren(); + c.add(borderRect); + c.add(horizontalLine); + + setColor(BLACK, TRANSPARENT); + } + + /** Updates the location, size and insets of the widget. */ + public void update(double x, double y, double w, double h, double inset) { + borderRect.setX(x); + borderRect.setY(y); + borderRect.setWidth(w); + borderRect.setHeight(h); + + horizontalLine.setStartX(x + inset); + horizontalLine.setEndX(x + w - inset); + horizontalLine.setStartY(y + h / 2); + horizontalLine.setEndY(y + h / 2); + + verticalLine.setStartX(x + w / 2); + verticalLine.setEndX(x + w / 2); + verticalLine.setStartY(y + inset); + verticalLine.setEndY(y + h - inset); + } + + /** Sets the state of the widget. */ + public void setState(boolean expanded) { + if(expanded) { + getChildren().remove(verticalLine); + } else if(verticalLine.getParent() != this) { + // avoid duplicate add + getChildren().add(verticalLine); + } + } + + /** Sets the fill of the rectangle and the stroke color. */ + public void setColor(Paint stroke, Paint fill) { + borderRect.setFill(fill); + borderRect.setStroke(stroke); + verticalLine.setStroke(stroke); + horizontalLine.setStroke(stroke); + } + + /** Determines if the given node is this widget or a part of it. */ + public boolean isHit(Node hit) { + return hit == this || hit.getParent() == this; + } +} diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/widgets/LinkArrowWidget.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/widgets/LinkArrowWidget.java new file mode 100644 index 0000000000000000000000000000000000000000..5354f14ca9d53cc3df88afb1867a266dfde65199 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/lwfxef/visual/widgets/LinkArrowWidget.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 fortiss GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ +package org.fortiss.tooling.common.ui.javafx.lwfxef.visual.widgets; + +import static java.lang.Math.atan2; +import static java.lang.Math.cos; +import static java.lang.Math.sin; +import static javafx.scene.paint.Color.BLACK; + +import javafx.collections.ObservableList; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Line; +import javafx.scene.shape.LineTo; +import javafx.scene.shape.MoveTo; +import javafx.scene.shape.Path; +import javafx.scene.shape.PathElement; + +/** A widget for displaying an arrow at the end of links. */ +public final class LinkArrowWidget extends Group { + /** The boolean flag for using line or filled triangle arrow head. */ + private final boolean useLineArrows; + /** The length of the arrow head. */ + private double arrowLength; + + /** Constructor. */ + public LinkArrowWidget(boolean useLineArrows, double anchorX, double anchorY, double directionX, + double directionY, double arrowLength) { + this.useLineArrows = useLineArrows; + this.arrowLength = arrowLength; + if(useLineArrows) { + addLines(anchorX, anchorY, directionX, directionY); + } else { + addFilledArrow(anchorX, anchorY, directionX, directionY); + } + } + + /** Fills the group with a filled arrow widget. */ + private void addFilledArrow(double anchorX, double anchorY, double directionX, + double directionY) { + ObservableList<Node> children = getChildren(); + children.clear(); + Path p = new Path(); + p.setFill(BLACK); + p.setStrokeWidth(1); + children.add(p); + updatePath(p, anchorX, anchorY, directionX, directionY); + } + + /** Updates the {@link Path} primitive for the filled arrow head. */ + private void updatePath(Path p, double anchorX, double anchorY, double directionX, + double directionY) { + double angle = atan2(directionX - anchorX, directionY - anchorY); + double angleLeft = angle - Math.PI / 6; + double lx = anchorX + sin(angleLeft) * arrowLength; + double ly = anchorY + cos(angleLeft) * arrowLength; + double angleRight = angle + Math.PI / 6; + double rx = anchorX + sin(angleRight) * arrowLength; + double ry = anchorY + cos(angleRight) * arrowLength; + + ObservableList<PathElement> l = p.getElements(); + l.clear(); + l.add(new MoveTo(anchorX, anchorY)); + l.add(new LineTo(lx, ly)); + l.add(new LineTo(rx, ry)); + l.add(new LineTo(anchorX, anchorY)); + } + + /** Fills the group with two lines forming the arrow head. */ + private void addLines(double anchorX, double anchorY, double directionX, double directionY) { + ObservableList<Node> children = getChildren(); + children.clear(); + Line leftLine = new Line(0, 0, 0, 0); + children.add(leftLine); + Line rightLine = new Line(0, 0, 0, 0); + children.add(rightLine); + updateLines(anchorX, anchorY, directionX, directionY); + } + + /** Updates the locations of the widget's lines */ + private void updateLines(double anchorX, double anchorY, double directionX, double directionY) { + // get(0) and cast works: see addLines(...) + Line left = (Line)getChildren().get(0); + double angle = atan2(directionX - anchorX, directionY - anchorY); + double angleLeft = angle - Math.PI / 4; + double lx = anchorX + sin(angleLeft) * arrowLength; + double ly = anchorY + cos(angleLeft) * arrowLength; + left.setStartX(anchorX); + left.setStartY(anchorY); + left.setEndX(lx); + left.setEndY(ly); + + // get(1) and cast works: see addLines(...) + Line right = (Line)getChildren().get(1); + double angleRight = angle + Math.PI / 4; + double rx = anchorX + sin(angleRight) * arrowLength; + double ry = anchorY + cos(angleRight) * arrowLength; + right.setStartX(anchorX); + right.setStartY(anchorY); + right.setEndX(rx); + right.setEndY(ry); + } + + /** Updates the arrow head with the new locations. */ + public void update(double anchorX, double anchorY, double directionX, double directionY, + double arrowLength) { + this.arrowLength = arrowLength; + if(useLineArrows) { + updateLines(anchorX, anchorY, directionX, directionY); + } else { + // get(0) and cast works: see AddFilledArrow(...) + Path p = (Path)getChildren().get(0); + updatePath(p, anchorX, anchorY, directionX, directionY); + } + } + + /** Sets the color of the arrow widget. */ + public void setColor(Paint color) { + if(useLineArrows) { + // get(0) and cast works: see addLines(...) + ((Line)getChildren().get(0)).setStroke(color); + // get(1) and cast works: see addLines(...) + ((Line)getChildren().get(1)).setStroke(color); + } else { + // get(0) and cast works: see AddFilledArrow(...) + Path p = (Path)getChildren().get(0); + p.setStroke(color); + p.setFill(color); + } + } +} diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/.ratings b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/.ratings index e3dae57d37ce91f7898d85eb9774c665040a8e9f..62c4ab516cd12fe289a932734684368e00f044be 100644 --- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/.ratings +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/.ratings @@ -3,7 +3,7 @@ IContextMenuContributor.java 0f09c76662c154cf52ddab61b417e82a42854162 GREEN IContextMenuMultiSelectionContributor.java 125b31dd38009bc2095b7e6bc860e946e39f58c4 GREEN IEditPartFactory.java 5729715847f553d95a5bad4a9211c7e6f458badd GREEN IModelEditor.java 962d7f7758abc88bbc6064c8b4eb32da00abf8e8 GREEN -IModelEditorBinding.java d6896569cfe7eb0d7f0b8e4a71573c50e8c75329 GREEN +IModelEditorBinding.java 844865d93252b6c4a648c23ff28bb28fd42c17aa YELLOW IModelElementHandler.java 21b4a96251e0267f156b4b8f2b95a97f6e81e646 GREEN ITutorialStepUI.java b8aee2b95857484ab6ad9ecd55b5de9f0ea158e5 GREEN ITutorialUIProvider.java aa0ff5db4d7ba0953e34edeb99f3e8279567e18f GREEN diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/IModelEditorBinding.java b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/IModelEditorBinding.java index d6896569cfe7eb0d7f0b8e4a71573c50e8c75329..844865d93252b6c4a648c23ff28bb28fd42c17aa 100644 --- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/IModelEditorBinding.java +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/IModelEditorBinding.java @@ -16,10 +16,10 @@ package org.fortiss.tooling.kernel.ui.extension; import org.eclipse.emf.ecore.EObject; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IControllerFactory; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelFactory; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisualFactory; import org.eclipse.ui.IEditorPart; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IControllerFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisualFactory; import org.fortiss.tooling.kernel.service.base.IEObjectAware; import org.fortiss.tooling.kernel.ui.service.IModelEditorBindingService; diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings index 3776fb18b863e417da1104e086324e0e0cd6c1fc..3747d405583494626be4fdece7f4d8e8214f2f49 100644 --- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings @@ -6,8 +6,8 @@ EReferencePropertySectionBase.java 0548da6778516003257f59d0b4c2b60d458be3b6 GREE EditorBase.java 9c09fff92945256bb8680992ae7bb2c78f47b150 GREEN FXEditorBase.java 2e520be0bbae7d0aebdff70218a124dbe0896ce2 GREEN IListPropertySection.java 8bb00fe7959583e794ff9437b7a77404c9a9e70f GREEN -LWFXEFEditorBase.java 2bd06235f20c18dc2e7d433700b2ad74a16664e2 GREEN -ModelEditorBindingBase.java c258cb0ea28d74440856cd2abf367408fbbc1279 GREEN +LWFXEFEditorBase.java 72a0735d0a516e53f34e485039471512c8ac6577 YELLOW +ModelEditorBindingBase.java b9b1a1c5a48a6e677d1f57ad55a6126d9703c4b5 YELLOW ModelElementHandlerBase.java 384727748f125c9d43f19d9c0eba4ba1be5a7a26 GREEN MultiEObjectActionBase.java 9e237d8ea640c4194e4877af4a9cfce88698e543 GREEN NamedCommentedModelElementHandlerBase.java 681b98b50b362f01abb7a36f108f4f11b9e51829 GREEN diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/LWFXEFEditorBase.java b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/LWFXEFEditorBase.java index 2bd06235f20c18dc2e7d433700b2ad74a16664e2..72a0735d0a516e53f34e485039471512c8ac6577 100644 --- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/LWFXEFEditorBase.java +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/LWFXEFEditorBase.java @@ -28,14 +28,14 @@ import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewer; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerSelection; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.change.Change; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IControllerFactory; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelFactory; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisualFactory; import org.eclipse.ui.IEditorPart; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewer; +import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerSelection; +import org.fortiss.tooling.common.ui.javafx.lwfxef.change.Change; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IControllerFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisualFactory; import org.fortiss.tooling.kernel.service.ICommandStackService; import org.fortiss.tooling.kernel.ui.extension.IModelEditorBinding; import org.fortiss.tooling.kernel.ui.extension.base.factory.DelegatingControllerFactory; diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/ModelEditorBindingBase.java b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/ModelEditorBindingBase.java index c258cb0ea28d74440856cd2abf367408fbbc1279..b9b1a1c5a48a6e677d1f57ad55a6126d9703c4b5 100644 --- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/ModelEditorBindingBase.java +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/ModelEditorBindingBase.java @@ -16,9 +16,9 @@ package org.fortiss.tooling.kernel.ui.extension.base; import org.eclipse.emf.ecore.EObject; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IControllerFactory; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelFactory; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisualFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IControllerFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisualFactory; import org.fortiss.tooling.kernel.service.base.IEObjectAware; import org.fortiss.tooling.kernel.ui.extension.IModelEditorBinding; import org.fortiss.tooling.kernel.ui.service.IModelEditorBindingService; diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/.ratings b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/.ratings index 75b475f75083ac3976a4740cc7fc19976f96542c..5d72fbf78d3b4ebb888b9c985348f093a1e4bc05 100644 --- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/.ratings +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/.ratings @@ -1,4 +1,4 @@ -DelegatingControllerFactory.java ad214d83b5821b39862b7c382c91a13c3dfddbd0 GREEN +DelegatingControllerFactory.java be19395684645d79d892e5caed079d4b7f5cb5b0 YELLOW DelegatingFactoryBase.java f421742267610f41bb6196346026d2f239d90ed0 GREEN -DelegatingModelFactory.java 717b706781879efe9efcb5ce4bf53723e39a3e1b GREEN -DelegatingVisualFactory.java 7e834acd12ae4d1c2b2b32a5456dc9f2b6d4e466 GREEN +DelegatingModelFactory.java f94ca989a5b97ad3f073040bb46b9c0a87e35d6d YELLOW +DelegatingVisualFactory.java 8bb82e9d48b2577655e577e7e807629c6fd6f2db YELLOW diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingControllerFactory.java b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingControllerFactory.java index ad214d83b5821b39862b7c382c91a13c3dfddbd0..be19395684645d79d892e5caed079d4b7f5cb5b0 100644 --- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingControllerFactory.java +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingControllerFactory.java @@ -18,14 +18,14 @@ package org.fortiss.tooling.kernel.ui.extension.base.factory; import java.util.List; import java.util.Optional; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IController; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IControllerFactory; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IDiagramMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IController; +import org.fortiss.tooling.common.ui.javafx.lwfxef.controller.IControllerFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; /** * Delegates the creational calls to concrete {@link IControllerFactory}s. The first non-null diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingModelFactory.java b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingModelFactory.java index 717b706781879efe9efcb5ce4bf53723e39a3e1b..f94ca989a5b97ad3f073040bb46b9c0a87e35d6d 100644 --- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingModelFactory.java +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingModelFactory.java @@ -28,7 +28,7 @@ import java.util.List; import java.util.Optional; import org.eclipse.emf.ecore.EObject; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelFactory; +import org.fortiss.tooling.common.ui.javafx.lwfxef.model.IModelFactory; import org.fortiss.tooling.kernel.ui.ToolingKernelUIActivator; /** diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingVisualFactory.java b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingVisualFactory.java index 7e834acd12ae4d1c2b2b32a5456dc9f2b6d4e466..8bb82e9d48b2577655e577e7e807629c6fd6f2db 100644 --- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingVisualFactory.java +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/factory/DelegatingVisualFactory.java @@ -17,16 +17,16 @@ package org.fortiss.tooling.kernel.ui.extension.base.factory; import java.util.List; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IContentVisual; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IDiagramAnchorageVisual; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.ILinkVisual; -import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisualFactory; import org.eclipse.ui.IEditorPart; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IContentMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IContentVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IDiagramAnchorageVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.ILinkVisual; +import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisualFactory; import org.fortiss.tooling.kernel.service.ITransformationService; import org.fortiss.tooling.kernel.ui.extension.base.LWFXEFEditorBase;