X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/1b4572dac9fb529d31b786f93e4f02c6f8aeeb21..9aee5d22513f0367febab54b38b3a7dc58d120bb:/src/kitemviews/kfileitemmodel.cpp diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 711b0797b..eac3ddf8b 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -21,11 +21,10 @@ #include "kfileitemmodel.h" -#include +#include "dolphin_generalsettings.h" + #include -#include -#include -#include //TODO: port to QCollator +#include "dolphindebug.h" #include "private/kfileitemmodelsortalgorithm.h" #include "private/kfileitemmodeldirlister.h" @@ -42,12 +41,11 @@ KFileItemModel::KFileItemModel(QObject* parent) : KItemModelBase("text", parent), m_dirLister(0), - m_naturalSorting(KGlobalSettings::naturalSorting()), + m_naturalSorting(GeneralSettings::naturalSorting()), m_sortDirsFirst(true), m_sortRole(NameRole), m_sortingProgressPercent(-1), m_roles(), - m_caseSensitivity(Qt::CaseInsensitive), m_itemData(), m_items(), m_filter(), @@ -60,6 +58,9 @@ KFileItemModel::KFileItemModel(QObject* parent) : m_expandedDirs(), m_urlsToExpand() { + m_collator.setCaseSensitivity(Qt::CaseInsensitive); + m_collator.setNumericMode(true); + m_dirLister = new KFileItemModelDirLister(this); m_dirLister->setDelayedMimeTypes(true); @@ -105,7 +106,7 @@ KFileItemModel::KFileItemModel(QObject* parent) : m_resortAllItemsTimer->setSingleShot(true); connect(m_resortAllItemsTimer, &QTimer::timeout, this, &KFileItemModel::resortAllItems); - connect(KGlobalSettings::self(), &KGlobalSettings::naturalSortingChanged, + connect(GeneralSettings::self(), &GeneralSettings::naturalSortingChanged, this, &KFileItemModel::slotNaturalSortingChanged); } @@ -338,7 +339,7 @@ QList > KFileItemModel::groups() const } #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed(); + qCDebug(DolphinDebug) << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed(); #endif } @@ -407,9 +408,9 @@ int KFileItemModel::index(const QUrl& url) const if (m_items.count() != m_itemData.count() && printDebugInfo) { printDebugInfo = false; - kWarning() << "The model is in an inconsistent state."; - kWarning() << "m_items.count() ==" << m_items.count(); - kWarning() << "m_itemData.count() ==" << m_itemData.count(); + qCWarning(DolphinDebug) << "The model is in an inconsistent state."; + qCWarning(DolphinDebug) << "m_items.count() ==" << m_items.count(); + qCWarning(DolphinDebug) << "m_itemData.count() ==" << m_itemData.count(); // Check if there are multiple items with the same URL. QMultiHash indexesForUrl; @@ -419,12 +420,12 @@ int KFileItemModel::index(const QUrl& url) const foreach (const QUrl& url, indexesForUrl.uniqueKeys()) { if (indexesForUrl.count(url) > 1) { - kWarning() << "Multiple items found with the URL" << url; + qCWarning(DolphinDebug) << "Multiple items found with the URL" << url; foreach (int index, indexesForUrl.values(url)) { const ItemData* data = m_itemData.at(index); - kWarning() << "index" << index << ":" << data->item; + qCWarning(DolphinDebug) << "index" << index << ":" << data->item; if (data->parent) { - kWarning() << "parent" << data->parent->item; + qCWarning(DolphinDebug) << "parent" << data->parent->item; } } } @@ -794,8 +795,8 @@ void KFileItemModel::resortAllItems() #ifdef KFILEITEMMODEL_DEBUG QElapsedTimer timer; timer.start(); - kDebug() << "==========================================================="; - kDebug() << "Resorting" << itemCount << "items"; + qCDebug(DolphinDebug) << "==========================================================="; + qCDebug(DolphinDebug) << "Resorting" << itemCount << "items"; #endif // Remember the order of the current URLs so @@ -857,7 +858,7 @@ void KFileItemModel::resortAllItems() } #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed(); + qCDebug(DolphinDebug) << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed(); #endif } @@ -1012,7 +1013,7 @@ void KFileItemModel::slotRefreshItems(const QList >& { Q_ASSERT(!items.isEmpty()); #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "Refreshing" << items.count() << "items"; + qCDebug(DolphinDebug) << "Refreshing" << items.count() << "items"; #endif // Get the indexes of all items that have been refreshed @@ -1078,7 +1079,7 @@ void KFileItemModel::slotRefreshItems(const QList >& void KFileItemModel::slotClear() { #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "Clearing all items"; + qCDebug(DolphinDebug) << "Clearing all items"; #endif qDeleteAll(m_filteredItems.values()); @@ -1104,7 +1105,7 @@ void KFileItemModel::slotClear() void KFileItemModel::slotNaturalSortingChanged() { - m_naturalSorting = KGlobalSettings::naturalSorting(); + m_naturalSorting = GeneralSettings::naturalSorting(); resortAllItems(); } @@ -1125,8 +1126,8 @@ void KFileItemModel::insertItems(QList& newItems) #ifdef KFILEITEMMODEL_DEBUG QElapsedTimer timer; timer.start(); - kDebug() << "==========================================================="; - kDebug() << "Inserting" << newItems.count() << "items"; + qCDebug(DolphinDebug) << "==========================================================="; + qCDebug(DolphinDebug) << "Inserting" << newItems.count() << "items"; #endif m_groups.clear(); @@ -1143,7 +1144,7 @@ void KFileItemModel::insertItems(QList& newItems) sort(newItems.begin(), newItems.end()); #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "[TIME] Sorting:" << timer.elapsed(); + qCDebug(DolphinDebug) << "[TIME] Sorting:" << timer.elapsed(); #endif KItemRangeList itemRanges; @@ -1172,7 +1173,7 @@ void KFileItemModel::insertItems(QList& newItems) while (sourceIndexNewItems >= 0) { ItemData* newItem = newItems.at(sourceIndexNewItems); - if (sourceIndexExistingItems >= 0 && lessThan(newItem, m_itemData.at(sourceIndexExistingItems))) { + 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) { @@ -1207,7 +1208,7 @@ void KFileItemModel::insertItems(QList& newItems) emit itemsInserted(itemRanges); #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "[TIME] Inserting of" << newItems.count() << "items:" << timer.elapsed(); + qCDebug(DolphinDebug) << "[TIME] Inserting of" << newItems.count() << "items:" << timer.elapsed(); #endif } @@ -1388,14 +1389,14 @@ void KFileItemModel::emitItemsChangedAndTriggerResorting(const KItemRangeList& i // (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))) { + && 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))) { + && 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))) { + if (lessThan(m_itemData.at(index + 1), m_itemData.at(index), m_collator)) { needsResorting = true; break; } @@ -1582,7 +1583,7 @@ QHash KFileItemModel::retrieveData(const KFileItem& item, return data; } -bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const +bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b, const QCollator& collator) const { int result = 0; @@ -1627,7 +1628,7 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const } } - result = sortRoleCompare(a, b); + result = sortRoleCompare(a, b, collator); return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0; } @@ -1638,24 +1639,36 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const class KFileItemModelLessThan { public: - KFileItemModelLessThan(const KFileItemModel* model) : - m_model(model) + KFileItemModelLessThan(const KFileItemModel* model, const QCollator& collator) : + m_model(model), + m_collator(collator) + { + } + + KFileItemModelLessThan(const KFileItemModelLessThan& other) : + m_model(other.m_model), + m_collator() { + m_collator.setCaseSensitivity(other.m_collator.caseSensitivity()); + m_collator.setIgnorePunctuation(other.m_collator.ignorePunctuation()); + m_collator.setLocale(other.m_collator.locale()); + m_collator.setNumericMode(other.m_collator.numericMode()); } bool operator()(const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b) const { - return m_model->lessThan(a, b); + return m_model->lessThan(a, b, m_collator); } private: const KFileItemModel* m_model; + QCollator m_collator; }; void KFileItemModel::sort(QList::iterator begin, QList::iterator end) const { - KFileItemModelLessThan lessThan(this); + KFileItemModelLessThan lessThan(this, m_collator); if (m_sortRole == NameRole) { // Sorting by name can be expensive, in particular if natural sorting is @@ -1670,7 +1683,7 @@ void KFileItemModel::sort(QList::iterator begin, } } -int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const +int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const QCollator& collator) const { const KFileItem& itemA = a->item; const KFileItem& itemB = b->item; @@ -1733,9 +1746,8 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const case ImageSizeRole: { // Alway use a natural comparing to interpret the numbers of a string like // "1600 x 1200" for having a correct sorting. - result = KStringHandler::naturalCompare(a->values.value("imageSize").toString(), - b->values.value("imageSize").toString(), - Qt::CaseSensitive); + result = collator.compare(a->values.value("imageSize").toString(), + b->values.value("imageSize").toString()); break; } @@ -1754,14 +1766,13 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const } // Fallback #1: Compare the text of the items - result = stringCompare(itemA.text(), itemB.text()); + result = stringCompare(itemA.text(), itemB.text(), collator); if (result != 0) { return result; } // Fallback #2: KFileItem::text() may not be unique in case UDS_DISPLAY_NAME is used - result = stringCompare(itemA.name(m_caseSensitivity == Qt::CaseInsensitive), - itemB.name(m_caseSensitivity == Qt::CaseInsensitive)); + result = stringCompare(itemA.name(), itemB.name(), collator); if (result != 0) { return result; } @@ -1772,26 +1783,21 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const return QString::compare(itemA.url().url(), itemB.url().url(), Qt::CaseSensitive); } -int KFileItemModel::stringCompare(const QString& a, const QString& b) const +int KFileItemModel::stringCompare(const QString& a, const QString& b, const QCollator& collator) const { - // Taken from KDirSortFilterProxyModel (kdelibs/kfile/kdirsortfilterproxymodel.*) - // Copyright (C) 2006 by Peter Penz - // Copyright (C) 2006 by Dominic Battre - // Copyright (C) 2006 by Martin Pool + if (m_naturalSorting) { + return collator.compare(a, b); + } - if (m_caseSensitivity == Qt::CaseInsensitive) { - const int result = m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseInsensitive) - : QString::compare(a, b, Qt::CaseInsensitive); - if (result != 0) { - // Only return the result, if the strings are not equal. If they are equal by a case insensitive - // comparison, still a deterministic sort order is required. A case sensitive - // comparison is done as fallback. - return result; - } + const int result = QString::compare(a, b, collator.caseSensitivity()); + if (result != 0 || collator.caseSensitivity() == Qt::CaseSensitive) { + // Only return the result, if the strings are not equal. If they are equal by a case insensitive + // comparison, still a deterministic sort order is required. A case sensitive + // comparison is done as fallback. + return result; } - return m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseSensitive) - : QString::compare(a, b, Qt::CaseSensitive); + return QString::compare(a, b, Qt::CaseSensitive); } bool KFileItemModel::useMaximumUpdateInterval() const @@ -1799,11 +1805,6 @@ bool KFileItemModel::useMaximumUpdateInterval() const return !m_dirLister->url().isLocalFile(); } -static bool localeAwareLessThan(const QChar& c1, const QChar& c2) -{ - return QString::localeAwareCompare(c1, c2) < 0; -} - QList > KFileItemModel::nameRoleGroups() const { Q_ASSERT(!m_itemData.isEmpty()); @@ -1837,6 +1838,10 @@ QList > KFileItemModel::nameRoleGroups() const } } + auto localeAwareLessThan = [this](const QChar& c1, const QChar& c2) -> bool { + return m_collator.compare(c1, c2) < 0; + }; + std::vector::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan); if (it != lettersAtoZ.end()) { if (localeAwareLessThan(newFirstChar, *it) && it != lettersAtoZ.begin()) { @@ -1934,7 +1939,7 @@ QList > KFileItemModel::dateRoleGroups() const switch (daysDistance) { case 0: newGroupValue = i18nc("@title:group Date", "Today"); break; case 1: newGroupValue = i18nc("@title:group Date", "Yesterday"); break; - default: newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: %A", "%A")); + default: newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: dddd", "dddd")); } break; case 1: @@ -1957,20 +1962,20 @@ QList > KFileItemModel::dateRoleGroups() const const QDate lastMonthDate = currentDate.addMonths(-1); if (lastMonthDate.year() == modifiedDate.year() && lastMonthDate.month() == modifiedDate.month()) { if (daysDistance == 1) { - newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Yesterday (%B, %Y)")); + newGroupValue = modifiedTime.toString(i18nc("@title:group Date: MMMM is full month name in current locale, and yyyy is full year number", "'Yesterday' (MMMM, yyyy)")); } else if (daysDistance <= 7) { - newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: %A, %B is full month name in current locale, and %Y is full year number", "%A (%B, %Y)")); + newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: dddd, MMMM is full month name in current locale, and yyyy is full year number", "dddd (MMMM, yyyy)")); } else if (daysDistance <= 7 * 2) { - newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "One Week Ago (%B, %Y)")); + newGroupValue = modifiedTime.toString(i18nc("@title:group Date: MMMM is full month name in current locale, and yyyy is full year number", "'One Week Ago' (MMMM, yyyy)")); } else if (daysDistance <= 7 * 3) { - newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Two Weeks Ago (%B, %Y)")); + newGroupValue = modifiedTime.toString(i18nc("@title:group Date: MMMM is full month name in current locale, and yyyy is full year number", "'Two Weeks Ago' (MMMM, yyyy)")); } else if (daysDistance <= 7 * 4) { - newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Three Weeks Ago (%B, %Y)")); + newGroupValue = modifiedTime.toString(i18nc("@title:group Date: MMMM is full month name in current locale, and yyyy is full year number", "'Three Weeks Ago' (MMMM, yyyy)")); } else { - newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Earlier on %B, %Y")); + newGroupValue = modifiedTime.toString(i18nc("@title:group Date: MMMM is full month name in current locale, and yyyy is full year number", "'Earlier on' MMMM, yyyy")); } } else { - newGroupValue = modifiedTime.toString(i18nc("@title:group The month and year: %B is full month name in current locale, and %Y is full year number", "%B, %Y")); + newGroupValue = modifiedTime.toString(i18nc("@title:group The month and year: MMMM is full month name in current locale, and yyyy is full year number", "MMMM, yyyy")); } } @@ -2208,19 +2213,19 @@ bool KFileItemModel::isConsistent() const // Check if m_items and m_itemData are consistent. const KFileItem item = fileItem(i); if (item.isNull()) { - qWarning() << "Item" << i << "is null"; + qCWarning(DolphinDebug) << "Item" << i << "is null"; return false; } const int itemIndex = index(item); if (itemIndex != i) { - qWarning() << "Item" << i << "has a wrong index:" << itemIndex; + qCWarning(DolphinDebug) << "Item" << i << "has a wrong index:" << itemIndex; return false; } // Check if the items are sorted correctly. - if (i > 0 && !lessThan(m_itemData.at(i - 1), m_itemData.at(i))) { - qWarning() << "The order of items" << i - 1 << "and" << i << "is wrong:" + if (i > 0 && !lessThan(m_itemData.at(i - 1), m_itemData.at(i), m_collator)) { + qCWarning(DolphinDebug) << "The order of items" << i - 1 << "and" << i << "is wrong:" << fileItem(i - 1) << fileItem(i); return false; } @@ -2230,13 +2235,13 @@ bool KFileItemModel::isConsistent() const const ItemData* parent = data->parent; if (parent) { if (expandedParentsCount(data) != expandedParentsCount(parent) + 1) { - qWarning() << "expandedParentsCount is inconsistent for parent" << parent->item << "and child" << data->item; + qCWarning(DolphinDebug) << "expandedParentsCount is inconsistent for parent" << parent->item << "and child" << data->item; return false; } const int parentIndex = index(parent->item); if (parentIndex >= i) { - qWarning() << "Index" << parentIndex << "of parent" << parent->item << "is not smaller than index" << i << "of child" << data->item; + qCWarning(DolphinDebug) << "Index" << parentIndex << "of parent" << parent->item << "is not smaller than index" << i << "of child" << data->item; return false; } }