-/*******************************************************************************\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;
+ }
+
+}