]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/canvas/impl/CanvasContext.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / canvas / impl / CanvasContext.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 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.g2d.canvas.impl;
13
14 import java.util.concurrent.atomic.AtomicBoolean;
15
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;
43
44 /**
45  * This class contains the UI Support the Elements need when rendering and interacting with 
46  * the Operating System. 
47  * 
48  * @author Toni Kalajainen
49  */
50 public class CanvasContext extends Context<ICanvasParticipant> implements ICanvasContext {
51
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;
59
60     protected IMouseCaptureContext   mouseCaptureCtx      = new MouseCaptureContext();
61     protected IMouseCursorContext    mouseCursorCtx       = new MouseCursorContext();
62
63     protected G2DSceneGraph          sceneGraph;
64     protected G2DParentNode          canvasNode           = null;
65     protected ITooltipProvider       tooltip;
66
67     protected final AtomicBoolean    locked               = new AtomicBoolean(false);
68
69     public CanvasContext(IThreadWorkQueue thread)
70     {
71         this(thread, new G2DSceneGraph());
72     }
73
74     /**
75      *
76      * @param thread context thread, or null if sync policy not used
77      */
78     public CanvasContext(IThreadWorkQueue thread, G2DSceneGraph sg)
79     {
80         super(ICanvasParticipant.class);
81         if ( thread == null )
82             throw new IllegalArgumentException("null");
83
84         sceneGraph = sg;
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);
89
90         this.thread         = thread;
91         eventHandlerStack   = new EventHandlerStack(thread);
92         eventQueue          = new EventQueue(eventHandlerStack);
93         hintStack.addHintContext(bottomHintContext, Integer.MIN_VALUE);
94
95         // Install scene graph as a handler into the canvas context event
96         // handler stack.
97         eventHandlerStack.add(this.sceneGraph.getEventHandler(), SceneGraphConstants.SCENEGRAPH_EVENT_PRIORITY);
98
99         this.addContextListener(thread, new IContextListener<ICanvasParticipant>() {
100             @Override
101             public void itemAdded(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {
102                 item.addedToContext(CanvasContext.this);
103             }
104             @Override
105             public void itemRemoved(IContext<ICanvasParticipant> sender,
106                     ICanvasParticipant item) {
107                 item.removedFromContext(CanvasContext.this);
108             }
109         });
110
111         eventQueue.addEventCoalesceler(MouseEventCoalescer.INSTANCE);
112         // Order event handling if events are added to the queue
113         eventQueue.addQueueListener(new IEventQueueListener() {
114             @Override
115             public void onEventAdded(IEventQueue queue, Event e, int index) {
116                 asyncHandleEvents();
117             }
118             @Override
119             public void onQueueEmpty(IEventQueue queue) {
120             }
121         });
122     }
123
124     public IHintStack getHintStack()
125     {
126         assertNotDisposed();
127         return hintStack;
128     }
129
130     private final Runnable eventHandling = new Runnable() {
131         @Override
132         public void run() {
133             if (!isAlive())
134                 return;
135             eventHandlingOrdered = false;
136             eventQueue.handleEvents();
137         }
138     };
139
140     synchronized void asyncHandleEvents() {
141         if (eventHandlingOrdered) return;
142         eventHandlingOrdered = true;
143         ThreadUtils.asyncExec(thread, eventHandling);
144     }
145
146     synchronized void syncHandleEvents() {
147         if (eventHandlingOrdered) return;
148         eventHandlingOrdered = true;
149         ThreadUtils.syncExec(thread, eventHandling);
150     }
151
152     @Override
153     public G2DSceneGraph getSceneGraph() {
154         return sceneGraph;
155     }
156
157     @Override
158     public void setCanvasNode(G2DParentNode node) {
159         this.canvasNode = node;
160     }
161
162     @Override
163     public G2DParentNode getCanvasNode() {
164         return canvasNode;
165     }
166
167     @Override
168     public IEventHandlerStack getEventHandlerStack() {
169         assertNotDisposed();
170         return eventHandlerStack;
171     }
172
173     @Override
174     public IThreadWorkQueue getThreadAccess() {
175         //assertNotDisposed();
176         return thread;
177     }
178
179     @Override
180     protected void doDispose() {
181         ThreadUtils.syncExec(getThreadAccess(), new Runnable() {
182             @Override
183             public void run() {
184                 clear();
185                 bottomHintContext.clearWithoutNotification();
186                 if (sceneGraph != null) {
187                     // Makes sure that scene graph nodes free their resources!
188                     sceneGraph.removeNodes();
189                     sceneGraph.cleanup();
190                     sceneGraph = null;
191                 }
192                 // HN: added to decrease memory leaks
193                 eventHandlerStack = null;
194                 hintStack = null;
195                 canvasNode.cleanup();
196                 canvasNode = null;
197                 eventQueue = null;
198                 mouseCaptureCtx = null;
199                 mouseCursorCtx = null;
200                 paintableCtx = null;
201                 listeners.clear();
202                 listeners = null;
203                 set.clear();
204                 set = null;
205                 tooltip = null;
206             }
207         });
208     }
209
210     @Override
211     public IHintContext getDefaultHintContext() {
212         return bottomHintContext;
213     }
214
215     @Override
216     public IMouseCursorContext getMouseCursorContext() {
217         return mouseCursorCtx;
218     }
219
220     @Override
221     public void setMouseCursorContext(IMouseCursorContext ctx) {
222         this.mouseCursorCtx = ctx;
223     }
224
225     @Override
226     public IMouseCaptureContext getMouseCaptureContext() {
227         return mouseCaptureCtx;
228     }
229
230     @Override
231     public void setMouseCaptureContext(IMouseCaptureContext mctx) {
232         this.mouseCaptureCtx = mctx;
233     }
234     
235     public ITooltipProvider getTooltipProvider() {
236                 return tooltip;
237         }
238
239         public void setTooltipProvider(ITooltipProvider tooltip) {
240                 this.tooltip = tooltip;
241         }
242
243         @Override
244     public IEventQueue getEventQueue() {
245         return eventQueue;
246     }
247
248     @Override
249     public IContentContext getContentContext() {
250         return paintableCtx;
251     }
252
253     @Override
254     public void setContentContext(IContentContext ctx) {
255         this.paintableCtx = ctx;
256     }
257
258     @Override
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();
264         }
265     }
266
267     @Override
268     public boolean isLocked() {
269         return this.locked.get();
270     }
271
272     /**
273      * Assert participant dependies are OK
274      */
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);
281                 }
282             }
283     }
284
285     @Override
286     public String toString() {
287         if (isDisposed())
288             return super.toString();
289
290         StringBuilder sb = new StringBuilder();
291         if (locked.get())
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();
300     }
301 }