]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/impl/ElementDiagram.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / diagram / impl / ElementDiagram.java
diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/impl/ElementDiagram.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/impl/ElementDiagram.java
new file mode 100644 (file)
index 0000000..b53e6da
--- /dev/null
@@ -0,0 +1,789 @@
+/*******************************************************************************\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