#include <QAbstractProxyModel>
#include <QAbstractItemView>
+#include <QListView>
#include <QTimer>
+/**
+ * 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<RevisionControlObserver::ItemState>& itemStates);
+ QList<RevisionControlObserver::ItemState> itemStates() const;
+
+protected:
+ virtual void run();
+
+private:
+ RevisionControlPlugin* m_plugin;
+ QList<RevisionControlObserver::ItemState> m_itemStates;
+};
+
+UpdateItemStatesThread::UpdateItemStatesThread(QObject* parent) :
+ QThread(parent)
+{
+}
+
+void UpdateItemStatesThread::setData(RevisionControlPlugin* plugin,
+ const QList<RevisionControlObserver::ItemState>& 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<RevisionControlObserver::ItemState> 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);
}
}
+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<QListView*>(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<ItemState> itemStates = m_updateItemStatesThread->itemStates();
+ foreach (const ItemState& itemState, itemStates) {
+ m_dolphinModel->setData(itemState.index,
+ QVariant(static_cast<int>(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<int>(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<ItemState> 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"
#include <libdolphin_export.h>
+#include <kfileitem.h>
+#include <revisioncontrolplugin.h>
#include <QObject>
+#include <QPersistentModelIndex>
#include <QString>
class DolphinModel;
class KDirLister;
class QAbstractItemView;
+class QThread;
class QTimer;
-class RevisionControlPlugin;
+class UpdateItemStatesThread;
/**
* @brief Observes all revision control plugins.
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