]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kfileitemmodel.cpp
Tell translators to leave the ' alone
[dolphin.git] / src / kitemviews / kfileitemmodel.cpp
index 08407d1efb047cd1c9a1cbc66b2c8023afb2c5d6..a6c5e48ec7b1e44cbaa61b78f4a9d07e47388333 100644 (file)
 #include "private/kfileitemmodeldirlister.h"
 #include "private/kfileitemmodelsortalgorithm.h"
 
+#include <kio_version.h>
 #include <KLocalizedString>
 #include <KUrlMimeData>
 
 #include <QElapsedTimer>
 #include <QMimeData>
+#include <QMimeDatabase>
 #include <QTimer>
 #include <QWidget>
-#include <QMutex>
+#include <QRecursiveMutex>
+#include <QIcon>
 
-Q_GLOBAL_STATIC_WITH_ARGS(QMutex, s_collatorMutex, (QMutex::Recursive))
+Q_GLOBAL_STATIC(QRecursiveMutex, s_collatorMutex)
 
 // #define KFILEITEMMODEL_DEBUG
 
@@ -60,7 +63,6 @@ KFileItemModel::KFileItemModel(QObject* parent) :
 
     connect(m_dirLister, &KFileItemModelDirLister::started, this, &KFileItemModel::directoryLoadingStarted);
     connect(m_dirLister, QOverload<>::of(&KCoreDirLister::canceled), this, &KFileItemModel::slotCanceled);
-    connect(m_dirLister, QOverload<const QUrl&>::of(&KCoreDirLister::completed), this, &KFileItemModel::slotCompleted);
     connect(m_dirLister, &KFileItemModelDirLister::itemsAdded, this, &KFileItemModel::slotItemsAdded);
     connect(m_dirLister, &KFileItemModelDirLister::itemsDeleted, this, &KFileItemModel::slotItemsDeleted);
     connect(m_dirLister, &KFileItemModelDirLister::refreshItems, this, &KFileItemModel::slotRefreshItems);
@@ -71,6 +73,12 @@ KFileItemModel::KFileItemModel(QObject* parent) :
     connect(m_dirLister, QOverload<const QUrl&, const QUrl&>::of(&KCoreDirLister::redirection), this, &KFileItemModel::directoryRedirection);
     connect(m_dirLister, &KFileItemModelDirLister::urlIsFileError, this, &KFileItemModel::urlIsFileError);
 
+#if KIO_VERSION < QT_VERSION_CHECK(5, 79, 0)
+    connect(m_dirLister, QOverload<const QUrl&>::of(&KCoreDirLister::completed), this, &KFileItemModel::slotCompleted);
+#else
+    connect(m_dirLister, &KCoreDirLister::listingDirCompleted, this, &KFileItemModel::slotCompleted);
+#endif
+
     // Apply default roles that should be determined
     resetRoles();
     m_requestRole[NameRole] = true;
@@ -917,9 +925,9 @@ void KFileItemModel::slotCompleted()
         // Note that the parent folder must be expanded before any of its subfolders become visible.
         // Therefore, some URLs in m_restoredExpandedUrls might not be visible yet
         // -> we expand the first visible URL we find in m_restoredExpandedUrls.
-        QMutableSetIterator<QUrl> it(m_urlsToExpand);
-        while (it.hasNext()) {
-            const QUrl url = it.next();
+        // Iterate over a const copy because items are deleted and inserted within the loop
+        const auto urlsToExpand = m_urlsToExpand;
+        for(const QUrl &url : urlsToExpand) {
             const int indexForUrl = index(url);
             if (indexForUrl >= 0) {
                 m_urlsToExpand.remove(url);
@@ -1007,6 +1015,8 @@ void KFileItemModel::slotItemsAdded(const QUrl &directoryUrl, const KFileItemLis
         // emitted during the maximum update interval.
         m_maximumUpdateIntervalTimer->start();
     }
+
+    Q_EMIT fileItemsChanged({KFileItem(directoryUrl)});
 }
 
 void KFileItemModel::slotItemsDeleted(const KFileItemList& items)
@@ -1015,6 +1025,7 @@ void KFileItemModel::slotItemsDeleted(const KFileItemList& items)
 
     QVector<int> indexesToRemove;
     indexesToRemove.reserve(items.count());
+    KFileItemList dirsChanged;
 
     for (const KFileItem& item : items) {
         const int indexForItem = index(item);
@@ -1028,6 +1039,11 @@ void KFileItemModel::slotItemsDeleted(const KFileItemList& items)
                 m_filteredItems.erase(it);
             }
         }
+
+        QUrl parentUrl = item.url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
+        if (dirsChanged.findByUrl(parentUrl).isNull()) {
+            dirsChanged << KFileItem(parentUrl);
+        }
     }
 
     std::sort(indexesToRemove.begin(), indexesToRemove.end());
@@ -1055,6 +1071,8 @@ void KFileItemModel::slotItemsDeleted(const KFileItemList& items)
     const KItemRangeList itemRanges = KItemRangeList::fromSortedContainer(indexesToRemove);
     removeFilteredChildren(itemRanges);
     removeItems(itemRanges, DeleteItemData);
+
+    Q_EMIT fileItemsChanged(dirsChanged);
 }
 
 void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items)
@@ -1069,6 +1087,7 @@ void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&
     indexes.reserve(items.count());
 
     QSet<QByteArray> changedRoles;
+    KFileItemList changedFiles;
 
     QListIterator<QPair<KFileItem, KFileItem> > it(items);
     while (it.hasNext()) {
@@ -1094,6 +1113,7 @@ void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&
 
             m_items.remove(oldItem.url());
             m_items.insert(newItem.url(), indexForItem);
+            changedFiles.append(newItem);
             indexes.append(indexForItem);
         } else {
             // Check if 'oldItem' is one of the filtered items.
@@ -1122,6 +1142,8 @@ void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&
     std::sort(indexes.begin(), indexes.end());
     const KItemRangeList itemRangeList = KItemRangeList::fromSortedContainer(indexes);
     emitItemsChangedAndTriggerResorting(itemRangeList, changedRoles);
+
+    Q_EMIT fileItemsChanged(changedFiles);
 }
 
 void KFileItemModel::slotClear()
@@ -1657,7 +1679,13 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
     }
 
     if (item.isMimeTypeKnown()) {
-        data.insert(sharedValue("iconName"), item.iconName());
+        QString iconName = item.iconName();
+        if (!QIcon::hasThemeIcon(iconName)) {
+            QMimeType mimeType = QMimeDatabase().mimeTypeForName(item.mimetype());
+            iconName = mimeType.genericIconName();
+        }
+
+        data.insert(sharedValue("iconName"), iconName);
 
         if (m_requestRole[TypeRole]) {
             data.insert(sharedValue("type"), item.mimeComment());
@@ -1705,7 +1733,16 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b, const QColla
         }
     }
 
-    if (m_sortDirsFirst || m_sortRole == SizeRole) {
+    // Show hidden files and folders last
+    const bool isHiddenA = a->item.isHidden();
+    const bool isHiddenB = b->item.isHidden();
+    if (isHiddenA && !isHiddenB) {
+        return false;
+    } else if (!isHiddenA && isHiddenB) {
+        return true;
+    }
+
+    if (m_sortDirsFirst || (DetailsModeSettings::directorySizeCount() && m_sortRole == SizeRole)) {
         const bool isDirA = a->item.isDir();
         const bool isDirB = b->item.isDir();
         if (isDirA && !isDirB) {
@@ -1754,25 +1791,19 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const
         break;
 
     case SizeRole: {
-        if (itemA.isDir()) {
-            // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan():
-            Q_ASSERT(itemB.isDir());
-
-            QVariant valueA, valueB;
-            if (DetailsModeSettings::directorySizeCount()) {
-                valueA = a->values.value("count");
-                valueB = b->values.value("count");
-            } else {
-                // use dir size then
-                valueA = a->values.value("size");
-                valueB = b->values.value("size");
-            }
-            if (valueA.isNull() && valueB.isNull()) {
-                result = 0;
-            } else if (valueA.isNull()) {
-                result = -1;
+        if (DetailsModeSettings::directorySizeCount() && itemA.isDir()) {
+            // folders first then
+            // items A and B are folders thanks to lessThan checks
+            auto valueA = a->values.value("count");
+            auto valueB = b->values.value("count");
+            if (valueA.isNull()) {
+                if (valueB.isNull()) {
+                    return 0;
+                } else {
+                    return -1;
+                }
             } else if (valueB.isNull()) {
-                result = +1;
+                return +1;
             } else {
                 if (valueA.toLongLong() < valueB.toLongLong()) {
                     return -1;
@@ -1780,18 +1811,25 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const
                     return +1;
                 }
             }
+        }
+        KIO::filesize_t sizeA = 0;
+        if (itemA.isDir()) {
+            sizeA = a->values.value("size").toULongLong();
         } else {
-            // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan():
-            Q_ASSERT(!itemB.isDir());
-            const KIO::filesize_t sizeA = itemA.size();
-            const KIO::filesize_t sizeB = itemB.size();
-            if (sizeA > sizeB) {
-                result = +1;
-            } else if (sizeA < sizeB) {
-                result = -1;
-            } else {
-                result = 0;
-            }
+            sizeA = itemA.size();
+        }
+        KIO::filesize_t sizeB = 0;
+        if (itemB.isDir()) {
+            sizeB = b->values.value("size").toULongLong();
+        } else {
+            sizeB = itemB.size();
+        }
+        if (sizeA > sizeB) {
+            result = +1;
+        } else if (sizeA < sizeB) {
+            result = -1;
+        } else {
+            result = 0;
         }
         break;
     }
@@ -2075,7 +2113,7 @@ QList<QPair<int, QVariant> > KFileItemModel::timeRoleGroups(const std::function<
                 if (daysDistance == 1) {
                     const KLocalizedString format = ki18nc("@title:group Date: "
                                                     "MMMM is full month name in current locale, and yyyy is "
-                                                    "full year number", "'Yesterday' (MMMM, yyyy)");
+                                                    "full year number. You must keep the ' don't use any fancy \" or « or similar. The ' is not shown to the user, it's there to mark a part of the text that should not be formatted as a date", "'Yesterday' (MMMM, yyyy)");
                     const QString translatedFormat = format.toString();
                     if (translatedFormat.count(QLatin1Char('\'')) == 2) {
                         newGroupValue = fileTime.toString(translatedFormat);
@@ -2090,7 +2128,7 @@ QList<QPair<int, QVariant> > KFileItemModel::timeRoleGroups(const std::function<
                 } 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",
+                        "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",
@@ -2098,7 +2136,7 @@ QList<QPair<int, QVariant> > KFileItemModel::timeRoleGroups(const std::function<
                 } else if (daysDistance <= 7 * 2) {
                     const KLocalizedString format = ki18nc("@title:group Date: "
                                                            "MMMM is full month name in current locale, and yyyy is "
-                                                           "full year number", "'One Week Ago' (MMMM, yyyy)");
+                                                           "full year number. You must keep the ' don't use any fancy \" or « or similar. The ' is not shown to the user, it's there to mark a part of the text that should not be formatted as a date", "'One Week Ago' (MMMM, yyyy)");
                     const QString translatedFormat = format.toString();
                     if (translatedFormat.count(QLatin1Char('\'')) == 2) {
                         newGroupValue = fileTime.toString(translatedFormat);
@@ -2113,7 +2151,7 @@ QList<QPair<int, QVariant> > KFileItemModel::timeRoleGroups(const std::function<
                 } else if (daysDistance <= 7 * 3) {
                     const KLocalizedString format = ki18nc("@title:group Date: "
                                                            "MMMM is full month name in current locale, and yyyy is "
-                                                           "full year number", "'Two Weeks Ago' (MMMM, yyyy)");
+                                                           "full year number. You must keep the ' don't use any fancy \" or « or similar. The ' is not shown to the user, it's there to mark a part of the text that should not be formatted as a date", "'Two Weeks Ago' (MMMM, yyyy)");
                     const QString translatedFormat = format.toString();
                     if (translatedFormat.count(QLatin1Char('\'')) == 2) {
                         newGroupValue = fileTime.toString(translatedFormat);
@@ -2128,7 +2166,7 @@ QList<QPair<int, QVariant> > KFileItemModel::timeRoleGroups(const std::function<
                 } else if (daysDistance <= 7 * 4) {
                     const KLocalizedString format = ki18nc("@title:group Date: "
                                                            "MMMM is full month name in current locale, and yyyy is "
-                                                           "full year number", "'Three Weeks Ago' (MMMM, yyyy)");
+                                                           "full year number. You must keep the ' don't use any fancy \" or « or similar. The ' is not shown to the user, it's there to mark a part of the text that should not be formatted as a date", "'Three Weeks Ago' (MMMM, yyyy)");
                     const QString translatedFormat = format.toString();
                     if (translatedFormat.count(QLatin1Char('\'')) == 2) {
                         newGroupValue = fileTime.toString(translatedFormat);
@@ -2143,7 +2181,7 @@ QList<QPair<int, QVariant> > KFileItemModel::timeRoleGroups(const std::function<
                 } else {
                     const KLocalizedString format = ki18nc("@title:group Date: "
                                                            "MMMM is full month name in current locale, and yyyy is "
-                                                           "full year number", "'Earlier on' MMMM, yyyy");
+                                                           "full year number. You must keep the ' don't use any fancy \" or « or similar. The ' is not shown to the user, it's there to mark a part of the text that should not be formatted as a date", "'Earlier on' MMMM, yyyy");
                     const QString translatedFormat = format.toString();
                     if (translatedFormat.count(QLatin1Char('\'')) == 2) {
                         newGroupValue = fileTime.toString(translatedFormat);