]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.maps/src/org/simantics/maps/eclipse/MapPainter.java
ef88e1c55ea9fdf266506ffbcd82c41b6a75a954
[simantics/district.git] / org.simantics.district.maps / src / org / simantics / maps / eclipse / MapPainter.java
1 /*******************************************************************************
2  * Copyright (c) 2012 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.maps.eclipse;
13
14 import java.awt.Color;
15 import java.awt.geom.AffineTransform;
16 import java.util.concurrent.ScheduledFuture;
17 import java.util.concurrent.TimeUnit;
18
19 import org.simantics.g2d.canvas.Hints;
20 import org.simantics.g2d.canvas.ICanvasContext;
21 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
22 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;
23 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;
24 import org.simantics.g2d.participant.MouseUtil;
25 import org.simantics.maps.sg.MapAttributionNode;
26 import org.simantics.maps.sg.MapLocationZoomInfoNode;
27 import org.simantics.maps.sg.MapNode;
28 import org.simantics.maps.sg.MapScaleNode;
29 import org.simantics.maps.sg.commands.MapCommands;
30 import org.simantics.scenegraph.g2d.G2DParentNode;
31 import org.simantics.scenegraph.g2d.events.Event;
32 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
33 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
34 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
35 import org.simantics.scenegraph.g2d.events.command.Commands;
36 import org.simantics.utils.datastructures.hints.HintListenerAdapter;
37 import org.simantics.utils.datastructures.hints.IHintContext.Key;
38 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
39 import org.simantics.utils.datastructures.hints.IHintListener;
40 import org.simantics.utils.datastructures.hints.IHintObservable;
41 import org.simantics.utils.threads.AWTThread;
42 import org.simantics.utils.threads.ThreadUtils;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 /**
47  * MapPainter is an ICanvasContext participant that uses the scene graph
48  * {@link MapNode} to draw tiled maps in the background of the canvas.
49  * 
50  * @author J-P Laine
51  * 
52  * @see MapNode
53  */
54 public class MapPainter extends AbstractCanvasParticipant {
55
56     private static final Logger LOGGER = LoggerFactory.getLogger(MapPainter.class);
57
58     /**
59      * Grid enabled status. Default value is True
60      */
61     public static final Key KEY_MAP_ENABLED  = new KeyOf(Boolean.class);
62
63     public static final Key KEY_MAP_BACKGROUND_COLOR  = new KeyOf(Object.class);
64
65     public static final double ZOOM_IN_LIMIT = 10000000.0;
66
67     public static final double ZOOM_OUT_LIMIT = 10.0;
68
69     IHintListener mapListener = new HintListenerAdapter() {
70         public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
71             ICanvasContext cc = getContext();
72             if (cc != null) {
73                 updateNode();
74                 cc.getContentContext().setDirty();
75             }
76         }
77     };
78
79     protected MapNode node = null;
80     protected MapScaleNode scaleNode = null;
81
82     private AffineTransform transform;
83
84     private MapLocationZoomInfoNode locationZoomInfoNode;
85
86     private ScheduledFuture<?> schedule;
87
88     public MapPainter(AffineTransform transform) {
89         this.transform = transform;
90     }
91
92     @Override
93     public void addedToContext(ICanvasContext ctx) {
94         super.addedToContext(ctx);
95         getHintStack().addKeyHintListener(getThread(), KEY_MAP_ENABLED, mapListener);
96         getHintStack().addKeyHintListener(getThread(), KEY_MAP_BACKGROUND_COLOR, mapListener);
97     }
98
99     @Override
100     public void removedFromContext(ICanvasContext ctx) {
101         getHintStack().removeKeyHintListener(getThread(), KEY_MAP_ENABLED, mapListener);
102         getHintStack().removeKeyHintListener(getThread(), KEY_MAP_BACKGROUND_COLOR, mapListener);
103         super.removedFromContext(ctx);
104     }
105
106     @EventHandler(priority = 0)
107     public boolean handleKeyEvent(CommandEvent e) {
108         if (e.command.equals( Commands.MAP_ENABLE )) {
109             setEnabled(true);
110             updateNode();
111             setDirty();
112             return true;
113         } else if (e.command.equals( MapCommands.MAP_BACKGROUND_COLOR_CHANGE )) {
114             ICanvasContext context = (ICanvasContext) e.getContext();
115             Color s = context.getHintStack().getHint(MapCommands.KEY_MAP_BACKGROUND_COLOR);
116             setBackgroundColor(s);
117             setDirty();
118             return true;
119         } else if (e.command.equals( Commands.MAP_DISABLE )) {
120             setEnabled(false);
121             updateNode();
122             setDirty();
123             return true;
124         } else if (e.command.equals( Commands.MAP_TOGGLE )) {
125             setEnabled(!isMapEnabled());
126             updateNode();
127             setDirty();
128             return true;
129         } else if (e.command.equals( Commands.ENABLE_PAINTING )) {
130             enablePainting();
131             updateNode();
132             setDirty();
133             return true;
134         }
135         return false;
136     }
137
138     @EventHandler(priority = 31)
139     public boolean handleEvent(Event e) {
140         if (e instanceof MouseMovedEvent) {
141             // here we should somehow re-render ?
142             if (locationZoomInfoNode.isEnabled()) {
143                 if (schedule == null || schedule.isDone()) {
144                     LOGGER.debug("current setDirty time" + System.currentTimeMillis());
145                     schedule = ThreadUtils.getNonBlockingWorkExecutor().schedule(() -> {
146                         AWTThread.getThreadAccess().asyncExec(this::setDirty);
147                     }, 100, TimeUnit.MILLISECONDS);
148                 } else {
149                     //LOGGER.debug("ingoring setDirty time" + System.currentTimeMillis());
150                 }
151             }
152         }
153         return false;
154     }
155
156     @SGInit
157     public void initSG(G2DParentNode parent) {
158         node = parent.addNode("map", MapNode.class);
159         node.setTransform(transform);
160         node.setEnabled(true);
161         node.setZIndex(Integer.MIN_VALUE + 999); // Just under the grid
162         
163         scaleNode = parent.addNode("mapScale", MapScaleNode.class);
164         scaleNode.setTransform(transform);
165         scaleNode.setEnabled(true);
166         scaleNode.setZIndex(Integer.MAX_VALUE - 999); // Just under the grid
167         
168         locationZoomInfoNode = parent.addNode("locationZoomInfo", MapLocationZoomInfoNode.class);
169         locationZoomInfoNode.setTransform(transform);
170         locationZoomInfoNode.setEnabled(true);
171         MouseUtil mouseUtil = getContext().getAtMostOneItemOfClass(MouseUtil.class);
172         locationZoomInfoNode.setMouseUtil(mouseUtil);
173         locationZoomInfoNode.setZIndex(Integer.MAX_VALUE - 999); // Just under the grid
174         
175         MapAttributionNode addNode = parent.addNode("mapAttribution", MapAttributionNode.class);
176         addNode.setTransform(transform);
177         addNode.setEnabled(true);
178         addNode.setZIndex(Integer.MAX_VALUE - 999);
179     }
180
181     @SGCleanup
182     public void cleanupSG() {
183         node.remove();
184     }
185
186     protected void updateNode() {
187         node.setEnabled(isPaintingEnabled());
188         node.setEnabled(isMapEnabled());
189         node.setBackgroundColor(getBackgroundColor());
190     }
191
192     boolean isPaintingEnabled() {
193         boolean enabled = isMapEnabled();
194         Boolean globalDisable = getHint(Hints.KEY_DISABLE_PAINTING);
195         return enabled && !Boolean.TRUE.equals(globalDisable);
196     }
197
198     public boolean isMapEnabled() {
199         Boolean enabled = getHint(KEY_MAP_ENABLED);
200         return !Boolean.FALSE.equals(enabled);
201     }
202
203     public void setEnabled(boolean enabled) {
204         setHint(KEY_MAP_ENABLED, enabled);
205     }
206
207     private void enablePainting() {
208         setHint(Hints.KEY_DISABLE_PAINTING, false);
209     }
210     
211     private void setBackgroundColor(Color backgroundColor) {
212         setHint(KEY_MAP_BACKGROUND_COLOR, backgroundColor);
213     }
214     
215     private Color getBackgroundColor() {
216         return getHint(KEY_MAP_BACKGROUND_COLOR);
217     }
218 }