/******************************************************************************* * 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.participant; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.simantics.g2d.canvas.IMouseCaptureContext; import org.simantics.g2d.canvas.IMouseCaptureHandle; import org.simantics.g2d.canvas.IMouseCaptureHandleListener; import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; import org.simantics.g2d.diagram.handler.PickContext; import org.simantics.g2d.diagram.handler.PickRequest; import org.simantics.g2d.diagram.handler.PickRequest.PickSorter; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.handler.HandleMouseEvent; import org.simantics.g2d.participant.TransformUtil; import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler; import org.simantics.scenegraph.g2d.events.MouseEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent; /** * This participant sends mouse events to elements that have a {@link HandleMouseEvent} * handler. * * @see HandleMouseEvent * @author Toni Kalajainen */ public class ElementInteractor extends AbstractDiagramParticipant { @Dependency PickContext pick; @Dependency TransformUtil util; private PickSorter pickSorter; public static final int INTERACTOR_PRIORITY = Integer.MAX_VALUE-1; Map mouseCaptureMap = new HashMap(); Map mouseFocus = new HashMap(); public ElementInteractor() { } public ElementInteractor(PickSorter pickSorter) { this.pickSorter = pickSorter; } private class ElementMouseCaptureHandle implements IMouseCaptureHandle { IMouseCaptureHandle origHandle; IElement element; @Override public void addMouseCaptureHandleListener(IMouseCaptureHandleListener listener) { origHandle.addMouseCaptureHandleListener(listener); } @Override public int mouseId() { return origHandle.mouseId(); } @Override public void release() { mouseCaptureMap.remove(origHandle.mouseId()); origHandle.release(); } @Override public void removeMouseCaptureHandleListener(IMouseCaptureHandleListener listener) { origHandle.removeMouseCaptureHandleListener(listener); } } /** * Capture mouse events to an element. * @param element * @param mouseId * @return capture handle or null if capture is not available in the context */ public IMouseCaptureHandle captureMouse(IElement element, int mouseId) { // Release old capture ElementMouseCaptureHandle prevHnd = mouseCaptureMap.get(mouseId); if (prevHnd!=null) { prevHnd.release(); mouseCaptureMap.remove(mouseId); } // Check that capture is available IMouseCaptureContext mcc = getContext().getMouseCaptureContext(); if (mcc==null) return null; // make new capture ElementMouseCaptureHandle hnd = new ElementMouseCaptureHandle(); hnd.origHandle = mcc.captureMouse(mouseId); hnd.element = element; mouseCaptureMap.put(mouseId, hnd); return hnd; } /** * Get all grabs of an element * @param e element * @return */ public Collection getGrabsOfElement(IElement e) { List result = new ArrayList(); for (ElementMouseCaptureHandle eh : mouseCaptureMap.values()) if (eh.element == e) result.add(eh); return result; } public IMouseCaptureHandle getGrabOfElement(IElement e, int mouseId) { for (ElementMouseCaptureHandle eh : mouseCaptureMap.values()) if (eh.element == e && eh.mouseId() == mouseId) return eh; return null; } @EventHandler(priority = INTERACTOR_PRIORITY) public boolean handleMouseEvent(MouseEvent me) { assertDependencies(); // Determine element for capture IElement currentFocus = null; ElementMouseCaptureHandle hnd = mouseCaptureMap.get(me.mouseId); // Mouse is captured if (hnd != null) { currentFocus = hnd.element; //System.out.println("capture: " + hnd); } else { // Pick element under the mouse Point2D controlPos = me.controlPosition; Point2D diagramPos = util.controlToCanvas(controlPos, null); PickRequest req = new PickRequest(diagramPos).context(getContext()); req.pickSorter = pickSorter; //req.pickSorter = PickRequest.PickSorter.CONNECTIONS_LAST; ArrayList result = new ArrayList(); pick.pick(diagram, req, result); if (result.size()>0) { _sortByOrder(result); currentFocus = result.get(result.size()-1); //System.out.println("Focus " + currentFocus + " " + result.size()); } } // Send enter & exit events to elements IElement prevFocus = mouseFocus.get(me.mouseId); // Focus has changed if (currentFocus!=prevFocus) { if (prevFocus!=null) { MouseExitEvent exit = new MouseExitEvent(getContext(), me.time, me.mouseId, me.buttons, me.stateMask, me.controlPosition, me.screenPosition); sendElementMouseEvent(prevFocus, exit); } if (currentFocus!=null) { MouseEnterEvent enter = new MouseEnterEvent(getContext(), me.time, me.mouseId, me.buttons, me.stateMask, me.controlPosition, me.screenPosition); sendElementMouseEvent(currentFocus, enter); } } mouseFocus.put(me.mouseId, currentFocus); // Send event to all handlers if (currentFocus==null) return false; //return sendElementMouseEvent(currentFocus, me); sendElementMouseEvent(currentFocus, me); return false; } private boolean sendElementMouseEvent(IElement e, MouseEvent me) { //System.out.println("sendElementMouseEvent(" + e + ", " + me + ")"); // FIXME: eating events here will cause DND to not work. Workaround is to not eat the events. Need proper fix. for (HandleMouseEvent eh : e.getElementClass().getItemsByClass(HandleMouseEvent.class)) { if (eh.handleMouseEvent(e, getContext(), me)) return false; } return false; } // copy-paste from ZOrderhandler (list is reverse, so element on top is first) void _sortByOrder(List list) { List elements = diagram.getElements(); final Map position = new HashMap(); for (IElement e : list) position.put(e, elements.indexOf(e)); Comparator c = new Comparator() { @Override public int compare(IElement o1, IElement o2) { int pos1 = position.get(o1); int pos2 = position.get(o2); return pos1-pos2; } }; Collections.sort(list, c); } }