-#include <KGlobalSettings>
-#include <KLocale>
-#include <KStringHandler>
-#include <KDebug>
-#include <kstringhandler_deprecated.h> //TODO: port to QCollator
+#include "dolphin_generalsettings.h"
+
+#include <KLocalizedString>
+#include <KUrlMimeData>
+
+#include "dolphindebug.h"
KFileItemModel::KFileItemModel(QObject* parent) :
KItemModelBase("text", parent),
m_dirLister(0),
KFileItemModel::KFileItemModel(QObject* parent) :
KItemModelBase("text", parent),
m_dirLister(0),
m_resortAllItemsTimer->setSingleShot(true);
connect(m_resortAllItemsTimer, &QTimer::timeout, this, &KFileItemModel::resortAllItems);
m_resortAllItemsTimer->setSingleShot(true);
connect(m_resortAllItemsTimer, &QTimer::timeout, this, &KFileItemModel::resortAllItems);
- connect(KGlobalSettings::self(), &KGlobalSettings::naturalSortingChanged,
- this, &KFileItemModel::slotNaturalSortingChanged);
+ connect(GeneralSettings::self(), &GeneralSettings::sortingChoiceChanged, this, &KFileItemModel::slotSortingChoiceChanged);
while (expandedDirs.hasNext()) {
expandedDirs.next();
m_dirLister->openUrl(expandedDirs.value(), KDirLister::Reload);
while (expandedDirs.hasNext()) {
expandedDirs.next();
m_dirLister->openUrl(expandedDirs.value(), KDirLister::Reload);
- KUrl url = m_itemData[index]->item.url();
- url.setFileName(currentValues["text"].toString());
+ QUrl url = m_itemData[index]->item.url();
+ url = url.adjusted(QUrl::RemoveFilename);
+ url.setPath(url.path() + currentValues["text"].toString());
// The following code has been taken from KDirModel::mimeData()
// (kdelibs/kio/kio/kdirmodel.cpp)
// Copyright (C) 2006 David Faure <faure@kde.org>
// The following code has been taken from KDirModel::mimeData()
// (kdelibs/kio/kio/kdirmodel.cpp)
// Copyright (C) 2006 David Faure <faure@kde.org>
- const bool different = canUseMostLocalUrls && mostLocalUrls != urls;
- if (different) {
- urls.populateMimeData(mostLocalUrls, data);
- } else {
- urls.populateMimeData(data);
- }
-
+ KUrlMimeData::setUrls(urls, mostLocalUrls, data);
const int blockSize = 1000;
const int currentBlockEnd = qMin(itemsInHash + blockSize, itemCount);
for (int i = itemsInHash; i < currentBlockEnd; ++i) {
const int blockSize = 1000;
const int currentBlockEnd = qMin(itemsInHash + blockSize, itemCount);
for (int i = itemsInHash; i < currentBlockEnd; ++i) {
- 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();
for (int i = 0; i < m_itemData.count(); ++i) {
indexesForUrl.insert(m_itemData.at(i)->item.url(), i);
}
for (int i = 0; i < m_itemData.count(); ++i) {
indexesForUrl.insert(m_itemData.at(i)->item.url(), i);
}
foreach (int index, indexesForUrl.values(url)) {
const ItemData* data = m_itemData.at(index);
foreach (int index, indexesForUrl.values(url)) {
const ItemData* data = m_itemData.at(index);
if (expanded) {
m_expandedDirs.insert(targetUrl, url);
m_dirLister->openUrl(url, KDirLister::Keep);
if (expanded) {
m_expandedDirs.insert(targetUrl, url);
m_dirLister->openUrl(url, KDirLister::Keep);
- const KUrl::List previouslyExpandedChildren = m_itemData.at(index)->values.value("previouslyExpandedChildren").value<KUrl::List>();
- foreach (const KUrl& url, previouslyExpandedChildren) {
- m_urlsToExpand.insert(url);
+ const QVariantList previouslyExpandedChildren = m_itemData.at(index)->values.value("previouslyExpandedChildren").value<QVariantList>();
+ foreach (const QVariant& var, previouslyExpandedChildren) {
+ m_urlsToExpand.insert(var.toUrl());
int childIndex = firstChildIndex;
while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) {
ItemData* itemData = m_itemData.at(childIndex);
if (itemData->values.value("isExpanded").toBool()) {
int childIndex = firstChildIndex;
while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) {
ItemData* itemData = m_itemData.at(childIndex);
if (itemData->values.value("isExpanded").toBool()) {
m_expandedDirs.remove(targetUrl);
m_dirLister->stop(url); // TODO: try to unit-test this, see https://bugs.kde.org/show_bug.cgi?id=332102#c11
expandedChildren.append(targetUrl);
m_expandedDirs.remove(targetUrl);
m_dirLister->stop(url); // TODO: try to unit-test this, see https://bugs.kde.org/show_bug.cgi?id=332102#c11
expandedChildren.append(targetUrl);
// expanded is added to m_urlsToExpand. KDirLister
// does not care whether the parent-URL has already been
// expanded.
// expanded is added to m_urlsToExpand. KDirLister
// does not care whether the parent-URL has already been
// expanded.
const QStringList subDirs = url.path().mid(pos).split(QDir::separator());
for (int i = 0; i < subDirs.count() - 1; ++i) {
const QStringList subDirs = url.path().mid(pos).split(QDir::separator());
for (int i = 0; i < subDirs.count() - 1; ++i) {
m_urlsToExpand.insert(urlToExpand);
}
// KDirLister::open() must called at least once to trigger an initial
// loading. The pending URLs that must be restored are handled
// in slotCompleted().
m_urlsToExpand.insert(urlToExpand);
}
// KDirLister::open() must called at least once to trigger an initial
// loading. The pending URLs that must be restored are handled
// in slotCompleted().
+void KFileItemModel::loadSortingSettings()
+{
+ using Choice = GeneralSettings::EnumSortingChoice;
+ switch (GeneralSettings::sortingChoice()) {
+ case Choice::NaturalSorting:
+ m_naturalSorting = true;
+ m_collator.setCaseSensitivity(Qt::CaseInsensitive);
+ break;
+ case Choice::CaseSensitiveSorting:
+ m_naturalSorting = false;
+ m_collator.setCaseSensitivity(Qt::CaseSensitive);
+ break;
+ case Choice::CaseInsensitiveSorting:
+ m_naturalSorting = false;
+ m_collator.setCaseSensitivity(Qt::CaseInsensitive);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
- kDebug() << "===========================================================";
- kDebug() << "Resorting" << itemCount << "items";
+ qCDebug(DolphinDebug) << "===========================================================";
+ qCDebug(DolphinDebug) << "Resorting" << itemCount << "items";
#endif
// Remember the order of the current URLs so
// that it can be determined which indexes have
// been moved because of the resorting.
#endif
// Remember the order of the current URLs so
// that it can be determined which indexes have
// been moved because of the resorting.
oldUrls.reserve(itemCount);
foreach (const ItemData* itemData, m_itemData) {
oldUrls.append(itemData->item.url());
oldUrls.reserve(itemCount);
foreach (const ItemData* itemData, m_itemData) {
oldUrls.append(itemData->item.url());
// 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.
// 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.
// might result in emitting the same items twice due to the Keep-parameter.
// This case happens if an item gets expanded, collapsed and expanded again
// before the items could be loaded for the first expansion.
// might result in emitting the same items twice due to the Keep-parameter.
// This case happens if an item gets expanded, collapsed and expanded again
// before the items could be loaded for the first expansion.
- kDebug() << "===========================================================";
- kDebug() << "Inserting" << newItems.count() << "items";
+ qCDebug(DolphinDebug) << "===========================================================";
+ qCDebug(DolphinDebug) << "Inserting" << newItems.count() << "items";
sort(newItems.begin(), newItems.end());
#ifdef KFILEITEMMODEL_DEBUG
sort(newItems.begin(), newItems.end());
#ifdef KFILEITEMMODEL_DEBUG
// Move an existing item to its new position. If any new items
// are behind it, push the item range to itemRanges.
if (rangeCount > 0) {
// Move an existing item to its new position. If any new items
// are behind it, push the item range to itemRanges.
if (rangeCount > 0) {
m_itemData.erase(m_itemData.end() - removedItemsCount, m_itemData.end());
// The indexes in m_items are not correct anymore. Therefore, we clear m_items.
m_itemData.erase(m_itemData.end() - removedItemsCount, m_itemData.end());
// The indexes in m_items are not correct anymore. Therefore, we clear m_items.
{
if (m_sortRole == TypeRole) {
// Try to resolve the MIME-types synchronously to prevent a reordering of
{
if (m_sortRole == TypeRole) {
// Try to resolve the MIME-types synchronously to prevent a reordering of
// (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
// (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
if (m_requestRole[DestinationRole]) {
QString destination = item.linkDest();
if (destination.isEmpty()) {
if (m_requestRole[DestinationRole]) {
QString destination = item.linkDest();
if (destination.isEmpty()) {
- 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());
+ }
+
+ ~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)
};
void KFileItemModel::sort(QList<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::iterator end) const
{
};
void KFileItemModel::sort(QList<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::iterator end) const
{
if (m_sortRole == NameRole) {
// Sorting by name can be expensive, in particular if natural sorting is
if (m_sortRole == NameRole) {
// Sorting by name can be expensive, in particular if natural sorting is
case ImageSizeRole: {
// Alway use a natural comparing to interpret the numbers of a string like
// "1600 x 1200" for having a correct sorting.
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());
- result = stringCompare(itemA.name(m_caseSensitivity == Qt::CaseInsensitive),
- itemB.name(m_caseSensitivity == Qt::CaseInsensitive));
+ result = stringCompare(itemA.name(), itemB.name(), collator);
- // Taken from KDirSortFilterProxyModel (kdelibs/kfile/kdirsortfilterproxymodel.*)
- // Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at>
- // Copyright (C) 2006 by Dominic Battre <dominic@battre.de>
- // Copyright (C) 2006 by Martin Pool <mbp@canonical.com>
+ 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);
QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
{
Q_ASSERT(!m_itemData.isEmpty());
QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
{
Q_ASSERT(!m_itemData.isEmpty());
if (newFirstChar.isLetter()) {
// Try to find a matching group in the range 'A' to 'Z'.
static std::vector<QChar> lettersAtoZ;
if (newFirstChar.isLetter()) {
// Try to find a matching group in the range 'A' to 'Z'.
static std::vector<QChar> lettersAtoZ;
if (lettersAtoZ.empty()) {
for (char c = 'A'; c <= 'Z'; ++c) {
lettersAtoZ.push_back(QLatin1Char(c));
}
}
if (lettersAtoZ.empty()) {
for (char c = 'A'; c <= 'Z'; ++c) {
lettersAtoZ.push_back(QLatin1Char(c));
}
}
std::vector<QChar>::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan);
if (it != lettersAtoZ.end()) {
if (localeAwareLessThan(newFirstChar, *it) && it != lettersAtoZ.begin()) {
std::vector<QChar>::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan);
if (it != lettersAtoZ.end()) {
if (localeAwareLessThan(newFirstChar, *it) && it != lettersAtoZ.begin()) {
switch (daysDistance / 7) {
case 0:
switch (daysDistance) {
case 0: newGroupValue = i18nc("@title:group Date", "Today"); break;
case 1: newGroupValue = i18nc("@title:group Date", "Yesterday"); break;
switch (daysDistance / 7) {
case 0:
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 Date: The week day name: dddd", "dddd"));
+ newGroupValue = i18nc("Can be used to script translation of \"dddd\""
+ "with context @title:group Date", "%1", newGroupValue);
- 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)"));
+ newGroupValue = i18nc("Can be used to script translation of "
+ "\"'Yesterday' (MMMM, yyyy)\" with context @title:group Date",
+ "%1", newGroupValue);
- 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 Date: "
+ "The week day name: dddd, MMMM is full month name "
+ "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",
+ "%1", newGroupValue);
- 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)"));
+ newGroupValue = i18nc("Can be used to script translation of "
+ "\"'One Week Ago' (MMMM, yyyy)\" with context @title:group Date",
+ "%1", newGroupValue);
- 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)"));
+ newGroupValue = i18nc("Can be used to script translation of "
+ "\"'Two Weeks Ago' (MMMM, yyyy)\" with context @title:group Date",
+ "%1", newGroupValue);
- 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)"));
+ newGroupValue = i18nc("Can be used to script translation of "
+ "\"'Three Weeks Ago' (MMMM, yyyy)\" with context @title:group Date",
+ "%1", newGroupValue);
- 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"));
+ newGroupValue = i18nc("Can be used to script translation of "
+ "\"'Earlier on' MMMM, yyyy\" with context @title:group Date",
+ "%1", newGroupValue);
- 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"));
+ newGroupValue = i18nc("Can be used to script translation of "
+ "\"MMMM, yyyy\" with context @title:group Date",
+ "%1", newGroupValue);
{ "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 },
{ "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
{ "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 },
{ "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
- { "copiedFrom", CopiedFromRole, I18N_NOOP2_NOSTRIP("@label", "Copied From"), I18N_NOOP2_NOSTRIP("@label", "Other"), true, 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 },
{ "owner", OwnerRole, I18N_NOOP2_NOSTRIP("@label", "Owner"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
{ "group", GroupRole, I18N_NOOP2_NOSTRIP("@label", "User Group"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
{ "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
{ "owner", OwnerRole, I18N_NOOP2_NOSTRIP("@label", "Owner"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
{ "group", GroupRole, I18N_NOOP2_NOSTRIP("@label", "User Group"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
bool KFileItemModel::isConsistent() const
{
// m_items may contain less items than m_itemData because m_items
bool KFileItemModel::isConsistent() const
{
// m_items may contain less items than m_itemData because m_items
- 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:"
const ItemData* parent = data->parent;
if (parent) {
if (expandedParentsCount(data) != expandedParentsCount(parent) + 1) {
const ItemData* parent = data->parent;
if (parent) {
if (expandedParentsCount(data) != expandedParentsCount(parent) + 1) {