Major refactoring of LWFXEF and other minor changes.

Also-By: Anna Haas <haas@fortiss.org>
Signed-off-by: Florian Hoelzl <hoelzl@fortiss.org>
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/.ratings b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/.ratings
index e766b3b..32be4e5 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/.ratings
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/.ratings
@@ -1,9 +1,9 @@
-BaseFunctionContentVisual.java d050b49ecd7097f036a89b59957564326e67e522 RED
+BaseFunctionContentVisual.java b6d69b6a8c84e0da44867341a596d46438e0dbb8 RED
 ConjunctiveFunctionContentVisual.java 2e335e4562b419bc9a6c2297d9974e5ad5d65113 YELLOW
 DisjunctiveFunctionContentVisual.java 0ad4cfdac1c828a3fbcc19b07d9aa72be26f36bf YELLOW
-FunctionContentVisualBase.java db9d500334a6b51dc08b2f7353065751c0f8c629 RED
-GraphicalFunctionEditorVisualFactory.java d887f4ca66eaaf49ec42cb16a0c9298f3df37ae0 YELLOW
-LineOrLinkVisual.java 9ce07ed806a1a12a24acecf63e53b6fe22797d7d RED
+FunctionContentVisualBase.java 3b6c3c09771023d72c00418a24b43c6ad2bad374 YELLOW
+GraphicalFunctionEditorVisualFactory.java d092271ea2b3a23abfccdaa56498571cd86801b8 YELLOW
+LineOrLinkVisual.java 2f8d3dd8f17aa8c511ad4f7ff30401df78330e53 YELLOW
 PlaceContentAnchorageVisual.java f5acb8686f43f5c78ab7ae15d8b569eb25a4840c YELLOW
 PlaceDiagramAnchorageVisual.java 8c58bb10f7d3c331b988f0af92d59a1eea3d9916 YELLOW
 PortContentAnchorageVisual.java 7aa6e0d3c218a536e908455732ee9eee9fbab542 YELLOW
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/BaseFunctionContentVisual.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/BaseFunctionContentVisual.java
index d050b49..b6d69b6 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/BaseFunctionContentVisual.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/BaseFunctionContentVisual.java
@@ -33,10 +33,4 @@
 	protected Paint getFillColor() {
 		return ANTIQUEWHITE;
 	}
-
-	/** {@inheritDoc} */
-	@Override
-	protected boolean useRoundedRectangle() {
-		return false;
-	}
 }
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/FunctionContentVisualBase.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/FunctionContentVisualBase.java
index db9d500..c86e53d 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/FunctionContentVisualBase.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/FunctionContentVisualBase.java
@@ -25,7 +25,6 @@
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular.RectangularContentVisualBase;
 
 import javafx.geometry.Rectangle2D;
-import javafx.scene.Node;
 
 /** Base class for {@link IContentVisual}s of {@link Function}s. */
 public abstract class FunctionContentVisualBase extends RectangularContentVisualBase {
@@ -63,12 +62,6 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean isLinkCreationHandle(Node n) {
-		return n == getRectangleShape();
-	}
-
-	/** {@inheritDoc} */
-	@Override
 	protected void createLinkTargetAllowedEffect(DiagramLayers layers) {
 		Utils.createLinkTargetAllowedEffect(getRectangleShape());
 	}
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/PortContentAnchorageVisual.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/PortContentAnchorageVisual.java
index 154ef1e..fd534d7 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/PortContentAnchorageVisual.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/PortContentAnchorageVisual.java
@@ -93,18 +93,18 @@
 	/** {@inheritDoc} */
 	@Override
 	protected void createLinkTargetAllowedEffect(DiagramLayers layers) {
-		Utils.createLinkTargetAllowedEffect(getCircleNode());
+		Utils.createLinkTargetAllowedEffect(getVisualCircle());
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	protected void createLinkTargetDeniedEffect(DiagramLayers layers) {
-		Utils.createLinkTargetDeniedEffect(getCircleNode());
+		Utils.createLinkTargetDeniedEffect(getVisualCircle());
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	protected void createLinkTargetDisabledEffect(DiagramLayers layers) {
-		getCircleNode().setEffect(null);
+		getVisualCircle().setEffect(null);
 	}
 }
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/PortDiagramAnchorageVisual.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/PortDiagramAnchorageVisual.java
index b8a2ce6..95ac586 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/PortDiagramAnchorageVisual.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/editor/visual/PortDiagramAnchorageVisual.java
@@ -87,18 +87,18 @@
 	/** {@inheritDoc} */
 	@Override
 	protected void createLinkTargetAllowedEffect(DiagramLayers layers) {
-		Utils.createLinkTargetAllowedEffect(getCircleNode());
+		Utils.createLinkTargetAllowedEffect(getVisualCircle());
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	protected void createLinkTargetDeniedEffect(DiagramLayers layers) {
-		Utils.createLinkTargetDeniedEffect(getCircleNode());
+		Utils.createLinkTargetDeniedEffect(getVisualCircle());
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	protected void createLinkTargetDisabledEffect(DiagramLayers layers) {
-		getCircleNode().setEffect(null);
+		getVisualCircle().setEffect(null);
 	}
 }
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/.ratings b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/.ratings
index 1d185f8..60f6b6f 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/.ratings
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/.ratings
@@ -1,4 +1,4 @@
-GraphicalTestView.java 1cbfe0c57356cd2166e36d0c36d798df8efed907 RED
+GraphicalTestView.java 4715835ab4251a20d8d33b1a1ee34fd104d660d5 YELLOW
 Images.java 57fb3d16e17c86a37fc965fef5ab77671a346754 RED
 SpiderChartTestView.java 912aec9eab4450af9f36dd15465295ed5af963ef YELLOW
 Subscene3DTestView.java 683686a7f0eeb9ef949b0e8d6f9f0f4155877232 YELLOW
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/GraphicalTestView.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/GraphicalTestView.java
index 1cbfe0c..4715835 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/GraphicalTestView.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/GraphicalTestView.java
@@ -13,17 +13,12 @@
  *******************************************************************************/
 package org.eclipse.systemfocus.examples.function.architecture.playground;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import javax.annotation.PostConstruct;
 
 import org.eclipse.e4.ui.di.Focus;
 import org.eclipse.systemfocus.examples.function.architecture.playground.controllers.ControllerFactory;
-import org.eclipse.systemfocus.examples.function.architecture.playground.model.IngredientFlow;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.MainRecipe;
-import org.eclipse.systemfocus.examples.function.architecture.playground.model.Recipe;
-import org.eclipse.systemfocus.examples.function.architecture.playground.model.WasteFlow;
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.ModelFactory;
 import org.eclipse.systemfocus.examples.function.architecture.playground.visuals.VisualFactory;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewer;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.configuration.GridConfiguration;
@@ -36,7 +31,7 @@
 /** A test view for the lightweight FX editor {@link DiagramViewer}. */
 @SuppressWarnings("javadoc")
 public class GraphicalTestView {
-	private MainRecipe modelRoot = new MainRecipe();
+	MainRecipe modelRoot = new MainRecipe();
 	private DiagramViewer viewer;
 
 	@PostConstruct
@@ -46,90 +41,7 @@
 		gridConfig.setDrawOuterBorder(true);
 		gridConfig.setIndicatorType(IndicatorType.CROSS);
 		gridConfig.setBackgroundColor(Color.WHITE);
-		IModelFactory mf = new IModelFactory() {
-			@Override
-			public List<Object> getContentAnchorageModels(Object parent) {
-				if(parent instanceof Recipe) {
-					Recipe b = (Recipe)parent;
-					List<Object> result = new ArrayList<>();
-					result.addAll(b.ingredients);
-					result.addAll(b.results);
-					result.addAll(b.waste);
-					return result;
-				}
-				throw new IllegalArgumentException(
-						"Unknown model element class: " + parent.getClass());
-			}
-
-			@Override
-			public Object getRootModel() {
-				return modelRoot;
-			}
-
-			@Override
-			public List<Object> getDiagramAnchorageModels() {
-				List<Object> result = new ArrayList<>();
-				result.addAll(modelRoot.ingredients);
-				result.addAll(modelRoot.results);
-				result.addAll(modelRoot.waste);
-				return result;
-			}
-
-			@Override
-			public List<Object> getContentModels() {
-				List<Object> result = new ArrayList<>();
-				result.addAll(modelRoot.subRecipes);
-				return result;
-			}
-
-			@Override
-			public List<Object> getLinkModels() {
-				List<Object> result = new ArrayList<>();
-				result.addAll(modelRoot.ingredientFlows);
-				result.addAll(modelRoot.wasteFlows);
-				return result;
-			}
-
-			@Override
-			public Object getLinkStart(Object link) {
-				if(link instanceof IngredientFlow) {
-					IngredientFlow flow = (IngredientFlow)link;
-					return flow.sourceIngredient;
-				}
-				if(link instanceof WasteFlow) {
-					WasteFlow flow = (WasteFlow)link;
-					return flow.sourceWaste;
-				}
-				throw new IllegalArgumentException(
-						"Unknown model element class: " + link.getClass());
-			}
-
-			@Override
-			public Object getLinkEnd(Object link) {
-				if(link instanceof IngredientFlow) {
-					IngredientFlow flow = (IngredientFlow)link;
-					return flow.targetIngredient;
-				}
-				if(link instanceof WasteFlow) {
-					WasteFlow flow = (WasteFlow)link;
-					return flow.targetWaste;
-				}
-				throw new IllegalArgumentException(
-						"Unknown model element class: " + link.getClass());
-			}
-
-			/** {@inheritDoc} */
-			@Override
-			public Object getParent(Object element) {
-				return null;
-			}
-
-			/** {@inheritDoc} */
-			@Override
-			public void update() {
-				// nothing to do here
-			}
-		};
+		IModelFactory mf = new ModelFactory(modelRoot);
 		viewer = new DiagramViewer(mf, new VisualFactory(), new ControllerFactory(), gridConfig,
 				null);
 		viewer.setLinkHighlightingEnabled(true);
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/.ratings b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/.ratings
index 05f6b8b..0dd5557 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/.ratings
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/.ratings
@@ -1,7 +1,9 @@
-ControllerFactory.java 4ead2831ecfde62a613dd87e3b2f418b9271175d RED
+ComponentRecipeContentController.java f52c5dcf97040aa4f3c78e1c2ccfc939917205ab RED
+CompositeRecipeContentController.java ca7f65b69b460f94f6af57099585aa0b30426034 RED
+ControllerFactory.java fefda2031fdc3a5f615292620c65764817011213 RED
 IngredientDiagramAnchorageController.java 5c3a5e27a20d12b6620a9ec338db2169379af02f RED
 IngredientFlowLinkController.java bf916dff1a77220da349cef03977cd01ea9b2d34 YELLOW
 MergeRecipeContentController.java 44e129102025c867242d5dbb765e4612eb6bf2f6 YELLOW
-SortRecipeContentController.java 27b5e3724f1b84e8e4cf2485f2c933f85f2dc460 RED
+SortRecipeContentController.java 8c390bbc69d4ca7ffd4270b479b5d25b1235dbb4 RED
 WasteDiagramAnchorageController.java a682f9009df2c5b00baf8fc4ae708957362402c6 YELLOW
 WasteFlowLinkController.java 6a12b7c21d802e79c14c0e3c86eb8351fbf4ffda YELLOW
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/ComponentRecipeContentController.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/ComponentRecipeContentController.java
new file mode 100644
index 0000000..f52c5dc
--- /dev/null
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/ComponentRecipeContentController.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2016, 2018 fortiss GmbH. 
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Florian Hoelzl (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.examples.function.architecture.playground.controllers;
+
+import static org.eclipse.systemfocus.kernel.base.utils.LayoutModelElementFactory.createRectangle;
+
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.Ingredient;
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.Recipe;
+import org.eclipse.systemfocus.kernel.base.utils.BoundsUtils;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.configuration.GridConfiguration;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IController;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.rectangular.RectangularResizableContentControllerBase;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle;
+
+import javafx.geometry.Side;
+
+/** {@link IController} implementation for {@link Recipe} visual. */
+public class ComponentRecipeContentController extends RectangularResizableContentControllerBase {
+	/** Constructor. */
+	public ComponentRecipeContentController(IContentMVCBundle mvcb) {
+		super(mvcb);
+	}
+
+	/** Returns the model element. */
+	public Recipe getRecipe() {
+		return (Recipe)super.getModel();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void resize(double dw, double dh) {
+		Recipe model = getRecipe();
+		GridConfiguration grid = getMVCBundle().getViewer().getGridConfig();
+		double nw = Math.max(model.bounds.getWidth() + dw,
+				grid != null ? grid.getHorizontalSpacing() : 1);
+		double nh = Math.max(model.bounds.getHeight() + dh,
+				grid != null ? grid.getVerticalSpacing() : 1);
+		model.bounds = createRectangle(model.bounds.getX(), model.bounds.getY(), (int)nw, (int)nh);
+		model.fireUpdateComplete();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void move(double dx, double dy) {
+		Recipe model = getRecipe();
+		GridConfiguration grid = getMVCBundle().getViewer().getGridConfig();
+		double nx =
+				Math.max(model.bounds.getX() + dx, grid != null ? grid.getHorizontalSpacing() : 1);
+		double ny =
+				Math.max(model.bounds.getY() + dy, grid != null ? grid.getVerticalSpacing() : 1);
+		model.bounds = createRectangle((int)nx, (int)ny, model.bounds.getWidth(),
+				model.bounds.getHeight());
+		model.fireUpdateComplete();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void moveAnchorage(IContentAnchorageMVCBundle anchorage, Side side, double offset) {
+		Ingredient ing = (Ingredient)anchorage.getModel();
+		ing.stickyOrientation = BoundsUtils.convertSide(side);
+		ing.stickyOffset = offset;
+		ing.fireUpdateComplete();
+	}
+}
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/CompositeRecipeContentController.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/CompositeRecipeContentController.java
new file mode 100644
index 0000000..38b5877
--- /dev/null
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/CompositeRecipeContentController.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2016, 2018 fortiss GmbH. 
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Florian Hoelzl (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.examples.function.architecture.playground.controllers;
+
+import static org.eclipse.systemfocus.kernel.base.utils.LayoutModelElementFactory.createRectangle;
+
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.CompositeRecipe;
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.Ingredient;
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.Recipe;
+import org.eclipse.systemfocus.kernel.base.model.layout.Rectangle;
+import org.eclipse.systemfocus.kernel.base.utils.BoundsUtils;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.configuration.GridConfiguration;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IController;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.rectangular.RectangularResizableContentControllerBase;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle;
+
+import javafx.geometry.Side;
+
+/** {@link IController} implementation for {@link Recipe} visual. */
+public class CompositeRecipeContentController extends RectangularResizableContentControllerBase {
+	/** Constructor. */
+	public CompositeRecipeContentController(IContentMVCBundle mvcb) {
+		super(mvcb);
+	}
+
+	/** Returns the model element. */
+	public CompositeRecipe getRecipe() {
+		// Wild cast works: see ModelFactory
+		return (CompositeRecipe)super.getModel();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void resize(double dw, double dh) {
+		Recipe model = getRecipe();
+		GridConfiguration grid = getMVCBundle().getViewer().getGridConfig();
+		double nw = Math.max(model.bounds.getWidth() + dw,
+				grid != null ? grid.getHorizontalSpacing() : 1);
+		double nh = Math.max(model.bounds.getHeight() + dh,
+				grid != null ? grid.getVerticalSpacing() : 1);
+		model.bounds = createRectangle(model.bounds.getX(), model.bounds.getY(), (int)nw, (int)nh);
+		model.fireUpdateComplete();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void move(double dx, double dy) {
+		Recipe model = getRecipe();
+		GridConfiguration grid = getMVCBundle().getViewer().getGridConfig();
+		double nx =
+				Math.max(model.bounds.getX() + dx, grid != null ? grid.getHorizontalSpacing() : 1);
+		double ny =
+				Math.max(model.bounds.getY() + dy, grid != null ? grid.getVerticalSpacing() : 1);
+		model.bounds = createRectangle((int)nx, (int)ny, model.bounds.getWidth(),
+				model.bounds.getHeight());
+		model.fireUpdateComplete();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void moveAnchorage(IContentAnchorageMVCBundle anchorage, Side side, double offset) {
+		Ingredient ing = (Ingredient)anchorage.getModel();
+		ing.stickyOrientation = BoundsUtils.convertSide(side);
+		ing.stickyOffset = offset;
+		ing.fireUpdateComplete();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean allowExpandCollapse() {
+		return true;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void expandCollapse() {
+		CompositeRecipe cr = getRecipe();
+		cr.expanded = !cr.expanded;
+		cr.bounds = computeCompositeSize(cr);
+		cr.fireUpdateComplete();
+	}
+
+	/** Computes the boundary for the composite. */
+	private Rectangle computeCompositeSize(CompositeRecipe recipe) {
+		int height = 40;
+		if(recipe.expanded) {
+			height += recipe.subRecipes.size() * 120;
+		}
+		Rectangle b = recipe.bounds;
+		return createRectangle(b.getX(), b.getY(), b.getWidth(), height);
+	}
+}
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/ControllerFactory.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/ControllerFactory.java
index 4ead283..fefda20 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/ControllerFactory.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/ControllerFactory.java
@@ -13,10 +13,13 @@
  *******************************************************************************/
 package org.eclipse.systemfocus.examples.function.architecture.playground.controllers;
 
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.CompositeRecipe;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.Ingredient;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.IngredientFlow;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.MainRecipe;
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.MergeBeansRecipe;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.Recipe;
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.SortBeansRecipe;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.Waste;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.WasteFlow;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IController;
@@ -62,10 +65,16 @@
 		Object model = mvcb.getModel();
 		if(model instanceof Recipe) {
 			Recipe r = (Recipe)model;
-			if(r.merge) {
+			if(r instanceof MergeBeansRecipe) {
 				return new MergeRecipeContentController(mvcb);
 			}
-			return new SortRecipeContentController(mvcb);
+			if(r instanceof SortBeansRecipe) {
+				return new SortRecipeContentController(mvcb);
+			}
+			if(r instanceof CompositeRecipe) {
+				return new CompositeRecipeContentController(mvcb);
+			}
+			return new ComponentRecipeContentController(mvcb);
 		}
 		throw new IllegalArgumentException("Unknown model element class: " + model.getClass());
 	}
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/SortRecipeContentController.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/SortRecipeContentController.java
index 27b5e37..8c390bb 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/SortRecipeContentController.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/controllers/SortRecipeContentController.java
@@ -34,14 +34,14 @@
 	}
 
 	/** Returns the model element. */
-	public Recipe getBuilding() {
+	public Recipe getRecipe() {
 		return (Recipe)super.getModel();
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	protected void resize(double dw, double dh) {
-		Recipe model = getBuilding();
+		Recipe model = getRecipe();
 		GridConfiguration grid = getMVCBundle().getViewer().getGridConfig();
 		double nw = Math.max(model.bounds.getWidth() + dw,
 				grid != null ? grid.getHorizontalSpacing() : 1);
@@ -54,7 +54,7 @@
 	/** {@inheritDoc} */
 	@Override
 	protected void move(double dx, double dy) {
-		Recipe model = getBuilding();
+		Recipe model = getRecipe();
 		GridConfiguration grid = getMVCBundle().getViewer().getGridConfig();
 		double nx =
 				Math.max(model.bounds.getX() + dx, grid != null ? grid.getHorizontalSpacing() : 1);
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/.ratings b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/.ratings
index 0f6ef64..4a0e129 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/.ratings
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/.ratings
@@ -1,11 +1,14 @@
-DeleteOperation.java d64e6682335914db46c0286c50082e05be716a90 YELLOW
+ComponentRecipe.java 01164da40a3e52e73e15a32b06d086856bfecb31 YELLOW
+CompositeRecipe.java 846f7c316a688af25ecd8c535861d23897aada2d YELLOW
+DeleteOperation.java eed34825d02911c3e53826a422631ef6097439ea RED
 Ingredient.java 523565b53a949da0e8e0578e6ef14c32e181fa35 YELLOW
 IngredientFlow.java 5ff4bce1d14b246db1cd8127de8a7e8b61e2735e YELLOW
-MainRecipe.java 3ec99e311170a5287723648f9ca9323d6d00d913 YELLOW
-MergeBeansRecipe.java 67828a116741743555e08ef9e82fd117ee5cd60d YELLOW
-NamedModelElementBase.java 5f1772ea357c194011b047eaa4f853bd51ce4db5 YELLOW
-Recipe.java afbe16a694b3775c45a38e7e3458bba213b0bd50 YELLOW
-SortBeansRecipe.java 7b1798f64cce6e7790e624be0a617bb93a2e3eaf YELLOW
+MainRecipe.java cef0fdeeddc3cf8773bea157d2b6d1c3cc592620 YELLOW
+MergeBeansRecipe.java 15f4c39419aa9be0865ba48bb0264b6cf6e65be8 YELLOW
+ModelFactory.java 069d5babce9103b1e99830a71e32d47bda383a4e YELLOW
+NamedModelElementBase.java b690424d7b143e98d737cd03361e417da0057f62 YELLOW
+Recipe.java 25c2c6f0dade2e8939dba3c5bc0744621ee63a5b YELLOW
+SortBeansRecipe.java d2216a02717d7646755a6227449ca49dd8ad8e45 YELLOW
 SpiderChartModel.java 335aa7f7a20224dfd167df6d9047207ede996dbe YELLOW
 Waste.java e29e0fe3216b34cdef836419af4aa33c3160c268 YELLOW
 WasteFlow.java 7e04c67a63d2f918e416953ddde0cb8a128b2f6f YELLOW
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/ComponentRecipe.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/ComponentRecipe.java
new file mode 100644
index 0000000..22a9753
--- /dev/null
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/ComponentRecipe.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2018 fortiss GmbH. 
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Florian Hoelzl (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.examples.function.architecture.playground.model;
+
+import static org.eclipse.systemfocus.kernel.base.utils.LayoutModelElementFactory.createRectangle;
+
+import org.eclipse.systemfocus.kernel.base.model.layout.EOrientation;
+
+/** A component recipe which is an atomic part of a composite recipe. */
+public final class ComponentRecipe extends Recipe {
+	/** Constructor. */
+	public ComponentRecipe(CompositeRecipe cr) {
+		name = "Recipe 0";
+		bounds = createRectangle(0, 0, 140, 100);
+		owner = cr;
+
+		Ingredient leftIngredient = new Ingredient();
+		leftIngredient.name = "leftIngredient";
+		leftIngredient.pure = false;
+		leftIngredient.freeBounds = createRectangle(0, 20, 20, 20);
+		leftIngredient.stickyOffset = 40;
+		leftIngredient.stickyOrientation = EOrientation.WEST;
+		leftIngredient.owner = this;
+		ingredients.add(leftIngredient);
+
+		Ingredient rightIngredient = new Ingredient();
+		rightIngredient.name = "rightIngredient";
+		rightIngredient.pure = true;
+		rightIngredient.freeBounds = createRectangle(0, 20, 20, 20);
+		leftIngredient.stickyOrientation = EOrientation.EAST;
+		rightIngredient.stickyOffset = 40;
+		rightIngredient.owner = this;
+		ingredients.add(rightIngredient);
+	}
+
+	/** Returns whether this component recipse is currently visible. */
+	public boolean isVisible() {
+		// wild cast works: see constructor
+		CompositeRecipe cr = (CompositeRecipe)owner;
+		return cr.expanded;
+	}
+}
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/CompositeRecipe.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/CompositeRecipe.java
new file mode 100644
index 0000000..6bd8d90
--- /dev/null
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/CompositeRecipe.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2018 fortiss GmbH. 
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Florian Hoelzl (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.examples.function.architecture.playground.model;
+
+import static org.eclipse.systemfocus.kernel.base.utils.LayoutModelElementFactory.createRectangle;
+
+import org.eclipse.systemfocus.kernel.base.model.layout.EOrientation;
+
+/** A composite recipe showing the hierarchic content nodes feature. */
+public final class CompositeRecipe extends Recipe {
+	/** Stores whether this composite recipe is expanded or not. */
+	public boolean expanded = true;
+
+	/** Constructor. */
+	public CompositeRecipe() {
+		Ingredient topIngredient = new Ingredient();
+		topIngredient.name = "topIngredient";
+		topIngredient.pure = false;
+		topIngredient.freeBounds = createRectangle(0, 20, 20, 20);
+		topIngredient.stickyOffset = 100;
+		topIngredient.stickyOrientation = EOrientation.NORTH;
+		topIngredient.owner = this;
+		ingredients.add(topIngredient);
+
+		ComponentRecipe comp0 = new ComponentRecipe(this);
+		comp0.name = "Recipe 0";
+		comp0.bounds = createRectangle(20, 40, 180, 100);
+		comp0.owner = this;
+		subRecipes.add(comp0);
+
+		ComponentRecipe comp1 = new ComponentRecipe(this);
+		comp1.name = "Recipe 1";
+		comp1.bounds = createRectangle(20, 160, 180, 100);
+		comp1.owner = this;
+		subRecipes.add(comp1);
+	}
+}
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/MainRecipe.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/MainRecipe.java
index 3ec99e3..cef0fde 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/MainRecipe.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/MainRecipe.java
@@ -35,6 +35,12 @@
 		sort.owner = this;
 		subRecipes.add(sort);
 
+		CompositeRecipe comp = new CompositeRecipe();
+		comp.name = "Composite Recipe";
+		comp.bounds = createRectangle(420, 320, 220, 280);
+		comp.owner = this;
+		subRecipes.add(comp);
+
 		Ingredient ingredientBlackBeans = new Ingredient();
 		ingredientBlackBeans.name = "blackBeans";
 		ingredientBlackBeans.freeBounds = createRectangle(20, 120, 20, 20);
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/MergeBeansRecipe.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/MergeBeansRecipe.java
index 67828a1..15f4c39 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/MergeBeansRecipe.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/MergeBeansRecipe.java
@@ -23,7 +23,6 @@
 	public MergeBeansRecipe() {
 		name = "MergeB&WBeans";
 		comment = "Merge the beans carefully one by one.";
-		merge = true;
 
 		Ingredient blackBeans = new Ingredient();
 		blackBeans.name = "blackBeans";
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/ModelFactory.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/ModelFactory.java
new file mode 100644
index 0000000..069d5ba
--- /dev/null
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/ModelFactory.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2018 fortiss GmbH. 
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     TODO: fullname hoelzl (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.examples.function.architecture.playground.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.model.IModelFactory;
+
+/** The {@link IModelFactory} for the recipe playground example. */
+public final class ModelFactory implements IModelFactory {
+	/** The main recipe. */
+	private final MainRecipe mainRecipe;
+
+	/** Constructor. */
+	public ModelFactory(MainRecipe mainRecipe) {
+		this.mainRecipe = mainRecipe;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<Object> getContentAnchorageModels(Object parent) {
+		if(parent instanceof Recipe) {
+			Recipe b = (Recipe)parent;
+			List<Object> result = new ArrayList<>();
+			result.addAll(b.ingredients);
+			result.addAll(b.results);
+			result.addAll(b.waste);
+			return result;
+		}
+		throw new IllegalArgumentException("Unknown model element class: " + parent.getClass());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Object getRootModel() {
+		return mainRecipe;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<Object> getDiagramAnchorageModels() {
+		List<Object> result = new ArrayList<>();
+		result.addAll(mainRecipe.ingredients);
+		result.addAll(mainRecipe.results);
+		result.addAll(mainRecipe.waste);
+		return result;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<Object> getContentModels() {
+		List<Object> result = new ArrayList<>();
+		for(Recipe r : mainRecipe.subRecipes) {
+			result.add(r);
+			if(r instanceof CompositeRecipe) {
+				result.addAll(r.subRecipes);
+			}
+		}
+		return result;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<Object> getLinkModels() {
+		List<Object> result = new ArrayList<>();
+		result.addAll(mainRecipe.ingredientFlows);
+		result.addAll(mainRecipe.wasteFlows);
+		return result;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Object getLinkStart(Object link) {
+		if(link instanceof IngredientFlow) {
+			IngredientFlow flow = (IngredientFlow)link;
+			return flow.sourceIngredient;
+		}
+		if(link instanceof WasteFlow) {
+			WasteFlow flow = (WasteFlow)link;
+			return flow.sourceWaste;
+		}
+		throw new IllegalArgumentException("Unknown model element class: " + link.getClass());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Object getLinkEnd(Object link) {
+		if(link instanceof IngredientFlow) {
+			IngredientFlow flow = (IngredientFlow)link;
+			return flow.targetIngredient;
+		}
+		if(link instanceof WasteFlow) {
+			WasteFlow flow = (WasteFlow)link;
+			return flow.targetWaste;
+		}
+		throw new IllegalArgumentException("Unknown model element class: " + link.getClass());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Object getParent(Object element) {
+		if(element instanceof ComponentRecipe) {
+			return ((Recipe)element).owner;
+		}
+		return null;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void update() {
+		// nothing to do here
+	}
+}
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/Recipe.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/Recipe.java
index afbe16a..25c2c6f 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/Recipe.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/Recipe.java
@@ -32,7 +32,6 @@
 	public List<Recipe> subRecipes = new ArrayList<>();
 	public List<IngredientFlow> ingredientFlows = new ArrayList<>();
 	public List<WasteFlow> wasteFlows = new ArrayList<>();
-	public boolean merge = true;
 	// layout data
 	public Rectangle bounds = createRectangle(50, 50, 100, 80);
 
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/SortBeansRecipe.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/SortBeansRecipe.java
index 7b1798f..d2216a0 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/SortBeansRecipe.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/model/SortBeansRecipe.java
@@ -23,7 +23,6 @@
 	public SortBeansRecipe() {
 		name = "SortBeans\nByColor";
 		comment = "Sort the beans using Quicksort!";
-		merge = false;
 
 		Ingredient blackAndWhiteBeans = new Ingredient();
 		blackAndWhiteBeans.name = "blackAndWhiteBeans";
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/.ratings b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/.ratings
index 212aff5..9dabe64 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/.ratings
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/.ratings
@@ -1,10 +1,13 @@
-CombinedIngredientContentAnchorageVisual.java b613392c728ee553fca40d6a08130558008ab4ba YELLOW
+CombinedIngredientContentAnchorageVisual.java c79dccbe83490158f76aa752763202433af19853 RED
+ComponentRecipeContentVisual.java 89298a110d244c0d55292b4a1e7632b3e40c4197 RED
+CompositeRecipeContentVisual.java 574166cb85e60da8bf02887a1c68d629e01d689b RED
 IngredientDiagramAnchorageVisual.java fa4c9b467d6ef82ed102c7411d2e6541f639587d YELLOW
 IngredientFlowLinkVisual.java c69b85ee553d8c222f10b69b4a1eade25bcbabc4 RED
 MergeRecipeContentVisual.java 5bfd767bf79f5e5ea19e49a2de9db6674d621af8 YELLOW
-PureIngredientContentAnchorageVisual.java 21678aa9ca33f3c0f3e18181776f7f0dd3a04f7c YELLOW
-SortRecipeContentVisual.java 31c080b24130f1f4b4029c1f38a66445ad080b0c RED
-VisualFactory.java 2e4928177cc5fbafc58bc8b73874863cd956d1b3 YELLOW
+PureIngredientContentAnchorageVisual.java 51bc5ff2e8b5fde46659d337430b7255d40eb42e RED
+RectangularRecipeVisualBase.java 90a7996df9b34a09335b040e3ec846d67c953940 RED
+SortRecipeContentVisual.java 0f7776ed670abc4aa0c81851810942460c4a0500 RED
+VisualFactory.java a657c64d7664fcf4535814679fdcc95e22b1dfe3 RED
 WasteContentAnchorageVisual.java 0274a960c4205141c6f9248c056f63cb32a57566 YELLOW
 WasteDiagramAnchorageVisual.java 4df7433151297d994ff2e3d0d4001a72650efde8 YELLOW
 WasteFlowLinkVisual.java 7fa5a0473a0304324790d95c18d374f2df4da77a RED
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/ComponentRecipeContentVisual.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/ComponentRecipeContentVisual.java
new file mode 100644
index 0000000..89298a1
--- /dev/null
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/ComponentRecipeContentVisual.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2018 fortiss GmbH.
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Florian Hoelzl (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.examples.function.architecture.playground.visuals;
+
+import static org.eclipse.systemfocus.kernel.common.ui.javafx.util.DummyIconUtils.getDummyIcon;
+
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.ComponentRecipe;
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.Recipe;
+import org.eclipse.systemfocus.kernel.base.model.layout.Rectangle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisual;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular.BeveledCornerContentVisualBase;
+
+import javafx.geometry.Dimension2D;
+import javafx.geometry.Rectangle2D;
+import javafx.scene.image.Image;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+
+/** {@link IVisual} for sort {@link Recipe}. */
+public final class ComponentRecipeContentVisual extends BeveledCornerContentVisualBase {
+	/** Constructor. */
+	public ComponentRecipeContentVisual(IContentMVCBundle mvcb) {
+		super(mvcb);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected boolean enableVisual() {
+		return getComponentRecipe().isVisible();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected boolean enableText() {
+		return enableVisual();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected boolean enableIcon() {
+		return enableVisual();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected boolean enableHitArea() {
+		return false;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected String getName() {
+		return getComponentRecipe().name;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected Image getIcon() {
+		return getDummyIcon();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Dimension2D getMinimumSize() {
+		return new Dimension2D(100, 80);
+	}
+
+	/** Returns the {@link ComponentRecipe} model element. */
+	private ComponentRecipe getComponentRecipe() {
+		return (ComponentRecipe)getModel();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Rectangle2D getModelBounds() {
+		// stored model bounds are relative to the parent
+		ComponentRecipe componentRecipe = getComponentRecipe();
+		Rectangle parentBounds = componentRecipe.owner.bounds;
+		Rectangle myBounds = componentRecipe.bounds;
+		int x = parentBounds.getX() + myBounds.getX();
+		int y = parentBounds.getY() + myBounds.getY();
+		return new Rectangle2D(x, y, myBounds.getWidth(), myBounds.getHeight());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected Paint getFillColor() {
+		return Color.LAVENDER;
+	}
+}
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/CompositeRecipeContentVisual.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/CompositeRecipeContentVisual.java
new file mode 100644
index 0000000..574166c
--- /dev/null
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/CompositeRecipeContentVisual.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2018 fortiss GmbH.
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Florian Hoelzl (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.examples.function.architecture.playground.visuals;
+
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.CompositeRecipe;
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.Recipe;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisual;
+
+import javafx.geometry.Point2D;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+
+/** {@link IVisual} for sort {@link Recipe}. */
+public final class CompositeRecipeContentVisual extends RectangularRecipeVisualBase {
+	/** Constructor. */
+	public CompositeRecipeContentVisual(IContentMVCBundle mvcb) {
+		super(mvcb);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected Point2D getTextAnchorLocation() {
+		return new Point2D(10, 10);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected Paint getFillColor() {
+		return Color.YELLOW;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected boolean enableExpandCollapseWidget() {
+		return true;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected boolean getExpandCollapseWidgetState() {
+		// wild cast works: see ModelFactory
+		CompositeRecipe cr = (CompositeRecipe)getModel();
+		return cr.expanded;
+	}
+}
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/MergeRecipeContentVisual.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/MergeRecipeContentVisual.java
index 933ac09..4e9c497 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/MergeRecipeContentVisual.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/MergeRecipeContentVisual.java
@@ -14,6 +14,7 @@
 package org.eclipse.systemfocus.examples.function.architecture.playground.visuals;
 
 import static org.eclipse.systemfocus.kernel.base.utils.BoundsUtils.convertRectangle;
+import static org.eclipse.systemfocus.kernel.common.ui.javafx.util.DummyIconUtils.getDummyIcon;
 
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.Recipe;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle;
@@ -22,6 +23,7 @@
 
 import javafx.geometry.Dimension2D;
 import javafx.geometry.Rectangle2D;
+import javafx.scene.image.Image;
 
 /** {@link IVisual} for merge {@link Recipe}. */
 public final class MergeRecipeContentVisual extends EllipticContentVisualBase {
@@ -38,6 +40,12 @@
 
 	/** {@inheritDoc} */
 	@Override
+	protected Image getIcon() {
+		return getDummyIcon();
+	}
+
+	/** {@inheritDoc} */
+	@Override
 	public Rectangle2D getModelBounds() {
 		return convertRectangle(getRecipe().bounds);
 	}
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/RectangularRecipeVisualBase.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/RectangularRecipeVisualBase.java
new file mode 100644
index 0000000..90a7996
--- /dev/null
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/RectangularRecipeVisualBase.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2018 fortiss GmbH. 
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     TODO: fullname hoelzl (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.examples.function.architecture.playground.visuals;
+
+import static org.eclipse.systemfocus.kernel.base.utils.BoundsUtils.convertRectangle;
+import static org.eclipse.systemfocus.kernel.common.ui.javafx.util.DummyIconUtils.getDummyIcon;
+
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.Recipe;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular.RectangularContentVisualBase;
+
+import javafx.geometry.Dimension2D;
+import javafx.geometry.Rectangle2D;
+import javafx.scene.image.Image;
+
+/** Base class for visuals of recipes displayed as rectangular shapes. */
+public abstract class RectangularRecipeVisualBase extends RectangularContentVisualBase {
+	/** Constructor. */
+	public RectangularRecipeVisualBase(IContentMVCBundle mvcb) {
+		super(mvcb);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected String getName() {
+		return getRecipe().name;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected Image getIcon() {
+		return getDummyIcon();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Rectangle2D getModelBounds() {
+		return convertRectangle(getRecipe().bounds);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Dimension2D getMinimumSize() {
+		return new Dimension2D(100, 80);
+	}
+
+	/** Returns the building model. */
+	private Recipe getRecipe() {
+		return (Recipe)super.getModel();
+	}
+}
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/SortRecipeContentVisual.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/SortRecipeContentVisual.java
index 31c080b..0f7776e 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/SortRecipeContentVisual.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/SortRecipeContentVisual.java
@@ -13,49 +13,14 @@
  *******************************************************************************/
 package org.eclipse.systemfocus.examples.function.architecture.playground.visuals;
 
-import static org.eclipse.systemfocus.kernel.base.utils.BoundsUtils.convertRectangle;
-
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.Recipe;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisual;
-import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular.RectangularContentVisualBase;
-
-import javafx.geometry.Dimension2D;
-import javafx.geometry.Rectangle2D;
 
 /** {@link IVisual} for sort {@link Recipe}. */
-public final class SortRecipeContentVisual extends RectangularContentVisualBase {
+public final class SortRecipeContentVisual extends RectangularRecipeVisualBase {
 	/** Constructor. */
 	public SortRecipeContentVisual(IContentMVCBundle mvcb) {
 		super(mvcb);
 	}
-
-	/** {@inheritDoc} */
-	@Override
-	protected String getName() {
-		return getRecipe().name;
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	public Rectangle2D getModelBounds() {
-		return convertRectangle(getRecipe().bounds);
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	public Dimension2D getMinimumSize() {
-		return new Dimension2D(100, 80);
-	}
-
-	/** Returns the building model. */
-	private Recipe getRecipe() {
-		return (Recipe)super.getModel();
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	protected boolean useRoundedRectangle() {
-		return false;
-	}
 }
diff --git a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/VisualFactory.java b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/VisualFactory.java
index 39500d3..a657c64 100644
--- a/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/VisualFactory.java
+++ b/example/org.eclipse.systemfocus.examples.function.architecture/src/org/eclipse/systemfocus/examples/function/architecture/playground/visuals/VisualFactory.java
@@ -13,9 +13,13 @@
  *******************************************************************************/
 package org.eclipse.systemfocus.examples.function.architecture.playground.visuals;
 
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.ComponentRecipe;
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.CompositeRecipe;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.Ingredient;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.IngredientFlow;
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.MergeBeansRecipe;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.Recipe;
+import org.eclipse.systemfocus.examples.function.architecture.playground.model.SortBeansRecipe;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.Waste;
 import org.eclipse.systemfocus.examples.function.architecture.playground.model.WasteFlow;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle;
@@ -35,10 +39,18 @@
 	public IContentVisual createContentVisual(IContentMVCBundle bundle) {
 		if(bundle.getModel() instanceof Recipe) {
 			Recipe r = (Recipe)bundle.getModel();
-			if(r.merge) {
+			if(r instanceof MergeBeansRecipe) {
 				return new MergeRecipeContentVisual(bundle);
 			}
-			return new SortRecipeContentVisual(bundle);
+			if(r instanceof SortBeansRecipe) {
+				return new SortRecipeContentVisual(bundle);
+			}
+			if(r instanceof CompositeRecipe) {
+				return new CompositeRecipeContentVisual(bundle);
+			}
+			if(r instanceof ComponentRecipe) {
+				return new ComponentRecipeContentVisual(bundle);
+			}
 		}
 		throw new IllegalArgumentException(
 				"Unknown model element class: " + bundle.getModel().getClass());
diff --git a/example/releng/org.eclipse.systemfocus.examples.function.architecture.product/linux64.launch b/example/releng/org.eclipse.systemfocus.examples.function.architecture.product/linux64.launch
index 84c9d7f..93ec588 100644
--- a/example/releng/org.eclipse.systemfocus.examples.function.architecture.product/linux64.launch
+++ b/example/releng/org.eclipse.systemfocus.examples.function.architecture.product/linux64.launch
@@ -27,7 +27,7 @@
 <stringAttribute key="pde.version" value="3.3"/>
 <stringAttribute key="product" value="org.eclipse.systemfocus.examples.function.architecture.product"/>
 <stringAttribute key="productFile" value="/org.eclipse.systemfocus.examples.function.architecture.product/org.eclipse.systemfocus.examples.function.architecture.product"/>
-<stringAttribute key="selected_target_plugins" value="com.google.guava@default:default,com.google.inject.multibindings@default:false,com.google.inject@default:default,com.ibm.icu.base@default:default,com.ibm.icu@default:default,com.jcraft.jsch@default:default,javaewah@default:default,javax.activation@default:default,javax.annotation.jre@default:default,javax.inject@default:default,javax.servlet@default:default,javax.xml.jre@default:default,org.apache.commons.collections@default:default,org.apache.commons.io@default:default,org.apache.commons.jxpath@default:default,org.apache.commons.lang3@default:default,org.apache.commons.lang@default:default,org.apache.commons.logging@default:default,org.apache.commons.math@default:default,org.apache.commons.net@default:default,org.apache.commons.pool@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.apache.felix.gogo.shell@default:default,org.apache.felix.scr@default:default,org.apache.log4j@default:default,org.eclipse.core.commands@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.databinding.observable@default:default,org.eclipse.core.databinding.property@default:default,org.eclipse.core.databinding@default:default,org.eclipse.core.expressions@default:default,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.runtime@default:true,org.eclipse.e4.core.commands@default:default,org.eclipse.e4.core.contexts@default:default,org.eclipse.e4.core.di.annotations@default:default,org.eclipse.e4.core.di.extensions.supplier@default:default,org.eclipse.e4.core.di.extensions@default:default,org.eclipse.e4.core.di@default:default,org.eclipse.e4.core.services@default:default,org.eclipse.e4.emf.xpath@default:default,org.eclipse.e4.ui.di@default:default,org.eclipse.e4.ui.model.workbench@default:default,org.eclipse.e4.ui.services@default:default,org.eclipse.e4.ui.workbench@default:default,org.eclipse.emf.common@default:default,org.eclipse.emf.databinding@default:default,org.eclipse.emf.ecore.change@default:default,org.eclipse.emf.ecore.xmi@default:default,org.eclipse.emf.ecore@default:default,org.eclipse.emf.edit@default:default,org.eclipse.equinox.app@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.concurrent@default:default,org.eclipse.equinox.console@default:default,org.eclipse.equinox.ds@2:true,org.eclipse.equinox.event@2:true,org.eclipse.equinox.launcher.gtk.linux.x86_64@default:false,org.eclipse.equinox.launcher@default:default,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.equinox.simpleconfigurator@1:true,org.eclipse.equinox.util@default:default,org.eclipse.fx.core.databinding@default:default,org.eclipse.fx.core.di.context@default:default,org.eclipse.fx.core.di@default:default,org.eclipse.fx.core.fxml@default:default,org.eclipse.fx.core.guice@default:default,org.eclipse.fx.core.log4j@default:default,org.eclipse.fx.core.slf4j@default:default,org.eclipse.fx.core@default:default,org.eclipse.fx.osgi.util@default:default,org.eclipse.fx.ui.animation@default:default,org.eclipse.fx.ui.controls@default:default,org.eclipse.fx.ui.databinding@default:default,org.eclipse.fx.ui.di@default:default,org.eclipse.fx.ui.dialogs@default:default,org.eclipse.fx.ui.keybindings.e4@default:default,org.eclipse.fx.ui.keybindings.generic@default:default,org.eclipse.fx.ui.keybindings@default:default,org.eclipse.fx.ui.panes@default:default,org.eclipse.fx.ui.services@default:default,org.eclipse.fx.ui.theme@default:default,org.eclipse.fx.ui.workbench.base@default:default,org.eclipse.fx.ui.workbench.fx@default:default,org.eclipse.fx.ui.workbench.renderers.base@default:default,org.eclipse.fx.ui.workbench.renderers.fx@default:default,org.eclipse.fx.ui.workbench.services@default:default,org.eclipse.jdt.annotation@default:default,org.eclipse.jgit@default:default,org.eclipse.osgi.compatibility.state@default:false,org.eclipse.osgi.services@default:default,org.eclipse.osgi.util@default:default,org.eclipse.osgi@-1:true,org.hamcrest.core@default:default,org.junit@default:default,org.slf4j.api@default:default"/>
+<stringAttribute key="selected_target_plugins" value="com.google.guava@default:default,com.google.inject.multibindings@default:false,com.google.inject@default:default,com.ibm.icu.base@default:default,com.ibm.icu@default:default,com.jcraft.jsch@default:default,javaewah@default:default,javax.activation@default:default,javax.annotation@default:default,javax.inject@default:default,javax.servlet@default:default,javax.xml.jre@default:default,org.apache.commons.collections@default:default,org.apache.commons.io@default:default,org.apache.commons.jxpath@default:default,org.apache.commons.lang3@default:default,org.apache.commons.lang@default:default,org.apache.commons.logging@default:default,org.apache.commons.math@default:default,org.apache.commons.net@default:default,org.apache.commons.pool@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.apache.felix.gogo.shell@default:default,org.apache.felix.scr@1:true,org.apache.log4j@default:default,org.eclipse.core.commands@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.databinding.observable@default:default,org.eclipse.core.databinding.property@default:default,org.eclipse.core.databinding@default:default,org.eclipse.core.expressions@default:default,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.runtime@default:true,org.eclipse.e4.core.commands@default:default,org.eclipse.e4.core.contexts@default:default,org.eclipse.e4.core.di.annotations@default:default,org.eclipse.e4.core.di.extensions.supplier@default:default,org.eclipse.e4.core.di.extensions@default:default,org.eclipse.e4.core.di@default:default,org.eclipse.e4.core.services@default:default,org.eclipse.e4.emf.xpath@default:default,org.eclipse.e4.ui.di@default:default,org.eclipse.e4.ui.model.workbench@default:default,org.eclipse.e4.ui.services@default:default,org.eclipse.e4.ui.workbench@default:default,org.eclipse.emf.common@default:default,org.eclipse.emf.databinding@default:default,org.eclipse.emf.ecore.change@default:default,org.eclipse.emf.ecore.xmi@default:default,org.eclipse.emf.ecore@default:default,org.eclipse.emf.edit@default:default,org.eclipse.equinox.app@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.concurrent@default:default,org.eclipse.equinox.console@default:default,org.eclipse.equinox.ds@2:true,org.eclipse.equinox.event@2:true,org.eclipse.equinox.launcher.gtk.linux.x86_64@default:false,org.eclipse.equinox.launcher@default:default,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.equinox.simpleconfigurator@1:true,org.eclipse.equinox.util@default:default,org.eclipse.fx.core.databinding@default:default,org.eclipse.fx.core.di.context@default:default,org.eclipse.fx.core.di@default:default,org.eclipse.fx.core.fxml@default:default,org.eclipse.fx.core.guice@default:default,org.eclipse.fx.core.log4j@default:default,org.eclipse.fx.core.slf4j@default:default,org.eclipse.fx.core@default:default,org.eclipse.fx.osgi.util@default:default,org.eclipse.fx.ui.animation@default:default,org.eclipse.fx.ui.controls@default:default,org.eclipse.fx.ui.databinding@default:default,org.eclipse.fx.ui.di@default:default,org.eclipse.fx.ui.dialogs@default:default,org.eclipse.fx.ui.keybindings.e4@default:default,org.eclipse.fx.ui.keybindings.generic@default:default,org.eclipse.fx.ui.keybindings@default:default,org.eclipse.fx.ui.panes@default:default,org.eclipse.fx.ui.services@default:default,org.eclipse.fx.ui.theme@default:default,org.eclipse.fx.ui.workbench.base@default:default,org.eclipse.fx.ui.workbench.fx@default:default,org.eclipse.fx.ui.workbench.renderers.base@default:default,org.eclipse.fx.ui.workbench.renderers.fx@default:default,org.eclipse.fx.ui.workbench.services@default:default,org.eclipse.jdt.annotation@default:default,org.eclipse.jgit@default:default,org.eclipse.osgi.compatibility.state@default:false,org.eclipse.osgi.services@default:default,org.eclipse.osgi.util@default:default,org.eclipse.osgi@-1:true,org.hamcrest.core@default:default,org.junit@default:default,org.slf4j.api@default:default"/>
 <stringAttribute key="selected_workspace_plugins" value="org.eclipse.systemfocus.examples.function.architecture@default:default,org.eclipse.systemfocus.kernel.base.ui@default:default,org.eclipse.systemfocus.kernel.base@default:default,org.eclipse.systemfocus.kernel.common.ui@default:default,org.eclipse.systemfocus.kernel.common@default:default,org.eclipse.systemfocus.kernel.core.ui@default:default,org.eclipse.systemfocus.kernel.core@default:default"/>
 <booleanAttribute key="show_selected_only" value="false"/>
 <booleanAttribute key="tracing" value="false"/>
diff --git a/oomph-setup/systemfocus-committer.setup b/oomph-setup/systemfocus-committer.setup
index f7c298d..81638da 100644
--- a/oomph-setup/systemfocus-committer.setup
+++ b/oomph-setup/systemfocus-committer.setup
@@ -42,12 +42,12 @@
       label="E(fx)clipse IDE, EMF SDK">
     <requirement
         name="org.eclipse.fx.ide.feature.feature.group"
-        versionRange="[3.0.0,3.1.0)"/>
+        versionRange="[3.3.0,3.4.0)"/>
     <requirement
         name="org.eclipse.emf.sdk.feature.group"
-        versionRange="[2.13.0,2.14.0)"/>
+        versionRange="[2.14.0,2.15.0)"/>
     <repository
-        url="http://download.eclipse.org/releases/oxygen"/>
+        url="http://download.eclipse.org/releases/photon"/>
     <description>Install the tools needed in the IDE to work with the source code for ${scope.project.label}.</description>
   </setupTask>
   <setupTask
@@ -77,13 +77,15 @@
       <requirement
           name="org.eclipse.fx.target.feature.feature.group"/>
       <repositoryList
-          name="Oxygen">
+          name="Photon">
         <repository
-            url="http://download.eclipse.org/efxclipse/runtime-released/3.2.0/site"/>
+            url="http://download.eclipse.org/efxclipse/runtime-released/3.3.0/site"/>
         <repository
-            url="http://download.eclipse.org/tools/orbit/downloads/drops/R20180330011457/repository/"/>
+            url="http://download.eclipse.org/tools/orbit/downloads/drops/R20180606145124/repository/"/>
         <repository
             url="http://download.eclipse.org/egit/updates/"/>
+        <repository
+            url="http://download.eclipse.org/releases/photon"/>
       </repositoryList>
     </targlet>
     <targlet
diff --git a/oomph-setup/systemfocus-contributor.setup b/oomph-setup/systemfocus-contributor.setup
index 9bbd9b4..5b7e4a8 100644
--- a/oomph-setup/systemfocus-contributor.setup
+++ b/oomph-setup/systemfocus-contributor.setup
@@ -42,12 +42,12 @@
       label="E(fx)clipse IDE, EMF SDK">
     <requirement
         name="org.eclipse.fx.ide.feature.feature.group"
-        versionRange="[3.0.0,3.1.0)"/>
+        versionRange="[3.3.0,3.4.0)"/>
     <requirement
         name="org.eclipse.emf.sdk.feature.group"
-        versionRange="[2.13.0,2.14.0)"/>
+        versionRange="[2.14.0,2.15.0)"/>
     <repository
-        url="http://download.eclipse.org/releases/oxygen"/>
+        url="http://download.eclipse.org/releases/photon"/>
     <description>Install the tools needed in the IDE to work with the source code for ${scope.project.label}.</description>
   </setupTask>
   <setupTask
@@ -77,13 +77,15 @@
       <requirement
           name="org.eclipse.fx.target.feature.feature.group"/>
       <repositoryList
-          name="Oxygen">
+          name="Photon">
         <repository
             url="http://download.eclipse.org/efxclipse/runtime-released/3.2.0/site"/>
         <repository
-            url="http://download.eclipse.org/tools/orbit/downloads/drops/R20180330011457/repository/"/>
+            url="http://download.eclipse.org/tools/orbit/downloads/drops/R20180606145124/repository/"/>
         <repository
             url="http://download.eclipse.org/egit/updates/"/>
+        <repository
+            url="http://download.eclipse.org/releases/photon"/>
       </repositoryList>
     </targlet>
     <targlet
diff --git a/org.eclipse.systemfocus.kernel.base.ui/src/org/eclipse/systemfocus/kernel/base/ui/graphical/editor/controller/.ratings b/org.eclipse.systemfocus.kernel.base.ui/src/org/eclipse/systemfocus/kernel/base/ui/graphical/editor/controller/.ratings
index 8a704ea..0c4c726 100644
--- a/org.eclipse.systemfocus.kernel.base.ui/src/org/eclipse/systemfocus/kernel/base/ui/graphical/editor/controller/.ratings
+++ b/org.eclipse.systemfocus.kernel.base.ui/src/org/eclipse/systemfocus/kernel/base/ui/graphical/editor/controller/.ratings
@@ -1,4 +1,4 @@
-EObjectBasedDiagramControllerBase.java 716bad8e6a6b052164e3d094da8f3fd5b6251d69 RED
+EObjectBasedDiagramControllerBase.java 0ae7fd02ae1dffd02a041b691313ff06b6f46c4a RED
 EObjectBasedRectangularResizableContentControllerBase.java 3d75a2c5e5a4578d027ecc98c9ca6dce59dd0775 YELLOW
 LayoutedModelElementBasedContentAnchorageController.java 21d885a9c68e3497ab5237853fe7384300e09c59 RED
 LayoutedModelElementBasedDiagramAnchorageController.java 7528b3f92480bb01299223b236afe9470a41f172 RED
diff --git a/org.eclipse.systemfocus.kernel.base/model/base.genmodel b/org.eclipse.systemfocus.kernel.base/model/base.genmodel
index 5f23d61..7a6a8fe 100644
--- a/org.eclipse.systemfocus.kernel.base/model/base.genmodel
+++ b/org.eclipse.systemfocus.kernel.base/model/base.genmodel
@@ -14,7 +14,8 @@
         <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference base.ecore#//element/IModelElement/specifications"/>
         <genFeatures notify="false" createChild="false" propertySortChoices="true"
             ecoreFeature="ecore:EReference base.ecore#//element/IModelElement/referencedBy"/>
-        <genOperations ecoreOperation="base.ecore#//element/IModelElement/addSpecification">
+        <genOperations ecoreOperation="base.ecore#//element/IModelElement/addSpecification"
+            body="org.eclipse.systemfocus.kernel.base.model.element.impl.IModelElementStaticImpl.addSpecification(this, spec);">
           <genParameters ecoreParameter="base.ecore#//element/IModelElement/addSpecification/spec"/>
         </genOperations>
       </genClasses>
@@ -75,34 +76,34 @@
       <genClasses ecoreClass="base.ecore#//layout/Point">
         <genFeatures createChild="false" ecoreFeature="ecore:EAttribute base.ecore#//layout/Point/x"/>
         <genFeatures createChild="false" ecoreFeature="ecore:EAttribute base.ecore#//layout/Point/y"/>
-        <genOperations ecoreOperation="base.ecore#//layout/Point/getDifference">
+        <genOperations ecoreOperation="base.ecore#//layout/Point/getDifference" body="return PointStaticImpl.getDifference(this, compPoint);">
           <genParameters ecoreParameter="base.ecore#//layout/Point/getDifference/compPoint"/>
         </genOperations>
-        <genOperations ecoreOperation="base.ecore#//layout/Point/getTranslated">
+        <genOperations ecoreOperation="base.ecore#//layout/Point/getTranslated" body="return PointStaticImpl.getTranslated(this, offsetX, offsetY);">
           <genParameters ecoreParameter="base.ecore#//layout/Point/getTranslated/offsetX"/>
           <genParameters ecoreParameter="base.ecore#//layout/Point/getTranslated/offsetY"/>
         </genOperations>
-        <genOperations ecoreOperation="base.ecore#//layout/Point/scale">
+        <genOperations ecoreOperation="base.ecore#//layout/Point/scale" body="return PointStaticImpl.scale(this, factor);">
           <genParameters ecoreParameter="base.ecore#//layout/Point/scale/factor"/>
         </genOperations>
-        <genOperations ecoreOperation="base.ecore#//layout/Point/translate">
+        <genOperations ecoreOperation="base.ecore#//layout/Point/translate" body="return PointStaticImpl.translate(this, dim.getWidth(), dim.getHeight());">
           <genParameters ecoreParameter="base.ecore#//layout/Point/translate/dim"/>
         </genOperations>
-        <genOperations ecoreOperation="base.ecore#//layout/Point/translate.1">
+        <genOperations ecoreOperation="base.ecore#//layout/Point/translate.1" body="return PointStaticImpl.translate(this, offsetX, offsetY);">
           <genParameters ecoreParameter="base.ecore#//layout/Point/translate.1/offsetX"/>
           <genParameters ecoreParameter="base.ecore#//layout/Point/translate.1/offsetY"/>
         </genOperations>
-        <genOperations ecoreOperation="base.ecore#//layout/Point/translate.2">
+        <genOperations ecoreOperation="base.ecore#//layout/Point/translate.2" body="return PointStaticImpl.translate(this, point.getX(), point.getY());">
           <genParameters ecoreParameter="base.ecore#//layout/Point/translate.2/point"/>
         </genOperations>
       </genClasses>
       <genClasses ecoreClass="base.ecore#//layout/Dimension">
         <genFeatures createChild="false" ecoreFeature="ecore:EAttribute base.ecore#//layout/Dimension/width"/>
         <genFeatures createChild="false" ecoreFeature="ecore:EAttribute base.ecore#//layout/Dimension/height"/>
-        <genOperations ecoreOperation="base.ecore#//layout/Dimension/getScaled">
+        <genOperations ecoreOperation="base.ecore#//layout/Dimension/getScaled" body="return DimensionStaticImpl.scale(this, factor);">
           <genParameters ecoreParameter="base.ecore#//layout/Dimension/getScaled/factor"/>
         </genOperations>
-        <genOperations ecoreOperation="base.ecore#//layout/Dimension/scale">
+        <genOperations ecoreOperation="base.ecore#//layout/Dimension/scale" body="return DimensionStaticImpl.scale(this, factor);">
           <genParameters ecoreParameter="base.ecore#//layout/Dimension/scale/factor"/>
         </genOperations>
       </genClasses>
@@ -111,11 +112,12 @@
         <genFeatures createChild="false" ecoreFeature="ecore:EAttribute base.ecore#//layout/Rectangle/y"/>
         <genFeatures createChild="false" ecoreFeature="ecore:EAttribute base.ecore#//layout/Rectangle/width"/>
         <genFeatures createChild="false" ecoreFeature="ecore:EAttribute base.ecore#//layout/Rectangle/height"/>
-        <genOperations ecoreOperation="base.ecore#//layout/Rectangle/setLocation">
+        <genOperations ecoreOperation="base.ecore#//layout/Rectangle/setLocation"
+            body="return RectangleStaticImpl.setLocation(this, x, y);">
           <genParameters ecoreParameter="base.ecore#//layout/Rectangle/setLocation/x"/>
           <genParameters ecoreParameter="base.ecore#//layout/Rectangle/setLocation/y"/>
         </genOperations>
-        <genOperations ecoreOperation="base.ecore#//layout/Rectangle/setSize">
+        <genOperations ecoreOperation="base.ecore#//layout/Rectangle/setSize" body="return RectangleStaticImpl.setSize(this, width, height);">
           <genParameters ecoreParameter="base.ecore#//layout/Rectangle/setSize/width"/>
           <genParameters ecoreParameter="base.ecore#//layout/Rectangle/setSize/height"/>
         </genOperations>
@@ -143,15 +145,21 @@
       <genClasses image="false" ecoreClass="base.ecore#//base/LocalConnectorBase"/>
       <genClasses image="false" ecoreClass="base.ecore#//base/ConnectionSegmentBase"/>
       <genClasses ecoreClass="base.ecore#//base/LibraryElementBase">
-        <genOperations ecoreOperation="base.ecore#//base/LibraryElementBase/getURI"/>
-        <genOperations ecoreOperation="base.ecore#//base/LibraryElementBase/getName"/>
+        <genOperations ecoreOperation="base.ecore#//base/LibraryElementBase/getURI"
+            body="return LibraryElementBaseStaticImpl.getURI(this);"/>
+        <genOperations ecoreOperation="base.ecore#//base/LibraryElementBase/getName"
+            body="return LibraryElementBaseStaticImpl.getName(this);"/>
       </genClasses>
       <genClasses image="false" ecoreClass="base.ecore#//base/DerivedAnnotationBase">
         <genTypeParameters ecoreTypeParameter="base.ecore#//base/DerivedAnnotationBase/T"/>
-        <genOperations ecoreOperation="base.ecore#//base/DerivedAnnotationBase/getValue"/>
-        <genOperations ecoreOperation="base.ecore#//base/DerivedAnnotationBase/getDerivedFeature"/>
-        <genOperations ecoreOperation="base.ecore#//base/DerivedAnnotationBase/isUserAnnotatedValuePreferred"/>
-        <genOperations ecoreOperation="base.ecore#//base/DerivedAnnotationBase/getUserAnnotatedValue"/>
+        <genOperations ecoreOperation="base.ecore#//base/DerivedAnnotationBase/getValue"
+            body="return DerivedAnnotationBaseStaticImpl.getValue(this);"/>
+        <genOperations ecoreOperation="base.ecore#//base/DerivedAnnotationBase/getDerivedFeature"
+            body="return null;"/>
+        <genOperations ecoreOperation="base.ecore#//base/DerivedAnnotationBase/isUserAnnotatedValuePreferred"
+            body="return true;"/>
+        <genOperations ecoreOperation="base.ecore#//base/DerivedAnnotationBase/getUserAnnotatedValue"
+            body="return null;"/>
       </genClasses>
     </nestedGenPackages>
     <nestedGenPackages prefix="Visualization" basePackage="org.eclipse.systemfocus.kernel.base.model"
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/.ratings b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/.ratings
index f0e8381..a8795f5 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/.ratings
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/.ratings
@@ -1,7 +1,6 @@
 DiagramLayers.java 14cd8766eec2aa4da4f698b134a6f2c22a0b5c2e YELLOW
-DiagramViewer.java 6a1b84bab3e11fd21b18aa7dd3255db8559de7f8 YELLOW
+DiagramViewer.java e024c1a291465de5c43ae7736574331993a314b4 RED
 DiagramViewerDefaultTags.java dd93a45a79cf386696762b12460080d8310001b4 YELLOW
-EDragGesture.java 222f421d3dc99dbed1dc28c6428a60ce8e6df615 YELLOW
 FeedbackChange.java 529ff9430cdac3a906bee9dd72de53bc04a2d071 YELLOW
-GridCanvasVisual.java 3345a95fd584eb1ec35ecde7d441801dd828b591 YELLOW
+GridCanvasVisual.java cdd8ff2a66fdf9b6b57cb0ac0c227f256443dc82 RED
 MouseState.java 80b5941c06c72765565a0d2d6092658d49fbb625 YELLOW
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/DiagramViewer.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/DiagramViewer.java
index 6a1b84b..3a5d110 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/DiagramViewer.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/DiagramViewer.java
@@ -30,7 +30,10 @@
 import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.SECONDARY_SELECTION_TAG;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.configuration.GridConfiguration;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IController;
@@ -92,13 +95,13 @@
 	/** The list of secondary selected {@link IMVCBundle}. */
 	private List<IMVCBundle> secondarySelectedBundles = new ArrayList<>();
 	/** The {@link IMVCBundle}s for content elements. */
-	private List<IContentMVCBundle> contentBundles = new ArrayList<>();
+	private Map<Object, IContentMVCBundle> contentBundles = new HashMap<>();
 	/** The {@link IMVCBundle}s for diagram anchorage elements. */
-	private List<IDiagramAnchorageMVCBundle> diagramAnchorageBundles = new ArrayList<>();
+	private Map<Object, IDiagramAnchorageMVCBundle> diagramAnchorageBundles = new HashMap<>();
 	/** The {@link IMVCBundle}s for content anchorage elements. */
-	private List<IContentAnchorageMVCBundle> contentAnchorageBundles = new ArrayList<>();
+	private Map<Object, IContentAnchorageMVCBundle> contentAnchorageBundles = new HashMap<>();
 	/** The {@link IMVCBundle}s for link elements. */
-	private List<ILinkMVCBundle> linkBundles = new ArrayList<>();
+	private Map<Object, ILinkMVCBundle> linkBundles = new HashMap<>();
 	/** The list of possible link targets. */
 	private List<IMVCBundle> possibleLinkTargets = new ArrayList<IMVCBundle>();
 	/** The {@link IDiagramMVCBundle} of this viewer. */
@@ -124,7 +127,7 @@
 	/** The current context menu. */
 	private ContextMenu contextMenu = null;
 	/** The graph for straight links. */
-	private LineLinkGraph lineLinkGraph = new LineLinkGraph();
+	private LineLinkGraph lineLinkGraph;
 
 	/** The selection changed runnable used to notify about selection changes. */
 	private final Runnable selectionChangedRunnable;
@@ -153,6 +156,8 @@
 		this.visualFactory = requireNonNull(visualFactory);
 		this.controllerFactory = requireNonNull(controllerFactory);
 
+		lineLinkGraph = null;
+
 		// grid canvas
 		gridCanvasVisual = new GridCanvasVisual(this, gridConfig);
 		// viewer pane
@@ -241,7 +246,7 @@
 		// update the viewer content
 		updateContentObjects();
 
-		linkHighlightingEnabled = true;
+		linkHighlighting = true;
 	}
 
 	/** Returns the {@link IDiagramMVCBundle}. */
@@ -327,8 +332,7 @@
 
 	/** Initializes the viewer content. */
 	private void updateContentObjects() {
-		// clean up previous visuals and bundles
-		detachVisualsFromSceneAndClearBundles();
+		possibleLinkTargets.clear();
 		// remember old selection
 		Object oldSelection =
 				primarySelectedBundle != null ? primarySelectedBundle.getModel() : null;
@@ -337,48 +341,108 @@
 		}
 		// before creating any content, allow model factory to update
 		modelFactory.update();
-		// create bundles from elements provided by the model factory
-		diagramBundle = new DiagramMVCBundle(getRootElement(), this);
-		diagramBundle.setVisual(getGridCanvasVisual());
+		// 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
+		// create diagram bundle if necessary
+		if(diagramBundle == null || diagramBundle.getModel() != getRootElement()) {
+			diagramBundle = new DiagramMVCBundle(getRootElement(), this);
+			diagramBundle.setVisual(getGridCanvasVisual());
+		}
 		IController diagramController = controllerFactory.createDiagramController(diagramBundle);
 		diagramBundle.setController(diagramController);
 		if(diagramController.isPossibleLinkTarget()) {
 			possibleLinkTargets.add(diagramBundle);
 		}
-		addVisualNodesAndAttach(diagramBundle);
-
 		// content and content anchorages
 		for(Object m : modelFactory.getContentModels()) {
-			initAttachedContentAnchorages(initContent(m, oldSelection), oldSelection);
+			IContentMVCBundle bundle = contentBundles.remove(m);
+			if(bundle == null) {
+				bundle = initContentBundle(m);
+			}
+			newContentBundles.put(m, bundle);
+			if(m == oldSelection) {
+				setSingleSelectedMVCBundle(bundle);
+			}
+			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);
+				}
+				if(cBundle.getController().isPossibleLinkTarget()) {
+					possibleLinkTargets.add(cBundle);
+				}
+			}
 		}
 		// setup parent relationship between content
 		for(Object m : modelFactory.getContentModels()) {
-			initParent(m);
-		}
-		// diagram anchorages
-		for(Object fi : modelFactory.getDiagramAnchorageModels()) {
-			initDiagramAnchorages(fi, oldSelection);
-		}
-		// links
-		for(Object c : modelFactory.getLinkModels()) {
-			initLinks(c, oldSelection);
-		}
-	}
-
-	/**
-	 * Initializes the parent-child relation between content bundles as given by the model factory.
-	 */
-	private void initParent(Object m) {
-		Object parentModel = modelFactory.getParent(m);
-		if(parentModel != null) {
-			IContentMVCBundle parentBundle = contentBundles.stream()
-					.filter(cmvcb -> cmvcb.getModel() == parentModel).findAny().orElse(null);
-			IContentMVCBundleWithParent elementBundle = (IContentMVCBundleWithParent)contentBundles
-					.stream().filter(cmvcb -> cmvcb.getModel() == m).findAny().orElse(null);
-			if(elementBundle != null && parentBundle != null) {
-				elementBundle.setParentBundle(parentBundle);
+			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);
+				}
 			}
 		}
+		// diagram anchorages
+		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);
+			}
+			if(bundle.getController().isPossibleLinkTarget()) {
+				possibleLinkTargets.add(bundle);
+			}
+		}
+		// links
+		for(Object link : modelFactory.getLinkModels()) {
+			ILinkMVCBundle bundle = linkBundles.remove(link);
+			if(bundle == null) {
+				bundle = initLinks(link, newDiagramAnchorageBundles, newContentAnchorageBundles);
+			}
+			newLinkBundles.put(link, bundle);
+			if(link == oldSelection) {
+				setSingleSelectedMVCBundle(bundle);
+			}
+		}
+		// iterate through all old maps and remove MVCB references
+		for(ILinkMVCBundle b : linkBundles.values()) {
+			b.remove();
+		}
+		for(IContentAnchorageMVCBundle b : contentAnchorageBundles.values()) {
+			b.remove();
+		}
+		for(IDiagramAnchorageMVCBundle b : diagramAnchorageBundles.values()) {
+			b.remove();
+		}
+		for(IContentMVCBundle b : contentBundles.values()) {
+			b.remove();
+		}
+		// set new maps active
+		this.contentBundles = newContentBundles;
+		this.diagramAnchorageBundles = newDiagramAnchorageBundles;
+		this.contentAnchorageBundles = newContentAnchorageBundles;
+		this.linkBundles = newLinkBundles;
+		// update visuals after clearing the scene graph
+		layers.clearLayers();
+		updateAllVisuals();
 	}
 
 	/** Returns the dummy {@link IVisual} use by the {@link #diagramBundle}. */
@@ -386,68 +450,16 @@
 		return gridCanvasVisual;
 	}
 
-	/** Detaches existing visuals from the scene and clears all bundles. */
-	private void detachVisualsFromSceneAndClearBundles() {
-		possibleLinkTargets.clear();
-
-		for(ILinkMVCBundle lBundle : linkBundles) {
-			removeVisualNodesAndDetach(lBundle);
-		}
-		linkBundles.clear();
-		for(IContentAnchorageMVCBundle caBundle : contentAnchorageBundles) {
-			removeVisualNodesAndDetach(caBundle);
-		}
-		contentAnchorageBundles.clear();
-		for(IContentMVCBundle cBundle : contentBundles) {
-			removeVisualNodesAndDetach(cBundle);
-		}
-		contentBundles.clear();
-		for(IDiagramAnchorageMVCBundle daBundle : diagramAnchorageBundles) {
-			removeVisualNodesAndDetach(daBundle);
-		}
-		diagramAnchorageBundles.clear();
-		if(diagramBundle != null) {
-			removeVisualNodesAndDetach(diagramBundle);
-			diagramBundle = null;
-		}
-		// make sure no forgotten nodes remain
-		layers.clearLayers();
-	}
-
-	/** Removes the visual nodes and detaches the bundle from listeners. */
-	private void removeVisualNodesAndDetach(IMVCBundle bundle) {
-		if(bundle.getVisual() != null) {
-			bundle.getVisual().removeNodes(layers);
-		}
-		bundle.detach();
-	}
-
-	/** Adds the visual nodes and attaches the bundle to listeners. */
-	private void addVisualNodesAndAttach(IMVCBundle bundle) {
-		if(bundle.getVisual() != null) {
-			bundle.getVisual().addNodes(layers);
-		}
-		bundle.attach();
-	}
-
 	/**
 	 * Initializes the {@link MVCBundleBase} for the given element and adds it to the
 	 * visuals.
 	 */
-	private IContentMVCBundle initContent(Object m, Object oldSelection) {
+	private IContentMVCBundle initContentBundle(Object m) {
 		IContentMVCBundle mvcb = new ContentMVCBundle(m, diagramBundle, this);
 		IContentVisual visual = visualFactory.createContentVisual(mvcb);
 		mvcb.setVisual(visual);
 		IController contentController = controllerFactory.createContentController(mvcb);
 		mvcb.setController(contentController);
-		if(contentController.isPossibleLinkTarget()) {
-			possibleLinkTargets.add(mvcb);
-		}
-		contentBundles.add(mvcb);
-		addVisualNodesAndAttach(mvcb);
-		if(m == oldSelection) {
-			setSingleSelectedMVCBundle(mvcb);
-		}
 		return mvcb;
 	}
 
@@ -455,32 +467,14 @@
 	 * Initializes the {@link MVCBundleBase} for the given element and adds it to the
 	 * visuals.
 	 */
-	private void initDiagramAnchorages(Object m, Object oldSelection) {
+	private DiagramAnchorageMVCBundle initDiagramAnchorages(Object m) {
 		DiagramAnchorageMVCBundle mvcb = new DiagramAnchorageMVCBundle(m, diagramBundle, this);
 		IDiagramAnchorageVisual visual = visualFactory.createDiagramAnchorageVisual(mvcb);
 		mvcb.setVisual(visual);
 		IController diagramAnchorageController =
 				controllerFactory.createDiagramAnchorageController(mvcb);
 		mvcb.setController(diagramAnchorageController);
-		if(diagramAnchorageController.isPossibleLinkTarget()) {
-			possibleLinkTargets.add(mvcb);
-		}
-		diagramAnchorageBundles.add(mvcb);
-		addVisualNodesAndAttach(mvcb);
-		if(m == oldSelection) {
-			setSingleSelectedMVCBundle(mvcb);
-		}
-	}
-
-	/** Initializes the attached child {@link MVCBundleBase}s. */
-	private void initAttachedContentAnchorages(IContentMVCBundle pBundle, Object oldSelection) {
-		for(Object cElement : modelFactory.getContentAnchorageModels(pBundle.getModel())) {
-			IContentAnchorageMVCBundle cBundle =
-					initSingleAttachedContentAnchorage(pBundle, cElement);
-			if(cElement == oldSelection) {
-				setSingleSelectedMVCBundle(cBundle);
-			}
-		}
+		return mvcb;
 	}
 
 	/** Adds a content anchorage for the given element and content bundle. */
@@ -492,26 +486,25 @@
 		IController contentAnchorageController =
 				controllerFactory.createContentAnchorageController(cBundle);
 		cBundle.setController(contentAnchorageController);
-		if(contentAnchorageController.isPossibleLinkTarget()) {
-			possibleLinkTargets.add(cBundle);
-		}
-		contentAnchorageBundles.add(cBundle);
-		addVisualNodesAndAttach(cBundle);
 		return cBundle;
 	}
 
 	/** Initializes the link {@link ILinkMVCBundle}s. */
-	private void initLinks(Object link, Object oldSelection) {
+	private ILinkMVCBundle initLinks(Object link,
+			Map<Object, IDiagramAnchorageMVCBundle> diagramAnchorageBundles,
+			Map<Object, IContentAnchorageMVCBundle> contentAnchorageBundles) {
 		Object startModel = modelFactory.getLinkStart(link);
 		if(startModel == null) {
-			return;
+			return null;
 		}
-		IAnchorageMVCBundle startBundle = getBundleForAnchorageModel(startModel);
+		IAnchorageMVCBundle startBundle = getBundleForAnchorageModel(startModel,
+				diagramAnchorageBundles, contentAnchorageBundles);
 		Object endModel = modelFactory.getLinkEnd(link);
 		if(endModel == null) {
-			return;
+			return null;
 		}
-		IAnchorageMVCBundle endBundle = getBundleForAnchorageModel(endModel);
+		IAnchorageMVCBundle endBundle = getBundleForAnchorageModel(endModel,
+				diagramAnchorageBundles, contentAnchorageBundles);
 		LinkMVCBundle lBundle =
 				new LinkMVCBundle(link, diagramBundle, startBundle, endBundle, this);
 		ILinkVisual visual = visualFactory.createLinkVisual(lBundle);
@@ -521,49 +514,25 @@
 		if(linkController.isPossibleLinkTarget()) {
 			possibleLinkTargets.add(lBundle);
 		}
-		linkBundles.add(lBundle);
-		addVisualNodesAndAttach(lBundle);
-		if(link == oldSelection) {
-			setSingleSelectedMVCBundle(lBundle);
-		}
+		return lBundle;
 	}
 
 	/** Returns the {@link MVCBundleBase} of the given anchorage node's model. */
-	private IAnchorageMVCBundle getBundleForAnchorageModel(Object m) {
-		for(IAnchorageMVCBundle b : diagramAnchorageBundles) {
-			if(b.getModel() == m) {
-				return b;
-			}
+	private IAnchorageMVCBundle getBundleForAnchorageModel(Object m,
+			Map<Object, IDiagramAnchorageMVCBundle> diagramAnchorageBundles,
+			Map<Object, IContentAnchorageMVCBundle> contentAnchorageBundles) {
+		IAnchorageMVCBundle result = diagramAnchorageBundles.get(m);
+		if(result != null) {
+			return result;
 		}
-		for(IAnchorageMVCBundle b : contentAnchorageBundles) {
-			if(b.getModel() == m) {
-				return b;
-			}
+		result = contentAnchorageBundles.get(m);
+		if(result != null) {
+			return result;
 		}
 		throw new IllegalArgumentException(
 				"Anchorage node model is neither a diagram nor a content anchorage.");
 	}
 
-	/** Returns the {@link MVCBundleBase} of the given content model. */
-	private IContentMVCBundle getBundleForContentModel(Object m) {
-		for(IContentMVCBundle b : contentBundles) {
-			if(b.getModel() == m) {
-				return b;
-			}
-		}
-		throw new IllegalArgumentException("Content model has no valid content bundle.");
-	}
-
-	/** Returns the {@link MVCBundleBase} of the given link node's model. */
-	private ILinkMVCBundle getBundleForLinkModel(Object m) {
-		for(ILinkMVCBundle b : linkBundles) {
-			if(b.getModel() == m) {
-				return b;
-			}
-		}
-		throw new IllegalArgumentException("Link model has no valid link bundle.");
-	}
-
 	/** Returns the viewer's visual node. */
 	public Pane getVisualNode() {
 		return scrolledPane;
@@ -586,28 +555,24 @@
 
 	/** Sets the selected {@link IMVCBundle} as single primary selection. */
 	public void setSingleSelectedMVCBundle(IMVCBundle sel) {
-		// single selection clears multi-selection
+		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();
-		if(primarySelectedBundle == sel) {
-			return;
-		}
 		// single selection clears old selection
 		IMVCBundle oldSelection = primarySelectedBundle;
 		if(oldSelection != null) {
-			oldSelection.removeTag(FOCUS_TAG);
-			oldSelection.removeTag(PRIMARY_SELECTION_TAG);
-			oldSelection.getVisual().updateNodes(layers);
+			oldSelection.getVisual().applyEffect(NO_EFFECT, layers);
 		}
 		primarySelectedBundle = sel;
 		if(primarySelectedBundle != null) {
 			primarySelectedBundle.getVisual().requestFocus();
-			primarySelectedBundle.addTag(FOCUS_TAG);
-			primarySelectedBundle.addTag(PRIMARY_SELECTION_TAG);
-			primarySelectedBundle.getVisual().updateNodes(layers);
+			primarySelectedBundle.getVisual().applyEffect(PRIMARY_SELECTED_FOCUSED, layers);
 		}
 		fireSelectionChanged();
 	}
@@ -729,16 +694,17 @@
 	/** Updates every visual (and only its appearance not the structure). */
 	public void updateAllVisuals() {
 		layers.setScale(getCurrentZoomFactor());
-		for(IContentMVCBundle b : contentBundles) {
+		getGridCanvasVisual().updateNodes(layers);
+		for(IContentMVCBundle b : contentBundles.values()) {
 			b.getVisual().updateNodes(layers);
 		}
-		for(IDiagramAnchorageMVCBundle b : diagramAnchorageBundles) {
+		for(IDiagramAnchorageMVCBundle b : diagramAnchorageBundles.values()) {
 			b.getVisual().updateNodes(layers);
 		}
-		for(IContentAnchorageMVCBundle b : contentAnchorageBundles) {
+		for(IContentAnchorageMVCBundle b : contentAnchorageBundles.values()) {
 			b.getVisual().updateNodes(layers);
 		}
-		for(ILinkMVCBundle b : linkBundles) {
+		for(ILinkMVCBundle b : linkBundles.values()) {
 			b.getVisual().updateNodes(layers);
 		}
 	}
@@ -755,147 +721,6 @@
 		horizontalScrollbar.setValue(hscroll);
 	}
 
-	/**
-	 * Adds the given element to the viewer updating the anchorages and links from the model
-	 * factory. The caller is responsible to call {@link #updateAllVisuals()} when all add and
-	 * remove operations are finished.
-	 */
-	public void addContent(Object model) {
-		boolean notContained = contentBundles.stream().noneMatch(b -> b.getModel() == model);
-		if(!notContained) {
-			return;
-		}
-		initAttachedContentAnchorages(initContent(model, null), null);
-		initParent(model);
-		List<?> newContentAnchorages = modelFactory.getContentAnchorageModels(model);
-		for(Object link : modelFactory.getLinkModels()) {
-			Object start = modelFactory.getLinkStart(link);
-			Object end = modelFactory.getLinkEnd(link);
-			boolean oldLink = newContentAnchorages.stream().noneMatch(a -> a == start || a == end);
-			if(oldLink) {
-				continue;
-			}
-			initLinks(link, null);
-		}
-	}
-
-	/**
-	 * Adds the given content anchorage to the diagram, assuming the content model was already
-	 * added, and updates the links. The caller is responsible to call {@link #updateAllVisuals()}
-	 * when all add and remove operations are finished.
-	 */
-	public void addContentAnchorage(Object contentModel, Object anchorageModel) {
-		boolean notContained =
-				contentAnchorageBundles.stream().noneMatch(b -> b.getModel() == anchorageModel);
-		if(!notContained) {
-			return;
-		}
-		IContentMVCBundle contentBundle = getBundleForContentModel(contentModel);
-		initSingleAttachedContentAnchorage(contentBundle, anchorageModel);
-		updateLinksAfterNewAnchorageAppeared(anchorageModel);
-	}
-
-	/** Adds links, which are visible after the given anchorage appeared. */
-	private void updateLinksAfterNewAnchorageAppeared(Object anchorageModel) {
-		for(Object link : modelFactory.getLinkModels()) {
-			Object start = modelFactory.getLinkStart(link);
-			Object end = modelFactory.getLinkEnd(link);
-			boolean oldLink = anchorageModel != start && anchorageModel != end;
-			if(oldLink) {
-				continue;
-			}
-			initLinks(link, null);
-		}
-	}
-
-	/**
-	 * Adds the given diagram anchorage to the diagram and updates links. The caller is responsible
-	 * to call {@link #updateAllVisuals()} when all add and
-	 * remove operations are finished.
-	 */
-	public void addDiagramAnchorage(Object anchorageModel) {
-		boolean notContained =
-				diagramAnchorageBundles.stream().noneMatch(b -> b.getModel() == anchorageModel);
-		if(!notContained) {
-			return;
-		}
-		initDiagramAnchorages(anchorageModel, null);
-		updateLinksAfterNewAnchorageAppeared(anchorageModel);
-	}
-
-	/**
-	 * Adds the given link to the diagram if both anchorages are already contained. The caller is
-	 * responsible to call {@link #updateAllVisuals()} when all add and
-	 * remove operations are finished.
-	 */
-	public void addLink(Object linkModel) {
-		boolean notContained = linkBundles.stream().noneMatch(b -> b.getModel() == linkModel);
-		if(!notContained) {
-			return;
-		}
-		initLinks(linkModel, null);
-	}
-
-	/**
-	 * Removes the given element to the viewer updating the anchorages and links from the model
-	 * factory.The caller is responsible to call {@link #updateAllVisuals()} when all add and
-	 * remove operations are finished.
-	 */
-	public void removeContent(Object model) {
-		IContentMVCBundle contentBundle = getBundleForContentModel(model);
-		if(contentBundle == null) {
-			return;
-		}
-		for(IAnchorageMVCBundle abundle : contentBundle.getAnchorages()) {
-			detachAndRemoveContentAnchorageBundle(abundle);
-		}
-		// finally remove the content
-		removeVisualNodesAndDetach(contentBundle);
-		contentBundle.remove();
-		contentBundles.remove(contentBundle);
-	}
-
-	/** Detaches all visuals of the given anchorage bundle and its connected links. */
-	private void detachAndRemoveContentAnchorageBundle(IAnchorageMVCBundle abundle) {
-		// first remove any links connected to any content anchorage
-		for(ILinkMVCBundle lbundle : abundle.getIncomingLinks()) {
-			removeVisualNodesAndDetach(lbundle);
-			lbundle.remove();
-		}
-		for(ILinkMVCBundle lbundle : abundle.getOutgoingLinks()) {
-			removeVisualNodesAndDetach(lbundle);
-			lbundle.remove();
-		}
-		linkBundles.removeAll(abundle.getIncomingLinks());
-		linkBundles.removeAll(abundle.getOutgoingLinks());
-		// then any content anchorage
-		removeVisualNodesAndDetach(abundle);
-		abundle.remove();
-		// one of these two is the removal, the other is a no-op
-		contentAnchorageBundles.remove(abundle);
-		diagramAnchorageBundles.remove(abundle);
-	}
-
-	/**
-	 * Removes the given content or diagram anchorage from the diagram and updates the links. The
-	 * caller is responsible to call {@link #updateAllVisuals()} when all add and remove operations
-	 * are finished.
-	 */
-	public void removeAnchorage(Object anchorageModel) {
-		IAnchorageMVCBundle abundle = getBundleForAnchorageModel(anchorageModel);
-		detachAndRemoveContentAnchorageBundle(abundle);
-	}
-
-	/**
-	 * Removes the given link to the diagram if both anchorages are already contained. The caller is
-	 * responsible to call {@link #updateAllVisuals()} when all add and
-	 * remove operations are finished.
-	 */
-	public void removeLink(Object linkModel) {
-		ILinkMVCBundle lbundle = getBundleForLinkModel(linkModel);
-		removeVisualNodesAndDetach(lbundle);
-	}
-
 	/** Adds the given node to the feedback group for the given owner. */
 	public void addFeedback(Node node, IMVCBundle owner) {
 		layers.getVisualFeedbackLayer().add(node, owner);
@@ -993,16 +818,17 @@
 
 	/** Returns linkBundles. */
 	public List<ILinkMVCBundle> getLinkBundles() {
-		return linkBundles;
+		// FIXME: hack
+		return linkBundles.values().stream().collect(Collectors.toList());
 	}
 
-	/** Returns whether link highlighting is enabled. */
-	public boolean isLinkHighlightingEnabled() {
-		return linkHighlightingEnabled;
+	/** Sets the linkHighlighting. */
+	public void setLinkHighlighting(boolean value) {
+		linkHighlighting = value;
 	}
 
-	/** Sets the link highlighting enabled flag. */
-	public void setLinkHighlightingEnabled(boolean linkHighlightingEnabled) {
-		this.linkHighlightingEnabled = linkHighlightingEnabled;
+	/** Returns the linkHighlighting. */
+	public boolean getLinkHighlighting() {
+		return linkHighlighting;
 	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/GridCanvasVisual.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/GridCanvasVisual.java
index 3345a95..cdd8ff2 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/GridCanvasVisual.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/GridCanvasVisual.java
@@ -61,20 +61,10 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public void addNodes(DiagramLayers layers) {
-		layers.getBottomLayer().add(gridCanvas, getMVCBundle());
-	}
-
-	/** {@inheritDoc} */
-	@Override
 	public void updateNodes(DiagramLayers layers) {
-		// TODO: properties of grid might have changed
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	public void removeNodes(DiagramLayers layers) {
-		layers.getBottomLayer().remove(gridCanvas);
+		if(gridCanvas.getParent() == null) {
+			layers.getBottomLayer().add(gridCanvas, getMVCBundle());
+		}
 	}
 
 	/** {@inheritDoc} */
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/.ratings b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/.ratings
index 608c2cf..739c2ca 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/.ratings
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/.ratings
@@ -1,8 +1,8 @@
 ClickControllerBase.java 31af12f85c374de0fe81e16f1879c9a81e11e8bc YELLOW
-ControllerBase.java ed0ab2278b44fba802ce094ee10c35fb9a74a9a1 YELLOW
+ControllerBase.java 143555187d0950c89743dbebff7fd48b13982811 YELLOW
 DefaultContentAnchorageControllerBase.java 2e3189972cf34f5cc868bd458c84cb3d2f0eddf1 YELLOW
 DefaultDiagramAnchorageControllerBase.java 0df0e7868a61043611eb3db15ad8108c5cbb7f76 YELLOW
 DefaultDiagramController.java 2e41d84e472816615bcefa9dd9683500d17021e0 YELLOW
 DragControllerBase.java 217c6fd1a212d82581478a350c5746def868639f YELLOW
 LinkBendPointControllerBase.java 3be7d0474d026c362e81f404e8d1899b478880b0 YELLOW
-ResizableContentControllerBase.java 57a19ddd32a6c42c299b8cf30e609c2b33584d45 YELLOW
+ResizableContentControllerBase.java abe55b824acb3944c61ac1374ce98486c5ad021f YELLOW
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/ControllerBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/ControllerBase.java
index ed0ab22..69eea5e 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/ControllerBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/ControllerBase.java
@@ -27,11 +27,13 @@
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IController;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IDragController;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.IKeyPressController;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IMVCBundle;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.MVCBundleTag;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl.MVCBundlePartBase;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisual;
 
+import javafx.geometry.Bounds;
 import javafx.geometry.Point2D;
 import javafx.geometry.Rectangle2D;
 import javafx.scene.Cursor;
@@ -67,33 +69,36 @@
 	/** {@inheritDoc} */
 	@Override
 	public IClickController getClickController(Node node, Point2D locationOnNode) {
-		if(selectOnClick()) {
-			return new ClickControllerBase() {
-				/** {@inheritDoc} */
-				@Override
-				public void singleClick(Point2D location, boolean ctrlKeyDown,
-						boolean shiftKeyDown) {
-					if(shiftKeyDown) {
-						getViewer().handleShiftSelectionOf(getMVCBundle());
-					} else {
-						getViewer().setSingleSelectedMVCBundle(getMVCBundle());
-					}
+		return new ClickControllerBase() {
+			/** {@inheritDoc} */
+			@Override
+			public void singleClick(Point2D location, boolean ctrlKeyDown, boolean shiftKeyDown) {
+				if(!selectOnClick()) {
+					return;
 				}
+				if(shiftKeyDown) {
+					getViewer().handleShiftSelectionOf(getMVCBundle());
+				} else {
+					getViewer().setSingleSelectedMVCBundle(getMVCBundle());
+				}
+			}
 
-				/** {@inheritDoc} */
-				@Override
-				public void secondaryClick(Point2D location, boolean ctrlKeyDown,
-						boolean shiftKeyDown) {
-					if(ctrlKeyDown) {
-						delete();
-					} else {
-						getViewer().showContextMenu(location.getX(), location.getY(),
-								getMVCBundle());
-					}
+			/** {@inheritDoc} */
+			@Override
+			public void secondaryClick(Point2D location, boolean ctrlKeyDown,
+					boolean shiftKeyDown) {
+				if(selectOnClick()) {
+					getViewer().setSingleSelectedMVCBundle(getMVCBundle());
 				}
-			};
-		}
-		return null;
+				if(ctrlKeyDown) {
+					delete();
+				} else {
+					Bounds b = node.getBoundsInParent();
+					getViewer().showContextMenu(location.getX() + b.getMinX(),
+							location.getY() + b.getMinY(), getMVCBundle());
+				}
+			}
+		};
 	}
 
 	/** Returns whether the visual should be selected on single click. */
@@ -130,13 +135,13 @@
 		public void safeDragInProgress(IMVCBundle bundle, Node node, Point2D location) {
 			Point2D delta = getDeltas(location);
 			FeedbackChange fchg = new FeedbackChange(delta.getX(), delta.getY(), 0, 0);
-			getMVCBundle().setFeedbackChange(fchg);
+			setFeedbackChangeRecursively(fchg);
 		}
 
 		@Override
 		public void safeDragCompleted(IMVCBundle bundle, Node node, Point2D location) {
 			Point2D delta = getDeltas(location);
-			getMVCBundle().setFeedbackChange(FeedbackChange.EMPTY);
+			setFeedbackChangeRecursively(FeedbackChange.EMPTY);
 			// apply model effect for the move last
 			move(delta.getX(), delta.getY());
 		}
@@ -284,4 +289,20 @@
 				return Cursor.DEFAULT;
 		}
 	}
+
+	/**
+	 * Recursively sets the given feedback change to this controllers bundle and all child bundles.
+	 */
+	public void setFeedbackChangeRecursively(FeedbackChange fc) {
+		IMVCBundle thisBundle = getMVCBundle();
+		thisBundle.setFeedbackChange(fc);
+		if(thisBundle instanceof IContentMVCBundle) {
+			for(IMVCBundle cb : ((IContentMVCBundle)thisBundle).getChildren()) {
+				IController cc = cb.getController();
+				if(cc instanceof ControllerBase) {
+					((ControllerBase)cc).setFeedbackChangeRecursively(fc);
+				}
+			}
+		}
+	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/DefaultDiagramController.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/DefaultDiagramController.java
index 2e41d84..1f847c4 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/DefaultDiagramController.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/DefaultDiagramController.java
@@ -16,8 +16,10 @@
 import java.util.LinkedList;
 import java.util.List;
 
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.ILinkController;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IMVCBundle;
 
+import javafx.scene.Node;
 import javafx.scene.control.Menu;
 import javafx.scene.control.MenuItem;
 
@@ -55,4 +57,30 @@
 		menuList.addAll(superList);
 		return menuList;
 	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<MenuItem> contextMenuContributions(double x, double y) {
+		List<MenuItem> superList = super.contextMenuContributions(x, y);
+
+		MenuItem enableLinkHighlighting = new MenuItem("Enable Link Highlighting");
+		enableLinkHighlighting.setOnAction(evt -> {
+			getViewer().setLinkHighlighting(true);
+			getViewer().updateAllVisuals();
+		});
+		MenuItem disableLinkHighlighting = new MenuItem("Disable Link Highlighting");
+		disableLinkHighlighting.setOnAction(evt -> {
+			getViewer().setLinkHighlighting(false);
+			getViewer().updateAllVisuals();
+		});
+
+		Menu menu = new Menu("Display ...");
+		menu.getItems().add(enableLinkHighlighting);
+		menu.getItems().add(disableLinkHighlighting);
+
+		List<MenuItem> menuList = new LinkedList<>();
+		menuList.add(menu);
+		menuList.addAll(superList);
+		return menuList;
+	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/ResizableContentControllerBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/ResizableContentControllerBase.java
index 57a19dd..a9fcce9 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/ResizableContentControllerBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/controller/base/ResizableContentControllerBase.java
@@ -23,6 +23,7 @@
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewer;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.EDragGesture;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.FeedbackChange;
+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.mvc.IContentAnchorageMVCBundle;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentMVCBundle;
@@ -46,6 +47,34 @@
 		super(mvcb);
 	}
 
+	/** {@inheritDoc} */
+	@Override
+	public IClickController getClickController(Node node, Point2D locationOnNode) {
+		// wild cast works: see constructor
+		IContentVisual visual = (IContentVisual)getVisual();
+		if(allowExpandCollapse() && visual.isExpandCollapseWidget(node)) {
+			return new ClickControllerBase() {
+				/** {@inheritDoc} */
+				@Override
+				public void singleClick(Point2D location, boolean ctrlKeyDown,
+						boolean shiftKeyDown) {
+					expandCollapse();
+				}
+			};
+		}
+		return super.getClickController(node, locationOnNode);
+	}
+
+	/** 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;
@@ -82,14 +111,14 @@
 		public void safeDragInProgress(IMVCBundle bundle, Node node, Point2D location) {
 			Point2D delta = location.subtract(startLocation);
 			FeedbackChange fchg = new FeedbackChange(0, 0, getHorizontalDelta(delta.getX()), 0);
-			getMVCBundle().setFeedbackChange(fchg);
+			setFeedbackChangeRecursively(fchg);
 		}
 
 		@Override
 		public void safeDragCompleted(IMVCBundle bundle, Node node, Point2D location) {
 			Point2D delta = location.subtract(startLocation);
 			double dX = getHorizontalDelta(delta.getX());
-			getMVCBundle().setFeedbackChange(FeedbackChange.EMPTY);
+			setFeedbackChangeRecursively(FeedbackChange.EMPTY);
 			resize(dX, 0);
 		}
 	};
@@ -105,14 +134,14 @@
 		public void safeDragInProgress(IMVCBundle bundle, Node node, Point2D location) {
 			Point2D delta = location.subtract(startLocation);
 			FeedbackChange fchg = new FeedbackChange(0, 0, 0, getVerticalDelta(delta.getY()));
-			getMVCBundle().setFeedbackChange(fchg);
+			setFeedbackChangeRecursively(fchg);
 		}
 
 		@Override
 		public void safeDragCompleted(IMVCBundle bundle, Node node, Point2D location) {
 			Point2D delta = location.subtract(startLocation);
 			double dY = getVerticalDelta(delta.getY());
-			getMVCBundle().setFeedbackChange(FeedbackChange.EMPTY);
+			setFeedbackChangeRecursively(FeedbackChange.EMPTY);
 			resize(0, dY);
 		}
 	};
@@ -130,13 +159,13 @@
 					Point2D delta = location.subtract(startLocation);
 					FeedbackChange fchg = new FeedbackChange(0, 0, getHorizontalDelta(delta.getX()),
 							getVerticalDelta(delta.getY()));
-					getMVCBundle().setFeedbackChange(fchg);
+					setFeedbackChangeRecursively(fchg);
 				}
 
 				@Override
 				public void safeDragCompleted(IMVCBundle bundle, Node node, Point2D location) {
 					Point2D delta = location.subtract(startLocation);
-					getMVCBundle().setFeedbackChange(FeedbackChange.EMPTY);
+					setFeedbackChangeRecursively(FeedbackChange.EMPTY);
 					resize(getHorizontalDelta(delta.getX()), getVerticalDelta(delta.getY()));
 				}
 			};
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/.ratings b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/.ratings
index 7d9678c..1978931 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/.ratings
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/.ratings
@@ -5,6 +5,6 @@
 IDiagramAnchorageMVCBundle.java e552ee54a27b237fad44adfd733261509ea1d7c4 YELLOW
 IDiagramMVCBundle.java 3c67b609c29af875be79f57cdb9391e53d3529d0 YELLOW
 ILinkMVCBundle.java 6427c00b82c3debcc46762fdfb39efbed013b1cd YELLOW
-IMVCBundle.java 18049be79ecbce53417046334b430d877cc26ae6 YELLOW
+IMVCBundle.java 325b7c24da7966fcf1633c49b92e73ac9924c6c6 YELLOW
 IMVCBundlePart.java f64a158c7ccdb07c21e404f9c25afd56655144e9 YELLOW
 MVCBundleTag.java 9c6aad8f894607e0ed6413a5b7dd34178dcae278 YELLOW
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/IMVCBundle.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/IMVCBundle.java
index 18049be..325b7c2 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/IMVCBundle.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/IMVCBundle.java
@@ -16,6 +16,7 @@
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewer;
 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.impl.MVCBundleTag;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IVisual;
 
 /** Interface for bundles composed of model, view, and controllers. */
@@ -50,18 +51,12 @@
 	/** Detaches listeners from the visual and from the underlying model. */
 	void detach();
 
-	/** Adds the given tag to this bundle. */
+	/** Adds the given tag to this {@link IMVCBundle}. */
 	void addTag(MVCBundleTag tag);
 
-	/** Returns the tag of this bundle with the given tag identifier. */
-	MVCBundleTag getTag(String tagIdentifier);
-
-	/** Returns whether this bundle is tagged with the given tag (using equals()). */
-	boolean hasTag(MVCBundleTag tag);
-
-	/** Removes the tag from this bundle. */
+	/** Removes the given tag from this {@link IMVCBundle}. */
 	void removeTag(MVCBundleTag tag);
 
-	/** Removes the bundle from any other bundle it is related to. */
-	void remove();
+	/** Returns whether this {@link IMVCBundle} has the given tag. */
+	boolean isTagged(MVCBundleTag tag);
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/.ratings b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/.ratings
index 316aa6b..22c8664 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/.ratings
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/.ratings
@@ -1,10 +1,10 @@
-AnchorageMVCBundleBase.java f7d1e309601809e60f501635dd7a5aeafb4d5944 YELLOW
-ContentAnchorageMVCBundle.java c1ad94481520f0c872074084e4a84dde531e60f6 YELLOW
-ContentMVCBundle.java d75ad6f02206958e13b1899f7fa30adf8ff4debf YELLOW
+AnchorageMVCBundleBase.java 62a35a9ab409fd3b03ff226fb7ef05c9c8f797e0 YELLOW
+ContentAnchorageMVCBundle.java 3b0208d05b69fef3e9280e6e3243f3cc0d4c6b5f YELLOW
+ContentMVCBundle.java 08db7c3c32a20a3766a294185f5a8e23eacbc6c8 YELLOW
 DefaultMVCBTags.java 6b39227bd505179fd75568785ce328ad05e23174 YELLOW
-DiagramAnchorageMVCBundle.java f38b0e5e427ac646d026b1b7dbfa05466409093d YELLOW
-DiagramMVCBundle.java 1ae946b7839deda10727b75ea679fdfdcea20fbe YELLOW
-LinkMVCBundle.java d406a6bf96c58c395ee53c0c74130248bbf1c76b YELLOW
-MVCBundleBase.java d1781f0397efc18db17a8671bc0355285a39f843 YELLOW
+DiagramAnchorageMVCBundle.java 4e7bf8bdaec72793c357fc10ad325117a7137fed YELLOW
+DiagramMVCBundle.java 5bd8853fe230850d9d4d1d091cde51b49329858b YELLOW
+LinkMVCBundle.java 1eecc7985863fb936beacc3ae11f2ee8c6aa3809 YELLOW
+MVCBundleBase.java ccc7f3db1267473fe665bd5b7ede5162e3253020 YELLOW
 MVCBundlePartBase.java c4c453c86ae7933a4c154dc46e31159c03482931 YELLOW
 MVCBundleTag.java 8d977977a8de160afcb606f0eae85283d2068d91 YELLOW
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/DefaultMVCBTags.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/DefaultMVCBTags.java
new file mode 100644
index 0000000..6b39227
--- /dev/null
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/DefaultMVCBTags.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2018 fortiss GmbH. 
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     TODO: fullname anna (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl;
+
+import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl.MVCBundleTag.createDefaultTag;
+
+/** Default {@link MVCBundleTag}s defined and used by the LWFXEF framework. */
+public final class DefaultMVCBTags {
+	/** Constructor. */
+	private DefaultMVCBTags() {
+		// class is not intended for instantiation
+	}
+
+	/** The default tag for highlighting support on incoming links. */
+	public static MVCBundleTag HIGHLIGHT_INCOMING_LINK_TAG =
+			createDefaultTag("HIGHLIGHT", "INCOMING_LINK");
+
+	/** The default tag for highlighting support on outgoing links. */
+	public static MVCBundleTag HIGHLIGHT_OUTGOING_LINK_TAG =
+			createDefaultTag("HIGHLIGHT", "OUTGOING_LINK");
+}
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/MVCBundleBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/MVCBundleBase.java
index d1781f0..fa7877b 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/MVCBundleBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/MVCBundleBase.java
@@ -15,8 +15,7 @@
 
 import static java.util.Objects.requireNonNull;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.HashSet;
 
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewer;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.FeedbackChange;
@@ -47,7 +46,7 @@
 	/** 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<>();
+	private HashSet<MVCBundleTag> tagSet = new HashSet<>();
 
 	/** Constructor. */
 	public MVCBundleBase(Object m, DiagramViewer v) {
@@ -146,38 +145,21 @@
 		return getClass().getSimpleName() + " MVC bundle of " + getModel().toString();
 	}
 
-	/** Adds the given tag to this bundle. */
+	/** {@inheritDoc} */
 	@Override
-	public void addTag(MVCBundleTag tag) {
-		tagMap.put(tag.getTagIdentifier(), tag);
-	}
-
-	/** Returns the tag of this bundle with the given tag identifier. */
-	@Override
-	public MVCBundleTag getTag(String tagIdentifier) {
-		return tagMap.get(tagIdentifier);
+	public boolean isTagged(MVCBundleTag tag) {
+		return tagSet.contains(tag);
 	}
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean hasTag(MVCBundleTag tag) {
-		if(tag == null) {
-			return false;
-		}
-		MVCBundleTag storedTag = tagMap.get(tag.getTagIdentifier());
-		if(storedTag != null) {
-			Object userObject = storedTag.getUserObject();
-			if(userObject == null) {
-				return tag.getUserObject() == null;
-			}
-			return userObject.equals(tag.getUserObject());
-		}
-		return false;
+	public void addTag(MVCBundleTag tag) {
+		tagSet.add(tag);
 	}
 
-	/** Removes the tag from this bundle. */
+	/** {@inheritDoc} */
 	@Override
 	public void removeTag(MVCBundleTag tag) {
-		tagMap.remove(tag.getTagIdentifier());
+		tagSet.remove(tag);
 	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/MVCBundleTag.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/MVCBundleTag.java
new file mode 100644
index 0000000..8d97797
--- /dev/null
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/mvc/impl/MVCBundleTag.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2018 fortiss GmbH. 
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     TODO: fullname anna (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl;
+
+/** Class encapsulating a String tag. */
+public class MVCBundleTag {
+	/** The string identifier. */
+	private final String tagID;
+
+	/** Constructor. */
+	private MVCBundleTag(String tagID) {
+		this.tagID = tagID;
+	}
+
+	/** Creates a tag for the given user domain and tag ID. */
+	public static MVCBundleTag createUserTag(String userDomain, String userTagID) {
+		return new MVCBundleTag("USER_TAG_" + userDomain + "_" + userTagID);
+	}
+
+	/** Creates a tag for the given user domain and tag ID. */
+	public static MVCBundleTag createDefaultTag(String domain, String tagID) {
+		return new MVCBundleTag("DEFAULT_TAG_" + domain + "_" + tagID);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public int hashCode() {
+		return tagID.hashCode();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean equals(Object obj) {
+		if(obj instanceof MVCBundleTag) {
+			return ((MVCBundleTag)obj).tagID.equals(tagID);
+		}
+		return false;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public String toString() {
+		return tagID.toString();
+	}
+}
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/.ratings b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/.ratings
index 62936eb..ffae850 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/.ratings
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/.ratings
@@ -3,5 +3,5 @@
 IContentVisual.java 9c174d17b0e4cbb96f8cc4e6ad2b0924cd2beaf5 YELLOW
 IDiagramAnchorageVisual.java 080a9e85a4ea68c9f49a30579f6c8dea9e1a489e YELLOW
 ILinkVisual.java 21af7b41510103de023efc4d3013c4a6cb02351f YELLOW
-IVisual.java a64324dc4081045da22b8d0314f16cd8bb56df40 YELLOW
+IVisual.java 5d08fe4686dbcd50366611a0ae8d4dedfafdcf8b YELLOW
 IVisualFactory.java 3257ec98c26ce1b79229a5293b1b769dc461d30f YELLOW
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/IContentVisual.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/IContentVisual.java
index 9c174d1..703cd6b 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/IContentVisual.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/IContentVisual.java
@@ -15,6 +15,7 @@
 
 import javafx.geometry.Dimension2D;
 import javafx.geometry.Point2D;
+import javafx.scene.Node;
 
 /**
  * {@link IVisual} for content nodes moving freely on the diagram background.
@@ -35,4 +36,7 @@
 
 	/** Returns the location on the border of this visual for the given ICAV. */
 	Point2D getAnchorageLocation(IContentAnchorageVisual visual);
+
+	/** Returns whether the given node is the expand/collapse widget. */
+	boolean isExpandCollapseWidget(Node node);
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/IVisual.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/IVisual.java
index a64324d..5d08fe4 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/IVisual.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/IVisual.java
@@ -26,15 +26,9 @@
  * {@link IVisualFactory} and usually represent a single model element.
  */
 public interface IVisual extends IMVCBundlePart {
-	/** Adds the visual's nodes to the given layer group. */
-	void addNodes(DiagramLayers layers);
-
-	/** Updates the visual's nodes. */
+	/** Updates the visual's nodes possibly adding or removing nodes from the scene graph. */
 	void updateNodes(DiagramLayers layers);
 
-	/** Removes the visual's nodes. */
-	void removeNodes(DiagramLayers layers);
-
 	/**
 	 * Returns the current bounds computed from model and feedback coordinates relative to the
 	 * diagram origin.
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/.ratings b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/.ratings
index 57cf3aa..19bad5e 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/.ratings
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/.ratings
@@ -1,7 +1,6 @@
-ContentAnchorageVisualBase.java 5b26ce94fe3df56abb498a85ad5ae2370e471773 YELLOW
-ContentVisualBase.java ec0b05f0245618dd851283c59f12bdf18220951a YELLOW
-DiagramAnchorageVisualBase.java 7026a5ae501b975061f47ebda9a9f871f947c42e YELLOW
+ContentAnchorageVisualBase.java f6294adcb087172910a4ebcabd45ffde05374d3d YELLOW
+ContentVisualBase.java d45a9274f6df69e894327027d0504fc242631dac YELLOW
+DiagramAnchorageVisualBase.java 344738f52e4ea3dfcd12fa3cc3a23bc95536f941 YELLOW
 LinkVisualBase.java 8459ace47660234116acbdcf7038bede96828c46 YELLOW
 MVCBundlePartWithEffectsBase.java 971c7cdb403598aa0e8f9f871401caa884b7bf60 YELLOW
-ResizableContentVisualBase.java d2d7e54951a90d1fea56dbf87d903013343791d2 YELLOW
-VisualBase.java 2bea191ca6736c2e89c222473803c0e4890820eb YELLOW
+VisualBase.java d5c24486ca77114d093972d379f6ceaeca88125c YELLOW
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/ContentAnchorageVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/ContentAnchorageVisualBase.java
index 5b26ce9..f6294ad 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/ContentAnchorageVisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/ContentAnchorageVisualBase.java
@@ -13,26 +13,38 @@
  *******************************************************************************/
 package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base;
 
+import static javafx.scene.paint.Color.TRANSPARENT;
+
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramLayers;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.FeedbackChange;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.configuration.GridConfiguration;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle;
-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 javafx.geometry.Dimension2D;
 import javafx.geometry.Point2D;
 import javafx.geometry.Rectangle2D;
+import javafx.scene.effect.DropShadow;
 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 {
+	/** The visible shape of this visual. */
+	protected final Shape visualShape;
+	/** The invisible hit area of this visual. */
+	protected final Shape hitAreaShape;
+
 	/** Constructor. */
-	public ContentAnchorageVisualBase(IContentAnchorageMVCBundle mvcb) {
+	public ContentAnchorageVisualBase(IContentAnchorageMVCBundle mvcb, Shape visual, Shape hit) {
 		super(mvcb);
+		this.visualShape = visual;
+		this.hitAreaShape = hit;
+		hitAreaShape.setStroke(TRANSPARENT);
+		hitAreaShape.setFill(TRANSPARENT);
 	}
 
 	/** {@inheritDoc} */
@@ -42,19 +54,62 @@
 		return (IContentAnchorageMVCBundle)super.getMVCBundle();
 	}
 
-	/**
-	 * {@inheritDoc}
-	 * <P>
-	 * Updates visuals of incoming and outgoing links.
-	 */
+	/** Determines whether the hit area of this visual is enabled or not. */
+	protected boolean enableHitArea() {
+		return true;
+	}
+
+	/** Determines whether the visual shape is enabled or not. */
+	protected boolean enableVisual() {
+		return true;
+	}
+
+	/** Returns the size of the hit area as an outset of the visible rectangle. */
+	protected double getHitAreaOutset() {
+		return 8;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void createHoverEffect(DiagramLayers layers) {
+		super.createHoverEffect(layers);
+		Color shadow = getHoverShadowColor();
+		if(shadow != null && visualShape != null) {
+			visualShape.setEffect(new DropShadow(10, 3, 3, shadow));
+		}
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void removeHoverEffect(DiagramLayers layers) {
+		super.removeHoverEffect(layers);
+		if(visualShape != null) {
+			visualShape.setEffect(null);
+		}
+	}
+
+	/** {@inheritDoc} */
 	@Override
 	public void updateNodes(DiagramLayers layers) {
 		super.updateNodes(layers);
-		for(ILinkMVCBundle incoming : getMVCBundle().getIncomingLinks()) {
-			incoming.getVisual().updateNodes(layers);
+		IContentAnchorageMVCBundle mvcBundle = getMVCBundle();
+		if(visualShape != null) {
+			if(enableVisual()) {
+				if(visualShape.getParent() == null) {
+					layers.getAnchorageLayer().add(visualShape, mvcBundle);
+				}
+			} else {
+				layers.getAnchorageLayer().remove(visualShape);
+			}
 		}
-		for(ILinkMVCBundle outgoing : getMVCBundle().getOutgoingLinks()) {
-			outgoing.getVisual().updateNodes(layers);
+		if(hitAreaShape != null) {
+			if(enableHitArea()) {
+				if(hitAreaShape.getParent() == null) {
+					layers.getAnchorageInteractionLayer().add(hitAreaShape, mvcBundle);
+				}
+			} else {
+				layers.getAnchorageInteractionLayer().remove(hitAreaShape);
+			}
 		}
 	}
 
@@ -105,4 +160,19 @@
 	protected Paint getHighlightOutgoingLinkBorderColor() {
 		return Color.TOMATO.darker();
 	}
+
+	/** Returns the color of the hover drop shadow. */
+	protected Color getHoverShadowColor() {
+		return Color.DARKGRAY.darker();
+	}
+
+	/** Returns the hit area shape. */
+	public Shape getHitAreaShape() {
+		return hitAreaShape;
+	}
+
+	/** Returns visual shape. */
+	public Shape getVisualShape() {
+		return visualShape;
+	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/ContentVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/ContentVisualBase.java
index ec0b05f..d45a927 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/ContentVisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/ContentVisualBase.java
@@ -13,19 +13,204 @@
  *******************************************************************************/
 package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base;
 
+import static javafx.scene.paint.Color.DARKGRAY;
+import static javafx.scene.paint.Color.TRANSPARENT;
+import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG;
+import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG;
+
+import java.util.List;
+
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramLayers;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.FeedbackChange;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.configuration.GridConfiguration;
+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.ILinkMVCBundle;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.MVCBundleTag;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IContentVisual;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.handles.ExpandCollapseWidget;
 
 import javafx.geometry.Dimension2D;
+import javafx.geometry.Insets;
+import javafx.geometry.Point2D;
+import javafx.geometry.Rectangle2D;
+import javafx.geometry.VPos;
+import javafx.scene.Node;
+import javafx.scene.effect.DropShadow;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.Rectangle;
+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 selection rectangle (only one at any time). */
+	protected final Rectangle selectionFeedbackRectangle = new Rectangle();
+	/** The text of this visual. */
+	protected final Text text = new Text();
+	/** The icon of this visual. */
+	protected final ImageView icon = new ImageView();
+	/** The rectangle node of this visual. */
+	protected final Shape visualShape;
+	/** The invisible shape used for mouse hit detection. */
+	protected final Shape hitAreaShape;
+	/** The expanded / collapsed indicator widget. */
+	protected final ExpandCollapseWidget expandCollapse;
+
 	/** Constructor. */
-	public ContentVisualBase(IContentMVCBundle mvcb) {
+	public ContentVisualBase(IContentMVCBundle mvcb, Shape visual, Shape hit) {
 		super(mvcb);
+		this.visualShape = visual;
+		this.hitAreaShape = hit;
+		this.expandCollapse = new ExpandCollapseWidget(0, 0, 20, 20, 3);
+		hitAreaShape.setFill(TRANSPARENT);
+		hitAreaShape.setStroke(TRANSPARENT);
+		selectionFeedbackRectangle.setFill(TRANSPARENT);
+	}
+
+	/** Determines whether the visual shape is enabled or not. */
+	protected boolean enableVisual() {
+		return true;
+	}
+
+	/** Determines whether the hit area of this visual is enabled or not. */
+	protected boolean enableHitArea() {
+		return true;
+	}
+
+	/** 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 Point2D getTextAnchorLocation() {
+		Insets i = getTextInsets();
+		double croppedHeight = getCurrentBounds().getHeight() - i.getTop() - i.getBottom();
+		return new Point2D(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
+	protected void applySelectedNotFocusedEffect(DiagramLayers layers) {
+		updateSelectionFeedbackRectangle(getSelectedNotFocusedColor(), layers);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void applySecondarySelectedEffect(DiagramLayers layers) {
+		updateSelectionFeedbackRectangle(getSecondarySelectedColor(), layers);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public final void applySelectedFocusedEffect(DiagramLayers layers) {
+		updateSelectionFeedbackRectangle(getSelectedFocusedColor(), layers);
+		IContentMVCBundle bundle = getMVCBundle();
+
+		if(getViewer().isLinkHighlightingEnabled()) {
+			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 removeAllEffects(DiagramLayers layers) {
+		layers.getVisualFeedbackLayer().remove(selectionFeedbackRectangle);
+
+		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} */
@@ -45,9 +230,94 @@
 	@Override
 	public void updateNodes(DiagramLayers layers) {
 		super.updateNodes(layers);
-		for(IMVCBundle cb : getMVCBundle().getAnchorages()) {
-			cb.getVisual().updateNodes(layers);
+		IContentMVCBundle mvcBundle = getMVCBundle();
+		if(visualShape != null) {
+			if(enableVisual()) {
+				if(visualShape.getParent() == null) {
+					layers.getContentLayer().add(visualShape, mvcBundle);
+				}
+			} else {
+				layers.getContentLayer().remove(visualShape);
+			}
 		}
+		if(hitAreaShape != null) {
+			if(enableHitArea()) {
+				if(hitAreaShape.getParent() == null) {
+					layers.getContentInteractionLayer().add(hitAreaShape, mvcBundle);
+				}
+			} else {
+				layers.getContentInteractionLayer().remove(hitAreaShape);
+			}
+		}
+		Rectangle2D bounds = getCurrentBounds();
+		double leftX = bounds.getMinX();
+		double rightX = bounds.getMaxX();
+		double upperY = bounds.getMinY();
+
+		Insets i = getTextInsets();
+		Point2D 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, mvcBundle);
+			}
+			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, mvcBundle);
+			}
+			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(expandCollapse.getParent() == null) {
+				layers.getContentInteractionLayer().add(expandCollapse, mvcBundle);
+			}
+			Rectangle2D ecWidgetLocation = getExpandCollapseWidgetRectangle();
+			double inset = getExpandCollapseWidgetInset();
+			double x = leftX + ecWidgetLocation.getMinX();
+			double y = upperY + ecWidgetLocation.getMinY();
+			expandCollapse.update(x, y, ecWidgetLocation.getWidth(), ecWidgetLocation.getHeight(),
+					inset);
+			expandCollapse.setState(getExpandCollapseWidgetState());
+		} else {
+			layers.getContentInteractionLayer().remove(expandCollapse);
+		}
+	}
+
+	/** Returns the current state of the epand/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} */
@@ -56,4 +326,48 @@
 		// wild cast works: see constructor
 		return (IContentMVCBundle)super.getMVCBundle();
 	}
+
+	/** Updates and adds the selection feedback rectangle. */
+	private void updateSelectionFeedbackRectangle(Paint color, DiagramLayers layers) {
+		Rectangle2D bounds = getCurrentBounds();
+		if(bounds == null) {
+			return;
+		}
+		selectionFeedbackRectangle.setX(bounds.getMinX());
+		selectionFeedbackRectangle.setY(bounds.getMinY());
+		selectionFeedbackRectangle.setWidth(bounds.getWidth());
+		selectionFeedbackRectangle.setHeight(bounds.getHeight());
+		selectionFeedbackRectangle.setStroke(color);
+		layers.getVisualFeedbackLayer().add(selectionFeedbackRectangle, getMVCBundle());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean isExpandCollapseWidget(Node widget) {
+		return expandCollapse.isHit(widget);
+	}
+
+	/** Returns the drop shadow color. */
+	protected Color getHoverShadowColor() {
+		return DARKGRAY.darker();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void createHoverEffect(DiagramLayers layers) {
+		super.createHoverEffect(layers);
+		Color shadow = getHoverShadowColor();
+		if(shadow != null && visualShape != null) {
+			visualShape.setEffect(new DropShadow(10, 3, 3, shadow));
+		}
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void removeHoverEffect(DiagramLayers layers) {
+		super.removeHoverEffect(layers);
+		if(visualShape != null) {
+			visualShape.setEffect(null);
+		}
+	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/DiagramAnchorageVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/DiagramAnchorageVisualBase.java
index 7026a5a..344738f 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/DiagramAnchorageVisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/DiagramAnchorageVisualBase.java
@@ -13,22 +13,34 @@
  *******************************************************************************/
 package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base;
 
+import static javafx.scene.paint.Color.TRANSPARENT;
+
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramLayers;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.FeedbackChange;
 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.IDiagramAnchorageVisual;
 
 import javafx.geometry.Rectangle2D;
+import javafx.scene.effect.DropShadow;
 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 {
+	/** The visible shape of this visual. */
+	protected final Shape visualShape;
+	/** The invisible hit area of this visual. */
+	protected final Shape hitAreaShape;
+
 	/** Constructor. */
-	public DiagramAnchorageVisualBase(IDiagramAnchorageMVCBundle mvcb) {
+	public DiagramAnchorageVisualBase(IDiagramAnchorageMVCBundle mvcb, Shape visual, Shape hit) {
 		super(mvcb);
+		this.visualShape = visual;
+		this.hitAreaShape = hit;
+		hitAreaShape.setStroke(TRANSPARENT);
+		hitAreaShape.setFill(TRANSPARENT);
 	}
 
 	/** {@inheritDoc} */
@@ -38,15 +50,62 @@
 		return (IDiagramAnchorageMVCBundle)super.getMVCBundle();
 	}
 
+	/** Determines whether the visual shape is enabled or not. */
+	protected boolean enableVisual() {
+		return true;
+	}
+
+	/** Determines whether the hit area of this visual is enabled or not. */
+	protected boolean enableHitArea() {
+		return true;
+	}
+
+	/** Returns the size of the hit area as an outset of the visible rectangle. */
+	protected double getHitAreaOutset() {
+		return 8;
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	public void updateNodes(DiagramLayers layers) {
 		super.updateNodes(layers);
-		for(ILinkMVCBundle incoming : getMVCBundle().getIncomingLinks()) {
-			incoming.getVisual().updateNodes(layers);
+		IDiagramAnchorageMVCBundle mvcBundle = getMVCBundle();
+		if(visualShape != null) {
+			if(enableVisual()) {
+				if(visualShape.getParent() == null) {
+					layers.getAnchorageLayer().add(visualShape, mvcBundle);
+				}
+			} else {
+				layers.getAnchorageLayer().remove(visualShape);
+			}
 		}
-		for(ILinkMVCBundle outgoing : getMVCBundle().getOutgoingLinks()) {
-			outgoing.getVisual().updateNodes(layers);
+		if(hitAreaShape != null) {
+			if(enableHitArea()) {
+				if(hitAreaShape.getParent() == null) {
+					layers.getAnchorageInteractionLayer().add(hitAreaShape, mvcBundle);
+				}
+			} else {
+				layers.getAnchorageInteractionLayer().remove(hitAreaShape);
+			}
+		}
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void createHoverEffect(DiagramLayers layers) {
+		super.createHoverEffect(layers);
+		Color shadow = getHoverShadowColor();
+		if(shadow != null && visualShape != null) {
+			visualShape.setEffect(new DropShadow(10, 3, 3, shadow));
+		}
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected void removeHoverEffect(DiagramLayers layers) {
+		super.removeHoverEffect(layers);
+		if(visualShape != null) {
+			visualShape.setEffect(null);
 		}
 	}
 
@@ -77,4 +136,9 @@
 	protected Paint getHighlightOutgoingLinkBorderColor() {
 		return Color.TOMATO.darker();
 	}
+
+	/** Returns the color of the hover drop shadow. */
+	protected Color getHoverShadowColor() {
+		return Color.DARKGRAY.darker();
+	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/ResizableContentVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/ResizableContentVisualBase.java
deleted file mode 100644
index d2d7e54..0000000
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/ResizableContentVisualBase.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2016, 2018 fortiss GmbH. 
- * 
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v2.0 which is available at
- * http://www.eclipse.org/legal/epl-v20.html
- * 
- * SPDX-License-Identifier: EPL-2.0
- * 
- * Contributors:
- *     Florian Hoelzl (fortiss GmbH) - initial implementation
- *
- *******************************************************************************/
-package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base;
-
-import static javafx.scene.paint.Color.DARKGRAY;
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG;
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG;
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.handles.ArrowResizeHandle.createHorizontalHandle;
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.handles.ArrowResizeHandle.createHorizontalVerticalHandle;
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.handles.ArrowResizeHandle.createVerticalHandle;
-
-import java.util.List;
-
-import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramLayers;
-import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.FeedbackChange;
-import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.configuration.GridConfiguration;
-import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.controller.base.ResizableContentControllerBase;
-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.ILinkMVCBundle;
-import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IMVCBundle;
-import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.MVCBundleTag;
-import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IContentVisual;
-import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.handles.ArrowResizeHandle;
-
-import javafx.geometry.Point2D;
-import javafx.geometry.Rectangle2D;
-import javafx.scene.Node;
-import javafx.scene.paint.Color;
-import javafx.scene.shape.Rectangle;
-
-/** Base class for resizable {@link IContentVisual}s. */
-public abstract class ResizableContentVisualBase extends ContentVisualBase {
-	/** The fill color of the resize handles. */
-	private Color handleFill = Color.DARKGRAY.darker().saturate().saturate();
-
-	/** The selection rectangle (only one at any time). */
-	private final Rectangle selectionFeedbackRectangle = new Rectangle();
-	/** The horizontal resize handle. */
-	private ArrowResizeHandle hHandle;
-	/** The vertical resize handle. */
-	private ArrowResizeHandle vHandle;
-	/** The horizontal and vertical resize handle. */
-	private ArrowResizeHandle hvHandle;
-
-	/** Constructor. */
-	public ResizableContentVisualBase(IContentMVCBundle mvcb) {
-		super(mvcb);
-		selectionFeedbackRectangle.setFill(null);
-		selectionFeedbackRectangle.setStroke(DARKGRAY.darker().saturate().saturate());
-		selectionFeedbackRectangle.setStrokeWidth(2);
-	}
-
-	/** Sets the handle fill color. */
-	public final void setHandleFill(Color handleFill) {
-		this.handleFill = handleFill;
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	protected void applySelectedNotFocusedEffect(DiagramLayers layers) {
-		Rectangle2D b = getCurrentBounds();
-		if(b == null) {
-			return;
-		}
-		alterSelectionRectangle(b, getSelectedNotFocusedColor(), layers);
-		removeResizeFeedback(layers);
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	protected void applySecondarySelectedEffect(DiagramLayers layers) {
-		Rectangle2D b = getCurrentBounds();
-		if(b == null) {
-			return;
-		}
-		alterSelectionRectangle(b, getSecondarySelectedColor(), layers);
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	public final void applySelectedFocusedEffect(DiagramLayers layers) {
-		Rectangle2D b = getCurrentBounds();
-		if(b == null) {
-			return;
-		}
-		alterSelectionRectangle(b, getSelectedFocusedColor(), layers);
-		IContentMVCBundle bundle = getMVCBundle();
-		if(getController() instanceof ResizableContentControllerBase) {
-			ResizableContentControllerBase ctrl = (ResizableContentControllerBase)getController();
-			if(ctrl.allowHorizontalResize()) {
-				Point2D p = getHorizontalResizeHandleLocation(b);
-				double x = b.getMinX() + p.getX();
-				double y = b.getMinY() + p.getY();
-				hHandle = createHorizontalHandle(x, y, getHandleSize(), handleFill);
-				layers.getContentInteractionLayer().add(hHandle, bundle);
-			}
-			if(ctrl.allowVerticalResize()) {
-				Point2D p = getVerticalResizeHandleLocation(b);
-				double x = b.getMinX() + p.getX();
-				double y = b.getMinY() + p.getY();
-				vHandle = createVerticalHandle(x, y, getHandleSize(), handleFill);
-				layers.getContentInteractionLayer().add(vHandle, bundle);
-			}
-			if(ctrl.allowHorizontalResize() && ctrl.allowVerticalResize()) {
-				Point2D p = getHorizontalVerticalResizeHandleLocation(b);
-				double x = b.getMinX() + p.getX();
-				double y = b.getMinY() + p.getY();
-				hvHandle = createHorizontalVerticalHandle(x, y, getHandleSize(), handleFill);
-				layers.getContentInteractionLayer().add(hvHandle, bundle);
-			}
-		}
-		if(getViewer().isLinkHighlightingEnabled()) {
-			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);
-				}
-			}
-		}
-	}
-
-	/** 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);
-	}
-
-	/** Creates the selection feedback rectangle. */
-	private void alterSelectionRectangle(Rectangle2D bounds, Color color, DiagramLayers layers) {
-		selectionFeedbackRectangle.setX(bounds.getMinX());
-		selectionFeedbackRectangle.setY(bounds.getMinY());
-		selectionFeedbackRectangle.setWidth(bounds.getWidth());
-		selectionFeedbackRectangle.setHeight(bounds.getHeight());
-		selectionFeedbackRectangle.setStroke(color);
-		layers.getVisualFeedbackLayer().add(selectionFeedbackRectangle, getMVCBundle());
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	protected void removeAllEffects(DiagramLayers layers) {
-		removeResizeFeedback(layers);
-		layers.getVisualFeedbackLayer().remove(selectionFeedbackRectangle);
-
-		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);
-			}
-		}
-	}
-
-	/** Removes the resize handles. */
-	private void removeResizeFeedback(DiagramLayers layers) {
-		if(getController() instanceof ResizableContentControllerBase) {
-			ResizableContentControllerBase ctrl = (ResizableContentControllerBase)getController();
-			if(ctrl.allowHorizontalResize()) {
-				layers.getContentInteractionLayer().remove(hHandle);
-			}
-			if(ctrl.allowVerticalResize()) {
-				layers.getContentInteractionLayer().remove(vHandle);
-			}
-			if(ctrl.allowHorizontalResize() && ctrl.allowVerticalResize()) {
-				layers.getContentInteractionLayer().remove(hvHandle);
-			}
-		}
-	}
-
-	/** Returns whether the given node is the feedback handle for horizontal resize. */
-	public final boolean isHorizontalFeedbackHandle(Node node) {
-		return node == hHandle;
-	}
-
-	/** Returns whether the given node is the feedback handle for vertical resize. */
-	public final boolean isVerticalFeedbackHandle(Node node) {
-		return node == vHandle;
-	}
-
-	/** Returns whether the given node is the feedback handle for horizontal and vertical resize. */
-	public final boolean isHorizontalVerticalFeedbackHandle(Node node) {
-		return node == hvHandle;
-	}
-
-	/** Returns the location of the horizontal resize handle. Sub-classes may override. */
-	protected Point2D getHorizontalResizeHandleLocation(Rectangle2D r) {
-		return new Point2D(r.getWidth(), r.getHeight() / 2);
-	}
-
-	/** Returns the location of the vertical resize handle. Sub-classes may override. */
-	protected Point2D getVerticalResizeHandleLocation(Rectangle2D r) {
-		return new Point2D(r.getWidth() / 2, r.getHeight());
-	}
-
-	/**
-	 * Returns the location of the horizontal and vertical resize handle. Sub-classes may override.
-	 */
-	protected Point2D getHorizontalVerticalResizeHandleLocation(Rectangle2D r) {
-		return new Point2D(r.getWidth(), r.getHeight());
-	}
-
-	/** Returns the size of the handle. Sub-classes may override. */
-	protected double getHandleSize() {
-		GridConfiguration gconf = getMVCBundle().getViewer().getGridConfig();
-		return Math.min(gconf.getHorizontalSpacing(), gconf.getVerticalSpacing());
-	}
-
-	/**
-	 * {@inheritDoc}
-	 * <p>
-	 * Sub-classes may override, but must call super implementation to update selection feedback
-	 * properly.
-	 */
-	@Override
-	public void updateNodes(DiagramLayers layers) {
-		super.updateNodes(layers);
-		if(selectionFeedbackRectangle.getParent() == null) {
-			return;
-		}
-		Rectangle2D b = getCurrentBounds();
-		if(b == null) {
-			return;
-		}
-		selectionFeedbackRectangle.setX(b.getMinX());
-		selectionFeedbackRectangle.setY(b.getMinY());
-		selectionFeedbackRectangle.setWidth(b.getWidth());
-		selectionFeedbackRectangle.setHeight(b.getHeight());
-		if(getController() instanceof ResizableContentControllerBase) {
-			ResizableContentControllerBase ctrl = (ResizableContentControllerBase)getController();
-			if(ctrl.allowHorizontalResize()) {
-				Point2D p = getHorizontalResizeHandleLocation(b);
-				double x = b.getMinX() + p.getX();
-				double y = b.getMinY() + p.getY();
-				hHandle.update(x, y);
-			}
-			if(ctrl.allowVerticalResize()) {
-				Point2D p = getVerticalResizeHandleLocation(b);
-				double x = b.getMinX() + p.getX();
-				double y = b.getMinY() + p.getY();
-				vHandle.update(x, y);
-			}
-			if(ctrl.allowHorizontalResize() && ctrl.allowVerticalResize()) {
-				Point2D p = getHorizontalVerticalResizeHandleLocation(b);
-				double x = b.getMinX() + p.getX();
-				double y = b.getMinY() + p.getY();
-				hvHandle.update(x, y);
-			}
-		}
-	}
-
-	/** Returns the name of the model element. */
-	protected String getName() {
-		return "";
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	public Rectangle2D getCurrentBounds() {
-		FeedbackChange fb = getFeedbackChange();
-		Rectangle2D mb = getModelBounds();
-		return fb.applyTo(mb);
-	}
-}
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/VisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/VisualBase.java
index 2bea191..d5c2448 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/VisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/base/VisualBase.java
@@ -149,17 +149,6 @@
 		return new Point2D(x, y);
 	}
 
-	/**
-	 * Returns whether the given node acts as a link creation handle for this visual.
-	 * 
-	 * @param node
-	 *            the node to be checked
-	 * @return if the handle can be used to start creating a link
-	 */
-	public boolean isLinkCreationHandle(Node node) {
-		return false;
-	}
-
 	/** {@inheritDoc} */
 	@Override
 	public Point2D getLinkAnchorage(Point2D indication) {
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/.ratings b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/.ratings
index 6620819..a699416 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/.ratings
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/.ratings
@@ -1,6 +1,6 @@
 CircularContentAnchorageVisualBase.java 82eef95197b3fab073e7c7d3e0e4fc7e7949d61d YELLOW
 CircularDiagramAnchorageVisualBase.java f2729ec109d8930e9d5204ad4a326dfa5e943bbd YELLOW
-CurveLinkVisualBase.java 1c31b6d7f250df2a3869c5b3e97dc336edf8273d YELLOW
+CurveLinkVisualBase.java 1b425fcf517bae417cbdadb762e4a535c9d3719b RED
 CurveSegment.java 4cb4ac1c878a7a053defa0e4c719c305210b0341 YELLOW
 EllipticBorderLocation.java e2d393fc3543b905af9845a07c031f95fb8f1118 YELLOW
-EllipticContentVisualBase.java d57a8a991e253bc0ba7d354dd26872e515ad3bf7 YELLOW
+EllipticContentVisualBase.java 948c71d8f34a8bba3824d8e72fdd68099ef1ace5 RED
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CircularContentAnchorageVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CircularContentAnchorageVisualBase.java
index 82eef95..9d4f20f 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CircularContentAnchorageVisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CircularContentAnchorageVisualBase.java
@@ -13,12 +13,10 @@
  *******************************************************************************/
 package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.elliptic;
 
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG;
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG;
-
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramLayers;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.EDragGesture;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl.DefaultMVCBTags;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base.ContentAnchorageVisualBase;
 
@@ -29,20 +27,9 @@
 
 /** Base class for {@link IContentAnchorageVisual}s depicted as circles. */
 public abstract class CircularContentAnchorageVisualBase extends ContentAnchorageVisualBase {
-	/** The circle node of this visual. */
-	private Circle circle;
-
 	/** Constructor. */
 	public CircularContentAnchorageVisualBase(IContentAnchorageMVCBundle mvcb) {
-		super(mvcb);
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	public void addNodes(DiagramLayers layers) {
-		circle = new Circle();
-		updateCircleProperties();
-		layers.getAnchorageLayer().add(circle, getMVCBundle());
+		super(mvcb, new Circle(), new Circle());
 	}
 
 	/** {@inheritDoc} */
@@ -52,33 +39,33 @@
 		updateCircleProperties();
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public void removeNodes(DiagramLayers layers) {
-		layers.getAnchorageLayer().remove(circle);
-		circle = null;
-	}
-
 	/** Sets all properties of the main node. */
 	private void updateCircleProperties() {
 		Rectangle2D b = getCurrentBounds();
+		Circle circle = (Circle)getVisualShape();
 		circle.setCenterX(b.getMinX() + b.getWidth() / 2);
 		circle.setCenterY(b.getMinY() + b.getHeight() / 2);
 		circle.setRadius(b.getWidth() / 2 - getInset());
 		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)) {
+		} else if(getMVCBundle().isTagged(DefaultMVCBTags.HIGHLIGHT_OUTGOING_LINK_TAG)) {
 			circle.setFill(getHighlightOutgoingLinkColor());
 			circle.setStroke(getHighlightOutgoingLinkBorderColor());
 		} else {
 			circle.setFill(getFillColor());
 			circle.setStroke(getBorderColor());
 		}
+
+		if(enableHitArea()) {
+			Circle ha = (Circle)getHitAreaShape();
+			ha.setCenterX(b.getMinX() + b.getWidth() / 2);
+			ha.setCenterY(b.getMinY() + b.getHeight() / 2);
+			ha.setRadius(b.getWidth() / 2 - getInset() + getHitAreaOutset());
+		}
 	}
 
 	/** Returns the insets of the filled circle subtracted from {@link #getDimensions()}. */
@@ -89,7 +76,19 @@
 	/** {@inheritDoc} */
 	@Override
 	public EDragGesture getDragGesture(Node n, Point2D locationOnNode) {
-		if(n == circle) {
+		if(n == getHitAreaCircle()) {
+			Circle ha = getHitAreaCircle();
+			double x = locationOnNode.getX();
+			double y = locationOnNode.getY();
+			double outset = getHitAreaOutset();
+			double inset = getInset();
+			double height = outset + 2 * ha.getRadius();
+			if(x < outset + inset || x > height || y < outset + inset || y > height) {
+				return EDragGesture.NEW_LINK;
+			}
+			return EDragGesture.MOVE;
+		}
+		if(n == getVisualShape()) {
 			return EDragGesture.MOVE;
 		}
 		return EDragGesture.NONE;
@@ -97,18 +96,19 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean isLinkCreationHandle(Node n) {
-		return n == circle;
-	}
-
-	/** {@inheritDoc} */
-	@Override
 	public void requestFocus() {
-		circle.requestFocus();
+		getVisualCircle().requestFocus();
 	}
 
-	/** Returns the circle node. */
-	protected final Circle getCircleNode() {
-		return circle;
+	/** Returns the visual circle. */
+	protected final Circle getVisualCircle() {
+		// wild cast works: see constructor
+		return (Circle)visualShape;
+	}
+
+	/** Returns the hit area circle. */
+	protected final Circle getHitAreaCircle() {
+		// wild cast works: see constructor
+		return (Circle)hitAreaShape;
 	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CircularDiagramAnchorageVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CircularDiagramAnchorageVisualBase.java
index f2729ec..d1d89e5 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CircularDiagramAnchorageVisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CircularDiagramAnchorageVisualBase.java
@@ -13,12 +13,10 @@
  *******************************************************************************/
 package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.elliptic;
 
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG;
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG;
-
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramLayers;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.EDragGesture;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl.DefaultMVCBTags;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base.DiagramAnchorageVisualBase;
 
 import javafx.geometry.Point2D;
@@ -28,24 +26,26 @@
 
 /** Base class for {@link DiagramAnchorageVisualBase free interface visuals} depicted as circles. */
 public abstract class CircularDiagramAnchorageVisualBase extends DiagramAnchorageVisualBase {
-	/** The circle node of this visual. */
-	private Circle circle;
-
 	/** Constructor. */
 	public CircularDiagramAnchorageVisualBase(IDiagramAnchorageMVCBundle mvcb) {
-		super(mvcb);
+		super(mvcb, new Circle(), new Circle());
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public void addNodes(DiagramLayers layers) {
-		circle = new Circle();
-		updateCircleProperties();
-		layers.getAnchorageLayer().add(circle, getMVCBundle());
+	/** Returns the visual circle. */
+	protected final Circle getVisualCircle() {
+		// wild cast works: see constructor
+		return (Circle)visualShape;
+	}
+
+	/** Returns the hit area circle. */
+	protected final Circle getHitAreaCircle() {
+		// wild cast works: see constructor
+		return (Circle)hitAreaShape;
 	}
 
 	/** Sets the properties of the circle node. */
 	private void updateCircleProperties() {
+		Circle circle = getVisualCircle();
 		Rectangle2D b = getCurrentBounds();
 		double cx = b.getMinX() + b.getWidth() / 2;
 		circle.setCenterX(cx);
@@ -55,17 +55,24 @@
 		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)) {
+		} else if(getMVCBundle().isTagged(DefaultMVCBTags.HIGHLIGHT_OUTGOING_LINK_TAG)) {
 			circle.setFill(getHighlightOutgoingLinkColor());
 			circle.setStroke(getHighlightOutgoingLinkBorderColor());
 		} else {
 			circle.setFill(getFillColor());
 			circle.setStroke(getBorderColor());
 		}
+
+		if(enableHitArea()) {
+			Circle ha = getHitAreaCircle();
+			double outset = getHitAreaOutset();
+			ha.setCenterX(cx);
+			ha.setCenterY(cy);
+			ha.setRadius(b.getWidth() / 2 + outset);
+		}
 	}
 
 	/** {@inheritDoc} */
@@ -75,13 +82,6 @@
 		updateCircleProperties();
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public void removeNodes(DiagramLayers layers) {
-		layers.getAnchorageLayer().remove(circle);
-		circle = null;
-	}
-
 	/** Returns the insets of the filled circle subtracted from {@link #getDimensions()}. */
 	protected double getInset() {
 		return 2;
@@ -90,7 +90,19 @@
 	/** {@inheritDoc} */
 	@Override
 	public EDragGesture getDragGesture(Node n, Point2D locationOnNode) {
-		if(n == circle) {
+		if(n == getHitAreaCircle()) {
+			Circle ha = getHitAreaCircle();
+			double x = locationOnNode.getX();
+			double y = locationOnNode.getY();
+			double outset = getHitAreaOutset();
+			double inset = getInset();
+			double height = outset + 2 * ha.getRadius();
+			if(x < outset + inset || x > height || y < outset + inset || y > height) {
+				return EDragGesture.NEW_LINK;
+			}
+			return EDragGesture.MOVE;
+		}
+		if(n == getVisualCircle()) {
 			return EDragGesture.MOVE;
 		}
 		return EDragGesture.NONE;
@@ -98,18 +110,7 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean isLinkCreationHandle(Node n) {
-		return n == circle;
-	}
-
-	/** {@inheritDoc} */
-	@Override
 	public void requestFocus() {
-		circle.requestFocus();
-	}
-
-	/** Returns the circle node. */
-	protected final Circle getCircleNode() {
-		return circle;
+		getVisualCircle().requestFocus();
 	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CurveLinkVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CurveLinkVisualBase.java
index 1c31b6d..899852a 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CurveLinkVisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/CurveLinkVisualBase.java
@@ -26,7 +26,7 @@
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.FeedbackChange;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IAnchorageMVCBundle;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle;
-import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl.DefaultMVCBTags;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl.LinkMVCBundle;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base.LinkVisualBase;
 
@@ -40,15 +40,16 @@
 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);
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public void addNodes(DiagramLayers layers) {
+	/** Adds the curve segments to the scene graph. */
+	private void addCurveSegments(DiagramLayers layers) {
 		LinkMVCBundle linkBundle = getLinkBundle();
 		// start point
 		Point2D sp = getStartAnchorPoint();
@@ -75,27 +76,37 @@
 		for(CurveSegment cs : segments) {
 			cs.addLinkNodes(layers, linkBundle);
 		}
+		curvesAddedToSceneGraph = true;
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public void removeNodes(DiagramLayers layers) {
+	/** Removes the curve segments from the scene graph. */
+	private void removeCurveSegments(DiagramLayers layers) {
 		for(CurveSegment cs : segments) {
 			cs.removeLinkNodes(layers);
 			cs.removeFeedbackNodes(layers);
 		}
 		segments.clear();
+		curvesAddedToSceneGraph = false;
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	public void updateNodes(DiagramLayers layers) {
 		super.updateNodes(layers);
+		if(!enableVisual()) {
+			if(curvesAddedToSceneGraph) {
+				removeCurveSegments(layers);
+			}
+			return;
+		}
 		int pts = getNumerOfBendPoints();
 		int expectedSegments = 1 + (pts - 2) / 3;
+		if(!curvesAddedToSceneGraph) {
+			addCurveSegments(layers);
+		}
 		if(segments.size() != expectedSegments) {
-			removeNodes(layers);
-			addNodes(layers);
+			removeCurveSegments(layers);
+			addCurveSegments(layers);
 			if(getViewer().getSelectedMVCBundle() == getLinkBundle()) {
 				if(getViewer().hasFocus()) {
 					applySelectedFocusedEffect(layers);
@@ -123,24 +134,26 @@
 		}
 		// end point
 		Point2D ep = getEndAnchorPoint();
-		segments.get(segIndex).update(sp, cp1, cp2, ep, 0, bpFeedbackSize, getArrowLength());
+		segments.get(segIndex).update(sp, cp1, cp2, ep, 0, bpFeedbackSize);
 
-		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();
+		if(getMVCBundle().isTagged(DefaultMVCBTags.HIGHLIGHT_INCOMING_LINK_TAG)) {
+			for(CurveSegment seg : segments) {
+				seg.getVisibleCurve().setStroke(getHighlightIncomingLinkColor());
+			}
+		} else if(getMVCBundle().isTagged(DefaultMVCBTags.HIGHLIGHT_OUTGOING_LINK_TAG)) {
+			for(CurveSegment seg : segments) {
+				seg.getVisibleCurve().setStroke(getHighlightOutgoingLinkColor());
+			}
+		} else {
+			for(CurveSegment seg : segments) {
+				seg.getVisibleCurve().setStroke(getLineColor());
 			}
 		}
-		for(CurveSegment seg : segments) {
-			seg.getVisibleCurve().setStroke(color);
-		}
+	}
+
+	/** Determines whether the line segments should be shown. */
+	protected boolean enableVisual() {
+		return true;
 	}
 
 	/** {@inheritDoc} */
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/EllipticContentVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/EllipticContentVisualBase.java
index d57a8a9..cdb56b9 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/EllipticContentVisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/elliptic/EllipticContentVisualBase.java
@@ -17,9 +17,7 @@
 import static java.lang.Math.max;
 import static java.lang.Math.toDegrees;
 import static javafx.scene.paint.Color.DARKORANGE;
-import static javafx.scene.paint.Color.GRAY;
 import static javafx.scene.paint.Color.ORANGE;
-import static javafx.scene.paint.Color.TRANSPARENT;
 import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.elliptic.EllipticBorderLocation.getClosestLocationOnBounds;
 
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramLayers;
@@ -27,49 +25,21 @@
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.configuration.GridConfiguration;
 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.base.ResizableContentVisualBase;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base.ContentVisualBase;
 
 import javafx.geometry.Bounds;
 import javafx.geometry.Dimension2D;
-import javafx.geometry.Insets;
 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.Ellipse;
-import javafx.scene.text.Text;
-import javafx.scene.text.TextAlignment;
 
-/** Base class for {@link ResizableContentVisualBase content visuals} depicted by ellipses. */
-public abstract class EllipticContentVisualBase extends ResizableContentVisualBase {
-	/** The ellipse node of this visual. */
-	private final Ellipse ellipse;
-	/** The text of this visual. */
-	private final Text text;
-	/** The invisible hit area shape. */
-	private final Ellipse hitArea;
-
+/** Base class for {@link ContentVisualBase content visuals} depicted by ellipses. */
+public abstract class EllipticContentVisualBase extends ContentVisualBase {
 	/** Constructor. */
 	public EllipticContentVisualBase(IContentMVCBundle mvcb) {
-		super(mvcb);
-		ellipse = new Ellipse();
-		text = new Text();
-		hitArea = new Ellipse();
-		hitArea.setFill(TRANSPARENT);
-		hitArea.setStroke(Color.BLACK);
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	public void addNodes(DiagramLayers layers) {
-		updateNodeProperties();
-		IContentMVCBundle mvcBundle = getMVCBundle();
-		layers.getContentLayer().add(ellipse, mvcBundle);
-		layers.getTextLayer().add(text, mvcBundle);
-		layers.getContentInteractionLayer().add(hitArea, mvcBundle);
+		super(mvcb, new Ellipse(), new Ellipse());
 	}
 
 	/** {@inheritDoc} */
@@ -81,14 +51,6 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public void removeNodes(DiagramLayers layers) {
-		layers.getContentInteractionLayer().remove(hitArea);
-		layers.getTextLayer().remove(text);
-		layers.getContentLayer().remove(ellipse);
-	}
-
-	/** {@inheritDoc} */
-	@Override
 	public Point2D getClosestAnchorageLocation(Point2D point, IContentAnchorageVisual visual) {
 		Rectangle2D pb = getCurrentBounds();
 		Dimension2D dimension = visual.getDimensions();
@@ -115,14 +77,6 @@
 		return ebl.getLocation().add(pb.getMinX(), pb.getMinY());
 	}
 
-	/**
-	 * Returns the offset of the label to the ellipse center. Negative values move the label towards
-	 * the top-left of the ellipse.
-	 */
-	protected Dimension2D getLabelOffset() {
-		return new Dimension2D(-40, -5);
-	}
-
 	/** {@inheritDoc} */
 	@Override
 	protected Paint getBorderColor() {
@@ -140,6 +94,7 @@
 		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);
@@ -152,59 +107,18 @@
 
 		double lnk = getHitAreaStartLinkSize();
 		double res = getHitAreaResizeSize();
-		hitArea.setCenterX(bounds.getMinX() + w2);
-		hitArea.setCenterY(bounds.getMinY() + h2);
-		hitArea.setRadiusX(w2 + lnk + res / 2);
-		hitArea.setRadiusY(h2 + lnk + res / 2);
-
-		text.setText(getName());
-		Insets i = getTextInsets();
-		Point2D anchorLocation = getTextAnchorLocation();
-		text.setX(bounds.getMinX() + anchorLocation.getX() + i.getLeft());
-		text.setY(bounds.getMinY() + anchorLocation.getY() + i.getTop());
-		text.setWrappingWidth(bounds.getWidth() - i.getLeft() - i.getRight());
-		text.setTextAlignment(getHorizontalTextAlignment());
-		text.setTextOrigin(getVerticalTextAlignment());
-	}
-
-	/** Returns the anchor location relative to the parent bounds. */
-	protected Point2D getTextAnchorLocation() {
-		Insets i = getTextInsets();
-		double croppedHeight = getCurrentBounds().getHeight() - i.getTop() - i.getBottom();
-		return new Point2D(0, croppedHeight / 2);
-	}
-
-	/** Returns the insets of the text label. */
-	protected Insets getTextInsets() {
-		return new Insets(5, 5, 5, 5);
-	}
-
-	/** 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.CENTER;
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	protected void createHoverEffect(DiagramLayers layers) {
-		ellipse.setEffect(new DropShadow(10, 3, 3, GRAY));
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	protected void removeHoverEffect(DiagramLayers layers) {
-		ellipse.setEffect(null);
+		// wild cast woirks: see constructor
+		Ellipse hitShape = (Ellipse)hitAreaShape;
+		hitShape.setCenterX(bounds.getMinX() + w2);
+		hitShape.setCenterY(bounds.getMinY() + h2);
+		hitShape.setRadiusX(w2 + lnk + res / 2);
+		hitShape.setRadiusY(h2 + lnk + res / 2);
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	public EDragGesture getDragGesture(Node n, Point2D locationOnNode) {
-		if(n == hitArea) {
+		if(n == hitAreaShape) {
 			Bounds bounds = n.getBoundsInLocal();
 			double w = bounds.getWidth();
 			double h = bounds.getHeight();
@@ -227,7 +141,6 @@
 			if(hitResizeArea <= 1.0) {
 				// check angle
 				double angle = (360.0 + toDegrees(atan2(-y, x))) % 360.0;
-				System.out.println(angle);
 				if(angle <= 45.0 || angle >= 345.0) {
 					return EDragGesture.RESIZE_H;
 				}
@@ -242,7 +155,7 @@
 			// else startlink
 			return EDragGesture.NEW_LINK;
 		}
-		if(n == ellipse || n == text) {
+		if(n == getVisualEllipse() || n == text) {
 			return EDragGesture.MOVE;
 		}
 		return EDragGesture.NONE;
@@ -251,7 +164,7 @@
 	/** {@inheritDoc} */
 	@Override
 	public void requestFocus() {
-		ellipse.requestFocus();
+		getVisualEllipse().requestFocus();
 	}
 
 	/**
@@ -269,4 +182,10 @@
 		GridConfiguration gridConfig = getViewer().getGridConfig();
 		return max(gridConfig.getVerticalSpacing(), gridConfig.getHorizontalSpacing());
 	}
+
+	/** Returns the ellipse visual. */
+	private Ellipse getVisualEllipse() {
+		// wild cast works: see constructor
+		return (Ellipse)visualShape;
+	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/handles/.ratings b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/handles/.ratings
index 324dd8d..838cb82 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/handles/.ratings
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/handles/.ratings
@@ -1,2 +1,2 @@
-ArrowResizeHandle.java 3bc07b12b563d15b7fdff600a4bea9a55140e110 YELLOW
+ExpandCollapseWidget.java 1bd44adb4a72296f3cd73cb93e03992975f4b471 YELLOW
 LinkArrowWidget.java afa9db594e22c7f6f4b6f66672a4a454414b0519 YELLOW
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/handles/ArrowResizeHandle.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/handles/ArrowResizeHandle.java
deleted file mode 100644
index 3bc07b1..0000000
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/handles/ArrowResizeHandle.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2016, 2018 fortiss GmbH. 
- * 
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v2.0 which is available at
- * http://www.eclipse.org/legal/epl-v20.html
- * 
- * SPDX-License-Identifier: EPL-2.0
- * 
- * Contributors:
- *     Florian Hoelzl (fortiss GmbH) - initial implementation
- *
- *******************************************************************************/
-package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.handles;
-
-import javafx.collections.ObservableList;
-import javafx.scene.paint.Color;
-import javafx.scene.shape.LineTo;
-import javafx.scene.shape.MoveTo;
-import javafx.scene.shape.Path;
-import javafx.scene.shape.PathElement;
-
-/** Class for creating default resize handles (arrows with custom fill color). */
-public final class ArrowResizeHandle extends Path {
-	/** Creates the horizontal resize handle. */
-	public static ArrowResizeHandle createHorizontalHandle(double x, double y, double s,
-			Color fill) {
-		return new ArrowResizeHandle(x, y, s, 0, fill);
-	}
-
-	/** Creates the vertical resize handle. */
-	public static ArrowResizeHandle createVerticalHandle(double x, double y, double s, Color fill) {
-		return new ArrowResizeHandle(x, y, s, 90, fill);
-	}
-
-	/** Creates the horizontal and vertical resize handle. */
-	public static ArrowResizeHandle createHorizontalVerticalHandle(double x, double y, double s,
-			Color fill) {
-		return new ArrowResizeHandle(x, y, s, 45, fill);
-	}
-
-	/** The size of this arrow resize handle. */
-	private final double size;
-
-	/** Constructor. */
-	private ArrowResizeHandle(double x, double y, double size, double angle, Color fill) {
-		this.size = size;
-		update(x, y);
-		setRotate(angle);
-		setFill(fill);
-		setStrokeWidth(1);
-	}
-
-	/** Updates the handle location. */
-	public void update(double x, double y) {
-		double s2 = size / 2;
-		double s4 = size / 4;
-		ObservableList<PathElement> l = getElements();
-		l.clear();
-		l.add(new MoveTo(x + s2, y));
-		l.add(new LineTo(x, y - s2));
-		l.add(new LineTo(x, y - s4));
-		l.add(new LineTo(x - s2, y - s4));
-		l.add(new LineTo(x - s2, y + s4));
-		l.add(new LineTo(x, y + s4));
-		l.add(new LineTo(x, y + s2));
-		l.add(new LineTo(x + s2, y));
-	}
-}
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/handles/ExpandCollapseWidget.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/handles/ExpandCollapseWidget.java
new file mode 100644
index 0000000..1bd44ad
--- /dev/null
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/handles/ExpandCollapseWidget.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2018 fortiss GmbH. 
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Florian Hoelzl (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.handles;
+
+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.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/.ratings b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/.ratings
index 83404ec..5de2354 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/.ratings
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/.ratings
@@ -1,7 +1,8 @@
+BeveledCornerContentVisualBase.java 9b4a244ebe496a9e5e0a2ab96d8ad43961c8858c RED
 LineLinkGraph.java dfa63d3c13547d142c27085c9b74d33c9226db38 YELLOW
-LineLinkVisualBase.java fc42a33af7b56e776beadd49d5683886cc36293e RED
-LineSegment.java ec1cde0fc9bb58326144149a1360a61d27dc4135 YELLOW
+LineLinkVisualBase.java a4fdca23c1371859acc69d9d4c86c0dc600fdaa1 RED
+LineSegment.java c97df0ad6d95c621b73da11a3296ea6f95a73c45 RED
 RectangularBorderLocation.java c6c8641012c31e0bb939e6bdf0dfa1f020cc3c51 YELLOW
 RectangularContentAnchorageVisualBase.java cf2040293cc468cf8e97cd3656bc66580a08eea6 YELLOW
-RectangularContentVisualBase.java e03b8f2f7818f5afe4d48afac6b7ccb1a6c00725 YELLOW
+RectangularContentVisualBase.java 825b93e3cfa1c00a631af700ac710ce2c1e58d6f YELLOW
 RectangularDiagramAnchorageVisualBase.java f08633cd9bed8c933af7591a6405de1b56294c61 YELLOW
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/BeveledCornerContentVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/BeveledCornerContentVisualBase.java
new file mode 100644
index 0000000..3e5bf6a
--- /dev/null
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/BeveledCornerContentVisualBase.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * Copyright (c) 2016, 2018 fortiss GmbH. 
+ * 
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v2.0 which is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Florian Hoelzl (fortiss GmbH) - initial implementation
+ *
+ *******************************************************************************/
+package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular;
+
+import static java.lang.Math.max;
+import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular.RectangularBorderLocation.getClosestLocationOnBounds;
+
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramLayers;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.EDragGesture;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.configuration.GridConfiguration;
+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.base.ContentVisualBase;
+
+import javafx.collections.ObservableList;
+import javafx.geometry.Bounds;
+import javafx.geometry.Dimension2D;
+import javafx.geometry.Point2D;
+import javafx.geometry.Rectangle2D;
+import javafx.scene.Node;
+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 rectangles. */
+// TODO: lots of cloned code with RectangularContentVisualBase
+public abstract class BeveledCornerContentVisualBase extends ContentVisualBase {
+	/** Constructor. */
+	public BeveledCornerContentVisualBase(IContentMVCBundle mvcb) {
+		super(mvcb, new Path(), new Path());
+	}
+
+	/** Updates the properties of the rectangle and the label. */
+	protected void updateNodeProperties() {
+		Rectangle2D bounds = getCurrentBounds();
+		double leftX = bounds.getMinX();
+		double rightX = bounds.getMaxX();
+		double upperY = bounds.getMinY();
+		double lowerY = bounds.getMaxY();
+		Path bevelShape = getBevelShape();
+		Dimension2D bevelDist = getBevelDistance();
+		double dx = bevelDist.getWidth();
+		double dy = bevelDist.getHeight();
+		createBevelPath(leftX, rightX, upperY, lowerY, dx, dy, bevelShape.getElements(), 0, 0);
+		Path ha = (Path)hitAreaShape;
+		createBevelPath(leftX, rightX, upperY, lowerY, dx, dy, ha.getElements(),
+				getHitAreaResizeSize(), getHitAreaStartLinkSize());
+		bevelShape.setOpacity(getOpacity());
+		bevelShape.setStroke(getBorderColor());
+		bevelShape.setStrokeWidth(getBorderWidth());
+		bevelShape.setStrokeType(getBorderType());
+		bevelShape.setFill(getFillColor());
+	}
+
+	/** Creates the path of the bevel shape. */
+	private void createBevelPath(double leftX, double rightX, double upperY, double lowerY,
+			double dx, double dy, ObservableList<PathElement> path, double resize, double link) {
+		double insets = link + resize / 2;
+		path.clear();
+		path.add(new MoveTo(leftX - insets, upperY + dy - insets));
+		path.add(new LineTo(leftX + dx - insets, upperY - insets));
+		path.add(new LineTo(rightX - dx + insets, upperY - insets));
+		path.add(new LineTo(rightX + insets, upperY + dy - insets));
+		path.add(new LineTo(rightX + insets, lowerY - dy + insets));
+		path.add(new LineTo(rightX - dx + insets, lowerY + insets));
+		path.add(new LineTo(leftX + dx - insets, lowerY + insets));
+		path.add(new LineTo(leftX - insets, lowerY - dy + insets));
+		path.add(new LineTo(leftX - insets, upperY + dy - insets));
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void updateNodes(DiagramLayers layers) {
+		super.updateNodes(layers);
+		updateNodeProperties();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Point2D getClosestAnchorageLocation(Point2D point, IContentAnchorageVisual visual) {
+		Rectangle2D pb = getCurrentBounds();
+		RectangularBorderLocation rbl =
+				getClosestLocationOnBounds(point, pb, visual.getDimensions());
+		return rbl.getLocation();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Point2D getAnchorageLocation(IContentAnchorageVisual visual) {
+		Rectangle2D pb = getCurrentBounds();
+		RectangularBorderLocation rbl = new RectangularBorderLocation(visual.getSide(),
+				visual.getOffset(), pb, visual.getDimensions());
+		return rbl.getLocation();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Point2D getLinkAnchorage(Point2D indication) {
+		Rectangle2D b = getCurrentBounds();
+		RectangularBorderLocation loc = getClosestLocationOnBounds(
+				indication.add(-b.getMinX(), -b.getMinY()), b, new Dimension2D(1, 1));
+		return loc.getLocation().add(b.getMinX(), b.getMinY());
+	}
+
+	/** Returns the dimensions of the bevel distance. */
+	protected Dimension2D getBevelDistance() {
+		return new Dimension2D(20, 20);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public EDragGesture getDragGesture(Node n, Point2D locationOnNode) {
+		if(n == hitAreaShape) {
+			double x = locationOnNode.getX();
+			double y = locationOnNode.getY();
+			double l = getHitAreaStartLinkSize();
+			double r = getHitAreaResizeSize();
+			Bounds bounds = n.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(n == visualShape || n == text) {
+			return EDragGesture.MOVE;
+		}
+		return EDragGesture.NONE;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void requestFocus() {
+		visualShape.requestFocus();
+	}
+
+	/** Returns the bevel shape. */
+	protected Path getBevelShape() {
+		// wild cast works: see constructor
+		return (Path)visualShape;
+	}
+
+	/**
+	 * 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() {
+		GridConfiguration gridConfig = getViewer().getGridConfig();
+		return max(gridConfig.getVerticalSpacing(), gridConfig.getHorizontalSpacing());
+	}
+
+	/** Returns the size of the hit area in pixel used to resize the content visual. */
+	protected double getHitAreaResizeSize() {
+		GridConfiguration gridConfig = getViewer().getGridConfig();
+		return max(gridConfig.getVerticalSpacing(), gridConfig.getHorizontalSpacing());
+	}
+}
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/LineLinkVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/LineLinkVisualBase.java
index fc42a33..5139563 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/LineLinkVisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/LineLinkVisualBase.java
@@ -22,6 +22,7 @@
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.FeedbackChange;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IAnchorageMVCBundle;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.ILinkMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl.DefaultMVCBTags;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl.LinkMVCBundle;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base.LinkVisualBase;
 
@@ -35,15 +36,16 @@
 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);
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public void addNodes(DiagramLayers layers) {
+	/** Creates the line segments and adds them to the scene graph. */
+	private void addLineSegments(DiagramLayers layers) {
 		Object bpModel = null;
 		// start point
 		Point2D startAnchorPoint = getStartAnchorPoint();
@@ -72,52 +74,32 @@
 		for(LineSegment ls : segments) {
 			ls.addLinkNodes(layers, bundle);
 		}
+		linesAddedToSceneGraph = true;
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public void removeNodes(DiagramLayers layers) {
+	/** Removes the line segments from the scene graph. */
+	private void removeLineSegments(DiagramLayers layers) {
 		for(LineSegment ls : segments) {
 			ls.removeLinkNodes(layers);
 			ls.removeFeedbackNodes(layers);
 		}
 		segments.clear();
+		linesAddedToSceneGraph = false;
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	public void updateNodes(DiagramLayers layers) {
 		super.updateNodes(layers);
-		// update every lineLinkVisualBase
-		List<ILinkMVCBundle> linkBundle = getViewer().getLinkBundles();
-		for(ILinkMVCBundle l : linkBundle) {
-			if(l.getVisual() instanceof LineLinkVisualBase) {
-				LineLinkVisualBase visual = (LineLinkVisualBase)l.getVisual();
-				visual.updateThisNodes(layers);
+		if(enableVisual()) {
+			if(!linesAddedToSceneGraph) {
+				addLineSegments(layers);
+			}
+		} else {
+			if(linesAddedToSceneGraph) {
+				removeLineSegments(layers);
 			}
 		}
-		// FIXME: merged from master
-		// int pts = getNumberOfBendPoints();
-		// if(segments.size() != pts + 1) {
-		// removeNodes(layers);
-		// addNodes(layers);
-		// if(getViewer().getSelectedMVCBundle() == getLinkBundle()) {
-		// if(getViewer().hasFocus()) {
-		// applySelectedFocusedEffect(layers);
-		// } else {
-		// applySelectedNotFocusedEffect(layers);
-		// }
-		// }
-		// }
-	}
-
-	/** Updates this visual's nodes. */
-	public void updateThisNodes(DiagramLayers layers) {
-		super.updateNodes(layers);
-
-		removeNodes(layers);
-		addNodes(layers);
-
 		if(getViewer().getSelectedMVCBundle() == getLinkBundle()) {
 			if(getViewer().hasFocus()) {
 				applySelectedFocusedEffect(layers);
@@ -126,11 +108,11 @@
 			}
 		}
 
-		if(getMVCBundle().hasTag(DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG)) {
+		if(getMVCBundle().isTagged(DefaultMVCBTags.HIGHLIGHT_INCOMING_LINK_TAG)) {
 			for(LineSegment seg : segments) {
 				seg.setStrokeColor(getHighlightIncomingLinkColor());
 			}
-		} else if(getMVCBundle().hasTag(DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG)) {
+		} else if(getMVCBundle().isTagged(DefaultMVCBTags.HIGHLIGHT_OUTGOING_LINK_TAG)) {
 			for(LineSegment seg : segments) {
 				seg.setStrokeColor(getHighlightOutgoingLinkColor());
 			}
@@ -139,28 +121,11 @@
 				seg.setStrokeColor(getLineColor());
 			}
 		}
-		// FIXME: merged from master
-		// // end point
-		// Point2D endPoint = getEndAnchorPoint();
-		// segments.get(segIndex).update(sx, sy, endPoint.getX(), endPoint.getY(), 0,
-		// getArrowLength());
-		//
-		// 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.getVisibleLine().setStroke(color);
-		// }
+	}
+
+	/** Determines whether the line segments should be shown. */
+	protected boolean enableVisual() {
+		return true;
 	}
 
 	/** {@inheritDoc} */
@@ -373,11 +338,4 @@
 		}
 		return false;
 	}
-
-	/** {@inheritDoc} */
-	@Override
-	public EDragGesture getDragGesture(Node node, Point2D locationOnNode) {
-		// FIXME: bendpoint create gesture
-		return EDragGesture.NONE;
-	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/LineSegment.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/LineSegment.java
index c97df0a..ed70700 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/LineSegment.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/LineSegment.java
@@ -141,6 +141,9 @@
 	/** Computes the visible lines. If this line intersects with another, it will be interrupted. */
 	private void computeVisibleLines() {
 		visibleLines.add(visibleLine);
+		if(lineLinkGraph == null) {
+			return;
+		}
 
 		for(Line otherLine : lineLinkGraph.getLine2segment().keySet()) {
 			List<Line> newLines = new LinkedList<>();
@@ -222,7 +225,9 @@
 	public void removeLinkNodes(DiagramLayers layers) {
 		ILayer linkLayer = layers.getLinkLayer();
 
-		lineLinkGraph.removeSegment(this, visibleLines);
+		if(lineLinkGraph != null) {
+			lineLinkGraph.removeSegment(this, visibleLines);
+		}
 
 		for(Line line : visibleLines) {
 			linkLayer.remove(line);
@@ -257,4 +262,14 @@
 			bridge.setStroke(color);
 		}
 	}
+
+	/** Sets the color of the visibleLines. */
+	public void setStrokeColor(Paint color) {
+		for(Line line : visibleLines) {
+			line.setStroke(color);
+		}
+		for(Arc bridge : bridges) {
+			bridge.setStroke(color);
+		}
+	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentAnchorageVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentAnchorageVisualBase.java
index cf20402..9fadce3 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentAnchorageVisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentAnchorageVisualBase.java
@@ -13,12 +13,10 @@
  *******************************************************************************/
 package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular;
 
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG;
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG;
-
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramLayers;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.EDragGesture;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IContentAnchorageMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl.DefaultMVCBTags;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.IContentAnchorageVisual;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base.ContentAnchorageVisualBase;
 
@@ -30,20 +28,21 @@
 
 /** Base class for {@link IContentAnchorageVisual}s depicted as rectangles (or squares). */
 public abstract class RectangularContentAnchorageVisualBase extends ContentAnchorageVisualBase {
-	/** The rectangle node of this visual. */
-	private Rectangle rectangle;
-
 	/** Constructor. */
 	public RectangularContentAnchorageVisualBase(IContentAnchorageMVCBundle mvcb) {
-		super(mvcb);
+		super(mvcb, new Rectangle(), new Rectangle());
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public void addNodes(DiagramLayers layers) {
-		rectangle = new Rectangle();
-		updateRectangleProperties();
-		layers.getAnchorageLayer().add(rectangle, getMVCBundle());
+	/** Returns the visual rectangle. */
+	protected final Rectangle getVisualRectangle() {
+		// wild cast works: see constructor
+		return (Rectangle)visualShape;
+	}
+
+	/** Returns the hit area rectangle. */
+	protected final Rectangle getHitAreaRectangle() {
+		// wild cast works: see constructor
+		return (Rectangle)hitAreaShape;
 	}
 
 	/** {@inheritDoc} */
@@ -53,17 +52,11 @@
 		updateRectangleProperties();
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public void removeNodes(DiagramLayers layers) {
-		layers.getAnchorageLayer().remove(rectangle);
-		rectangle = null;
-	}
-
 	/** Updates all properties of the rectangle node. */
 	private void updateRectangleProperties() {
 		Rectangle2D bounds = getCurrentBounds();
 		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();
@@ -73,17 +66,26 @@
 		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)) {
+		} else if(getMVCBundle().isTagged(DefaultMVCBTags.HIGHLIGHT_OUTGOING_LINK_TAG)) {
 			rectangle.setFill(getHighlightOutgoingLinkColor());
 			rectangle.setStroke(getHighlightOutgoingLinkBorderColor());
 		} else {
 			rectangle.setFill(getFillColor());
 			rectangle.setStroke(getBorderColor());
 		}
+
+		if(enableHitArea()) {
+			// 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);
+		}
 	}
 
 	/** Returns the insets for the filled rectangle subtracted from {@link #getDimensions()}. */
@@ -94,7 +96,19 @@
 	/** {@inheritDoc} */
 	@Override
 	public EDragGesture getDragGesture(Node n, Point2D locationOnNode) {
-		if(n == rectangle) {
+		if(n == getHitAreaRectangle()) {
+			// wild cast works: see addNodes(...)
+			Rectangle ha = getHitAreaRectangle();
+			double x = locationOnNode.getX();
+			double y = locationOnNode.getY();
+			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;
@@ -102,13 +116,7 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean isLinkCreationHandle(Node n) {
-		return n == rectangle;
-	}
-
-	/** {@inheritDoc} */
-	@Override
 	public void requestFocus() {
-		rectangle.requestFocus();
+		getVisualRectangle().requestFocus();
 	}
 }
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentVisualBase.java
index e03b8f2..825b93e 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentVisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularContentVisualBase.java
@@ -14,8 +14,6 @@
 package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular;
 
 import static java.lang.Math.max;
-import static javafx.scene.paint.Color.GRAY;
-import static javafx.scene.paint.Color.TRANSPARENT;
 import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular.RectangularBorderLocation.getClosestLocationOnBounds;
 
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramLayers;
@@ -23,153 +21,51 @@
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.configuration.GridConfiguration;
 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.base.ResizableContentVisualBase;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base.ContentVisualBase;
 
-import javafx.collections.ObservableList;
 import javafx.geometry.Bounds;
 import javafx.geometry.Dimension2D;
-import javafx.geometry.Insets;
 import javafx.geometry.Point2D;
 import javafx.geometry.Rectangle2D;
-import javafx.geometry.VPos;
 import javafx.scene.Node;
-import javafx.scene.effect.DropShadow;
-import javafx.scene.shape.LineTo;
-import javafx.scene.shape.MoveTo;
-import javafx.scene.shape.Path;
-import javafx.scene.shape.PathElement;
 import javafx.scene.shape.Rectangle;
-import javafx.scene.shape.Shape;
-import javafx.scene.text.Text;
-import javafx.scene.text.TextAlignment;
 
-/** Base class for {@link ResizableContentVisualBase content visuals} depicted by rectangles. */
-public abstract class RectangularContentVisualBase extends ResizableContentVisualBase {
-	/** The rectangle node of this visual. */
-	protected final Shape rectangleOrBevelShape;
-	/** The text of this visual. */
-	protected final Text text;
-	/** The invisible shape used for mouse hit detection. */
-	private final Shape hitArea;
-
+/** Base class for {@link ContentVisualBase content visuals} depicted by rectangles. */
+// TODO: lots of clone code with BeveledCornerContentVisualBase
+public abstract class RectangularContentVisualBase extends ContentVisualBase {
 	/** Constructor. */
 	public RectangularContentVisualBase(IContentMVCBundle mvcb) {
-		super(mvcb);
-		text = new Text();
-		if(useRoundedRectangle()) {
-			rectangleOrBevelShape = new Rectangle();
-			hitArea = new Rectangle();
-		} else {
-			rectangleOrBevelShape = new Path();
-			hitArea = new Path();
-		}
-		hitArea.setFill(TRANSPARENT);
-		hitArea.setStroke(TRANSPARENT);
+		super(mvcb, new Rectangle(), new Rectangle());
 	}
 
 	/** Updates the properties of the rectangle and the label. */
 	protected void updateNodeProperties() {
 		Rectangle2D bounds = getCurrentBounds();
 		double leftX = bounds.getMinX();
-		double rightX = bounds.getMaxX();
 		double upperY = bounds.getMinY();
-		double lowerY = bounds.getMaxY();
 		double width = bounds.getWidth();
 		double height = bounds.getHeight();
-		if(useRoundedRectangle()) {
-			Rectangle rectangle = getRectangleShape();
-			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());
-			// resize hit area
-			Rectangle ha = (Rectangle)hitArea;
-			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);
-		} else {
-			Path bevelShape = getBevelShape();
-			Dimension2D bevelDist = getBevelDistance();
-			double dx = bevelDist.getWidth();
-			double dy = bevelDist.getHeight();
-			createBevelPath(leftX, rightX, upperY, lowerY, dx, dy, bevelShape.getElements(), 0, 0);
-			Path ha = (Path)hitArea;
-			// FIXME
-			createBevelPath(leftX, rightX, upperY, lowerY, dx, dy, ha.getElements(),
-					getHitAreaResizeSize(), getHitAreaStartLinkSize());
-			bevelShape.setOpacity(getOpacity());
-			bevelShape.setStroke(getBorderColor());
-			bevelShape.setStrokeWidth(getBorderWidth());
-			bevelShape.setStrokeType(getBorderType());
-			bevelShape.setFill(getFillColor());
-		}
-		text.setText(getName());
-		Insets i = getTextInsets();
-		Point2D anchorLocation = getTextAnchorLocation();
-		text.setX(leftX + anchorLocation.getX() + i.getLeft());
-		text.setY(upperY + anchorLocation.getY() + i.getTop());
-		text.setWrappingWidth(width - i.getLeft() - i.getRight());
-		text.setTextAlignment(getHorizontalTextAlignment());
-		text.setTextOrigin(getVerticalTextAlignment());
-	}
-
-	/** Creates the path of the bevel shape. */
-	private void createBevelPath(double leftX, double rightX, double upperY, double lowerY,
-			double dx, double dy, ObservableList<PathElement> path, double resize, double link) {
-		double insets = link + resize / 2;
-		path.clear();
-		path.add(new MoveTo(leftX - insets, upperY + dy - insets));
-		path.add(new LineTo(leftX + dx - insets, upperY - insets));
-		path.add(new LineTo(rightX - dx + insets, upperY - insets));
-		path.add(new LineTo(rightX + insets, upperY + dy - insets));
-		path.add(new LineTo(rightX + insets, lowerY - dy + insets));
-		path.add(new LineTo(rightX - dx + insets, lowerY + insets));
-		path.add(new LineTo(leftX + dx - insets, lowerY + insets));
-		path.add(new LineTo(leftX - insets, lowerY - dy + insets));
-		path.add(new LineTo(leftX - insets, upperY + dy - insets));
-	}
-
-	/** Returns the anchor location relative to the parent bounds. */
-	protected Point2D getTextAnchorLocation() {
-		Insets i = getTextInsets();
-		double croppedHeight = getCurrentBounds().getHeight() - i.getTop() - i.getBottom();
-		return new Point2D(0, croppedHeight / 2);
-	}
-
-	/** Returns the insets of the text label. */
-	protected Insets getTextInsets() {
-		return new Insets(5, 5, 5, 5);
-	}
-
-	/** 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.CENTER;
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	public void addNodes(DiagramLayers layers) {
-		IContentMVCBundle mvcBundle = getMVCBundle();
-		updateNodeProperties();
-		layers.getContentLayer().add(rectangleOrBevelShape, mvcBundle);
-		layers.getTextLayer().add(text, mvcBundle);
-		layers.getContentInteractionLayer().add(hitArea, mvcBundle);
+		Rectangle rectangle = getRectangleShape();
+		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());
+		// resize hit area
+		Rectangle ha = (Rectangle)hitAreaShape;
+		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);
 	}
 
 	/** {@inheritDoc} */
@@ -181,14 +77,6 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public void removeNodes(DiagramLayers layers) {
-		layers.getTextLayer().remove(text);
-		layers.getContentLayer().remove(rectangleOrBevelShape);
-		layers.getContentInteractionLayer().remove(hitArea);
-	}
-
-	/** {@inheritDoc} */
-	@Override
 	public Point2D getClosestAnchorageLocation(Point2D point, IContentAnchorageVisual visual) {
 		Rectangle2D pb = getCurrentBounds();
 		RectangularBorderLocation rbl =
@@ -219,34 +107,10 @@
 		return new Dimension2D(15, 15);
 	}
 
-	/** Returns the dimensions of the bevel distance. */
-	protected Dimension2D getBevelDistance() {
-		return new Dimension2D(20, 20);
-	}
-
-	/** Returns whether this shape uses a rounded rectangle or a bevel eight-corner shape. */
-	protected boolean useRoundedRectangle() {
-		return true;
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	protected void createHoverEffect(DiagramLayers layers) {
-		DropShadow ds = new DropShadow(10, 3, 3, GRAY);
-		rectangleOrBevelShape.setEffect(ds);
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	protected void removeHoverEffect(DiagramLayers layers) {
-		rectangleOrBevelShape.setEffect(null);
-	}
-
 	/** {@inheritDoc} */
 	@Override
 	public EDragGesture getDragGesture(Node n, Point2D locationOnNode) {
-		if(n == hitArea) {
-			// if(useRoundedRectangle()) {
+		if(n == hitAreaShape) {
 			double x = locationOnNode.getX();
 			double y = locationOnNode.getY();
 			double l = getHitAreaStartLinkSize();
@@ -272,11 +136,8 @@
 				return EDragGesture.RESIZE_VH;
 			}
 			return EDragGesture.NONE;
-			// }
-			// TODO: hit area of bevel shape
-			// return EDragGesture.NONE;
 		}
-		if(n == rectangleOrBevelShape || n == text) {
+		if(n == visualShape || n == text) {
 			return EDragGesture.MOVE;
 		}
 		return EDragGesture.NONE;
@@ -285,19 +146,13 @@
 	/** {@inheritDoc} */
 	@Override
 	public void requestFocus() {
-		rectangleOrBevelShape.requestFocus();
+		visualShape.requestFocus();
 	}
 
-	/** Returns the rectangle shape if {@link #useRoundedRectangle()} is {@code true}. */
+	/** Returns the rectangle shape. */
 	protected Rectangle getRectangleShape() {
 		// wild cast works: see constructor
-		return useRoundedRectangle() ? (Rectangle)rectangleOrBevelShape : null;
-	}
-
-	/** Returns the bevel shape if {@link #useRoundedRectangle()} is {@code false}. */
-	protected Path getBevelShape() {
-		// wild cast works: see constructor
-		return useRoundedRectangle() ? null : (Path)rectangleOrBevelShape;
+		return (Rectangle)visualShape;
 	}
 
 	/**
diff --git a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularDiagramAnchorageVisualBase.java b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularDiagramAnchorageVisualBase.java
index f08633c..173c547 100644
--- a/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularDiagramAnchorageVisualBase.java
+++ b/org.eclipse.systemfocus.kernel.common.ui/src/org/eclipse/systemfocus/kernel/common/ui/javafx/lwfxef/visual/rectangular/RectangularDiagramAnchorageVisualBase.java
@@ -13,12 +13,10 @@
  *******************************************************************************/
 package org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.rectangular;
 
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_INCOMING_LINK_TAG;
-import static org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramViewerDefaultTags.HIGHLIGHT_OUTGOING_LINK_TAG;
-
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.DiagramLayers;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.EDragGesture;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.IDiagramAnchorageMVCBundle;
+import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.mvc.impl.DefaultMVCBTags;
 import org.eclipse.systemfocus.kernel.common.ui.javafx.lwfxef.visual.base.DiagramAnchorageVisualBase;
 
 import javafx.geometry.Point2D;
@@ -30,42 +28,54 @@
  * Base class for {@link DiagramAnchorageVisualBase free interface visuals} depicted as rectangles.
  */
 public abstract class RectangularDiagramAnchorageVisualBase extends DiagramAnchorageVisualBase {
-	/** The rectangle node of this visual. */
-	private Rectangle rectangle;
-
 	/** Constructor. */
 	public RectangularDiagramAnchorageVisualBase(IDiagramAnchorageMVCBundle mvcb) {
-		super(mvcb);
+		super(mvcb, new Rectangle(), new Rectangle());
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public void addNodes(DiagramLayers layers) {
-		rectangle = new Rectangle();
-		layers.getAnchorageLayer().add(rectangle, getMVCBundle());
-		updateRectangleProperties();
+	/** Returns the visual rectangle. */
+	protected final Rectangle getVisualRectangle() {
+		// wild cast works: see constructor
+		return (Rectangle)visualShape;
+	}
+
+	/** Returns the hit area rectangle. */
+	protected final Rectangle getHitAreaRectangle() {
+		// wild cast works: see constructor
+		return (Rectangle)hitAreaShape;
 	}
 
 	/** Sets the properties of the rectangle node. */
 	private void updateRectangleProperties() {
+		Rectangle visual = getVisualRectangle();
 		Rectangle2D bounds = getCurrentBounds();
-		rectangle.setX(bounds.getMinX() + 1);
-		rectangle.setY(bounds.getMinY() + 1);
-		rectangle.setWidth(bounds.getWidth() - 2);
-		rectangle.setHeight(bounds.getHeight() - 2);
-		rectangle.setOpacity(getOpacity());
-		rectangle.setStrokeWidth(getBorderWidth());
-		rectangle.setStrokeType(getBorderType());
+		visual.setX(bounds.getMinX() + 1);
+		visual.setY(bounds.getMinY() + 1);
+		visual.setWidth(bounds.getWidth() - 2);
+		visual.setHeight(bounds.getHeight() - 2);
+		visual.setOpacity(getOpacity());
+		visual.setStrokeWidth(getBorderWidth());
+		visual.setStrokeType(getBorderType());
 
 		if(getMVCBundle().hasTag(HIGHLIGHT_INCOMING_LINK_TAG)) {
-			rectangle.setFill(getHighlightIncomingLinkColor());
-			rectangle.setStroke(getHighlightIncomingLinkBorderColor());
+			visual.setFill(getHighlightIncomingLinkColor());
+			visual.setStroke(getHighlightIncomingLinkBorderColor());
 		} else if(getMVCBundle().hasTag(HIGHLIGHT_OUTGOING_LINK_TAG)) {
-			rectangle.setFill(getHighlightOutgoingLinkColor());
-			rectangle.setStroke(getHighlightOutgoingLinkBorderColor());
+			visual.setFill(getHighlightOutgoingLinkColor());
+			visual.setStroke(getHighlightOutgoingLinkBorderColor());
 		} else {
-			rectangle.setFill(getFillColor());
-			rectangle.setStroke(getBorderColor());
+			visual.setFill(getFillColor());
+			visual.setStroke(getBorderColor());
+		}
+
+		if(enableHitArea()) {
+			// 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);
 		}
 	}
 
@@ -78,15 +88,20 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public void removeNodes(DiagramLayers layers) {
-		layers.getAnchorageLayer().remove(rectangle);
-		rectangle = null;
-	}
-
-	/** {@inheritDoc} */
-	@Override
 	public EDragGesture getDragGesture(Node n, Point2D locationOnNode) {
-		if(n == rectangle) {
+		if(n == getHitAreaRectangle()) {
+			// wild cast works: see addNodes(...)
+			Rectangle ha = getHitAreaRectangle();
+			double x = locationOnNode.getX();
+			double y = locationOnNode.getY();
+			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;
@@ -94,13 +109,7 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean isLinkCreationHandle(Node n) {
-		return n == rectangle;
-	}
-
-	/** {@inheritDoc} */
-	@Override
 	public void requestFocus() {
-		rectangle.requestFocus();
+		getVisualRectangle().requestFocus();
 	}
 }
diff --git a/org.eclipse.systemfocus.kernel.core/model/kernel.genmodel b/org.eclipse.systemfocus.kernel.core/model/kernel.genmodel
index c7c6eea..4cf5838 100644
--- a/org.eclipse.systemfocus.kernel.core/model/kernel.genmodel
+++ b/org.eclipse.systemfocus.kernel.core/model/kernel.genmodel
@@ -65,7 +65,8 @@
         <genFeatures createChild="false" ecoreFeature="ecore:EAttribute kernel.ecore#//constraints/ConstraintInstance/constraintID"/>
       </genClasses>
       <genClasses ecoreClass="kernel.ecore#//constraints/IConstraintVerificationStatus">
-        <genOperations ecoreOperation="kernel.ecore#//constraints/IConstraintVerificationStatus/getConstraint"/>
+        <genOperations ecoreOperation="kernel.ecore#//constraints/IConstraintVerificationStatus/getConstraint"
+            body="return (ConstraintInstance)this.eContainer();"/>
       </genClasses>
       <genClasses ecoreClass="kernel.ecore#//constraints/ConstrainedWithChecksum">
         <genFeatures notify="false" createChild="false" propertySortChoices="true"