/* This file is part of the KDE Kontact Plugin Interface Library. Copyright (c) 2003,2008 David Faure 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 "uniqueapphandler.h" #include #include "core.h" #include #include #include #include #include #include #include #include #include #ifdef Q_WS_WIN # include #endif /* Test plan for the various cases of interaction between standalone apps and kontact: 1) start kontact, select "Mail". 1a) type "korganizer" -> it switches to korganizer 1b) type "kmail" -> it switches to kmail 1c) type "kaddressbook" -> it switches to kaddressbook 1d) type "kmail foo@kde.org" -> it opens a kmail composer, without switching 1e) type "knode" -> it switches to knode [unless configured to be external] 1f) type "kaddressbook --new-contact" -> it opens a kaddressbook contact window 1g) type "knode news://foobar/group" -> it pops up "can't resolve hostname" 2) close kontact. Launch kmail. Launch kontact again. 2a) click "Mail" icon -> kontact doesn't load a part, but activates the kmail window 2b) type "kmail foo@kde.org" -> standalone kmail opens composer. 2c) close kmail, click "Mail" icon -> kontact loads the kmail part. 2d) type "kmail" -> kontact is brought to front 3) close kontact. Launch korganizer, then kontact. 3a) both Todo and Calendar activate the running korganizer. 3b) type "korganizer" -> standalone korganizer is brought to front 3c) close korganizer, click Calendar or Todo -> kontact loads part. 3d) type "korganizer" -> kontact is brought to front 4) close kontact. Launch kaddressbook, then kontact. 4a) "Contacts" icon activate the running kaddressbook. 4b) type "kaddressbook" -> standalone kaddressbook is brought to front 4c) close kaddressbook, type "kaddressbook -a foo@kde.org" -> kontact loads part and opens editor 4d) type "kaddressbook" -> kontact is brought to front 5) close kontact. Launch knode, then kontact. 5a) "News" icon activate the running knode. 5b) type "knode" -> standalone knode is brought to front 5c) close knode, type "knode news://foobar/group" -> kontact loads knode and pops up msgbox 5d) type "knode" -> kontact is brought to front 6) start "kontact --module summaryplugin" 6a) type "qdbus org.kde.kmail /kmail_PimApplication newInstance '' ''" -> kontact switches to kmail (#103775) 6b) type "kmail" -> kontact is brought to front 6c) type "kontact" -> kontact is brought to front 6d) type "kontact --module summaryplugin" -> kontact switches to summary */ using namespace KontactInterface; //@cond PRIVATE class UniqueAppHandler::Private { public: Plugin *mPlugin; }; //@endcond UniqueAppHandler::UniqueAppHandler( Plugin *plugin ) : QObject( plugin ), d( new Private ) { //kDebug() << "plugin->objectName():" << plugin->objectName(); d->mPlugin = plugin; QDBusConnection session = QDBusConnection::sessionBus(); const QString appName = plugin->objectName(); session.registerService( QLatin1String("org.kde.") + appName ); const QString objectName = QLatin1Char( '/' ) + appName + QLatin1String("_PimApplication"); session.registerObject( objectName, this, QDBusConnection::ExportAllSlots ); } UniqueAppHandler::~UniqueAppHandler() { QDBusConnection session = QDBusConnection::sessionBus(); const QString appName = parent()->objectName(); session.unregisterService( QLatin1String("org.kde.") + appName ); delete d; } // DBUS call int UniqueAppHandler::newInstance( const QByteArray &asn_id, const QByteArray &args ) { if ( !asn_id.isEmpty() ) { kapp->setStartupId( asn_id ); } KCmdLineArgs::reset(); // forget options defined by other "applications" loadCommandLineOptions(); // implemented by plugin // This bit is duplicated from KUniqueApplicationAdaptor::newInstance() QDataStream ds( args ); KCmdLineArgs::loadAppArgs( ds ); return newInstance(); } static QWidget *s_mainWidget = 0; // Plugin-specific newInstance implementation, called by above method int KontactInterface::UniqueAppHandler::newInstance() { if ( s_mainWidget ) { s_mainWidget->show(); KWindowSystem::forceActiveWindow( s_mainWidget->winId() ); KStartupInfo::appStarted(); } // Then ensure the part appears in kontact d->mPlugin->core()->selectPlugin( d->mPlugin ); return 0; } Plugin *UniqueAppHandler::plugin() const { return d->mPlugin; } bool KontactInterface::UniqueAppHandler::load() { (void)d->mPlugin->part(); // load the part without bringing it to front return true; } //@cond PRIVATE class UniqueAppWatcher::Private { public: UniqueAppHandlerFactoryBase *mFactory; Plugin *mPlugin; bool mRunningStandalone; }; //@endcond UniqueAppWatcher::UniqueAppWatcher( UniqueAppHandlerFactoryBase *factory, Plugin *plugin ) : QObject( plugin ), d( new Private ) { d->mFactory = factory; d->mPlugin = plugin; // The app is running standalone if 1) that name is known to D-Bus const QString serviceName = QLatin1String("org.kde.") + plugin->objectName(); //Needed for wince build #undef interface d->mRunningStandalone = QDBusConnection::sessionBus().interface()->isServiceRegistered( serviceName ); #ifdef Q_WS_WIN if ( d->mRunningStandalone ) { QList pids; KPIMUtils::getProcessesIdForName( plugin->objectName(), pids ); const int mypid = getpid(); bool processExits = false; foreach ( int pid, pids ) { if ( mypid != pid ) { processExits = true; break; } } if ( !processExits ) { d->mRunningStandalone = false; } } #endif QString owner = QDBusConnection::sessionBus().interface()->serviceOwner( serviceName ); if ( d->mRunningStandalone && ( owner == QDBusConnection::sessionBus().baseService() ) ) { d->mRunningStandalone = false; } //kDebug() << " plugin->objectName()=" << plugin->objectName() // << " running standalone:" << d->mRunningStandalone; if ( d->mRunningStandalone ) { QObject::connect( QDBusConnection::sessionBus().interface(), SIGNAL(serviceOwnerChanged(QString,QString,QString)), this, SLOT(slotApplicationRemoved(QString,QString,QString)) ); } else { d->mFactory->createHandler( d->mPlugin ); } } UniqueAppWatcher::~UniqueAppWatcher() { delete d->mFactory; delete d; } bool UniqueAppWatcher::isRunningStandalone() const { return d->mRunningStandalone; } void KontactInterface::UniqueAppWatcher::slotApplicationRemoved( const QString &name, const QString &oldOwner, const QString &newOwner ) { if ( oldOwner.isEmpty() || !newOwner.isEmpty() ) { return; } const QString serviceName = QLatin1String("org.kde.") + d->mPlugin->objectName(); if ( name == serviceName && d->mRunningStandalone ) { d->mFactory->createHandler( d->mPlugin ); d->mRunningStandalone = false; } } void KontactInterface::UniqueAppHandler::setMainWidget( QWidget *widget ) { s_mainWidget = widget; } QWidget *KontactInterface::UniqueAppHandler::mainWidget() { return s_mainWidget; }