+#ifdef KFILEITEMMODEL_DEBUG
+ qCDebug(DolphinDebug) << "[TIME] Sorting:" << timer.elapsed();
+#endif
+
+ KItemRangeList itemRanges;
+ const int existingItemCount = m_itemData.count();
+ const int newItemCount = newItems.count();
+ const int totalItemCount = existingItemCount + newItemCount;
+
+ if (existingItemCount == 0) {
+ // Optimization for the common special case that there are no
+ // items in the model yet. Happens, e.g., when entering a folder.
+ m_itemData = newItems;
+ itemRanges << KItemRange(0, newItemCount);
+ } else {
+ m_itemData.reserve(totalItemCount);
+ for (int i = existingItemCount; i < totalItemCount; ++i) {
+ m_itemData.append(nullptr);
+ }
+
+ // We build the new list m_itemData in reverse order to minimize
+ // the number of moves and guarantee O(N) complexity.
+ int targetIndex = totalItemCount - 1;
+ int sourceIndexExistingItems = existingItemCount - 1;
+ int sourceIndexNewItems = newItemCount - 1;
+
+ int rangeCount = 0;
+
+ while (sourceIndexNewItems >= 0) {
+ ItemData *newItem = newItems.at(sourceIndexNewItems);
+ 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) {
+ itemRanges << KItemRange(sourceIndexExistingItems + 1, rangeCount);
+ rangeCount = 0;
+ }
+
+ m_itemData[targetIndex] = m_itemData.at(sourceIndexExistingItems);
+ --sourceIndexExistingItems;
+ } else {
+ // Insert a new item into the list.
+ ++rangeCount;
+ m_itemData[targetIndex] = newItem;
+ --sourceIndexNewItems;
+ }
+ --targetIndex;
+ }
+
+ // Push the final item range to itemRanges.
+ if (rangeCount > 0) {
+ itemRanges << KItemRange(sourceIndexExistingItems + 1, rangeCount);
+ }
+
+ // Note that itemRanges is still sorted in reverse order.
+ std::reverse(itemRanges.begin(), itemRanges.end());
+ }
+
+ // The indexes in m_items are not correct anymore. Therefore, we clear m_items.
+ // It will be re-populated with the updated indices if index(const QUrl&) is called.
+ m_items.clear();
+
+ Q_EMIT itemsInserted(itemRanges);
+
+#ifdef KFILEITEMMODEL_DEBUG
+ qCDebug(DolphinDebug) << "[TIME] Inserting of" << newItems.count() << "items:" << timer.elapsed();
+#endif
+}
+
+void KFileItemModel::removeItems(const KItemRangeList &itemRanges, RemoveItemsBehavior behavior)
+{
+ if (itemRanges.isEmpty()) {
+ return;
+ }
+
+ m_groups.clear();
+
+ // Step 1: Remove the items from m_itemData, and free the ItemData.
+ int removedItemsCount = 0;
+ for (const KItemRange &range : itemRanges) {
+ removedItemsCount += range.count;
+
+ for (int index = range.index; index < range.index + range.count; ++index) {
+ if (behavior == DeleteItemData || (behavior == DeleteItemDataIfUnfiltered && !m_filteredItems.contains(m_itemData.at(index)->item))) {
+ delete m_itemData.at(index);
+ }
+
+ m_itemData[index] = nullptr;
+ }
+ }
+
+ // Step 2: Remove the ItemData pointers from the list m_itemData.
+ int target = itemRanges.at(0).index;
+ int source = itemRanges.at(0).index + itemRanges.at(0).count;
+ int nextRange = 1;
+
+ const int oldItemDataCount = m_itemData.count();
+ while (source < oldItemDataCount) {
+ m_itemData[target] = m_itemData[source];
+ ++target;
+ ++source;
+
+ if (nextRange < itemRanges.count() && source == itemRanges.at(nextRange).index) {
+ // Skip the items in the next removed range.
+ source += itemRanges.at(nextRange).count;
+ ++nextRange;
+ }
+ }
+
+ m_itemData.erase(m_itemData.end() - removedItemsCount, m_itemData.end());
+
+ // The indexes in m_items are not correct anymore. Therefore, we clear m_items.
+ // It will be re-populated with the updated indices if index(const QUrl&) is called.
+ m_items.clear();
+
+ Q_EMIT itemsRemoved(itemRanges);
+}
+
+QList<KFileItemModel::ItemData *> KFileItemModel::createItemDataList(const QUrl &parentUrl, const KFileItemList &items) const
+{
+ if (m_sortRole == TypeRole || m_groupRole == 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);
+ }
+
+ // We search for the parent in m_itemData and then in m_filteredItems if necessary
+ const int parentIndex = index(parentUrl);
+ ItemData *parentItem = parentIndex < 0 ? m_filteredItems.value(KFileItem(parentUrl), nullptr) : m_itemData.at(parentIndex);
+
+ QList<ItemData *> itemDataList;
+ itemDataList.reserve(items.count());
+
+ for (const KFileItem &item : items) {
+ ItemData *itemData = new ItemData();
+ itemData->item = item;
+ itemData->parent = parentItem;
+ itemDataList.append(itemData);
+ }
+
+ return itemDataList;
+}
+
+void KFileItemModel::prepareItemsWithRole(QList<ItemData *> &itemDataList, RoleType roleType)
+{
+ switch (roleType) {
+ case ExtensionRole:
+ case PermissionsRole:
+ case OwnerRole:
+ 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.
+ for (ItemData *itemData : std::as_const(itemDataList)) {
+ if (itemData->values.isEmpty()) {
+ itemData->values = retrieveData(itemData->item, itemData->parent);
+ }
+ }
+ break;
+
+ case TypeRole:
+ // At least store the data including the file type for items with known MIME type.
+ for (ItemData *itemData : std::as_const(itemDataList)) {
+ if (itemData->values.isEmpty()) {
+ const KFileItem item = itemData->item;
+ if (item.isDir() || item.isMimeTypeKnown()) {
+ itemData->values = retrieveData(itemData->item, itemData->parent);
+ }
+ }
+ }
+ break;
+
+ default:
+ // The other roles are either resolved by KFileItemModelRolesUpdater
+ // (this includes the SizeRole for directories), or they do not need
+ // to be stored in the QHash "values" for sorting because the data can
+ // be retrieved directly from the KFileItem (NameRole, SizeRole for files,
+ // DateRole).
+ break;
+ }
+}
+
+void KFileItemModel::prepareItemsForSorting(QList<ItemData *> &itemDataList)
+{
+ prepareItemsWithRole(itemDataList, m_sortRole);
+ prepareItemsWithRole(itemDataList, m_groupRole);
+}
+
+int KFileItemModel::expandedParentsCount(const ItemData *data)
+{
+ // The hash 'values' is only guaranteed to contain the key "expandedParentsCount"
+ // if the corresponding item is expanded, and it is not a top-level item.
+ const ItemData *parent = data->parent;
+ if (parent) {
+ if (parent->parent) {
+ Q_ASSERT(parent->values.contains("expandedParentsCount"));
+ return parent->values.value("expandedParentsCount").toInt() + 1;
+ } else {
+ return 1;
+ }
+ } else {
+ return 0;
+ }
+}
+
+void KFileItemModel::removeExpandedItems()
+{
+ QVector<int> indexesToRemove;
+
+ const int maxIndex = m_itemData.count() - 1;
+ for (int i = 0; i <= maxIndex; ++i) {
+ const ItemData *itemData = m_itemData.at(i);
+ if (itemData->parent) {
+ indexesToRemove.append(i);
+ }
+ }
+
+ removeItems(KItemRangeList::fromSortedContainer(indexesToRemove), DeleteItemData);
+ m_expandedDirs.clear();
+
+ // Also remove all filtered items which have a parent.
+ QHash<KFileItem, ItemData *>::iterator it = m_filteredItems.begin();
+ const QHash<KFileItem, ItemData *>::iterator end = m_filteredItems.end();
+
+ while (it != end) {
+ if (it.value()->parent) {
+ delete it.value();
+ it = m_filteredItems.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+void KFileItemModel::emitItemsChangedAndTriggerResorting(const KItemRangeList &itemRanges, const QSet<QByteArray> &changedRoles)
+{
+ Q_EMIT itemsChanged(itemRanges, changedRoles);
+
+ // Trigger a resorting if necessary. Note that this can happen even if the sort
+ // role has not changed at all because the file name can be used as a fallback.
+ if (changedRoles.contains(sortRole()) || changedRoles.contains(roleForType(NameRole))
+ || (changedRoles.contains("count") && sortRole() == "size")) { // "count" is used in the "size" sort role, so this might require a resorting.
+ for (const KItemRange &range : itemRanges) {
+ bool needsResorting = false;
+
+ const int first = range.index;
+ const int last = range.index + range.count - 1;
+
+ // Resorting the model is necessary if
+ // (a) The first item in the range is "lessThan" its predecessor,
+ // (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), m_collator)) {
+ needsResorting = true;
+ } else if (last < count() - 1 && 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), m_collator)) {
+ needsResorting = true;
+ break;
+ }
+ }
+ }
+
+ if (needsResorting) {
+ scheduleResortAllItems();
+ return;
+ }
+ }
+ }
+
+ if (groupedSorting() && changedRoles.contains(sortRole())) {
+ // The position is still correct, but the groups might have changed
+ // if the changed item is either the first or the last item in a
+ // group.
+ // In principle, we could try to find out if the item really is the
+ // first or last one in its group and then update the groups
+ // (possibly with a delayed timer to make sure that we don't
+ // re-calculate the groups very often if items are updated one by
+ // one), but starting m_resortAllItemsTimer is easier.
+ m_resortAllItemsTimer->start();
+ }
+}
+
+void KFileItemModel::resetRoles()
+{
+ for (int i = 0; i < RolesCount; ++i) {
+ m_requestRole[i] = false;
+ }
+}
+
+KFileItemModel::RoleType KFileItemModel::typeForRole(const QByteArray &role) const
+{
+ static QHash<QByteArray, RoleType> roles;
+ if (roles.isEmpty()) {
+ // Insert user visible roles that can be accessed with
+ // KFileItemModel::roleInformation()
+ int count = 0;
+ const RoleInfoMap *map = rolesInfoMap(count);
+ for (int i = 0; i < count; ++i) {
+ roles.insert(map[i].role, map[i].roleType);
+ }
+
+ // Insert internal roles (take care to synchronize the implementation
+ // 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);
+
+ Q_ASSERT(roles.count() == RolesCount);
+ }
+
+ return roles.value(role, NoRole);
+}
+
+QByteArray KFileItemModel::roleForType(RoleType roleType) const
+{
+ static QHash<RoleType, QByteArray> roles;
+ if (roles.isEmpty()) {
+ // Insert user visible roles that can be accessed with
+ // KFileItemModel::roleInformation()
+ int count = 0;
+ const RoleInfoMap *map = rolesInfoMap(count);
+ for (int i = 0; i < count; ++i) {
+ roles.insert(map[i].roleType, map[i].role);
+ }
+
+ // Insert internal roles (take care to synchronize the implementation
+ // 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");
+
+ Q_ASSERT(roles.count() == RolesCount);
+ };
+
+ return roles.value(roleType);
+}
+
+QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem &item, const ItemData *parent) const
+{
+ // It is important to insert only roles that are fast to retrieve. E.g.
+ // KFileItem::iconName() can be very expensive if the MIME-type is unknown
+ // and hence will be retrieved asynchronously by KFileItemModelRolesUpdater.
+ QHash<QByteArray, QVariant> data;
+ data.insert(sharedValue("url"), item.url());
+
+ const bool isDir = item.isDir();
+ if (m_requestRole[IsDirRole] && isDir) {
+ data.insert(sharedValue("isDir"), true);
+ }
+
+ if (m_requestRole[IsLinkRole] && item.isLink()) {
+ data.insert(sharedValue("isLink"), true);
+ }
+
+ if (m_requestRole[IsHiddenRole]) {
+ data.insert(sharedValue("isHidden"), item.isHidden() || item.mimetype() == QStringLiteral("application/x-trash"));
+ }
+
+ if (m_requestRole[NameRole]) {
+ data.insert(sharedValue("text"), item.text());
+ }
+
+ if (m_requestRole[ExtensionRole] && !isDir) {
+ // TODO KF6 use KFileItem::suffix 464722
+ data.insert(sharedValue("extension"), QFileInfo(item.name()).suffix());
+ }
+
+ if (m_requestRole[SizeRole] && !isDir) {
+ data.insert(sharedValue("size"), item.size());
+ }
+
+ if (m_requestRole[ModificationTimeRole]) {
+ // Don't use KFileItem::timeString() or KFileItem::time() as this is too expensive when
+ // having several thousands of items. Instead read the raw number from UDSEntry directly
+ // and the formatting of the date-time will be done on-demand by the view when the date will be shown.
+ const long long dateTime = item.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
+ data.insert(sharedValue("modificationtime"), dateTime);
+ }
+
+ if (m_requestRole[CreationTimeRole]) {
+ // Don't use KFileItem::timeString() or KFileItem::time() as this is too expensive when
+ // having several thousands of items. Instead read the raw number from UDSEntry directly
+ // and the formatting of the date-time will be done on-demand by the view when the date will be shown.
+ const long long dateTime = item.entry().numberValue(KIO::UDSEntry::UDS_CREATION_TIME, -1);
+ data.insert(sharedValue("creationtime"), dateTime);
+ }
+
+ if (m_requestRole[AccessTimeRole]) {
+ // Don't use KFileItem::timeString() or KFileItem::time() as this is too expensive when
+ // having several thousands of items. Instead read the raw number from UDSEntry directly
+ // and the formatting of the date-time will be done on-demand by the view when the date will be shown.
+ const long long dateTime = item.entry().numberValue(KIO::UDSEntry::UDS_ACCESS_TIME, -1);
+ data.insert(sharedValue("accesstime"), dateTime);
+ }
+
+ if (m_requestRole[PermissionsRole]) {
+ data.insert(sharedValue("permissions"), QVariantList() << item.permissionsString() << item.permissions());
+ }
+
+ if (m_requestRole[OwnerRole]) {
+ data.insert(sharedValue("owner"), item.user());
+ }
+
+ if (m_requestRole[GroupRole]) {
+ data.insert(sharedValue("group"), item.group());
+ }
+
+ if (m_requestRole[DestinationRole]) {
+ QString destination = item.linkDest();
+ if (destination.isEmpty()) {
+ destination = QLatin1Char('-');
+ }
+ data.insert(sharedValue("destination"), destination);
+ }
+
+ if (m_requestRole[PathRole]) {
+ QString path;
+ if (item.url().scheme() == QLatin1String("trash")) {
+ path = item.entry().stringValue(KIO::UDSEntry::UDS_EXTRA);
+ } else {
+ // For performance reasons cache the home-path in a static QString
+ // (see QDir::homePath() for more details)
+ static QString homePath;
+ if (homePath.isEmpty()) {
+ homePath = QDir::homePath();
+ }
+
+ path = item.localPath();
+ if (path.startsWith(homePath)) {
+ path.replace(0, homePath.length(), QLatin1Char('~'));
+ }
+ }
+
+ const int index = path.lastIndexOf(item.text());
+ path = path.mid(0, index - 1);
+ 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);
+ }
+
+ if (m_requestRole[ExpandedParentsCountRole]) {
+ if (parent) {
+ const int level = expandedParentsCount(parent) + 1;
+ data.insert(sharedValue("expandedParentsCount"), level);
+ }
+ }
+
+ if (item.isMimeTypeKnown()) {
+ 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());
+ }
+ } else if (m_requestRole[TypeRole] && isDir) {
+ static const QString folderMimeType = item.mimeComment();
+ data.insert(sharedValue("type"), folderMimeType);
+ }
+
+ return data;
+}
+
+bool KFileItemModel::lessThan(const ItemData *a, const ItemData *b, const QCollator &collator) const
+{
+ int result = 0;
+
+ if (a->parent != b->parent) {
+ const int expansionLevelA = expandedParentsCount(a);
+ const int expansionLevelB = expandedParentsCount(b);
+
+ // If b has a higher expansion level than a, check if a is a parent
+ // of b, and make sure that both expansion levels are equal otherwise.
+ for (int i = expansionLevelB; i > expansionLevelA; --i) {
+ if (b->parent == a) {
+ return true;
+ }
+ b = b->parent;
+ }
+
+ // If a has a higher expansion level than a, check if b is a parent
+ // of a, and make sure that both expansion levels are equal otherwise.
+ for (int i = expansionLevelA; i > expansionLevelB; --i) {
+ if (a->parent == b) {
+ return false;
+ }
+ a = a->parent;
+ }
+
+ Q_ASSERT(expandedParentsCount(a) == expandedParentsCount(b));
+
+ // Compare the last parents of a and b which are different.
+ while (a->parent != b->parent) {
+ a = a->parent;
+ b = b->parent;
+ }
+ }
+
+ result = groupRoleCompare(a, b, collator);
+ if (result == 0) {
+ // Show hidden files and folders last
+ if (m_sortHiddenLast) {
+ 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 || (ContentDisplaySettings::directorySizeMode() == ContentDisplaySettings::EnumDirectorySizeMode::ContentCount && m_sortRole == SizeRole)) {
+ const bool isDirA = a->item.isDir();
+ const bool isDirB = b->item.isDir();
+ if (isDirA && !isDirB) {
+ return true;
+ } else if (!isDirA && isDirB) {
+ return false;
+ }
+ }
+ result = sortRoleCompare(a, b, collator);
+ result = (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0;
+ } else {
+ result = (groupOrder() == Qt::AscendingOrder) ? result < 0 : result > 0;
+ }
+ return result;
+}
+
+void KFileItemModel::sort(const QList<KFileItemModel::ItemData *>::iterator &begin, const QList<KFileItemModel::ItemData *>::iterator &end) const
+{
+ auto lambdaLessThan = [&](const KFileItemModel::ItemData *a, const KFileItemModel::ItemData *b) {
+ return lessThan(a, b, m_collator);
+ };
+
+ if (m_sortRole == NameRole || isRoleValueNatural(m_sortRole)) {
+ // Sorting by string can be expensive, in particular if natural sorting is
+ // enabled. Use all CPU cores to speed up the sorting process.
+ static const int numberOfThreads = QThread::idealThreadCount();
+ parallelMergeSort(begin, end, lambdaLessThan, numberOfThreads);
+ } else {
+ // Sorting by other roles is quite fast. Use only one thread to prevent
+ // problems caused by non-reentrant comparison functions, see
+ // https://bugs.kde.org/show_bug.cgi?id=312679
+ mergeSort(begin, end, lambdaLessThan);
+ }
+}
+
+int KFileItemModel::sortRoleCompare(const ItemData *a, const ItemData *b, const QCollator &collator) const
+{
+ // This function must never return 0, because that would break stable
+ // sorting, which leads to all kinds of bugs.
+ // See: https://bugs.kde.org/show_bug.cgi?id=433247
+ // If two items have equal sort values, let the fallbacks at the bottom of
+ // the function handle it.
+ const KFileItem &itemA = a->item;
+ const KFileItem &itemB = b->item;
+
+ int result = 0;
+
+ switch (m_sortRole) {
+ case NameRole:
+ // The name role is handled as default fallback after the switch
+ break;
+
+ case SizeRole: {
+ if (ContentDisplaySettings::directorySizeMode() == ContentDisplaySettings::EnumDirectorySizeMode::ContentCount && 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 -1;
+ }
+ } else if (valueB.isNull()) {
+ return +1;
+ } else {
+ if (valueA.toLongLong() < valueB.toLongLong()) {
+ return -1;
+ } else if (valueA.toLongLong() > valueB.toLongLong()) {
+ return +1;
+ }
+ }
+ break;
+ }
+
+ KIO::filesize_t sizeA = 0;
+ if (itemA.isDir()) {
+ sizeA = a->values.value("size").toULongLong();
+ } else {
+ sizeA = itemA.size();
+ }
+ KIO::filesize_t sizeB = 0;
+ if (itemB.isDir()) {
+ sizeB = b->values.value("size").toULongLong();
+ } else {
+ sizeB = itemB.size();
+ }
+ if (sizeA < sizeB) {
+ return -1;
+ } else if (sizeA > sizeB) {
+ return +1;
+ }
+ break;
+ }
+
+ case ModificationTimeRole: {
+ const long long dateTimeA = itemA.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
+ const long long dateTimeB = itemB.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
+ if (dateTimeA < dateTimeB) {
+ return -1;
+ } else if (dateTimeA > dateTimeB) {
+ return +1;
+ }
+ break;
+ }
+
+ case AccessTimeRole: {
+ const long long dateTimeA = itemA.entry().numberValue(KIO::UDSEntry::UDS_ACCESS_TIME, -1);
+ const long long dateTimeB = itemB.entry().numberValue(KIO::UDSEntry::UDS_ACCESS_TIME, -1);
+ if (dateTimeA < dateTimeB) {
+ return -1;
+ } else if (dateTimeA > dateTimeB) {
+ return +1;
+ }
+ break;
+ }
+
+ case CreationTimeRole: {
+ const long long dateTimeA = itemA.entry().numberValue(KIO::UDSEntry::UDS_CREATION_TIME, -1);
+ const long long dateTimeB = itemB.entry().numberValue(KIO::UDSEntry::UDS_CREATION_TIME, -1);
+ if (dateTimeA < dateTimeB) {
+ return -1;
+ } else if (dateTimeA > dateTimeB) {
+ return +1;
+ }
+ break;
+ }
+
+ case DeletionTimeRole: {
+ const QDateTime dateTimeA = a->values.value("deletiontime").toDateTime();
+ const QDateTime dateTimeB = b->values.value("deletiontime").toDateTime();
+ if (dateTimeA < dateTimeB) {
+ return -1;
+ } else if (dateTimeA > dateTimeB) {
+ return +1;
+ }
+ break;
+ }