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