]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kfileitemmodel.cpp
natural sort: exclude extension when comparing filenames
[dolphin.git] / src / kitemviews / kfileitemmodel.cpp
index c694da9f256f041f91d1ed0712032474db7266c8..f8c30b9e45d42ff04404240b346f0f3dd283534a 100644 (file)
@@ -20,6 +20,9 @@
 #include <KLocalizedString>
 #include <KUrlMimeData>
 
+#ifndef QT_NO_ACCESSIBILITY
+#include <QAccessible>
+#endif
 #include <QElapsedTimer>
 #include <QIcon>
 #include <QMimeData>
@@ -202,13 +205,20 @@ bool KFileItemModel::setData(int index, const QHash<QByteArray, QVariant> &value
         return false;
     }
 
-    m_itemData[index]->values = currentValues;
     if (changedRoles.contains("text")) {
         QUrl url = m_itemData[index]->item.url();
+        m_items.remove(url);
         url = url.adjusted(QUrl::RemoveFilename);
         url.setPath(url.path() + currentValues["text"].toString());
         m_itemData[index]->item.setUrl(url);
+        m_items.insert(url, index);
+
+        if (!changedRoles.contains("url")) {
+            changedRoles.insert("url");
+            currentValues["url"] = url;
+        }
     }
+    m_itemData[index]->values = currentValues;
 
     emitItemsChangedAndTriggerResorting(KItemRangeList() << KItemRange(index, 1), changedRoles);
 
@@ -2239,7 +2249,24 @@ int KFileItemModel::stringCompare(const QString &a, const QString &b, const QCol
     QMutexLocker collatorLock(s_collatorMutex());
 
     if (m_naturalSorting) {
-        return collator.compare(a, b);
+        // Split extension, taking into account it can be empty
+        constexpr QString::SectionFlags flags = QString::SectionSkipEmpty | QString::SectionIncludeLeadingSep;
+
+        // Sort by baseName first
+        const QString aBaseName = a.section('.', 0, 0, flags);
+        const QString bBaseName = b.section('.', 0, 0, flags);
+
+        const int res = collator.compare(aBaseName, bBaseName);
+        if (res != 0 || (aBaseName.length() == a.length() && bBaseName.length() == b.length())) {
+            return res;
+        }
+
+        // sliced() has undefined behavior when pos < 0 or pos > size().
+        Q_ASSERT(aBaseName.length() <= a.length() && aBaseName.length() >= 0);
+        Q_ASSERT(bBaseName.length() <= b.length() && bBaseName.length() >= 0);
+
+        // baseNames were equal, sort by extension
+        return collator.compare(a.sliced(aBaseName.length()), b.sliced(bBaseName.length()));
     }
 
     const int result = QString::compare(a, b, collator.caseSensitivity());
@@ -2385,6 +2412,7 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
             continue;
         }
 
+        const QLocale locale;
         const QDateTime fileTime = fileTimeCb(m_itemData.at(i));
         const QDate fileDate = fileTime.date();
         if (fileDate == previousFileDate) {
@@ -2407,7 +2435,7 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
                     newGroupValue = i18nc("@title:group Date", "Yesterday");
                     break;
                 default:
-                    newGroupValue = fileTime.toString(i18nc("@title:group Date: The week day name: dddd", "dddd"));
+                    newGroupValue = locale.toString(fileTime, i18nc("@title:group Date: The week day name: dddd", "dddd"));
                     newGroupValue = i18nc(
                         "Can be used to script translation of \"dddd\""
                         "with context @title:group Date",
@@ -2443,7 +2471,7 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
                         "'Yesterday' (MMMM, yyyy)");
                     const QString translatedFormat = format.toString();
                     if (translatedFormat.count(QLatin1Char('\'')) == 2) {
-                        newGroupValue = fileTime.toString(translatedFormat);
+                        newGroupValue = locale.toString(fileTime, translatedFormat);
                         newGroupValue = i18nc(
                             "Can be used to script translation of "
                             "\"'Yesterday' (MMMM, yyyy)\" with context @title:group Date",
@@ -2453,14 +2481,14 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
                         qCWarning(DolphinDebug).nospace()
                             << "A wrong translation was found: " << translatedFormat << ". Please file a bug report at bugs.kde.org";
                         const QString untranslatedFormat = format.toString({QLatin1String("en_US")});
-                        newGroupValue = fileTime.toString(untranslatedFormat);
+                        newGroupValue = locale.toString(fileTime, untranslatedFormat);
                     }
                 } else if (daysDistance <= 7) {
-                    newGroupValue =
-                        fileTime.toString(i18nc("@title:group Date: "
-                                                "The week day name: dddd, MMMM is full month name "
-                                                "in current locale, and yyyy is full year number.",
-                                                "dddd (MMMM, yyyy)"));
+                    newGroupValue = locale.toString(fileTime,
+                                                    i18nc("@title:group Date: "
+                                                          "The week day name: dddd, MMMM is full month name "
+                                                          "in current locale, and yyyy is full year number.",
+                                                          "dddd (MMMM, yyyy)"));
                     newGroupValue = i18nc(
                         "Can be used to script translation of "
                         "\"dddd (MMMM, yyyy)\" with context @title:group Date",
@@ -2475,7 +2503,7 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
                         "'One Week Ago' (MMMM, yyyy)");
                     const QString translatedFormat = format.toString();
                     if (translatedFormat.count(QLatin1Char('\'')) == 2) {
-                        newGroupValue = fileTime.toString(translatedFormat);
+                        newGroupValue = locale.toString(fileTime, translatedFormat);
                         newGroupValue = i18nc(
                             "Can be used to script translation of "
                             "\"'One Week Ago' (MMMM, yyyy)\" with context @title:group Date",
@@ -2485,7 +2513,7 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
                         qCWarning(DolphinDebug).nospace()
                             << "A wrong translation was found: " << translatedFormat << ". Please file a bug report at bugs.kde.org";
                         const QString untranslatedFormat = format.toString({QLatin1String("en_US")});
-                        newGroupValue = fileTime.toString(untranslatedFormat);
+                        newGroupValue = locale.toString(fileTime, untranslatedFormat);
                     }
                 } else if (daysDistance <= 7 * 3) {
                     const KLocalizedString format = ki18nc(
@@ -2496,7 +2524,7 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
                         "'Two Weeks Ago' (MMMM, yyyy)");
                     const QString translatedFormat = format.toString();
                     if (translatedFormat.count(QLatin1Char('\'')) == 2) {
-                        newGroupValue = fileTime.toString(translatedFormat);
+                        newGroupValue = locale.toString(fileTime, translatedFormat);
                         newGroupValue = i18nc(
                             "Can be used to script translation of "
                             "\"'Two Weeks Ago' (MMMM, yyyy)\" with context @title:group Date",
@@ -2506,7 +2534,7 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
                         qCWarning(DolphinDebug).nospace()
                             << "A wrong translation was found: " << translatedFormat << ". Please file a bug report at bugs.kde.org";
                         const QString untranslatedFormat = format.toString({QLatin1String("en_US")});
-                        newGroupValue = fileTime.toString(untranslatedFormat);
+                        newGroupValue = locale.toString(fileTime, untranslatedFormat);
                     }
                 } else if (daysDistance <= 7 * 4) {
                     const KLocalizedString format = ki18nc(
@@ -2517,7 +2545,7 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
                         "'Three Weeks Ago' (MMMM, yyyy)");
                     const QString translatedFormat = format.toString();
                     if (translatedFormat.count(QLatin1Char('\'')) == 2) {
-                        newGroupValue = fileTime.toString(translatedFormat);
+                        newGroupValue = locale.toString(fileTime, translatedFormat);
                         newGroupValue = i18nc(
                             "Can be used to script translation of "
                             "\"'Three Weeks Ago' (MMMM, yyyy)\" with context @title:group Date",
@@ -2527,7 +2555,7 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
                         qCWarning(DolphinDebug).nospace()
                             << "A wrong translation was found: " << translatedFormat << ". Please file a bug report at bugs.kde.org";
                         const QString untranslatedFormat = format.toString({QLatin1String("en_US")});
-                        newGroupValue = fileTime.toString(untranslatedFormat);
+                        newGroupValue = locale.toString(fileTime, untranslatedFormat);
                     }
                 } else {
                     const KLocalizedString format = ki18nc(
@@ -2538,7 +2566,7 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
                         "'Earlier on' MMMM, yyyy");
                     const QString translatedFormat = format.toString();
                     if (translatedFormat.count(QLatin1Char('\'')) == 2) {
-                        newGroupValue = fileTime.toString(translatedFormat);
+                        newGroupValue = locale.toString(fileTime, translatedFormat);
                         newGroupValue = i18nc(
                             "Can be used to script translation of "
                             "\"'Earlier on' MMMM, yyyy\" with context @title:group Date",
@@ -2548,15 +2576,15 @@ QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<Q
                         qCWarning(DolphinDebug).nospace()
                             << "A wrong translation was found: " << translatedFormat << ". Please file a bug report at bugs.kde.org";
                         const QString untranslatedFormat = format.toString({QLatin1String("en_US")});
-                        newGroupValue = fileTime.toString(untranslatedFormat);
+                        newGroupValue = locale.toString(fileTime, untranslatedFormat);
                     }
                 }
             } else {
-                newGroupValue =
-                    fileTime.toString(i18nc("@title:group "
-                                            "The month and year: MMMM is full month name in current locale, "
-                                            "and yyyy is full year number",
-                                            "MMMM, yyyy"));
+                newGroupValue = locale.toString(fileTime,
+                                                i18nc("@title:group "
+                                                      "The month and year: MMMM is full month name in current locale, "
+                                                      "and yyyy is full year number",
+                                                      "MMMM, yyyy"));
                 newGroupValue = i18nc(
                     "Can be used to script translation of "
                     "\"MMMM, yyyy\" with context @title:group Date",
@@ -2755,6 +2783,7 @@ const KFileItemModel::RoleInfoMap *KFileItemModel::rolesInfoMap(int &count)
         { "releaseYear",         ReleaseYearRole,         kli18nc("@label", "Release Year"),         kli18nc("@label", "Audio"),    KLazyLocalizedString(),                    true,            true  },
         { "aspectRatio",         AspectRatioRole,         kli18nc("@label", "Aspect Ratio"),         kli18nc("@label", "Video"),    KLazyLocalizedString(),                    true,            true  },
         { "frameRate",           FrameRateRole,           kli18nc("@label", "Frame Rate"),           kli18nc("@label", "Video"),    KLazyLocalizedString(),                    true,            true  },
+        { "duration",            DurationRole,            kli18nc("@label", "Duration"),             kli18nc("@label", "Video"),    KLazyLocalizedString(),                    true,            true  },
         { "path",                PathRole,                kli18nc("@label", "Path"),                 kli18nc("@label", "Other"),    KLazyLocalizedString(),                    false,           false },
         { "extension",           ExtensionRole,           kli18nc("@label", "File Extension"),       kli18nc("@label", "Other"),    KLazyLocalizedString(),                    false,           false },
         { "deletiontime",        DeletionTimeRole,        kli18nc("@label", "Deletion Time"),        kli18nc("@label", "Other"),    KLazyLocalizedString(),                    false,           false },
@@ -2856,13 +2885,14 @@ bool KFileItemModel::isConsistent() const
 
 void KFileItemModel::slotListerError(KIO::Job *job)
 {
-    if (job->error() == KIO::ERR_IS_FILE) {
+    const int jobError = job->error();
+    if (jobError == KIO::ERR_IS_FILE) {
         if (auto *listJob = qobject_cast<KIO::ListJob *>(job)) {
             Q_EMIT urlIsFileError(listJob->url());
         }
     } else {
         const QString errorString = job->errorString();
-        Q_EMIT errorMessage(!errorString.isEmpty() ? errorString : i18nc("@info:status", "Unknown error."));
+        Q_EMIT errorMessage(!errorString.isEmpty() ? errorString : i18nc("@info:status", "Unknown error."), jobError);
     }
 }