]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kfileitemmodel.cpp
Merge branch 'frameworks'
[dolphin.git] / src / kitemviews / kfileitemmodel.cpp
index 711b0797be1d416fc7b94694963516fdb542e8aa..eac3ddf8b5c39d4769ab3dac500f2d00a05ca33f 100644 (file)
 
 #include "kfileitemmodel.h"
 
-#include <KGlobalSettings>
+#include "dolphin_generalsettings.h"
+
 #include <KLocalizedString>
-#include <KStringHandler>
-#include <KDebug>
-#include <kstringhandler_deprecated.h> //TODO: port to QCollator
+#include "dolphindebug.h"
 
 #include "private/kfileitemmodelsortalgorithm.h"
 #include "private/kfileitemmodeldirlister.h"
 KFileItemModel::KFileItemModel(QObject* parent) :
     KItemModelBase("text", parent),
     m_dirLister(0),
-    m_naturalSorting(KGlobalSettings::naturalSorting()),
+    m_naturalSorting(GeneralSettings::naturalSorting()),
     m_sortDirsFirst(true),
     m_sortRole(NameRole),
     m_sortingProgressPercent(-1),
     m_roles(),
-    m_caseSensitivity(Qt::CaseInsensitive),
     m_itemData(),
     m_items(),
     m_filter(),
@@ -60,6 +58,9 @@ KFileItemModel::KFileItemModel(QObject* parent) :
     m_expandedDirs(),
     m_urlsToExpand()
 {
+    m_collator.setCaseSensitivity(Qt::CaseInsensitive);
+    m_collator.setNumericMode(true);
+
     m_dirLister = new KFileItemModelDirLister(this);
     m_dirLister->setDelayedMimeTypes(true);
 
@@ -105,7 +106,7 @@ KFileItemModel::KFileItemModel(QObject* parent) :
     m_resortAllItemsTimer->setSingleShot(true);
     connect(m_resortAllItemsTimer, &QTimer::timeout, this, &KFileItemModel::resortAllItems);
 
-    connect(KGlobalSettings::self(), &KGlobalSettings::naturalSortingChanged,
+    connect(GeneralSettings::self(), &GeneralSettings::naturalSortingChanged,
             this, &KFileItemModel::slotNaturalSortingChanged);
 }
 
@@ -338,7 +339,7 @@ QList<QPair<int, QVariant> > KFileItemModel::groups() const
         }
 
 #ifdef KFILEITEMMODEL_DEBUG
-        kDebug() << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed();
+        qCDebug(DolphinDebug) << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed();
 #endif
     }
 
@@ -407,9 +408,9 @@ int KFileItemModel::index(const QUrl& url) const
         if (m_items.count() != m_itemData.count() && printDebugInfo) {
             printDebugInfo = false;
 
-            kWarning() << "The model is in an inconsistent state.";
-            kWarning() << "m_items.count()    ==" << m_items.count();
-            kWarning() << "m_itemData.count() ==" << m_itemData.count();
+            qCWarning(DolphinDebug) << "The model is in an inconsistent state.";
+            qCWarning(DolphinDebug) << "m_items.count()    ==" << m_items.count();
+            qCWarning(DolphinDebug) << "m_itemData.count() ==" << m_itemData.count();
 
             // Check if there are multiple items with the same URL.
             QMultiHash<QUrl, int> indexesForUrl;
@@ -419,12 +420,12 @@ int KFileItemModel::index(const QUrl& url) const
 
             foreach (const QUrl& url, indexesForUrl.uniqueKeys()) {
                 if (indexesForUrl.count(url) > 1) {
-                    kWarning() << "Multiple items found with the URL" << url;
+                    qCWarning(DolphinDebug) << "Multiple items found with the URL" << url;
                     foreach (int index, indexesForUrl.values(url)) {
                         const ItemData* data = m_itemData.at(index);
-                        kWarning() << "index" << index << ":" << data->item;
+                        qCWarning(DolphinDebug) << "index" << index << ":" << data->item;
                         if (data->parent) {
-                            kWarning() << "parent" << data->parent->item;
+                            qCWarning(DolphinDebug) << "parent" << data->parent->item;
                         }
                     }
                 }
@@ -794,8 +795,8 @@ void KFileItemModel::resortAllItems()
 #ifdef KFILEITEMMODEL_DEBUG
     QElapsedTimer timer;
     timer.start();
-    kDebug() << "===========================================================";
-    kDebug() << "Resorting" << itemCount << "items";
+    qCDebug(DolphinDebug) << "===========================================================";
+    qCDebug(DolphinDebug) << "Resorting" << itemCount << "items";
 #endif
 
     // Remember the order of the current URLs so
@@ -857,7 +858,7 @@ void KFileItemModel::resortAllItems()
     }
 
 #ifdef KFILEITEMMODEL_DEBUG
-    kDebug() << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed();
+    qCDebug(DolphinDebug) << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed();
 #endif
 }
 
@@ -1012,7 +1013,7 @@ void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&
 {
     Q_ASSERT(!items.isEmpty());
 #ifdef KFILEITEMMODEL_DEBUG
-    kDebug() << "Refreshing" << items.count() << "items";
+    qCDebug(DolphinDebug) << "Refreshing" << items.count() << "items";
 #endif
 
     // Get the indexes of all items that have been refreshed
@@ -1078,7 +1079,7 @@ void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&
 void KFileItemModel::slotClear()
 {
 #ifdef KFILEITEMMODEL_DEBUG
-    kDebug() << "Clearing all items";
+    qCDebug(DolphinDebug) << "Clearing all items";
 #endif
 
     qDeleteAll(m_filteredItems.values());
@@ -1104,7 +1105,7 @@ void KFileItemModel::slotClear()
 
 void KFileItemModel::slotNaturalSortingChanged()
 {
-    m_naturalSorting = KGlobalSettings::naturalSorting();
+    m_naturalSorting = GeneralSettings::naturalSorting();
     resortAllItems();
 }
 
@@ -1125,8 +1126,8 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
 #ifdef KFILEITEMMODEL_DEBUG
     QElapsedTimer timer;
     timer.start();
-    kDebug() << "===========================================================";
-    kDebug() << "Inserting" << newItems.count() << "items";
+    qCDebug(DolphinDebug) << "===========================================================";
+    qCDebug(DolphinDebug) << "Inserting" << newItems.count() << "items";
 #endif
 
     m_groups.clear();
@@ -1143,7 +1144,7 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
     sort(newItems.begin(), newItems.end());
 
 #ifdef KFILEITEMMODEL_DEBUG
-    kDebug() << "[TIME] Sorting:" << timer.elapsed();
+    qCDebug(DolphinDebug) << "[TIME] Sorting:" << timer.elapsed();
 #endif
 
     KItemRangeList itemRanges;
@@ -1172,7 +1173,7 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
 
         while (sourceIndexNewItems >= 0) {
             ItemData* newItem = newItems.at(sourceIndexNewItems);
-            if (sourceIndexExistingItems >= 0 && lessThan(newItem, m_itemData.at(sourceIndexExistingItems))) {
+            if (sourceIndexExistingItems >= 0 && lessThan(newItem, m_itemData.at(sourceIndexExistingItems), m_collator)) {
                 // Move an existing item to its new position. If any new items
                 // are behind it, push the item range to itemRanges.
                 if (rangeCount > 0) {
@@ -1207,7 +1208,7 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
     emit itemsInserted(itemRanges);
 
 #ifdef KFILEITEMMODEL_DEBUG
-    kDebug() << "[TIME] Inserting of" << newItems.count() << "items:" << timer.elapsed();
+    qCDebug(DolphinDebug) << "[TIME] Inserting of" << newItems.count() << "items:" << timer.elapsed();
 #endif
 }
 
@@ -1388,14 +1389,14 @@ void KFileItemModel::emitItemsChangedAndTriggerResorting(const KItemRangeList& i
             // (b)  the successor of the last item is "lessThan" the last item, or
             // (c)  the internal order of the items in the range is incorrect.
             if (first > 0
-                && lessThan(m_itemData.at(first), m_itemData.at(first - 1))) {
+                && lessThan(m_itemData.at(first), m_itemData.at(first - 1), m_collator)) {
                 needsResorting = true;
             } else if (last < count() - 1
-                && lessThan(m_itemData.at(last + 1), m_itemData.at(last))) {
+                && lessThan(m_itemData.at(last + 1), m_itemData.at(last), m_collator)) {
                 needsResorting = true;
             } else {
                 for (int index = first; index < last; ++index) {
-                    if (lessThan(m_itemData.at(index + 1), m_itemData.at(index))) {
+                    if (lessThan(m_itemData.at(index + 1), m_itemData.at(index), m_collator)) {
                         needsResorting = true;
                         break;
                     }
@@ -1582,7 +1583,7 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
     return data;
 }
 
-bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const
+bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b, const QCollator& collator) const
 {
     int result = 0;
 
@@ -1627,7 +1628,7 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const
         }
     }
 
-    result = sortRoleCompare(a, b);
+    result = sortRoleCompare(a, b, collator);
 
     return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0;
 }
@@ -1638,24 +1639,36 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const
 class KFileItemModelLessThan
 {
 public:
-    KFileItemModelLessThan(const KFileItemModel* model) :
-        m_model(model)
+    KFileItemModelLessThan(const KFileItemModel* model, const QCollator& collator) :
+        m_model(model),
+        m_collator(collator)
+    {
+    }
+
+    KFileItemModelLessThan(const KFileItemModelLessThan& other) :
+        m_model(other.m_model),
+        m_collator()
     {
+        m_collator.setCaseSensitivity(other.m_collator.caseSensitivity());
+        m_collator.setIgnorePunctuation(other.m_collator.ignorePunctuation());
+        m_collator.setLocale(other.m_collator.locale());
+        m_collator.setNumericMode(other.m_collator.numericMode());
     }
 
     bool operator()(const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b) const
     {
-        return m_model->lessThan(a, b);
+        return m_model->lessThan(a, b, m_collator);
     }
 
 private:
     const KFileItemModel* m_model;
+    QCollator m_collator;
 };
 
 void KFileItemModel::sort(QList<KFileItemModel::ItemData*>::iterator begin,
                           QList<KFileItemModel::ItemData*>::iterator end) const
 {
-    KFileItemModelLessThan lessThan(this);
+    KFileItemModelLessThan lessThan(this, m_collator);
 
     if (m_sortRole == NameRole) {
         // Sorting by name can be expensive, in particular if natural sorting is
@@ -1670,7 +1683,7 @@ void KFileItemModel::sort(QList<KFileItemModel::ItemData*>::iterator begin,
     }
 }
 
-int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
+int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const QCollator& collator) const
 {
     const KFileItem& itemA = a->item;
     const KFileItem& itemB = b->item;
@@ -1733,9 +1746,8 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
     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);
+        result = collator.compare(a->values.value("imageSize").toString(),
+                                  b->values.value("imageSize").toString());
         break;
     }
 
@@ -1754,14 +1766,13 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
     }
 
     // Fallback #1: Compare the text of the items
-    result = stringCompare(itemA.text(), itemB.text());
+    result = stringCompare(itemA.text(), itemB.text(), collator);
     if (result != 0) {
         return result;
     }
 
     // Fallback #2: KFileItem::text() may not be unique in case UDS_DISPLAY_NAME is used
-    result = stringCompare(itemA.name(m_caseSensitivity == Qt::CaseInsensitive),
-                           itemB.name(m_caseSensitivity == Qt::CaseInsensitive));
+    result = stringCompare(itemA.name(), itemB.name(), collator);
     if (result != 0) {
         return result;
     }
@@ -1772,26 +1783,21 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
     return QString::compare(itemA.url().url(), itemB.url().url(), Qt::CaseSensitive);
 }
 
-int KFileItemModel::stringCompare(const QString& a, const QString& b) const
+int KFileItemModel::stringCompare(const QString& a, const QString& b, const QCollator& collator) const
 {
-    // Taken from KDirSortFilterProxyModel (kdelibs/kfile/kdirsortfilterproxymodel.*)
-    // Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at>
-    // Copyright (C) 2006 by Dominic Battre <dominic@battre.de>
-    // Copyright (C) 2006 by Martin Pool <mbp@canonical.com>
+    if (m_naturalSorting) {
+        return collator.compare(a, b);
+    }
 
-    if (m_caseSensitivity == Qt::CaseInsensitive) {
-        const int result = m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseInsensitive)
-                                            : QString::compare(a, b, Qt::CaseInsensitive);
-        if (result != 0) {
-            // Only return the result, if the strings are not equal. If they are equal by a case insensitive
-            // comparison, still a deterministic sort order is required. A case sensitive
-            // comparison is done as fallback.
-            return result;
-        }
+    const int result = QString::compare(a, b, collator.caseSensitivity());
+    if (result != 0 || collator.caseSensitivity() == Qt::CaseSensitive) {
+        // Only return the result, if the strings are not equal. If they are equal by a case insensitive
+        // comparison, still a deterministic sort order is required. A case sensitive
+        // comparison is done as fallback.
+        return result;
     }
 
-    return m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseSensitive)
-                            : QString::compare(a, b, Qt::CaseSensitive);
+    return QString::compare(a, b, Qt::CaseSensitive);
 }
 
 bool KFileItemModel::useMaximumUpdateInterval() const
@@ -1799,11 +1805,6 @@ bool KFileItemModel::useMaximumUpdateInterval() const
     return !m_dirLister->url().isLocalFile();
 }
 
-static bool localeAwareLessThan(const QChar& c1, const QChar& c2)
-{
-    return QString::localeAwareCompare(c1, c2) < 0;
-}
-
 QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
 {
     Q_ASSERT(!m_itemData.isEmpty());
@@ -1837,6 +1838,10 @@ QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
                     }
                 }
 
+                auto localeAwareLessThan = [this](const QChar& c1, const QChar& c2) -> bool {
+                    return m_collator.compare(c1, c2) < 0;
+                };
+
                 std::vector<QChar>::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan);
                 if (it != lettersAtoZ.end()) {
                     if (localeAwareLessThan(newFirstChar, *it) && it != lettersAtoZ.begin()) {
@@ -1934,7 +1939,7 @@ QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
                 switch (daysDistance) {
                 case 0:  newGroupValue = i18nc("@title:group Date", "Today"); break;
                 case 1:  newGroupValue = i18nc("@title:group Date", "Yesterday"); break;
-                default: newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: %A", "%A"));
+                default: newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: dddd", "dddd"));
                 }
                 break;
             case 1:
@@ -1957,20 +1962,20 @@ QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
             const QDate lastMonthDate = currentDate.addMonths(-1);
             if  (lastMonthDate.year() == modifiedDate.year() && lastMonthDate.month() == modifiedDate.month()) {
                 if (daysDistance == 1) {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Yesterday (%B, %Y)"));
+                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: MMMM is full month name in current locale, and yyyy is full year number", "'Yesterday' (MMMM, yyyy)"));
                 } else if (daysDistance <= 7) {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: %A, %B is full month name in current locale, and %Y is full year number", "%A (%B, %Y)"));
+                    newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: dddd, MMMM is full month name in current locale, and yyyy is full year number", "dddd (MMMM, yyyy)"));
                 } else if (daysDistance <= 7 * 2) {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "One Week Ago (%B, %Y)"));
+                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: MMMM is full month name in current locale, and yyyy is full year number", "'One Week Ago' (MMMM, yyyy)"));
                 } else if (daysDistance <= 7 * 3) {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Two Weeks Ago (%B, %Y)"));
+                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: MMMM is full month name in current locale, and yyyy is full year number", "'Two Weeks Ago' (MMMM, yyyy)"));
                 } else if (daysDistance <= 7 * 4) {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Three Weeks Ago (%B, %Y)"));
+                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: MMMM is full month name in current locale, and yyyy is full year number", "'Three Weeks Ago' (MMMM, yyyy)"));
                 } else {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Earlier on %B, %Y"));
+                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: MMMM is full month name in current locale, and yyyy is full year number", "'Earlier on' MMMM, yyyy"));
                 }
             } else {
-                newGroupValue = modifiedTime.toString(i18nc("@title:group The month and year: %B is full month name in current locale, and %Y is full year number", "%B, %Y"));
+                newGroupValue = modifiedTime.toString(i18nc("@title:group The month and year: MMMM is full month name in current locale, and yyyy is full year number", "MMMM, yyyy"));
             }
         }
 
@@ -2208,19 +2213,19 @@ bool KFileItemModel::isConsistent() const
         // Check if m_items and m_itemData are consistent.
         const KFileItem item = fileItem(i);
         if (item.isNull()) {
-            qWarning() << "Item" << i << "is null";
+            qCWarning(DolphinDebug) << "Item" << i << "is null";
             return false;
         }
 
         const int itemIndex = index(item);
         if (itemIndex != i) {
-            qWarning() << "Item" << i << "has a wrong index:" << itemIndex;
+            qCWarning(DolphinDebug) << "Item" << i << "has a wrong index:" << itemIndex;
             return false;
         }
 
         // Check if the items are sorted correctly.
-        if (i > 0 && !lessThan(m_itemData.at(i - 1), m_itemData.at(i))) {
-            qWarning() << "The order of items" << i - 1 << "and" << i << "is wrong:"
+        if (i > 0 && !lessThan(m_itemData.at(i - 1), m_itemData.at(i), m_collator)) {
+            qCWarning(DolphinDebug) << "The order of items" << i - 1 << "and" << i << "is wrong:"
                 << fileItem(i - 1) << fileItem(i);
             return false;
         }
@@ -2230,13 +2235,13 @@ bool KFileItemModel::isConsistent() const
         const ItemData* parent = data->parent;
         if (parent) {
             if (expandedParentsCount(data) != expandedParentsCount(parent) + 1) {
-                qWarning() << "expandedParentsCount is inconsistent for parent" << parent->item << "and child" << data->item;
+                qCWarning(DolphinDebug) << "expandedParentsCount is inconsistent for parent" << parent->item << "and child" << data->item;
                 return false;
             }
 
             const int parentIndex = index(parent->item);
             if (parentIndex >= i) {
-                qWarning() << "Index" << parentIndex << "of parent" << parent->item << "is not smaller than index" << i << "of child" << data->item;
+                qCWarning(DolphinDebug) << "Index" << parentIndex << "of parent" << parent->item << "is not smaller than index" << i << "of child" << data->item;
                 return false;
             }
         }