]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/impl/ElementDiagram.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / diagram / impl / ElementDiagram.java
index b53e6dada8b76a9b782036882a99be94e20fa839..7bec43780df1bb9904e943a347b6b98fedeeb624 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.g2d.diagram.impl;\r
-\r
-import java.awt.Color;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.Comparator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.concurrent.atomic.AtomicBoolean;\r
-\r
-import org.simantics.g2d.canvas.Hints;\r
-import org.simantics.g2d.canvas.ICanvasContext;\r
-import org.simantics.g2d.canvas.ICanvasParticipant;\r
-import org.simantics.g2d.canvas.IContentContext;\r
-import org.simantics.g2d.canvas.IMouseCaptureContext;\r
-import org.simantics.g2d.canvas.IMouseCursorContext;\r
-import org.simantics.g2d.canvas.impl.MouseCaptureContext;\r
-import org.simantics.g2d.canvas.impl.MouseCursorContext;\r
-import org.simantics.g2d.canvas.impl.PaintableContextImpl;\r
-import org.simantics.g2d.chassis.ITooltipProvider;\r
-import org.simantics.g2d.diagram.DiagramClass;\r
-import org.simantics.g2d.diagram.DiagramHints;\r
-import org.simantics.g2d.diagram.IDiagram;\r
-import org.simantics.g2d.diagram.participant.DiagramParticipant;\r
-import org.simantics.g2d.diagram.participant.ElementInteractor;\r
-import org.simantics.g2d.diagram.participant.ElementPainter;\r
-import org.simantics.g2d.diagram.participant.Selection;\r
-import org.simantics.g2d.diagram.participant.TerminalPainter;\r
-import org.simantics.g2d.diagram.participant.ZOrderHandler;\r
-import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.multileveldiagram.TransitionFunction;\r
-import org.simantics.g2d.multileveldiagram.ZoomTransitionParticipant;\r
-import org.simantics.g2d.participant.BackgroundPainter;\r
-import org.simantics.g2d.participant.CanvasGrab;\r
-import org.simantics.g2d.participant.GridPainter;\r
-import org.simantics.g2d.participant.HandPainter;\r
-import org.simantics.g2d.participant.KeyToCommand;\r
-import org.simantics.g2d.participant.KeyUtil;\r
-import org.simantics.g2d.participant.MouseUtil;\r
-import org.simantics.g2d.participant.PointerPainter;\r
-import org.simantics.g2d.participant.RulerPainter;\r
-import org.simantics.g2d.participant.SubCanvas;\r
-import org.simantics.g2d.participant.SymbolUtil;\r
-import org.simantics.g2d.participant.TimeParticipant;\r
-import org.simantics.g2d.participant.TransformUtil;\r
-import org.simantics.g2d.scenegraph.SceneGraphConstants;\r
-import org.simantics.scenegraph.g2d.G2DParentNode;\r
-import org.simantics.scenegraph.g2d.G2DSceneGraph;\r
-import org.simantics.scenegraph.g2d.events.Event;\r
-import org.simantics.scenegraph.g2d.events.EventHandlerStack;\r
-import org.simantics.scenegraph.g2d.events.EventQueue;\r
-import org.simantics.scenegraph.g2d.events.IEventHandlerStack;\r
-import org.simantics.scenegraph.g2d.events.IEventQueue;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEventCoalescer;\r
-import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
-import org.simantics.scenegraph.g2d.events.IEventQueue.IEventQueueListener;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDoubleClickedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseWheelMovedEvent;\r
-import org.simantics.scenegraph.g2d.events.command.CommandKeyBinding;\r
-import org.simantics.utils.datastructures.context.Context;\r
-import org.simantics.utils.datastructures.context.IContext;\r
-import org.simantics.utils.datastructures.context.IContextListener;\r
-import org.simantics.utils.datastructures.hints.HintContext;\r
-import org.simantics.utils.datastructures.hints.HintStack;\r
-import org.simantics.utils.datastructures.hints.IHintContext;\r
-import org.simantics.utils.datastructures.hints.IHintListener;\r
-import org.simantics.utils.datastructures.hints.IHintStack;\r
-import org.simantics.utils.threads.IThreadWorkQueue;\r
-import org.simantics.utils.threads.ThreadUtils;\r
-\r
-/**\r
- * Creates subcanvas on top of parent canvas. Size and position of the subcanvas\r
- * can be changed.\r
- * \r
- * Beware: every CanvasParticipant that handles mouseEvents must be replaced\r
- * since mouse coordinates must be translated. Currently Implemented\r
- * participants: - MouseUtil - ElementInteractor\r
- * \r
- * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
- * \r
- * TODO: CanvasContext implementation needs to implement scene graph methods too\r
- */\r
-public class ElementDiagram implements IDiagram {\r
-    private final ICanvasContext ctx;\r
-\r
-    private final ICanvasContext parentCtx;\r
-\r
-    private IDiagram             diagram;\r
-\r
-    private final SubCanvas      subCanvas;\r
-\r
-    private Rectangle2D          canvasRect = new Rectangle2D.Double(0, 0, 200, 200);\r
-\r
-    private int                  canvasPosX = 100;\r
-\r
-    private int                  canvasPosY = 100;\r
-\r
-    public ElementDiagram(ICanvasContext parentCtx) {\r
-        this.parentCtx = parentCtx;\r
-        ctx = createCanvas(parentCtx.getThreadAccess(),parentCtx.getSceneGraph());\r
-        ICanvasParticipant mouseUtil = ctx.getSingleItem(MouseUtil.class);\r
-        if (mouseUtil != null) {\r
-            ctx.remove(mouseUtil);\r
-            ctx.add(new ElementDiagramMouseUtil());\r
-            // ctx.add(new MouseUtil());\r
-        }\r
-        ICanvasParticipant elementInteractor = ctx.getSingleItem(ElementInteractor.class);\r
-        if (elementInteractor != null) {\r
-            ctx.remove(elementInteractor);\r
-            ctx.add(new ElementDiagramElementInteractor());\r
-        }\r
-        diagram = createDiagram();\r
-        ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram);\r
-        subCanvas = new SubCanvas(ctx, 10000, Integer.MAX_VALUE - 100, 10000);\r
-        parentCtx.add(subCanvas);\r
-    }\r
-\r
-    public void setSize(int x, int y, int width, int height) {\r
-        canvasPosX = x;\r
-        canvasPosY = y;\r
-        canvasRect = new Rectangle2D.Double(0, 0, width, height);\r
-        getCanvas().getCanvasNode().setTransform(new AffineTransform(1,0,0,1,x,y));\r
-       \r
-    }\r
-\r
-    public Rectangle2D getSize() {\r
-        return canvasRect;\r
-    }\r
-\r
-    /**\r
-     * Override this for custom functionality\r
-     * \r
-     * @return\r
-     */\r
-    public IDiagram createDiagram() {\r
-        IDiagram d = Diagram.spawnNew(DiagramClass.DEFAULT);\r
-        return d;\r
-    }\r
-\r
-    /**\r
-     * Override this for custom functionality\r
-     * \r
-     * @param thread\r
-     * @return\r
-     */\r
-    public ICanvasContext createCanvas(IThreadWorkQueue thread, G2DSceneGraph sg) {\r
-        return createDefaultCanvas(thread,sg);\r
-    }\r
-\r
-    public void setDiagram(IDiagram diagram) {\r
-        this.diagram = diagram;\r
-        ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram);\r
-    }\r
-\r
-    public ICanvasContext getCanvas() {\r
-        return ctx;\r
-    }\r
-\r
-    public ICanvasContext getParentCanvas() {\r
-        return parentCtx;\r
-    }\r
-\r
-    public SubCanvas getSubCanvas() {\r
-        return subCanvas;\r
-    }\r
-\r
-    public IDiagram getDiagram() {\r
-        return diagram;\r
-    }\r
-\r
-    @Override\r
-    public void addElement(IElement element) {\r
-        diagram.addElement(element);\r
-    }\r
-\r
-    @Override\r
-    public void removeElement(IElement element) {\r
-        diagram.removeElement(element);\r
-    }\r
-\r
-    @Override\r
-    public void addCompositionListener(CompositionListener listener) {\r
-        diagram.addCompositionListener(listener);\r
-    }\r
-\r
-    @Override\r
-    public void addCompositionVetoListener(CompositionVetoListener listener) {\r
-        diagram.addCompositionVetoListener(listener);\r
-    }\r
-\r
-    @Override\r
-    public void addHintListener(IHintListener listener) {\r
-        diagram.addHintListener(listener);\r
-    }\r
-\r
-    @Override\r
-    public void addHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {\r
-        diagram.addHintListener(threadAccess, listener);\r
-    }\r
-\r
-    @Override\r
-    public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {\r
-        diagram.addKeyHintListener(threadAccess, key, listener);\r
-    }\r
-\r
-    @Override\r
-    public void addKeyHintListener(Key key, IHintListener listener) {\r
-        diagram.addKeyHintListener(key, listener);\r
-    }\r
-\r
-    @Override\r
-    public boolean bringToTop(IElement e) {\r
-        return diagram.bringToTop(e);\r
-    }\r
-\r
-    @Override\r
-    public boolean bringUp(IElement e) {\r
-        return diagram.bringUp(e);\r
-    }\r
-\r
-    @Override\r
-    public void destroy() {\r
-        diagram.destroy();\r
-    }\r
-\r
-    @Override\r
-    public void dispose() {\r
-        diagram.dispose();\r
-    }\r
-\r
-    @Override\r
-    public DiagramClass getDiagramClass() {\r
-        return diagram.getDiagramClass();\r
-    }\r
-\r
-    @Override\r
-    public boolean containsElement(IElement element) {\r
-        return diagram.containsElement(element);\r
-    }\r
-\r
-    @Override\r
-    public List<IElement> getElements() {\r
-        return diagram.getElements();\r
-    }\r
-\r
-    @Override\r
-    public void sort(Comparator<IElement> comparator) {\r
-        diagram.sort(comparator);\r
-    }\r
-\r
-    @Override\r
-    public List<IElement> getSnapshot() {\r
-        return diagram.getSnapshot();\r
-    }\r
-\r
-    @Override\r
-    public void clearWithoutNotification() {\r
-        diagram.clearWithoutNotification();\r
-    }\r
-\r
-    @Override\r
-    public boolean containsHint(Key key) {\r
-        return diagram.containsHint(key);\r
-    }\r
-\r
-    @Override\r
-    public <E> E getHint(Key key) {\r
-        return diagram.getHint(key);\r
-    }\r
-\r
-    @Override\r
-    public Map<Key, Object> getHints() {\r
-        return diagram.getHints();\r
-    }\r
-\r
-    @Override\r
-    public Map<Key, Object> getHintsUnsafe() {\r
-        return diagram.getHintsUnsafe();\r
-    }\r
-\r
-    @Override\r
-    public <E extends Key> Map<E, Object> getHintsOfClass(Class<E> clazz) {\r
-        return diagram.getHintsOfClass(clazz);\r
-    }\r
-\r
-    @Override\r
-    public boolean moveTo(IElement e, int position) {\r
-        return diagram.moveTo(e, position);\r
-    }\r
-\r
-    @Override\r
-    public void removeCompositionListener(CompositionListener listener) {\r
-        diagram.removeCompositionListener(listener);\r
-    }\r
-\r
-    @Override\r
-    public void removeCompositionVetoListener(CompositionVetoListener listener) {\r
-        diagram.removeCompositionVetoListener(listener);\r
-    }\r
-\r
-    @Override\r
-    public <E> E removeHint(Key key) {\r
-        return diagram.removeHint(key);\r
-    }\r
-\r
-    @Override\r
-    public void removeHintListener(IHintListener listener) {\r
-        diagram.removeHintListener(listener);\r
-    }\r
-\r
-    @Override\r
-    public void removeHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {\r
-        diagram.removeHintListener(threadAccess, listener);\r
-    }\r
-\r
-    @Override\r
-    public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {\r
-        diagram.removeKeyHintListener(threadAccess, key, listener);\r
-    }\r
-\r
-    @Override\r
-    public void removeKeyHintListener(Key key, IHintListener listener) {\r
-        diagram.removeKeyHintListener(key, listener);\r
-    }\r
-\r
-    @Override\r
-    public boolean sendDown(IElement e) {\r
-        return diagram.sendDown(e);\r
-    }\r
-\r
-    @Override\r
-    public boolean sendToBottom(IElement e) {\r
-        return diagram.sendToBottom(e);\r
-    }\r
-\r
-    @Override\r
-    public void setHint(Key key, Object value) {\r
-        diagram.setHint(key, value);\r
-    }\r
-\r
-    @Override\r
-    public void setHints(Map<Key, Object> hints) {\r
-        diagram.setHints(hints);\r
-    }\r
-\r
-    public ICanvasContext createDefaultCanvas(IThreadWorkQueue thread, G2DSceneGraph sg) {\r
-        // Create canvas context and a layer of interactors\r
-        ICanvasContext canvasContext = new ElementDiagramCanvasContext(thread,sg);\r
-        IHintContext h = canvasContext.getDefaultHintContext();\r
-\r
-        //canvasContext.add(new PanZoomRotateHandler()); // Must be before\r
-        // TransformUtil\r
-\r
-        // Support & Util Participants\r
-        canvasContext.add(new TransformUtil());\r
-        canvasContext.add(new MouseUtil());\r
-        canvasContext.add(new KeyUtil());\r
-        canvasContext.add(new CanvasGrab());\r
-        canvasContext.add(new SymbolUtil());\r
-        canvasContext.add(new TimeParticipant());\r
-\r
-        // Debug participant(s)\r
-        // canvasContext.add( new PointerPainter() );\r
-        canvasContext.add(new HandPainter());\r
-        h.setHint(PointerPainter.KEY_PAINT_POINTER, true);\r
-\r
-        // Pan & Zoom & Rotate\r
-        // canvasContext.add( new MousePanZoomInteractor() );\r
-        // canvasContext.add( new MultitouchPanZoomRotateInteractor() );\r
-        // canvasContext.add( new OrientationRestorer() );\r
-\r
-        // Grid & Ruler & Background\r
-        canvasContext.add(new GridPainter());\r
-        canvasContext.add(new RulerPainter());\r
-        canvasContext.add(new BackgroundPainter());\r
-        h.setHint(Hints.KEY_GRID_COLOR, new Color(0.9f, 0.9f, 0.9f));\r
-        h.setHint(Hints.KEY_BACKGROUND_COLOR, Color.LIGHT_GRAY);\r
-\r
-        // Key bindings\r
-        canvasContext.add(new KeyToCommand(CommandKeyBinding.DEFAULT_BINDINGS));\r
-\r
-        // //// Diagram Participants //////\r
-        canvasContext.add(new PointerInteractor());\r
-        canvasContext.add(new ElementInteractor());\r
-        canvasContext.add(new Selection());\r
-        canvasContext.add(new DiagramParticipant());\r
-        canvasContext.add(new ElementPainter());\r
-        canvasContext.add(new TerminalPainter(true, true, false, true));\r
-        //canvasContext.add(new ElementHeartbeater());\r
-        canvasContext.add(new ZOrderHandler());\r
-        canvasContext.add(new ZoomTransitionParticipant(TransitionFunction.SIGMOID));\r
-        h.setHint(Hints.KEY_TOOL, Hints.PANTOOL);\r
-\r
-        return canvasContext;\r
-    }\r
-\r
-    public class ElementDiagramCanvasContext extends Context<ICanvasParticipant> implements ICanvasContext {\r
-\r
-        protected HintStack              hintStack            = new HintStack();\r
-\r
-        protected HintContext            bottomHintContext    = new HintContext();\r
-\r
-        protected IEventHandlerStack     eventHandlerStack    = null;\r
-\r
-        protected boolean                eventHandlingOrdered = false;\r
-\r
-        protected EventQueue             eventQueue           = null;\r
-\r
-        protected IContentContext        paintableCtx         = new PaintableContextImpl();\r
-\r
-        protected final IThreadWorkQueue thread;\r
-\r
-        protected IMouseCaptureContext   mouseCaptureCtx      = new MouseCaptureContext();\r
-\r
-        protected IMouseCursorContext    mouseCursorCtx       = new MouseCursorContext();\r
-        \r
-        protected G2DSceneGraph             sg;\r
-        protected G2DParentNode          canvasNode           = null;\r
-        \r
-        protected ITooltipProvider       tooltip;\r
-\r
-        /**\r
-         * \r
-         * @param thread context thread, or null if sync policy not used\r
-         */\r
-        public ElementDiagramCanvasContext(IThreadWorkQueue thread, G2DSceneGraph sg) {\r
-            super(ICanvasParticipant.class);\r
-            if (thread == null)\r
-                throw new IllegalArgumentException("null");\r
-            this.thread = thread;\r
-            eventHandlerStack = new EventHandlerStack(thread);\r
-            eventQueue = new EventQueue(eventHandlerStack);\r
-            hintStack.addHintContext(bottomHintContext, Integer.MIN_VALUE);\r
-\r
-            \r
-            \r
-            this.sg = sg;\r
-            canvasNode = sg.addNode("elementd" + SceneGraphConstants.NAVIGATION_NODE_NAME , G2DParentNode.class); // Add dummy parent node\r
-            canvasNode.setZIndex(1000);\r
-            \r
-            this.addContextListener(thread, new IContextListener<ICanvasParticipant>() {\r
-                @Override\r
-                public void itemAdded(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {\r
-                    item.addedToContext(ElementDiagramCanvasContext.this);\r
-                }\r
-\r
-                @Override\r
-                public void itemRemoved(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {\r
-                    item.removedFromContext(ElementDiagramCanvasContext.this);\r
-                }\r
-            });\r
-\r
-            eventQueue.addEventCoalesceler(MouseEventCoalescer.INSTANCE);\r
-            // Order event handling if events are added to the queue\r
-            eventQueue.addQueueListener(new IEventQueueListener() {\r
-                @Override\r
-                public void onEventAdded(IEventQueue queue, Event e, int index) {\r
-                    asyncHandleEvents();\r
-                }\r
-\r
-                @Override\r
-                public void onQueueEmpty(IEventQueue queue) {\r
-                }\r
-            });\r
-        }\r
-\r
-        public IHintStack getHintStack() {\r
-            assertNotDisposed();\r
-            return hintStack;\r
-        }\r
-\r
-        private final Runnable eventHandling = new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                eventHandlingOrdered = false;\r
-                eventQueue.handleEvents();\r
-            }\r
-        };\r
-\r
-        synchronized void asyncHandleEvents() {\r
-            if (eventHandlingOrdered)\r
-                return;\r
-            eventHandlingOrdered = true;\r
-            ThreadUtils.asyncExec(thread, eventHandling);\r
-        }\r
-\r
-        synchronized void syncHandleEvents() {\r
-            if (eventHandlingOrdered)\r
-                return;\r
-            eventHandlingOrdered = true;\r
-            ThreadUtils.syncExec(thread, eventHandling);\r
-        }\r
-\r
-        @Override\r
-        public IEventHandlerStack getEventHandlerStack() {\r
-            assertNotDisposed();\r
-            return eventHandlerStack;\r
-        }\r
-\r
-        @Override\r
-        public IThreadWorkQueue getThreadAccess() {\r
-            // assertNotDisposed();\r
-            return thread;\r
-        }\r
-\r
-        @Override\r
-        protected void doDispose() {\r
-            ThreadUtils.syncExec(getThreadAccess(), new Runnable() {\r
-                @Override\r
-                public void run() {\r
-                    clear();\r
-                    \r
-                    // HN: added to decrease memory leaks\r
-                    if (sg != null) {\r
-                        // Makes sure that scene graph nodes free their resources!\r
-                        sg.removeNodes();\r
-                        sg = null;\r
-                    }\r
-                }\r
-            });\r
-        }\r
-\r
-        @Override\r
-        public IHintContext getDefaultHintContext() {\r
-            return bottomHintContext;\r
-        }\r
-\r
-        @Override\r
-        public IMouseCursorContext getMouseCursorContext() {\r
-            return mouseCursorCtx;\r
-        }\r
-\r
-        @Override\r
-        public void setMouseCursorContext(IMouseCursorContext ctx) {\r
-            this.mouseCursorCtx = ctx;\r
-        }\r
-\r
-        @Override\r
-        public IMouseCaptureContext getMouseCaptureContext() {\r
-            return mouseCaptureCtx;\r
-        }\r
-\r
-        @Override\r
-        public void setMouseCaptureContext(IMouseCaptureContext mctx) {\r
-            this.mouseCaptureCtx = mctx;\r
-        }\r
-\r
-        @Override\r
-        public IEventQueue getEventQueue() {\r
-            return eventQueue;\r
-        }\r
-\r
-        @Override\r
-        public IContentContext getContentContext() {\r
-            return paintableCtx;\r
-        }\r
-\r
-        @Override\r
-        public void setContentContext(IContentContext ctx) {\r
-            this.paintableCtx = ctx;\r
-        }\r
-\r
-        @Override\r
-        public G2DSceneGraph getSceneGraph() {\r
-            return sg;\r
-        }\r
-\r
-        @Override\r
-        public G2DParentNode getCanvasNode() {\r
-            return canvasNode;\r
-        }\r
-\r
-        @Override\r
-        public void setCanvasNode(G2DParentNode node) {\r
-            throw new RuntimeException("Cannot set canvasNode");\r
-\r
-        }\r
-\r
-        protected final AtomicBoolean locked = new AtomicBoolean(false);\r
-\r
-        @Override\r
-        public boolean isLocked() {\r
-            return this.locked.get();\r
-        }\r
-\r
-        @Override\r
-        public void setLocked(boolean locked) {\r
-            boolean previous = this.locked.getAndSet(locked);\r
-            if (!locked && previous != locked) {\r
-                // The context was unlocked!\r
-                getContentContext().setDirty();\r
-            }\r
-        }\r
-\r
-               @Override\r
-               public ITooltipProvider getTooltipProvider() {\r
-                       return tooltip;\r
-               }\r
-\r
-               @Override\r
-               public void setTooltipProvider(ITooltipProvider tooltipProvider) {\r
-                       this.tooltip = tooltipProvider;\r
-               }\r
-\r
-    }\r
-\r
-    public class ElementDiagramMouseUtil extends MouseUtil {\r
-\r
-        @Override\r
-        @EventHandler(priority = Integer.MAX_VALUE)\r
-        public boolean handleMouseEvent(MouseEvent e) {\r
-            MouseEvent ne = createMouseEvent(e);\r
-            if (ne != null)\r
-                return handleMouseEvent2(ne);\r
-            return false;\r
-        }\r
-\r
-        /**\r
-         * Copy-pasted MouseUtil.handleMouseEvent with one modification;\r
-         * Generating MouseClickEvents has been removed, because it created\r
-         * duplicated events (with incorrect coordinates).\r
-         * \r
-         * @param e\r
-         * @return\r
-         */\r
-        public boolean handleMouseEvent2(MouseEvent e) {\r
-            assertDependencies();\r
-            if (e instanceof MouseEnterEvent) {\r
-                Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);\r
-                // Reset mouse\r
-                MouseInfo mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons);\r
-                miceInfo.put(e.mouseId, mi);\r
-            } else if (e instanceof MouseExitEvent) {\r
-                miceInfo.remove(e.mouseId);\r
-            } else if (e instanceof MouseMovedEvent) {\r
-                Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);\r
-                double deltaDistance = 0;\r
-                MouseInfo mi = miceInfo.get(e.mouseId);\r
-                if (mi == null) {\r
-                    mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, 0/*e.buttons*/);\r
-                    miceInfo.put(e.mouseId, mi);\r
-                } else {\r
-                    deltaDistance = e.controlPosition.distance(mi.controlPosition);\r
-                    mi.controlPosition = e.controlPosition;\r
-                    mi.canvasPosition = canvasPosition;\r
-                }\r
-\r
-                if (deltaDistance > 0)\r
-                    mi.addDistanceForButtons(deltaDistance);\r
-\r
-                // Send mouse drag events.\r
-                for (ButtonInfo bi : mi.buttonPressInfo.values()) {\r
-                    if (!bi.down)\r
-                        continue;\r
-                    if (bi.deltaMotion <= profile.movementTolerance)\r
-                        continue;\r
-                    if (bi.drag)\r
-                        continue;\r
-                    bi.drag = true;\r
-                    MouseDragBegin db = new MouseDragBegin(this, e.time, e.mouseId, e.buttons, e.stateMask, bi.button,\r
-                            bi.canvasPosition, bi.controlPosition, e.controlPosition, e.screenPosition);\r
-                    getContext().getEventQueue().queueFirst(db);\r
-                }\r
-\r
-            } else if (e instanceof MouseButtonPressedEvent) {\r
-                Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);\r
-                MouseButtonPressedEvent me = (MouseButtonPressedEvent) e;\r
-                MouseInfo mi = miceInfo.get(e.mouseId);\r
-                if (mi == null) {\r
-                    mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons);\r
-                    miceInfo.put(e.mouseId, mi);\r
-                } else {\r
-                    mi.controlPosition = e.controlPosition;\r
-                    mi.canvasPosition = canvasPosition;\r
-                }\r
-                mi.setButtonPressed(me.button, e.stateMask, e.controlPosition, canvasPosition, e.time);\r
-            } else if (e instanceof MouseButtonReleasedEvent) {\r
-                MouseButtonReleasedEvent me = (MouseButtonReleasedEvent) e;\r
-                Point2D canvasPosition = util.controlToCanvas(me.controlPosition, null);\r
-                MouseInfo mi = miceInfo.get(me.mouseId);\r
-                if (mi == null) {\r
-                    mi = new MouseInfo(e.mouseId, me.controlPosition, canvasPosition, 0/*me.buttons*/);\r
-                    miceInfo.put(me.mouseId, mi);\r
-                } else {\r
-                    mi.controlPosition = me.controlPosition;\r
-                    mi.canvasPosition = canvasPosition;\r
-                }\r
-                ButtonInfo bi = mi.releaseButton(me.button, me.time);\r
-                if (bi == null)\r
-                    return false;\r
-\r
-                if (me.holdTime > profile.clickHoldTimeTolerance)\r
-                    return false;\r
-                if (bi.deltaMotion > profile.movementTolerance)\r
-                    return false;\r
-                // This is a click\r
-\r
-                long timeSinceLastClick = me.time - bi.lastClickEventTime;\r
-                bi.lastClickEventTime = me.time;\r
-\r
-                // reset click counter\r
-                if (timeSinceLastClick > profile.consecutiveToleranceTime)\r
-                    bi.clickCount = 0;\r
-                else\r
-                    bi.clickCount++;\r
-\r
-            }\r
-            return false;\r
-        }\r
-    }\r
-\r
-    public class ElementDiagramElementInteractor extends ElementInteractor {\r
-        @Override\r
-        @EventHandler(priority = INTERACTOR_PRIORITY)\r
-        public boolean handleMouseEvent(MouseEvent me) {\r
-            MouseEvent ne = createMouseEvent(me);\r
-            if (ne != null)\r
-                return super.handleMouseEvent(ne);\r
-            return false;\r
-        }\r
-    }\r
-\r
-    public MouseEvent createMouseEvent(MouseEvent e) {\r
-        MouseEvent newEvent = null;\r
-        double x = e.controlPosition.getX();\r
-        double y = e.controlPosition.getY();\r
-        Point2D newPos = new Point2D.Double(x - canvasPosX, y - canvasPosY);\r
-\r
-        if (!canvasRect.contains(newPos))\r
-            return new MouseExitEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos, e.screenPosition);\r
-        if (e instanceof MouseButtonPressedEvent) {\r
-            newEvent = new MouseButtonPressedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,\r
-                    ((MouseButtonEvent) e).button, newPos, e.screenPosition);\r
-        } else if (e instanceof MouseButtonReleasedEvent) {\r
-            newEvent = new MouseButtonReleasedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,\r
-                    ((MouseButtonEvent) e).button, ((MouseButtonReleasedEvent) e).holdTime, newPos, e.screenPosition);\r
-        } else if (e instanceof MouseDoubleClickedEvent) {\r
-            newEvent = new MouseDoubleClickedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,\r
-                    ((MouseButtonEvent) e).button, newPos, e.screenPosition);\r
-        } else if (e instanceof MouseClickEvent) {\r
-            newEvent = new MouseClickEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,\r
-                    ((MouseButtonEvent) e).button, ((MouseClickEvent) e).clickCount, newPos, e.screenPosition);\r
-        } else if (e instanceof MouseDragBegin) {\r
-            newEvent = new MouseDragBegin(e.context, e.time, e.mouseId, e.buttons, e.stateMask,\r
-                    ((MouseButtonEvent) e).button, ((MouseDragBegin) e).startCanvasPos,\r
-                    ((MouseDragBegin) e).startControlPos, newPos, e.screenPosition);\r
-        } else if (e instanceof MouseEnterEvent) {\r
-            newEvent = new MouseEnterEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,\r
-                    e.screenPosition);\r
-        } else if (e instanceof MouseExitEvent) {\r
-            newEvent = new MouseExitEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,\r
-                    e.screenPosition);\r
-        } else if (e instanceof MouseMovedEvent) {\r
-            newEvent = new MouseMovedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,\r
-                    e.screenPosition);\r
-        } else if (e instanceof MouseWheelMovedEvent) {\r
-            newEvent = new MouseWheelMovedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,\r
-                    e.screenPosition, ((MouseWheelMovedEvent) e).scrollType, ((MouseWheelMovedEvent) e).scrollAmount,\r
-                    ((MouseWheelMovedEvent) e).wheelRotation);\r
-        } else {\r
-            throw new Error("Unknow event " + e.getClass() + " " + e);\r
-        }\r
-        // System.out.println(newPos + " " + newEvent + " "+ e);\r
-        return newEvent;\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.g2d.diagram.impl;
+
+import java.awt.Color;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.simantics.g2d.canvas.Hints;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.canvas.ICanvasParticipant;
+import org.simantics.g2d.canvas.IContentContext;
+import org.simantics.g2d.canvas.IMouseCaptureContext;
+import org.simantics.g2d.canvas.IMouseCursorContext;
+import org.simantics.g2d.canvas.impl.MouseCaptureContext;
+import org.simantics.g2d.canvas.impl.MouseCursorContext;
+import org.simantics.g2d.canvas.impl.PaintableContextImpl;
+import org.simantics.g2d.chassis.ITooltipProvider;
+import org.simantics.g2d.diagram.DiagramClass;
+import org.simantics.g2d.diagram.DiagramHints;
+import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.diagram.participant.DiagramParticipant;
+import org.simantics.g2d.diagram.participant.ElementInteractor;
+import org.simantics.g2d.diagram.participant.ElementPainter;
+import org.simantics.g2d.diagram.participant.Selection;
+import org.simantics.g2d.diagram.participant.TerminalPainter;
+import org.simantics.g2d.diagram.participant.ZOrderHandler;
+import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.multileveldiagram.TransitionFunction;
+import org.simantics.g2d.multileveldiagram.ZoomTransitionParticipant;
+import org.simantics.g2d.participant.BackgroundPainter;
+import org.simantics.g2d.participant.CanvasGrab;
+import org.simantics.g2d.participant.GridPainter;
+import org.simantics.g2d.participant.HandPainter;
+import org.simantics.g2d.participant.KeyToCommand;
+import org.simantics.g2d.participant.KeyUtil;
+import org.simantics.g2d.participant.MouseUtil;
+import org.simantics.g2d.participant.PointerPainter;
+import org.simantics.g2d.participant.RulerPainter;
+import org.simantics.g2d.participant.SubCanvas;
+import org.simantics.g2d.participant.SymbolUtil;
+import org.simantics.g2d.participant.TimeParticipant;
+import org.simantics.g2d.participant.TransformUtil;
+import org.simantics.g2d.scenegraph.SceneGraphConstants;
+import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.G2DSceneGraph;
+import org.simantics.scenegraph.g2d.events.Event;
+import org.simantics.scenegraph.g2d.events.EventHandlerStack;
+import org.simantics.scenegraph.g2d.events.EventQueue;
+import org.simantics.scenegraph.g2d.events.IEventHandlerStack;
+import org.simantics.scenegraph.g2d.events.IEventQueue;
+import org.simantics.scenegraph.g2d.events.MouseEvent;
+import org.simantics.scenegraph.g2d.events.MouseEventCoalescer;
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
+import org.simantics.scenegraph.g2d.events.IEventQueue.IEventQueueListener;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDoubleClickedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseWheelMovedEvent;
+import org.simantics.scenegraph.g2d.events.command.CommandKeyBinding;
+import org.simantics.utils.datastructures.context.Context;
+import org.simantics.utils.datastructures.context.IContext;
+import org.simantics.utils.datastructures.context.IContextListener;
+import org.simantics.utils.datastructures.hints.HintContext;
+import org.simantics.utils.datastructures.hints.HintStack;
+import org.simantics.utils.datastructures.hints.IHintContext;
+import org.simantics.utils.datastructures.hints.IHintListener;
+import org.simantics.utils.datastructures.hints.IHintStack;
+import org.simantics.utils.threads.IThreadWorkQueue;
+import org.simantics.utils.threads.ThreadUtils;
+
+/**
+ * Creates subcanvas on top of parent canvas. Size and position of the subcanvas
+ * can be changed.
+ * 
+ * Beware: every CanvasParticipant that handles mouseEvents must be replaced
+ * since mouse coordinates must be translated. Currently Implemented
+ * participants: - MouseUtil - ElementInteractor
+ * 
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
+ * 
+ * TODO: CanvasContext implementation needs to implement scene graph methods too
+ */
+public class ElementDiagram implements IDiagram {
+    private final ICanvasContext ctx;
+
+    private final ICanvasContext parentCtx;
+
+    private IDiagram             diagram;
+
+    private final SubCanvas      subCanvas;
+
+    private Rectangle2D          canvasRect = new Rectangle2D.Double(0, 0, 200, 200);
+
+    private int                  canvasPosX = 100;
+
+    private int                  canvasPosY = 100;
+
+    public ElementDiagram(ICanvasContext parentCtx) {
+        this.parentCtx = parentCtx;
+        ctx = createCanvas(parentCtx.getThreadAccess(),parentCtx.getSceneGraph());
+        ICanvasParticipant mouseUtil = ctx.getSingleItem(MouseUtil.class);
+        if (mouseUtil != null) {
+            ctx.remove(mouseUtil);
+            ctx.add(new ElementDiagramMouseUtil());
+            // ctx.add(new MouseUtil());
+        }
+        ICanvasParticipant elementInteractor = ctx.getSingleItem(ElementInteractor.class);
+        if (elementInteractor != null) {
+            ctx.remove(elementInteractor);
+            ctx.add(new ElementDiagramElementInteractor());
+        }
+        diagram = createDiagram();
+        ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram);
+        subCanvas = new SubCanvas(ctx, 10000, Integer.MAX_VALUE - 100, 10000);
+        parentCtx.add(subCanvas);
+    }
+
+    public void setSize(int x, int y, int width, int height) {
+        canvasPosX = x;
+        canvasPosY = y;
+        canvasRect = new Rectangle2D.Double(0, 0, width, height);
+        getCanvas().getCanvasNode().setTransform(new AffineTransform(1,0,0,1,x,y));
+       
+    }
+
+    public Rectangle2D getSize() {
+        return canvasRect;
+    }
+
+    /**
+     * Override this for custom functionality
+     * 
+     * @return
+     */
+    public IDiagram createDiagram() {
+        IDiagram d = Diagram.spawnNew(DiagramClass.DEFAULT);
+        return d;
+    }
+
+    /**
+     * Override this for custom functionality
+     * 
+     * @param thread
+     * @return
+     */
+    public ICanvasContext createCanvas(IThreadWorkQueue thread, G2DSceneGraph sg) {
+        return createDefaultCanvas(thread,sg);
+    }
+
+    public void setDiagram(IDiagram diagram) {
+        this.diagram = diagram;
+        ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram);
+    }
+
+    public ICanvasContext getCanvas() {
+        return ctx;
+    }
+
+    public ICanvasContext getParentCanvas() {
+        return parentCtx;
+    }
+
+    public SubCanvas getSubCanvas() {
+        return subCanvas;
+    }
+
+    public IDiagram getDiagram() {
+        return diagram;
+    }
+
+    @Override
+    public void addElement(IElement element) {
+        diagram.addElement(element);
+    }
+
+    @Override
+    public void removeElement(IElement element) {
+        diagram.removeElement(element);
+    }
+
+    @Override
+    public void addCompositionListener(CompositionListener listener) {
+        diagram.addCompositionListener(listener);
+    }
+
+    @Override
+    public void addCompositionVetoListener(CompositionVetoListener listener) {
+        diagram.addCompositionVetoListener(listener);
+    }
+
+    @Override
+    public void addHintListener(IHintListener listener) {
+        diagram.addHintListener(listener);
+    }
+
+    @Override
+    public void addHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {
+        diagram.addHintListener(threadAccess, listener);
+    }
+
+    @Override
+    public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {
+        diagram.addKeyHintListener(threadAccess, key, listener);
+    }
+
+    @Override
+    public void addKeyHintListener(Key key, IHintListener listener) {
+        diagram.addKeyHintListener(key, listener);
+    }
+
+    @Override
+    public boolean bringToTop(IElement e) {
+        return diagram.bringToTop(e);
+    }
+
+    @Override
+    public boolean bringUp(IElement e) {
+        return diagram.bringUp(e);
+    }
+
+    @Override
+    public void destroy() {
+        diagram.destroy();
+    }
+
+    @Override
+    public void dispose() {
+        diagram.dispose();
+    }
+
+    @Override
+    public DiagramClass getDiagramClass() {
+        return diagram.getDiagramClass();
+    }
+
+    @Override
+    public boolean containsElement(IElement element) {
+        return diagram.containsElement(element);
+    }
+
+    @Override
+    public List<IElement> getElements() {
+        return diagram.getElements();
+    }
+
+    @Override
+    public void sort(Comparator<IElement> comparator) {
+        diagram.sort(comparator);
+    }
+
+    @Override
+    public List<IElement> getSnapshot() {
+        return diagram.getSnapshot();
+    }
+
+    @Override
+    public void clearWithoutNotification() {
+        diagram.clearWithoutNotification();
+    }
+
+    @Override
+    public boolean containsHint(Key key) {
+        return diagram.containsHint(key);
+    }
+
+    @Override
+    public <E> E getHint(Key key) {
+        return diagram.getHint(key);
+    }
+
+    @Override
+    public Map<Key, Object> getHints() {
+        return diagram.getHints();
+    }
+
+    @Override
+    public Map<Key, Object> getHintsUnsafe() {
+        return diagram.getHintsUnsafe();
+    }
+
+    @Override
+    public <E extends Key> Map<E, Object> getHintsOfClass(Class<E> clazz) {
+        return diagram.getHintsOfClass(clazz);
+    }
+
+    @Override
+    public boolean moveTo(IElement e, int position) {
+        return diagram.moveTo(e, position);
+    }
+
+    @Override
+    public void removeCompositionListener(CompositionListener listener) {
+        diagram.removeCompositionListener(listener);
+    }
+
+    @Override
+    public void removeCompositionVetoListener(CompositionVetoListener listener) {
+        diagram.removeCompositionVetoListener(listener);
+    }
+
+    @Override
+    public <E> E removeHint(Key key) {
+        return diagram.removeHint(key);
+    }
+
+    @Override
+    public void removeHintListener(IHintListener listener) {
+        diagram.removeHintListener(listener);
+    }
+
+    @Override
+    public void removeHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {
+        diagram.removeHintListener(threadAccess, listener);
+    }
+
+    @Override
+    public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {
+        diagram.removeKeyHintListener(threadAccess, key, listener);
+    }
+
+    @Override
+    public void removeKeyHintListener(Key key, IHintListener listener) {
+        diagram.removeKeyHintListener(key, listener);
+    }
+
+    @Override
+    public boolean sendDown(IElement e) {
+        return diagram.sendDown(e);
+    }
+
+    @Override
+    public boolean sendToBottom(IElement e) {
+        return diagram.sendToBottom(e);
+    }
+
+    @Override
+    public void setHint(Key key, Object value) {
+        diagram.setHint(key, value);
+    }
+
+    @Override
+    public void setHints(Map<Key, Object> hints) {
+        diagram.setHints(hints);
+    }
+
+    public ICanvasContext createDefaultCanvas(IThreadWorkQueue thread, G2DSceneGraph sg) {
+        // Create canvas context and a layer of interactors
+        ICanvasContext canvasContext = new ElementDiagramCanvasContext(thread,sg);
+        IHintContext h = canvasContext.getDefaultHintContext();
+
+        //canvasContext.add(new PanZoomRotateHandler()); // Must be before
+        // TransformUtil
+
+        // Support & Util Participants
+        canvasContext.add(new TransformUtil());
+        canvasContext.add(new MouseUtil());
+        canvasContext.add(new KeyUtil());
+        canvasContext.add(new CanvasGrab());
+        canvasContext.add(new SymbolUtil());
+        canvasContext.add(new TimeParticipant());
+
+        // Debug participant(s)
+        // canvasContext.add( new PointerPainter() );
+        canvasContext.add(new HandPainter());
+        h.setHint(PointerPainter.KEY_PAINT_POINTER, true);
+
+        // Pan & Zoom & Rotate
+        // canvasContext.add( new MousePanZoomInteractor() );
+        // canvasContext.add( new MultitouchPanZoomRotateInteractor() );
+        // canvasContext.add( new OrientationRestorer() );
+
+        // Grid & Ruler & Background
+        canvasContext.add(new GridPainter());
+        canvasContext.add(new RulerPainter());
+        canvasContext.add(new BackgroundPainter());
+        h.setHint(Hints.KEY_GRID_COLOR, new Color(0.9f, 0.9f, 0.9f));
+        h.setHint(Hints.KEY_BACKGROUND_COLOR, Color.LIGHT_GRAY);
+
+        // Key bindings
+        canvasContext.add(new KeyToCommand(CommandKeyBinding.DEFAULT_BINDINGS));
+
+        // //// Diagram Participants //////
+        canvasContext.add(new PointerInteractor());
+        canvasContext.add(new ElementInteractor());
+        canvasContext.add(new Selection());
+        canvasContext.add(new DiagramParticipant());
+        canvasContext.add(new ElementPainter());
+        canvasContext.add(new TerminalPainter(true, true, false, true));
+        //canvasContext.add(new ElementHeartbeater());
+        canvasContext.add(new ZOrderHandler());
+        canvasContext.add(new ZoomTransitionParticipant(TransitionFunction.SIGMOID));
+        h.setHint(Hints.KEY_TOOL, Hints.PANTOOL);
+
+        return canvasContext;
+    }
+
+    public class ElementDiagramCanvasContext extends Context<ICanvasParticipant> implements ICanvasContext {
+
+        protected HintStack              hintStack            = new HintStack();
+
+        protected HintContext            bottomHintContext    = new HintContext();
+
+        protected IEventHandlerStack     eventHandlerStack    = null;
+
+        protected boolean                eventHandlingOrdered = false;
+
+        protected EventQueue             eventQueue           = null;
+
+        protected IContentContext        paintableCtx         = new PaintableContextImpl();
+
+        protected final IThreadWorkQueue thread;
+
+        protected IMouseCaptureContext   mouseCaptureCtx      = new MouseCaptureContext();
+
+        protected IMouseCursorContext    mouseCursorCtx       = new MouseCursorContext();
+        
+        protected G2DSceneGraph             sg;
+        protected G2DParentNode          canvasNode           = null;
+        
+        protected ITooltipProvider       tooltip;
+
+        /**
+         * 
+         * @param thread context thread, or null if sync policy not used
+         */
+        public ElementDiagramCanvasContext(IThreadWorkQueue thread, G2DSceneGraph sg) {
+            super(ICanvasParticipant.class);
+            if (thread == null)
+                throw new IllegalArgumentException("null");
+            this.thread = thread;
+            eventHandlerStack = new EventHandlerStack(thread);
+            eventQueue = new EventQueue(eventHandlerStack);
+            hintStack.addHintContext(bottomHintContext, Integer.MIN_VALUE);
+
+            
+            
+            this.sg = sg;
+            canvasNode = sg.addNode("elementd" + SceneGraphConstants.NAVIGATION_NODE_NAME , G2DParentNode.class); // Add dummy parent node
+            canvasNode.setZIndex(1000);
+            
+            this.addContextListener(thread, new IContextListener<ICanvasParticipant>() {
+                @Override
+                public void itemAdded(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {
+                    item.addedToContext(ElementDiagramCanvasContext.this);
+                }
+
+                @Override
+                public void itemRemoved(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {
+                    item.removedFromContext(ElementDiagramCanvasContext.this);
+                }
+            });
+
+            eventQueue.addEventCoalesceler(MouseEventCoalescer.INSTANCE);
+            // Order event handling if events are added to the queue
+            eventQueue.addQueueListener(new IEventQueueListener() {
+                @Override
+                public void onEventAdded(IEventQueue queue, Event e, int index) {
+                    asyncHandleEvents();
+                }
+
+                @Override
+                public void onQueueEmpty(IEventQueue queue) {
+                }
+            });
+        }
+
+        public IHintStack getHintStack() {
+            assertNotDisposed();
+            return hintStack;
+        }
+
+        private final Runnable eventHandling = new Runnable() {
+            @Override
+            public void run() {
+                eventHandlingOrdered = false;
+                eventQueue.handleEvents();
+            }
+        };
+
+        synchronized void asyncHandleEvents() {
+            if (eventHandlingOrdered)
+                return;
+            eventHandlingOrdered = true;
+            ThreadUtils.asyncExec(thread, eventHandling);
+        }
+
+        synchronized void syncHandleEvents() {
+            if (eventHandlingOrdered)
+                return;
+            eventHandlingOrdered = true;
+            ThreadUtils.syncExec(thread, eventHandling);
+        }
+
+        @Override
+        public IEventHandlerStack getEventHandlerStack() {
+            assertNotDisposed();
+            return eventHandlerStack;
+        }
+
+        @Override
+        public IThreadWorkQueue getThreadAccess() {
+            // assertNotDisposed();
+            return thread;
+        }
+
+        @Override
+        protected void doDispose() {
+            ThreadUtils.syncExec(getThreadAccess(), new Runnable() {
+                @Override
+                public void run() {
+                    clear();
+                    
+                    // HN: added to decrease memory leaks
+                    if (sg != null) {
+                        // Makes sure that scene graph nodes free their resources!
+                        sg.removeNodes();
+                        sg = null;
+                    }
+                }
+            });
+        }
+
+        @Override
+        public IHintContext getDefaultHintContext() {
+            return bottomHintContext;
+        }
+
+        @Override
+        public IMouseCursorContext getMouseCursorContext() {
+            return mouseCursorCtx;
+        }
+
+        @Override
+        public void setMouseCursorContext(IMouseCursorContext ctx) {
+            this.mouseCursorCtx = ctx;
+        }
+
+        @Override
+        public IMouseCaptureContext getMouseCaptureContext() {
+            return mouseCaptureCtx;
+        }
+
+        @Override
+        public void setMouseCaptureContext(IMouseCaptureContext mctx) {
+            this.mouseCaptureCtx = mctx;
+        }
+
+        @Override
+        public IEventQueue getEventQueue() {
+            return eventQueue;
+        }
+
+        @Override
+        public IContentContext getContentContext() {
+            return paintableCtx;
+        }
+
+        @Override
+        public void setContentContext(IContentContext ctx) {
+            this.paintableCtx = ctx;
+        }
+
+        @Override
+        public G2DSceneGraph getSceneGraph() {
+            return sg;
+        }
+
+        @Override
+        public G2DParentNode getCanvasNode() {
+            return canvasNode;
+        }
+
+        @Override
+        public void setCanvasNode(G2DParentNode node) {
+            throw new RuntimeException("Cannot set canvasNode");
+
+        }
+
+        protected final AtomicBoolean locked = new AtomicBoolean(false);
+
+        @Override
+        public boolean isLocked() {
+            return this.locked.get();
+        }
+
+        @Override
+        public void setLocked(boolean locked) {
+            boolean previous = this.locked.getAndSet(locked);
+            if (!locked && previous != locked) {
+                // The context was unlocked!
+                getContentContext().setDirty();
+            }
+        }
+
+               @Override
+               public ITooltipProvider getTooltipProvider() {
+                       return tooltip;
+               }
+
+               @Override
+               public void setTooltipProvider(ITooltipProvider tooltipProvider) {
+                       this.tooltip = tooltipProvider;
+               }
+
+    }
+
+    public class ElementDiagramMouseUtil extends MouseUtil {
+
+        @Override
+        @EventHandler(priority = Integer.MAX_VALUE)
+        public boolean handleMouseEvent(MouseEvent e) {
+            MouseEvent ne = createMouseEvent(e);
+            if (ne != null)
+                return handleMouseEvent2(ne);
+            return false;
+        }
+
+        /**
+         * Copy-pasted MouseUtil.handleMouseEvent with one modification;
+         * Generating MouseClickEvents has been removed, because it created
+         * duplicated events (with incorrect coordinates).
+         * 
+         * @param e
+         * @return
+         */
+        public boolean handleMouseEvent2(MouseEvent e) {
+            assertDependencies();
+            if (e instanceof MouseEnterEvent) {
+                Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);
+                // Reset mouse
+                MouseInfo mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons);
+                miceInfo.put(e.mouseId, mi);
+            } else if (e instanceof MouseExitEvent) {
+                miceInfo.remove(e.mouseId);
+            } else if (e instanceof MouseMovedEvent) {
+                Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);
+                double deltaDistance = 0;
+                MouseInfo mi = miceInfo.get(e.mouseId);
+                if (mi == null) {
+                    mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, 0/*e.buttons*/);
+                    miceInfo.put(e.mouseId, mi);
+                } else {
+                    deltaDistance = e.controlPosition.distance(mi.controlPosition);
+                    mi.controlPosition = e.controlPosition;
+                    mi.canvasPosition = canvasPosition;
+                }
+
+                if (deltaDistance > 0)
+                    mi.addDistanceForButtons(deltaDistance);
+
+                // Send mouse drag events.
+                for (ButtonInfo bi : mi.buttonPressInfo.values()) {
+                    if (!bi.down)
+                        continue;
+                    if (bi.deltaMotion <= profile.movementTolerance)
+                        continue;
+                    if (bi.drag)
+                        continue;
+                    bi.drag = true;
+                    MouseDragBegin db = new MouseDragBegin(this, e.time, e.mouseId, e.buttons, e.stateMask, bi.button,
+                            bi.canvasPosition, bi.controlPosition, e.controlPosition, e.screenPosition);
+                    getContext().getEventQueue().queueFirst(db);
+                }
+
+            } else if (e instanceof MouseButtonPressedEvent) {
+                Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);
+                MouseButtonPressedEvent me = (MouseButtonPressedEvent) e;
+                MouseInfo mi = miceInfo.get(e.mouseId);
+                if (mi == null) {
+                    mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons);
+                    miceInfo.put(e.mouseId, mi);
+                } else {
+                    mi.controlPosition = e.controlPosition;
+                    mi.canvasPosition = canvasPosition;
+                }
+                mi.setButtonPressed(me.button, e.stateMask, e.controlPosition, canvasPosition, e.time);
+            } else if (e instanceof MouseButtonReleasedEvent) {
+                MouseButtonReleasedEvent me = (MouseButtonReleasedEvent) e;
+                Point2D canvasPosition = util.controlToCanvas(me.controlPosition, null);
+                MouseInfo mi = miceInfo.get(me.mouseId);
+                if (mi == null) {
+                    mi = new MouseInfo(e.mouseId, me.controlPosition, canvasPosition, 0/*me.buttons*/);
+                    miceInfo.put(me.mouseId, mi);
+                } else {
+                    mi.controlPosition = me.controlPosition;
+                    mi.canvasPosition = canvasPosition;
+                }
+                ButtonInfo bi = mi.releaseButton(me.button, me.time);
+                if (bi == null)
+                    return false;
+
+                if (me.holdTime > profile.clickHoldTimeTolerance)
+                    return false;
+                if (bi.deltaMotion > profile.movementTolerance)
+                    return false;
+                // This is a click
+
+                long timeSinceLastClick = me.time - bi.lastClickEventTime;
+                bi.lastClickEventTime = me.time;
+
+                // reset click counter
+                if (timeSinceLastClick > profile.consecutiveToleranceTime)
+                    bi.clickCount = 0;
+                else
+                    bi.clickCount++;
+
+            }
+            return false;
+        }
+    }
+
+    public class ElementDiagramElementInteractor extends ElementInteractor {
+        @Override
+        @EventHandler(priority = INTERACTOR_PRIORITY)
+        public boolean handleMouseEvent(MouseEvent me) {
+            MouseEvent ne = createMouseEvent(me);
+            if (ne != null)
+                return super.handleMouseEvent(ne);
+            return false;
+        }
+    }
+
+    public MouseEvent createMouseEvent(MouseEvent e) {
+        MouseEvent newEvent = null;
+        double x = e.controlPosition.getX();
+        double y = e.controlPosition.getY();
+        Point2D newPos = new Point2D.Double(x - canvasPosX, y - canvasPosY);
+
+        if (!canvasRect.contains(newPos))
+            return new MouseExitEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos, e.screenPosition);
+        if (e instanceof MouseButtonPressedEvent) {
+            newEvent = new MouseButtonPressedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,
+                    ((MouseButtonEvent) e).button, newPos, e.screenPosition);
+        } else if (e instanceof MouseButtonReleasedEvent) {
+            newEvent = new MouseButtonReleasedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,
+                    ((MouseButtonEvent) e).button, ((MouseButtonReleasedEvent) e).holdTime, newPos, e.screenPosition);
+        } else if (e instanceof MouseDoubleClickedEvent) {
+            newEvent = new MouseDoubleClickedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,
+                    ((MouseButtonEvent) e).button, newPos, e.screenPosition);
+        } else if (e instanceof MouseClickEvent) {
+            newEvent = new MouseClickEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,
+                    ((MouseButtonEvent) e).button, ((MouseClickEvent) e).clickCount, newPos, e.screenPosition);
+        } else if (e instanceof MouseDragBegin) {
+            newEvent = new MouseDragBegin(e.context, e.time, e.mouseId, e.buttons, e.stateMask,
+                    ((MouseButtonEvent) e).button, ((MouseDragBegin) e).startCanvasPos,
+                    ((MouseDragBegin) e).startControlPos, newPos, e.screenPosition);
+        } else if (e instanceof MouseEnterEvent) {
+            newEvent = new MouseEnterEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,
+                    e.screenPosition);
+        } else if (e instanceof MouseExitEvent) {
+            newEvent = new MouseExitEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,
+                    e.screenPosition);
+        } else if (e instanceof MouseMovedEvent) {
+            newEvent = new MouseMovedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,
+                    e.screenPosition);
+        } else if (e instanceof MouseWheelMovedEvent) {
+            newEvent = new MouseWheelMovedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,
+                    e.screenPosition, ((MouseWheelMovedEvent) e).scrollType, ((MouseWheelMovedEvent) e).scrollAmount,
+                    ((MouseWheelMovedEvent) e).wheelRotation);
+        } else {
+            throw new Error("Unknow event " + e.getClass() + " " + e);
+        }
+        // System.out.println(newPos + " " + newEvent + " "+ e);
+        return newEvent;
+    }
+
+}