• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDEUI

kgesturemap.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002     Copyright (C) 2006,2007 Andreas Hartmetz (ahartmetz@gmail.com)
00003 
00004     This library is free software; you can redistribute it and/or
00005     modify it under the terms of the GNU Library General Public
00006     License as published by the Free Software Foundation; either
00007     version 2 of the License, or (at your option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to
00016     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017     Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "kgesturemap.h"
00021 
00022 #include <kapplication.h>
00023 #include <kaction.h>
00024 #include <QtGui/QActionEvent>
00025 
00026 #include <kglobal.h>
00027 
00028 #include <kdebug.h>
00029 
00030 /*
00031  This is a class for internal use by the KDE libraries only. This class
00032  may change or go away without notice so don't try to use it in non-kdelibs
00033  code.
00034  */
00035 
00036 class KGestureMapContainer {
00037 public:
00038     KGestureMap gestureMap;
00039 };
00040 
00041 
00042 K_GLOBAL_STATIC(KGestureMapContainer, g_instance)
00043 
00044 
00045 KGestureMap::~KGestureMap()
00046 {
00047 }
00048 
00049 
00050 KGestureMap *KGestureMap::self()
00051 {
00052     return &g_instance->gestureMap;
00053 }
00054 
00055 
00056 KGestureMap::KGestureMap()
00057 {
00058     m_gestureTimeout.setSingleShot(true);
00059     connect(&m_gestureTimeout, SIGNAL(timeout()), this, SLOT(stopAcquisition()));
00060     //It would be nice to install the filter on demand. Unfortunately,
00061     //undesired behavior might result due to changing invocation
00062     //orders of different event filters.
00063     if (qApp)
00064         qApp->installEventFilter(this);
00065 }
00066 
00067 
00068 void KGestureMap::addGesture(const KShapeGesture &gesture, KAction *act)
00069 {
00070     if (!gesture.isValid() || !act)
00071         return;
00072     kDebug(283) << "KGestureMap::addGesture(KShapeGesture ...)";
00073     if (!m_shapeGestures.contains(gesture))
00074         m_shapeGestures.insert(gesture, act);
00075     else
00076         kDebug(283) << "Tried to register an action for a gesture already taken";
00077 }
00078 
00079 
00080 void KGestureMap::addGesture(const KRockerGesture &gesture, KAction *act)
00081 {
00082     if (!gesture.isValid() || !act)
00083         return;
00084     kDebug(283) << "KGestureMap::addGesture(KRockerGesture ...)";
00085     if (!m_rockerGestures.contains(gesture))
00086         m_rockerGestures.insert(gesture, act);
00087     else
00088         kDebug(283) << "Tried to register an action for a gesture already taken";
00089 }
00090 
00091 
00092 void KGestureMap::removeGesture(const KShapeGesture &gesture, KAction *act)
00093 {
00094     if (!gesture.isValid())
00095         return;
00096     kDebug(283) << "KGestureMap::removeGesture(KShapeGesture ...)";
00097     KAction *oldAct = m_shapeGestures.value(gesture);
00098     if (oldAct == act || !act /*wildcard*/)
00099         m_shapeGestures.remove(gesture);
00100 }
00101 
00102 
00103 void KGestureMap::removeGesture(const KRockerGesture &gesture, KAction *act)
00104 {
00105     if (!gesture.isValid())
00106         return;
00107     kDebug(283) << "KGestureMap::removeGesture(KRockerGesture ...)";
00108     KAction *oldAct = m_rockerGestures.value(gesture);
00109     if (oldAct == act || !act /*wildcard*/)
00110         m_rockerGestures.remove(gesture);
00111 }
00112 
00113 
00114 KAction *KGestureMap::findAction(const KShapeGesture &gesture) const
00115 {
00116     return m_shapeGestures.value(gesture);
00117 }
00118 
00119 
00120 KAction *KGestureMap::findAction(const KRockerGesture &gesture) const
00121 {
00122     return m_rockerGestures.value(gesture);
00123 }
00124 
00125 
00126 void KGestureMap::installEventFilterOnMe(KApplication *app)
00127 {
00128     app->installEventFilter(this);
00129 }
00130 
00131 
00132 inline int KGestureMap::bitCount(int n)
00133 {
00134     int count = 0;
00135     while (n) {
00136         n &= (n - 1);
00137         count++;
00138     }
00139     return count;
00140 }
00141 
00142 
00143 void KGestureMap::handleAction(KAction *kact)
00144 {
00145     if (!kact)
00146         return;
00147     kDebug(283) << "handleAction";
00148     //TODO: only activate in the action's context, just like keyboard shortcuts
00149     kact->trigger();
00150     return;
00151 }
00152 
00153 
00154 void KGestureMap::matchShapeGesture()
00155 {
00156     //TODO: tune and tweak until satisfied with result :)
00157     m_shapeGesture.setShape(m_points);
00158     float dist, minDist = 20.0;
00159     KAction *bestMatch = 0;
00160 
00161     for (QHash<KShapeGesture, KAction *>::const_iterator it = m_shapeGestures.constBegin(); 
00162         it != m_shapeGestures.constEnd(); ++it) {
00163         dist = m_shapeGesture.distance(it.key(), 1000.0);
00164         if (dist < minDist) {
00165             minDist = dist;
00166             bestMatch = it.value();
00167         }
00168     }
00169     handleAction(bestMatch);
00170 }
00171 
00172 
00173 //slot
00174 void KGestureMap::stopAcquisition()
00175 {
00176     m_gestureTimeout.stop();
00177     m_acquiring = false;
00178 }
00179 
00180 
00181 //TODO: Probably kwin, kded and others should not have a gesture map.
00182 //Maybe making them friends and providing a private "die()" function would work.
00183 /* 
00184  * Act on rocker gestures immediately and collect movement data for evaluation.
00185  * The decision when to consume and when to relay an event is quite tricky.
00186  * I decided to only consume clicks that belong to completed rocker gestures.
00187  * A user might e.g. go back in a browser several times using rocker gestures,
00188  * thus changing what's under the cursor every time. This might lead to
00189  * unintended clicks on links where there was free space before.
00190  */
00191 
00192 bool KGestureMap::eventFilter(QObject *obj, QEvent *e)
00193 {
00194     //disable until it does not interfere with other input any more
00195     return false;
00196     Q_UNUSED(obj);
00197     int type = e->type();
00198 
00199     //catch right-clicks disguised as context menu events. if we ignore a
00200     //context menu event caused by a right-click, it should get resent
00201     //as a right-click event, according to documentation.
00202     //### this is preliminary
00203     if (type == QEvent::ContextMenu) {
00204         QContextMenuEvent *cme = static_cast<QContextMenuEvent *>(e);
00205         if (cme->reason() == QContextMenuEvent::Mouse) {
00206             cme->ignore();
00207             return true;
00208         }
00209         return false;
00210     }
00211 
00212     if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove)
00213         return false;
00214 
00215     QMouseEvent *me = static_cast<QMouseEvent *>(e);
00216     if (type == QEvent::MouseButtonPress) {
00217         int nButtonsDown = bitCount(me->buttons());
00218         kDebug(283) << "number of buttons down:" << nButtonsDown;
00219 
00220         //right button down starts gesture acquisition
00221         if (nButtonsDown == 1 && me->button() == Qt::RightButton) {
00222             //"startAcquisition()"
00223             m_acquiring = true;
00224             m_gestureTimeout.start(4000);
00225             kDebug(283) << "========================";
00226             m_points.clear();
00227             m_points.append(me->pos());
00228             return true;
00229         } else if (nButtonsDown != 2)
00230             return false;
00231 
00232         //rocker gestures. do not trigger any movement gestures from now on.
00233         stopAcquisition();
00234         int buttonHeld = me->buttons() ^ me->button();
00235         m_rockerGesture.setButtons(static_cast<Qt::MouseButton>(buttonHeld), me->button());
00236         KAction *match = m_rockerGestures.value(m_rockerGesture);
00237         if (!match)
00238             return false;
00239         handleAction(match);
00240         return true;
00241     }
00242 
00243     if (m_acquiring) {
00244         if (type == QEvent::MouseMove) {
00245             m_points.append(me->pos());
00246             //abort to avoid using too much memory. 1010 points should be enough
00247             //for everyone! :)
00248             //next reallocation of m_points would happen at 1012 items
00249             if (m_points.size() > 1010)
00250                 stopAcquisition();
00251             return true;
00252         } else if (type == QEvent::MouseButtonRelease && me->button() == Qt::RightButton) {
00253             stopAcquisition();
00254 
00255             //TODO: pre-selection of gestures by length (optimization), if necessary
00256             //possibly apply other heuristics
00257             //then try all remaining gestures for sufficiently small distance
00258             int dist = 0;
00259             for (int i = 1; i < m_points.size(); i++) {
00260                 dist += (m_points[i] - m_points[i-1]).manhattanLength();
00261                 if (dist > 40) {
00262                     matchShapeGesture();
00263                     return true;
00264                 }
00265                 //this was probably a small glitch while right-clicking if we get here.
00266                 //TODO: open the context menu or do whatever happens on right-click (how?)
00267             }
00268             return false;
00269         }
00270     }
00271     return false;
00272 }
00273 
00274 #include "kgesturemap.moc"

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal