diff --git a/akonadi/collectionmodel_p.cpp b/akonadi/collectionmodel_p.cpp index 4c1ee0b87..785fdcfe4 100644 --- a/akonadi/collectionmodel_p.cpp +++ b/akonadi/collectionmodel_p.cpp @@ -1,328 +1,330 @@ /* Copyright (c) 2006 - 2008 Volker Krause 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. */ //@cond PRIVATE #include "collectionmodel_p.h" #include "collectionmodel.h" #include "collectionutils_p.h" #include "collectionfetchjob.h" #include "collectionstatistics.h" #include "collectionstatisticsjob.h" #include "monitor.h" #include "session.h" #include #include #include +#include #include using namespace Akonadi; void CollectionModelPrivate::collectionRemoved( const Akonadi::Collection &collection ) { Q_Q( CollectionModel ); QModelIndex colIndex = indexForId( collection.id() ); if ( colIndex.isValid() ) { QModelIndex parentIndex = q->parent( colIndex ); // collection is still somewhere in the hierarchy removeRowFromModel( colIndex.row(), parentIndex ); } else { if ( collections.contains( collection.id() ) ) { // collection is orphan, ie. the parent has been removed already collections.remove( collection.id() ); childCollections.remove( collection.id() ); } } } void CollectionModelPrivate::collectionChanged( const Akonadi::Collection &collection ) { Q_Q( CollectionModel ); // What kind of change is it ? Collection::Id oldParentId = collections.value( collection.id() ).parent(); Collection::Id newParentId = collection.parent(); if ( newParentId != oldParentId && oldParentId >= 0 ) { // It's a move removeRowFromModel( indexForId( collections[ collection.id() ].id() ).row(), indexForId( oldParentId ) ); Collection newParent; if ( newParentId == Collection::root().id() ) newParent = Collection::root(); else newParent = collections.value( newParentId ); CollectionFetchJob *job = new CollectionFetchJob( newParent, CollectionFetchJob::Recursive, session ); job->includeUnsubscribed( unsubscribed ); q->connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)), q, SLOT(collectionsChanged(Akonadi::Collection::List)) ); q->connect( job, SIGNAL( result( KJob* ) ), q, SLOT( listDone( KJob* ) ) ); } else { // It's a simple change CollectionFetchJob *job = new CollectionFetchJob( collection, CollectionFetchJob::Base, session ); job->includeUnsubscribed( unsubscribed ); q->connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)), q, SLOT(collectionsChanged(Akonadi::Collection::List)) ); q->connect( job, SIGNAL( result( KJob* ) ), q, SLOT( listDone( KJob* ) ) ); } } void CollectionModelPrivate::updateDone( KJob *job ) { if ( job->error() ) { // TODO: handle job errors kWarning( 5250 ) << "Job error:" << job->errorString(); } else { CollectionStatisticsJob *csjob = static_cast( job ); Collection result = csjob->collection(); collectionStatisticsChanged( result.id(), csjob->statistics() ); } } void CollectionModelPrivate::collectionStatisticsChanged( Collection::Id collection, const Akonadi::CollectionStatistics &statistics ) { Q_Q( CollectionModel ); if ( !collections.contains( collection ) ) kWarning( 5250 ) << "Got statistics response for non-existing collection:" << collection; else { collections[ collection ].setStatistics( statistics ); Collection col = collections.value( collection ); QModelIndex startIndex = indexForId( col.id() ); QModelIndex endIndex = indexForId( col.id(), q->columnCount( q->parent( startIndex ) ) - 1 ); emit q->dataChanged( startIndex, endIndex ); } } void CollectionModelPrivate::listDone( KJob *job ) { if ( job->error() ) { kWarning( 5250 ) << "Job error: " << job->errorString() << endl; } } void CollectionModelPrivate::editDone( KJob * job ) { if ( job->error() ) { kWarning( 5250 ) << "Edit failed: " << job->errorString(); } } void CollectionModelPrivate::dropResult(KJob * job) { if ( job->error() ) { kWarning( 5250 ) << "Paste failed:" << job->errorString(); // TODO: error handling } } void CollectionModelPrivate::collectionsChanged( const Collection::List &cols ) { Q_Q( CollectionModel ); foreach ( Collection col, cols ) { if ( collections.contains( col.id() ) ) { // If the collection is already known to the model, we simply update it... col.setStatistics( collections.value( col.id() ).statistics() ); collections[ col.id() ] = col; QModelIndex startIndex = indexForId( col.id() ); QModelIndex endIndex = indexForId( col.id(), q->columnCount( q->parent( startIndex ) ) - 1 ); emit q->dataChanged( startIndex, endIndex ); continue; } // ... otherwise we add it to the set of collections we need to handle. m_newChildCollections[ col.parent() ].append( col.id() ); m_newCollections.insert( col.id(), col ); } // Handle the collections in m_newChildCollections. If the collections // parent is already in the model, the collection can be added to the model. // Otherwise it is persisted until it has a valid parent in the model. int currentSize = m_newChildCollections.size(); int lastSize = -1; while ( currentSize > 0 ) { lastSize = currentSize; QMutableHashIterator< Collection::Id, QList< Collection::Id > > i( m_newChildCollections ); while ( i.hasNext() ) { i.next(); // the key is the parent of new collections. It may itself also be new, // but that will be handled later. Collection::Id colId = i.key(); QList< Collection::Id > newChildCols = i.value(); int newChildCount = newChildCols.size(); // if ( newChildCount == 0 ) // { // // Sanity check. // kDebug() << "No new child collections have been added to the collection:" << colId; // i.remove(); // currentSize--; // break; // } if ( collections.contains( colId ) || colId == Collection::root().id() ) { QModelIndex parentIndex = indexForId( colId ); int currentChildCount = childCollections.value( colId ).size(); q->beginInsertRows( parentIndex, currentChildCount, // Start index is at the end of existing collections. currentChildCount + newChildCount - 1 ); // End index is the result of the insertion. foreach( Collection::Id id, newChildCols ) { Collection c = m_newCollections.take( id ); collections.insert( id, c ); } childCollections[ colId ] << newChildCols; q->endInsertRows(); i.remove(); currentSize--; break; } } // We iterated through once without adding any more collections to the model. if ( currentSize == lastSize ) { // The remaining collections in the list do not have a valid parent in the model yet. They // might arrive in the next batch from the monitor, so they're still in m_newCollections // and m_newChildCollections. kDebug() << "Some collections did not have a parent in the model yet!"; break; } } } QModelIndex CollectionModelPrivate::indexForId( Collection::Id id, int column ) const { Q_Q( const CollectionModel ); if ( !collections.contains( id ) ) return QModelIndex(); Collection::Id parentId = collections.value( id ).parent(); // check if parent still exist or if this is an orphan collection if ( parentId != Collection::root().id() && !collections.contains( parentId ) ) return QModelIndex(); QList list = childCollections.value( parentId ); int row = list.indexOf( id ); if ( row >= 0 ) return q->createIndex( row, column, reinterpret_cast( collections.value( list.at(row) ).id() ) ); return QModelIndex(); } bool CollectionModelPrivate::removeRowFromModel( int row, const QModelIndex & parent ) { Q_Q( CollectionModel ); QList list; Collection parentCol; if ( parent.isValid() ) { parentCol = collections.value( parent.internalId() ); Q_ASSERT( parentCol.id() == parent.internalId() ); list = childCollections.value( parentCol.id() ); } else { parentCol = Collection::root(); list = childCollections.value( Collection::root().id() ); } if ( row < 0 || row >= list.size() ) { kWarning( 5250 ) << "Index out of bounds:" << row <<" parent:" << parentCol.id(); return false; } q->beginRemoveRows( parent, row, row ); Collection::Id delColId = list.takeAt( row ); foreach( Collection::Id childColId, childCollections[ delColId ] ) collections.remove( childColId ); collections.remove( delColId ); childCollections.remove( delColId ); // remove children of deleted collection childCollections.insert( parentCol.id(), list ); // update children of parent q->endRemoveRows(); return true; } bool CollectionModelPrivate::supportsContentType(const QModelIndex & index, const QStringList & contentTypes) { if ( !index.isValid() ) return false; Collection col = collections.value( index.internalId() ); Q_ASSERT( col.isValid() ); QStringList ct = col.contentMimeTypes(); foreach ( const QString &a, ct ) { if ( contentTypes.contains( a ) ) return true; } return false; } void CollectionModelPrivate::init() { Q_Q( CollectionModel ); - session = new Session( QByteArray("CollectionModel-") + QByteArray::number( qrand() ), q ); + session = new Session( QCoreApplication::instance()->applicationName().toUtf8() + + QByteArray("-CollectionModel-") + QByteArray::number( qrand() ), q ); QTimer::singleShot( 0, q, SLOT(startFirstListJob()) ); // monitor collection changes monitor = new Monitor(); monitor->setCollectionMonitored( Collection::root() ); monitor->fetchCollection( true ); // ### Hack to get the kmail resource folder icons KIconLoader::global()->addAppDir( QLatin1String( "kmail" ) ); KIconLoader::global()->addAppDir( QLatin1String( "kdepim" ) ); // monitor collection changes q->connect( monitor, SIGNAL(collectionChanged(const Akonadi::Collection&)), q, SLOT(collectionChanged(const Akonadi::Collection&)) ); q->connect( monitor, SIGNAL(collectionAdded(Akonadi::Collection,Akonadi::Collection)), q, SLOT(collectionChanged(Akonadi::Collection)) ); q->connect( monitor, SIGNAL(collectionRemoved(Akonadi::Collection)), q, SLOT(collectionRemoved(Akonadi::Collection)) ); q->connect( monitor, SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), q, SLOT(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)) ); } void CollectionModelPrivate::startFirstListJob() { Q_Q( CollectionModel ); // start a list job CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive, session ); job->includeUnsubscribed( unsubscribed ); q->connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)), q, SLOT(collectionsChanged(Akonadi::Collection::List)) ); q->connect( job, SIGNAL(result(KJob*)), q, SLOT(listDone(KJob*)) ); } //@endcond diff --git a/akonadi/itemmodel.cpp b/akonadi/itemmodel.cpp index 428e6dfdf..4207f2352 100644 --- a/akonadi/itemmodel.cpp +++ b/akonadi/itemmodel.cpp @@ -1,401 +1,403 @@ /* Copyright (c) 2006 - 2007 Volker Krause 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 "itemmodel.h" #include "itemfetchjob.h" #include "itemfetchscope.h" #include "monitor.h" #include "pastehelper.h" #include "session.h" #include #include #include #include +#include #include #include using namespace Akonadi; /** * @internal * * This struct is used for optimization reasons. * because it embeds the row. * * Semantically, we could have used an item instead. */ struct ItemContainer { ItemContainer( const Item& i, int r ) { item = i; row = r; } Item item; int row; }; /** * @internal */ class ItemModel::Private { public: Private( ItemModel *parent ) : mParent( parent ), monitor( new Monitor() ) { - session = new Session( QByteArray("ItemModel-") + QByteArray::number( qrand() ), mParent ); + session = new Session( QCoreApplication::instance()->applicationName().toUtf8() + + QByteArray("-ItemModel-") + QByteArray::number( qrand() ), mParent ); monitor->ignoreSession( session ); mParent->connect( monitor, SIGNAL(itemChanged( const Akonadi::Item&, const QSet& )), mParent, SLOT(itemChanged( const Akonadi::Item&, const QSet& )) ); mParent->connect( monitor, SIGNAL(itemMoved( const Akonadi::Item&, const Akonadi::Collection&, const Akonadi::Collection& )), mParent, SLOT(itemMoved( const Akonadi::Item&, const Akonadi::Collection&, const Akonadi::Collection& ) ) ); mParent->connect( monitor, SIGNAL(itemAdded( const Akonadi::Item&, const Akonadi::Collection& )), mParent, SLOT(itemAdded( const Akonadi::Item& )) ); mParent->connect( monitor, SIGNAL(itemRemoved(Akonadi::Item)), mParent, SLOT(itemRemoved(Akonadi::Item)) ); mParent->connect( monitor, SIGNAL(itemLinked(const Akonadi::Item&, const Akonadi::Collection&)), mParent, SLOT(itemAdded(const Akonadi::Item&)) ); mParent->connect( monitor, SIGNAL(itemUnlinked(const Akonadi::Item&, const Akonadi::Collection&)), mParent, SLOT(itemRemoved(const Akonadi::Item&)) ); } ~Private() { delete monitor; } void listingDone( KJob* ); void itemChanged( const Akonadi::Item&, const QSet& ); void itemsAdded( const Akonadi::Item::List &list ); void itemAdded( const Akonadi::Item &item ); void itemMoved( const Akonadi::Item&, const Akonadi::Collection& src, const Akonadi::Collection& dst ); void itemRemoved( const Akonadi::Item& ); int rowForItem( const Akonadi::Item& ); ItemModel *mParent; QList items; QHash itemHash; Collection collection; Monitor *monitor; Session *session; }; void ItemModel::Private::listingDone( KJob * job ) { ItemFetchJob *fetch = static_cast( job ); Q_UNUSED( fetch ); if ( job->error() ) { // TODO kWarning( 5250 ) << "Item query failed:" << job->errorString(); } } int ItemModel::Private::rowForItem( const Akonadi::Item& item ) { ItemContainer *container = itemHash.value( item ); if ( !container ) return -1; /* Try to find the item directly; If items have been removed, this first try won't succeed because the ItemContainer rows have not been updated (costs too much). */ if ( container->row < items.count() && items.at( container->row ) == container ) return container->row; else { // Slow solution if the fist one has not succeeded int row = -1; for ( int i = 0; i < items.size(); ++i ) { if ( items.at( i )->item == item ) { row = i; break; } } return row; } } void ItemModel::Private::itemChanged( const Akonadi::Item &item, const QSet& ) { int row = rowForItem( item ); if ( row < 0 ) return; items[ row ]->item = item; itemHash.remove( item ); itemHash[ item ] = items[ row ]; QModelIndex start = mParent->index( row, 0, QModelIndex() ); QModelIndex end = mParent->index( row, mParent->columnCount( QModelIndex() ) - 1 , QModelIndex() ); mParent->dataChanged( start, end ); } void ItemModel::Private::itemMoved( const Akonadi::Item &item, const Akonadi::Collection& colSrc, const Akonadi::Collection& colDst ) { if ( colSrc == collection && colDst != collection ) // item leaving this model { itemRemoved( item ); return; } if ( colDst == collection && colSrc != collection ) { itemAdded( item ); return; } } void ItemModel::Private::itemsAdded( const Akonadi::Item::List &list ) { if ( list.isEmpty() ) return; mParent->beginInsertRows( QModelIndex(), items.count(), items.count() + list.count() - 1 ); foreach( const Item &item, list ) { ItemContainer *c = new ItemContainer( item, items.count() ); items.append( c ); itemHash[ item ] = c; } mParent->endInsertRows(); } void ItemModel::Private::itemAdded( const Akonadi::Item &item ) { Item::List l; l << item; itemsAdded( l ); } void ItemModel::Private::itemRemoved( const Akonadi::Item &_item ) { int row = rowForItem( _item ); if ( row < 0 ) return; mParent->beginRemoveRows( QModelIndex(), row, row ); const Item item = items.at( row )->item; Q_ASSERT( item.isValid() ); itemHash.remove( item ); delete items.takeAt( row ); mParent->endRemoveRows(); } ItemModel::ItemModel( QObject *parent ) : QAbstractTableModel( parent ), d( new Private( this ) ) { setSupportedDragActions( Qt::MoveAction | Qt::CopyAction ); } ItemModel::~ItemModel() { delete d; } QVariant ItemModel::data( const QModelIndex & index, int role ) const { if ( !index.isValid() ) return QVariant(); if ( index.row() >= d->items.count() ) return QVariant(); const Item item = d->items.at( index.row() )->item; if ( !item.isValid() ) return QVariant(); if ( role == Qt::DisplayRole ) { switch ( index.column() ) { case Id: return QString::number( item.id() ); case RemoteId: return item.remoteId(); case MimeType: return item.mimeType(); default: return QVariant(); } } if ( role == IdRole ) return item.id(); if ( role == ItemRole ) { QVariant var; var.setValue( item ); return var; } if ( role == MimeTypeRole ) return item.mimeType(); return QVariant(); } int ItemModel::rowCount( const QModelIndex & parent ) const { if ( !parent.isValid() ) return d->items.count(); return 0; } int ItemModel::columnCount(const QModelIndex & parent) const { if ( !parent.isValid() ) return 3; // keep in sync with Column enum return 0; } QVariant ItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) { switch ( section ) { case Id: return i18n( "Id" ); case RemoteId: return i18n( "Remote Id" ); case MimeType: return i18n( "MimeType" ); default: return QString(); } } return QAbstractTableModel::headerData( section, orientation, role ); } void ItemModel::setCollection( const Collection &collection ) { kDebug( 5250 ); if ( d->collection == collection ) return; d->monitor->setCollectionMonitored( d->collection, false ); d->collection = collection; d->monitor->setCollectionMonitored( d->collection, true ); // the query changed, thus everything we have already is invalid qDeleteAll( d->items ); d->items.clear(); reset(); // stop all running jobs d->session->clear(); // start listing job ItemFetchJob* job = new ItemFetchJob( collection, session() ); job->setFetchScope( d->monitor->itemFetchScope() ); connect( job, SIGNAL(itemsReceived(Akonadi::Item::List)), SLOT(itemsAdded(Akonadi::Item::List)) ); connect( job, SIGNAL(result(KJob*)), SLOT(listingDone(KJob*)) ); emit collectionChanged( collection ); } void ItemModel::setFetchScope( const ItemFetchScope &fetchScope ) { d->monitor->setItemFetchScope( fetchScope ); } ItemFetchScope &ItemModel::fetchScope() { return d->monitor->itemFetchScope(); } Item ItemModel::itemForIndex( const QModelIndex & index ) const { if ( !index.isValid() ) return Akonadi::Item(); if ( index.row() >= d->items.count() ) return Akonadi::Item(); Item item = d->items.at( index.row() )->item; Q_ASSERT( item.isValid() ); return item; } Qt::ItemFlags ItemModel::flags( const QModelIndex &index ) const { Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index); if (index.isValid()) return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; else return Qt::ItemIsDropEnabled | defaultFlags; } QStringList ItemModel::mimeTypes() const { return QStringList(); } Session * ItemModel::session() const { return d->session; } QMimeData *ItemModel::mimeData( const QModelIndexList &indexes ) const { QMimeData *data = new QMimeData(); // Add item uri to the mimedata for dropping in external applications KUrl::List urls; foreach ( const QModelIndex &index, indexes ) { if ( index.column() != 0 ) continue; urls << itemForIndex( index ).url( Item::UrlWithMimeType ); } urls.populateMimeData( data ); return data; } QModelIndex ItemModel::indexForItem( const Akonadi::Item &item, const int column ) const { return index( d->rowForItem( item ), column ); } bool ItemModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) { Q_UNUSED( row ); Q_UNUSED( column ); Q_UNUSED( parent ); KJob* job = PasteHelper::paste( data, d->collection, action != Qt::MoveAction ); // TODO: error handling return job; } Collection ItemModel::collection() const { return d->collection; } #include "itemmodel.moc" diff --git a/akonadi/session.cpp b/akonadi/session.cpp index c8013a3dd..cd67493f5 100644 --- a/akonadi/session.cpp +++ b/akonadi/session.cpp @@ -1,294 +1,297 @@ /* Copyright (c) 2007 Volker Krause 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 "session.h" #include "session_p.h" #include "imapparser_p.h" #include "job.h" #include "job_p.h" #include "servermanager_p.h" #include "xdgbasedirs_p.h" #include #include +#include #include #include #include #include #include #define PIPELINE_LENGTH 2 using namespace Akonadi; //@cond PRIVATE void SessionPrivate::startNext() { QTimer::singleShot( 0, mParent, SLOT(doStartNext()) ); } void SessionPrivate::reconnect() { // should be checking connection method and value validity if ( socket->state() != QLocalSocket::ConnectedState && socket->state() != QLocalSocket::ConnectingState ) { #ifdef Q_OS_WIN //krazy:exclude=cpp const QString namedPipe = mConnectionSettings->value( QLatin1String( "Data/NamedPipe" ), QLatin1String( "Akonadi" ) ).toString(); socket->connectToServer( namedPipe ); #else const QString defaultSocketDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi" ) ); const QString path = mConnectionSettings->value( QLatin1String( "Data/UnixPath" ), defaultSocketDir + QLatin1String( "/akonadiserver.socket" ) ).toString(); socket->connectToServer( path ); #endif } } void SessionPrivate::socketError() { if ( currentJob ) currentJob->d_ptr->lostConnection(); connected = false; QTimer::singleShot( 1000, mParent, SLOT(reconnect()) ); } void SessionPrivate::dataReceived() { while ( socket->bytesAvailable() > 0 ) { if ( parser->continuationSize() > 1 ) { const QByteArray data = socket->read( qMin( socket->bytesAvailable(), parser->continuationSize() - 1 ) ); parser->parseBlock( data ); } else if ( socket->canReadLine() ) { if ( !parser->parseNextLine( socket->readLine() ) ) continue; // response not yet completed // handle login response if ( parser->tag() == QByteArray("0") ) { if ( parser->data().startsWith( "OK" ) ) { connected = true; startNext(); } else { kWarning( 5250 ) << "Unable to login to Akonadi server:" << parser->data(); socket->close(); QTimer::singleShot( 1000, mParent, SLOT(reconnect()) ); } } // send login command if ( parser->tag() == "*" && parser->data().startsWith( "OK Akonadi" ) ) { const int pos = parser->data().indexOf( "[PROTOCOL" ); if ( pos > 0 ) { qint64 tmp = 0; ImapParser::parseNumber( parser->data(), tmp, 0, pos + 9 ); protocolVersion = tmp; Internal::setServerProtocolVersion( tmp ); } kDebug( 5250 ) << "Server protocol version is:" << protocolVersion; writeData( "0 LOGIN " + sessionId + '\n' ); // work for the current job } else { if ( currentJob ) currentJob->d_ptr->handleResponse( parser->tag(), parser->data() ); } // reset parser stuff parser->reset(); } else { break; // nothing we can do for now } } } bool SessionPrivate::canPipelineNext() { if ( queue.isEmpty() || pipeline.count() >= PIPELINE_LENGTH ) return false; if ( pipeline.isEmpty() && currentJob ) return currentJob->d_ptr->mWriteFinished; if ( !pipeline.isEmpty() ) return pipeline.last()->d_ptr->mWriteFinished; return false; } void SessionPrivate::doStartNext() { if ( !connected || (queue.isEmpty() && pipeline.isEmpty()) ) return; if ( canPipelineNext() ) { Akonadi::Job *nextJob = queue.dequeue(); pipeline.enqueue( nextJob ); startJob( nextJob ); } if ( jobRunning ) return; jobRunning = true; if ( !pipeline.isEmpty() ) { currentJob = pipeline.dequeue(); } else { currentJob = queue.dequeue(); startJob( currentJob ); } } void SessionPrivate::startJob( Job *job ) { if ( protocolVersion < minimumProtocolVersion() ) { job->setError( Job::ProtocolVersionMismatch ); job->setErrorText( i18n( "Protocol version %1 found, expected at least %2", protocolVersion, minimumProtocolVersion() ) ); job->emitResult(); } else { job->d_ptr->startQueued(); } } void SessionPrivate::jobDone(KJob * job) { if( job == currentJob ) { if ( pipeline.isEmpty() ) { jobRunning = false; currentJob = 0; } else { currentJob = pipeline.dequeue(); } startNext(); } // ### better handle the other cases too, user might have canceled jobs else { kDebug( 5250 ) << job << "Non-current job finished."; } } void SessionPrivate::jobWriteFinished( Akonadi::Job* job ) { Q_ASSERT( (job == currentJob && pipeline.isEmpty()) || (job = pipeline.last()) ); startNext(); } void SessionPrivate::jobDestroyed(QObject * job) { queue.removeAll( static_cast( job ) ); // ### likely not enough to really cancel already running jobs pipeline.removeAll( static_cast( job ) ); if ( currentJob == job ) currentJob = 0; } void SessionPrivate::addJob(Job * job) { queue.append( job ); QObject::connect( job, SIGNAL(result(KJob*)), mParent, SLOT(jobDone(KJob*)) ); QObject::connect( job, SIGNAL(writeFinished(Akonadi::Job*)), mParent, SLOT(jobWriteFinished(Akonadi::Job*)) ); QObject::connect( job, SIGNAL(destroyed(QObject*)), mParent, SLOT(jobDestroyed(QObject*)) ); startNext(); } int SessionPrivate::nextTag() { return theNextTag++; } void SessionPrivate::writeData(const QByteArray & data) { socket->write( data ); } //@endcond Session::Session(const QByteArray & sessionId, QObject * parent) : QObject( parent ), d( new SessionPrivate( this ) ) { - if ( !sessionId.isEmpty() ) + if ( !sessionId.isEmpty() ) { d->sessionId = sessionId; - else - d->sessionId = QByteArray::number( qrand() ); + } else { + d->sessionId = QCoreApplication::instance()->applicationName().toUtf8() + + "-" + QByteArray::number( qrand() ); + } d->connected = false; d->theNextTag = 1; d->currentJob = 0; d->jobRunning = false; const QString connectionConfigFile = XdgBaseDirs::akonadiConnectionConfigFile(); QFileInfo fileInfo( connectionConfigFile ); if ( !fileInfo.exists() ) { kWarning( 5250 ) << "Akonadi Client Session: connection config file '" << "akonadi/akonadiconnectionrc can not be found in '" << XdgBaseDirs::homePath( "config" ) << "' nor in any of " << XdgBaseDirs::systemPathList( "config" ); } d->mConnectionSettings = new QSettings( connectionConfigFile, QSettings::IniFormat ); // should check connection method d->socket = new QLocalSocket( this ); connect( d->socket, SIGNAL(disconnected()), SLOT(socketError()) ); connect( d->socket, SIGNAL(error(QLocalSocket::LocalSocketError)), SLOT(socketError()) ); connect( d->socket, SIGNAL(readyRead()), SLOT(dataReceived()) ); d->reconnect(); } Session::~Session() { clear(); delete d; } QByteArray Session::sessionId() const { return d->sessionId; } QThreadStorage instances; void SessionPrivate::createDefaultSession( const QByteArray &sessionId ) { Q_ASSERT_X( !sessionId.isEmpty(), "SessionPrivate::createDefaultSession", "You tried to create a default session with empty session id!" ); Q_ASSERT_X( !instances.hasLocalData(), "SessionPrivate::createDefaultSession", "You tried to create a default session twice!" ); instances.setLocalData( new Session( sessionId ) ); } Session* Session::defaultSession() { if ( !instances.hasLocalData() ) instances.setLocalData( new Session() ); return instances.localData(); } void Session::clear() { foreach ( Job* job, d->queue ) job->kill( KJob::EmitResult ); d->queue.clear(); if ( d->currentJob ) d->currentJob->kill( KJob::EmitResult ); } #include "session.moc"