Falkon Develop
Cross-platform Qt-based web browser
mousegesturerecognizer.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the mouse gesture package.
3 * Copyright (C) 2006 Johan Thelin <e8johan@gmail.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the
8 * following conditions are met:
9 *
10 * - Redistributions of source code must retain the above
11 * copyright notice, this list of conditions and the
12 * following disclaimer.
13 * - Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions and
15 * the following disclaimer in the documentation and/or
16 * other materials provided with the distribution.
17 * - The names of its contributors may be used to endorse
18 * or promote products derived from this software without
19 * specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
22 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
39
40using namespace Gesture;
41
42// Private data structure
46
49
51};
52
53// Class implementation
54
55MouseGestureRecognizer::MouseGestureRecognizer(int minimumMovement, double minimumMatch, bool allowDiagonals)
56{
57 d = new Private;
58 d->minimumMovement2 = minimumMovement * minimumMovement;
59 d->minimumMatch = minimumMatch;
60
61 d->allowDiagonals = allowDiagonals;
62}
63
65{
66 delete d;
67}
68
70{
71 d->gestures.push_back(gesture);
72}
73
75{
76 d->gestures.clear();
77}
78
80{
81 d->positions.clear();
82 d->positions.push_back(Pos(x, y));
83}
84
86{
87 bool matched = false;
88
89 if (x != d->positions.back().x || y != d->positions.back().y) {
90 d->positions.push_back(Pos(x, y));
91 }
92
93 int dx = x - d->positions.at(0).x;
94 int dy = y - d->positions.at(0).y;
95
96 if (dx * dx + dy * dy < d->minimumMovement2) {
97 return false;
98 }
99
100 if (d->positions.size() > 1) {
101 matched = recognizeGesture();
102 }
103
104 d->positions.clear();
105
106 return matched;
107}
108
110{
111 d->positions.clear();
112}
113
115{
116 int dx, dy;
117
118 dx = x - d->positions.back().x;
119 dy = y - d->positions.back().y;
120
121 if (dx * dx + dy * dy >= d->minimumMovement2) {
122 d->positions.push_back(Pos(x, y));
123 }
124}
125
127{
128 return d->positions;
129}
130
131bool MouseGestureRecognizer::recognizeGesture()
132{
133 PosList directions = simplify(limitDirections(d->positions, d->allowDiagonals));
134 double minLength = calcLength(directions) * d->minimumMatch;
135
136 while (directions.size() > 0 && calcLength(directions) > minLength) {
137 for (GestureList::const_iterator gi = d->gestures.begin(); gi != d->gestures.end(); ++gi) {
138 if (gi->directions.size() == directions.size()) {
139 bool match = true;
140 PosList::const_iterator pi = directions.begin();
141 for (DirectionList::const_iterator di = gi->directions.begin(); di != gi->directions.end() && match; ++di, ++pi) {
142 switch (*di) {
143 case UpLeft:
144 if (!(pi->y < 0 && pi->x < 0)) {
145 match = false;
146 }
147
148 break;
149 case UpRight:
150 if (!(pi->y < 0 && pi->x > 0)) {
151 match = false;
152 }
153
154 break;
155 case DownLeft:
156 if (!(pi->y > 0 && pi->x < 0)) {
157 match = false;
158 }
159
160 break;
161 case DownRight:
162 if (!(pi->y > 0 && pi->x > 0)) {
163 match = false;
164 }
165
166 break;
167 case Up:
168 if (pi->y >= 0 || pi->x != 0) {
169 match = false;
170 }
171
172 break;
173 case Down:
174 if (pi->y <= 0 || pi->x != 0) {
175 match = false;
176 }
177
178 break;
179 case Left:
180 if (pi->x >= 0 || pi->y != 0) {
181 match = false;
182 }
183
184 break;
185 case Right:
186 if (pi->x <= 0 || pi->y != 0) {
187 match = false;
188 }
189
190 break;
191 case AnyHorizontal:
192 if (pi->x == 0 || pi->y != 0) {
193 match = false;
194 }
195
196 break;
197 case AnyVertical:
198 if (pi->y == 0 || pi->x != 0) {
199 match = false;
200 }
201
202 break;
203 case NoMatch:
204 match = false;
205
206 break;
207 }
208 }
209
210 if (match) {
211 gi->callbackClass->callback();
212 return true;
213 }
214 }
215 }
216
217 directions = simplify(removeShortest(directions));
218 }
219
220 for (GestureList::const_iterator gi = d->gestures.begin(); gi != d->gestures.end(); ++gi) {
221 if (gi->directions.size() == 1) {
222 if (gi->directions.back() == NoMatch) {
223 gi->callbackClass->callback();
224 return true;
225 }
226 }
227 }
228
229 return false;
230}
231
232// Support functions implementation
233
234/*
235 * limitDirections - limits the directions of a list to up, down, left or right.
236 *
237 * Notice! This function converts the list to a set of relative moves instead of a set of screen coordinates.
238 */
239PosList MouseGestureRecognizer::limitDirections(const PosList &positions, bool allowDiagonals)
240{
241 PosList res;
242 int lastx, lasty;
243 bool firstTime = true;
244
245 for (PosList::const_iterator ii = positions.begin(); ii != positions.end(); ++ii) {
246 if (firstTime) {
247 lastx = ii->x;
248 lasty = ii->y;
249
250 firstTime = false;
251 }
252 else {
253 int dx, dy;
254
255 dx = ii->x - lastx;
256 dy = ii->y - lasty;
257
258 const int directions[8][2] = { {0, 15}, {0, -15}, {15, 0}, { -15, 0}, {10, 10}, { -10, 10}, { -10, -10}, {10, -10} };
259 int maxValue = 0;
260 int maxIndex = -1;
261
262 for (int i = 0; i < (allowDiagonals ? 8 : 4); i++) {
263 int value = dx * directions[i][0] + dy * directions[i][1];
264 if (value > maxValue) {
265 maxValue = value;
266 maxIndex = i;
267 }
268 }
269
270 if (maxIndex == -1) {
271 dx = dy = 0;
272 }
273 else {
274 dx = directions[maxIndex][0]; // * abs(sqrt(maxValue))
275 dy = directions[maxIndex][1]; // * abs(sqrt(maxValue))
276 }
277
278 res.push_back(Pos(dx, dy));
279
280 lastx = ii->x;
281 lasty = ii->y;
282 }
283 }
284
285 return res;
286}
287
288/*
289 * simplify - joins together continuous movements in the same direction.
290 *
291 * Notice! This function expects a list of limited directions.
292 */
293PosList MouseGestureRecognizer::simplify(const PosList &positions)
294{
295 PosList res;
296 int lastdx = 0, lastdy = 0;
297 bool firstTime = true;
298
299 for (PosList::const_iterator ii = positions.begin(); ii != positions.end(); ++ii) {
300 if (firstTime) {
301 lastdx = ii->x;
302 lastdy = ii->y;
303
304 firstTime = false;
305 }
306 else {
307 bool joined = false;
308
309 //horizontal lines
310 if (((lastdx > 0 && ii->x > 0) || (lastdx < 0 && ii->x < 0)) && (lastdy == 0 && ii->y == 0)) {
311 lastdx += ii->x;
312 joined = true;
313 }
314 //vertical
315 if (((lastdy > 0 && ii->y > 0) || (lastdy < 0 && ii->y < 0)) && (lastdx == 0 && ii->x == 0)) {
316 lastdy += ii->y;
317 joined = true;
318 }
319 //down right/left
320 if (((lastdx > 0 && ii->x > 0) || (lastdx < 0 && ii->x < 0)) && (lastdy > 0 && ii->y > 0)) {
321 lastdx += ii->x;
322 lastdy += ii->y;
323 joined = true;
324 }
325 //up left/right
326 if (((lastdx > 0 && ii->x > 0) || (lastdx < 0 && ii->x < 0)) && (lastdy < 0 && ii->y < 0)) {
327 lastdx += ii->x;
328 lastdy += ii->y;
329 joined = true;
330 }
331
332 if (!joined) {
333 res.push_back(Pos(lastdx, lastdy));
334
335 lastdx = ii->x;
336 lastdy = ii->y;
337 }
338 }
339 }
340
341 if (lastdx != 0 || lastdy != 0) {
342 res.push_back(Pos(lastdx, lastdy));
343 }
344
345 return res;
346}
347
348/*
349 * removeShortest - removes the shortest segment from a list of movements.
350 *
351 * If there are several equally short segments, the first one is removed.
352 */
353PosList MouseGestureRecognizer::removeShortest(const PosList &positions)
354{
355 PosList res;
356
357 int shortestSoFar;
358 PosList::const_iterator shortest;
359 bool firstTime = true;
360
361 for (PosList::const_iterator ii = positions.begin(); ii != positions.end(); ++ii) {
362 if (firstTime) {
363 shortestSoFar = ii->x * ii->x + ii->y * ii->y;
364 shortest = ii;
365
366 firstTime = false;
367 }
368 else {
369 if ((ii->x * ii->x + ii->y * ii->y) < shortestSoFar) {
370 shortestSoFar = ii->x * ii->x + ii->y * ii->y;
371 shortest = ii;
372 }
373 }
374 }
375
376 for (PosList::const_iterator ii = positions.begin(); ii != positions.end(); ++ii) {
377 if (ii != shortest) {
378 res.push_back(*ii);
379 }
380 }
381
382 return res;
383}
384
385/*
386 * calcLength - calculates the total length of the movements from a list of relative movements.
387 */
388int MouseGestureRecognizer::calcLength(const PosList &positions)
389{
390 int res = 0;
391
392 for (PosList::const_iterator ii = positions.begin(); ii != positions.end(); ++ii) {
393 if (ii->x > 0) {
394 res += ii->x;
395 }
396 else if (ii->x < 0) {
397 res -= ii->x;
398 }
399 else if (ii->y > 0) {
400 res += ii->y;
401 }
402 else {
403 res -= ii->y;
404 }
405 }
406
407 return res;
408}
409
void addGestureDefinition(const GestureDefinition &gesture)
int value(const QColor &c)
Definition: colors.cpp:238
std::vector< Pos > PosList
std::vector< GestureDefinition > GestureList
i
Definition: i18n.py:23