]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kfileitemmodel.cpp
Add role for file creation time.
[dolphin.git] / src / kitemviews / kfileitemmodel.cpp
index 8626f947d949bbf5b2d54b6753706882231c6c25..b725d950797b4620f61955b373f0b8e2399e6360 100644 (file)
@@ -24,6 +24,8 @@
 #include "dolphin_generalsettings.h"
 
 #include <KLocalizedString>
+#include <KUrlMimeData>
+
 #include "dolphindebug.h"
 
 #include "private/kfileitemmodelsortalgorithm.h"
@@ -78,6 +80,7 @@ KFileItemModel::KFileItemModel(QObject* parent) :
     connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)()>(&KFileItemModelDirLister::clear), this, &KFileItemModel::slotClear);
     connect(m_dirLister, &KFileItemModelDirLister::infoMessage, this, &KFileItemModel::infoMessage);
     connect(m_dirLister, &KFileItemModelDirLister::errorMessage, this, &KFileItemModel::errorMessage);
+    connect(m_dirLister, &KFileItemModelDirLister::percent, this, &KFileItemModel::directoryLoadingProgress);
     connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)(const QUrl&, const QUrl&)>(&KFileItemModelDirLister::redirection), this, &KFileItemModel::directoryRedirection);
     connect(m_dirLister, &KFileItemModelDirLister::urlIsFileError, this, &KFileItemModel::urlIsFileError);
 
@@ -89,6 +92,7 @@ KFileItemModel::KFileItemModel(QObject* parent) :
     m_roles.insert("text");
     m_roles.insert("isDir");
     m_roles.insert("isLink");
+    m_roles.insert("isHidden");
 
     // For slow KIO-slaves like used for searching it makes sense to show results periodically even
     // before the completed() or canceled() signal has been emitted.
@@ -112,7 +116,7 @@ KFileItemModel::KFileItemModel(QObject* parent) :
 KFileItemModel::~KFileItemModel()
 {
     qDeleteAll(m_itemData);
-    qDeleteAll(m_filteredItems.values());
+    qDeleteAll(m_filteredItems);
     qDeleteAll(m_pendingItemsToInsert);
 }
 
@@ -249,7 +253,7 @@ QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const
     bool canUseMostLocalUrls = true;
     const ItemData* lastAddedItem = 0;
 
-    foreach (int index, indexes) {
+    for (int index : indexes) {
         const ItemData* itemData = m_itemData.at(index);
         const ItemData* parent = itemData->parent;
 
@@ -265,7 +269,7 @@ QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const
         lastAddedItem = itemData;
         const KFileItem& item = itemData->item;
         if (!item.isNull()) {
-            urls << item.targetUrl();
+            urls << item.url();
 
             bool isLocal;
             mostLocalUrls << item.mostLocalUrl(isLocal);
@@ -275,13 +279,7 @@ QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const
         }
     }
 
-    const bool different = canUseMostLocalUrls && mostLocalUrls != urls;
-    if (different) {
-        data->setUrls(mostLocalUrls);
-    } else {
-        data->setUrls(urls);
-    }
-
+    KUrlMimeData::setUrls(urls, mostLocalUrls, data);
     return data;
 }
 
@@ -331,7 +329,26 @@ QList<QPair<int, QVariant> > KFileItemModel::groups() const
         switch (typeForRole(sortRole())) {
         case NameRole:        m_groups = nameRoleGroups(); break;
         case SizeRole:        m_groups = sizeRoleGroups(); break;
-        case DateRole:        m_groups = dateRoleGroups(); break;
+        case ModificationTimeRole:
+            m_groups = timeRoleGroups([](const ItemData *item) {
+                return item->item.time(KFileItem::ModificationTime);
+            });
+            break;
+        case CreationTimeRole:
+            m_groups = timeRoleGroups([](const ItemData *item) {
+                return item->item.time(KFileItem::CreationTime);
+            });
+            break;
+        case AccessTimeRole:
+            m_groups = timeRoleGroups([](const ItemData *item) {
+                return item->item.time(KFileItem::AccessTime);
+            });
+            break;
+        case DeletionTimeRole:
+            m_groups = timeRoleGroups([](const ItemData *item) {
+                return item->values.value("deletiontime").toDateTime();
+            });
+            break;
         case PermissionsRole: m_groups = permissionRoleGroups(); break;
         case RatingRole:      m_groups = ratingRoleGroups(); break;
         default:              m_groups = genericStringRoleGroups(sortRole()); break;
@@ -420,12 +437,15 @@ int KFileItemModel::index(const QUrl& url) const
             foreach (const QUrl& url, indexesForUrl.uniqueKeys()) {
                 if (indexesForUrl.count(url) > 1) {
                     qCWarning(DolphinDebug) << "Multiple items found with the URL" << url;
-                    foreach (int index, indexesForUrl.values(url)) {
-                        const ItemData* data = m_itemData.at(index);
-                        qCWarning(DolphinDebug) << "index" << index << ":" << data->item;
+
+                    auto it = indexesForUrl.find(url);
+                    while (it != indexesForUrl.end() && it.key() == url) {
+                        const ItemData* data = m_itemData.at(it.value());
+                        qCWarning(DolphinDebug) << "index" << it.value() << ":" << data->item;
                         if (data->parent) {
                             qCWarning(DolphinDebug) << "parent" << data->parent->item;
                         }
+                        ++it;
                     }
                 }
             }
@@ -594,7 +614,12 @@ int KFileItemModel::expandedParentsCount(int index) const
 
 QSet<QUrl> KFileItemModel::expandedDirectories() const
 {
-    return m_expandedDirs.values().toSet();
+    QSet<QUrl> result;
+    const auto dirs = m_expandedDirs;
+    for (const auto &dir : dirs) {
+        result.insert(dir);
+    }
+    return result;
 }
 
 void KFileItemModel::restoreExpandedDirectories(const QSet<QUrl> &urls)
@@ -1102,7 +1127,7 @@ void KFileItemModel::slotClear()
     qCDebug(DolphinDebug) << "Clearing all items";
 #endif
 
-    qDeleteAll(m_filteredItems.values());
+    qDeleteAll(m_filteredItems);
     m_filteredItems.clear();
     m_groups.clear();
 
@@ -1314,6 +1339,7 @@ void KFileItemModel::prepareItemsForSorting(QList<ItemData*>& itemDataList)
     case GroupRole:
     case DestinationRole:
     case PathRole:
+    case DeletionTimeRole:
         // These roles can be determined with retrieveData, and they have to be stored
         // in the QHash "values" for the sorting.
         foreach (ItemData* itemData, itemDataList) {
@@ -1466,6 +1492,7 @@ KFileItemModel::RoleType KFileItemModel::typeForRole(const QByteArray& role) con
         // with KFileItemModel::roleForType() in case if a change is done).
         roles.insert("isDir", IsDirRole);
         roles.insert("isLink", IsLinkRole);
+        roles.insert("isHidden", IsHiddenRole);
         roles.insert("isExpanded", IsExpandedRole);
         roles.insert("isExpandable", IsExpandableRole);
         roles.insert("expandedParentsCount", ExpandedParentsCountRole);
@@ -1492,6 +1519,7 @@ QByteArray KFileItemModel::roleForType(RoleType roleType) const
         // with KFileItemModel::typeForRole() in case if a change is done).
         roles.insert(IsDirRole, "isDir");
         roles.insert(IsLinkRole, "isLink");
+        roles.insert(IsHiddenRole, "isHidden");
         roles.insert(IsExpandedRole, "isExpanded");
         roles.insert(IsExpandableRole, "isExpandable");
         roles.insert(ExpandedParentsCountRole, "expandedParentsCount");
@@ -1519,6 +1547,10 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
         data.insert(sharedValue("isLink"), true);
     }
 
+    if (m_requestRole[IsHiddenRole] && item.isHidden()) {
+        data.insert(sharedValue("isHidden"), true);
+    }
+
     if (m_requestRole[NameRole]) {
         data.insert(sharedValue("text"), item.text());
     }
@@ -1527,12 +1559,28 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
         data.insert(sharedValue("size"), item.size());
     }
 
-    if (m_requestRole[DateRole]) {
+    if (m_requestRole[ModificationTimeRole]) {
         // Don't use KFileItem::timeString() as this is too expensive when
         // having several thousands of items. Instead the formatting of the
         // date-time will be done on-demand by the view when the date will be shown.
         const QDateTime dateTime = item.time(KFileItem::ModificationTime);
-        data.insert(sharedValue("date"), dateTime);
+        data.insert(sharedValue("modificationtime"), dateTime);
+    }
+
+    if (m_requestRole[CreationTimeRole]) {
+        // Don't use KFileItem::timeString() as this is too expensive when
+        // having several thousands of items. Instead the formatting of the
+        // date-time will be done on-demand by the view when the date will be shown.
+        const QDateTime dateTime = item.time(KFileItem::CreationTime);
+        data.insert(sharedValue("creationtime"), dateTime);
+    }
+
+    if (m_requestRole[AccessTimeRole]) {
+        // Don't use KFileItem::timeString() as this is too expensive when
+        // having several thousands of items. Instead the formatting of the
+        // date-time will be done on-demand by the view when the date will be shown.
+        const QDateTime dateTime = item.time(KFileItem::AccessTime);
+        data.insert(sharedValue("accesstime"), dateTime);
     }
 
     if (m_requestRole[PermissionsRole]) {
@@ -1550,7 +1598,7 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
     if (m_requestRole[DestinationRole]) {
         QString destination = item.linkDest();
         if (destination.isEmpty()) {
-            destination = QLatin1String("-");
+            destination = QStringLiteral("-");
         }
         data.insert(sharedValue("destination"), destination);
     }
@@ -1578,6 +1626,14 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
         data.insert(sharedValue("path"), path);
     }
 
+    if (m_requestRole[DeletionTimeRole]) {
+        QDateTime deletionTime;
+        if (item.url().scheme() == QLatin1String("trash")) {
+            deletionTime = QDateTime::fromString(item.entry().stringValue(KIO::UDSEntry::UDS_EXTRA + 1), Qt::ISODate);
+        }
+        data.insert(sharedValue("deletiontime"), deletionTime);
+    }
+
     if (m_requestRole[IsExpandableRole] && isDir) {
         data.insert(sharedValue("isExpandable"), true);
     }
@@ -1675,6 +1731,16 @@ public:
         m_collator.setNumericMode(other.m_collator.numericMode());
     }
 
+    ~KFileItemModelLessThan() = default;
+    //We do not delete m_model as the pointer was passed from outside ant it will be deleted elsewhere.
+
+    KFileItemModelLessThan& operator=(const KFileItemModelLessThan& other)
+    {
+        m_model = other.m_model;
+        m_collator = other.m_collator;
+        return *this;
+    }
+
     bool operator()(const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b) const
     {
         return m_model->lessThan(a, b, m_collator);
@@ -1747,7 +1813,7 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const
         break;
     }
 
-    case DateRole: {
+    case ModificationTimeRole: {
         const QDateTime dateTimeA = itemA.time(KFileItem::ModificationTime);
         const QDateTime dateTimeB = itemB.time(KFileItem::ModificationTime);
         if (dateTimeA < dateTimeB) {
@@ -1758,6 +1824,28 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const
         break;
     }
 
+    case CreationTimeRole: {
+        const QDateTime dateTimeA = itemA.time(KFileItem::CreationTime);
+        const QDateTime dateTimeB = itemB.time(KFileItem::CreationTime);
+        if (dateTimeA < dateTimeB) {
+            result = -1;
+        } else if (dateTimeA > dateTimeB) {
+            result = +1;
+        }
+        break;
+    }
+
+    case DeletionTimeRole: {
+        const QDateTime dateTimeA = a->values.value("deletiontime").toDateTime();
+        const QDateTime dateTimeB = b->values.value("deletiontime").toDateTime();
+        if (dateTimeA < dateTimeB) {
+            result = -1;
+        } else if (dateTimeA > dateTimeB) {
+            result = +1;
+        }
+        break;
+    }
+
     case RatingRole: {
         result = a->values.value("rating").toInt() - b->values.value("rating").toInt();
         break;
@@ -1852,13 +1940,14 @@ QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
             if (newFirstChar.isLetter()) {
                 // Try to find a matching group in the range 'A' to 'Z'.
                 static std::vector<QChar> lettersAtoZ;
+                lettersAtoZ.reserve('Z' - 'A' + 1);
                 if (lettersAtoZ.empty()) {
                     for (char c = 'A'; c <= 'Z'; ++c) {
                         lettersAtoZ.push_back(QLatin1Char(c));
                     }
                 }
 
-                auto localeAwareLessThan = [this](const QChar& c1, const QChar& c2) -> bool {
+                auto localeAwareLessThan = [this](QChar c1, QChar c2) -> bool {
                     return m_collator.compare(c1, c2) < 0;
                 };
 
@@ -1926,7 +2015,7 @@ QList<QPair<int, QVariant> > KFileItemModel::sizeRoleGroups() const
     return groups;
 }
 
-QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
+QList<QPair<int, QVariant> > KFileItemModel::timeRoleGroups(std::function<QDateTime(const ItemData *)> fileTimeCb) const
 {
     Q_ASSERT(!m_itemData.isEmpty());
 
@@ -1935,26 +2024,26 @@ QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
 
     const QDate currentDate = QDate::currentDate();
 
-    QDate previousModifiedDate;
+    QDate previousFileDate;
     QString groupValue;
     for (int i = 0; i <= maxIndex; ++i) {
         if (isChildItem(i)) {
             continue;
         }
 
-        const QDateTime modifiedTime = m_itemData.at(i)->item.time(KFileItem::ModificationTime);
-        const QDate modifiedDate = modifiedTime.date();
-        if (modifiedDate == previousModifiedDate) {
+        const QDateTime fileTime = fileTimeCb(m_itemData.at(i));
+        const QDate fileDate = fileTime.date();
+        if (fileDate == previousFileDate) {
             // The current item is in the same group as the previous item
             continue;
         }
-        previousModifiedDate = modifiedDate;
+        previousFileDate = fileDate;
 
-        const int daysDistance = modifiedDate.daysTo(currentDate);
+        const int daysDistance = fileDate.daysTo(currentDate);
 
         QString newGroupValue;
-        if (currentDate.year() == modifiedDate.year() &&
-            currentDate.month() == modifiedDate.month()) {
+        if (currentDate.year() == fileDate.year() &&
+            currentDate.month() == fileDate.month()) {
 
             switch (daysDistance / 7) {
             case 0:
@@ -1962,7 +2051,7 @@ QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
                 case 0:  newGroupValue = i18nc("@title:group Date", "Today"); break;
                 case 1:  newGroupValue = i18nc("@title:group Date", "Yesterday"); break;
                 default:
-                    newGroupValue = modifiedTime.toString(
+                    newGroupValue = fileTime.toString(
                         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", "%1", newGroupValue);
@@ -1986,18 +2075,18 @@ QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
             }
         } else {
             const QDate lastMonthDate = currentDate.addMonths(-1);
-            if  (lastMonthDate.year() == modifiedDate.year() &&
-                 lastMonthDate.month() == modifiedDate.month()) {
+            if  (lastMonthDate.year() == fileDate.year() &&
+                 lastMonthDate.month() == fileDate.month()) {
 
                 if (daysDistance == 1) {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: "
+                    newGroupValue = fileTime.toString(i18nc("@title:group Date: "
                         "MMMM is full month name in current locale, and yyyy is "
                         "full year number", "'Yesterday' (MMMM, yyyy)"));
                     newGroupValue = i18nc("Can be used to script translation of "
                         "\"'Yesterday' (MMMM, yyyy)\" with context @title:group Date",
                         "%1", newGroupValue);
                 } else if (daysDistance <= 7) {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: "
+                    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)"));
@@ -2005,28 +2094,28 @@ QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
                         "\"dddd (MMMM, yyyy)\" with context @title:group Date",
                         "%1", newGroupValue);
                 } else if (daysDistance <= 7 * 2) {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: "
+                    newGroupValue = fileTime.toString(i18nc("@title:group Date: "
                         "MMMM is full month name in current locale, and yyyy is "
                         "full year number", "'One Week Ago' (MMMM, yyyy)"));
                     newGroupValue = i18nc("Can be used to script translation of "
                         "\"'One Week Ago' (MMMM, yyyy)\" with context @title:group Date",
                         "%1", newGroupValue);
                 } else if (daysDistance <= 7 * 3) {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: "
+                    newGroupValue = fileTime.toString(i18nc("@title:group Date: "
                         "MMMM is full month name in current locale, and yyyy is "
                         "full year number", "'Two Weeks Ago' (MMMM, yyyy)"));
                     newGroupValue = i18nc("Can be used to script translation of "
                         "\"'Two Weeks Ago' (MMMM, yyyy)\" with context @title:group Date",
                         "%1", newGroupValue);
                 } else if (daysDistance <= 7 * 4) {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: "
+                    newGroupValue = fileTime.toString(i18nc("@title:group Date: "
                         "MMMM is full month name in current locale, and yyyy is "
                         "full year number", "'Three Weeks Ago' (MMMM, yyyy)"));
                     newGroupValue = i18nc("Can be used to script translation of "
                         "\"'Three Weeks Ago' (MMMM, yyyy)\" with context @title:group Date",
                         "%1", newGroupValue);
                 } else {
-                    newGroupValue = modifiedTime.toString(i18nc("@title:group Date: "
+                    newGroupValue = fileTime.toString(i18nc("@title:group Date: "
                         "MMMM is full month name in current locale, and yyyy is "
                         "full year number", "'Earlier on' MMMM, yyyy"));
                     newGroupValue = i18nc("Can be used to script translation of "
@@ -2034,7 +2123,7 @@ QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
                         "%1", newGroupValue);
                 }
             } else {
-                newGroupValue = modifiedTime.toString(i18nc("@title:group "
+                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 = i18nc("Can be used to script translation of "
@@ -2205,11 +2294,14 @@ const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count)
         { 0,             NoRole,          0, 0,                                             0, 0,                                     false, false },
         { "text",        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 },
+        { "modificationtime",        ModificationTimeRole,        I18N_NOOP2_NOSTRIP("@label", "Modified"),             0, 0,                                     false, false },
+        { "creationtime",        CreationTimeRole,        I18N_NOOP2_NOSTRIP("@label", "Created"),             0, 0,                                     false, false },
+        { "accesstime",        AccessTimeRole,        I18N_NOOP2_NOSTRIP("@label", "Accessed"),             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 },
+        { "title",       TitleRole,       I18N_NOOP2_NOSTRIP("@label", "Title"),            I18N_NOOP2_NOSTRIP("@label", "Document"), true,  true  },
         { "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  },
@@ -2219,6 +2311,7 @@ const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count)
         { "duration",    DurationRole,    I18N_NOOP2_NOSTRIP("@label", "Duration"),         I18N_NOOP2_NOSTRIP("@label", "Audio"),    true,  true  },
         { "track",       TrackRole,       I18N_NOOP2_NOSTRIP("@label", "Track"),            I18N_NOOP2_NOSTRIP("@label", "Audio"),    true,  true  },
         { "path",        PathRole,        I18N_NOOP2_NOSTRIP("@label", "Path"),             I18N_NOOP2_NOSTRIP("@label", "Other"),    false, false },
+        { "deletiontime",DeletionTimeRole,I18N_NOOP2_NOSTRIP("@label", "Deletion Time"),    I18N_NOOP2_NOSTRIP("@label", "Other"),    false, false },
         { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other"),    false, false },
         { "originUrl",   OriginUrlRole,   I18N_NOOP2_NOSTRIP("@label", "Downloaded From"),  I18N_NOOP2_NOSTRIP("@label", "Other"),    true,  false },
         { "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions"),      I18N_NOOP2_NOSTRIP("@label", "Other"),    false, false },
@@ -2313,4 +2406,3 @@ bool KFileItemModel::isConsistent() const
 
     return true;
 }
-