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