]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/NodeEventHandler.java
Merge "Minor refactorings related to SCL constructors"
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / events / NodeEventHandler.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
3  * 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.scenegraph.g2d.events;
13
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Comparator;
17 import java.util.List;
18
19 import org.simantics.scenegraph.INode;
20 import org.simantics.scenegraph.g2d.G2DFocusManager;
21 import org.simantics.scenegraph.g2d.G2DSceneGraph;
22 import org.simantics.scenegraph.g2d.IG2DNode;
23 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
24 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
25 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
30  * Delivers events (mouse, key, focus, command, time) to scene graph nodes that
31  * have registered to receive them.
32  * 
33  * @author Tuukka Lehtonen
34  */
35 public class NodeEventHandler implements IEventHandler {
36
37     private static final Logger LOGGER = LoggerFactory.getLogger(NodeEventHandler.class);
38
39     private static final boolean DEBUG_EVENTS       = false;
40     private static final boolean DEBUG_HANDLER_SORT = false;
41
42     private static final IEventHandler[] NONE = {};
43
44     public static class TreePreOrderComparator implements Comparator<IEventHandler> {
45
46         static enum Order {
47             ASCENDING,
48             DESCENDING
49         }
50
51         static class Temp {
52             ArrayList<INode> path1 = new ArrayList<INode>();
53             ArrayList<INode> path2 = new ArrayList<INode>();
54         }
55
56         private transient ThreadLocal<Temp> temp = new ThreadLocal<Temp>() {
57                 protected Temp initialValue() {
58                         return new Temp();
59                 }
60         };
61
62         Order order;
63
64         public TreePreOrderComparator(Order order) {
65             this.order = order;
66         }
67
68         void getTreePath(INode node, ArrayList<INode> result) {
69              result.clear();
70              for (; node != null; node = node.getParent())
71                  result.add(node);
72         }
73
74         void notSameGraph(INode o1, INode o2) {
75             throw new IllegalStateException("nodes " + o1 + " and " + o2
76                     + " not part of same scene graph.\n\t root 1: " + o1.getRootNode() + "\n\troot 2: "
77                     + o2.getRootNode());
78         }
79
80         @Override
81         public int compare(IEventHandler e1, IEventHandler e2) {
82             if (e1 == e2)
83                 return 0;
84
85             Temp tmp = temp.get();
86             ArrayList<INode> path1 = tmp.path1;
87             ArrayList<INode> path2 = tmp.path2;
88
89             try {
90                 // Get path to root node for both nodes
91                 getTreePath((INode) e1, path1);
92                 getTreePath((INode) e2, path2);
93
94                 // Sanity checks: nodes part of same scene graph
95                 if (path1.get(path1.size() - 1) != path2.get(path2.size() - 1))
96                         notSameGraph((INode)e1, (INode)e2);
97
98                 // Find first non-matching nodes in the paths starting from the root node
99                 int i1 = path1.size() - 1;
100                 int i2 = path2.size() - 1;
101                 for (; i1 >= 0 && i2 >= 0; --i1, --i2) {
102                     INode p1 = path1.get(i1);
103                     INode p2 = path2.get(i2);
104                     if (p1 != p2) {
105                         break;
106                     }
107                 }
108
109                 // Pre-order: a node that is on the tree path of another node is first
110                 if (i1 < 0)
111                     return Order.ASCENDING == order ? -1 : 1;
112                 if (i2 < 0)
113                     return Order.ASCENDING == order ? 1 : -1;
114
115                 return compare(path1.get(i1), path2.get(i2));
116             } finally {
117                 // Don't hold on to objects unnecessarily
118                 path1.clear();
119                 path2.clear();
120             }
121         }
122         
123         private int compare(INode n1, INode n2) {
124                 if(n1 instanceof IG2DNode) {
125                         if(n2 instanceof IG2DNode) {
126                         int z1 = ((IG2DNode)n1).getZIndex();
127                         int z2 = ((IG2DNode)n2).getZIndex();
128                         int c = Integer.compare(z1, z2);
129                         return order == Order.ASCENDING ? c : -c;
130                         }
131                         else
132                                 return -1; // sort IG2DNodes before non-IG2DNodes
133             }
134                 else {
135                         if(n2 instanceof IG2DNode)
136                                 return 1;
137                         else
138                                 return 0; // all non-IG2DNodes are equal in comparison
139                 }
140         }
141     };
142
143     TreePreOrderComparator COMPARATOR = new TreePreOrderComparator(TreePreOrderComparator.Order.DESCENDING);
144
145     /**
146      * {@link FocusEvent} are propagated first to the scene graph focus node,
147      * then to event handler nodes in scene graph tree pre-order.
148      */
149     protected List<IEventHandler>         focusListeners         = new ArrayList<IEventHandler>();
150     protected IEventHandler[]             sortedFocusListeners   = null;
151
152     /**
153      * {@link TimeEvent} are propagated first to the scene graph focus node,
154      * then to event handler nodes in scene graph tree pre-order.
155      */
156     protected List<IEventHandler>         timeListeners          = new ArrayList<IEventHandler>();
157     protected IEventHandler[]             sortedTimeListeners    = null;
158
159     /**
160      * {@link CommandEvent} are propagated first to the scene graph focus node,
161      * then to event handler nodes in scene graph tree pre-order.
162      */
163     protected List<IEventHandler>         commandListeners       = new ArrayList<IEventHandler>();
164     protected IEventHandler[]             sortedCommandListeners = null;
165
166     /**
167      * {@link KeyEvent} are propagated first to the scene graph focus node, then
168      * to event handler nodes in scene graph tree pre-order.
169      */
170     protected List<IEventHandler>         keyListeners           = new ArrayList<IEventHandler>();
171     protected IEventHandler[]             sortedKeyListeners     = null;
172
173     /**
174      * {@link MouseEvent} are propagated first to the scene graph focus node,
175      * then to event handler nodes in scene graph tree pre-order.
176      */
177     protected List<IEventHandler>         mouseListeners         = new ArrayList<IEventHandler>();
178     protected IEventHandler[]             sortedMouseListeners   = null;
179
180     /**
181      * {@link MouseDragBegin} events are propagated first to the scene graph focus node, then
182      * to event handler nodes in scene graph tree pre-order.
183      */
184     protected List<IEventHandler>         mouseDragBeginListeners = new ArrayList<IEventHandler>();
185     protected IEventHandler[]             sortedMouseDragBeginListeners = null;
186
187     /**
188      * The scene graph this instance handles event propagation for.
189      */
190     protected G2DSceneGraph               sg;
191
192     public NodeEventHandler(G2DSceneGraph sg) {
193         this.sg = sg;
194     }
195
196     @SuppressWarnings("unused")
197     private IEventHandler[] sort(IEventHandler[] sort) {
198         if (DEBUG_HANDLER_SORT)
199             debug("copy sort " + sort.length + " handlers");
200         return sortInplace(Arrays.copyOf(sort, sort.length));
201     }
202
203     private IEventHandler[] sortInplace(IEventHandler[] sort) {
204         if (DEBUG_HANDLER_SORT)
205             debug("in-place sort " + sort.length + " handlers");
206         Arrays.sort(sort, COMPARATOR);
207         return sort;
208     }
209
210     public boolean mousePressed(MouseButtonPressedEvent event) {
211         G2DFocusManager.INSTANCE.clearFocus();
212         try {
213 //        Point op = event.getPoint();
214 //        for (MouseListener l : mouseListeners.getListeners()) {
215 //            MouseEvent e = (MouseEvent) NodeUtil.transformEvent(event,(IG2DNode) l);
216 //            l.mousePressed(e);
217 //            event.translatePoint((int)(op.getX()-event.getX()), (int)(op.getY()-event.getY()));
218 //            if (e.isConsumed())
219 //                break;
220 //        }
221             return false;
222         } finally {
223             if (sg.getRootPane() != null) {
224                 if (G2DFocusManager.INSTANCE.getFocusOwner() == null) {
225                     sg.getRootPane().requestFocusInWindow();
226                     //sg.getRootPane().repaint(); //TODO : why repaint here? FocusOwner seems to be always null, so this causes unnecessary delays when interacting the canvas.
227                 }
228             }
229         }
230     }
231
232     private boolean handleEvent(Event e, IG2DNode focusNode, IEventHandler[] handlers) {
233         int typeMask = EventTypes.toTypeMask(e);
234         if (focusNode instanceof IEventHandler) {
235             IEventHandler h = (IEventHandler) focusNode;
236             if (eats(h.getEventMask(), typeMask)) {
237                 if (h.handleEvent(e))
238                     return true;
239             }
240         }
241         for (IEventHandler l : handlers) {
242             if (eats(l.getEventMask(), typeMask)) {
243                 if (l.handleEvent(e))
244                     return true;
245             }
246         }
247         return false;
248     }
249
250     private boolean handleMouseEvent(MouseEvent e, int eventType) {
251         IEventHandler[] sorted = sortedMouseListeners;
252         if (sorted == null)
253             sortedMouseListeners = sorted = sortInplace(mouseListeners.toArray(NONE));
254         return handleEvent(e, sg.getFocusNode(), sorted);
255     }
256
257     private boolean handleMouseDragBeginEvent(MouseEvent e, int eventType) {
258         IEventHandler[] sorted = sortedMouseDragBeginListeners;
259         if (sorted == null)
260             sortedMouseDragBeginListeners = sorted = sortInplace(mouseDragBeginListeners.toArray(NONE));
261         // Give null for focusNode because we want to propagate
262         // this event in scene tree pre-order only.
263         return handleEvent(e, null, sorted);
264     }
265
266     private boolean handleFocusEvent(FocusEvent e) {
267         IEventHandler[] sorted = sortedFocusListeners;
268         if (sorted == null)
269             sortedFocusListeners = sorted = sortInplace(focusListeners.toArray(NONE));
270         return handleEvent(e, null, sorted);
271     }
272
273     private boolean handleTimeEvent(TimeEvent e) {
274         IEventHandler[] sorted = sortedTimeListeners;
275         if (sorted == null)
276             sortedTimeListeners = sorted = sortInplace(timeListeners.toArray(NONE));
277         return handleEvent(e, null, sorted);
278     }
279
280     private boolean handleCommandEvent(CommandEvent e) {
281         IEventHandler[] sorted = sortedCommandListeners;
282         if (sorted == null)
283             sortedCommandListeners = sorted = sortInplace(commandListeners.toArray(NONE));
284         return handleEvent(e, sg.getFocusNode(), sorted);
285     }
286
287     private boolean handleKeyEvent(KeyEvent e) {
288         IEventHandler[] sorted = sortedKeyListeners;
289         if (sorted == null)
290             sortedKeyListeners = sorted = sortInplace(keyListeners.toArray(NONE));
291         return handleEvent(e, sg.getFocusNode(), sorted);
292     }
293
294     @Override
295     public int getEventMask() {
296         return EventTypes.AnyMask;
297     }
298
299     @Override
300     public boolean handleEvent(Event e) {
301         if (DEBUG_EVENTS)
302             debug("handle event: " + e);
303
304         int eventType = EventTypes.toType(e);
305         switch (eventType) {
306             case EventTypes.Command:
307                 return handleCommandEvent((CommandEvent) e);
308
309             case EventTypes.FocusGained:
310             case EventTypes.FocusLost:
311                 return handleFocusEvent((FocusEvent) e);
312
313             case EventTypes.KeyPressed:
314             case EventTypes.KeyReleased:
315                 return handleKeyEvent((KeyEvent) e);
316
317             case EventTypes.MouseDragBegin:
318                 return handleMouseDragBeginEvent((MouseEvent) e, eventType);
319
320             case EventTypes.MouseButtonPressed:
321             case EventTypes.MouseButtonReleased:
322             case EventTypes.MouseClick:
323             case EventTypes.MouseDoubleClick:
324             case EventTypes.MouseEnter:
325             case EventTypes.MouseExit:
326             case EventTypes.MouseMoved:
327             case EventTypes.MouseWheel:
328                 return handleMouseEvent((MouseEvent) e, eventType);
329
330             case EventTypes.Time:
331                 return handleTimeEvent((TimeEvent) e);
332         }
333         return false;
334     }
335
336     public void add(IEventHandler item) {
337         if (!(item instanceof IG2DNode))
338             throw new IllegalArgumentException("event handler must be an IG2DNode");
339
340         int mask = item.getEventMask();
341         if (eats(mask, EventTypes.CommandMask)) {
342             commandListeners.add(item);
343             sortedCommandListeners = null;
344         }
345         if (eats(mask, EventTypes.FocusMask)) {
346             focusListeners.add(item);
347             sortedFocusListeners = null;
348         }
349         if (eats(mask, EventTypes.KeyMask)) {
350             keyListeners.add(item);
351             sortedKeyListeners = null;
352         }
353         if (eats(mask, EventTypes.MouseDragBeginMask)) {
354             mouseDragBeginListeners.add(item);
355             sortedMouseDragBeginListeners = null;
356         }
357         if (eats(mask, EventTypes.MouseMask & ~EventTypes.MouseDragBeginMask)) {
358             mouseListeners.add(item);
359             sortedMouseListeners = null;
360         }
361         if (eats(mask, EventTypes.TimeMask)) {
362             timeListeners.add(item);
363             sortedTimeListeners = null;
364         }
365     }
366
367     public boolean remove(IEventHandler item) {
368         if (!(item instanceof IG2DNode))
369             throw new IllegalArgumentException("event handler must be an IG2DNode");
370
371         int mask = item.getEventMask();
372         boolean removed = false;
373         if (eats(mask, EventTypes.CommandMask)) {
374             removed |= commandListeners.remove(item);
375             sortedCommandListeners = null;
376         }
377         if (eats(mask, EventTypes.FocusMask)) {
378             removed |= focusListeners.remove(item);
379             sortedFocusListeners = null;
380         }
381         if (eats(mask, EventTypes.KeyMask)) {
382             removed |= keyListeners.remove(item);
383             sortedKeyListeners = null;
384         }
385         if (eats(mask, EventTypes.MouseDragBeginMask)) {
386             removed |= mouseDragBeginListeners.remove(item);
387             sortedMouseDragBeginListeners = null;
388         }
389         if (eats(mask, EventTypes.MouseMask & ~EventTypes.MouseDragBeginMask)) {
390             removed |= mouseListeners.remove(item);
391             sortedMouseListeners = null;
392         }
393         if (eats(mask, EventTypes.TimeMask)) {
394             removed |= timeListeners.remove(item);
395             sortedTimeListeners = null;
396         }
397         return removed;
398     }
399
400     private static boolean eats(int handlerMask, int eventTypeMask) {
401         return (handlerMask & eventTypeMask) != 0;
402     }
403
404     private void debug(String msg) {
405         System.out.println(getClass().getSimpleName() + ": " + msg);
406     }
407
408     public void dispose() {
409         commandListeners.clear();
410         commandListeners = null;
411         focusListeners.clear();
412         focusListeners = null;
413         keyListeners.clear();
414         keyListeners = null;
415         mouseListeners.clear();
416         mouseListeners = null;
417         sg = null;
418         sortedCommandListeners = null;
419         sortedKeyListeners = null;
420         sortedMouseListeners = null;
421
422         timeListeners.clear();
423         timeListeners = null;
424     }
425
426 }