]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kfileitemmodel.cpp
Improvements for slow sorting roles
[dolphin.git] / src / kitemviews / kfileitemmodel.cpp
index efc7b326e61e3510f1df929796ceab5e35d35d7e..685af8972901421934178b5d59877a476c187e06 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <KDirLister>
 #include <KDirModel>
+#include "kfileitemmodelsortalgorithm_p.h"
 #include <KGlobalSettings>
 #include <KLocale>
 #include <KStringHandler>
@@ -37,6 +38,7 @@ KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) :
     m_naturalSorting(KGlobalSettings::naturalSorting()),
     m_sortFoldersFirst(true),
     m_sortRole(NameRole),
+    m_sortProgressPercent(-1),
     m_roles(),
     m_caseSensitivity(Qt::CaseInsensitive),
     m_itemData(),
@@ -274,19 +276,8 @@ QList<QPair<int, QVariant> > KFileItemModel::groups() const
         case SizeRole:        m_groups = sizeRoleGroups(); break;
         case DateRole:        m_groups = dateRoleGroups(); break;
         case PermissionsRole: m_groups = permissionRoleGroups(); break;
-        case OwnerRole:       m_groups = genericStringRoleGroups("owner"); break;
-        case GroupRole:       m_groups = genericStringRoleGroups("group"); break;
-        case TypeRole:        m_groups = genericStringRoleGroups("type"); break;
-        case DestinationRole: m_groups = genericStringRoleGroups("destination"); break;
-        case PathRole:        m_groups = genericStringRoleGroups("path"); break;
-        case CommentRole:     m_groups = genericStringRoleGroups("comment"); break;
-        case TagsRole:        m_groups = genericStringRoleGroups("tags"); break;
         case RatingRole:      m_groups = ratingRoleGroups(); break;
-        case NoRole:          break;
-        case IsDirRole:       break;
-        case IsExpandedRole:  break;
-        case ExpandedParentsCountRole: break;
-        default:                 Q_ASSERT(false); break;
+        default:              m_groups = genericStringRoleGroups(sortRole()); break;
         }
 
 #ifdef KFILEITEMMODEL_DEBUG
@@ -558,6 +549,8 @@ QList<KFileItemModel::RoleInfo> KFileItemModel::rolesInformation()
                 info.role = map[i].role;
                 info.translation = map[i].roleTranslation;
                 info.group = map[i].groupTranslation;
+                info.requiresNepomuk = map[i].requiresNepomuk;
+                info.requiresIndexer = map[i].requiresIndexer;
                 rolesInfo.append(info);
             }
         }
@@ -622,7 +615,7 @@ void KFileItemModel::resortAllItems()
     m_items.clear();
 
     // Resort the items
-    sort(m_itemData.begin(), m_itemData.end());
+    KFileItemModelSortAlgorithm::sort(this, m_itemData.begin(), m_itemData.end());
     for (int i = 0; i < itemCount; ++i) {
         m_items.insert(m_itemData.at(i)->item.url(), i);
     }
@@ -886,6 +879,13 @@ void KFileItemModel::insertItems(const KFileItemList& items)
         return;
     }
 
+    if (m_sortRole == TypeRole) {
+        // Try to resolve the MIME-types synchronously to prevent a reordering of
+        // the items when sorting by type (per default MIME-types are resolved
+        // asynchronously by KFileItemModelRolesUpdater).
+        determineMimeTypes(items, 200);
+    }
+
 #ifdef KFILEITEMMODEL_DEBUG
     QElapsedTimer timer;
     timer.start();
@@ -896,7 +896,7 @@ void KFileItemModel::insertItems(const KFileItemList& items)
     m_groups.clear();
 
     QList<ItemData*> sortedItems = createItemDataList(items);
-    sort(sortedItems.begin(), sortedItems.end());
+    KFileItemModelSortAlgorithm::sort(this, sortedItems.begin(), sortedItems.end());
 
 #ifdef KFILEITEMMODEL_DEBUG
     kDebug() << "[TIME] Sorting:" << timer.elapsed();
@@ -975,7 +975,7 @@ void KFileItemModel::removeItems(const KFileItemList& items)
             sortedItems.append(m_itemData.at(index));
         }
     }
-    sort(sortedItems.begin(), sortedItems.end());
+    KFileItemModelSortAlgorithm::sort(this, sortedItems.begin(), sortedItems.end());
 
     QList<int> indexesToRemove;
     indexesToRemove.reserve(items.count());
@@ -1195,7 +1195,7 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item)
     if (m_requestRole[DestinationRole]) {
         QString destination = item.linkDest();
         if (destination.isEmpty()) {
-            destination = i18nc("@item:intable", "No destination");
+            destination = QLatin1String("-");
         }
         data.insert("destination", destination);
     }
@@ -1344,6 +1344,15 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
         break;
     }
 
+    case ImageSizeRole: {
+        // Alway use a natural comparing to interpret the numbers of a string like
+        // "1600 x 1200" for having a correct sorting.
+        result = KStringHandler::naturalCompare(a->values.value("imageSize").toString(),
+                                                b->values.value("imageSize").toString(),
+                                                Qt::CaseSensitive);
+        break;
+    }
+
     case PermissionsRole:
     case OwnerRole:
     case GroupRole:
@@ -1386,128 +1395,6 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
     return QString::compare(itemA.url().url(), itemB.url().url(), Qt::CaseSensitive);
 }
 
-void KFileItemModel::sort(QList<ItemData*>::iterator begin,
-                          QList<ItemData*>::iterator end)
-{
-    // The implementation is based on qStableSortHelper() from qalgorithms.h
-    // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-    // In opposite to qStableSort() it allows to use a member-function for the comparison of elements.
-
-    const int span = end - begin;
-    if (span < 2) {
-        return;
-    }
-
-    const QList<ItemData*>::iterator middle = begin + span / 2;
-    sort(begin, middle);
-    sort(middle, end);
-    merge(begin, middle, end);
-}
-
-void KFileItemModel::merge(QList<ItemData*>::iterator begin,
-                           QList<ItemData*>::iterator pivot,
-                           QList<ItemData*>::iterator end)
-{
-    // The implementation is based on qMerge() from qalgorithms.h
-    // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-
-    const int len1 = pivot - begin;
-    const int len2 = end - pivot;
-
-    if (len1 == 0 || len2 == 0) {
-        return;
-    }
-
-    if (len1 + len2 == 2) {
-        if (lessThan(*(begin + 1), *(begin))) {
-            qSwap(*begin, *(begin + 1));
-        }
-        return;
-    }
-
-    QList<ItemData*>::iterator firstCut;
-    QList<ItemData*>::iterator secondCut;
-    int len2Half;
-    if (len1 > len2) {
-        const int len1Half = len1 / 2;
-        firstCut = begin + len1Half;
-        secondCut = lowerBound(pivot, end, *firstCut);
-        len2Half = secondCut - pivot;
-    } else {
-        len2Half = len2 / 2;
-        secondCut = pivot + len2Half;
-        firstCut = upperBound(begin, pivot, *secondCut);
-    }
-
-    reverse(firstCut, pivot);
-    reverse(pivot, secondCut);
-    reverse(firstCut, secondCut);
-
-    const QList<ItemData*>::iterator newPivot = firstCut + len2Half;
-    merge(begin, firstCut, newPivot);
-    merge(newPivot, secondCut, end);
-}
-
-QList<KFileItemModel::ItemData*>::iterator KFileItemModel::lowerBound(QList<ItemData*>::iterator begin,
-                                                                      QList<ItemData*>::iterator end,
-                                                                      const ItemData* value)
-{
-    // The implementation is based on qLowerBound() from qalgorithms.h
-    // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-
-    QList<ItemData*>::iterator middle;
-    int n = int(end - begin);
-    int half;
-
-    while (n > 0) {
-        half = n >> 1;
-        middle = begin + half;
-        if (lessThan(*middle, value)) {
-            begin = middle + 1;
-            n -= half + 1;
-        } else {
-            n = half;
-        }
-    }
-    return begin;
-}
-
-QList<KFileItemModel::ItemData*>::iterator KFileItemModel::upperBound(QList<ItemData*>::iterator begin,
-                                                                      QList<ItemData*>::iterator end,
-                                                                      const ItemData* value)
-{
-    // The implementation is based on qUpperBound() from qalgorithms.h
-    // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-
-    QList<ItemData*>::iterator middle;
-    int n = end - begin;
-    int half;
-
-    while (n > 0) {
-        half = n >> 1;
-        middle = begin + half;
-        if (lessThan(value, *middle)) {
-            n = half;
-        } else {
-            begin = middle + 1;
-            n -= half + 1;
-        }
-    }
-    return begin;
-}
-
-void KFileItemModel::reverse(QList<ItemData*>::iterator begin,
-                             QList<ItemData*>::iterator end)
-{
-    // The implementation is based on qReverse() from qalgorithms.h
-    // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-
-    --end;
-    while (begin < end) {
-        qSwap(*begin++, *end--);
-    }
-}
-
 int KFileItemModel::stringCompare(const QString& a, const QString& b) const
 {
     // Taken from KDirSortFilterProxyModel (kdelibs/kfile/kdirsortfilterproxymodel.*)
@@ -1950,36 +1837,78 @@ KFileItemList KFileItemModel::childItems(const KFileItem& item) const
     return items;
 }
 
+void KFileItemModel::emitSortProgress(int resolvedCount)
+{
+    // Be tolerant against a resolvedCount with a wrong range.
+    // Although there should not be a case where KFileItemModelRolesUpdater
+    // (= caller) provides a wrong range, it is important to emit
+    // a useful progress information even if there is an unexpected
+    // implementation issue.
+
+    const int itemCount = count();
+    if (resolvedCount >= itemCount) {
+        m_sortProgressPercent = -1;
+        if (m_resortAllItemsTimer->isActive()) {
+            m_resortAllItemsTimer->stop();
+            resortAllItems();
+        }
+
+        emit sortProgress(100);
+    } else if (itemCount > 0) {
+        resolvedCount = qBound(0, resolvedCount, itemCount);
+
+        const int progress = resolvedCount * 100 / itemCount;
+        if (m_sortProgressPercent != progress) {
+            m_sortProgressPercent = progress;
+            emit sortProgress(progress);
+        }
+    }
+}
+
 const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count)
 {
     static const RoleInfoMap rolesInfoMap[] = {
-    //    role           roleType         role translation                                  group translation
-        { 0,             NoRole,          0, 0,                                             0, 0 },
-        { "name",        NameRole,        I18N_NOOP2_NOSTRIP("@label", "Name"),             0, 0 },
-        { "size",        SizeRole,        I18N_NOOP2_NOSTRIP("@label", "Size"),             0, 0 },
-        { "date",        DateRole,        I18N_NOOP2_NOSTRIP("@label", "Date"),             0, 0 },
-        { "type",        TypeRole,        I18N_NOOP2_NOSTRIP("@label", "Type"),             0, 0 },
-        { "rating",      RatingRole,      I18N_NOOP2_NOSTRIP("@label", "Rating"),           0, 0 },
-        { "tags",        TagsRole,        I18N_NOOP2_NOSTRIP("@label", "Tags"),             0, 0 },
-        { "comment",     CommentRole,     I18N_NOOP2_NOSTRIP("@label", "Comment"),          0, 0 },
-        { "wordCount",   WordCountRole,   I18N_NOOP2_NOSTRIP("@label", "Word Count"),       I18N_NOOP2_NOSTRIP("@label", "Document") },
-        { "lineCount",   LineCountRole,   I18N_NOOP2_NOSTRIP("@label", "Line Count"),       I18N_NOOP2_NOSTRIP("@label", "Document") },
-        { "imageSize",   ImageSizeRole,   I18N_NOOP2_NOSTRIP("@label", "Image Size"),       I18N_NOOP2_NOSTRIP("@label", "Image") },
-        { "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"),      I18N_NOOP2_NOSTRIP("@label", "Image") },
-        { "artist",      ArtistRole,      I18N_NOOP2_NOSTRIP("@label", "Artist"),           I18N_NOOP2_NOSTRIP("@label", "Music") },
-        { "album",       AlbumRole,       I18N_NOOP2_NOSTRIP("@label", "Album"),            I18N_NOOP2_NOSTRIP("@label", "Music") },
-        { "duration",    DurationRole,    I18N_NOOP2_NOSTRIP("@label", "Duration"),         I18N_NOOP2_NOSTRIP("@label", "Music") },
-        { "track",       TrackRole,       I18N_NOOP2_NOSTRIP("@label", "Track"),            I18N_NOOP2_NOSTRIP("@label", "Music") },
-        { "path",        PathRole,        I18N_NOOP2_NOSTRIP("@label", "Path"),             I18N_NOOP2_NOSTRIP("@label", "Other") },
-        { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other") },
-        { "copiedFrom",  CopiedFromRole,  I18N_NOOP2_NOSTRIP("@label", "Copied From"),      I18N_NOOP2_NOSTRIP("@label", "Other") },
-        { "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions"),      I18N_NOOP2_NOSTRIP("@label", "Other") },
-        { "owner",       OwnerRole,       I18N_NOOP2_NOSTRIP("@label", "Owner"),            I18N_NOOP2_NOSTRIP("@label", "Other") },
-        { "group",       GroupRole,       I18N_NOOP2_NOSTRIP("@label", "Group"),            I18N_NOOP2_NOSTRIP("@label", "Other") },
+    //  | role         | roleType       | role translation                                | group translation           | requires Nepomuk | requires indexer
+        { 0,             NoRole,          0, 0,                                             0, 0,                                     false, false },
+        { "name",        NameRole,        I18N_NOOP2_NOSTRIP("@label", "Name"),             0, 0,                                     false, false },
+        { "size",        SizeRole,        I18N_NOOP2_NOSTRIP("@label", "Size"),             0, 0,                                     false, false },
+        { "date",        DateRole,        I18N_NOOP2_NOSTRIP("@label", "Date"),             0, 0,                                     false, false },
+        { "type",        TypeRole,        I18N_NOOP2_NOSTRIP("@label", "Type"),             0, 0,                                     false, false },
+        { "rating",      RatingRole,      I18N_NOOP2_NOSTRIP("@label", "Rating"),           0, 0,                                     true,  false },
+        { "tags",        TagsRole,        I18N_NOOP2_NOSTRIP("@label", "Tags"),             0, 0,                                     true,  false },
+        { "comment",     CommentRole,     I18N_NOOP2_NOSTRIP("@label", "Comment"),          0, 0,                                     true,  false },
+        { "wordCount",   WordCountRole,   I18N_NOOP2_NOSTRIP("@label", "Word Count"),       I18N_NOOP2_NOSTRIP("@label", "Document"), true,  true  },
+        { "lineCount",   LineCountRole,   I18N_NOOP2_NOSTRIP("@label", "Line Count"),       I18N_NOOP2_NOSTRIP("@label", "Document"), true,  true  },
+        { "imageSize",   ImageSizeRole,   I18N_NOOP2_NOSTRIP("@label", "Image Size"),       I18N_NOOP2_NOSTRIP("@label", "Image"),    true,  true  },
+        { "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"),      I18N_NOOP2_NOSTRIP("@label", "Image"),    true,  true  },
+        { "artist",      ArtistRole,      I18N_NOOP2_NOSTRIP("@label", "Artist"),           I18N_NOOP2_NOSTRIP("@label", "Music"),    true,  true  },
+        { "album",       AlbumRole,       I18N_NOOP2_NOSTRIP("@label", "Album"),            I18N_NOOP2_NOSTRIP("@label", "Music"),    true,  true  },
+        { "duration",    DurationRole,    I18N_NOOP2_NOSTRIP("@label", "Duration"),         I18N_NOOP2_NOSTRIP("@label", "Music"),    true,  true  },
+        { "track",       TrackRole,       I18N_NOOP2_NOSTRIP("@label", "Track"),            I18N_NOOP2_NOSTRIP("@label", "Music"),    true,  true  },
+        { "path",        PathRole,        I18N_NOOP2_NOSTRIP("@label", "Path"),             I18N_NOOP2_NOSTRIP("@label", "Other"),    false, false },
+        { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other"),    false, false },
+        { "copiedFrom",  CopiedFromRole,  I18N_NOOP2_NOSTRIP("@label", "Copied From"),      I18N_NOOP2_NOSTRIP("@label", "Other"),    true,  false },
+        { "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions"),      I18N_NOOP2_NOSTRIP("@label", "Other"),    false, false },
+        { "owner",       OwnerRole,       I18N_NOOP2_NOSTRIP("@label", "Owner"),            I18N_NOOP2_NOSTRIP("@label", "Other"),    false, false },
+        { "group",       GroupRole,       I18N_NOOP2_NOSTRIP("@label", "User Group"),       I18N_NOOP2_NOSTRIP("@label", "Other"),    false, false },
     };
 
     count = sizeof(rolesInfoMap) / sizeof(RoleInfoMap);
     return rolesInfoMap;
 }
 
+void KFileItemModel::determineMimeTypes(const KFileItemList& items, int timeout)
+{
+    QElapsedTimer timer;
+    timer.start();
+    foreach (KFileItem item, items) {
+        item.determineMimeType();
+        if (timer.elapsed() > timeout) {
+            // Don't block the user interface, let the remaining items
+            // be resolved asynchronously.
+            return;
+        }
+    }
+}
+
 #include "kfileitemmodel.moc"