]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/canvas/impl/CanvasContext.java
Improved clearing of CanvasContext
[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.clear();
195                 hintStack = null;
196                 canvasNode.cleanup();
197                 canvasNode = null;
198                 eventQueue = null;
199                 mouseCaptureCtx = null;
200                 mouseCursorCtx = null;
201                 paintableCtx = null;
202                 listeners.clear();
203                 listeners = null;
204                 set.clear();
205                 set = null;
206                 tooltip = null;
207             }
208         });
209     }
210
211     @Override
212     public IHintContext getDefaultHintContext() {
213         return bottomHintContext;
214     }
215
216     @Override
217     public IMouseCursorContext getMouseCursorContext() {
218         return mouseCursorCtx;
219     }
220
221     @Override
222     public void setMouseCursorContext(IMouseCursorContext ctx) {
223         this.mouseCursorCtx = ctx;
224     }
225
226     @Override
227     public IMouseCaptureContext getMouseCaptureContext() {
228         return mouseCaptureCtx;
229     }
230
231     @Override
232     public void setMouseCaptureContext(IMouseCaptureContext mctx) {
233         this.mouseCaptureCtx = mctx;
234     }
235     
236     public ITooltipProvider getTooltipProvider() {
237                 return tooltip;
238         }
239
240         public void setTooltipProvider(ITooltipProvider tooltip) {
241                 this.tooltip = tooltip;
242         }
243
244         @Override
245     public IEventQueue getEventQueue() {
246         return eventQueue;
247     }
248
249     @Override
250     public IContentContext getContentContext() {
251         return paintableCtx;
252     }
253
254     @Override
255     public void setContentContext(IContentContext ctx) {
256         this.paintableCtx = ctx;
257     }
258
259     @Override
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();
265         }
266     }
267
268     @Override
269     public boolean isLocked() {
270         return this.locked.get();
271     }
272
273     /**
274      * Assert participant dependies are OK
275      */
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);
282                 }
283             }
284     }
285
286     @Override
287     public String toString() {
288         if (isDisposed())
289             return super.toString();
290
291         StringBuilder sb = new StringBuilder();
292         if (locked.get())
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();
301     }
302 }