--- /dev/null
+/*******************************************************************************\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.element.handler.impl;\r
+\r
+import java.awt.geom.Point2D;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.Map.Entry;\r
+\r
+import org.simantics.g2d.canvas.ICanvasContext;\r
+import org.simantics.g2d.canvas.IMouseCaptureHandle;\r
+import org.simantics.g2d.diagram.participant.DiagramParticipant;\r
+import org.simantics.g2d.diagram.participant.ElementInteractor;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.element.handler.HandleMouseEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent;\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.MouseDragBegin;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;\r
+import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
+import org.simantics.utils.datastructures.hints.IHintContext.MouseSpecificKeyOf;\r
+\r
+/**\r
+ * Base implementation for handlers that handle grabbable objects.\r
+ * \r
+ * TODO Esc button cancels grab\r
+ * \r
+ * @author Toni Kalajainen\r
+ */\r
+public abstract class AbstractGrabbable implements HandleMouseEvent {\r
+\r
+ private static final long serialVersionUID = -3620527648364111724L;\r
+ Double strayDistance;\r
+ protected int grabMouseButton = MouseEvent.LEFT_BUTTON;\r
+ \r
+ /**\r
+ * Create grabbable\r
+ * @param strayDistance stray distance or null for no limit\r
+ */\r
+ public AbstractGrabbable(Double strayDistance)\r
+ {\r
+ this.strayDistance = strayDistance;\r
+ }\r
+\r
+ public AbstractGrabbable()\r
+ {\r
+ this.strayDistance = 1000.0;\r
+ }\r
+ \r
+ @Override\r
+ public boolean handleMouseEvent(IElement e, ICanvasContext ctx, MouseEvent me) {\r
+ int pointerId = me.mouseId;\r
+ GrabInfo gi = getGrabInfo(e, ctx, pointerId);\r
+ \r
+ if ((me instanceof MouseClickEvent)||(me instanceof MouseDragBegin)) {\r
+ return gi!=null; \r
+ }\r
+ \r
+ if ((me instanceof MouseMovedEvent)) {\r
+ MouseMovedEvent mme = (MouseMovedEvent) me;\r
+ if (gi==null) return false;\r
+\r
+ gi.prevPosCanvas.setLocation( gi.dragPosCanvas );\r
+ gi.prevPosControl.setLocation( gi.dragPosControl );\r
+ gi.prevPosElement.setLocation( gi.dragPosElement );\r
+ \r
+ // Update drag positions\r
+ gi.dragPosControl.setLocation(me.controlPosition);\r
+ ElementUtils.controlToCanvasCoordinate(ctx, mme.controlPosition, gi.dragPosCanvas); \r
+ ElementUtils.controlToElementCoordinate(e, ctx, mme.controlPosition, gi.dragPosElement);\r
+ // Check if pointer has strayed too far, if so release grab\r
+ double dist = gi.dragPosControl.distance(gi.grabPosControl); \r
+\r
+ // Pointer has strayed too far -> release the pointer from the button\r
+ if (dist>strayDistance)\r
+ {\r
+ releaseGrab(e, ctx, pointerId);\r
+ onGrabCancel(gi, ctx);\r
+ return true;\r
+ }\r
+\r
+ // Handle event\r
+ onDrag(gi, ctx);\r
+ // Consume event\r
+ return true;\r
+ }\r
+ \r
+ // Mouse released\r
+ if (me instanceof MouseButtonReleasedEvent)\r
+ {\r
+ MouseButtonEvent mbe = (MouseButtonEvent) me;\r
+ if (mbe.button != grabMouseButton) return false;\r
+ if (gi==null) return false; \r
+ releaseGrab(e, ctx, pointerId);\r
+ onRelease(gi, ctx);\r
+ return true;\r
+ }\r
+ // Mouse pressed\r
+ if (me instanceof MouseButtonPressedEvent)\r
+ {\r
+ MouseButtonEvent mbe = (MouseButtonEvent) me;\r
+ if (mbe.button != grabMouseButton) return false;\r
+ if (!onGrabCheck(e, ctx, pointerId, me.controlPosition))\r
+ return false;\r
+ gi = grabMouse(e, ctx, pointerId, me.controlPosition);\r
+ onGrab(gi,ctx);\r
+ return true;\r
+ }\r
+ \r
+ return false;\r
+ }\r
+ \r
+ /**\r
+ * Checks whether grab accepted\r
+ * @param e element\r
+ * @param pickPos pick position in element coordinates \r
+ * @return true if position is grabbable\r
+ */\r
+ protected abstract boolean onGrabCheck(IElement e, ICanvasContext ctx, int pointerId, Point2D pickPos);\r
+ \r
+ protected abstract void onGrab(GrabInfo gi, ICanvasContext ctx);\r
+\r
+ /**\r
+ * Event when grab is released (and not canceled)\r
+ * Invoked after grab is released.\r
+ * @param gi\r
+ * @param ctx\r
+ */\r
+ protected abstract void onRelease(GrabInfo gi, ICanvasContext ctx);\r
+ \r
+ protected abstract void onDrag(GrabInfo gi, ICanvasContext ctx);\r
+ \r
+ /**\r
+ * Event notified when grab is canceled. Canceling occurs automatically\r
+ * when mouse strays too far and when user sends cancel command (presses esc) \r
+ * \r
+ * @param e\r
+ * @param grabPos\r
+ */\r
+ protected abstract void onGrabCancel(GrabInfo gi, ICanvasContext ctx);\r
+ \r
+ protected void cancelGrab() { \r
+ }\r
+ \r
+ public static class GrabInfo {\r
+ // Grabbed element\r
+ public IElement e;\r
+ // grabbing pointer\r
+ public int pointerId;\r
+ // Grab position\r
+ public Point2D grabPosControl = new Point2D.Double();\r
+ public Point2D grabPosCanvas = new Point2D.Double();\r
+ public Point2D grabPosElement = new Point2D.Double();\r
+ // Drag position\r
+ public Point2D dragPosControl = new Point2D.Double(); \r
+ public Point2D dragPosCanvas = new Point2D.Double();\r
+ public Point2D dragPosElement = new Point2D.Double();\r
+ // Prev position\r
+ public Point2D prevPosControl = new Point2D.Double(); \r
+ public Point2D prevPosCanvas = new Point2D.Double();\r
+ public Point2D prevPosElement = new Point2D.Double();\r
+ // Capture handle\r
+ private IMouseCaptureHandle hnd;\r
+ } \r
+\r
+ /**\r
+ * Remove GrabInfo object\r
+ * @param e\r
+ * @param ctx\r
+ * @param pointerId\r
+ */\r
+ private void removeGrabInfo(IElement e, ICanvasContext ctx, int pointerId)\r
+ {\r
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
+ Key key = new MouseSpecificKeyOf(pointerId, GrabInfo.class);\r
+ dp.removeElementHint(e, key);\r
+ }\r
+ \r
+ /**\r
+ * Set GrabInfo object\r
+ * @param e\r
+ * @param ctx\r
+ * @param pointerId\r
+ * @param gi\r
+ */\r
+ private void setGrabInfo(IElement e, ICanvasContext ctx, int pointerId, GrabInfo gi)\r
+ {\r
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
+ Key key = new MouseSpecificKeyOf(pointerId, GrabInfo.class);\r
+ dp.setElementHint(e, key, gi);\r
+ \r
+ }\r
+ \r
+ /**\r
+ * Get GrabInfo Object\r
+ * @param e\r
+ * @param ctx\r
+ * @param pointerId\r
+ * @return\r
+ */\r
+ private GrabInfo getGrabInfo(IElement e, ICanvasContext ctx, int pointerId)\r
+ {\r
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
+ Key key = new MouseSpecificKeyOf(pointerId, GrabInfo.class); \r
+ return dp.getElementHint(e, key);\r
+ }\r
+ \r
+ /**\r
+ * Get all pointer grabs\r
+ * @return\r
+ */\r
+ protected Map<Integer, GrabInfo> getGrabs(IElement e, ICanvasContext ctx)\r
+ {\r
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
+ Map<Key, Object> map = dp.getElementHints(e);\r
+ if (map==null) return null;\r
+ Map<Integer, GrabInfo> result = new HashMap<Integer, GrabInfo>();\r
+ for (Entry<Key, Object> entry : map.entrySet())\r
+ {\r
+ if (!(entry.getValue() instanceof GrabInfo)) continue;\r
+ GrabInfo gi = (GrabInfo) entry.getValue();\r
+ result.put(gi.pointerId, gi);\r
+ }\r
+ return result;\r
+ } \r
+ \r
+ protected int getGrabCount(IElement e, ICanvasContext ctx)\r
+ {\r
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
+ Map<Key, Object> map = dp.getElementHints(e);\r
+ if (map==null) return 0;\r
+ int result = 0;\r
+ for (Entry<Key, Object> entry : map.entrySet())\r
+ {\r
+ if (!(entry.getValue() instanceof GrabInfo)) continue;\r
+ result ++;\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ /**\r
+ * Release grab of a pointer\r
+ * @param e\r
+ * @param ctx\r
+ * @param pointerId\r
+ */\r
+ protected void releaseGrab(IElement e, ICanvasContext ctx, int pointerId)\r
+ {\r
+ GrabInfo gi = getGrabInfo(e, ctx, pointerId);\r
+ if (gi==null) return;\r
+ gi.hnd.release();\r
+ removeGrabInfo(e, ctx, pointerId);\r
+ }\r
+ \r
+ /**\r
+ * Grab a pointer\r
+ * @param e\r
+ * @param ctx\r
+ * @param pointerId\r
+ * @param grabPointControl\r
+ * @return\r
+ */\r
+ private GrabInfo grabMouse(IElement e, ICanvasContext ctx, int pointerId, Point2D grabPointControl)\r
+ {\r
+ // Release previous capture, if exists\r
+ releaseGrab(e, ctx, pointerId); \r
+ \r
+ ElementInteractor ei = ctx.getSingleItem(ElementInteractor.class);\r
+ IMouseCaptureHandle hnd = ei.captureMouse(e, pointerId);\r
+ if (hnd == null) return null;\r
+\r
+ Point2D grabPointCanvas = ElementUtils.controlToCanvasCoordinate(ctx, grabPointControl, new Point2D.Double());\r
+ Point2D grabPointElement = ElementUtils.controlToElementCoordinate(e, ctx, grabPointControl, new Point2D.Double());\r
+ \r
+ GrabInfo gi = new GrabInfo();\r
+ gi.e = e;\r
+ gi.hnd = hnd;\r
+ gi.pointerId = pointerId;\r
+ gi.grabPosControl.setLocation(grabPointControl);\r
+ gi.grabPosCanvas.setLocation(grabPointCanvas);\r
+ gi.grabPosElement.setLocation(grabPointElement);\r
+ \r
+ gi.dragPosControl.setLocation(grabPointControl);\r
+ gi.dragPosCanvas.setLocation(grabPointCanvas);\r
+ gi.dragPosElement.setLocation(grabPointElement);\r
+ \r
+ gi.prevPosControl.setLocation(grabPointControl);\r
+ gi.prevPosCanvas.setLocation(grabPointCanvas);\r
+ gi.prevPosElement.setLocation(grabPointElement);\r
+ \r
+ setGrabInfo(e, ctx, pointerId, gi);\r
+ \r
+ return gi;\r
+ } \r
+\r
+}\r