1 /*******************************************************************************
2 * Copyright (c) 2012 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.maps.eclipse;
14 import java.awt.Color;
15 import java.awt.geom.AffineTransform;
16 import java.util.concurrent.ScheduledFuture;
17 import java.util.concurrent.TimeUnit;
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.MapInfoNode;
27 import org.simantics.maps.sg.MapLocationInfoNode;
28 import org.simantics.maps.sg.MapNode;
29 import org.simantics.maps.sg.MapScaleNode;
30 import org.simantics.maps.sg.commands.MapCommands;
31 import org.simantics.scenegraph.g2d.G2DParentNode;
32 import org.simantics.scenegraph.g2d.events.Event;
33 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
34 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
35 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
36 import org.simantics.scenegraph.g2d.events.command.Commands;
37 import org.simantics.utils.datastructures.hints.HintListenerAdapter;
38 import org.simantics.utils.datastructures.hints.IHintContext.Key;
39 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
40 import org.simantics.utils.datastructures.hints.IHintListener;
41 import org.simantics.utils.datastructures.hints.IHintObservable;
42 import org.simantics.utils.threads.AWTThread;
43 import org.simantics.utils.threads.ThreadUtils;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * MapPainter is an ICanvasContext participant that uses the scene graph
49 * {@link MapNode} to draw tiled maps in the background of the canvas.
55 public class MapPainter extends AbstractCanvasParticipant {
57 private static final Logger LOGGER = LoggerFactory.getLogger(MapPainter.class);
60 * Grid enabled status. Default value is True
62 public static final Key KEY_MAP_ENABLED = new KeyOf(Boolean.class);
64 public static final Key KEY_MAP_BACKGROUND_COLOR = new KeyOf(Object.class);
66 public static final double ZOOM_IN_LIMIT = 10000000.0;
68 public static final double ZOOM_OUT_LIMIT = 10.0;
70 IHintListener mapListener = new HintListenerAdapter() {
71 public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
72 ICanvasContext cc = getContext();
75 cc.getContentContext().setDirty();
80 private MapNode mapNode;
81 private MapLocationInfoNode locationInfoNode;
82 private MapScaleNode scaleNode;
83 private MapInfoNode attributionNode;
85 private AffineTransform transform;
87 private ScheduledFuture<?> schedule;
89 public MapPainter(AffineTransform transform) {
90 this.transform = transform;
94 public void addedToContext(ICanvasContext ctx) {
95 super.addedToContext(ctx);
96 getHintStack().addKeyHintListener(getThread(), KEY_MAP_ENABLED, mapListener);
97 getHintStack().addKeyHintListener(getThread(), KEY_MAP_BACKGROUND_COLOR, mapListener);
101 public void removedFromContext(ICanvasContext ctx) {
102 getHintStack().removeKeyHintListener(getThread(), KEY_MAP_ENABLED, mapListener);
103 getHintStack().removeKeyHintListener(getThread(), KEY_MAP_BACKGROUND_COLOR, mapListener);
104 super.removedFromContext(ctx);
107 @EventHandler(priority = 0)
108 public boolean handleKeyEvent(CommandEvent e) {
109 if (e.command.equals( Commands.MAP_ENABLE )) {
114 } else if (e.command.equals( MapCommands.MAP_BACKGROUND_COLOR_CHANGE )) {
115 ICanvasContext context = (ICanvasContext) e.getContext();
116 Color s = context.getHintStack().getHint(MapCommands.KEY_MAP_BACKGROUND_COLOR);
117 setBackgroundColor(s);
120 } else if (e.command.equals( Commands.MAP_DISABLE )) {
125 } else if (e.command.equals( Commands.MAP_TOGGLE )) {
126 setEnabled(!isMapEnabled());
130 } else if (e.command.equals( Commands.ENABLE_PAINTING )) {
139 @EventHandler(priority = 31)
140 public boolean handleEvent(Event e) {
141 if (e instanceof MouseMovedEvent) {
142 // here we should somehow re-render ?
143 if (locationInfoNode != null && locationInfoNode.isEnabled()) {
144 if (schedule == null || schedule.isDone()) {
145 LOGGER.debug("current setDirty time" + System.currentTimeMillis());
146 schedule = ThreadUtils.getNonBlockingWorkExecutor().schedule(() -> {
147 AWTThread.getThreadAccess().asyncExec(this::setDirty);
148 }, 100, TimeUnit.MILLISECONDS);
150 //LOGGER.debug("ingoring setDirty time" + System.currentTimeMillis());
158 public void initSG(G2DParentNode parent) {
159 // Just under the grid
160 mapNode = parent.addNode("map", MapNode.class);
161 mapNode.setTransform(transform);
162 mapNode.setEnabled(true);
163 mapNode.setZIndex(Integer.MIN_VALUE + 999);
165 // On top of pretty much everything
166 attributionNode = parent.addNode("mapAttribution", MapAttributionNode.class);
167 attributionNode.setTransform(transform);
168 attributionNode.setZIndex(Integer.MAX_VALUE - 999);
169 attributionNode.setEnabled(true);
171 scaleNode = parent.addNode("mapScale", MapScaleNode.class);
172 scaleNode.setTransform(transform);
173 scaleNode.setZIndex(Integer.MAX_VALUE - 998);
174 scaleNode.setEnabled(true);
176 locationInfoNode = parent.addNode("mapLocationInfo", MapLocationInfoNode.class);
177 locationInfoNode.setTransform(transform);
178 locationInfoNode.setZIndex(Integer.MAX_VALUE - 997);
179 locationInfoNode.setEnabled(true);
180 locationInfoNode.setMouseUtil(getContext().getAtMostOneItemOfClass(MouseUtil.class));
184 public void cleanupSG() {
186 attributionNode.remove();
188 locationInfoNode.remove();
191 protected void updateNode() {
192 mapNode.setEnabled(isPaintingEnabled());
193 mapNode.setEnabled(isMapEnabled());
194 mapNode.setBackgroundColor(getBackgroundColor());
197 boolean isPaintingEnabled() {
198 boolean enabled = isMapEnabled();
199 Boolean globalDisable = getHint(Hints.KEY_DISABLE_PAINTING);
200 return enabled && !Boolean.TRUE.equals(globalDisable);
203 public boolean isMapEnabled() {
204 Boolean enabled = getHint(KEY_MAP_ENABLED);
205 return !Boolean.FALSE.equals(enabled);
208 public void setEnabled(boolean enabled) {
209 setHint(KEY_MAP_ENABLED, enabled);
212 private void enablePainting() {
213 setHint(Hints.KEY_DISABLE_PAINTING, false);
216 private void setBackgroundColor(Color backgroundColor) {
217 setHint(KEY_MAP_BACKGROUND_COLOR, backgroundColor);
220 private Color getBackgroundColor() {
221 return getHint(KEY_MAP_BACKGROUND_COLOR);