]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/element/handler/impl/AbstractClickable.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / element / handler / impl / AbstractClickable.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.element.handler.impl;\r
13 \r
14 import java.awt.geom.Point2D;\r
15 import java.lang.reflect.Method;\r
16 import java.util.Map;\r
17 \r
18 import org.simantics.g2d.canvas.ICanvasContext;\r
19 import org.simantics.g2d.diagram.participant.DiagramParticipant;\r
20 import org.simantics.g2d.element.ElementUtils;\r
21 import org.simantics.g2d.element.IElement;\r
22 import org.simantics.g2d.element.handler.Clickable;\r
23 import org.simantics.scenegraph.g2d.events.MouseEvent;\r
24 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;\r
25 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;\r
26 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;\r
27 import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
28 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
29 import org.simantics.utils.threads.Executable;\r
30 import org.simantics.utils.threads.IThreadWorkQueue;\r
31 import org.simantics.utils.threads.SyncListenerList;\r
32 import org.simantics.utils.threads.ThreadUtils;\r
33 \r
34 /**\r
35  * Base implementation for button handlers\r
36  * \r
37  * \r
38  * @author Toni Kalajainen\r
39  */\r
40 public abstract class AbstractClickable extends AbstractGrabbable implements Clickable {\r
41         \r
42         private static final long serialVersionUID = -8329973386869163106L;\r
43         \r
44         public static Key HOVER_KEY = new KeyOf(Boolean.class, "HOVER");\r
45         public static Key PRESS_STATUS_KEY = new KeyOf(PressStatus.class, "PRESS_STATUS");\r
46         \r
47         public AbstractClickable(Double strayDistance) {\r
48                 super(1000.0);\r
49         }\r
50         \r
51         public AbstractClickable() {\r
52                 super();\r
53         }       \r
54 \r
55         /**\r
56          * Get element press status\r
57          * @param e\r
58          * @param ctx\r
59          * @return\r
60          */\r
61         public PressStatus getPressStatus(IElement e, ICanvasContext ctx)\r
62         {\r
63                 Map<Integer, GrabInfo> gis = getGrabs(e, ctx);\r
64                 if (gis==null || gis.size()==0) {\r
65                         Boolean hover = e.getHint(HOVER_KEY);\r
66                         if (hover != null && hover)\r
67                                 return PressStatus.HOVER;\r
68                         return PressStatus.NORMAL;\r
69                 }\r
70                 \r
71                 // is held down \r
72                 boolean held = false;\r
73                 boolean pressing = false;\r
74                 \r
75                 for (GrabInfo gi : gis.values()) {\r
76                         held = true;\r
77                         pressing |= onPickCheck(e, ctx, gi.pointerId, gi.dragPosElement);\r
78                         if (pressing) break;\r
79                 }               \r
80                 \r
81                 if (pressing) return PressStatus.PRESSED;\r
82                 if (held) return PressStatus.HELD;\r
83                 Boolean hover = e.getHint(HOVER_KEY);\r
84                 if (hover != null && hover)\r
85                         return PressStatus.HOVER;\r
86 \r
87                 return PressStatus.NORMAL;\r
88         }\r
89         \r
90         \r
91         @Override\r
92         public boolean handleMouseEvent(IElement e, ICanvasContext ctx,\r
93                         MouseEvent me) {\r
94                 //System.out.println("AbstractClickable.hME element:" + e + " me:" + me);\r
95                 boolean b = super.handleMouseEvent(e, ctx, me);\r
96                 Boolean hovering;\r
97                 // DND drag starts causes DragBegin + ButtonReleasedEvents, and hovering must be set false\r
98                 if (!(me instanceof MouseExitEvent || me instanceof MouseDragBegin || me instanceof MouseButtonReleasedEvent))\r
99                         hovering = Boolean.valueOf(onPickCheck(e, ctx, me.mouseId, ElementUtils.controlToElementCoordinate(e, ctx, me.controlPosition, null)));\r
100                 else\r
101                         hovering = false;\r
102                 if (!hovering.equals(e.getHint(HOVER_KEY))) {\r
103                         e.setHint(HOVER_KEY, hovering);\r
104                 }\r
105                 \r
106                 // hackety hack\r
107                 PressStatus newStatus = getPressStatus(e, ctx);\r
108                 if (!newStatus.equals(e.getHint(PRESS_STATUS_KEY)))\r
109                         e.setHint(PRESS_STATUS_KEY, newStatus) ;\r
110                 return b;\r
111         }\r
112         \r
113         @Override\r
114         protected void onDrag(GrabInfo gi, ICanvasContext ctx) {\r
115                 \r
116         }\r
117 \r
118         @Override\r
119         protected void onGrab(GrabInfo gi, ICanvasContext ctx) {\r
120         }\r
121 \r
122         @Override\r
123         protected void onGrabCancel(GrabInfo gi, ICanvasContext ctx) {\r
124         }\r
125 \r
126         @Override\r
127         protected void onRelease(GrabInfo gi, ICanvasContext ctx) {\r
128                 // pick is pick until last mouse releases\r
129                 if (getGrabCount(gi.e, ctx)>0) return;\r
130                 \r
131                 boolean pick = onPickCheck(gi.e, ctx, gi.pointerId, gi.dragPosElement);\r
132                 if (pick) {\r
133                         onClicked(gi, ctx);\r
134                         fireClicked(gi.e, ctx);\r
135                 }\r
136         }\r
137         \r
138         protected boolean onGrabCheck(IElement e, ICanvasContext ctx, int pointerId, Point2D pickPos)\r
139         {\r
140                 Point2D elementPos = ElementUtils.controlToElementCoordinate(e, ctx, pickPos, null); \r
141                 return onPickCheck(e, ctx, pointerId, elementPos);\r
142         }\r
143         \r
144         protected abstract void onClicked(GrabInfo gi, ICanvasContext ctx);\r
145         \r
146         /**\r
147          * Pick check in element coordinates\r
148          * @param e\r
149          * @param ctx\r
150          * @param pointerId\r
151          * @param pickPos\r
152          * @return\r
153          */\r
154         protected abstract boolean onPickCheck(IElement e, ICanvasContext ctx, int pointerId, Point2D pickPos);\r
155 \r
156 \r
157         private static final Key KEY_CLICK_LISTENERS = new KeyOf(SyncListenerList.class);\r
158         \r
159         @Override\r
160         public void addListener(final IElement e, final ICanvasContext ctx, final IThreadWorkQueue thread, final ClickListener listener) {\r
161                 ThreadUtils.syncExec(ctx.getThreadAccess(), new Runnable() {\r
162                         @Override\r
163                         public void run() {\r
164                                 DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
165                                 SyncListenerList<ClickListener> list = dp.getElementHint(e, KEY_CLICK_LISTENERS);\r
166                                 if (list==null) {\r
167                                         list = new SyncListenerList<ClickListener>(ClickListener.class);\r
168                                         dp.setElementHint(e, KEY_CLICK_LISTENERS, list);\r
169                                 }\r
170                                 list.add(thread, listener);\r
171                         }});            \r
172         }\r
173 \r
174         @Override\r
175         public void removeListener(final IElement e, final ICanvasContext ctx, final IThreadWorkQueue thread, final ClickListener listener) {\r
176                 ThreadUtils.syncExec(ctx.getThreadAccess(), new Runnable() {\r
177                         @Override\r
178                         public void run() {\r
179                                 DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
180                                 SyncListenerList<ClickListener> list = dp.getElementHint(e, KEY_CLICK_LISTENERS);\r
181                                 if (list==null) return;\r
182                                 list.remove(thread, listener);\r
183                                 if (list.isEmpty())\r
184                                         dp.removeElementHint(e, KEY_CLICK_LISTENERS);\r
185                         }});            \r
186         }               \r
187         \r
188         private final static Method onClick = SyncListenerList.getMethod(ClickListener.class, "onClick");\r
189         public void fireClicked(IElement e, ICanvasContext ctx)\r
190         {\r
191                 DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
192                 SyncListenerList<ClickListener> list = dp.getElementHint(e, KEY_CLICK_LISTENERS);\r
193                 if (list==null) return;\r
194                 Executable exes[] = list.getExecutables(onClick, e, ctx);\r
195                 ThreadUtils.multiSyncExec(exes);\r
196         }\r
197         \r
198 }\r