]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/participant/DiagramModelActivityTracker.java
HiDPI scaling for diagram ruler and model activity tracker
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / participant / DiagramModelActivityTracker.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2018 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.diagram.participant;
13
14 import java.awt.Color;
15 import java.awt.Font;
16 import java.awt.geom.AffineTransform;
17 import java.awt.geom.Rectangle2D;
18
19 import org.simantics.Simantics;
20 import org.simantics.db.ReadGraph;
21 import org.simantics.db.Resource;
22 import org.simantics.db.common.request.PossibleIndexRoot;
23 import org.simantics.db.common.request.UnaryRead;
24 import org.simantics.db.exception.DatabaseException;
25 import org.simantics.db.layer0.adapter.Instances;
26 import org.simantics.db.layer0.request.IsActive;
27 import org.simantics.db.layer0.util.Layer0Utils;
28 import org.simantics.db.procedure.Listener;
29 import org.simantics.diagram.elements.TextNode;
30 import org.simantics.diagram.stubs.DiagramResource;
31 import org.simantics.g2d.canvas.Hints;
32 import org.simantics.g2d.canvas.ICanvasContext;
33 import org.simantics.g2d.canvas.SGDesignation;
34 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
35 import org.simantics.g2d.canvas.impl.HintReflection.HintListener;
36 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;
37 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;
38 import org.simantics.g2d.participant.RulerPainter;
39 import org.simantics.g2d.utils.Alignment;
40 import org.simantics.layer0.Layer0;
41 import org.simantics.modeling.ModelingResources;
42 import org.simantics.scenegraph.g2d.G2DParentNode;
43 import org.simantics.scenegraph.utils.DPIUtil;
44 import org.simantics.structural.stubs.StructuralResource2;
45 import org.simantics.utils.datastructures.hints.IHintContext;
46 import org.simantics.utils.datastructures.hints.IHintContext.Key;
47 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
48 import org.simantics.utils.datastructures.hints.IHintObservable;
49 import org.simantics.utils.ui.ErrorLogger;
50
51 /**
52  * DiagramModelActivityTracker tracks whether or not the specified input
53  * resource is part of the diagram model or not and modifies the canvas
54  * background color accordingly. If the diagram is not in an active model, an
55  * <b>INACTIVE MODEL</b> banner is also shown in the background using
56  * {@link TextNode}.
57  * 
58  * @author Tuukka Lehtonen
59  */
60 public class DiagramModelActivityTracker extends AbstractCanvasParticipant {
61
62     public static final Key    KEY_IN_ACTIVE_MODEL         = new KeyOf(String.class, "IN_ACTIVE_MODEL");
63     public static final Key    KEY_ACTIVE_BACKGROUND       = new KeyOf(Color.class, "ACTIVE_BACKGROUND");
64     public static final Key    KEY_INACTIVE_BACKGROUND     = new KeyOf(Color.class, "INACTIVE_BACKGROUND");
65
66     private static final Color DEFAULT_ACTIVE_BACKGROUND   = Color.WHITE;
67     private static final Color DEFAULT_INACTIVE_BACKGROUND = new Color(242, 242, 242);
68
69     Resource                   input;
70     IsInActiveModelListener    listener;
71     TextNode                   bannerNode;
72     Rectangle2D                bounds;
73     boolean                    rulerVisible = false;
74     double                     rulerSize = 0;
75
76     public DiagramModelActivityTracker(Resource input) {
77         if (input == null)
78             throw new NullPointerException("null input");
79         this.input = input;
80     }
81
82     @HintListener(Class=Hints.class, Field="KEY_CONTROL_BOUNDS")
83     public void controlBoundsChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
84         bounds = (Rectangle2D) newValue;
85         resetBannerNode(bounds, rulerVisible, rulerSize);
86     }
87
88     @HintListener(Class=RulerPainter.class, Field="KEY_RULER_ENABLED")
89     public void rulerToggled(IHintObservable sender, Key key, Object oldValue, Object newValue) {
90         rulerVisible = Boolean.TRUE.equals(newValue);
91         resetBannerNode(bounds, rulerVisible, rulerSize);
92     }
93
94     @HintListener(Class=RulerPainter.class, Field="KEY_RULER_SIZE")
95     public void rulerSizeChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
96         rulerSize = newValue != null ? (Double) newValue : 0.0;
97         resetBannerNode(bounds, rulerVisible, rulerSize);
98     }
99
100     private void resetBannerNode(Rectangle2D bounds, boolean rulerVisible, double rulerSize) {
101         if (bannerNode == null)
102             return;
103         if (bounds != null) {
104             AffineTransform at = new AffineTransform();
105             at.translate(5,5);
106             if (rulerVisible) {
107                 double s = DPIUtil.upscale(rulerSize);
108                 at.translate(s, s);
109             }
110             bannerNode.setTransform(at);
111             bannerNode.setColor(Color.BLACK);
112         } else {
113             // Disable rendering
114             bannerNode.setColor(null);
115         }
116     }
117
118     @HintListener(Class=DiagramModelActivityTracker.class, Field="KEY_IN_ACTIVE_MODEL")
119     public void containingModelActivityChanged(IHintObservable sender, Key key, Object oldValue, Object _newValue) {
120         String newValue = (String)_newValue;
121         if (bannerNode == null)
122             return;
123         if (newValue == null) {
124                 bannerNode.setText("");
125         } else {
126                 bannerNode.setText(newValue);
127             //bannerNode.setColor(new Color(192, 32, 32, 128));
128             //bannerNode.setColor(new Color(192, 32, 32, 32));
129         }
130     }
131
132     @Override
133     public void addedToContext(ICanvasContext ctx) {
134         super.addedToContext(ctx);
135
136         rulerVisible = Boolean.TRUE.equals(getHint(RulerPainter.KEY_RULER_ENABLED));
137         Double rs = getHint(RulerPainter.KEY_RULER_SIZE);
138         rulerSize = rs != null ? rs : 0;
139
140         listener = new IsInActiveModelListener(ctx);
141         Simantics.getSession().async(new IsActiveDiagram(input), listener);
142
143         if (!ctx.getHintStack().containsHint(KEY_ACTIVE_BACKGROUND))
144             ctx.getDefaultHintContext().setHint(KEY_ACTIVE_BACKGROUND, DEFAULT_ACTIVE_BACKGROUND);
145         if (!ctx.getHintStack().containsHint(KEY_INACTIVE_BACKGROUND))
146             ctx.getDefaultHintContext().setHint(KEY_INACTIVE_BACKGROUND, DEFAULT_INACTIVE_BACKGROUND);
147     }
148
149     @Override
150     public void removedFromContext(ICanvasContext ctx) {
151         if (listener != null) {
152             listener.dispose();
153             listener = null;
154         }
155         super.removedFromContext(ctx);
156     }
157
158     @SGInit(designation = SGDesignation.CONTROL)
159     public void initSG(G2DParentNode parent) {
160         bannerNode = parent.addNode("inactivity-banner", TextNode.class);
161         bannerNode.setZIndex(Integer.MIN_VALUE / 4);
162         //bannerNode.setZIndex(Integer.MAX_VALUE / 4);
163         bannerNode.setHorizontalAlignment((byte) Alignment.LEADING.ordinal());
164         bannerNode.setVerticalAlignment((byte) Alignment.LEADING.ordinal());
165         //bannerNode.setText("Model is not active.");
166         bannerNode.setText("");
167         int fontSize = DPIUtil.upscale(16);
168         bannerNode.setFont(new Font("Tahoma", Font.PLAIN, fontSize));
169         //bannerNode.setBackgroundColor(new Color(192, 192, 192, 128));
170         bannerNode.setPadding(10, 10);
171         bannerNode.setBorderColor(Color.GRAY);
172         bannerNode.setBorderWidth(0);
173         bannerNode.setEditable(false);
174         bannerNode.setColor(Color.BLACK);
175     }
176
177     @SGCleanup
178     public void cleanupSG() {
179         if (bannerNode != null) {
180             bannerNode.remove();
181             bannerNode = null;
182         }
183     }
184
185     static class IsActiveDiagram extends UnaryRead<Resource, String> {
186         public IsActiveDiagram(Resource parameter) {
187             super(parameter);
188         }
189         @Override
190         public String perform(ReadGraph graph) throws DatabaseException {
191                 
192             Resource indexRoot = graph.syncRequest( new PossibleIndexRoot(parameter) );
193             if (indexRoot == null) {
194                 return "This diagram is not part of any model or shared library";
195             }
196                 Layer0 L0 = Layer0.getInstance(graph);
197                 StructuralResource2 STR = StructuralResource2.getInstance(graph);
198                 ModelingResources MOD = ModelingResources.getInstance(graph);
199                 Resource composite = graph.getPossibleObject(parameter, MOD.DiagramToComposite);
200                 if(composite == null)
201                 return "This diagram does not have a corresponding configuration";
202                 
203                 Resource componentType = graph.getPossibleObject(composite, STR.Defines);
204                 if(componentType != null) {
205                         // This is a component type
206                         boolean published = Layer0Utils.isPublished(graph, componentType);
207                         if(published)
208                     return "This diagram defines a published (read-only) user component";
209                 }
210                 
211                 if(graph.isInstanceOf(indexRoot, L0.SharedOntology)) {
212                         return "";
213                 }
214                 
215                 boolean activeModel = graph.syncRequest( new IsActive(indexRoot) );
216                 if(!activeModel)
217                 return "The model of this diagram is not active";
218                 
219                 DiagramResource DIA = DiagramResource.getInstance(graph);
220                 Instances query = graph.adapt(DIA.DiagramActivityCondition, Instances.class);
221
222                 for(Resource condition : query.find(graph, indexRoot)) {
223                 String value = Simantics.tryInvokeSCL(graph, condition, DIA.DiagramActivityCondition_test, parameter);
224                 if(value != null && !value.isEmpty()) return value;
225                 }
226                 
227             return "";
228             
229         }
230     }
231
232     static class IsInActiveModelListener implements Listener<String> {
233         ICanvasContext context;
234         public IsInActiveModelListener(ICanvasContext context) {
235             this.context = context;
236         }
237         @Override
238         public void execute(final String result) {
239             final Color color = (result.isEmpty())
240                     ? context.getHintStack().<Color>getHint(KEY_ACTIVE_BACKGROUND)
241                     : context.getHintStack().<Color>getHint(KEY_INACTIVE_BACKGROUND);
242
243             context.getThreadAccess().asyncExec(new Runnable() {
244                 @Override
245                 public void run() {
246                     ICanvasContext ctx = context;
247                     if (ctx == null)
248                         return;
249                     if (ctx.isDisposed())
250                         return;
251                     IHintContext hints = ctx.getDefaultHintContext();
252                     if (color != null)
253                         hints.setHint(Hints.KEY_BACKGROUND_COLOR, color);
254                     hints.setHint(KEY_IN_ACTIVE_MODEL, result);
255                 }
256             });
257         }
258         public void dispose() {
259             context = null;
260         }
261         @Override
262         public boolean isDisposed() {
263             ICanvasContext context = this.context;
264             return context == null || context.isDisposed();
265         }
266         @Override
267         public void exception(Throwable t) {
268             ErrorLogger.defaultLogError(t);
269         }
270     }
271
272 }