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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.canvas.impl;
\r
14 import java.util.concurrent.atomic.AtomicBoolean;
\r
16 import org.simantics.g2d.canvas.ICanvasContext;
\r
17 import org.simantics.g2d.canvas.ICanvasParticipant;
\r
18 import org.simantics.g2d.canvas.IContentContext;
\r
19 import org.simantics.g2d.canvas.IMouseCaptureContext;
\r
20 import org.simantics.g2d.canvas.IMouseCursorContext;
\r
21 import org.simantics.g2d.chassis.ITooltipProvider;
\r
22 import org.simantics.g2d.scenegraph.SceneGraphConstants;
\r
23 import org.simantics.scenegraph.g2d.G2DParentNode;
\r
24 import org.simantics.scenegraph.g2d.G2DSceneGraph;
\r
25 import org.simantics.scenegraph.g2d.events.Event;
\r
26 import org.simantics.scenegraph.g2d.events.EventHandlerStack;
\r
27 import org.simantics.scenegraph.g2d.events.EventQueue;
\r
28 import org.simantics.scenegraph.g2d.events.IEventHandlerStack;
\r
29 import org.simantics.scenegraph.g2d.events.IEventQueue;
\r
30 import org.simantics.scenegraph.g2d.events.IEventQueue.IEventQueueListener;
\r
31 import org.simantics.scenegraph.g2d.events.MouseEventCoalescer;
\r
32 import org.simantics.scenegraph.g2d.nodes.DataNode;
\r
33 import org.simantics.utils.datastructures.context.Context;
\r
34 import org.simantics.utils.datastructures.context.IContext;
\r
35 import org.simantics.utils.datastructures.context.IContextListener;
\r
36 import org.simantics.utils.datastructures.hints.HintContext;
\r
37 import org.simantics.utils.datastructures.hints.HintStack;
\r
38 import org.simantics.utils.datastructures.hints.IHintContext;
\r
39 import org.simantics.utils.datastructures.hints.IHintStack;
\r
40 import org.simantics.utils.strings.EString;
\r
41 import org.simantics.utils.threads.IThreadWorkQueue;
\r
42 import org.simantics.utils.threads.ThreadUtils;
\r
45 * This class contains the UI Support the Elements need when rendering and interacting with
\r
46 * the Operating System.
\r
48 * @author Toni Kalajainen
\r
50 public class CanvasContext extends Context<ICanvasParticipant> implements ICanvasContext {
\r
52 protected HintStack hintStack = new HintStack();
\r
53 protected HintContext bottomHintContext = new HintContext();
\r
54 protected IEventHandlerStack eventHandlerStack = null;
\r
55 protected boolean eventHandlingOrdered = false;
\r
56 protected EventQueue eventQueue = null;
\r
57 protected IContentContext paintableCtx = new PaintableContextImpl();
\r
58 protected final IThreadWorkQueue thread;
\r
60 protected IMouseCaptureContext mouseCaptureCtx = new MouseCaptureContext();
\r
61 protected IMouseCursorContext mouseCursorCtx = new MouseCursorContext();
\r
63 protected G2DSceneGraph sceneGraph;
\r
64 protected G2DParentNode canvasNode = null;
\r
65 protected ITooltipProvider tooltip;
\r
67 protected final AtomicBoolean locked = new AtomicBoolean(false);
\r
69 public CanvasContext(IThreadWorkQueue thread)
\r
71 this(thread, new G2DSceneGraph());
\r
76 * @param thread context thread, or null if sync policy not used
\r
78 public CanvasContext(IThreadWorkQueue thread, G2DSceneGraph sg)
\r
80 super(ICanvasParticipant.class);
\r
81 if ( thread == null )
\r
82 throw new IllegalArgumentException("null");
\r
85 canvasNode = sg.addNode(SceneGraphConstants.NAVIGATION_NODE_NAME, G2DParentNode.class); // Add dummy parent node
\r
86 canvasNode.setLookupId(SceneGraphConstants.NAVIGATION_NODE_NAME);
\r
87 DataNode dataNode = sg.addNode(SceneGraphConstants.DATA_NODE_NAME, DataNode.class);
\r
88 dataNode.setLookupId(SceneGraphConstants.DATA_NODE_NAME);
\r
90 this.thread = thread;
\r
91 eventHandlerStack = new EventHandlerStack(thread);
\r
92 eventQueue = new EventQueue(eventHandlerStack);
\r
93 hintStack.addHintContext(bottomHintContext, Integer.MIN_VALUE);
\r
95 // Install scene graph as a handler into the canvas context event
\r
97 eventHandlerStack.add(this.sceneGraph.getEventHandler(), SceneGraphConstants.SCENEGRAPH_EVENT_PRIORITY);
\r
99 this.addContextListener(thread, new IContextListener<ICanvasParticipant>() {
\r
101 public void itemAdded(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {
\r
102 item.addedToContext(CanvasContext.this);
\r
105 public void itemRemoved(IContext<ICanvasParticipant> sender,
\r
106 ICanvasParticipant item) {
\r
107 item.removedFromContext(CanvasContext.this);
\r
111 eventQueue.addEventCoalesceler(MouseEventCoalescer.INSTANCE);
\r
112 // Order event handling if events are added to the queue
\r
113 eventQueue.addQueueListener(new IEventQueueListener() {
\r
115 public void onEventAdded(IEventQueue queue, Event e, int index) {
\r
116 asyncHandleEvents();
\r
119 public void onQueueEmpty(IEventQueue queue) {
\r
124 public IHintStack getHintStack()
\r
126 assertNotDisposed();
\r
130 private final Runnable eventHandling = new Runnable() {
\r
132 public void run() {
\r
135 eventHandlingOrdered = false;
\r
136 eventQueue.handleEvents();
\r
140 synchronized void asyncHandleEvents() {
\r
141 if (eventHandlingOrdered) return;
\r
142 eventHandlingOrdered = true;
\r
143 ThreadUtils.asyncExec(thread, eventHandling);
\r
146 synchronized void syncHandleEvents() {
\r
147 if (eventHandlingOrdered) return;
\r
148 eventHandlingOrdered = true;
\r
149 ThreadUtils.syncExec(thread, eventHandling);
\r
153 public G2DSceneGraph getSceneGraph() {
\r
158 public void setCanvasNode(G2DParentNode node) {
\r
159 this.canvasNode = node;
\r
163 public G2DParentNode getCanvasNode() {
\r
168 public IEventHandlerStack getEventHandlerStack() {
\r
169 assertNotDisposed();
\r
170 return eventHandlerStack;
\r
174 public IThreadWorkQueue getThreadAccess() {
\r
175 //assertNotDisposed();
\r
180 protected void doDispose() {
\r
181 ThreadUtils.syncExec(getThreadAccess(), new Runnable() {
\r
183 public void run() {
\r
185 bottomHintContext.clearWithoutNotification();
\r
186 if (sceneGraph != null) {
\r
187 // Makes sure that scene graph nodes free their resources!
\r
188 sceneGraph.removeNodes();
\r
189 sceneGraph.cleanup();
\r
192 // HN: added to decrease memory leaks
\r
193 eventHandlerStack = null;
\r
195 canvasNode.cleanup();
\r
198 mouseCaptureCtx = null;
\r
199 mouseCursorCtx = null;
\r
200 paintableCtx = null;
\r
211 public IHintContext getDefaultHintContext() {
\r
212 return bottomHintContext;
\r
216 public IMouseCursorContext getMouseCursorContext() {
\r
217 return mouseCursorCtx;
\r
221 public void setMouseCursorContext(IMouseCursorContext ctx) {
\r
222 this.mouseCursorCtx = ctx;
\r
226 public IMouseCaptureContext getMouseCaptureContext() {
\r
227 return mouseCaptureCtx;
\r
231 public void setMouseCaptureContext(IMouseCaptureContext mctx) {
\r
232 this.mouseCaptureCtx = mctx;
\r
235 public ITooltipProvider getTooltipProvider() {
\r
239 public void setTooltipProvider(ITooltipProvider tooltip) {
\r
240 this.tooltip = tooltip;
\r
244 public IEventQueue getEventQueue() {
\r
249 public IContentContext getContentContext() {
\r
250 return paintableCtx;
\r
254 public void setContentContext(IContentContext ctx) {
\r
255 this.paintableCtx = ctx;
\r
259 public void setLocked(boolean locked) {
\r
260 boolean previous = this.locked.getAndSet(locked);
\r
261 if (!locked && previous != locked) {
\r
262 // The context was unlocked!
\r
263 getContentContext().setDirty();
\r
268 public boolean isLocked() {
\r
269 return this.locked.get();
\r
273 * Assert participant dependies are OK
\r
275 public void assertParticipantDependencies() {
\r
276 for (ICanvasParticipant ctx : toArray())
\r
277 if (ctx instanceof AbstractCanvasParticipant) {
\r
278 AbstractCanvasParticipant acp = (AbstractCanvasParticipant) ctx;
\r
279 if (!acp.depsSatisfied) {
\r
280 throw new AssertionError("Participant "+acp+" dependies unsatisfied");
\r
286 public String toString() {
\r
288 return super.toString();
\r
290 StringBuilder sb = new StringBuilder();
\r
292 sb.append("[CanvasContext@" + System.identityHashCode(this) + " is locked]");
\r
293 sb.append("Participants:\n");
\r
294 sb.append(EString.addPrefix( super.toString(), "\t" ));
\r
295 // sb.append("\n\nPainter stack:\n");
\r
296 // sb.append(EString.addPrefix( getPainterStack().toString(), "\t" ));
\r
297 sb.append("\n\nEvent handler stack:\n");
\r
298 sb.append(EString.addPrefix( getEventHandlerStack().toString(), "\t" ));
\r
299 return sb.toString();
\r