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