]> 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 e539b45a08dc06d23dd60c7d4047eb67317fe16c..e9b69566d5b542ddb5234961b32bbd1652b85b61 100644 (file)
@@ -43,6 +43,7 @@
 #ifdef HAVE_NEPOMUK
     #include "private/knepomukrolesprovider.h"
     #include <Nepomuk2/ResourceWatcher>
+    #include <Nepomuk2/ResourceManager>
 #endif
 
 // Required includes for subItemsCount():
@@ -88,7 +89,6 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO
     m_resolvableRoles(),
     m_enabledPlugins(),
     m_pendingSortRoleItems(),
-    m_pendingSortRoleIndexes(),
     m_pendingIndexes(),
     m_pendingPreviewItems(),
     m_previewJob(),
@@ -261,8 +261,9 @@ void KFileItemModelRolesUpdater::setPaused(bool paused)
             resolveNextSortRole();
         } else {
             m_state = Idle;
-            startUpdating();
         }
+
+        startUpdating();
     }
 }
 
@@ -272,31 +273,33 @@ void KFileItemModelRolesUpdater::setRoles(const QSet<QByteArray>& roles)
         m_roles = roles;
 
 #ifdef HAVE_NEPOMUK
-        // Check whether there is at least one role that must be resolved
-        // with the help of Nepomuk. If this is the case, a (quite expensive)
-        // resolving will be done in KFileItemModelRolesUpdater::rolesData() and
-        // the role gets watched for changes.
-        const KNepomukRolesProvider& rolesProvider = KNepomukRolesProvider::instance();
-        bool hasNepomukRole = false;
-        QSetIterator<QByteArray> it(roles);
-        while (it.hasNext()) {
-            const QByteArray& role = it.next();
-            if (rolesProvider.roles().contains(role)) {
-                hasNepomukRole = true;
-                break;
+        if (Nepomuk2::ResourceManager::instance()->initialized()) {
+            // Check whether there is at least one role that must be resolved
+            // with the help of Nepomuk. If this is the case, a (quite expensive)
+            // resolving will be done in KFileItemModelRolesUpdater::rolesData() and
+            // the role gets watched for changes.
+            const KNepomukRolesProvider& rolesProvider = KNepomukRolesProvider::instance();
+            bool hasNepomukRole = false;
+            QSetIterator<QByteArray> it(roles);
+            while (it.hasNext()) {
+                const QByteArray& role = it.next();
+                if (rolesProvider.roles().contains(role)) {
+                    hasNepomukRole = true;
+                    break;
+                }
             }
-        }
 
-        if (hasNepomukRole && !m_nepomukResourceWatcher) {
-            Q_ASSERT(m_nepomukUriItems.isEmpty());
+            if (hasNepomukRole && !m_nepomukResourceWatcher) {
+                Q_ASSERT(m_nepomukUriItems.isEmpty());
 
-            m_nepomukResourceWatcher = new Nepomuk2::ResourceWatcher(this);
-            connect(m_nepomukResourceWatcher, SIGNAL(propertyChanged(Nepomuk2::Resource,Nepomuk2::Types::Property,QVariantList,QVariantList)),
-                    this, SLOT(applyChangedNepomukRoles(Nepomuk2::Resource)));
-        } else if (!hasNepomukRole && m_nepomukResourceWatcher) {
-            delete m_nepomukResourceWatcher;
-            m_nepomukResourceWatcher = 0;
-            m_nepomukUriItems.clear();
+                m_nepomukResourceWatcher = new Nepomuk2::ResourceWatcher(this);
+                connect(m_nepomukResourceWatcher, SIGNAL(propertyChanged(Nepomuk2::Resource,Nepomuk2::Types::Property,QVariantList,QVariantList)),
+                        this, SLOT(applyChangedNepomukRoles(Nepomuk2::Resource,Nepomuk2::Types::Property)));
+            } else if (!hasNepomukRole && m_nepomukResourceWatcher) {
+                delete m_nepomukResourceWatcher;
+                m_nepomukResourceWatcher = 0;
+                m_nepomukUriItems.clear();
+            }
         }
 #endif
 
@@ -345,16 +348,13 @@ void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList& itemRan
 
         applySortProgressToModel();
 
-        // If there are still items whose sort role is unknown, return
-        // and handle them asynchronously.
-        if (!m_pendingSortRoleItems.isEmpty()) {
-            if (m_state != ResolvingSortRole) {
-                // Trigger the asynchronous determination of the sort role.
-                killPreviewJob();
-                m_state = ResolvingSortRole;
-                resolveNextSortRole();
-            }
-            return;
+        // If there are still items whose sort role is unknown, check if the
+        // asynchronous determination of the sort role is already in progress,
+        // and start it if that is not the case.
+        if (!m_pendingSortRoleItems.isEmpty() && m_state != ResolvingSortRole) {
+            killPreviewJob();
+            m_state = ResolvingSortRole;
+            resolveNextSortRole();
         }
     }
 
@@ -419,7 +419,6 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRang
 
         m_finishedItems.clear();
         m_pendingSortRoleItems.clear();
-        m_pendingSortRoleIndexes.clear();
         m_pendingIndexes.clear();
         m_pendingPreviewItems.clear();
         m_recentlyChangedItems.clear();
@@ -449,9 +448,6 @@ void KFileItemModelRolesUpdater::slotItemsMoved(const KItemRange& itemRange, QLi
     Q_UNUSED(itemRange);
     Q_UNUSED(movedToIndexes);
 
-    // The indexes of the items with missing sort role are not valid any more.
-    m_pendingSortRoleIndexes.clear();
-
     // The visible items might have changed.
     startUpdating();
 }
@@ -492,7 +488,6 @@ void KFileItemModelRolesUpdater::slotSortRoleChanged(const QByteArray& current,
 
     if (m_resolvableRoles.contains(current)) {
         m_pendingSortRoleItems.clear();
-        m_pendingSortRoleIndexes.clear();
         m_finishedItems.clear();
 
         const int count = m_model->count();
@@ -519,7 +514,6 @@ void KFileItemModelRolesUpdater::slotSortRoleChanged(const QByteArray& current,
     } else {
         m_state = Idle;
         m_pendingSortRoleItems.clear();
-        m_pendingSortRoleIndexes.clear();
         applySortProgressToModel();
     }
 }
@@ -576,6 +570,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>)),
@@ -622,7 +632,7 @@ void KFileItemModelRolesUpdater::slotPreviewJobFinished()
     m_state = Idle;
 
     if (!m_pendingPreviewItems.isEmpty()) {
-        startPreviewJob(m_pendingPreviewItems);
+        startPreviewJob();
     } else {
         if (!m_changedItems.isEmpty()) {
             updateChangedItems();
@@ -636,55 +646,20 @@ void KFileItemModelRolesUpdater::resolveNextSortRole()
         return;
     }
 
-    if (m_pendingSortRoleItems.count() != m_pendingSortRoleIndexes.count()) {
-        // The indexes with missing sort role have to be updated.
-        m_pendingSortRoleIndexes.clear();
-        foreach (const KFileItem& item, m_pendingSortRoleItems) {
-            const int index = m_model->index(item);
-            if (index < 0) {
-                m_pendingSortRoleItems.remove(item);
-            } else {
-                m_pendingSortRoleIndexes.append(index);
-            }
-        }
-
-        std::sort(m_pendingSortRoleIndexes.begin(), m_pendingSortRoleIndexes.end());
-    }
-
-    // Try to update an item in the visible range.
-    QList<int>::iterator it = std::lower_bound(m_pendingSortRoleIndexes.begin(),
-                                                m_pendingSortRoleIndexes.end(),
-                                                m_firstVisibleIndex);
-
-    // It seems that there is no such item. Start with the first item in the list.
-    if (it == m_pendingSortRoleIndexes.end()) {
-        it = m_pendingSortRoleIndexes.begin();
-    }
-
-    while (it != m_pendingSortRoleIndexes.end()) {
-        // TODO: Note that removing an index from the list m_pendingSortRoleIndexes
-        // at a random position is O(N). We might need a better solution
-        // to make sure that this does not harm the performance if
-        // many items have to be sorted.
-        const int index = *it;
-        const KFileItem item = m_model->fileItem(index);
+    QSet<KFileItem>::iterator it = m_pendingSortRoleItems.begin();
+    while (it != m_pendingSortRoleItems.end()) {
+        const KFileItem item = *it;
+        const int index = m_model->index(item);
 
         // Continue if the sort role has already been determined for the
         // item, and the item has not been changed recently.
         if (!m_changedItems.contains(item) && m_model->data(index).contains(m_model->sortRole())) {
-            m_pendingSortRoleItems.remove(item);
-            m_pendingSortRoleIndexes.erase(it);
-
-            // Check if we are at the end of the list (note that the list's end has changed).
-            if (it != m_pendingSortRoleIndexes.end()) {
-                ++it;
-            }
+            it = m_pendingSortRoleItems.erase(it);
             continue;
         }
 
         applySortRole(index);
-        m_pendingSortRoleItems.remove(item);
-        m_pendingSortRoleIndexes.erase(it);
+        m_pendingSortRoleItems.erase(it);
         break;
     }
 
@@ -706,7 +681,7 @@ void KFileItemModelRolesUpdater::resolveNextSortRole()
 
 void KFileItemModelRolesUpdater::resolveNextPendingRoles()
 {
-    if (m_state != ResolvingAllRoles && m_state != PreviewJobRunning) {
+    if (m_state != ResolvingAllRoles) {
         return;
     }
 
@@ -718,21 +693,15 @@ void KFileItemModelRolesUpdater::resolveNextPendingRoles()
             continue;
         }
 
-        if (m_previewShown) {
-            // Only determine the icon. The other roles are resolved when the preview is received.
-            applyResolvedRoles(item, ResolveFast);
-        } else {
-            applyResolvedRoles(item, ResolveAll);
-            m_finishedItems.insert(item);
-            m_changedItems.remove(item);
-        }
-
+        applyResolvedRoles(item, ResolveAll);
+        m_finishedItems.insert(item);
+        m_changedItems.remove(item);
         break;
     }
 
     if (!m_pendingIndexes.isEmpty()) {
         QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles()));
-    } else if (m_state != PreviewJobRunning) {
+    } else {
         m_state = Idle;
 
         if (m_clearPreviews) {
@@ -768,9 +737,13 @@ 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()) {
+        return;
+    }
+
     const KUrl itemUrl = m_nepomukUriItems.value(resource.uri());
     const KFileItem item = m_model->fileItem(itemUrl);
 
@@ -783,6 +756,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();
@@ -836,9 +817,7 @@ void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path)
 
 void KFileItemModelRolesUpdater::startUpdating()
 {
-    // Updating the items in and near the visible area makes sense only
-    // if sorting is finished.
-    if (m_state == ResolvingSortRole || m_state == Paused) {
+    if (m_state == Paused) {
         return;
     }
 
@@ -848,16 +827,6 @@ void KFileItemModelRolesUpdater::startUpdating()
         return;
     }
 
-    int lastVisibleIndex = m_lastVisibleIndex;
-    if (lastVisibleIndex <= 0) {
-        // Guess a reasonable value for the last visible index if the view
-        // has not told us about the real value yet.
-        lastVisibleIndex = qMin(m_firstVisibleIndex + m_maximumVisibleItems, m_model->count() - 1);
-        if (lastVisibleIndex <= 0) {
-            lastVisibleIndex = qMin(200, m_model->count() - 1);
-        }
-    }
-
     // Terminate all updates that are currently active.
     killPreviewJob();
     m_pendingIndexes.clear();
@@ -866,37 +835,29 @@ void KFileItemModelRolesUpdater::startUpdating()
     timer.start();
 
     // Determine the icons for the visible items synchronously.
-    int index;
-    for (index = m_firstVisibleIndex; index <= lastVisibleIndex && timer.elapsed() < MaxBlockTimeout; ++index) {
-        const KFileItem item = m_model->fileItem(index);
-        applyResolvedRoles(item, ResolveFast);
+    updateVisibleIcons();
+
+    // A detailed update of the items in and near the visible area
+    // only makes sense if sorting is finished.
+    if (m_state == ResolvingSortRole) {
+        return;
     }
-    const int firstIndexWithoutIcon = index;
 
     // Start the preview job or the asynchronous resolving of all roles.
     QList<int> indexes = indexesToResolve();
 
     if (m_previewShown) {
-        KFileItemList itemsToResolve;
+        m_pendingPreviewItems.clear();
+        m_pendingPreviewItems.reserve(indexes.count());
+
         foreach (int index, indexes) {
             const KFileItem item = m_model->fileItem(index);
             if (!m_finishedItems.contains(item)) {
-                itemsToResolve.append(m_model->fileItem(index));
-
-                // Remember the items which have no icon yet. A fast
-                // asynchronous resolving will be done to make sure
-                // that icons are loaded as quickly as possible, i.e.,
-                // before the previews arrive.
-                if (index < m_firstVisibleIndex || index >= firstIndexWithoutIcon) {
-                    m_pendingIndexes.append(index);
-                }
+                m_pendingPreviewItems.append(item);
             }
         }
 
-        startPreviewJob(itemsToResolve);
-
-        // Determine the icons asynchronously as fast as possible.
-        QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles()));
+        startPreviewJob();
     } else {
         m_pendingIndexes = indexes;
         // Trigger the asynchronous resolving of all roles.
@@ -905,11 +866,38 @@ void KFileItemModelRolesUpdater::startUpdating()
     }
 }
 
-void KFileItemModelRolesUpdater::startPreviewJob(const KFileItemList items)
+void KFileItemModelRolesUpdater::updateVisibleIcons()
+{
+    int lastVisibleIndex = m_lastVisibleIndex;
+    if (lastVisibleIndex <= 0) {
+        // Guess a reasonable value for the last visible index if the view
+        // has not told us about the real value yet.
+        lastVisibleIndex = qMin(m_firstVisibleIndex + m_maximumVisibleItems, m_model->count() - 1);
+        if (lastVisibleIndex <= 0) {
+            lastVisibleIndex = qMin(200, m_model->count() - 1);
+        }
+    }
+
+    QElapsedTimer timer;
+    timer.start();
+
+    // Try to determine the final icons for all visible items.
+    int index;
+    for (index = m_firstVisibleIndex; index <= lastVisibleIndex && timer.elapsed() < MaxBlockTimeout; ++index) {
+        const KFileItem item = m_model->fileItem(index);
+        applyResolvedRoles(item, ResolveFast);
+    }
+
+    // KFileItemListView::initializeItemListWidget(KItemListWidget*) will load
+    // preliminary icons (i.e., without mime type determination) for the
+    // remaining items.
+}
+
+void KFileItemModelRolesUpdater::startPreviewJob()
 {
     m_state = PreviewJobRunning;
 
-    if (items.isEmpty()) {
+    if (m_pendingPreviewItems.isEmpty()) {
         QTimer::singleShot(0, this, SLOT(slotPreviewJobFinished()));
         return;
     }
@@ -924,43 +912,30 @@ void KFileItemModelRolesUpdater::startPreviewJob(const KFileItemList items)
 
     // KIO::filePreview() will request the MIME-type of all passed items, which (in the
     // worst case) might block the application for several seconds. To prevent such
-    // a blocking, we only pass items with known mime type to the preview job
-    // (if the icon has already been determined for an item in startUpdating()
-    // or resolveNextPendingRoles(), the type is known).
-    // This also prevents that repeated expensive mime type determinations are
-    // triggered here if a huge folder is loaded, and startUpdating() is called
-    // repeatedly.
-    //
-    // Note that we always pass at least one item to the preview job to prevent
-    // that we get an endless startPreviewJob()/slotPreviewJobFinished() loop
-    // if there are no items with known mime types yet for some reason.
-    const int count = items.count();
-    int previewJobItemCount = 1;
-
-    // TODO: This will start a job with one item only if this function is
-    // called from slotPreviewJobFinished(), and resolveNextPendingRoles()
-    // has not reached the items yet. This can happen if the previous preview
-    // job has finished very fast because generating previews failed for all
-    // items.
-    //
-    // Idea to improve this: if the mime type of the first item is unknown,
-    // determine mime types synchronously for a while.
-    while (previewJobItemCount < qMin(count, m_maximumVisibleItems) &&
-           items.at(previewJobItemCount).isMimeTypeKnown()) {
-        ++previewJobItemCount;
-    }
-
+    // a blocking, we only pass items with known mime type to the preview job.
+    const int count = m_pendingPreviewItems.count();
     KFileItemList itemSubSet;
-    itemSubSet.reserve(previewJobItemCount);
-    m_pendingPreviewItems.clear();
-    m_pendingPreviewItems.reserve(count - previewJobItemCount);
-
-    for (int i = 0; i < previewJobItemCount; ++i) {
-        itemSubSet.append(items.at(i));
-    }
+    itemSubSet.reserve(count);
+
+    if (m_pendingPreviewItems.first().isMimeTypeKnown()) {
+        // Some mime types are known already, probably because they were
+        // determined when loading the icons for the visible items. Start
+        // a preview job for all items at the beginning of the list which
+        // have a known mime type.
+        do {
+            itemSubSet.append(m_pendingPreviewItems.takeFirst());
+        } while (!m_pendingPreviewItems.isEmpty() && m_pendingPreviewItems.first().isMimeTypeKnown());
+    } else {
+        // Determine mime types for MaxBlockTimeout ms, and start a preview
+        // job for the corresponding items.
+        QElapsedTimer timer;
+        timer.start();
 
-    for (int i = previewJobItemCount; i < count; ++i) {
-        m_pendingPreviewItems.append(items.at(i));
+        do {
+            const KFileItem item = m_pendingPreviewItems.takeFirst();
+            item.determineMimeType();
+            itemSubSet.append(item);
+        } while (!m_pendingPreviewItems.isEmpty() && timer.elapsed() < MaxBlockTimeout);
     }
 
     KIO::PreviewJob* job = new KIO::PreviewJob(itemSubSet, cacheSize, &m_enabledPlugins);
@@ -1027,21 +1002,16 @@ void KFileItemModelRolesUpdater::updateChangedItems()
     std::sort(visibleChangedIndexes.begin(), visibleChangedIndexes.end());
 
     if (m_previewShown) {
-        KFileItemList visibleChangedItems;
-        KFileItemList invisibleChangedItems;
-
         foreach (int index, visibleChangedIndexes) {
-            visibleChangedItems.append(m_model->fileItem(index));
+            m_pendingPreviewItems.append(m_model->fileItem(index));
         }
 
         foreach (int index, invisibleChangedIndexes) {
-            invisibleChangedItems.append(m_model->fileItem(index));
+            m_pendingPreviewItems.append(m_model->fileItem(index));
         }
 
-        if (m_previewJob) {
-            m_pendingPreviewItems += visibleChangedItems + invisibleChangedItems;
-        } else {
-            startPreviewJob(visibleChangedItems + invisibleChangedItems);
+        if (!m_previewJob) {
+            startPreviewJob();
         }
     } else {
         const bool resolvingInProgress = !m_pendingIndexes.isEmpty();
@@ -1059,11 +1029,6 @@ void KFileItemModelRolesUpdater::applySortRole(int index)
     QHash<QByteArray, QVariant> data;
     const KFileItem item = m_model->fileItem(index);
 
-    if (index >= m_firstVisibleIndex && index <= m_lastVisibleIndex) {
-        // Determine the icon.
-        applyResolvedRoles(item, ResolveFast);
-    }
-
     if (m_model->sortRole() == "type") {
         if (!item.isMimeTypeKnown()) {
             item.determineMimeType();
@@ -1105,9 +1070,7 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(const KFileItem& item, Resol
     if (!item.isMimeTypeKnown() || !item.isFinalIconKnown()) {
         item.determineMimeType();
         iconChanged = true;
-    } else if (m_state == ResolvingSortRole || m_state == PreviewJobRunning) {
-        // We are currently performing a fast determination of all icons
-        // in the visible area.
+    } else {
         const int index = m_model->index(item);
         if (!m_model->data(index).contains("iconName")) {
             iconChanged = true;