Merge commit '0b471805f017da83d715a0d8409f53bdd009d31e'
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / diagram / style / IssueDecorationStyle.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.modeling.ui.diagram.style;\r
13 \r
14 import java.awt.geom.AffineTransform;\r
15 import java.awt.geom.Rectangle2D;\r
16 import java.util.ArrayList;\r
17 import java.util.Collections;\r
18 import java.util.List;\r
19 import java.util.Map;\r
20 import java.util.Set;\r
21 \r
22 import org.simantics.db.ReadGraph;\r
23 import org.simantics.db.Resource;\r
24 import org.simantics.db.common.procedure.adapter.TransientCacheListener;\r
25 import org.simantics.db.exception.DatabaseException;\r
26 import org.simantics.db.layer0.variable.Variable;\r
27 import org.simantics.db.layer0.variable.Variables;\r
28 import org.simantics.diagram.profile.StyleBase;\r
29 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
30 import org.simantics.issues.Severity;\r
31 import org.simantics.issues.common.IssueResourcesContexts;\r
32 import org.simantics.issues.common.ListModelIssuesBySeverity;\r
33 import org.simantics.modeling.ModelingResources;\r
34 import org.simantics.modeling.ui.Activator;\r
35 import org.simantics.modeling.ui.diagram.style.IssueDecorationStyle.IssueResult;\r
36 import org.simantics.scenegraph.INode;\r
37 import org.simantics.scenegraph.g2d.nodes.Decoration;\r
38 import org.simantics.scenegraph.g2d.nodes.DecorationSVGNode;\r
39 import org.simantics.scenegraph.g2d.nodes.SVGNode;\r
40 import org.simantics.scenegraph.profile.EvaluationContext;\r
41 import org.simantics.scenegraph.profile.common.ProfileVariables;\r
42 import org.simantics.scenegraph.utils.NodeUtil;\r
43 import org.simantics.utils.datastructures.map.Tuple;\r
44 \r
45 \r
46 /**\r
47  * @author Tuukka Lehtonen\r
48  */\r
49 public class IssueDecorationStyle extends StyleBase<IssueResult> {\r
50 \r
51     private static final String DECORATION_NODE_NAME = "issueDecorations";\r
52 \r
53     private List<Resource> getContexts(ReadGraph graph, Resource element) throws DatabaseException {\r
54 \r
55         ModelingResources MOD = ModelingResources.getInstance(graph);\r
56         List<Resource> result = new ArrayList<Resource>(3);\r
57         result.add(element);\r
58         Resource config = graph.getPossibleObject(element, MOD.ElementToComponent);\r
59         if (config != null && result.indexOf(config) == -1) result.add(config);\r
60         config = graph.getPossibleObject(element, MOD.DiagramConnectionToConnection);\r
61         if (config != null && result.indexOf(config) == -1) result.add(config);\r
62         // For diagram reference element support\r
63         config = graph.getPossibleObject(element, MOD.HasParentComponent);\r
64         if (config != null && result.indexOf(config) == -1) result.add(config);\r
65         return result;\r
66 \r
67     }\r
68 \r
69     @Override\r
70     public IssueResult calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException {\r
71 \r
72         Resource model = Variables.getModel(graph, configuration);\r
73         if (model == null)\r
74             return null;\r
75 \r
76         List<Resource> contexts = getContexts(graph, element);\r
77         AffineTransform transform = DiagramGraphUtil.getAffineTransform(graph, element);\r
78         Map<Severity, List<Resource>> issuesBySeverity = graph.syncRequest(new ListModelIssuesBySeverity(model, true, true, Severity.NOTE),\r
79                 TransientCacheListener.<Map<Severity, List<Resource>>>instance());\r
80 \r
81         for (Severity severity : Severity.values()) {\r
82             List<Resource> issues = issuesBySeverity.get(severity);\r
83             if (issues != null) {\r
84                 Set<Resource> issueContexts = graph.syncRequest(new IssueResourcesContexts(issues));\r
85                 if (!Collections.disjoint(issueContexts, contexts))\r
86                     return new IssueResult(severity, transform);\r
87             }\r
88         }\r
89 \r
90         return null;\r
91     }\r
92 \r
93     @Override\r
94     public void applyStyleForNode(EvaluationContext observer, INode node, IssueResult result) {\r
95         if (result == null) {\r
96             ProfileVariables.denyChild(node, "", DECORATION_NODE_NAME);\r
97             return;\r
98         }\r
99 \r
100         SVGNode svgNode = ProfileVariables.claimChild(node, "", DECORATION_NODE_NAME, DecorationSVGNode.class, observer);\r
101 \r
102         svgNode.setZIndex( Integer.MAX_VALUE );\r
103         svgNode.setTransform(getDecorationPosition(node)); \r
104 \r
105         String svgData = svgDataForSeverity(result.getSeverity());\r
106         if (svgData != null)\r
107             svgNode.setData(svgData);\r
108     }\r
109 \r
110     /**\r
111      * Returns position of the decoration.\r
112      * By default decoration is placed to the top left corner.  Override this method to change the position.\r
113      *  \r
114      * @param node\r
115      * @return\r
116      */\r
117     protected AffineTransform getDecorationPosition(INode node) {\r
118         Rectangle2D bounds = NodeUtil.getLocalBounds(node, Decoration.class);\r
119 \r
120         double tx = bounds.getX();\r
121         double ty = bounds.getY();\r
122         return AffineTransform.getTranslateInstance(tx, ty);\r
123     }\r
124 \r
125     protected String svgDataForSeverity(Severity s) {\r
126         switch (s) {\r
127         case FATAL: return Activator.FATAL_SVG_TEXT;\r
128         case ERROR: return Activator.ERROR_SVG_TEXT;\r
129         case WARNING: return Activator.WARNING_SVG_TEXT;\r
130         case INFO: return Activator.INFO_SVG_TEXT;\r
131         case NOTE: return Activator.NOTE_SVG_TEXT;\r
132         default: return null;\r
133         }\r
134     }\r
135 \r
136     @Override\r
137     protected void cleanupStyleForNode(INode node) {\r
138         ProfileVariables.denyChild(node, "", DECORATION_NODE_NAME);\r
139     }\r
140 \r
141     @Override\r
142     public String toString() {\r
143         return "Issue decoration";\r
144     }\r
145     \r
146     /**\r
147      * This is needed to keep the issue decoration up-to-date when its parent\r
148      * element moves.\r
149      */\r
150     public static class IssueResult extends Tuple {\r
151         public IssueResult(Severity severity, AffineTransform transform) {\r
152             super(severity, transform);\r
153         }\r
154         public Severity getSeverity() {\r
155             return (Severity) getField(0);\r
156         }\r
157     }\r
158 \r
159 }\r
160 \r
161 \r