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;
196 canvasNode.cleanup();
199 mouseCaptureCtx = null;
200 mouseCursorCtx = null;
212 public IHintContext getDefaultHintContext() {
213 return bottomHintContext;
217 public IMouseCursorContext getMouseCursorContext() {
218 return mouseCursorCtx;
222 public void setMouseCursorContext(IMouseCursorContext ctx) {
223 this.mouseCursorCtx = ctx;
227 public IMouseCaptureContext getMouseCaptureContext() {
228 return mouseCaptureCtx;
232 public void setMouseCaptureContext(IMouseCaptureContext mctx) {
233 this.mouseCaptureCtx = mctx;
236 public ITooltipProvider getTooltipProvider() {
240 public void setTooltipProvider(ITooltipProvider tooltip) {
241 this.tooltip = tooltip;
245 public IEventQueue getEventQueue() {
250 public IContentContext getContentContext() {
255 public void setContentContext(IContentContext ctx) {
256 this.paintableCtx = ctx;
260 public void setLocked(boolean locked) {
261 boolean previous = this.locked.getAndSet(locked);
262 if (!locked && previous != locked) {
263 // The context was unlocked!
264 getContentContext().setDirty();
269 public boolean isLocked() {
270 return this.locked.get();
274 * Assert participant dependies are OK
276 public void assertParticipantDependencies() {
277 for (ICanvasParticipant ctx : toArray())
278 if (ctx instanceof AbstractCanvasParticipant) {
279 AbstractCanvasParticipant acp = (AbstractCanvasParticipant) ctx;
280 if (!acp.depsSatisfied) {
281 throw new AssertionError("Participant "+acp+" dependencies unsatisfied : " + acp.missingDependencies);
287 public String toString() {
289 return super.toString();
291 StringBuilder sb = new StringBuilder();
293 sb.append("[CanvasContext@" + System.identityHashCode(this) + " is locked]");
294 sb.append("Participants:\n");
295 sb.append(EString.addPrefix( super.toString(), "\t" ));
296 // sb.append("\n\nPainter stack:\n");
297 // sb.append(EString.addPrefix( getPainterStack().toString(), "\t" ));
298 sb.append("\n\nEvent handler stack:\n");
299 sb.append(EString.addPrefix( getEventHandlerStack().toString(), "\t" ));
300 return sb.toString();