]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kfileitemmodelrolesupdater.cpp
Merge remote-tracking branch 'origin/KDE/4.11'
[dolphin.git] / src / kitemviews / kfileitemmodelrolesupdater.cpp
index eaaab6bc0529f1f72a2bc81ea3b1cd3729f8280b..d6445c981f72dcf5a0aa45851d169b056feff14b 100644 (file)
 #include <KConfig>
 #include <KConfigGroup>
 #include <KDebug>
-#include <KDirWatch>
 #include <KFileItem>
 #include <KGlobal>
 #include <KIO/JobUiDelegate>
 #include <KIO/PreviewJob>
 
 #include "private/kpixmapmodifier.h"
+#include "private/kdirectorycontentscounter.h"
 
 #include <QApplication>
 #include <QPainter>
     #include <Nepomuk2/ResourceManager>
 #endif
 
-// Required includes for subItemsCount():
-#ifdef Q_WS_WIN
-    #include <QDir>
-#else
-    #include <dirent.h>
-    #include <QFile>
-#endif
-
 // #define KFILEITEMMODELROLESUPDATER_DEBUG
 
 namespace {
@@ -89,16 +81,13 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO
     m_resolvableRoles(),
     m_enabledPlugins(),
     m_pendingSortRoleItems(),
-    m_hasUnknownIcons(false),
-    m_firstIndexWithoutIcon(0),
     m_pendingIndexes(),
     m_pendingPreviewItems(),
     m_previewJob(),
     m_recentlyChangedItemsTimer(0),
     m_recentlyChangedItems(),
     m_changedItems(),
-    m_dirWatcher(0),
-    m_watchedDirs()
+    m_directoryContentsCounter(0)
   #ifdef HAVE_NEPOMUK
   , m_nepomukResourceWatcher(0),
     m_nepomukUriItems()
@@ -137,10 +126,9 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO
     m_resolvableRoles += KNepomukRolesProvider::instance().roles();
 #endif
 
-    // When folders are expandable or the item-count is shown for folders, it is necessary
-    // to watch the number of items of the sub-folder to be able to react on changes.
-    m_dirWatcher = new KDirWatch(this);
-    connect(m_dirWatcher, SIGNAL(dirty(QString)), this, SLOT(slotDirWatchDirty(QString)));
+    m_directoryContentsCounter = new KDirectoryContentsCounter(m_model, this);
+    connect(m_directoryContentsCounter, SIGNAL(result(QString,int)),
+            this,                       SLOT(slotDirectoryContentsCountReceived(QString,int)));
 }
 
 KFileItemModelRolesUpdater::~KFileItemModelRolesUpdater()
@@ -296,7 +284,7 @@ void KFileItemModelRolesUpdater::setRoles(const QSet<QByteArray>& roles)
 
                 m_nepomukResourceWatcher = new Nepomuk2::ResourceWatcher(this);
                 connect(m_nepomukResourceWatcher, SIGNAL(propertyChanged(Nepomuk2::Resource,Nepomuk2::Types::Property,QVariantList,QVariantList)),
-                        this, SLOT(applyChangedNepomukRoles(Nepomuk2::Resource)));
+                        this, SLOT(applyChangedNepomukRoles(Nepomuk2::Resource,Nepomuk2::Types::Property)));
             } else if (!hasNepomukRole && m_nepomukResourceWatcher) {
                 delete m_nepomukResourceWatcher;
                 m_nepomukResourceWatcher = 0;
@@ -333,10 +321,6 @@ void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList& itemRan
     QElapsedTimer timer;
     timer.start();
 
-    const int firstInsertedIndex = itemRanges.first().index;
-    m_firstIndexWithoutIcon = qMin(m_firstIndexWithoutIcon, firstInsertedIndex);
-    m_hasUnknownIcons = true;
-
     // Determine the sort role synchronously for as many items as possible.
     if (m_resolvableRoles.contains(m_model->sortRole())) {
         int insertedCount = 0;
@@ -373,30 +357,6 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRang
 
     const bool allItemsRemoved = (m_model->count() == 0);
 
-    if (m_hasUnknownIcons) {
-        const int firstRemovedIndex = itemRanges.first().index;
-        m_firstIndexWithoutIcon = qMin(m_firstIndexWithoutIcon, firstRemovedIndex);
-    }
-
-    if (!m_watchedDirs.isEmpty()) {
-        // Don't let KDirWatch watch for removed items
-        if (allItemsRemoved) {
-            foreach (const QString& path, m_watchedDirs) {
-                m_dirWatcher->removeDir(path);
-            }
-            m_watchedDirs.clear();
-        } else {
-            QMutableSetIterator<QString> it(m_watchedDirs);
-            while (it.hasNext()) {
-                const QString& path = it.next();
-                if (m_model->index(KUrl(path)) < 0) {
-                    m_dirWatcher->removeDir(path);
-                    it.remove();
-                }
-            }
-        }
-    }
-
 #ifdef HAVE_NEPOMUK
     if (m_nepomukResourceWatcher) {
         // Don't let the ResourceWatcher watch for removed items
@@ -459,11 +419,6 @@ void KFileItemModelRolesUpdater::slotItemsMoved(const KItemRange& itemRange, QLi
     Q_UNUSED(itemRange);
     Q_UNUSED(movedToIndexes);
 
-    if (m_hasUnknownIcons) {
-        const int firstMovedIndex = itemRange.index;
-        m_firstIndexWithoutIcon = qMin(m_firstIndexWithoutIcon, firstMovedIndex);
-    }
-
     // The visible items might have changed.
     startUpdating();
 }
@@ -586,6 +541,22 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPi
     }
 
     QHash<QByteArray, QVariant> data = rolesData(item);
+
+    const QStringList overlays = data["iconOverlays"].toStringList();
+    // Strangely KFileItem::overlays() returns empty string-values, so
+    // we need to check first whether an overlay must be drawn at all.
+    // It is more efficient to do it here, as KIconLoader::drawOverlays()
+    // assumes that an overlay will be drawn and has some additional
+    // setup time.
+    foreach (const QString& overlay, overlays) {
+        if (!overlay.isEmpty()) {
+            // There is at least one overlay, draw all overlays above m_pixmap
+            // and cancel the check
+            KIconLoader::global()->drawOverlays(overlays, scaledPixmap, KIconLoader::Desktop);
+            break;
+        }
+    }
+
     data.insert("iconPixmap", scaledPixmap);
 
     disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
@@ -737,7 +708,7 @@ void KFileItemModelRolesUpdater::resolveRecentlyChangedItems()
     updateChangedItems();
 }
 
-void KFileItemModelRolesUpdater::applyChangedNepomukRoles(const Nepomuk2::Resource& resource)
+void KFileItemModelRolesUpdater::applyChangedNepomukRoles(const Nepomuk2::Resource& resource, const Nepomuk2::Types::Property& property)
 {
 #ifdef HAVE_NEPOMUK
     if (!Nepomuk2::ResourceManager::instance()->initialized()) {
@@ -756,6 +727,14 @@ void KFileItemModelRolesUpdater::applyChangedNepomukRoles(const Nepomuk2::Resour
     QHash<QByteArray, QVariant> data = rolesData(item);
 
     const KNepomukRolesProvider& rolesProvider = KNepomukRolesProvider::instance();
+    const QByteArray role = rolesProvider.roleForPropertyUri(property.uri());
+    if (!role.isEmpty() && m_roles.contains(role)) {
+        // Overwrite the changed role value with an empty QVariant, because the roles
+        // provider doesn't overwrite it when the property value list is empty.
+        // See bug 322348
+        data.insert(role, QVariant());
+    }
+
     QHashIterator<QByteArray, QVariant> it(rolesProvider.roleValues(resource, m_roles));
     while (it.hasNext()) {
         it.next();
@@ -775,7 +754,7 @@ void KFileItemModelRolesUpdater::applyChangedNepomukRoles(const Nepomuk2::Resour
 #endif
 }
 
-void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path)
+void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count)
 {
     const bool getSizeRole = m_roles.contains("size");
     const bool getIsExpandableRole = m_roles.contains("isExpandable");
@@ -783,16 +762,9 @@ void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path)
     if (getSizeRole || getIsExpandableRole) {
         const int index = m_model->index(KUrl(path));
         if (index >= 0) {
-            if (!m_model->fileItem(index).isDir()) {
-                // If INotify is used, KDirWatch issues the dirty() signal
-                // also for changed files inside the directory, even if we
-                // don't enable this behavior explicitly (see bug 309740).
-                return;
-            }
 
             QHash<QByteArray, QVariant> data;
 
-            const int count = subItemsCount(path);
             if (getSizeRole) {
                 data.insert("size", count);
             }
@@ -800,9 +772,11 @@ void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path)
                 data.insert("isExpandable", count > 0);
             }
 
-            // Note that we do not block the itemsChanged signal here.
-            // This ensures that a new preview will be generated.
+            disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+                       this,    SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
             m_model->setData(index, data);
+            connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+                    this,    SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
         }
     }
 }
@@ -829,13 +803,6 @@ void KFileItemModelRolesUpdater::startUpdating()
     // Determine the icons for the visible items synchronously.
     updateVisibleIcons();
 
-    // Try to do at least a fast icon loading (without determining the
-    // mime type) for all items, to reduce the risk that the user sees
-    // "unknown" icons when scrolling.
-    if (m_hasUnknownIcons) {
-        updateAllIconsFast(MaxBlockTimeout - timer.elapsed());
-    }
-
     // A detailed update of the items in and near the visible area
     // only makes sense if sorting is finished.
     if (m_state == ResolvingSortRole) {
@@ -887,58 +854,9 @@ void KFileItemModelRolesUpdater::updateVisibleIcons()
         applyResolvedRoles(item, ResolveFast);
     }
 
-    if (index > lastVisibleIndex) {
-        return;
-    }
-
-    // If this didn't work before MaxBlockTimeout was reached, at least
-    // prevent that the user sees 'unknown' icons.
-    disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
-               this,    SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
-
-    while (index <= lastVisibleIndex) {
-        if (!m_model->data(index).contains("iconName")) {
-            const KFileItem item = m_model->fileItem(index);
-            QHash<QByteArray, QVariant> data;
-            data.insert("iconName", item.iconName());
-            m_model->setData(index, data);
-        }
-        ++index;
-    }
-
-    connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
-            this,    SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
-}
-
-void KFileItemModelRolesUpdater::updateAllIconsFast(int timeout)
-{
-    if (timeout <= 0) {
-        return;
-    }
-
-    QElapsedTimer timer;
-    timer.start();
-
-    disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
-               this,    SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
-
-    const int count = m_model->count();
-    while (m_firstIndexWithoutIcon < count && timer.elapsed() < timeout) {
-        if (!m_model->data(m_firstIndexWithoutIcon).contains("iconName")) {
-            const KFileItem item = m_model->fileItem(m_firstIndexWithoutIcon);
-            QHash<QByteArray, QVariant> data;
-            data.insert("iconName", item.iconName());
-            m_model->setData(m_firstIndexWithoutIcon, data);
-        }
-        ++m_firstIndexWithoutIcon;
-    }
-
-    if (m_firstIndexWithoutIcon == count) {
-        m_hasUnknownIcons = false;
-    }
-
-    connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
-            this,    SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+    // KFileItemListView::initializeItemListWidget(KItemListWidget*) will load
+    // preliminary icons (i.e., without mime type determination) for the
+    // remaining items.
 }
 
 void KFileItemModelRolesUpdater::startPreviewJob()
@@ -1085,7 +1003,7 @@ void KFileItemModelRolesUpdater::applySortRole(int index)
         data.insert("type", item.mimeComment());
     } else if (m_model->sortRole() == "size" && item.isLocalFile() && item.isDir()) {
         const QString path = item.localPath();
-        data.insert("size", subItemsCount(path));
+        data.insert("size", m_directoryContentsCounter->countDirectoryContentsSynchronously(path));
     } else {
         // Probably the sort role is a Nepomuk role - just determine all roles.
         data = rolesData(item);
@@ -1153,7 +1071,7 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(const KFileItem& item, Resol
     return false;
 }
 
-QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem& item) const
+QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem& item)
 {
     QHash<QByteArray, QVariant> data;
 
@@ -1162,19 +1080,10 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
 
     if ((getSizeRole || getIsExpandableRole) && item.isDir()) {
         if (item.isLocalFile()) {
+            // Tell m_directoryContentsCounter that we want to count the items
+            // inside the directory. The result will be received in slotDirectoryContentsCountReceived.
             const QString path = item.localPath();
-            const int count = subItemsCount(path);
-            if (getSizeRole) {
-                data.insert("size", count);
-            }
-            if (getIsExpandableRole) {
-                data.insert("isExpandable", count > 0);
-            }
-
-            if (!m_dirWatcher->contains(path)) {
-                m_dirWatcher->addDir(path);
-                m_watchedDirs.insert(path);
-            }
+            m_directoryContentsCounter->addDirectory(path);
         } else if (getSizeRole) {
             data.insert("size", -1); // -1 indicates an unknown number of items
         }
@@ -1218,61 +1127,6 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
     return data;
 }
 
-int KFileItemModelRolesUpdater::subItemsCount(const QString& path) const
-{
-    const bool countHiddenFiles = m_model->showHiddenFiles();
-    const bool showFoldersOnly  = m_model->showDirectoriesOnly();
-
-#ifdef Q_WS_WIN
-    QDir dir(path);
-    QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System;
-    if (countHiddenFiles) {
-        filters |= QDir::Hidden;
-    }
-    if (showFoldersOnly) {
-        filters |= QDir::Dirs;
-    } else {
-        filters |= QDir::AllEntries;
-    }
-    return dir.entryList(filters).count();
-#else
-    // Taken from kdelibs/kio/kio/kdirmodel.cpp
-    // Copyright (C) 2006 David Faure <faure@kde.org>
-
-    int count = -1;
-    DIR* dir = ::opendir(QFile::encodeName(path));
-    if (dir) {  // krazy:exclude=syscalls
-        count = 0;
-        struct dirent *dirEntry = 0;
-        while ((dirEntry = ::readdir(dir))) {
-            if (dirEntry->d_name[0] == '.') {
-                if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) {
-                    // Skip "." or hidden files
-                    continue;
-                }
-                if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') {
-                    // Skip ".."
-                    continue;
-                }
-            }
-
-            // If only directories are counted, consider an unknown file type and links also
-            // as directory instead of trying to do an expensive stat()
-            // (see bugs 292642 and 299997).
-            const bool countEntry = !showFoldersOnly ||
-                                    dirEntry->d_type == DT_DIR ||
-                                    dirEntry->d_type == DT_LNK ||
-                                    dirEntry->d_type == DT_UNKNOWN;
-            if (countEntry) {
-                ++count;
-            }
-        }
-        ::closedir(dir);
-    }
-    return count;
-#endif
-}
-
 void KFileItemModelRolesUpdater::updateAllPreviews()
 {
     if (m_state == Paused) {