KDEUI
kgesture.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kgesture.h"
00021 #include <klocalizedstring.h>
00022 #include <kdebug.h>
00023 #include <math.h>
00024 #include <QStringList>
00025
00026 inline float metric(float dx, float dy)
00027 {
00028
00029 return (dx*dx + dy*dy);
00030 }
00031
00032 class KShapeGesturePrivate
00033 {
00034 public:
00035 KShapeGesturePrivate()
00036 {
00037 }
00038 KShapeGesturePrivate(const KShapeGesturePrivate &other)
00039 : m_shape(other.m_shape),
00040 m_lengthTo(other.m_lengthTo),
00041 m_curveLength(other.m_curveLength)
00042 {
00043 }
00044 QPolygon m_shape;
00045 QVector<float> m_lengthTo;
00046 float m_curveLength;
00047 QString m_friendlyName;
00048 };
00049
00050 KShapeGesture::KShapeGesture()
00051 : d(new KShapeGesturePrivate)
00052 {
00053 }
00054
00055
00056 KShapeGesture::KShapeGesture(const QPolygon &shape)
00057 : d(new KShapeGesturePrivate)
00058 {
00059 setShape(shape);
00060 }
00061
00062
00063 KShapeGesture::KShapeGesture(const QString &description)
00064 : d(new KShapeGesturePrivate)
00065 {
00066 QStringList sl = description.split(',');
00067 d->m_friendlyName = sl.takeFirst();
00068
00069 bool ok = true;
00070 QPolygon poly;
00071 int x, y;
00072 QStringList::const_iterator it = sl.constBegin();
00073 while (it != sl.constEnd()) {
00074 x = (*it).toInt(&ok);
00075 ++it;
00076 if (!ok || it == sl.constEnd())
00077 break;
00078 y = (*it).toInt(&ok);
00079 if (!ok)
00080 break;
00081 ++it;
00082 poly.append(QPoint(x, y));
00083 }
00084 if (!ok) {
00085 d->m_friendlyName.clear();
00086 return;
00087 }
00088
00089 setShape(poly);
00090 }
00091
00092
00093 KShapeGesture::KShapeGesture(const KShapeGesture &other)
00094 : d(new KShapeGesturePrivate(*(other.d)))
00095 {
00096 }
00097
00098
00099 KShapeGesture::~KShapeGesture()
00100 {
00101 delete d;
00102 }
00103
00104
00105 void KShapeGesture::setShape(const QPolygon &shape)
00106 {
00107
00108
00109 d->m_shape = shape;
00110 QRect bounding = shape.boundingRect();
00111
00112
00113
00114
00115 bounding.setWidth(bounding.width() - 1);
00116 bounding.setHeight(bounding.height() - 1);
00117
00118 float xScale = bounding.width() ? 100.0 / bounding.width() : 1.0;
00119 float yScale = bounding.height() ? 100.0 / bounding.height() : 1.0;
00120 d->m_shape.translate(-bounding.left(), -bounding.top());
00121 for (int i=0; i < d->m_shape.size(); i++) {
00122 d->m_shape[i].setX((int)(xScale * (float)d->m_shape[i].x()));
00123 d->m_shape[i].setY((int)(yScale * (float)d->m_shape[i].y()));
00124 }
00125
00126
00127 Q_ASSERT(d->m_shape.size() > 1);
00128 d->m_curveLength = 0.0;
00129 d->m_lengthTo.clear();
00130 d->m_lengthTo.reserve(d->m_shape.size());
00131 d->m_lengthTo.append(d->m_curveLength);
00132
00133 int prevX = d->m_shape[0].x();
00134 int prevY = d->m_shape[0].y();
00135 for (int i=1; i < d->m_shape.size(); i++) {
00136 int curX = d->m_shape[i].x();
00137 int curY = d->m_shape[i].y();
00138 d->m_curveLength += metric(curX-prevX, curY - prevY);
00139 d->m_lengthTo.append(d->m_curveLength);
00140 prevX = curX;
00141 prevY = curY;
00142 }
00143 }
00144
00145
00146 void KShapeGesture::setShapeName(const QString &friendlyName)
00147 {
00148 d->m_friendlyName = friendlyName;
00149 }
00150
00151
00152 QString KShapeGesture::shapeName() const
00153 {
00154 return d->m_friendlyName;
00155 }
00156
00157
00158 bool KShapeGesture::isValid() const
00159 {
00160 return !d->m_shape.isEmpty();
00161 }
00162
00163
00164 QString KShapeGesture::toString() const
00165 {
00166 if (!isValid())
00167 return QString();
00168
00169
00170 QString ret = d->m_friendlyName;
00171
00172 int i;
00173 for (i = 0; i < d->m_shape.size(); i++) {
00174 ret.append(',');
00175 ret.append(QString::number(d->m_shape[i].x()));
00176 ret.append(',');
00177 ret.append(QString::number(d->m_shape[i].y()));
00178 }
00179
00180 return ret;
00181 }
00182
00183
00184 QByteArray KShapeGesture::toSvg(const QString &attributes) const
00185 {
00186 if (!isValid()) {
00187 return QByteArray();
00188
00189 }
00190 const char *prolog = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
00191 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
00192 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">"
00193 "<svg width=\"100\" height=\"100\" version=\"1.1\" "
00194 "xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M";
00195 const char *epilog1 = "\" fill=\"none\" ";
00196 const char *epilog2 = " /></svg>";
00197 QByteArray ret(prolog);
00198
00199 ret.append(QString::number(d->m_shape[0].x()).toUtf8());
00200 ret.append(",");
00201 ret.append(QString::number(d->m_shape[0].y()).toUtf8());
00202
00203 for (int i=1; i < d->m_shape.size(); i++) {
00204 ret.append("L");
00205 ret.append(QString::number(d->m_shape[i].x()).toUtf8());
00206 ret.append(",");
00207 ret.append(QString::number(d->m_shape[i].y()).toUtf8());
00208 }
00209
00210 ret.append(epilog1);
00211 ret.append(attributes.toUtf8());
00212 ret.append(epilog2);
00213 return ret;
00214 }
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225 float KShapeGesture::distance(const KShapeGesture &other, float abortThreshold) const
00226 {
00227 Q_UNUSED(abortThreshold);
00228 const QPolygon &o_shape = other.d->m_shape;
00229 const QVector<float> &o_lengthTo = other.d->m_lengthTo;
00230 float x = 0;
00231 float y = 0;
00232 float mx = 0;
00233 float my = 0;
00234 float position = 0;
00235 float ox = 0;
00236 float oy = 0;
00237 float oposition = 0;
00238 float omx = 0;
00239 float omy = 0;
00240 float oxB = 0;
00241 float oyB = 0;
00242 float opositionB = 0;
00243 float omxB = 0;
00244 float omyB = 0;
00245 float dist = 0;
00246 float distB = 0;
00247 float desiredPosition = 0;
00248 float strokeLength = 0;
00249 float retval = 0.0;
00250 int pointIndex = 0, opointIndex = 0, opointIndexB = 0;
00251
00252
00253 x = d->m_shape[0].x();
00254 y = d->m_shape[0].y();
00255 strokeLength = d->m_lengthTo[1];
00256 mx = (d->m_shape[1].x() - x) / strokeLength;
00257 my = (d->m_shape[1].y() - y) / strokeLength;
00258 position = 0.0;
00259
00260
00261 ox = o_shape[0].x();
00262 oy = o_shape[0].y();
00263 strokeLength = o_lengthTo[1];
00264 omx = (o_shape[1].x() - ox) / strokeLength;
00265 omy = (o_shape[1].y() - oy) / strokeLength;
00266 oposition = 0.0;
00267 dist = metric(ox-x, oy-y);
00268
00269 for (int i = 0; i <= 30; i++) {
00270
00271
00272 desiredPosition = d->m_curveLength / 30.0001 * (float)i;
00273 if (desiredPosition > d->m_lengthTo[pointIndex+1]) {
00274
00275 while (desiredPosition > d->m_lengthTo[pointIndex+1])
00276 pointIndex++;
00277
00278 x = d->m_shape[pointIndex].x();
00279 y = d->m_shape[pointIndex].y();
00280 position = d->m_lengthTo[pointIndex];
00281 strokeLength = d->m_lengthTo[pointIndex+1] - position;
00282 mx = (d->m_shape[pointIndex+1].x() - x) / strokeLength;
00283 my = (d->m_shape[pointIndex+1].y() - y) / strokeLength;
00284 }
00285 x += mx * (desiredPosition - position);
00286 y += my * (desiredPosition - position);
00287 position = desiredPosition;
00288
00289
00290 desiredPosition = qMin(oposition + other.d->m_curveLength / 15.00005,
00291 other.d->m_curveLength - 0.0001);
00292 if (i == 0 || desiredPosition > o_lengthTo[opointIndexB+1]) {
00293
00294 while (desiredPosition > o_lengthTo[opointIndexB+1])
00295 opointIndexB++;
00296
00297 oxB = o_shape[opointIndexB].x();
00298 oyB = o_shape[opointIndexB].y();
00299 opositionB = o_lengthTo[opointIndexB];
00300 strokeLength = o_lengthTo[opointIndexB+1] - opositionB;
00301 omxB = (o_shape[opointIndexB+1].x() - oxB) / strokeLength;
00302 omyB = (o_shape[opointIndexB+1].y() - oyB) / strokeLength;
00303 }
00304 oxB += omxB * (desiredPosition - opositionB);
00305 oyB += omyB * (desiredPosition - opositionB);
00306 opositionB = desiredPosition;
00307 distB = metric(oxB-x, oyB-y);
00308
00309
00310 for (int j = 0; j < 6; j++) {
00311 desiredPosition = (oposition + opositionB) * 0.5;
00312 if (dist < distB) {
00313
00314
00315 oxB = ox; oyB = oy;
00316 omxB = omx; omyB = omy;
00317 opointIndexB = opointIndex; opositionB = oposition;
00318
00319 if (desiredPosition > o_lengthTo[opointIndexB+1]) {
00320
00321 while (desiredPosition > o_lengthTo[opointIndexB+1])
00322 opointIndexB++;
00323
00324 oxB = o_shape[opointIndexB].x();
00325 oyB = o_shape[opointIndexB].y();
00326 opositionB = o_lengthTo[opointIndexB];
00327 strokeLength = o_lengthTo[opointIndexB+1] - opositionB;
00328 omxB = (o_shape[opointIndexB+1].x() - oxB) / strokeLength;
00329 omyB = (o_shape[opointIndexB+1].y() - oyB) / strokeLength;
00330 }
00331 oxB += omxB * (desiredPosition - opositionB);
00332 oyB += omyB * (desiredPosition - opositionB);
00333 opositionB = desiredPosition;
00334 distB = metric(oxB-x, oyB-y);
00335 } else {
00336
00337 if (desiredPosition > o_lengthTo[opointIndex+1]) {
00338
00339 while (desiredPosition > o_lengthTo[opointIndex+1])
00340 opointIndex++;
00341
00342 ox = o_shape[opointIndex].x();
00343 oy = o_shape[opointIndex].y();
00344 oposition = o_lengthTo[opointIndex];
00345 strokeLength = o_lengthTo[opointIndex+1] - oposition;
00346 omx = (o_shape[opointIndex+1].x() - ox) / strokeLength;
00347 omy = (o_shape[opointIndex+1].y() - oy) / strokeLength;
00348 }
00349 ox += omx * (desiredPosition - oposition);
00350 oy += omy * (desiredPosition - oposition);
00351 oposition = desiredPosition;
00352 dist = metric(ox-x, oy-y);
00353 }
00354 }
00355 retval += qMin(dist, distB);
00356 }
00357
00358 return retval / 30.0;
00359 }
00360
00361
00362 KShapeGesture &KShapeGesture::operator=(const KShapeGesture &other)
00363 {
00364 d->m_lengthTo = other.d->m_lengthTo;
00365 d->m_shape = other.d->m_shape;
00366 d->m_curveLength = other.d->m_curveLength;
00367 return *this;
00368 }
00369
00370
00371 bool KShapeGesture::operator==(const KShapeGesture &other) const
00372 {
00373
00374 if (fabs(d->m_curveLength - other.d->m_curveLength) > 0.1)
00375 return false;
00376 return d->m_shape == other.d->m_shape;
00377 }
00378
00379 bool KShapeGesture::operator!=(const KShapeGesture &other) const
00380 {
00381 return !operator==(other);
00382 }
00383
00384 uint KShapeGesture::hashable() const
00385 {
00386 uint hash = 0;
00387
00388 foreach (const QPoint &point, d->m_shape)
00389 hash += qHash(point.x()) + qHash(point.y());
00390
00391 return hash;
00392 }
00393
00394
00395
00396
00397
00398
00399 class KRockerGesturePrivate
00400 {
00401 public:
00402 KRockerGesturePrivate()
00403 : m_hold(Qt::NoButton),
00404 m_thenPush(Qt::NoButton)
00405 {
00406 }
00407 KRockerGesturePrivate(const KRockerGesturePrivate &other)
00408 : m_hold(other.m_hold),
00409 m_thenPush(other.m_thenPush)
00410 {
00411 }
00412 Qt::MouseButton m_hold;
00413 Qt::MouseButton m_thenPush;
00414 };
00415
00416 KRockerGesture::KRockerGesture()
00417 : d( new KRockerGesturePrivate )
00418 {
00419 }
00420
00421
00422 KRockerGesture::KRockerGesture(Qt::MouseButton hold, Qt::MouseButton thenPush)
00423 : d( new KRockerGesturePrivate )
00424 {
00425 setButtons(hold, thenPush);
00426 }
00427
00428
00429 KRockerGesture::KRockerGesture(const QString &description)
00430 : d( new KRockerGesturePrivate )
00431 {
00432 if (description.length() != 2)
00433 return;
00434
00435 Qt::MouseButton hold, thenPush;
00436 Qt::MouseButton *current = &hold;
00437 for (int i = 0; i < 2; i++) {
00438 switch (description[i].toLatin1()) {
00439 case 'L':
00440 *current = Qt::LeftButton;
00441 break;
00442 case 'R':
00443 *current = Qt::RightButton;
00444 break;
00445 case 'M':
00446 *current = Qt::MidButton;
00447 break;
00448 case '1':
00449 *current = Qt::XButton1;
00450 break;
00451 case '2':
00452 *current = Qt::XButton2;
00453 break;
00454 default:
00455 return;
00456 }
00457 current = &thenPush;
00458 }
00459 d->m_hold = hold;
00460 d->m_thenPush = thenPush;
00461 }
00462
00463
00464 KRockerGesture::KRockerGesture(const KRockerGesture &other)
00465 : d( new KRockerGesturePrivate(*(other.d)) )
00466 {
00467 }
00468
00469
00470 KRockerGesture::~KRockerGesture()
00471 {
00472 delete d;
00473 }
00474
00475
00476 void KRockerGesture::setButtons(Qt::MouseButton hold, Qt::MouseButton thenPush)
00477 {
00478 if (hold == thenPush) {
00479 d->m_hold = Qt::NoButton;
00480 d->m_thenPush = Qt::NoButton;
00481 return;
00482 }
00483
00484 int button = hold;
00485 for (int i = 0; i < 2; i++) {
00486 switch (button) {
00487 case Qt::LeftButton:
00488 case Qt::RightButton:
00489 case Qt::MidButton:
00490 case Qt::XButton1:
00491 case Qt::XButton2:
00492 break;
00493 default:
00494 d->m_hold = Qt::NoButton;
00495 d->m_thenPush = Qt::NoButton;
00496 return;
00497 }
00498 button = thenPush;
00499 }
00500
00501 d->m_hold = hold;
00502 d->m_thenPush = thenPush;
00503 }
00504
00505
00506 void KRockerGesture::getButtons(Qt::MouseButton *hold, Qt::MouseButton *thenPush) const
00507 {
00508 *hold = d->m_hold;
00509 *thenPush = d->m_thenPush;
00510 }
00511
00512
00513 QString KRockerGesture::mouseButtonName(Qt::MouseButton button)
00514 {
00515 switch (button) {
00516 case Qt::LeftButton:
00517 return i18nc("left mouse button", "left button");
00518 break;
00519 case Qt::MidButton:
00520 return i18nc("middle mouse button", "middle button");
00521 break;
00522 case Qt::RightButton:
00523 return i18nc("right mouse button", "right button");
00524 break;
00525 default:
00526 return i18nc("a nonexistent value of mouse button", "invalid button");
00527 break;
00528 }
00529 }
00530
00531
00532 QString KRockerGesture::rockerName() const
00533 {
00534 if (!isValid())
00535 return QString();
00536
00537
00538 else
00539 return i18nc("a kind of mouse gesture: hold down one mouse button, then press another button",
00540 "Hold %1, then push %2", mouseButtonName(d->m_hold), mouseButtonName(d->m_thenPush));
00541 }
00542
00543
00544 bool KRockerGesture::isValid() const
00545 {
00546 return (d->m_hold != Qt::NoButton);
00547 }
00548
00549
00550 QString KRockerGesture::toString() const
00551 {
00552 if (!isValid())
00553 return QString();
00554 QString ret;
00555 int button = d->m_hold;
00556 char desc;
00557 for (int i = 0; i < 2; i++) {
00558 switch (button) {
00559 case Qt::LeftButton:
00560 desc = 'L';
00561 break;
00562 case Qt::RightButton:
00563 desc = 'R';
00564 break;
00565 case Qt::MidButton:
00566 desc = 'M';
00567 break;
00568 case Qt::XButton1:
00569 desc = '1';
00570 break;
00571 case Qt::XButton2:
00572 desc = '2';
00573 break;
00574 default:
00575 return QString();
00576 }
00577 ret.append(desc);
00578 button = d->m_thenPush;
00579 }
00580 return ret;
00581 }
00582
00583
00584 KRockerGesture &KRockerGesture::operator=(const KRockerGesture &other)
00585 {
00586 d->m_hold = other.d->m_hold;
00587 d->m_thenPush = other.d->m_thenPush;
00588 return *this;
00589 }
00590
00591
00592 bool KRockerGesture::operator==(const KRockerGesture &other) const
00593 {
00594 return d->m_hold == other.d->m_hold && d->m_thenPush == other.d->m_thenPush;
00595 }
00596
00597 bool KRockerGesture::operator!=(const KRockerGesture &other) const
00598 {
00599 return !operator==(other);
00600 }
00601
00602 uint KRockerGesture::hashable() const
00603 {
00604
00605 return qHash(d->m_hold) + d->m_thenPush;
00606 }