]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictDiagramViewer.java
Prevent IndexRoot requests from crashing when network diagram is removed
[simantics/district.git] / org.simantics.district.network.ui / src / org / simantics / district / network / ui / DistrictDiagramViewer.java
1 package org.simantics.district.network.ui;
2
3 import java.awt.Color;
4 import java.awt.geom.AffineTransform;
5 import java.util.Collections;
6 import java.util.Map;
7 import java.util.Objects;
8 import java.util.concurrent.TimeUnit;
9 import java.util.function.Consumer;
10 import java.util.function.Supplier;
11
12 import org.simantics.datatypes.literal.RGB;
13 import org.simantics.db.ReadGraph;
14 import org.simantics.db.Resource;
15 import org.simantics.db.common.request.PossibleIndexRoot;
16 import org.simantics.db.common.request.UnaryRead;
17 import org.simantics.db.exception.DatabaseException;
18 import org.simantics.db.procedure.Listener;
19 import org.simantics.diagram.ui.DiagramModelHints;
20 import org.simantics.district.network.DistrictNetworkUtil;
21 import org.simantics.district.network.ontology.DistrictNetworkResource;
22 import org.simantics.district.network.ui.internal.Activator;
23 import org.simantics.district.network.ui.nodes.DistrictRenderingPreparationNode;
24 import org.simantics.district.network.ui.participants.DNPointerInteractor;
25 import org.simantics.district.network.ui.participants.DynamicVisualisationContributionsParticipant;
26 import org.simantics.district.network.ui.participants.ElevationServerParticipant;
27 import org.simantics.district.network.ui.participants.MapRulerPainter;
28 import org.simantics.district.network.visualisations.DynamicVisualisations;
29 import org.simantics.district.network.visualisations.model.ColorBarOptions;
30 import org.simantics.district.network.visualisations.model.DynamicColorContribution;
31 import org.simantics.district.network.visualisations.model.DynamicSizeContribution;
32 import org.simantics.district.network.visualisations.model.SizeBarOptions;
33 import org.simantics.g2d.canvas.ICanvasContext;
34 import org.simantics.g2d.canvas.impl.CanvasContext;
35 import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;
36 import org.simantics.g2d.diagram.participant.DelayedBatchElementPainter;
37 import org.simantics.g2d.diagram.participant.ElementPainter;
38 import org.simantics.g2d.diagram.participant.Selection;
39 import org.simantics.g2d.diagram.participant.ZOrderHandler;
40 import org.simantics.g2d.participant.BackgroundPainter;
41 import org.simantics.g2d.participant.GridPainter;
42 import org.simantics.g2d.participant.PanZoomRotateHandler;
43 import org.simantics.g2d.participant.RenderingQualityInteractor;
44 import org.simantics.g2d.participant.TransformUtil;
45 import org.simantics.g2d.participant.ZoomToAreaHandler;
46 import org.simantics.g2d.scenegraph.SceneGraphConstants;
47 import org.simantics.maps.MapScalingTransform;
48 import org.simantics.maps.eclipse.MapPainter;
49 import org.simantics.maps.sg.commands.MapCommands;
50 import org.simantics.modeling.ui.diagramEditor.DiagramViewer;
51 import org.simantics.scenegraph.g2d.G2DParentNode;
52 import org.simantics.scenegraph.g2d.events.command.Command;
53 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
54 import org.simantics.scenegraph.g2d.events.command.Commands;
55 import org.simantics.utils.datastructures.hints.IHintContext;
56 import org.simantics.utils.datastructures.hints.IHintContext.Key;
57 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 public class DistrictDiagramViewer extends DiagramViewer {
62
63         private static final Logger LOGGER = LoggerFactory.getLogger(DistrictDiagramViewer.class);
64
65     @Override
66     protected void addDiagramParticipants(ICanvasContext ctx) {
67         ctx.add(new ZOrderHandler());
68         ctx.add(new Selection());
69         ctx.add(new ElementPainter());
70         ctx.add(new DNPointerInteractor());
71         
72         AffineTransform tr = new AffineTransform(MapScalingTransform.INSTANCE);
73         ctx.add(new MapPainter(tr));
74         
75         DynamicVisualisationContributionsParticipant dynamicVisualisationContributionsParticipant = new DynamicVisualisationContributionsParticipant(tr);
76         ctx.add(new NetworkDrawingParticipant(dynamicVisualisationContributionsParticipant, tr));
77         ctx.add(new ElevationServerParticipant(tr));
78         ctx.add(dynamicVisualisationContributionsParticipant);
79         
80         // Optimize AffineTransform memory allocations during district diagram rendering
81         G2DParentNode spatialRoot = (G2DParentNode) ctx.getSceneGraph().lookupNode(SceneGraphConstants.SPATIAL_ROOT_NODE_ID);
82         DistrictRenderingPreparationNode prepNode = new DistrictRenderingPreparationNode();
83         prepNode.setZIndex(Integer.MIN_VALUE / 2);
84         spatialRoot.addNode("districtRenderingPrepareNode", prepNode);
85     }
86     
87     protected String getPopupId() {
88         return "#DistrictDiagramPopup";
89     }
90
91     @Override
92     protected void fillInitialDiagramHints(Resource diagram, IHintContext initialHints) throws DatabaseException {
93         super.fillInitialDiagramHints(diagram, initialHints);
94         
95     }
96
97     @Override
98     public void initializeCanvasContext(CanvasContext ctx) {
99         super.initializeCanvasContext(ctx);
100         IHintContext h = ctx.getDefaultHintContext();
101         h.setHint(PanZoomRotateHandler.KEY_ZOOM_IN_LIMIT, 10000.0);
102         h.setHint(PanZoomRotateHandler.KEY_ZOOM_OUT_LIMIT, 0.01);
103         h.setHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE, diagramResource);
104     }
105
106     @Override
107     protected void addPainterParticipants(ICanvasContext ctx) {
108         ctx.add(new RenderingQualityInteractor());
109         ctx.add(new DelayedBatchElementPainter(PickFilter.FILTER_MONITORS, 500, TimeUnit.MILLISECONDS));
110     }
111     
112     @Override
113     protected void addGridRulerBackgroundParticipants(CanvasContext ctx) {
114         ctx.add(new GridPainter());
115         ctx.add(new MapRulerPainter());
116         ctx.add(new BackgroundPainter());
117     }
118     
119     protected void addViewManipulationParticipants(CanvasContext ctx) {
120         // Let's replace with our special util
121         TransformUtil util = ctx.getAtMostOneItemOfClass(TransformUtil.class);
122         if (util != null)
123             ctx.remove(util);
124         ctx.add(new DistrictTransformUtil());
125         ctx.add(new DistrictPanZoomRotateHandler());
126         //ctx.add(new MousePanZoomInteractor());
127         //ctx.add(new MultitouchPanZoomRotateInteractor());
128         // ctx.add( new OrientationRestorer() );
129         ctx.add(new ZoomToAreaHandler());
130     }
131
132     @Override
133     protected void loadPageSettings(ICanvasContext ctx) {
134         super.loadPageSettings(ctx);
135         // this might be the wrong place to start such listening but at least
136         // super.loadPageSettings() does async-db-operations
137         setupDrawMapEnabled();
138         setupBackgroundColor();
139         setupColoringObjects();
140         setupColorBarOptions();
141         setupSizingObjects();
142         setupSizeBarOptions();
143         
144         // add listeners
145         DistrictDiagramViewerListener[] listeners = Activator.getInstance().getDistrictDiagramViewerListeners();
146         if (listeners != null) {
147             for (DistrictDiagramViewerListener listener : listeners) {
148                 listener.diagramLoaded(getRuntime(), canvasContext);
149             }
150         }
151     }
152     
153     @Override
154     public void dispose() {
155         DistrictDiagramViewerListener[] listeners = Activator.getInstance().getDistrictDiagramViewerListeners();
156         if (listeners != null) {
157             Resource runtime = getRuntime();
158             for (DistrictDiagramViewerListener listener : listeners) {
159                 listener.diagramDisposed(runtime, canvasContext);
160             }
161         }
162         super.dispose();
163     }
164     
165     private void setupDrawMapEnabled() {
166         sessionContext.getSession().asyncRequest(new DrawMapEnabledRequest(getInputResource()), new DrawMapEnabledListener(
167                 result -> canvasContext.getEventQueue().queueEvent(new CommandEvent(canvasContext, System.currentTimeMillis(), result ? Commands.MAP_ENABLE : Commands.MAP_DISABLE)),
168                 () -> DistrictDiagramViewer.this.isDisposed()));
169     }
170
171     private void setupBackgroundColor() {
172         sessionContext.getSession().asyncRequest(new MapBackgroundColorRequest(getInputResource()), new MapBackgroundColorListener(
173                 result -> queueBackgroundColorChangeEvent(result),
174                 () -> DistrictDiagramViewer.this.isDisposed()));
175     }
176
177     private void queueBackgroundColorChangeEvent(RGB.Integer result) {
178         if (result != null) {
179             Color backgroundColor = new Color(result.red, result.green, result.blue);
180             canvasContext.getDefaultHintContext().setHint(MapCommands.KEY_MAP_BACKGROUND_COLOR, backgroundColor);
181             canvasContext.getEventQueue().queueEvent(new CommandEvent(canvasContext, System.currentTimeMillis(), MapCommands.MAP_BACKGROUND_COLOR_CHANGE));
182         }
183     }
184
185     private void setupColoringObjects() {
186         sessionContext.getSession().asyncRequest(new ColoringObjectsRequest(getInputResource()), new ColoringObjectsListener(
187                 result -> queueColoringObjectsChangeEvent(result),
188                 () -> DistrictDiagramViewer.this.isDisposed()));
189     }
190
191     
192     private void setupColorBarOptions() {
193         sessionContext.getSession().asyncRequest(new ColorBarOptionsRequest(getInputResource()), new ColorBarOptionsListener(
194                 result -> queueColorBarOptionsChangeEvent(result),
195                 () -> DistrictDiagramViewer.this.isDisposed()));
196     }
197
198     private void setupSizingObjects() {
199         sessionContext.getSession().asyncRequest(new SizingObjectsRequest(getInputResource()), new SizingObjectsListener(
200                 result -> queueSizingObjectsChangeEvent(result),
201                 () -> DistrictDiagramViewer.this.isDisposed()));
202     }
203
204     
205     private void setupSizeBarOptions() {
206         sessionContext.getSession().asyncRequest(new SizeBarOptionsRequest(getInputResource()), new SizeBarOptionsListener(
207                 result -> queueSizeBarOptionsChangeEvent(result),
208                 () -> DistrictDiagramViewer.this.isDisposed()));
209     }
210     
211     public static final Key KEY_MAP_COLOR_BAR_OPTIONS = new KeyOf(ColorBarOptions.class, "colorBarOptions");
212     public static final Command MAP_COLOR_BAR_OPTIONS_CHANGE = new Command("colorBarOptionsChange");
213     public static final Key KEY_MAP_SIZE_BAR_OPTIONS = new KeyOf(SizeBarOptions.class, "sizeBarOptions");
214     public static final Command MAP_SIZE_BAR_OPTIONS_CHANGE = new Command("sizeBarOptionsChange");
215     
216     public static final Key KEY_MAP_COLORING_OBJECTS = new KeyOf(Map.class, "coloringObjects");
217     public static final Command MAP_COLORING_OBJECTS_CHANGE = new Command("coloringObjectsChange");
218     
219     public static final Key KEY_MAP_SIZING_OBJECTS = new KeyOf(Map.class, "sizingObjects");
220     public static final Command MAP_SIZING_OBJECTS_CHANGE = new Command("sizingObjectsChange");
221
222     
223     private void queueColoringObjectsChangeEvent(Map<String, DynamicColorContribution> result) {
224         queueEventInternal(KEY_MAP_COLORING_OBJECTS, MAP_COLORING_OBJECTS_CHANGE, result);
225     }
226     
227     private void queueColorBarOptionsChangeEvent(ColorBarOptions result) {
228         queueEventInternal(KEY_MAP_COLOR_BAR_OPTIONS, MAP_COLOR_BAR_OPTIONS_CHANGE, result);
229     }
230
231     private void queueSizingObjectsChangeEvent(Map<String, DynamicSizeContribution> result) {
232         queueEventInternal(KEY_MAP_SIZING_OBJECTS, MAP_SIZING_OBJECTS_CHANGE, result);
233     }
234     
235     private void queueSizeBarOptionsChangeEvent(SizeBarOptions result) {
236         queueEventInternal(KEY_MAP_SIZE_BAR_OPTIONS, MAP_SIZE_BAR_OPTIONS_CHANGE, result);
237     }
238
239     private void queueEventInternal(Key key, Command command, Object result) {
240         if (result != null && !canvasContext.isDisposed()) {
241             canvasContext.getThreadAccess().asyncExec(() -> {
242                 canvasContext.getDefaultHintContext().setHint(key, result);
243                 canvasContext.getEventQueue().queueEvent(new CommandEvent(canvasContext, System.currentTimeMillis(), command));
244             });
245         } else {
246             LOGGER.info("Result is either null or canvasContext is disposed", String.valueOf(result));
247         }
248     }
249
250     private static class DrawMapEnabledRequest extends UnaryRead<Resource, Boolean> {
251
252         public DrawMapEnabledRequest(Resource diagram) {
253             super(diagram);
254         }
255
256         @Override
257         public Boolean perform(ReadGraph graph) throws DatabaseException {
258             return DistrictNetworkUtil.drawMapEnabled(graph, parameter);
259         }
260     }
261
262     private static class DrawMapEnabledListener implements Listener<Boolean> {
263
264         private static final Logger LOGGER = LoggerFactory.getLogger(DrawMapEnabledListener.class);
265
266         private Consumer<Boolean> callback;
267         private Supplier<Boolean> isDisposed;
268         
269         private Boolean lastResult;
270
271         public DrawMapEnabledListener(Consumer<Boolean> callback, Supplier<Boolean> isDisposed) {
272             this.callback = callback;
273             this.isDisposed = isDisposed;
274         }
275
276         @Override
277         public void execute(Boolean result) {
278             // Minor optimization
279             if (!Objects.equals(lastResult, result)) {
280                 lastResult = result;
281                 callback.accept(result);
282             }
283         }
284
285         @Override
286         public void exception(Throwable t) {
287             LOGGER.error("Could not listen if draw map is enabled", t);
288         }
289
290         @Override
291         public boolean isDisposed() {
292             return isDisposed.get();
293         }
294     }
295     
296     private static class MapBackgroundColorRequest extends UnaryRead<Resource, RGB.Integer> {
297
298         public MapBackgroundColorRequest(Resource diagram) {
299             super(diagram);
300         }
301
302         @Override
303         public RGB.Integer perform(ReadGraph graph) throws DatabaseException {
304             return DistrictNetworkUtil.backgroundColor(graph, parameter);
305         }
306     }
307
308     private static class MapBackgroundColorListener implements Listener<RGB.Integer> {
309
310         private static final Logger LOGGER = LoggerFactory.getLogger(MapBackgroundColorListener.class);
311
312         private Consumer<RGB.Integer> callback;
313         private Supplier<Boolean> isDisposed;
314         
315         private RGB.Integer lastResult;
316
317         public MapBackgroundColorListener(Consumer<RGB.Integer> callback, Supplier<Boolean> isDisposed) {
318             this.callback = callback;
319             this.isDisposed = isDisposed;
320         }
321
322         @Override
323         public void execute(RGB.Integer result) {
324             if (!Objects.equals(lastResult, result)) {
325                 lastResult = result;
326                 callback.accept(result);
327             }
328         }
329
330         @Override
331         public void exception(Throwable t) {
332             LOGGER.error("Could not listen map background color", t);
333         }
334
335         @Override
336         public boolean isDisposed() {
337             return isDisposed.get();
338         }
339     }
340
341     private static class ColorBarOptionsRequest extends UnaryRead<Resource, ColorBarOptions> {
342
343         public ColorBarOptionsRequest(Resource diagram) {
344             super(diagram);
345         }
346
347         @Override
348         public ColorBarOptions perform(ReadGraph graph) throws DatabaseException {
349             DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
350             Resource model = graph.syncRequest(new PossibleIndexRoot(parameter));
351             if (model != null) {
352                 Resource vf = DynamicVisualisations.getVisualisationFolder(graph, model);
353                 if (vf != null) {
354                     Resource activeVisualisation = graph.getPossibleObject(vf, DN.Diagram_hasActiveVisualisation);
355                     if (activeVisualisation != null) {
356                         return DynamicVisualisations.colorBarOptions(graph, activeVisualisation);
357                     }
358                 } else {
359                     LOGGER.debug("No visualisation folder available for model {}", model);
360                 }
361             }
362             return ColorBarOptions.useDefault();
363         }
364     }
365
366     private static class ColoringObjectsRequest extends UnaryRead<Resource, Map<String,DynamicColorContribution>> {
367
368         public ColoringObjectsRequest(Resource diagram) {
369             super(diagram);
370         }
371
372         @Override
373         public Map<String, DynamicColorContribution> perform(ReadGraph graph) throws DatabaseException {
374             DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
375             Resource model = graph.syncRequest(new PossibleIndexRoot(parameter));
376             if (model != null) {
377                 Resource vf = DynamicVisualisations.getVisualisationFolder(graph, model);
378                 if (vf != null) {
379                     Resource activeVisualisation = graph.getPossibleObject(vf, DN.Diagram_hasActiveVisualisation);
380                     if (activeVisualisation != null) {
381                         return DynamicVisualisations.colorContributions(graph, activeVisualisation);
382                     }
383                 } else {
384                     LOGGER.debug("No visualisation folder available for model {}", model);
385                 }
386             }
387             return Collections.emptyMap();
388         }
389     }
390     
391     private static class ColoringObjectsListener implements Listener<Map<String,DynamicColorContribution>> {
392
393         private static final Logger LOGGER = LoggerFactory.getLogger(ColoringObjectsListener.class);
394
395         private Consumer<Map<String,DynamicColorContribution>> callback;
396         private Supplier<Boolean> isDisposed;
397         
398         //private Map<String, DynamicColorContribution> lastResult
399
400         public ColoringObjectsListener(Consumer<Map<String,DynamicColorContribution>> callback, Supplier<Boolean> isDisposed) {
401             this.callback = callback;
402             this.isDisposed = isDisposed;
403         }
404
405         @Override
406         public void execute(Map<String,DynamicColorContribution> result) {
407             callback.accept(result);
408         }
409
410         @Override
411         public void exception(Throwable t) {
412             LOGGER.error("Could not listen ColorBarOptions", t);
413         }
414
415         @Override
416         public boolean isDisposed() {
417             return isDisposed.get();
418         }
419     }
420     
421     private static class ColorBarOptionsListener implements Listener<ColorBarOptions> {
422
423         private static final Logger LOGGER = LoggerFactory.getLogger(ColorBarOptionsListener.class);
424
425         private Consumer<ColorBarOptions> callback;
426         private Supplier<Boolean> isDisposed;
427
428         public ColorBarOptionsListener(Consumer<ColorBarOptions> callback, Supplier<Boolean> isDisposed) {
429             this.callback = callback;
430             this.isDisposed = isDisposed;
431         }
432
433         @Override
434         public void execute(ColorBarOptions result) {
435             callback.accept(result);
436         }
437
438         @Override
439         public void exception(Throwable t) {
440             LOGGER.error("Could not listen ColorBarOptions", t);
441         }
442
443         @Override
444         public boolean isDisposed() {
445             return isDisposed.get();
446         }
447     }
448     
449     private static class SizeBarOptionsRequest extends UnaryRead<Resource, SizeBarOptions> {
450
451         public SizeBarOptionsRequest(Resource diagram) {
452             super(diagram);
453         }
454
455         @Override
456         public SizeBarOptions perform(ReadGraph graph) throws DatabaseException {
457             DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
458             Resource model = graph.syncRequest(new PossibleIndexRoot(parameter));
459             if (model != null) {
460                 Resource vf = DynamicVisualisations.getVisualisationFolder(graph, model);
461                 if (vf != null) {
462                     Resource activeVisualisation = graph.getPossibleObject(vf, DN.Diagram_hasActiveVisualisation);
463                     if (activeVisualisation != null) {
464                         return DynamicVisualisations.sizeBarOptions(graph, activeVisualisation);
465                     }
466                 } else {
467                     LOGGER.debug("No visualisation folder available for model {}", model);
468                 }
469             }
470             return SizeBarOptions.useDefault();
471         }
472     }
473
474     private static class SizeBarOptionsListener implements Listener<SizeBarOptions> {
475
476         private static final Logger LOGGER = LoggerFactory.getLogger(SizeBarOptionsListener.class);
477
478         private Consumer<SizeBarOptions> callback;
479         private Supplier<Boolean> isDisposed;
480
481         public SizeBarOptionsListener(Consumer<SizeBarOptions> callback, Supplier<Boolean> isDisposed) {
482             this.callback = callback;
483             this.isDisposed = isDisposed;
484         }
485
486         @Override
487         public void execute(SizeBarOptions result) {
488             callback.accept(result);
489         }
490
491         @Override
492         public void exception(Throwable t) {
493             LOGGER.error("Could not listen SizeBarOptions", t);
494         }
495
496         @Override
497         public boolean isDisposed() {
498             return isDisposed.get();
499         }
500     }
501     
502     private static class SizingObjectsRequest extends UnaryRead<Resource, Map<String, DynamicSizeContribution>> {
503
504         public SizingObjectsRequest(Resource diagram) {
505             super(diagram);
506         }
507
508         @Override
509         public Map<String, DynamicSizeContribution> perform(ReadGraph graph) throws DatabaseException {
510             DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
511             Resource model = graph.syncRequest(new PossibleIndexRoot(parameter));
512             if (model != null) {
513                 Resource vf = DynamicVisualisations.getVisualisationFolder(graph, model);
514                 if (vf != null) {
515                     Resource activeVisualisation = graph.getPossibleObject(vf, DN.Diagram_hasActiveVisualisation);
516                     if (activeVisualisation != null) {
517                         return DynamicVisualisations.sizeContributions(graph, activeVisualisation);
518                     }
519                 } else {
520                     LOGGER.debug("No visualisation folder available for model {}", model);
521                 }
522             }
523             return Collections.emptyMap();
524         }
525     }
526     
527     private static class SizingObjectsListener implements Listener<Map<String,DynamicSizeContribution>> {
528
529         private static final Logger LOGGER = LoggerFactory.getLogger(SizingObjectsListener.class);
530
531         private Consumer<Map<String,DynamicSizeContribution>> callback;
532         private Supplier<Boolean> isDisposed;
533
534         public SizingObjectsListener(Consumer<Map<String, DynamicSizeContribution>> callback, Supplier<Boolean> isDisposed) {
535             this.callback = callback;
536             this.isDisposed = isDisposed;
537         }
538
539         @Override
540         public void execute(Map<String, DynamicSizeContribution> result) {
541             callback.accept(result);
542         }
543
544         @Override
545         public void exception(Throwable t) {
546             LOGGER.error("Could not listen ColorBarOptions", t);
547         }
548
549         @Override
550         public boolean isDisposed() {
551             return isDisposed.get();
552         }
553     }
554 }