]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/impl/ElementDiagram.java
Merge "Minor refactorings related to SCL constructors"
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / diagram / impl / ElementDiagram.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.impl;
13
14 import java.awt.Color;
15 import java.awt.geom.AffineTransform;
16 import java.awt.geom.Point2D;
17 import java.awt.geom.Rectangle2D;
18 import java.util.Comparator;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.concurrent.atomic.AtomicBoolean;
22
23 import org.simantics.g2d.canvas.Hints;
24 import org.simantics.g2d.canvas.ICanvasContext;
25 import org.simantics.g2d.canvas.ICanvasParticipant;
26 import org.simantics.g2d.canvas.IContentContext;
27 import org.simantics.g2d.canvas.IMouseCaptureContext;
28 import org.simantics.g2d.canvas.IMouseCursorContext;
29 import org.simantics.g2d.canvas.impl.MouseCaptureContext;
30 import org.simantics.g2d.canvas.impl.MouseCursorContext;
31 import org.simantics.g2d.canvas.impl.PaintableContextImpl;
32 import org.simantics.g2d.chassis.ITooltipProvider;
33 import org.simantics.g2d.diagram.DiagramClass;
34 import org.simantics.g2d.diagram.DiagramHints;
35 import org.simantics.g2d.diagram.IDiagram;
36 import org.simantics.g2d.diagram.participant.DiagramParticipant;
37 import org.simantics.g2d.diagram.participant.ElementInteractor;
38 import org.simantics.g2d.diagram.participant.ElementPainter;
39 import org.simantics.g2d.diagram.participant.Selection;
40 import org.simantics.g2d.diagram.participant.TerminalPainter;
41 import org.simantics.g2d.diagram.participant.ZOrderHandler;
42 import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor;
43 import org.simantics.g2d.element.IElement;
44 import org.simantics.g2d.multileveldiagram.TransitionFunction;
45 import org.simantics.g2d.multileveldiagram.ZoomTransitionParticipant;
46 import org.simantics.g2d.participant.BackgroundPainter;
47 import org.simantics.g2d.participant.CanvasGrab;
48 import org.simantics.g2d.participant.GridPainter;
49 import org.simantics.g2d.participant.HandPainter;
50 import org.simantics.g2d.participant.KeyToCommand;
51 import org.simantics.g2d.participant.KeyUtil;
52 import org.simantics.g2d.participant.MouseUtil;
53 import org.simantics.g2d.participant.PointerPainter;
54 import org.simantics.g2d.participant.RulerPainter;
55 import org.simantics.g2d.participant.SubCanvas;
56 import org.simantics.g2d.participant.SymbolUtil;
57 import org.simantics.g2d.participant.TimeParticipant;
58 import org.simantics.g2d.participant.TransformUtil;
59 import org.simantics.g2d.scenegraph.SceneGraphConstants;
60 import org.simantics.scenegraph.g2d.G2DParentNode;
61 import org.simantics.scenegraph.g2d.G2DSceneGraph;
62 import org.simantics.scenegraph.g2d.events.Event;
63 import org.simantics.scenegraph.g2d.events.EventHandlerStack;
64 import org.simantics.scenegraph.g2d.events.EventQueue;
65 import org.simantics.scenegraph.g2d.events.IEventHandlerStack;
66 import org.simantics.scenegraph.g2d.events.IEventQueue;
67 import org.simantics.scenegraph.g2d.events.MouseEvent;
68 import org.simantics.scenegraph.g2d.events.MouseEventCoalescer;
69 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
70 import org.simantics.scenegraph.g2d.events.IEventQueue.IEventQueueListener;
71 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonEvent;
72 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
73 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
74 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
75 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDoubleClickedEvent;
76 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
77 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent;
78 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;
79 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
80 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseWheelMovedEvent;
81 import org.simantics.scenegraph.g2d.events.command.CommandKeyBinding;
82 import org.simantics.utils.datastructures.context.Context;
83 import org.simantics.utils.datastructures.context.IContext;
84 import org.simantics.utils.datastructures.context.IContextListener;
85 import org.simantics.utils.datastructures.hints.HintContext;
86 import org.simantics.utils.datastructures.hints.HintStack;
87 import org.simantics.utils.datastructures.hints.IHintContext;
88 import org.simantics.utils.datastructures.hints.IHintListener;
89 import org.simantics.utils.datastructures.hints.IHintStack;
90 import org.simantics.utils.threads.IThreadWorkQueue;
91 import org.simantics.utils.threads.ThreadUtils;
92
93 /**
94  * Creates subcanvas on top of parent canvas. Size and position of the subcanvas
95  * can be changed.
96  * 
97  * Beware: every CanvasParticipant that handles mouseEvents must be replaced
98  * since mouse coordinates must be translated. Currently Implemented
99  * participants: - MouseUtil - ElementInteractor
100  * 
101  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
102  * 
103  * TODO: CanvasContext implementation needs to implement scene graph methods too
104  */
105 public class ElementDiagram implements IDiagram {
106     private final ICanvasContext ctx;
107
108     private final ICanvasContext parentCtx;
109
110     private IDiagram             diagram;
111
112     private final SubCanvas      subCanvas;
113
114     private Rectangle2D          canvasRect = new Rectangle2D.Double(0, 0, 200, 200);
115
116     private int                  canvasPosX = 100;
117
118     private int                  canvasPosY = 100;
119
120     public ElementDiagram(ICanvasContext parentCtx) {
121         this.parentCtx = parentCtx;
122         ctx = createCanvas(parentCtx.getThreadAccess(),parentCtx.getSceneGraph());
123         ICanvasParticipant mouseUtil = ctx.getSingleItem(MouseUtil.class);
124         if (mouseUtil != null) {
125             ctx.remove(mouseUtil);
126             ctx.add(new ElementDiagramMouseUtil());
127             // ctx.add(new MouseUtil());
128         }
129         ICanvasParticipant elementInteractor = ctx.getSingleItem(ElementInteractor.class);
130         if (elementInteractor != null) {
131             ctx.remove(elementInteractor);
132             ctx.add(new ElementDiagramElementInteractor());
133         }
134         diagram = createDiagram();
135         ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram);
136         subCanvas = new SubCanvas(ctx, 10000, Integer.MAX_VALUE - 100, 10000);
137         parentCtx.add(subCanvas);
138     }
139
140     public void setSize(int x, int y, int width, int height) {
141         canvasPosX = x;
142         canvasPosY = y;
143         canvasRect = new Rectangle2D.Double(0, 0, width, height);
144         getCanvas().getCanvasNode().setTransform(new AffineTransform(1,0,0,1,x,y));
145        
146     }
147
148     public Rectangle2D getSize() {
149         return canvasRect;
150     }
151
152     /**
153      * Override this for custom functionality
154      * 
155      * @return
156      */
157     public IDiagram createDiagram() {
158         IDiagram d = Diagram.spawnNew(DiagramClass.DEFAULT);
159         return d;
160     }
161
162     /**
163      * Override this for custom functionality
164      * 
165      * @param thread
166      * @return
167      */
168     public ICanvasContext createCanvas(IThreadWorkQueue thread, G2DSceneGraph sg) {
169         return createDefaultCanvas(thread,sg);
170     }
171
172     public void setDiagram(IDiagram diagram) {
173         this.diagram = diagram;
174         ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram);
175     }
176
177     public ICanvasContext getCanvas() {
178         return ctx;
179     }
180
181     public ICanvasContext getParentCanvas() {
182         return parentCtx;
183     }
184
185     public SubCanvas getSubCanvas() {
186         return subCanvas;
187     }
188
189     public IDiagram getDiagram() {
190         return diagram;
191     }
192
193     @Override
194     public void addElement(IElement element) {
195         diagram.addElement(element);
196     }
197
198     @Override
199     public void removeElement(IElement element) {
200         diagram.removeElement(element);
201     }
202
203     @Override
204     public void addCompositionListener(CompositionListener listener) {
205         diagram.addCompositionListener(listener);
206     }
207
208     @Override
209     public void addCompositionVetoListener(CompositionVetoListener listener) {
210         diagram.addCompositionVetoListener(listener);
211     }
212
213     @Override
214     public void addHintListener(IHintListener listener) {
215         diagram.addHintListener(listener);
216     }
217
218     @Override
219     public void addHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {
220         diagram.addHintListener(threadAccess, listener);
221     }
222
223     @Override
224     public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {
225         diagram.addKeyHintListener(threadAccess, key, listener);
226     }
227
228     @Override
229     public void addKeyHintListener(Key key, IHintListener listener) {
230         diagram.addKeyHintListener(key, listener);
231     }
232
233     @Override
234     public boolean bringToTop(IElement e) {
235         return diagram.bringToTop(e);
236     }
237
238     @Override
239     public boolean bringUp(IElement e) {
240         return diagram.bringUp(e);
241     }
242
243     @Override
244     public void destroy() {
245         diagram.destroy();
246     }
247
248     @Override
249     public void dispose() {
250         diagram.dispose();
251     }
252
253     @Override
254     public DiagramClass getDiagramClass() {
255         return diagram.getDiagramClass();
256     }
257
258     @Override
259     public boolean containsElement(IElement element) {
260         return diagram.containsElement(element);
261     }
262
263     @Override
264     public List<IElement> getElements() {
265         return diagram.getElements();
266     }
267
268     @Override
269     public void sort(Comparator<IElement> comparator) {
270         diagram.sort(comparator);
271     }
272
273     @Override
274     public List<IElement> getSnapshot() {
275         return diagram.getSnapshot();
276     }
277
278     @Override
279     public void clearWithoutNotification() {
280         diagram.clearWithoutNotification();
281     }
282
283     @Override
284     public boolean containsHint(Key key) {
285         return diagram.containsHint(key);
286     }
287
288     @Override
289     public <E> E getHint(Key key) {
290         return diagram.getHint(key);
291     }
292
293     @Override
294     public Map<Key, Object> getHints() {
295         return diagram.getHints();
296     }
297
298     @Override
299     public Map<Key, Object> getHintsUnsafe() {
300         return diagram.getHintsUnsafe();
301     }
302
303     @Override
304     public <E extends Key> Map<E, Object> getHintsOfClass(Class<E> clazz) {
305         return diagram.getHintsOfClass(clazz);
306     }
307
308     @Override
309     public boolean moveTo(IElement e, int position) {
310         return diagram.moveTo(e, position);
311     }
312
313     @Override
314     public void removeCompositionListener(CompositionListener listener) {
315         diagram.removeCompositionListener(listener);
316     }
317
318     @Override
319     public void removeCompositionVetoListener(CompositionVetoListener listener) {
320         diagram.removeCompositionVetoListener(listener);
321     }
322
323     @Override
324     public <E> E removeHint(Key key) {
325         return diagram.removeHint(key);
326     }
327
328     @Override
329     public void removeHintListener(IHintListener listener) {
330         diagram.removeHintListener(listener);
331     }
332
333     @Override
334     public void removeHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {
335         diagram.removeHintListener(threadAccess, listener);
336     }
337
338     @Override
339     public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {
340         diagram.removeKeyHintListener(threadAccess, key, listener);
341     }
342
343     @Override
344     public void removeKeyHintListener(Key key, IHintListener listener) {
345         diagram.removeKeyHintListener(key, listener);
346     }
347
348     @Override
349     public boolean sendDown(IElement e) {
350         return diagram.sendDown(e);
351     }
352
353     @Override
354     public boolean sendToBottom(IElement e) {
355         return diagram.sendToBottom(e);
356     }
357
358     @Override
359     public void setHint(Key key, Object value) {
360         diagram.setHint(key, value);
361     }
362
363     @Override
364     public void setHints(Map<Key, Object> hints) {
365         diagram.setHints(hints);
366     }
367
368     public ICanvasContext createDefaultCanvas(IThreadWorkQueue thread, G2DSceneGraph sg) {
369         // Create canvas context and a layer of interactors
370         ICanvasContext canvasContext = new ElementDiagramCanvasContext(thread,sg);
371         IHintContext h = canvasContext.getDefaultHintContext();
372
373         //canvasContext.add(new PanZoomRotateHandler()); // Must be before
374         // TransformUtil
375
376         // Support & Util Participants
377         canvasContext.add(new TransformUtil());
378         canvasContext.add(new MouseUtil());
379         canvasContext.add(new KeyUtil());
380         canvasContext.add(new CanvasGrab());
381         canvasContext.add(new SymbolUtil());
382         canvasContext.add(new TimeParticipant());
383
384         // Debug participant(s)
385         // canvasContext.add( new PointerPainter() );
386         canvasContext.add(new HandPainter());
387         h.setHint(PointerPainter.KEY_PAINT_POINTER, true);
388
389         // Pan & Zoom & Rotate
390         // canvasContext.add( new MousePanZoomInteractor() );
391         // canvasContext.add( new MultitouchPanZoomRotateInteractor() );
392         // canvasContext.add( new OrientationRestorer() );
393
394         // Grid & Ruler & Background
395         canvasContext.add(new GridPainter());
396         canvasContext.add(new RulerPainter());
397         canvasContext.add(new BackgroundPainter());
398         h.setHint(Hints.KEY_GRID_COLOR, new Color(0.9f, 0.9f, 0.9f));
399         h.setHint(Hints.KEY_BACKGROUND_COLOR, Color.LIGHT_GRAY);
400
401         // Key bindings
402         canvasContext.add(new KeyToCommand(CommandKeyBinding.DEFAULT_BINDINGS));
403
404         // //// Diagram Participants //////
405         canvasContext.add(new PointerInteractor());
406         canvasContext.add(new ElementInteractor());
407         canvasContext.add(new Selection());
408         canvasContext.add(new DiagramParticipant());
409         canvasContext.add(new ElementPainter());
410         canvasContext.add(new TerminalPainter(true, true, false, true));
411         //canvasContext.add(new ElementHeartbeater());
412         canvasContext.add(new ZOrderHandler());
413         canvasContext.add(new ZoomTransitionParticipant(TransitionFunction.SIGMOID));
414         h.setHint(Hints.KEY_TOOL, Hints.PANTOOL);
415
416         return canvasContext;
417     }
418
419     public class ElementDiagramCanvasContext extends Context<ICanvasParticipant> implements ICanvasContext {
420
421         protected HintStack              hintStack            = new HintStack();
422
423         protected HintContext            bottomHintContext    = new HintContext();
424
425         protected IEventHandlerStack     eventHandlerStack    = null;
426
427         protected boolean                eventHandlingOrdered = false;
428
429         protected EventQueue             eventQueue           = null;
430
431         protected IContentContext        paintableCtx         = new PaintableContextImpl();
432
433         protected final IThreadWorkQueue thread;
434
435         protected IMouseCaptureContext   mouseCaptureCtx      = new MouseCaptureContext();
436
437         protected IMouseCursorContext    mouseCursorCtx       = new MouseCursorContext();
438         
439         protected G2DSceneGraph              sg;
440         protected G2DParentNode          canvasNode           = null;
441         
442         protected ITooltipProvider       tooltip;
443
444         /**
445          * 
446          * @param thread context thread, or null if sync policy not used
447          */
448         public ElementDiagramCanvasContext(IThreadWorkQueue thread, G2DSceneGraph sg) {
449             super(ICanvasParticipant.class);
450             if (thread == null)
451                 throw new IllegalArgumentException("null");
452             this.thread = thread;
453             eventHandlerStack = new EventHandlerStack(thread);
454             eventQueue = new EventQueue(eventHandlerStack);
455             hintStack.addHintContext(bottomHintContext, Integer.MIN_VALUE);
456
457             
458             
459             this.sg = sg;
460             canvasNode = sg.addNode("elementd" + SceneGraphConstants.NAVIGATION_NODE_NAME , G2DParentNode.class); // Add dummy parent node
461             canvasNode.setZIndex(1000);
462             
463             this.addContextListener(thread, new IContextListener<ICanvasParticipant>() {
464                 @Override
465                 public void itemAdded(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {
466                     item.addedToContext(ElementDiagramCanvasContext.this);
467                 }
468
469                 @Override
470                 public void itemRemoved(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {
471                     item.removedFromContext(ElementDiagramCanvasContext.this);
472                 }
473             });
474
475             eventQueue.addEventCoalesceler(MouseEventCoalescer.INSTANCE);
476             // Order event handling if events are added to the queue
477             eventQueue.addQueueListener(new IEventQueueListener() {
478                 @Override
479                 public void onEventAdded(IEventQueue queue, Event e, int index) {
480                     asyncHandleEvents();
481                 }
482
483                 @Override
484                 public void onQueueEmpty(IEventQueue queue) {
485                 }
486             });
487         }
488
489         public IHintStack getHintStack() {
490             assertNotDisposed();
491             return hintStack;
492         }
493
494         private final Runnable eventHandling = new Runnable() {
495             @Override
496             public void run() {
497                 eventHandlingOrdered = false;
498                 eventQueue.handleEvents();
499             }
500         };
501
502         synchronized void asyncHandleEvents() {
503             if (eventHandlingOrdered)
504                 return;
505             eventHandlingOrdered = true;
506             ThreadUtils.asyncExec(thread, eventHandling);
507         }
508
509         synchronized void syncHandleEvents() {
510             if (eventHandlingOrdered)
511                 return;
512             eventHandlingOrdered = true;
513             ThreadUtils.syncExec(thread, eventHandling);
514         }
515
516         @Override
517         public IEventHandlerStack getEventHandlerStack() {
518             assertNotDisposed();
519             return eventHandlerStack;
520         }
521
522         @Override
523         public IThreadWorkQueue getThreadAccess() {
524             // assertNotDisposed();
525             return thread;
526         }
527
528         @Override
529         protected void doDispose() {
530             ThreadUtils.syncExec(getThreadAccess(), new Runnable() {
531                 @Override
532                 public void run() {
533                     clear();
534                     
535                     // HN: added to decrease memory leaks
536                     if (sg != null) {
537                         // Makes sure that scene graph nodes free their resources!
538                         sg.removeNodes();
539                         sg = null;
540                     }
541                 }
542             });
543         }
544
545         @Override
546         public IHintContext getDefaultHintContext() {
547             return bottomHintContext;
548         }
549
550         @Override
551         public IMouseCursorContext getMouseCursorContext() {
552             return mouseCursorCtx;
553         }
554
555         @Override
556         public void setMouseCursorContext(IMouseCursorContext ctx) {
557             this.mouseCursorCtx = ctx;
558         }
559
560         @Override
561         public IMouseCaptureContext getMouseCaptureContext() {
562             return mouseCaptureCtx;
563         }
564
565         @Override
566         public void setMouseCaptureContext(IMouseCaptureContext mctx) {
567             this.mouseCaptureCtx = mctx;
568         }
569
570         @Override
571         public IEventQueue getEventQueue() {
572             return eventQueue;
573         }
574
575         @Override
576         public IContentContext getContentContext() {
577             return paintableCtx;
578         }
579
580         @Override
581         public void setContentContext(IContentContext ctx) {
582             this.paintableCtx = ctx;
583         }
584
585         @Override
586         public G2DSceneGraph getSceneGraph() {
587             return sg;
588         }
589
590         @Override
591         public G2DParentNode getCanvasNode() {
592             return canvasNode;
593         }
594
595         @Override
596         public void setCanvasNode(G2DParentNode node) {
597             throw new RuntimeException("Cannot set canvasNode");
598
599         }
600
601         protected final AtomicBoolean locked = new AtomicBoolean(false);
602
603         @Override
604         public boolean isLocked() {
605             return this.locked.get();
606         }
607
608         @Override
609         public void setLocked(boolean locked) {
610             boolean previous = this.locked.getAndSet(locked);
611             if (!locked && previous != locked) {
612                 // The context was unlocked!
613                 getContentContext().setDirty();
614             }
615         }
616
617                 @Override
618                 public ITooltipProvider getTooltipProvider() {
619                         return tooltip;
620                 }
621
622                 @Override
623                 public void setTooltipProvider(ITooltipProvider tooltipProvider) {
624                         this.tooltip = tooltipProvider;
625                 }
626
627     }
628
629     public class ElementDiagramMouseUtil extends MouseUtil {
630
631         @Override
632         @EventHandler(priority = Integer.MAX_VALUE)
633         public boolean handleMouseEvent(MouseEvent e) {
634             MouseEvent ne = createMouseEvent(e);
635             if (ne != null)
636                 return handleMouseEvent2(ne);
637             return false;
638         }
639
640         /**
641          * Copy-pasted MouseUtil.handleMouseEvent with one modification;
642          * Generating MouseClickEvents has been removed, because it created
643          * duplicated events (with incorrect coordinates).
644          * 
645          * @param e
646          * @return
647          */
648         public boolean handleMouseEvent2(MouseEvent e) {
649             assertDependencies();
650             if (e instanceof MouseEnterEvent) {
651                 Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);
652                 // Reset mouse
653                 MouseInfo mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons);
654                 miceInfo.put(e.mouseId, mi);
655             } else if (e instanceof MouseExitEvent) {
656                 miceInfo.remove(e.mouseId);
657             } else if (e instanceof MouseMovedEvent) {
658                 Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);
659                 double deltaDistance = 0;
660                 MouseInfo mi = miceInfo.get(e.mouseId);
661                 if (mi == null) {
662                     mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, 0/*e.buttons*/);
663                     miceInfo.put(e.mouseId, mi);
664                 } else {
665                     deltaDistance = e.controlPosition.distance(mi.controlPosition);
666                     mi.controlPosition = e.controlPosition;
667                     mi.canvasPosition = canvasPosition;
668                 }
669
670                 if (deltaDistance > 0)
671                     mi.addDistanceForButtons(deltaDistance);
672
673             } else if (e instanceof MouseButtonPressedEvent) {
674                 Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);
675                 MouseButtonPressedEvent me = (MouseButtonPressedEvent) e;
676                 MouseInfo mi = miceInfo.get(e.mouseId);
677                 if (mi == null) {
678                     mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons);
679                     miceInfo.put(e.mouseId, mi);
680                 } else {
681                     mi.controlPosition = e.controlPosition;
682                     mi.canvasPosition = canvasPosition;
683                 }
684                 mi.setButtonPressed(me.button, e.stateMask, e.controlPosition, canvasPosition, e.time);
685             } else if (e instanceof MouseButtonReleasedEvent) {
686                 MouseButtonReleasedEvent me = (MouseButtonReleasedEvent) e;
687                 Point2D canvasPosition = util.controlToCanvas(me.controlPosition, null);
688                 MouseInfo mi = miceInfo.get(me.mouseId);
689                 if (mi == null) {
690                     mi = new MouseInfo(e.mouseId, me.controlPosition, canvasPosition, 0/*me.buttons*/);
691                     miceInfo.put(me.mouseId, mi);
692                 } else {
693                     mi.controlPosition = me.controlPosition;
694                     mi.canvasPosition = canvasPosition;
695                 }
696                 ButtonInfo bi = mi.releaseButton(me.button, me.time);
697                 if (bi == null)
698                     return false;
699
700                 if (me.holdTime > profile.clickHoldTimeTolerance)
701                     return false;
702                 if (bi.deltaMotion > profile.movementTolerance)
703                     return false;
704                 // This is a click
705
706                 long timeSinceLastClick = me.time - bi.lastClickEventTime;
707                 bi.lastClickEventTime = me.time;
708
709                 // reset click counter
710                 if (timeSinceLastClick > profile.consecutiveToleranceTime)
711                     bi.clickCount = 0;
712                 else
713                     bi.clickCount++;
714
715             }
716             return false;
717         }
718     }
719
720     public class ElementDiagramElementInteractor extends ElementInteractor {
721         @Override
722         @EventHandler(priority = INTERACTOR_PRIORITY)
723         public boolean handleMouseEvent(MouseEvent me) {
724             MouseEvent ne = createMouseEvent(me);
725             if (ne != null)
726                 return super.handleMouseEvent(ne);
727             return false;
728         }
729     }
730
731     public MouseEvent createMouseEvent(MouseEvent e) {
732         MouseEvent newEvent = null;
733         double x = e.controlPosition.getX();
734         double y = e.controlPosition.getY();
735         Point2D newPos = new Point2D.Double(x - canvasPosX, y - canvasPosY);
736
737         if (!canvasRect.contains(newPos))
738             return new MouseExitEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos, e.screenPosition);
739         if (e instanceof MouseButtonPressedEvent) {
740             newEvent = new MouseButtonPressedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,
741                     ((MouseButtonEvent) e).button, newPos, e.screenPosition);
742         } else if (e instanceof MouseButtonReleasedEvent) {
743             newEvent = new MouseButtonReleasedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,
744                     ((MouseButtonEvent) e).button, ((MouseButtonReleasedEvent) e).holdTime, newPos, e.screenPosition);
745         } else if (e instanceof MouseDoubleClickedEvent) {
746             newEvent = new MouseDoubleClickedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,
747                     ((MouseButtonEvent) e).button, newPos, e.screenPosition);
748         } else if (e instanceof MouseClickEvent) {
749             newEvent = new MouseClickEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask,
750                     ((MouseButtonEvent) e).button, ((MouseClickEvent) e).clickCount, newPos, e.screenPosition);
751         } else if (e instanceof MouseDragBegin) {
752             newEvent = new MouseDragBegin(e.context, e.time, e.mouseId, e.buttons, e.stateMask,
753                     ((MouseButtonEvent) e).button, ((MouseDragBegin) e).startCanvasPos,
754                     ((MouseDragBegin) e).startControlPos, newPos, e.screenPosition);
755         } else if (e instanceof MouseEnterEvent) {
756             newEvent = new MouseEnterEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,
757                     e.screenPosition);
758         } else if (e instanceof MouseExitEvent) {
759             newEvent = new MouseExitEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,
760                     e.screenPosition);
761         } else if (e instanceof MouseMovedEvent) {
762             newEvent = new MouseMovedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,
763                     e.screenPosition);
764         } else if (e instanceof MouseWheelMovedEvent) {
765             newEvent = new MouseWheelMovedEvent(e.context, e.time, e.mouseId, e.buttons, e.stateMask, newPos,
766                     e.screenPosition, ((MouseWheelMovedEvent) e).scrollType, ((MouseWheelMovedEvent) e).scrollAmount,
767                     ((MouseWheelMovedEvent) e).wheelRotation);
768         } else {
769             throw new Error("Unknow event " + e.getClass() + " " + e);
770         }
771         // System.out.println(newPos + " " + newEvent + " "+ e);
772         return newEvent;
773     }
774
775 }