--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.diagram.participant;\r
+\r
+import java.awt.Color;\r
+import java.awt.Font;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.simantics.Simantics;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.common.request.PossibleIndexRoot;\r
+import org.simantics.db.common.request.UnaryRead;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.adapter.Instances;\r
+import org.simantics.db.layer0.request.IsActive;\r
+import org.simantics.db.layer0.util.Layer0Utils;\r
+import org.simantics.db.procedure.Listener;\r
+import org.simantics.diagram.elements.TextNode;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.g2d.canvas.Hints;\r
+import org.simantics.g2d.canvas.ICanvasContext;\r
+import org.simantics.g2d.canvas.SGDesignation;\r
+import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;\r
+import org.simantics.g2d.canvas.impl.HintReflection.HintListener;\r
+import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;\r
+import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;\r
+import org.simantics.g2d.utils.Alignment;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+import org.simantics.utils.datastructures.hints.IHintContext;\r
+import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
+import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
+import org.simantics.utils.datastructures.hints.IHintObservable;\r
+import org.simantics.utils.ui.ErrorLogger;\r
+\r
+/**\r
+ * DiagramModelActivityTracker tracks whether or not the specified input\r
+ * resource is part of the diagram model or not and modifies the canvas\r
+ * background color accordingly. If the diagram is not in an active model, an\r
+ * <b>INACTIVE MODEL</b> banner is also shown in the background using\r
+ * {@link TextNode}.\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class DiagramModelActivityTracker extends AbstractCanvasParticipant {\r
+\r
+ public static final Key KEY_IN_ACTIVE_MODEL = new KeyOf(String.class, "IN_ACTIVE_MODEL");\r
+ public static final Key KEY_ACTIVE_BACKGROUND = new KeyOf(Color.class, "ACTIVE_BACKGROUND");\r
+ public static final Key KEY_INACTIVE_BACKGROUND = new KeyOf(Color.class, "INACTIVE_BACKGROUND");\r
+\r
+ private static final Color DEFAULT_ACTIVE_BACKGROUND = Color.WHITE;\r
+ private static final Color DEFAULT_INACTIVE_BACKGROUND = new Color(242, 242, 242);\r
+\r
+ Resource input;\r
+ IsInActiveModelListener listener;\r
+ TextNode bannerNode;\r
+\r
+ public DiagramModelActivityTracker(Resource input) {\r
+ if (input == null)\r
+ throw new NullPointerException("null input");\r
+ this.input = input;\r
+ }\r
+\r
+ @HintListener(Class=Hints.class, Field="KEY_CONTROL_BOUNDS")\r
+ public void controlBoundsChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {\r
+ if (bannerNode == null)\r
+ return;\r
+ if (newValue != null) {\r
+ Rectangle2D bounds = (Rectangle2D) newValue;\r
+ AffineTransform at = new AffineTransform();\r
+ at.translate(5,5);\r
+// at.translate(bounds.getWidth() / 2, bounds.getHeight() / 2);\r
+// at.scale(2, 2);\r
+// at.rotate(-Math.PI/6);\r
+// at.translate(bounds.getWidth() - 150, bounds.getHeight() - 30);\r
+// at.scale(1, 1);\r
+ bannerNode.setTransform(at);\r
+ } else {\r
+ // Disable rendering\r
+ bannerNode.setColor(null);\r
+ }\r
+ }\r
+\r
+ @HintListener(Class=DiagramModelActivityTracker.class, Field="KEY_IN_ACTIVE_MODEL")\r
+ public void containingModelActivityChanged(IHintObservable sender, Key key, Object oldValue, Object _newValue) {\r
+ String newValue = (String)_newValue;\r
+ if (bannerNode == null)\r
+ return;\r
+ if (newValue == null) {\r
+ bannerNode.setText("");\r
+ } else {\r
+ bannerNode.setText(newValue);\r
+ //bannerNode.setColor(new Color(192, 32, 32, 128));\r
+ //bannerNode.setColor(new Color(192, 32, 32, 32));\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void addedToContext(ICanvasContext ctx) {\r
+ super.addedToContext(ctx);\r
+\r
+ listener = new IsInActiveModelListener(ctx);\r
+ Simantics.getSession().async(new IsActiveDiagram(input), listener);\r
+\r
+ if (!ctx.getHintStack().containsHint(KEY_ACTIVE_BACKGROUND))\r
+ ctx.getDefaultHintContext().setHint(KEY_ACTIVE_BACKGROUND, DEFAULT_ACTIVE_BACKGROUND);\r
+ if (!ctx.getHintStack().containsHint(KEY_INACTIVE_BACKGROUND))\r
+ ctx.getDefaultHintContext().setHint(KEY_INACTIVE_BACKGROUND, DEFAULT_INACTIVE_BACKGROUND);\r
+ }\r
+\r
+ @Override\r
+ public void removedFromContext(ICanvasContext ctx) {\r
+ if (listener != null) {\r
+ listener.dispose();\r
+ listener = null;\r
+ }\r
+ super.removedFromContext(ctx);\r
+ }\r
+\r
+ @SGInit(designation = SGDesignation.CONTROL)\r
+ public void initSG(G2DParentNode parent) {\r
+ bannerNode = parent.addNode("inactivity-banner", TextNode.class);\r
+ bannerNode.setZIndex(Integer.MIN_VALUE / 4);\r
+ //bannerNode.setZIndex(Integer.MAX_VALUE / 4);\r
+ bannerNode.setHorizontalAlignment((byte) Alignment.LEADING.ordinal());\r
+ bannerNode.setVerticalAlignment((byte) Alignment.LEADING.ordinal());\r
+ //bannerNode.setText("Model is not active.");\r
+ bannerNode.setText("");\r
+ bannerNode.setFont(Font.decode("Arial 16"));\r
+ //bannerNode.setBackgroundColor(new Color(192, 192, 192, 128));\r
+ bannerNode.setPadding(10, 10);\r
+ bannerNode.setBorderColor(Color.GRAY);\r
+ bannerNode.setBorderWidth(0);\r
+ bannerNode.setEditable(false);\r
+ }\r
+\r
+ @SGCleanup\r
+ public void cleanupSG() {\r
+ if (bannerNode != null) {\r
+ bannerNode.remove();\r
+ bannerNode = null;\r
+ }\r
+ }\r
+\r
+ static class IsActiveDiagram extends UnaryRead<Resource, String> {\r
+ public IsActiveDiagram(Resource parameter) {\r
+ super(parameter);\r
+ }\r
+ @Override\r
+ public String perform(ReadGraph graph) throws DatabaseException {\r
+ \r
+ Resource indexRoot = graph.syncRequest( new PossibleIndexRoot(parameter) );\r
+ if (indexRoot == null) {\r
+ return "This diagram is not part of any model or shared library";\r
+ }\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
+ ModelingResources MOD = ModelingResources.getInstance(graph);\r
+ Resource composite = graph.getPossibleObject(parameter, MOD.DiagramToComposite);\r
+ if(composite == null)\r
+ return "This diagram does not have a corresponding configuration";\r
+ \r
+ Resource componentType = graph.getPossibleObject(composite, STR.Defines);\r
+ if(componentType != null) {\r
+ // This is a component type\r
+ boolean published = Layer0Utils.isPublished(graph, componentType);\r
+ if(published)\r
+ return "This diagram defines a published (read-only) user component";\r
+ }\r
+ \r
+ if(graph.isInstanceOf(indexRoot, L0.SharedOntology)) {\r
+ return "";\r
+ }\r
+ \r
+ boolean activeModel = graph.syncRequest( new IsActive(indexRoot) );\r
+ if(!activeModel)\r
+ return "The model of this diagram is not active";\r
+ \r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+ Instances query = graph.adapt(DIA.DiagramActivityCondition, Instances.class);\r
+\r
+ for(Resource condition : query.find(graph, indexRoot)) {\r
+ String value = Simantics.tryInvokeSCL(graph, condition, DIA.DiagramActivityCondition_test, parameter);\r
+ if(value != null && !value.isEmpty()) return value;\r
+ }\r
+ \r
+ return "";\r
+ \r
+ }\r
+ }\r
+\r
+ static class IsInActiveModelListener implements Listener<String> {\r
+ ICanvasContext context;\r
+ public IsInActiveModelListener(ICanvasContext context) {\r
+ this.context = context;\r
+ }\r
+ @Override\r
+ public void execute(final String result) {\r
+ final Color color = (result.isEmpty())\r
+ ? context.getHintStack().<Color>getHint(KEY_ACTIVE_BACKGROUND)\r
+ : context.getHintStack().<Color>getHint(KEY_INACTIVE_BACKGROUND);\r
+\r
+ context.getThreadAccess().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ ICanvasContext ctx = context;\r
+ if (ctx == null)\r
+ return;\r
+ if (ctx.isDisposed())\r
+ return;\r
+ IHintContext hints = ctx.getDefaultHintContext();\r
+ if (color != null)\r
+ hints.setHint(Hints.KEY_BACKGROUND_COLOR, color);\r
+ hints.setHint(KEY_IN_ACTIVE_MODEL, result);\r
+ }\r
+ });\r
+ }\r
+ public void dispose() {\r
+ context = null;\r
+ }\r
+ @Override\r
+ public boolean isDisposed() {\r
+ ICanvasContext context = this.context;\r
+ return context == null || context.isDisposed();\r
+ }\r
+ @Override\r
+ public void exception(Throwable t) {\r
+ ErrorLogger.defaultLogError(t);\r
+ }\r
+ }\r
+\r
+}\r