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