File indexing completed on 2025-01-05 04:35:06
0001 /* 0002 * This file is part of the mouse gesture package. 0003 * Copyright (C) 2006 Johan Thelin <e8johan@gmail.com> 0004 * All rights reserved. 0005 * 0006 * Redistribution and use in source and binary forms, with or 0007 * without modification, are permitted provided that the 0008 * following conditions are met: 0009 * 0010 * - Redistributions of source code must retain the above 0011 * copyright notice, this list of conditions and the 0012 * following disclaimer. 0013 * - Redistributions in binary form must reproduce the 0014 * above copyright notice, this list of conditions and 0015 * the following disclaimer in the documentation and/or 0016 * other materials provided with the distribution. 0017 * - The names of its contributors may be used to endorse 0018 * or promote products derived from this software without 0019 * specific prior written permission. 0020 * 0021 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 0022 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 0023 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 0024 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 0025 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 0026 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 0027 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 0028 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 0029 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 0030 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 0031 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 0032 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 0033 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 0034 * POSSIBILITY OF SUCH DAMAGE. 0035 * 0036 */ 0037 0038 #include "mousegesturerecognizer.h" 0039 0040 using namespace Gesture; 0041 0042 // Private data structure 0043 struct MouseGestureRecognizer::Private { 0044 PosList positions; 0045 GestureList gestures; 0046 0047 int minimumMovement2; 0048 double minimumMatch; 0049 0050 bool allowDiagonals; 0051 }; 0052 0053 // Class implementation 0054 0055 MouseGestureRecognizer::MouseGestureRecognizer(int minimumMovement, double minimumMatch, bool allowDiagonals) 0056 { 0057 d = new Private; 0058 d->minimumMovement2 = minimumMovement * minimumMovement; 0059 d->minimumMatch = minimumMatch; 0060 0061 d->allowDiagonals = allowDiagonals; 0062 } 0063 0064 MouseGestureRecognizer::~MouseGestureRecognizer() 0065 { 0066 delete d; 0067 } 0068 0069 void MouseGestureRecognizer::addGestureDefinition(const GestureDefinition &gesture) 0070 { 0071 d->gestures.push_back(gesture); 0072 } 0073 0074 void MouseGestureRecognizer::clearGestureDefinitions() 0075 { 0076 d->gestures.clear(); 0077 } 0078 0079 void MouseGestureRecognizer::startGesture(int x, int y) 0080 { 0081 d->positions.clear(); 0082 d->positions.push_back(Pos(x, y)); 0083 } 0084 0085 bool MouseGestureRecognizer::endGesture(int x, int y) 0086 { 0087 bool matched = false; 0088 0089 if (x != d->positions.back().x || y != d->positions.back().y) { 0090 d->positions.push_back(Pos(x, y)); 0091 } 0092 0093 int dx = x - d->positions.at(0).x; 0094 int dy = y - d->positions.at(0).y; 0095 0096 if (dx * dx + dy * dy < d->minimumMovement2) { 0097 return false; 0098 } 0099 0100 if (d->positions.size() > 1) { 0101 matched = recognizeGesture(); 0102 } 0103 0104 d->positions.clear(); 0105 0106 return matched; 0107 } 0108 0109 void MouseGestureRecognizer::abortGesture() 0110 { 0111 d->positions.clear(); 0112 } 0113 0114 void MouseGestureRecognizer::addPoint(int x, int y) 0115 { 0116 int dx, dy; 0117 0118 dx = x - d->positions.back().x; 0119 dy = y - d->positions.back().y; 0120 0121 if (dx * dx + dy * dy >= d->minimumMovement2) { 0122 d->positions.push_back(Pos(x, y)); 0123 } 0124 } 0125 0126 PosList MouseGestureRecognizer::currentPath() const 0127 { 0128 return d->positions; 0129 } 0130 0131 bool MouseGestureRecognizer::recognizeGesture() 0132 { 0133 PosList directions = simplify(limitDirections(d->positions, d->allowDiagonals)); 0134 double minLength = calcLength(directions) * d->minimumMatch; 0135 0136 while (directions.size() > 0 && calcLength(directions) > minLength) { 0137 for (GestureList::const_iterator gi = d->gestures.begin(); gi != d->gestures.end(); ++gi) { 0138 if (gi->directions.size() == directions.size()) { 0139 bool match = true; 0140 PosList::const_iterator pi = directions.begin(); 0141 for (DirectionList::const_iterator di = gi->directions.begin(); di != gi->directions.end() && match; ++di, ++pi) { 0142 switch (*di) { 0143 case UpLeft: 0144 if (!(pi->y < 0 && pi->x < 0)) { 0145 match = false; 0146 } 0147 0148 break; 0149 case UpRight: 0150 if (!(pi->y < 0 && pi->x > 0)) { 0151 match = false; 0152 } 0153 0154 break; 0155 case DownLeft: 0156 if (!(pi->y > 0 && pi->x < 0)) { 0157 match = false; 0158 } 0159 0160 break; 0161 case DownRight: 0162 if (!(pi->y > 0 && pi->x > 0)) { 0163 match = false; 0164 } 0165 0166 break; 0167 case Up: 0168 if (pi->y >= 0 || pi->x != 0) { 0169 match = false; 0170 } 0171 0172 break; 0173 case Down: 0174 if (pi->y <= 0 || pi->x != 0) { 0175 match = false; 0176 } 0177 0178 break; 0179 case Left: 0180 if (pi->x >= 0 || pi->y != 0) { 0181 match = false; 0182 } 0183 0184 break; 0185 case Right: 0186 if (pi->x <= 0 || pi->y != 0) { 0187 match = false; 0188 } 0189 0190 break; 0191 case AnyHorizontal: 0192 if (pi->x == 0 || pi->y != 0) { 0193 match = false; 0194 } 0195 0196 break; 0197 case AnyVertical: 0198 if (pi->y == 0 || pi->x != 0) { 0199 match = false; 0200 } 0201 0202 break; 0203 case NoMatch: 0204 match = false; 0205 0206 break; 0207 } 0208 } 0209 0210 if (match) { 0211 gi->callbackClass->callback(); 0212 return true; 0213 } 0214 } 0215 } 0216 0217 directions = simplify(removeShortest(directions)); 0218 } 0219 0220 for (GestureList::const_iterator gi = d->gestures.begin(); gi != d->gestures.end(); ++gi) { 0221 if (gi->directions.size() == 1) { 0222 if (gi->directions.back() == NoMatch) { 0223 gi->callbackClass->callback(); 0224 return true; 0225 } 0226 } 0227 } 0228 0229 return false; 0230 } 0231 0232 // Support functions implementation 0233 0234 /* 0235 * limitDirections - limits the directions of a list to up, down, left or right. 0236 * 0237 * Notice! This function converts the list to a set of relative moves instead of a set of screen coordinates. 0238 */ 0239 PosList MouseGestureRecognizer::limitDirections(const PosList &positions, bool allowDiagonals) 0240 { 0241 PosList res; 0242 int lastx, lasty; 0243 bool firstTime = true; 0244 0245 for (PosList::const_iterator ii = positions.begin(); ii != positions.end(); ++ii) { 0246 if (firstTime) { 0247 lastx = ii->x; 0248 lasty = ii->y; 0249 0250 firstTime = false; 0251 } 0252 else { 0253 int dx, dy; 0254 0255 dx = ii->x - lastx; 0256 dy = ii->y - lasty; 0257 0258 const int directions[8][2] = { {0, 15}, {0, -15}, {15, 0}, { -15, 0}, {10, 10}, { -10, 10}, { -10, -10}, {10, -10} }; 0259 int maxValue = 0; 0260 int maxIndex = -1; 0261 0262 for (int i = 0; i < (allowDiagonals ? 8 : 4); i++) { 0263 int value = dx * directions[i][0] + dy * directions[i][1]; 0264 if (value > maxValue) { 0265 maxValue = value; 0266 maxIndex = i; 0267 } 0268 } 0269 0270 if (maxIndex == -1) { 0271 dx = dy = 0; 0272 } 0273 else { 0274 dx = directions[maxIndex][0]; // * abs(sqrt(maxValue)) 0275 dy = directions[maxIndex][1]; // * abs(sqrt(maxValue)) 0276 } 0277 0278 res.push_back(Pos(dx, dy)); 0279 0280 lastx = ii->x; 0281 lasty = ii->y; 0282 } 0283 } 0284 0285 return res; 0286 } 0287 0288 /* 0289 * simplify - joins together continuous movements in the same direction. 0290 * 0291 * Notice! This function expects a list of limited directions. 0292 */ 0293 PosList MouseGestureRecognizer::simplify(const PosList &positions) 0294 { 0295 PosList res; 0296 int lastdx = 0, lastdy = 0; 0297 bool firstTime = true; 0298 0299 for (PosList::const_iterator ii = positions.begin(); ii != positions.end(); ++ii) { 0300 if (firstTime) { 0301 lastdx = ii->x; 0302 lastdy = ii->y; 0303 0304 firstTime = false; 0305 } 0306 else { 0307 bool joined = false; 0308 0309 //horizontal lines 0310 if (((lastdx > 0 && ii->x > 0) || (lastdx < 0 && ii->x < 0)) && (lastdy == 0 && ii->y == 0)) { 0311 lastdx += ii->x; 0312 joined = true; 0313 } 0314 //vertical 0315 if (((lastdy > 0 && ii->y > 0) || (lastdy < 0 && ii->y < 0)) && (lastdx == 0 && ii->x == 0)) { 0316 lastdy += ii->y; 0317 joined = true; 0318 } 0319 //down right/left 0320 if (((lastdx > 0 && ii->x > 0) || (lastdx < 0 && ii->x < 0)) && (lastdy > 0 && ii->y > 0)) { 0321 lastdx += ii->x; 0322 lastdy += ii->y; 0323 joined = true; 0324 } 0325 //up left/right 0326 if (((lastdx > 0 && ii->x > 0) || (lastdx < 0 && ii->x < 0)) && (lastdy < 0 && ii->y < 0)) { 0327 lastdx += ii->x; 0328 lastdy += ii->y; 0329 joined = true; 0330 } 0331 0332 if (!joined) { 0333 res.push_back(Pos(lastdx, lastdy)); 0334 0335 lastdx = ii->x; 0336 lastdy = ii->y; 0337 } 0338 } 0339 } 0340 0341 if (lastdx != 0 || lastdy != 0) { 0342 res.push_back(Pos(lastdx, lastdy)); 0343 } 0344 0345 return res; 0346 } 0347 0348 /* 0349 * removeShortest - removes the shortest segment from a list of movements. 0350 * 0351 * If there are several equally short segments, the first one is removed. 0352 */ 0353 PosList MouseGestureRecognizer::removeShortest(const PosList &positions) 0354 { 0355 PosList res; 0356 0357 int shortestSoFar; 0358 PosList::const_iterator shortest; 0359 bool firstTime = true; 0360 0361 for (PosList::const_iterator ii = positions.begin(); ii != positions.end(); ++ii) { 0362 if (firstTime) { 0363 shortestSoFar = ii->x * ii->x + ii->y * ii->y; 0364 shortest = ii; 0365 0366 firstTime = false; 0367 } 0368 else { 0369 if ((ii->x * ii->x + ii->y * ii->y) < shortestSoFar) { 0370 shortestSoFar = ii->x * ii->x + ii->y * ii->y; 0371 shortest = ii; 0372 } 0373 } 0374 } 0375 0376 for (PosList::const_iterator ii = positions.begin(); ii != positions.end(); ++ii) { 0377 if (ii != shortest) { 0378 res.push_back(*ii); 0379 } 0380 } 0381 0382 return res; 0383 } 0384 0385 /* 0386 * calcLength - calculates the total length of the movements from a list of relative movements. 0387 */ 0388 int MouseGestureRecognizer::calcLength(const PosList &positions) 0389 { 0390 int res = 0; 0391 0392 for (PosList::const_iterator ii = positions.begin(); ii != positions.end(); ++ii) { 0393 if (ii->x > 0) { 0394 res += ii->x; 0395 } 0396 else if (ii->x < 0) { 0397 res -= ii->x; 0398 } 0399 else if (ii->y > 0) { 0400 res += ii->y; 0401 } 0402 else { 0403 res -= ii->y; 0404 } 0405 } 0406 0407 return res; 0408 } 0409