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