diff --git a/kdeui/kernel/kapplication.cpp b/kdeui/kernel/kapplication.cpp index 7ff70c770c..d2734c615e 100644 --- a/kdeui/kernel/kapplication.cpp +++ b/kdeui/kernel/kapplication.cpp @@ -1,1151 +1,1162 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) Copyright (C) 1998, 1999, 2000 KDE Team This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #undef QT_NO_TRANSLATION #include #define QT_NO_TRANSLATION #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef QT_NO_TRANSLATION #include "kapplication.h" #define QT_NO_TRANSLATION #include "kauthorized.h" #include "kaboutdata.h" #include "kcheckaccelerators.h" #include "kcrash.h" #include "kconfig.h" #include "kcmdlineargs.h" #include "kclipboard.h" #include "kglobalsettings.h" #include "kdebug.h" #include "kglobal.h" #include "kiconloader.h" #include "klibloader.h" #include "klocale.h" #include "ksessionmanager.h" #include "kstandarddirs.h" #include "kstandardshortcut.h" #include "ktoolinvocation.h" +#include "kgesturemap.h" #include "kurl.h" #if defined Q_WS_X11 #include #include #endif #include #ifdef HAVE_SYS_STAT_H #include #endif #include #ifndef Q_WS_WIN #include "kwin.h" #endif #include #include // getenv(), srand(), rand() #include #include #include #include #include #include #include #if defined Q_WS_X11 //#ifndef Q_WS_QWS //FIXME(E): NetWM should talk to QWS... #include #endif #ifdef HAVE_PATHS_H #include #endif #ifdef Q_WS_X11 #include #include #include #include #include #include #endif #ifdef Q_WS_MACX // ick #undef Status #include #include #include #endif #include #include #include #include KApplication* KApplication::KApp = 0L; bool KApplication::loadedByKdeinit = false; #ifdef Q_WS_X11 static Atom atom_DesktopWindow; static Atom atom_NetSupported; static Atom kde_xdnd_drop; #endif // duplicated from patched Qt, so that there won't be unresolved symbols if Qt gets // replaced by unpatched one KDEUI_EXPORT bool qt_qclipboard_bailout_hack = false; template class QList; #ifdef Q_WS_X11 extern "C" { static int kde_xio_errhandler( Display * dpy ) { return kapp->xioErrhandler( dpy ); } static int kde_x_errhandler( Display *dpy, XErrorEvent *err ) { return kapp->xErrhandler( dpy, err ); } } #endif #ifdef Q_WS_WIN void KApplication_init_windows(); #endif /* Private data to make keeping binary compatibility easier */ class KApplication::Private { public: Private(const QByteArray &cName) : componentData(cName), checkAccelerators(0), + gestureMap(0), startup_id("0"), app_started_timer(0), session_save(false) #ifdef Q_WS_X11 , oldIceIOErrorHandler(0) , oldXErrorHandler(0) , oldXIOErrorHandler(0) #endif { } Private(const KComponentData &cData) : componentData(cData), checkAccelerators(0), + gestureMap(0), startup_id("0"), app_started_timer(0), session_save(false) #ifdef Q_WS_X11 , oldIceIOErrorHandler(0) , oldXErrorHandler(0) , oldXIOErrorHandler(0) #endif { } Private() : componentData(KCmdLineArgs::aboutData()), checkAccelerators(0), + gestureMap(0), startup_id( "0" ), app_started_timer( 0 ), session_save( false ) #ifdef Q_WS_X11 ,oldIceIOErrorHandler( 0 ) ,oldXErrorHandler( 0 ) ,oldXIOErrorHandler( 0 ) #endif { } ~Private() { } KComponentData componentData; KCheckAccelerators* checkAccelerators; + KGestureMap* gestureMap; QByteArray startup_id; QTimer* app_started_timer; bool session_save; #ifdef Q_WS_X11 IceIOErrorHandler oldIceIOErrorHandler; int (*oldXErrorHandler)(Display*,XErrorEvent*); int (*oldXIOErrorHandler)(Display*); #endif QString sessionKey; QString pSessionConfigFile; }; static QList *x11Filter = 0; /** * Installs a handler for the SIGPIPE signal. It is thrown when you write to * a pipe or socket that has been closed. * The handler is installed automatically in the constructor, but you may * need it if your application or component does not have a KApplication * instance. */ static void installSigpipeHandler() { #ifdef Q_OS_UNIX struct sigaction act; act.sa_handler = SIG_IGN; sigemptyset( &act.sa_mask ); act.sa_flags = 0; sigaction( SIGPIPE, &act, 0 ); #endif } void KApplication::installX11EventFilter( QWidget* filter ) { if ( !filter ) return; if (!x11Filter) x11Filter = new QList; connect ( filter, SIGNAL( destroyed() ), this, SLOT( x11FilterDestroyed() ) ); x11Filter->append( filter ); } void KApplication::x11FilterDestroyed() { removeX11EventFilter( static_cast< const QWidget* >( sender())); } void KApplication::removeX11EventFilter( const QWidget* filter ) { if ( !x11Filter || !filter ) return; x11Filter->removeAll( filter ); if ( x11Filter->isEmpty() ) { delete x11Filter; x11Filter = 0; } } bool KApplication::notify(QObject *receiver, QEvent *event) { QEvent::Type t = event->type(); if( t == QEvent::Show && receiver->isWidgetType()) { QWidget* w = static_cast< QWidget* >( receiver ); #if defined Q_WS_X11 if( w->isTopLevel() && !startupId().isEmpty()) // TODO better done using window group leader? KStartupInfo::setWindowStartupId( w->winId(), startupId()); #endif if( w->isTopLevel() && !( w->windowFlags() & Qt::X11BypassWindowManagerHint ) && w->windowType() != Qt::Popup && !event->spontaneous()) { if( d->app_started_timer == NULL ) { d->app_started_timer = new QTimer( this ); connect( d->app_started_timer, SIGNAL( timeout()), SLOT( checkAppStartedSlot())); } if( !d->app_started_timer->isActive()) { d->app_started_timer->setSingleShot( true ); d->app_started_timer->start( 0 ); } } } return QApplication::notify(receiver, event); } void KApplication::checkAppStartedSlot() { #if defined Q_WS_X11 KStartupInfo::handleAutoAppStartedSending(); #endif } /* Auxiliary function to calculate a a session config name used for the instance specific config object. Syntax: "session/_" */ QString KApplication::sessionConfigName() const { #ifdef QT_NO_SESSIONMANAGER #error QT_NO_SESSIONMANAGER was set, this will not compile. Reconfigure Qt with Session management support. #endif QString sessKey = sessionKey(); if ( sessKey.isEmpty() && !d->sessionKey.isEmpty() ) sessKey = d->sessionKey; return QString(QLatin1String("session/%1_%2_%3")).arg(applicationName()).arg(sessionId()).arg(sessKey); } #ifdef Q_WS_X11 static SmcConn mySmcConnection = 0; #else // FIXME(E): Implement for Qt Embedded // Possibly "steal" XFree86's libSM? #endif KApplication::KApplication(bool GUIenabled) : QApplication(KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv(), GUIenabled), d(new Private) { read_app_startup_id(); setApplicationName(QLatin1String(d->componentData.componentName())); setOrganizationDomain( KCmdLineArgs::about->organizationDomain() ); installSigpipeHandler(); init(GUIenabled); } #ifdef Q_WS_X11 KApplication::KApplication(Display *dpy, Qt::HANDLE visual, Qt::HANDLE colormap) : QApplication(dpy, KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv(), visual, colormap), d(new Private) { read_app_startup_id(); setApplicationName(QLatin1String(d->componentData.componentName())); setOrganizationDomain( KCmdLineArgs::about->organizationDomain() ); installSigpipeHandler(); init(); } KApplication::KApplication(Display *dpy, Qt::HANDLE visual, Qt::HANDLE colormap, const KComponentData &cData) : QApplication(dpy, KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv(), visual, colormap), d (new Private(cData)) { read_app_startup_id(); setApplicationName(QLatin1String(d->componentData.componentName())); setOrganizationDomain(d->componentData.aboutData()->organizationDomain()); installSigpipeHandler(); init(); } #endif KApplication::KApplication(bool GUIenabled, const KComponentData &cData) : QApplication(KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv(), GUIenabled), d (new Private(cData)) { read_app_startup_id(); setApplicationName(QLatin1String(d->componentData.componentName())); setOrganizationDomain(d->componentData.aboutData()->organizationDomain()); installSigpipeHandler(); init(); } #ifdef Q_WS_X11 KApplication::KApplication(Display *display, int& argc, char** argv, const QByteArray& rAppName, bool GUIenabled) : QApplication(display), d(new Private(rAppName)) { Q_UNUSED(GUIenabled); read_app_startup_id(); setApplicationName(QLatin1String(rAppName)); installSigpipeHandler(); KCmdLineArgs::initIgnore(argc, argv, rAppName.data()); init(); } #endif int KApplication::xioErrhandler( Display* dpy ) { if(kapp) { emit aboutToQuit(); #ifdef Q_WS_X11 d->oldXIOErrorHandler( dpy ); #else Q_UNUSED(dpy); #endif } exit( 1 ); return 0; } int KApplication::xErrhandler( Display* dpy, void* err_ ) { // no idea how to make forward decl. for XErrorEvent #ifdef Q_WS_X11 XErrorEvent* err = static_cast< XErrorEvent* >( err_ ); if(kapp) { // add KDE specific stuff here d->oldXErrorHandler( dpy, err ); } #endif return 0; } void KApplication::iceIOErrorHandler( _IceConn *conn ) { emit aboutToQuit(); #ifdef Q_WS_X11 if ( d->oldIceIOErrorHandler != NULL ) (*d->oldIceIOErrorHandler)( conn ); #endif exit( 1 ); } class KDETranslator : public QTranslator { public: KDETranslator(QObject *parent) : QTranslator(parent) { setObjectName(QLatin1String("kdetranslator")); } virtual QString translate(const char* context, const char *sourceText, const char* message) const { return KGlobal::locale()->translateQt(context, sourceText, message); } }; void KApplication::init(bool GUIenabled) { if ((getuid() != geteuid()) || (getgid() != getegid())) { fprintf(stderr, "The KDE libraries are not designed to run with suid privileges.\n"); ::exit(127); } if ( type() == GuiClient ) { QStringList plugins = KGlobal::dirs()->resourceDirs( "qtplugins" ); QStringList::Iterator it = plugins.begin(); while (it != plugins.end()) { addLibraryPath( *it ); ++it; } } KApp = this; parseCommandLine(); if(GUIenabled) (void) KClipboardSynchronizer::self(); extern KDECORE_EXPORT bool kde_kdebug_enable_dbus_interface; kde_kdebug_enable_dbus_interface = true; QApplication::setDesktopSettingsAware( false ); #ifdef Q_WS_X11 //FIXME(E) // create all required atoms in _one_ roundtrip to the X server if ( type() == GuiClient ) { const int max = 20; Atom* atoms[max]; char* names[max]; Atom atoms_return[max]; int n = 0; atoms[n] = &atom_DesktopWindow; names[n++] = (char *) "KDE_DESKTOP_WINDOW"; atoms[n] = &atom_NetSupported; names[n++] = (char *) "_NET_SUPPORTED"; atoms[n] = &kde_xdnd_drop; names[n++] = (char *) "XdndDrop"; XInternAtoms( QX11Info::display(), names, n, false, atoms_return ); for (int i = 0; i < n; i++ ) *atoms[i] = atoms_return[i]; } #endif // sanity checking, to make sure we've connected extern void qDBusBindToApplication(); qDBusBindToApplication(); QDBusConnectionInterface *bus = 0; if (!QDBusConnection::sessionBus().isConnected() || !(bus = QDBusConnection::sessionBus().interface())) { } extern bool s_kuniqueapplication_startCalled; if ( bus && !s_kuniqueapplication_startCalled ) // don't register again if KUniqueApplication did so already { QStringList parts = organizationDomain().split(QLatin1Char('.'), QString::SkipEmptyParts); QString reversedDomain; if (parts.isEmpty()) reversedDomain = QLatin1String("local."); else foreach (const QString& s, parts) { reversedDomain.prepend(QLatin1Char('.')); reversedDomain.prepend(s); } const QString pidSuffix = QString::number( getpid() ).prepend( QLatin1String("-") ); const QString serviceName = reversedDomain + applicationName() + pidSuffix; if ( bus->registerService(serviceName) == QDBusConnectionInterface::ServiceNotRegistered ) { kError(101) << "Couldn't register name '" << serviceName << "' with DBUS - another process owns it already!" << endl; ::exit(126); } } QDBusConnection::sessionBus().registerObject(QLatin1String("/MainApplication"), this, QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportScriptableProperties | QDBusConnection::ExportAdaptors); // Trigger creation of locale. (void) KGlobal::locale(); KSharedConfig::Ptr config = d->componentData.config(); QByteArray readOnly = getenv("KDE_HOME_READONLY"); if (readOnly.isEmpty() && applicationName() != QLatin1String("kdialog")) { if (KAuthorized::authorize(QLatin1String("warn_unwritable_config"))) config->checkConfigFilesWritable(true); } if (type() == GuiClient) { #ifdef Q_WS_X11 // this is important since we fork() to launch the help (Matthias) fcntl(ConnectionNumber(QX11Info::display()), F_SETFD, FD_CLOEXEC); // set up the fancy (=robust and error ignoring ) KDE xio error handlers (Matthias) d->oldXErrorHandler = XSetErrorHandler( kde_x_errhandler ); d->oldXIOErrorHandler = XSetIOErrorHandler( kde_xio_errhandler ); #endif // Trigger initial settings KGlobalSettings::self(); d->checkAccelerators = new KCheckAccelerators( this ); + d->gestureMap = new KGestureMap( this ); connect(KToolInvocation::self(), SIGNAL(kapplication_hook(QStringList&, QByteArray&)), this, SLOT(slot_KToolInvication_hook(QStringList&,QByteArray&))); } #ifdef Q_WS_MAC if (type() == GuiClient) { QSystemTrayIcon *trayIcon; QPixmap pixmap = KIconLoader::global()->loadIcon( KCmdLineArgs::appName(), K3Icon::NoGroup, K3Icon::SizeEnormous, K3Icon::DefaultState, 0L, false ); if (!pixmap.isNull() && QSystemTrayIcon::isSystemTrayAvailable()) { trayIcon = new QSystemTrayIcon(this); trayIcon->setIcon(QIcon(pixmap)); /* it's counter-intuitive, but once you do setIcon it's already set the dock icon... ->show actually shows an icon in the menu bar too :P */ // trayIcon->show(); } } #endif // save and restore the RTL setting, as installTranslator calls qt_detectRTLLanguage, // which makes it impossible to use the -reverse cmdline switch with KDE apps // FIXME is this still needed? it looks like QApplication takes care of this bool rtl = isRightToLeft(); installTranslator(new KDETranslator(this)); setLayoutDirection( rtl ? Qt::RightToLeft:Qt::LeftToRight); if (i18nc( "Dear Translator! Translate this string to the string 'LTR' in " "left-to-right languages (as English) or to 'RTL' in right-to-left " "languages (such as Hebrew and Arabic) to get proper widget layout.", "LTR" ) == QLatin1String("RTL")) rtl = !rtl; setLayoutDirection( rtl ? Qt::RightToLeft:Qt::LeftToRight); pSessionConfig = 0L; bSessionManagement = true; qRegisterMetaType(); qRegisterMetaType(); #ifdef Q_WS_WIN KApplication_init_windows(); #endif } KConfig* KApplication::sessionConfig() { if (!pSessionConfig) // create an instance specific config object pSessionConfig = new KConfig( sessionConfigName(), KConfig::NoGlobals ); return pSessionConfig; } void KApplication::reparseConfiguration() { KGlobal::config()->reparseConfiguration(); } void KApplication::quit() { QApplication::quit(); } +KGestureMap* KApplication::gestureMap() const +{ + return d->gestureMap; +} + void KApplication::disableSessionManagement() { bSessionManagement = false; } void KApplication::enableSessionManagement() { bSessionManagement = true; #ifdef Q_WS_X11 // Session management support in Qt/KDE is awfully broken. // If konqueror disables session management right after its startup, // and enables it later (preloading stuff), it won't be properly // saved on session shutdown. // I'm not actually sure why it doesn't work, but saveState() // doesn't seem to be called on session shutdown, possibly // because disabling session management after konqueror startup // disabled it somehow. Forcing saveState() here for this application // seems to fix it. if( mySmcConnection ) { SmcRequestSaveYourself( mySmcConnection, SmSaveLocal, False, SmInteractStyleAny, False, False ); // flush the request IceFlush(SmcGetIceConnection(mySmcConnection)); } #endif } void KApplication::commitData( QSessionManager& sm ) { d->session_save = true; bool canceled = false; foreach (KSessionManager *it, KSessionManager::sessionClients()) { if ( ( canceled = !it->commitData( sm ) ) ) break; } if ( canceled ) sm.cancel(); if ( sm.allowsInteraction() ) { QWidgetList donelist, todolist; QWidget* w; commitDataRestart: todolist = QApplication::topLevelWidgets(); for ( int i = 0; i < todolist.size(); ++i ) { w = todolist.at( i ); if( !w ) break; if ( donelist.contains( w ) ) continue; if ( !w->isHidden() && !w->inherits( "KMainWindow" ) ) { QCloseEvent e; sendEvent( w, &e ); if ( !e.isAccepted() ) break; //canceled donelist.append( w ); //grab the new list that was just modified by our closeevent goto commitDataRestart; } } } if ( !bSessionManagement ) sm.setRestartHint( QSessionManager::RestartNever ); else sm.setRestartHint( QSessionManager::RestartIfRunning ); d->session_save = false; } void KApplication::saveState( QSessionManager& sm ) { d->session_save = true; #ifdef Q_WS_X11 static bool firstTime = true; mySmcConnection = (SmcConn) sm.handle(); if ( !bSessionManagement ) { sm.setRestartHint( QSessionManager::RestartNever ); d->session_save = false; return; } else sm.setRestartHint( QSessionManager::RestartIfRunning ); if ( firstTime ) { firstTime = false; d->session_save = false; return; // no need to save the state. } // remove former session config if still existing, we want a new // and fresh one. Note that we do not delete the config file here, // this is done by the session manager when it executes the // discard commands. In fact it would be harmful to remove the // file here, as the session might be stored under a different // name, meaning the user still might need it eventually. if ( pSessionConfig ) { delete pSessionConfig; pSessionConfig = 0; } // tell the session manager about our new lifecycle QStringList restartCommand = sm.restartCommand(); QByteArray multiHead = getenv("KDE_MULTIHEAD"); if (multiHead.toLower() == "true") { // if multihead is enabled, we save our -display argument so that // we are restored onto the correct head... one problem with this // is that the display is hard coded, which means we cannot restore // to a different display (ie. if we are in a university lab and try, // try to restore a multihead session, our apps could be started on // someone else's display instead of our own) QByteArray displayname = getenv("DISPLAY"); if (! displayname.isNull()) { // only store the command if we actually have a DISPLAY // environment variable restartCommand.append(QLatin1String("-display")); restartCommand.append(QLatin1String(displayname)); } sm.setRestartCommand( restartCommand ); } // finally: do session management emit saveYourself(); // for compatibility bool canceled = false; foreach(KSessionManager* it, KSessionManager::sessionClients()) { if(canceled) break; canceled = !it->saveState( sm ); } // if we created a new session config object, register a proper discard command if ( pSessionConfig ) { pSessionConfig->sync(); QStringList discard; discard << QLatin1String("rm") << KStandardDirs::locateLocal("config", sessionConfigName()); sm.setDiscardCommand( discard ); } else { sm.setDiscardCommand( QStringList( QLatin1String("") ) ); } if ( canceled ) sm.cancel(); #else // FIXME(E): Implement for Qt Embedded #endif d->session_save = false; } bool KApplication::sessionSaving() const { return d->session_save; } void KApplication::parseCommandLine( ) { KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde"); if ( type() != Tty ) { if (args && args->isSet("icon")) { QPixmap largeIcon = DesktopIcon(QFile::decodeName(args->getOption("icon"))); QIcon icon = windowIcon(); icon.addPixmap(largeIcon, QIcon::Normal, QIcon::On); setWindowIcon(icon); } else { QIcon icon = windowIcon(); QPixmap largeIcon = DesktopIcon(QFile::decodeName(d->componentData.componentName())); icon.addPixmap(largeIcon, QIcon::Normal, QIcon::On); setWindowIcon(icon); } } if (!args) return; if (args->isSet("config")) { QString config = QString::fromLocal8Bit(args->getOption("config")); d->componentData.setConfigName(config); } if (args->isSet("style")) { extern KDEUI_EXPORT QString kde_overrideStyle; // see KGlobalSettings. Should we have a static setter? QStringList styles = QStyleFactory::keys(); QString reqStyle(QLatin1String(args->getOption("style").toLower())); for (QStringList::ConstIterator it = styles.begin(); it != styles.end(); ++it) if ((*it).toLower() == reqStyle) { kde_overrideStyle = *it; break; } if (kde_overrideStyle.isEmpty()) fprintf(stderr, "%s", i18n("The style %1 was not found\n", reqStyle).toLocal8Bit().data()); } bool nocrashhandler = (getenv("KDE_DEBUG") != NULL); if (!nocrashhandler && args->isSet("crashhandler")) { // set default crash handler / set emergency save function to nothing KCrash::setCrashHandler(KCrash::defaultCrashHandler); KCrash::setEmergencySaveFunction(NULL); KCrash::setApplicationName(QLatin1String(args->appName())); } #ifdef Q_WS_X11 if ( args->isSet( "waitforwm" ) ) { Atom type; (void) desktop(); // trigger desktop creation, we need PropertyNotify events for the root window int format; unsigned long length, after; unsigned char *data; while ( XGetWindowProperty( QX11Info::display(), QX11Info::appRootWindow(), atom_NetSupported, 0, 1, false, AnyPropertyType, &type, &format, &length, &after, &data ) != Success || !length ) { if ( data ) XFree( data ); XEvent event; XWindowEvent( QX11Info::display(), QX11Info::appRootWindow(), PropertyChangeMask, &event ); } if ( data ) XFree( data ); } #else // FIXME(E): Implement for Qt Embedded #endif if (args->isSet("smkey")) { d->sessionKey = QLatin1String(args->getOption("smkey")); } } extern void kDebugCleanup(); KApplication::~KApplication() { // First call the static deleters and then call KLibLoader::cleanup() // The static deleters may delete libraries for which they need KLibLoader. // KLibLoader will take care of the remaining ones. KGlobal::deleteStaticDeleters(); #ifdef Q_WS_X11 if ( d->oldXErrorHandler != NULL ) XSetErrorHandler( d->oldXErrorHandler ); if ( d->oldXIOErrorHandler != NULL ) XSetIOErrorHandler( d->oldXIOErrorHandler ); if ( d->oldIceIOErrorHandler != NULL ) IceSetIOErrorHandler( d->oldIceIOErrorHandler ); #endif delete d; KApp = 0; #ifdef Q_WS_X11 mySmcConnection = 0; #else // FIXME(E): Implement for Qt Embedded #endif } #ifdef Q_WS_X11 class KAppX11HackWidget: public QWidget { public: bool publicx11Event( XEvent * e) { return x11Event( e ); } }; #endif #ifdef Q_WS_X11 bool KApplication::x11EventFilter( XEvent *_event ) { switch ( _event->type ) { case ClientMessage: { #if KDE_IS_VERSION( 3, 90, 90 ) #warning This should be already in Qt, check. #endif // Workaround for focus stealing prevention not working when dragging e.g. text from KWrite // to KDesktop -> the dialog asking for filename doesn't get activated. This is because // Qt-3.2.x doesn't have concept of qt_x_user_time at all, and Qt-3.3.0b1 passes the timestamp // in the XdndDrop message in incorrect field (and doesn't update qt_x_user_time either). // Patch already sent, future Qt version should have this fixed. if( _event->xclient.message_type == kde_xdnd_drop ) { // if the message is XdndDrop if( _event->xclient.data.l[ 1 ] == 1 << 24 // and it's broken the way it's in Qt-3.2.x && _event->xclient.data.l[ 2 ] == 0 && _event->xclient.data.l[ 4 ] == 0 && _event->xclient.data.l[ 3 ] != 0 ) { if( QX11Info::appUserTime() == 0 || NET::timestampCompare( _event->xclient.data.l[ 3 ], QX11Info::appUserTime() ) > 0 ) { // and the timestamp looks reasonable QX11Info::setAppUserTime(_event->xclient.data.l[ 3 ]); // update our qt_x_user_time from it } } else // normal DND, only needed until Qt updates qt_x_user_time from XdndDrop { if( QX11Info::appUserTime() == 0 || NET::timestampCompare( _event->xclient.data.l[ 2 ], QX11Info::appUserTime() ) > 0 ) { // the timestamp looks reasonable QX11Info::setAppUserTime(_event->xclient.data.l[ 2 ]); // update our qt_x_user_time from it } } } } default: break; } if (x11Filter) { foreach (const QWidget *w, *x11Filter) { if (((KAppX11HackWidget*) w)->publicx11Event(_event)) return true; } } return false; } #endif // Q_WS_X11 void KApplication::updateUserTimestamp( int time ) { #if defined Q_WS_X11 if( time == 0 ) { // get current X timestamp Window w = XCreateSimpleWindow( QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0, 0, 0 ); XSelectInput( QX11Info::display(), w, PropertyChangeMask ); unsigned char data[ 1 ]; XChangeProperty( QX11Info::display(), w, XA_ATOM, XA_ATOM, 8, PropModeAppend, data, 1 ); XEvent ev; XWindowEvent( QX11Info::display(), w, PropertyChangeMask, &ev ); time = ev.xproperty.time; XDestroyWindow( QX11Info::display(), w ); } if( QX11Info::appUserTime() == 0 || NET::timestampCompare( time, QX11Info::appUserTime()) > 0 ) // time > appUserTime QX11Info::setAppUserTime(time); #endif } unsigned long KApplication::userTimestamp() const { #if defined Q_WS_X11 return QX11Info::appUserTime(); #else return 0; #endif } void KApplication::updateRemoteUserTimestamp( const QString& service, int time ) { #if defined Q_WS_X11 if( time == 0 ) time = QX11Info::appUserTime(); QDBusInterface(service, QLatin1String("/MainApplication"), QString(QLatin1String("org.kde.KApplication"))) .call(QLatin1String("updateUserTimestamp"), time); #endif } QString KApplication::tempSaveName( const QString& pFilename ) { QString aFilename; if( QDir::isRelativePath(pFilename) ) { kWarning(101) << "Relative filename passed to KApplication::tempSaveName" << endl; aFilename = QFileInfo( QDir( QLatin1String(".") ), pFilename ).absoluteFilePath(); } else aFilename = pFilename; QDir aAutosaveDir( QDir::homePath() + QLatin1String("/autosave/") ); if( !aAutosaveDir.exists() ) { if( !aAutosaveDir.mkdir( aAutosaveDir.absolutePath() ) ) { // Last chance: use temp dir aAutosaveDir.setPath( KGlobal::dirs()->saveLocation("tmp") ); } } aFilename.replace( QLatin1String("/"), QLatin1String("\\!") ) .prepend( QLatin1Char('#') ) .append( QLatin1Char('#') ) .prepend( QLatin1Char('/') ).prepend( aAutosaveDir.absolutePath() ); return aFilename; } QString KApplication::checkRecoverFile( const QString& pFilename, bool& bRecover ) { QString aFilename; if( QDir::isRelativePath(pFilename) ) { kWarning(101) << "Relative filename passed to KApplication::tempSaveName" << endl; aFilename = QFileInfo( QDir( QLatin1String(".") ), pFilename ).absoluteFilePath(); } else aFilename = pFilename; QDir aAutosaveDir( QDir::homePath() + QLatin1String("/autosave/") ); if( !aAutosaveDir.exists() ) { if( !aAutosaveDir.mkdir( aAutosaveDir.absolutePath() ) ) { // Last chance: use temp dir aAutosaveDir.setPath( KGlobal::dirs()->saveLocation("tmp") ); } } aFilename.replace( QLatin1String("/"), QLatin1String("\\!") ) .prepend( QLatin1Char('#') ) .append( QLatin1Char('#') ) .prepend( QLatin1Char('/') ) .prepend( aAutosaveDir.absolutePath() ); if( QFile( aFilename ).exists() ) { bRecover = true; return aFilename; } else { bRecover = false; return pFilename; } } void KApplication::setTopWidget( QWidget *topWidget ) { if( !topWidget ) return; // set the specified caption if ( !topWidget->inherits("KMainWindow") ) { // KMainWindow does this already for us topWidget->setWindowTitle(KGlobal::caption()); } #if defined Q_WS_X11 //#ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded // set the app startup notification window property KStartupInfo::setWindowStartupId( topWidget->winId(), startupId()); #endif } QByteArray KApplication::startupId() const { return d->startup_id; } void KApplication::setStartupId( const QByteArray& startup_id ) { if( startup_id == d->startup_id ) return; #if defined Q_WS_X11 KStartupInfo::handleAutoAppStartedSending(); // finish old startup notification if needed #endif if( startup_id.isEmpty()) d->startup_id = "0"; else { d->startup_id = startup_id; #if defined Q_WS_X11 KStartupInfoId id; id.initId( startup_id ); long timestamp = id.timestamp(); if( timestamp != 0 ) updateUserTimestamp( timestamp ); #endif } } // read the startup notification env variable, save it and unset it in order // not to propagate it to processes started from this app void KApplication::read_app_startup_id() { #if defined Q_WS_X11 KStartupInfoId id = KStartupInfo::currentStartupIdEnv(); KStartupInfo::resetStartupEnv(); d->startup_id = id.id(); #endif } // Hook called by KToolInvocation void KApplication::slot_KToolInvication_hook(QStringList& envs,QByteArray& startup_id) { #ifdef Q_WS_X11 if (QX11Info::display()) { QByteArray dpystring(XDisplayString(QX11Info::display())); envs << QString::fromLatin1( QByteArray("DISPLAY=") + dpystring ); } else if( getenv( "DISPLAY" )) { QByteArray dpystring( getenv( "DISPLAY" )); envs << QString::fromLatin1( QByteArray("DISPLAY=") + dpystring ); } if(startup_id.isEmpty()) startup_id = KStartupInfo::createNewStartupId(); #else Q_UNUSED(envs); Q_UNUSED(startup_id); #endif } void KApplication::setSynchronizeClipboard(bool synchronize) { KClipboardSynchronizer::self()->setSynchronizing(synchronize); KClipboardSynchronizer::self()->setReverseSynchronizing(synchronize); } #include "kapplication.moc" diff --git a/kdeui/kernel/kapplication.h b/kdeui/kernel/kapplication.h index 302ad01198..7e35b40318 100644 --- a/kdeui/kernel/kapplication.h +++ b/kdeui/kernel/kapplication.h @@ -1,496 +1,499 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) Copyright (c) 1998, 1999 KDE Team This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KAPP_H #define KAPP_H // Version macros. Never put this further down. #include "kdeversion.h" #include "kdelibs_export.h" class KConfig; class KConfigBase; class KCharsets; +class KGestureMap; #ifdef KDE3_SUPPORT #include #include #include #include #include #include #endif typedef unsigned long Atom; #if !defined(Q_WS_X11) typedef void Display; #endif #include #include #include #ifdef Q_WS_X11 #include #endif struct _IceConn; class QPixmap; class KSessionManaged; class KStyle; class KUrl; #define kapp KApplication::kApplication() /** * Controls and provides information to all KDE applications. * * Only one object of this class can be instantiated in a single app. * This instance is always accessible via the 'kapp' global variable. * * This class provides the following services to all KDE applications. * * @li It controls the event queue (see QApplication ). * @li It provides the application with KDE resources such as * accelerators, common menu entries, a KConfig object. session * management events, help invocation etc. * @li Installs an empty signal handler for the SIGPIPE signal. * If you want to catch this signal * yourself, you have set a new signal handler after KApplication's * constructor has run. * @li It can start new services * * * @short Controls and provides information to all KDE applications. * @author Matthias Kalle Dalheimer */ class KDEUI_EXPORT KApplication : public QApplication { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.KApplication") public: /** * This constructor is the one you should use. * It takes aboutData and command line arguments from KCmdLineArgs. * * @param GUIenabled Set to false to disable all GUI stuff. */ explicit KApplication(bool GUIenabled = true); #ifdef Q_WS_X11 /** * Constructor. Parses command-line arguments. Use this constructor when you * you need to use a non-default visual or colormap. * * @param display Will be passed to Qt as the X display. The display must be * valid and already opened. * * @param visual A pointer to the X11 visual that should be used by the * application. Note that only TrueColor visuals are supported on depths * greater than 8 bpp. If this parameter is NULL, the default visual will * be used instead. * * @param colormap The colormap that should be used by the application. If * this parameter is 0, the default colormap will be used instead. */ explicit KApplication(Display *display, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); /** * Constructor. Parses command-line arguments. Use this constructor to use KApplication * in a Motif or Xt program. * * @param display Will be passed to Qt as the X display. The display must be valid and already * opened. * * @param argc command line argument count * * @param argv command line argument value(s) * * @param rAppName application name. Will be used for finding the * associated message files and icon files, and as the default * registration name for DCOP. This is a mandatory parameter. * * @param GUIenabled Set to false to disable all GUI stuff. */ KApplication(Display *display, int& argc, char** argv, const QByteArray& rAppName, bool GUIenabled=true); #endif virtual ~KApplication(); /** * Returns the current application object. * * This is similar to the global QApplication pointer qApp. It * allows access to the single global KApplication object, since * more than one cannot be created in the same application. It * saves you the trouble of having to pass the pointer explicitly * to every function that may require it. * @return the current application object */ static KApplication* kApplication() { return KApp; } /** * Returns the application session config object. * * @return A pointer to the application's instance specific * KConfig object. * @see KConfig */ KConfig* sessionConfig(); #ifdef KDE3_SUPPORT /** * Is the application restored from the session manager? * * @return If true, this application was restored by the session manager. * Note that this may mean the config object returned by * sessionConfig() contains data saved by a session closedown. * @see sessionConfig() * @deprecated use qApp->isSessionRestored() */ inline KDE_DEPRECATED bool isRestored() const { return QApplication::isSessionRestored(); } #endif /** * Disables session management for this application. * * Useful in case your application is started by the * initial "startkde" script. */ void disableSessionManagement(); /** * Enables session management for this application, formerly * disabled by calling disableSessionManagement(). You usually * shouldn't call this function, as session management is enabled * by default. */ void enableSessionManagement(); /** * Reimplemented for internal purposes, mainly the highlevel * handling of session management with KSessionManaged. * @internal */ void commitData( QSessionManager& sm ); /** * Reimplemented for internal purposes, mainly the highlevel * handling of session management with KSessionManaged. * @internal */ void saveState( QSessionManager& sm ); /** * Returns true if the application is currently saving its session * data (most probably before KDE logout). This is intended for use * mainly in KMainWindow::queryClose() and KMainWindow::queryExit(). * * @see KMainWindow::queryClose * @see KMainWindow::queryExit */ bool sessionSaving() const; #ifdef KDE3_SUPPORT /** * Returns a QPixmap with the application icon. * @return the application icon * @deprecated Use QApplication::windowIcon() */ inline KDE_DEPRECATED QPixmap icon() const { int size = IconSize(K3Icon::Desktop); return windowIcon().pixmap(size,size); } /** * Returns the mini-icon for the application as a QPixmap. * @return the application's mini icon * @deprecated Use QApplication::windowIcon() */ inline KDE_DEPRECATED QPixmap miniIcon() const { int size = IconSize(K3Icon::Small); return windowIcon().pixmap(size,size); } #endif /** * Sets the top widget of the application. * This means basically applying the right window caption. * An application may have several top widgets. You don't * need to call this function manually when using KMainWindow. * * @param topWidget A top widget of the application. * * @see icon(), caption() **/ void setTopWidget( QWidget *topWidget ); /** * Get a file name in order to make a temporary copy of your document. * * @param pFilename The full path to the current file of your * document. * @return A new filename for auto-saving. * @deprecated use KTemporaryFile, KSaveFile or KAutoSaveFile instead */ static KDE_DEPRECATED QString tempSaveName( const QString& pFilename ); /** * Check whether an auto-save file exists for the document you want to * open. * * @param pFilename The full path to the document you want to open. * @param bRecover This gets set to true if there was a recover * file. * @return The full path of the file to open. */ static QString checkRecoverFile( const QString& pFilename, bool& bRecover ); #ifdef KDE3_SUPPORT #ifdef Q_WS_X11 /** * Get the X11 display * @return the X11 Display * @deprecated use QX11Info::display() */ static inline KDE_DEPRECATED Display *getDisplay() { return QX11Info::display(); } #endif #endif /** * Installs widget filter as global X11 event filter. * * The widget * filter receives XEvents in its standard QWidget::x11Event() function. * * Warning: Only do this when absolutely necessary. An installed X11 filter * can slow things down. **/ void installX11EventFilter( QWidget* filter ); /** * Removes global X11 event filter previously installed by * installX11EventFilter(). */ void removeX11EventFilter( const QWidget* filter ); #ifdef KDE3_SUPPORT /** * Generates a uniform random number. * @return A truly unpredictable number in the range [0, RAND_MAX) * @deprecated Use KRandom::random() */ static inline KDE_DEPRECATED int random() { return KRandom::random(); } /** * Generates a random string. It operates in the range [A-Za-z0-9] * @param length Generate a string of this length. * @return the random string * @deprecated use KRandom::randomString() instead. */ static inline KDE_DEPRECATED QString randomString(int length) { return KRandom::randomString(length); } #endif /** * Returns the app startup notification identifier for this running * application. * @return the startup notification identifier */ QByteArray startupId() const; /** * @internal * Sets a new value for the application startup notification window property for newly * created toplevel windows. * @param startup_id the startup notification identifier * @see KStartupInfo::setNewStartupId */ void setStartupId( const QByteArray& startup_id ); /** * Sets how the primary and clipboard selections are synchronized in an X11 environment */ void setSynchronizeClipboard(bool synchronize); public Q_SLOTS: /** * Updates the last user action timestamp to the given time, or to the current time, * if 0 is given. Do not use unless you're really sure what you're doing. * Consult focus stealing prevention section in kdebase/kwin/README. */ Q_SCRIPTABLE void updateUserTimestamp( int time = 0 ); // D-Bus slots: Q_SCRIPTABLE void reparseConfiguration(); Q_SCRIPTABLE void quit(); public: /** * Returns the last user action timestamp or 0 if no user activity has taken place yet. * @see updateuserTimestamp */ unsigned long userTimestamp() const; /** * Updates the last user action timestamp in the application registered to DCOP with dcopId * to the given time, or to this application's user time, if 0 is given. * Use before causing user interaction in the remote application, e.g. invoking a dialog * in the application using a DCOP call. * Consult focus stealing prevention section in kdebase/kwin/README. */ void updateRemoteUserTimestamp( const QString& service, int time = 0 ); #ifdef KDE3_SUPPORT /** * Returns the argument to --geometry if any, so the geometry can be set * wherever necessary * @return the geometry argument, or QString() if there is none * @deprecated please use the following code instead: * * * QString geometry; * KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde"); * if (args && args->isSet("geometry")) * geometry = args->getOption("geometry"); * * */ static inline KDE_DEPRECATED QString geometryArgument() { KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde"); return args->isSet("geometry") ? QString::fromLatin1( args->getOption("geometry") ) : QString(); } #endif protected: /** * @internal Used by KUniqueApplication */ KApplication(bool GUIenabled, const KComponentData &cData); #ifdef Q_WS_X11 /** * @internal Used by KUniqueApplication */ KApplication(Display *display, Qt::HANDLE visual, Qt::HANDLE colormap, const KComponentData &cData); /** * Used to catch X11 events */ bool x11EventFilter( XEvent * ); #endif /// Current application object. static KApplication *KApp; private Q_SLOTS: void x11FilterDestroyed(); void checkAppStartedSlot(); void slot_KToolInvication_hook(QStringList&,QByteArray&); private: QString sessionConfigName() const; KConfig* pSessionConfig; //instance specific application config object bool bSessionManagement; void init(bool GUIenabled=true); void parseCommandLine( ); // Handle KDE arguments (Using KCmdLineArgs) void read_app_startup_id(); + friend class KAction; + KGestureMap* gestureMap() const; public: /** @internal */ bool notify( QObject* receiver, QEvent* event ); /** @internal */ int xErrhandler( Display*, void* ); /** @internal */ int xioErrhandler( Display* ); /** * @internal */ void iceIOErrorHandler( _IceConn *conn ); /** * @internal */ static bool loadedByKdeinit; /** * @internal */ static void startKdeinit(); Q_SIGNALS: /** Session management asks you to save the state of your application. This signal is provided for compatibility only. For new applications, simply use KMainWindow. By reimplementing KMainWindow::queryClose(), KMainWindow::saveProperties() and KMainWindow::readProperties() you can simply handle session management for applications with multiple toplevel windows. For purposes without KMainWindow, create an instance of KSessionManaged and reimplement the functions KSessionManaged::commitData() and/or KSessionManaged::saveState() If you still want to use this signal, here is what you should do: Connect to this signal in order to save your data. Do NOT manipulate the UI in that slot, it is blocked by the session manager. Use the sessionConfig() KConfig object to store all your instance specific data. Do not do any closing at this point! The user may still select Cancel wanting to continue working with your application. Cleanups could be done after aboutToQuit(). */ void saveYourself(); private: #ifndef KDE3_SUPPORT KConfig *config() { return KGlobal::config().data(); } #endif KApplication(const KApplication&); KApplication& operator=(const KApplication&); private: //### KDE4: This is to catch invalid implicit conversions, may want to reconsider KApplication(bool, bool); class Private; Private* const d; }; #endif diff --git a/kdeui/shortcuts/kgesturemap.cpp b/kdeui/shortcuts/kgesturemap.cpp index 0f3d03ae76..31ec83b6a8 100644 --- a/kdeui/shortcuts/kgesturemap.cpp +++ b/kdeui/shortcuts/kgesturemap.cpp @@ -1,247 +1,249 @@ /* This file is part of the KDE libraries Copyright (C) 2006,2007 Andreas Hartmetz (ahartmetz@gmail.com) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "kgesturemap.h" /* This is a class for internal use by the KDE libraries only. This class may change or go away without notice so don't try to use it in non-kdelibs code. */ KGestureMap::KGestureMap(KApplication *parent) : QObject(parent), m_gestureTimeout(this), m_acquiring(false) { Q_ASSERT(parent); m_gestureTimeout.setSingleShot(true); connect(&m_gestureTimeout, SIGNAL(timeout()), this, SLOT(stopAcquisition())); //It would be nice to install the filter on demand. Unfortunately, //undesired behavior might result due to changing invocation //orders of different event filters. parent->installEventFilter(this); } void KGestureMap::addGesture(const KShapeGesture &gesture, KAction *act) { kDebug() << "KGestureMap::addGesture(KShapeGesture ...)"<< endl; if (!gesture.isValid() || !act) return; if (!m_shapeGestures.contains(gesture)) m_shapeGestures.insert(gesture, act); else kDebug() << "Tried to register an action for a gesture already taken"<< endl; } void KGestureMap::addGesture(const KRockerGesture &gesture, KAction *act) { kDebug() << "KGestureMap::addGesture(KRockerGesture ...)"<< endl; if (!gesture.isValid() || !act) return; if (!m_rockerGestures.contains(gesture)) m_rockerGestures.insert(gesture, act); else kDebug() << "Tried to register an action for a gesture already taken"<< endl; } void KGestureMap::removeGesture(const KShapeGesture &gesture, KAction *act) { kDebug() << "KGestureMap::removeGesture(KShapeGesture ...)"<< endl; if (!gesture.isValid()) return; KAction *oldAct = m_shapeGestures.value(gesture); if (oldAct == act || !act /*wildcard*/) m_shapeGestures.remove(gesture); } void KGestureMap::removeGesture(const KRockerGesture &gesture, KAction *act) { kDebug() << "KGestureMap::removeGesture(KRockerGesture ...)"<< endl; if (!gesture.isValid()) return; KAction *oldAct = m_rockerGestures.value(gesture); if (oldAct == act || !act /*wildcard*/) m_rockerGestures.remove(gesture); } KAction *KGestureMap::findAction(const KShapeGesture &gesture) const { return m_shapeGestures.value(gesture); } KAction *KGestureMap::findAction(const KRockerGesture &gesture) const { return m_rockerGestures.value(gesture); } inline int KGestureMap::bitCount(int n) { int count = 0; while (n) { n &= (n - 1); count++; } return count; } void KGestureMap::handleAction(KAction *kact) { if (!kact) return; kDebug() << "handleAction"<< endl; //TODO: only activate in the action's context, just like keyboard shortcuts kact->trigger(); return; } void KGestureMap::matchShapeGesture() { //TODO: tune and tweak until satisfied with result :) m_shapeGesture.setShape(m_points); float dist, minDist = 20.0; KAction *bestMatch = 0; for (QHash::const_iterator it = m_shapeGestures.constBegin(); it != m_shapeGestures.constEnd(); ++it) { dist = m_shapeGesture.distance(it.key(), 1000.0); if (dist < minDist) { minDist = dist; bestMatch = it.value(); } } handleAction(bestMatch); } //slot void KGestureMap::stopAcquisition() { m_gestureTimeout.stop(); m_acquiring = false; } //TODO: Probably kwin, kded and others should not have a gesture map. //Maybe making them friends and providing a private "die()" function would work. /* * Act on rocker gestures immediately and collect movement data for evaluation. * The decision when to consume and when to relay an event is quite tricky. * I decided to only consume clicks that belong to completed rocker gestures. * A user might e.g. go back in a browser several times using rocker gestures, * thus changing what's under the cursor every time. This might lead to * unintended clicks on links where there was free space before. */ bool KGestureMap::eventFilter(QObject *obj, QEvent *e) { + //disable until it does not interfere with other input any more + return false; Q_UNUSED(obj); int type = e->type(); //catch right-clicks disguised as context menu events. if we ignore a //context menu event caused by a right-click, it should get resent //as a right-click event, according to documentation. //### this is preliminary if (type == QEvent::ContextMenu) { QContextMenuEvent *cme = static_cast(e); if (cme->reason() == QContextMenuEvent::Mouse) { cme->ignore(); return true; } return false; } if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove) return false; QMouseEvent *me = static_cast(e); if (type == QEvent::MouseButtonPress) { int nButtonsDown = bitCount(me->buttons()); kDebug() << "number of buttons down:" << nButtonsDown << endl; //right button down starts gesture acquisition if (nButtonsDown == 1 && me->button() == Qt::RightButton) { //"startAcquisition()" m_acquiring = true; m_gestureTimeout.start(4000); kDebug() << "========================"<< endl; m_points.clear(); m_points.append(me->pos()); return true; } else if (nButtonsDown != 2) return false; //rocker gestures. do not trigger any movement gestures from now on. stopAcquisition(); int buttonHeld = me->buttons() ^ me->button(); m_rockerGesture.setButtons(static_cast(buttonHeld), me->button()); KAction *match = m_rockerGestures.value(m_rockerGesture); if (!match) return false; handleAction(match); return true; } if (m_acquiring) { if (type == QEvent::MouseMove) { m_points.append(me->pos()); //abort to avoid using too much memory. 1010 points should be enough //for everyone! :) //next reallocation of m_points would happen at 1012 items if (m_points.size() > 1010) stopAcquisition(); return true; } else if (type == QEvent::MouseButtonRelease && me->button() == Qt::RightButton) { stopAcquisition(); //TODO: pre-selection of gestures by length (optimization), if necessary //possibly apply other heuristics //then try all remaining gestures for sufficiently small distance int dist = 0; for (int i = 1; i < m_points.size(); i++) { dist += (m_points[i] - m_points[i-1]).manhattanLength(); if (dist > 40) { matchShapeGesture(); return true; } //this was probably a small glitch while right-clicking if we get here. //TODO: open the context menu or do whatever happens on right-click (how?) } return false; } } return false; } #include "kgesturemap.moc"