diff --git a/src/revisioncontrolobserver.cpp b/src/revisioncontrolobserver.cpp index 9b8cb25838..9d11154e02 100644 --- a/src/revisioncontrolobserver.cpp +++ b/src/revisioncontrolobserver.cpp @@ -26,15 +26,75 @@ #include #include +#include #include +/** + * The performance of updating the revision state of items depends + * on the used plugin. To prevent that Dolphin gets blocked by a + * slow plugin, the updating is delegated to a thread. + */ +class LIBDOLPHINPRIVATE_EXPORT UpdateItemStatesThread : public QThread +{ +public: + UpdateItemStatesThread(QObject* parent); + void setData(RevisionControlPlugin* plugin, + const QList& itemStates); + QList itemStates() const; + +protected: + virtual void run(); + +private: + RevisionControlPlugin* m_plugin; + QList m_itemStates; +}; + +UpdateItemStatesThread::UpdateItemStatesThread(QObject* parent) : + QThread(parent) +{ +} + +void UpdateItemStatesThread::setData(RevisionControlPlugin* plugin, + const QList& itemStates) +{ + m_plugin = plugin; + m_itemStates = itemStates; +} + +void UpdateItemStatesThread::run() +{ + Q_ASSERT(m_itemStates.count() > 0); + Q_ASSERT(m_plugin != 0); + + // it is assumed that all items have the same parent directory + const QString directory = m_itemStates.first().item.url().directory(KUrl::AppendTrailingSlash); + + if (m_plugin->beginRetrieval(directory)) { + const int count = m_itemStates.count(); + for (int i = 0; i < count; ++i) { + m_itemStates[i].revision = m_plugin->revisionState(m_itemStates[i].item); + } + m_plugin->endRetrieval(); + } +} + +QList UpdateItemStatesThread::itemStates() const +{ + return m_itemStates; +} + +// --- + RevisionControlObserver::RevisionControlObserver(QAbstractItemView* view) : QObject(view), + m_pendingItemStatesUpdate(false), m_view(view), m_dirLister(0), m_dolphinModel(0), m_dirVerificationTimer(0), - m_plugin(0) + m_plugin(0), + m_updateItemStatesThread(0) { Q_ASSERT(view != 0); @@ -101,24 +161,70 @@ void RevisionControlObserver::verifyDirectory() } } +void RevisionControlObserver::applyUpdatedItemStates() +{ + // Updating items with non-uniform item sizes is a serious bottleneck + // in QListView. Temporary disable the non-uniform item sizes. + QListView* listView = qobject_cast(m_view); + bool uniformSizes = true; + if (listView != 0) { + uniformSizes = listView->uniformItemSizes(); + //listView->setUniformItemSizes(true); TODO: does not work as well as in KFilePreviewGenerator + } + + const QList itemStates = m_updateItemStatesThread->itemStates(); + foreach (const ItemState& itemState, itemStates) { + m_dolphinModel->setData(itemState.index, + QVariant(static_cast(itemState.revision)), + Qt::DecorationRole); + } + + if (listView != 0) { + listView->setUniformItemSizes(uniformSizes); + } + + m_view->viewport()->repaint(); // TODO: this should not be necessary, as DolphinModel::setData() calls dataChanged() + + if (m_pendingItemStatesUpdate) { + m_pendingItemStatesUpdate = false; + updateItemStates(); + } +} + void RevisionControlObserver::updateItemStates() { Q_ASSERT(m_plugin != 0); - const KUrl directory = m_dirLister->url(); - if (!m_plugin->beginRetrieval(directory.toLocalFile(KUrl::AddTrailingSlash))) { + if (m_updateItemStatesThread == 0) { + m_updateItemStatesThread = new UpdateItemStatesThread(this); + connect(m_updateItemStatesThread, SIGNAL(finished()), + this, SLOT(applyUpdatedItemStates())); + } + if (m_updateItemStatesThread->isRunning()) { + // An update is currently ongoing. Wait until the thread has finished + // the update (see applyUpdatedItemStates()). + m_pendingItemStatesUpdate = true; return; } - + const int rowCount = m_dolphinModel->rowCount(); - for (int row = 0; row < rowCount; ++row) { - const QModelIndex index = m_dolphinModel->index(row, DolphinModel::Revision); - const KFileItem item = m_dolphinModel->itemForIndex(index); - const RevisionControlPlugin::RevisionState revision = m_plugin->revisionState(item); - m_dolphinModel->setData(index, QVariant(static_cast(revision)), Qt::DecorationRole); + if (rowCount > 0) { + // Build a list of all items in the current directory and delegate + // this list to the thread, which adjusts the revision states. + QList itemStates; + for (int row = 0; row < rowCount; ++row) { + const QModelIndex index = m_dolphinModel->index(row, DolphinModel::Revision); + + ItemState itemState; + itemState.index = index; + itemState.item = m_dolphinModel->itemForIndex(index); + itemState.revision = RevisionControlPlugin::LocalRevision; + + itemStates.append(itemState); + } + + m_updateItemStatesThread->setData(m_plugin, itemStates); + m_updateItemStatesThread->start(); // applyUpdatedItemStates() is called when finished } - m_view->viewport()->repaint(); // TODO: this should not be necessary, as DolphinModel::setData() calls dataChanged() - - m_plugin->endRetrieval(); } #include "revisioncontrolobserver.moc" diff --git a/src/revisioncontrolobserver.h b/src/revisioncontrolobserver.h index 14ea0528ea..c5cf9bb0dc 100644 --- a/src/revisioncontrolobserver.h +++ b/src/revisioncontrolobserver.h @@ -22,14 +22,18 @@ #include +#include +#include #include +#include #include class DolphinModel; class KDirLister; class QAbstractItemView; +class QThread; class QTimer; -class RevisionControlPlugin; +class UpdateItemStatesThread; /** * @brief Observes all revision control plugins. @@ -50,16 +54,31 @@ public: private slots: void delayedDirectoryVerification(); void verifyDirectory(); - + void applyUpdatedItemStates(); + private: void updateItemStates(); private: + struct ItemState + { + QPersistentModelIndex index; + KFileItem item; + RevisionControlPlugin::RevisionState revision; + }; + + bool m_pendingItemStatesUpdate; + QAbstractItemView* m_view; KDirLister* m_dirLister; DolphinModel* m_dolphinModel; + QTimer* m_dirVerificationTimer; + RevisionControlPlugin* m_plugin; + UpdateItemStatesThread* m_updateItemStatesThread; + + friend class UpdateItemStatesThread; }; #endif // REVISIONCONTROLOBSERVER_H