X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/a24327cd50ef17b953ecb908d260b73460158107..50149d6abb8a0a978db3c6afb5238bc42a4a89c8:/src/kitemviews/kfileitemmodel.cpp diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index e8cd8f393..ef80b4edb 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -14,16 +14,19 @@ #include "private/kfileitemmodeldirlister.h" #include "private/kfileitemmodelsortalgorithm.h" +#include #include #include #include #include +#include #include #include -#include +#include +#include -Q_GLOBAL_STATIC_WITH_ARGS(QMutex, s_collatorMutex, (QMutex::Recursive)) +Q_GLOBAL_STATIC(QRecursiveMutex, s_collatorMutex) // #define KFILEITEMMODEL_DEBUG @@ -31,6 +34,7 @@ KFileItemModel::KFileItemModel(QObject* parent) : KItemModelBase("text", parent), m_dirLister(nullptr), m_sortDirsFirst(true), + m_sortHiddenLast(true), m_sortRole(NameRole), m_sortingProgressPercent(-1), m_roles(), @@ -60,7 +64,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::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 +74,12 @@ KFileItemModel::KFileItemModel(QObject* parent) : connect(m_dirLister, QOverload::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::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; @@ -204,6 +213,19 @@ bool KFileItemModel::sortDirectoriesFirst() const return m_sortDirsFirst; } +void KFileItemModel::setSortHiddenLast(bool hiddenLast) +{ + if (hiddenLast != m_sortHiddenLast) { + m_sortHiddenLast = hiddenLast; + resortAllItems(); + } +} + +bool KFileItemModel::sortHiddenLast() const +{ + return m_sortHiddenLast; +} + void KFileItemModel::setShowHiddenFiles(bool show) { m_dirLister->setShowingDotFiles(show); @@ -487,7 +509,7 @@ void KFileItemModel::setRoles(const QSet& roles) m_itemData[i]->values = retrieveData(m_itemData.at(i)->item, m_itemData.at(i)->parent); } - emit itemsChanged(KItemRangeList() << KItemRange(0, count()), changedRoles); + Q_EMIT itemsChanged(KItemRangeList() << KItemRange(0, count()), changedRoles); } // Clear the 'values' of all filtered items. They will be re-populated with the @@ -892,13 +914,13 @@ void KFileItemModel::resortAllItems() movedToIndexes.append(newIndex); } - emit itemsMoved(KItemRange(firstMovedIndex, movedItemsCount), movedToIndexes); + Q_EMIT itemsMoved(KItemRange(firstMovedIndex, movedItemsCount), movedToIndexes); } else if (groupedSorting()) { // The groups might have changed even if the order of the items has not. const QList > oldGroups = m_groups; m_groups.clear(); if (groups() != oldGroups) { - emit groupsChanged(); + Q_EMIT groupsChanged(); } } @@ -917,7 +939,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. - for (const QUrl& url : qAsConst(m_urlsToExpand)) { + // 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); @@ -934,7 +958,7 @@ void KFileItemModel::slotCompleted() m_urlsToExpand.clear(); } - emit directoryLoadingCompleted(); + Q_EMIT directoryLoadingCompleted(); } void KFileItemModel::slotCanceled() @@ -942,7 +966,7 @@ void KFileItemModel::slotCanceled() m_maximumUpdateIntervalTimer->stop(); dispatchPendingItemsToInsert(); - emit directoryLoadingCanceled(); + Q_EMIT directoryLoadingCanceled(); } void KFileItemModel::slotItemsAdded(const QUrl &directoryUrl, const KFileItemList& items) @@ -1005,6 +1029,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) @@ -1013,6 +1039,7 @@ void KFileItemModel::slotItemsDeleted(const KFileItemList& items) QVector indexesToRemove; indexesToRemove.reserve(items.count()); + KFileItemList dirsChanged; for (const KFileItem& item : items) { const int indexForItem = index(item); @@ -1026,6 +1053,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()); @@ -1053,6 +1085,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 >& items) @@ -1067,6 +1101,7 @@ void KFileItemModel::slotRefreshItems(const QList >& indexes.reserve(items.count()); QSet changedRoles; + KFileItemList changedFiles; QListIterator > it(items); while (it.hasNext()) { @@ -1092,6 +1127,7 @@ void KFileItemModel::slotRefreshItems(const QList >& 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. @@ -1120,6 +1156,8 @@ void KFileItemModel::slotRefreshItems(const QList >& std::sort(indexes.begin(), indexes.end()); const KItemRangeList itemRangeList = KItemRangeList::fromSortedContainer(indexes); emitItemsChangedAndTriggerResorting(itemRangeList, changedRoles); + + Q_EMIT fileItemsChanged(changedFiles); } void KFileItemModel::slotClear() @@ -1143,7 +1181,7 @@ void KFileItemModel::slotClear() qDeleteAll(m_itemData); m_itemData.clear(); m_items.clear(); - emit itemsRemoved(KItemRangeList() << KItemRange(0, removedCount)); + Q_EMIT itemsRemoved(KItemRangeList() << KItemRange(0, removedCount)); } m_expandedDirs.clear(); @@ -1259,7 +1297,7 @@ void KFileItemModel::insertItems(QList& newItems) // It will be re-populated with the updated indices if index(const QUrl&) is called. m_items.clear(); - emit itemsInserted(itemRanges); + Q_EMIT itemsInserted(itemRanges); #ifdef KFILEITEMMODEL_DEBUG qCDebug(DolphinDebug) << "[TIME] Inserting of" << newItems.count() << "items:" << timer.elapsed(); @@ -1312,7 +1350,7 @@ void KFileItemModel::removeItems(const KItemRangeList& itemRanges, RemoveItemsBe // It will be re-populated with the updated indices if index(const QUrl&) is called. m_items.clear(); - emit itemsRemoved(itemRanges); + Q_EMIT itemsRemoved(itemRanges); } QList KFileItemModel::createItemDataList(const QUrl& parentUrl, const KFileItemList& items) const @@ -1428,7 +1466,7 @@ void KFileItemModel::removeExpandedItems() void KFileItemModel::emitItemsChangedAndTriggerResorting(const KItemRangeList& itemRanges, const QSet& changedRoles) { - emit itemsChanged(itemRanges, 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. @@ -1655,7 +1693,13 @@ QHash 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()); @@ -1703,7 +1747,18 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b, const QColla } } - if (m_sortDirsFirst || m_sortRole == SizeRole) { + // 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 || (DetailsModeSettings::directorySizeCount() && m_sortRole == SizeRole)) { const bool isDirA = a->item.isDir(); const bool isDirB = b->item.isDir(); if (isDirA && !isDirB) { @@ -1752,44 +1807,53 @@ 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()) { + result = 0; + break; + } else { + result = -1; + break; + } } else if (valueB.isNull()) { result = +1; + break; } else { if (valueA.toLongLong() < valueB.toLongLong()) { - return -1; + result = -1; + break; + } else if (valueA.toLongLong() > valueB.toLongLong()) { + result = +1; + break; } else { - return +1; + result = 0; + break; } } + } + 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; } @@ -1986,16 +2050,24 @@ QList > KFileItemModel::sizeRoleGroups() const } const KFileItem& item = m_itemData.at(i)->item; - const KIO::filesize_t fileSize = !item.isNull() ? item.size() : ~0U; + KIO::filesize_t fileSize = !item.isNull() ? item.size() : ~0U; QString newGroupValue; if (!item.isNull() && item.isDir()) { - newGroupValue = i18nc("@title:group Size", "Folders"); - } else if (fileSize < 5 * 1024 * 1024) { - newGroupValue = i18nc("@title:group Size", "Small"); - } else if (fileSize < 10 * 1024 * 1024) { - newGroupValue = i18nc("@title:group Size", "Medium"); - } else { - newGroupValue = i18nc("@title:group Size", "Big"); + if (DetailsModeSettings::directorySizeCount() || m_sortDirsFirst) { + newGroupValue = i18nc("@title:group Size", "Folders"); + } else { + fileSize = m_itemData.at(i)->values.value("size").toULongLong(); + } + } + + if (newGroupValue.isEmpty()) { + if (fileSize < 5 * 1024 * 1024) { // < 5 MB + newGroupValue = i18nc("@title:group Size", "Small"); + } else if (fileSize < 10 * 1024 * 1024) { // < 10 MB + newGroupValue = i18nc("@title:group Size", "Medium"); + } else { + newGroupValue = i18nc("@title:group Size", "Big"); + } } if (newGroupValue != groupValue) { @@ -2073,7 +2145,7 @@ QList > 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); @@ -2088,7 +2160,7 @@ QList > 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", @@ -2096,7 +2168,7 @@ QList > 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); @@ -2111,7 +2183,7 @@ QList > 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); @@ -2126,7 +2198,7 @@ QList > 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); @@ -2141,7 +2213,7 @@ QList > 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); @@ -2307,14 +2379,14 @@ void KFileItemModel::emitSortProgress(int resolvedCount) resortAllItems(); } - emit directorySortingProgress(100); + Q_EMIT directorySortingProgress(100); } else if (itemCount > 0) { resolvedCount = qBound(0, resolvedCount, itemCount); const int progress = resolvedCount * 100 / itemCount; if (m_sortingProgressPercent != progress) { m_sortingProgressPercent = progress; - emit directorySortingProgress(progress); + Q_EMIT directorySortingProgress(progress); } } }