]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kfileitemmodelrolesupdater.cpp
Make sure that all visible items have an icon
[dolphin.git] / src / kitemviews / kfileitemmodelrolesupdater.cpp
index e539b45a08dc06d23dd60c7d4047eb67317fe16c..28024fd6802316f097521e2fe8fe2b902000aa63 100644 (file)
@@ -88,7 +88,6 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO
     m_resolvableRoles(),
     m_enabledPlugins(),
     m_pendingSortRoleItems(),
-    m_pendingSortRoleIndexes(),
     m_pendingIndexes(),
     m_pendingPreviewItems(),
     m_previewJob(),
@@ -261,8 +260,9 @@ void KFileItemModelRolesUpdater::setPaused(bool paused)
             resolveNextSortRole();
         } else {
             m_state = Idle;
-            startUpdating();
         }
+
+        startUpdating();
     }
 }
 
@@ -345,16 +345,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 +416,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 +445,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 +485,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 +511,6 @@ void KFileItemModelRolesUpdater::slotSortRoleChanged(const QByteArray& current,
     } else {
         m_state = Idle;
         m_pendingSortRoleItems.clear();
-        m_pendingSortRoleIndexes.clear();
         applySortProgressToModel();
     }
 }
@@ -622,7 +613,7 @@ void KFileItemModelRolesUpdater::slotPreviewJobFinished()
     m_state = Idle;
 
     if (!m_pendingPreviewItems.isEmpty()) {
-        startPreviewJob(m_pendingPreviewItems);
+        startPreviewJob();
     } else {
         if (!m_changedItems.isEmpty()) {
             updateChangedItems();
@@ -636,55 +627,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;
     }
 
@@ -836,9 +792,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 +802,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 +810,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 +841,56 @@ 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);
+    }
+
+    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::startPreviewJob()
 {
     m_state = PreviewJobRunning;
 
-    if (items.isEmpty()) {
+    if (m_pendingPreviewItems.isEmpty()) {
         QTimer::singleShot(0, this, SLOT(slotPreviewJobFinished()));
         return;
     }
@@ -924,43 +905,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 +995,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 +1022,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();