• Skip to content
  • Skip to link menu
KDE 4.5 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

akonadi

specialcollectionshelperjobs.cpp

00001 /*
00002     Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>
00003 
00004     This library is free software; you can redistribute it and/or modify it
00005     under the terms of the GNU Library General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or (at your
00007     option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful, but WITHOUT
00010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00012     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 the
00016     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017     02110-1301, USA.
00018 */
00019 
00020 #include "specialcollectionshelperjobs_p.h"
00021 
00022 #include "specialcollectionattribute_p.h"
00023 #include "specialcollections.h"
00024 
00025 #include <akonadi/agentinstance.h>
00026 #include <akonadi/agentinstancecreatejob.h>
00027 #include <akonadi/agentmanager.h>
00028 #include <akonadi/collectionfetchjob.h>
00029 #include <akonadi/collectionfetchscope.h>
00030 #include <akonadi/collectionmodifyjob.h>
00031 #include <akonadi/entitydisplayattribute.h>
00032 #include <akonadi/resourcesynchronizationjob.h>
00033 
00034 #include <KDebug>
00035 #include <KLocalizedString>
00036 #include <KStandardDirs>
00037 #include <kcoreconfigskeleton.h>
00038 
00039 #include <QtDBus/QDBusConnectionInterface>
00040 #include <QtDBus/QDBusInterface>
00041 #include <QtDBus/QDBusServiceWatcher>
00042 #include <QtCore/QMetaMethod>
00043 #include <QtCore/QTime>
00044 #include <QtCore/QTimer>
00045 
00046 #define DBUS_SERVICE_NAME QLatin1String( "org.kde.pim.SpecialCollections" )
00047 #define LOCK_WAIT_TIMEOUT_SECONDS 3
00048 
00049 using namespace Akonadi;
00050 
00051 // convenient methods to get/set the default resource id
00052 static void setDefaultResourceId( KCoreConfigSkeleton *settings, const QString &value )
00053 {
00054   KConfigSkeletonItem *item = settings->findItem( QLatin1String( "DefaultResourceId" ) );
00055   Q_ASSERT( item );
00056   item->setProperty( value );
00057 }
00058 
00059 static QString defaultResourceId( KCoreConfigSkeleton *settings )
00060 {
00061   const KConfigSkeletonItem *item = settings->findItem( QLatin1String( "DefaultResourceId" ) );
00062   Q_ASSERT( item );
00063   return item->property().toString();
00064 }
00065 
00066 static QVariant::Type argumentType( const QMetaObject *mo, const QString &method )
00067 {
00068   QMetaMethod m;
00069   for ( int i = 0; i < mo->methodCount(); ++i ) {
00070     const QString signature = QString::fromLatin1( mo->method( i ).signature() );
00071     if ( signature.startsWith( method ) )
00072       m = mo->method( i );
00073   }
00074 
00075   if ( !m.signature() )
00076     return QVariant::Invalid;
00077 
00078   const QList<QByteArray> argTypes = m.parameterTypes();
00079   if ( argTypes.count() != 1 )
00080     return QVariant::Invalid;
00081 
00082   return QVariant::nameToType( argTypes.first() );
00083 }
00084 
00085 // ===================== ResourceScanJob ============================
00086 
00090 class Akonadi::ResourceScanJob::Private
00091 {
00092   public:
00093     Private( KCoreConfigSkeleton *settings, ResourceScanJob *qq );
00094 
00095     void fetchResult( KJob *job ); // slot
00096 
00097     ResourceScanJob *const q;
00098 
00099     // Input:
00100     QString mResourceId;
00101     KCoreConfigSkeleton *mSettings;
00102 
00103     // Output:
00104     Collection mRootCollection;
00105     Collection::List mSpecialCollections;
00106 };
00107 
00108 ResourceScanJob::Private::Private( KCoreConfigSkeleton *settings, ResourceScanJob *qq )
00109   : q( qq ), mSettings( settings )
00110 {
00111 }
00112 
00113 void ResourceScanJob::Private::fetchResult( KJob *job )
00114 {
00115   if ( job->error() ) {
00116     kWarning() << job->errorText();
00117     return;
00118   }
00119 
00120   CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>( job );
00121   Q_ASSERT( fetchJob );
00122 
00123   Q_ASSERT( !mRootCollection.isValid() );
00124   Q_ASSERT( mSpecialCollections.isEmpty() );
00125   foreach ( const Collection &collection, fetchJob->collections() ) {
00126     if ( collection.parentCollection() == Collection::root() ) {
00127       if ( mRootCollection.isValid() )
00128         kWarning() << "Resource has more than one root collection. I don't know what to do.";
00129       else
00130         mRootCollection = collection;
00131     }
00132 
00133     if ( collection.hasAttribute<SpecialCollectionAttribute>() )
00134       mSpecialCollections.append( collection );
00135   }
00136 
00137   kDebug() << "Fetched root collection" << mRootCollection.id()
00138            << "and" << mSpecialCollections.count() << "local folders"
00139            << "(total" << fetchJob->collections().count() << "collections).";
00140 
00141   if ( !mRootCollection.isValid() ) {
00142     q->setError( Unknown );
00143     q->setErrorText( i18n( "Could not fetch root collection of resource %1.", mResourceId ) );
00144     q->emitResult();
00145     return;
00146   }
00147 
00148   // We are done!
00149   q->emitResult();
00150 }
00151 
00152 
00153 
00154 ResourceScanJob::ResourceScanJob( const QString &resourceId, KCoreConfigSkeleton *settings, QObject *parent )
00155   : Job( parent ),
00156     d( new Private( settings, this ) )
00157 {
00158   setResourceId( resourceId );
00159 }
00160 
00161 ResourceScanJob::~ResourceScanJob()
00162 {
00163   delete d;
00164 }
00165 
00166 QString ResourceScanJob::resourceId() const
00167 {
00168   return d->mResourceId;
00169 }
00170 
00171 void ResourceScanJob::setResourceId( const QString &resourceId )
00172 {
00173   d->mResourceId = resourceId;
00174 }
00175 
00176 Collection ResourceScanJob::rootResourceCollection() const
00177 {
00178   return d->mRootCollection;
00179 }
00180 
00181 Collection::List ResourceScanJob::specialCollections() const
00182 {
00183   return d->mSpecialCollections;
00184 }
00185 
00186 void ResourceScanJob::doStart()
00187 {
00188   if ( d->mResourceId.isEmpty() ) {
00189     kError() << "No resource ID given.";
00190     setError( Job::Unknown );
00191     setErrorText( i18n( "No resource ID given." ) );
00192     emitResult();
00193     return;
00194   }
00195 
00196   CollectionFetchJob *fetchJob = new CollectionFetchJob( Collection::root(),
00197                                                          CollectionFetchJob::Recursive, this );
00198   fetchJob->fetchScope().setResource( d->mResourceId );
00199   fetchJob->fetchScope().setIncludeStatistics( true );
00200   connect( fetchJob, SIGNAL( result( KJob* ) ), this, SLOT( fetchResult( KJob* ) ) );
00201 }
00202 
00203 
00204 // ===================== DefaultResourceJob ============================
00205 
00209 class Akonadi::DefaultResourceJobPrivate
00210 {
00211   public:
00212     DefaultResourceJobPrivate( KCoreConfigSkeleton *settings, DefaultResourceJob *qq );
00213 
00214     void tryFetchResource();
00215     void resourceCreateResult( KJob *job ); // slot
00216     void resourceSyncResult( KJob *job ); // slot
00217     void collectionFetchResult( KJob *job ); // slot
00218     void collectionModifyResult( KJob *job ); // slot
00219 
00220     DefaultResourceJob *const q;
00221     KCoreConfigSkeleton *mSettings;
00222     bool mResourceWasPreexisting;
00223     int mPendingModifyJobs;
00224     QString mDefaultResourceType;
00225     QVariantMap mDefaultResourceOptions;
00226     QList<QByteArray> mKnownTypes;
00227     QMap<QByteArray, QString> mNameForTypeMap;
00228     QMap<QByteArray, QString> mIconForTypeMap;
00229 };
00230 
00231 DefaultResourceJobPrivate::DefaultResourceJobPrivate( KCoreConfigSkeleton *settings, DefaultResourceJob *qq )
00232   : q( qq ),
00233     mSettings( settings ),
00234     mResourceWasPreexisting( true /* for safety, so as not to accidentally delete data */ ),
00235     mPendingModifyJobs( 0 )
00236 {
00237 }
00238 
00239 void DefaultResourceJobPrivate::tryFetchResource()
00240 {
00241   // Get the resourceId from config. Another instance might have changed it in the meantime.
00242   mSettings->readConfig();
00243 
00244   const QString resourceId = defaultResourceId( mSettings );
00245 
00246   kDebug() << "Read defaultResourceId" << resourceId << "from config.";
00247 
00248   const AgentInstance resource = AgentManager::self()->instance( resourceId );
00249   if ( resource.isValid() ) {
00250     // The resource exists; scan it.
00251     mResourceWasPreexisting = true;
00252     kDebug() << "Found resource" << resourceId;
00253     q->setResourceId( resourceId );
00254     q->ResourceScanJob::doStart();
00255   } else {
00256     // Try harder: maybe the default resource has been removed and another one added
00257     //             without updating the config file, in this case search for a resource
00258     //             of the same type and the default name
00259     const AgentInstance::List resources = AgentManager::self()->instances();
00260     foreach ( const AgentInstance &resource, resources ) {
00261       if ( resource.type().identifier() == mDefaultResourceType ) {
00262         if ( resource.name() == mDefaultResourceOptions.value( QLatin1String( "Name" ) ).toString() ) {
00263           // found a matching one...
00264           setDefaultResourceId( mSettings, resource.identifier() );
00265           mSettings->writeConfig();
00266           mResourceWasPreexisting = true;
00267           kDebug() << "Found resource" << resource.identifier();
00268           q->setResourceId( resource.identifier() );
00269           q->ResourceScanJob::doStart();
00270           return;
00271         }
00272       }
00273     }
00274 
00275     // Create the resource.
00276     mResourceWasPreexisting = false;
00277     kDebug() << "Creating maildir resource.";
00278     const AgentType type = AgentManager::self()->type( mDefaultResourceType );
00279     AgentInstanceCreateJob *job = new AgentInstanceCreateJob( type, q );
00280     QObject::connect( job, SIGNAL( result( KJob* ) ), q, SLOT( resourceCreateResult( KJob* ) ) );
00281     job->start(); // non-Akonadi::Job
00282   }
00283 }
00284 
00285 void DefaultResourceJobPrivate::resourceCreateResult( KJob *job )
00286 {
00287   if ( job->error() ) {
00288     kWarning() << job->errorText();
00289     //fail( i18n( "Failed to create the default resource (%1).", job->errorString() ) );
00290     q->setError( job->error() );
00291     q->setErrorText( job->errorText() );
00292     q->emitResult();
00293     return;
00294   }
00295 
00296   AgentInstance agent;
00297 
00298   // Get the resource instance.
00299   {
00300     AgentInstanceCreateJob *createJob = qobject_cast<AgentInstanceCreateJob*>( job );
00301     Q_ASSERT( createJob );
00302     agent = createJob->instance();
00303     setDefaultResourceId( mSettings, agent.identifier() );
00304     kDebug() << "Created maildir resource with id" << defaultResourceId( mSettings );
00305   }
00306 
00307   const QString defaultId = defaultResourceId( mSettings );
00308 
00309   // Configure the resource.
00310   {
00311     agent.setName( mDefaultResourceOptions.value( QLatin1String( "Name" ) ).toString() );
00312 
00313     QDBusInterface conf( QString::fromLatin1( "org.freedesktop.Akonadi.Resource." ) + defaultId,
00314                          QString::fromLatin1( "/Settings" ), QString() );
00315 
00316     if ( !conf.isValid() ) {
00317       q->setError( -1 );
00318       q->setErrorText( i18n( "Invalid resource identifier '%1'", defaultId ) );
00319       q->emitResult();
00320       return;
00321     }
00322 
00323     QMapIterator<QString, QVariant> it( mDefaultResourceOptions );
00324     while ( it.hasNext() ) {
00325       it.next();
00326 
00327       if ( it.key() == QLatin1String( "Name" ) )
00328         continue;
00329 
00330       const QString methodName = QString::fromLatin1( "set%1" ).arg( it.key() );
00331       const QVariant::Type argType = argumentType( conf.metaObject(), methodName );
00332       if ( argType == QVariant::Invalid ) {
00333         q->setError( Job::Unknown );
00334         q->setErrorText( i18n( "Failed to configure default resource via D-Bus." ) );
00335         q->emitResult();
00336         return;
00337       }
00338 
00339       QDBusReply<void> reply = conf.call( methodName, it.value() );
00340       if ( !reply.isValid() ) {
00341         q->setError( Job::Unknown );
00342         q->setErrorText( i18n( "Failed to configure default resource via D-Bus." ) );
00343         q->emitResult();
00344         return;
00345       }
00346     }
00347 
00348     agent.reconfigure();
00349   }
00350 
00351   // Sync the resource.
00352   {
00353     ResourceSynchronizationJob *syncJob = new ResourceSynchronizationJob( agent, q );
00354     QObject::connect( syncJob, SIGNAL( result( KJob* ) ), q, SLOT( resourceSyncResult( KJob* ) ) );
00355     syncJob->start(); // non-Akonadi
00356   }
00357 }
00358 
00359 void DefaultResourceJobPrivate::resourceSyncResult( KJob *job )
00360 {
00361   if ( job->error() ) {
00362     kWarning() << job->errorText();
00363     //fail( i18n( "ResourceSynchronizationJob failed (%1).", job->errorString() ) );
00364     return;
00365   }
00366 
00367   // Fetch the collections of the resource.
00368   kDebug() << "Fetching maildir collections.";
00369   CollectionFetchJob *fetchJob = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive, q );
00370   fetchJob->fetchScope().setResource( defaultResourceId( mSettings ) );
00371   QObject::connect( fetchJob, SIGNAL( result( KJob* ) ), q, SLOT( collectionFetchResult( KJob* ) ) );
00372 }
00373 
00374 void DefaultResourceJobPrivate::collectionFetchResult( KJob *job )
00375 {
00376   if ( job->error() ) {
00377     kWarning() << job->errorText();
00378     //fail( i18n( "Failed to fetch the root maildir collection (%1).", job->errorString() ) );
00379     return;
00380   }
00381 
00382   CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>( job );
00383   Q_ASSERT( fetchJob );
00384 
00385   const Collection::List collections = fetchJob->collections();
00386   kDebug() << "Fetched" << collections.count() << "collections.";
00387 
00388   // Find the root maildir collection.
00389   Collection::List toRecover;
00390   Collection resourceCollection;
00391   foreach ( const Collection &collection, collections ) {
00392     if ( collection.parentCollection() == Collection::root() ) {
00393       resourceCollection = collection;
00394       toRecover.append( collection );
00395       break;
00396     }
00397   }
00398 
00399   if ( !resourceCollection.isValid() ) {
00400     q->setError( Job::Unknown );
00401     q->setErrorText( i18n( "Failed to fetch the resource collection." ) );
00402     q->emitResult();
00403     return;
00404   }
00405 
00406   // Find all children of the resource collection.
00407   foreach ( const Collection &collection, collections ) {
00408     if ( collection.parentCollection() == resourceCollection ) {
00409       toRecover.append( collection );
00410     }
00411   }
00412 
00413   QHash<QString, QByteArray> typeForName;
00414   foreach ( const QByteArray &type, mKnownTypes ) {
00415     const QString displayName = mNameForTypeMap.value( type );
00416     typeForName[ displayName ] = type;
00417   }
00418 
00419   // These collections have been created by the maildir resource, when it
00420   // found the folders on disk. So give them the necessary attributes now.
00421   Q_ASSERT( mPendingModifyJobs == 0 );
00422   foreach ( Collection collection, toRecover ) {          // krazy:exclude=foreach
00423 
00424     // Find the type for the collection.
00425     QString name = collection.name();
00426     if ( collection.hasAttribute<EntityDisplayAttribute>() ) {
00427       const QString displayName = collection.attribute<EntityDisplayAttribute>()->displayName();
00428       if (!displayName.isEmpty())
00429         name = displayName;
00430     }
00431     const QByteArray type = typeForName.value( name );
00432 
00433     if ( !type.isEmpty() ) {
00434       kDebug() << "Recovering collection" << name;
00435       setCollectionAttributes( collection, type, mNameForTypeMap, mIconForTypeMap );
00436 
00437       CollectionModifyJob *modifyJob = new CollectionModifyJob( collection, q );
00438       QObject::connect( modifyJob, SIGNAL( result( KJob* ) ), q, SLOT( collectionModifyResult( KJob* ) ) );
00439       mPendingModifyJobs++;
00440     } else {
00441       kDebug() << "Searching for names: " << typeForName.keys();
00442       kDebug() << "Unknown collection name" << name << "-- not recovering.";
00443     }
00444   }
00445 
00446   if ( mPendingModifyJobs == 0 )
00447     q->emitResult();
00448 }
00449 
00450 void DefaultResourceJobPrivate::collectionModifyResult( KJob *job )
00451 {
00452   if ( job->error() ) {
00453     kWarning() << job->errorText();
00454     //fail( i18n( "Failed to modify the root maildir collection (%1).", job->errorString() ) );
00455     return;
00456   }
00457 
00458   Q_ASSERT( mPendingModifyJobs > 0 );
00459   mPendingModifyJobs--;
00460   kDebug() << "pendingModifyJobs now" << mPendingModifyJobs;
00461   if ( mPendingModifyJobs == 0 ) {
00462     // Write the updated config.
00463     kDebug() << "Writing defaultResourceId" << defaultResourceId( mSettings ) << "to config.";
00464     mSettings->writeConfig();
00465 
00466     // Scan the resource.
00467     q->setResourceId( defaultResourceId( mSettings ) );
00468     q->ResourceScanJob::doStart();
00469   }
00470 }
00471 
00472 
00473 
00474 DefaultResourceJob::DefaultResourceJob( KCoreConfigSkeleton *settings, QObject *parent )
00475   : ResourceScanJob( QString(), settings, parent ),
00476     d( new DefaultResourceJobPrivate( settings, this ) )
00477 {
00478 }
00479 
00480 DefaultResourceJob::~DefaultResourceJob()
00481 {
00482   delete d;
00483 }
00484 
00485 void DefaultResourceJob::setDefaultResourceType( const QString &type )
00486 {
00487   d->mDefaultResourceType = type;
00488 }
00489 
00490 void DefaultResourceJob::setDefaultResourceOptions( const QVariantMap &options )
00491 {
00492   d->mDefaultResourceOptions = options;
00493 }
00494 
00495 void DefaultResourceJob::setTypes( const QList<QByteArray> &types )
00496 {
00497   d->mKnownTypes = types;
00498 }
00499 
00500 void DefaultResourceJob::setNameForTypeMap( const QMap<QByteArray, QString> &map )
00501 {
00502   d->mNameForTypeMap = map;
00503 }
00504 
00505 void DefaultResourceJob::setIconForTypeMap( const QMap<QByteArray, QString> &map )
00506 {
00507   d->mIconForTypeMap = map;
00508 }
00509 
00510 void DefaultResourceJob::doStart()
00511 {
00512   d->tryFetchResource();
00513 }
00514 
00515 void DefaultResourceJob::slotResult( KJob *job )
00516 {
00517   if ( job->error() ) {
00518     kWarning() << job->errorText();
00519     // Do some cleanup.
00520     if ( !d->mResourceWasPreexisting ) {
00521       // We only removed the resource instance if we have created it.
00522       // Otherwise we might lose the user's data.
00523       const AgentInstance resource = AgentManager::self()->instance( defaultResourceId( d->mSettings ) );
00524       kDebug() << "Removing resource" << resource.identifier();
00525       AgentManager::self()->removeInstance( resource );
00526     }
00527   }
00528 
00529   Job::slotResult( job );
00530 }
00531 
00532 // ===================== GetLockJob ============================
00533 
00534 class Akonadi::GetLockJob::Private
00535 {
00536   public:
00537     Private( GetLockJob *qq );
00538 
00539     void doStart(); // slot
00540     void serviceOwnerChanged( const QString &name, const QString &oldOwner,
00541                               const QString &newOwner ); // slot
00542     void timeout(); // slot
00543 
00544     GetLockJob *const q;
00545     QTimer *mSafetyTimer;
00546 };
00547 
00548 GetLockJob::Private::Private( GetLockJob *qq )
00549   : q( qq ),
00550     mSafetyTimer( 0 )
00551 {
00552 }
00553 
00554 void GetLockJob::Private::doStart()
00555 {
00556   // Just doing registerService() and checking its return value is not sufficient,
00557   // since we may *already* own the name, and then registerService() returns true.
00558 
00559   QDBusConnection bus = QDBusConnection::sessionBus();
00560   const bool alreadyLocked = bus.interface()->isServiceRegistered( DBUS_SERVICE_NAME );
00561   const bool gotIt = bus.registerService( DBUS_SERVICE_NAME );
00562 
00563   if ( gotIt && !alreadyLocked ) {
00564     //kDebug() << "Got lock immediately.";
00565     q->emitResult();
00566   } else {
00567     QDBusServiceWatcher *watcher = new QDBusServiceWatcher( DBUS_SERVICE_NAME, QDBusConnection::sessionBus(),
00568                                                             QDBusServiceWatcher::WatchForOwnerChange, q );
00569     //kDebug() << "Waiting for lock.";
00570     connect( watcher, SIGNAL( serviceOwnerChanged( const QString&, const QString&, const QString& ) ),
00571              q, SLOT( serviceOwnerChanged( const QString&, const QString&, const QString& ) ) );
00572 
00573     mSafetyTimer = new QTimer( q );
00574     mSafetyTimer->setSingleShot( true );
00575     mSafetyTimer->setInterval( LOCK_WAIT_TIMEOUT_SECONDS * 1000 );
00576     mSafetyTimer->start();
00577     connect( mSafetyTimer, SIGNAL( timeout() ), q, SLOT( timeout() ) );
00578   }
00579 }
00580 
00581 void GetLockJob::Private::serviceOwnerChanged( const QString&, const QString&, const QString &newOwner )
00582 {
00583   if ( newOwner.isEmpty() ) {
00584     const bool gotIt = QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
00585     if ( gotIt ) {
00586       mSafetyTimer->stop();
00587       q->emitResult();
00588     }
00589   }
00590 }
00591 
00592 void GetLockJob::Private::timeout()
00593 {
00594   kWarning() << "Timeout trying to get lock.";
00595   q->setError( Job::Unknown );
00596   q->setErrorText( i18n( "Timeout trying to get lock." ) );
00597   q->emitResult();
00598 }
00599 
00600 
00601 GetLockJob::GetLockJob( QObject *parent )
00602   : KJob( parent ),
00603     d( new Private( this ) )
00604 {
00605 }
00606 
00607 GetLockJob::~GetLockJob()
00608 {
00609   delete d;
00610 }
00611 
00612 void GetLockJob::start()
00613 {
00614   QTimer::singleShot( 0, this, SLOT( doStart() ) );
00615 }
00616 
00617 void Akonadi::setCollectionAttributes( Akonadi::Collection &collection, const QByteArray &type,
00618                                        const QMap<QByteArray, QString> &nameForType,
00619                                        const QMap<QByteArray, QString> &iconForType )
00620 {
00621   {
00622     EntityDisplayAttribute *attr = new EntityDisplayAttribute;
00623     attr->setIconName( iconForType.value( type ) );
00624     attr->setDisplayName( nameForType.value( type ) );
00625     collection.addAttribute( attr );
00626   }
00627 
00628   {
00629     SpecialCollectionAttribute *attr = new SpecialCollectionAttribute;
00630     attr->setCollectionType( type );
00631     collection.addAttribute( attr );
00632   }
00633 }
00634 
00635 bool Akonadi::releaseLock()
00636 {
00637   return QDBusConnection::sessionBus().unregisterService( DBUS_SERVICE_NAME );
00638 }
00639 
00640 #include "specialcollectionshelperjobs_p.moc"

akonadi

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.7.1
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