]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementInteractor.java
Performance and resource consumption optimization for G2D picking
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / diagram / participant / ElementInteractor.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.g2d.diagram.participant;
13
14 import java.awt.geom.Point2D;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 import org.simantics.g2d.canvas.IMouseCaptureContext;
24 import org.simantics.g2d.canvas.IMouseCaptureHandle;
25 import org.simantics.g2d.canvas.IMouseCaptureHandleListener;
26 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
27 import org.simantics.g2d.diagram.handler.PickContext;
28 import org.simantics.g2d.diagram.handler.PickRequest;
29 import org.simantics.g2d.diagram.handler.PickRequest.PickSorter;
30 import org.simantics.g2d.element.IElement;
31 import org.simantics.g2d.element.handler.HandleMouseEvent;
32 import org.simantics.g2d.participant.TransformUtil;
33 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
34 import org.simantics.scenegraph.g2d.events.MouseEvent;
35 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent;
36 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;
37
38 /**
39  * This participant sends mouse events to elements that have a {@link HandleMouseEvent}
40  * handler.
41  * 
42  * @see HandleMouseEvent
43  * @author Toni Kalajainen
44  */
45 public class ElementInteractor extends AbstractDiagramParticipant {
46
47     @Dependency PickContext pick;
48     @Dependency TransformUtil util;
49
50     private PickSorter pickSorter;
51     
52     public static final int INTERACTOR_PRIORITY = Integer.MAX_VALUE-1;
53
54     Map<Integer, ElementMouseCaptureHandle> mouseCaptureMap =
55         new HashMap<Integer, ElementMouseCaptureHandle>();
56     Map<Integer, IElement> mouseFocus = new HashMap<Integer, IElement>();
57
58     public ElementInteractor() {
59     }
60
61     public ElementInteractor(PickSorter pickSorter) {
62         this.pickSorter = pickSorter;
63     }
64
65     private class ElementMouseCaptureHandle implements IMouseCaptureHandle {
66         IMouseCaptureHandle origHandle;
67         IElement element;
68         @Override
69         public void addMouseCaptureHandleListener(IMouseCaptureHandleListener listener) {
70             origHandle.addMouseCaptureHandleListener(listener);
71         }
72         @Override
73         public int mouseId() {
74             return origHandle.mouseId();
75         }
76         @Override
77         public void release() {
78             mouseCaptureMap.remove(origHandle.mouseId());
79             origHandle.release();
80         }
81         @Override
82         public void removeMouseCaptureHandleListener(IMouseCaptureHandleListener listener) {
83             origHandle.removeMouseCaptureHandleListener(listener);
84         }
85     }
86
87     /**
88      * Capture mouse events to an element.
89      * @param element
90      * @param mouseId
91      * @return capture handle or null if capture is not available in the context
92      */
93     public IMouseCaptureHandle captureMouse(IElement element, int mouseId)
94     {
95         // Release old capture
96         ElementMouseCaptureHandle prevHnd = mouseCaptureMap.get(mouseId);
97         if (prevHnd!=null) {
98             prevHnd.release();
99             mouseCaptureMap.remove(mouseId);
100         }
101
102         // Check that capture is available
103         IMouseCaptureContext mcc = getContext().getMouseCaptureContext();
104         if (mcc==null) return null;
105
106         // make new capture
107         ElementMouseCaptureHandle hnd = new ElementMouseCaptureHandle();
108         hnd.origHandle = mcc.captureMouse(mouseId);
109         hnd.element = element;
110         mouseCaptureMap.put(mouseId, hnd);
111         return hnd;
112     }
113
114     /**
115      * Get all grabs of an element
116      * @param e element
117      * @return
118      */
119     public Collection<IMouseCaptureHandle> getGrabsOfElement(IElement e)
120     {
121         List<IMouseCaptureHandle> result = new ArrayList<IMouseCaptureHandle>();
122         for (ElementMouseCaptureHandle eh : mouseCaptureMap.values())
123             if (eh.element == e)
124                 result.add(eh);
125         return result;
126     }
127
128     public IMouseCaptureHandle getGrabOfElement(IElement e, int mouseId)
129     {
130         for (ElementMouseCaptureHandle eh : mouseCaptureMap.values())
131             if (eh.element == e && eh.mouseId() == mouseId)
132                 return eh;
133         return null;
134     }
135
136     @EventHandler(priority = INTERACTOR_PRIORITY)
137     public boolean handleMouseEvent(MouseEvent me) {
138         assertDependencies();
139
140         // Determine element for capture
141         IElement currentFocus = null;
142         ElementMouseCaptureHandle hnd = mouseCaptureMap.get(me.mouseId);
143         // Mouse is captured
144         if (hnd != null) {
145             currentFocus = hnd.element;
146             //System.out.println("capture: " + hnd);
147         } else {
148             // Pick element under the mouse
149             Point2D controlPos = me.controlPosition;
150             Point2D diagramPos = util.controlToCanvas(controlPos, null);
151             PickRequest req = new PickRequest(diagramPos).context(getContext());
152             req.pickSorter = pickSorter;
153             //req.pickSorter = PickRequest.PickSorter.CONNECTIONS_LAST;
154             ArrayList<IElement> result = new ArrayList<IElement>();
155             pick.pick(diagram, req, result);
156             if (result.size()>0) {
157                 _sortByOrder(result);
158                 currentFocus = result.get(result.size()-1);
159                 //System.out.println("Focus " + currentFocus +  " " + result.size());
160             }
161
162         }
163
164         // Send enter & exit events to elements
165         IElement prevFocus = mouseFocus.get(me.mouseId);
166         // Focus has changed
167         if (currentFocus!=prevFocus) {
168             if (prevFocus!=null) {
169                 MouseExitEvent exit = new MouseExitEvent(getContext(), me.time, me.mouseId, me.buttons, me.stateMask, me.controlPosition, me.screenPosition);
170                 sendElementMouseEvent(prevFocus, exit);
171             }
172             if (currentFocus!=null) {
173                 MouseEnterEvent enter = new MouseEnterEvent(getContext(), me.time, me.mouseId, me.buttons, me.stateMask, me.controlPosition, me.screenPosition);
174                 sendElementMouseEvent(currentFocus, enter);
175             }
176         }
177         mouseFocus.put(me.mouseId, currentFocus);
178
179         // Send event to all handlers
180         if (currentFocus==null) return false;
181         //return sendElementMouseEvent(currentFocus, me);
182         sendElementMouseEvent(currentFocus, me);
183         return false;
184     }
185
186     private boolean sendElementMouseEvent(IElement e, MouseEvent me)
187     {
188         //System.out.println("sendElementMouseEvent(" + e + ", " + me + ")");
189         // FIXME: eating events here will cause DND to not work. Workaround is to not eat the events. Need proper fix.
190         for (HandleMouseEvent eh : e.getElementClass().getItemsByClass(HandleMouseEvent.class))
191         {
192             if (eh.handleMouseEvent(e, getContext(), me)) return false;
193         }
194         return false;
195     }
196
197     // copy-paste from ZOrderhandler (list is reverse, so element on top is first)
198     void _sortByOrder(List<IElement> list)
199     {
200         List<IElement> elements = diagram.getElements();
201         final Map<IElement, Integer> position = new HashMap<IElement, Integer>();
202         for (IElement e : list)
203             position.put(e, elements.indexOf(e));
204         Comparator<IElement> c = new Comparator<IElement>() {
205             @Override
206             public int compare(IElement o1, IElement o2) {
207                 int pos1 = position.get(o1);
208                 int pos2 = position.get(o2);
209                 return pos1-pos2;
210             }
211         };
212         Collections.sort(list, c);
213     }
214
215 }