#include "kfileitemmodel.h"
-#include <KGlobalSettings>
+#include "dolphin_generalsettings.h"
+
#include <KLocalizedString>
-#include <KStringHandler>
-#include <KDebug>
+#include "dolphindebug.h"
#include "private/kfileitemmodelsortalgorithm.h"
#include "private/kfileitemmodeldirlister.h"
KFileItemModel::KFileItemModel(QObject* parent) :
KItemModelBase("text", parent),
m_dirLister(0),
- m_naturalSorting(KGlobalSettings::naturalSorting()),
m_sortDirsFirst(true),
m_sortRole(NameRole),
m_sortingProgressPercent(-1),
m_expandedDirs(),
m_urlsToExpand()
{
- m_collator.setCaseSensitivity(Qt::CaseInsensitive);
m_collator.setNumericMode(true);
+ loadSortingSettings();
+
m_dirLister = new KFileItemModelDirLister(this);
m_dirLister->setDelayedMimeTypes(true);
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);
}
KFileItemModel::~KFileItemModel()
}
#ifdef KFILEITEMMODEL_DEBUG
- kDebug() << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed();
+ qCDebug(DolphinDebug) << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed();
#endif
}
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<QUrl, int> indexesForUrl;
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;
}
}
}
resortAllItems();
}
+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();
+ }
+}
+
void KFileItemModel::resortAllItems()
{
m_resortAllItemsTimer->stop();
#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
}
#ifdef KFILEITEMMODEL_DEBUG
- kDebug() << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed();
+ qCDebug(DolphinDebug) << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed();
#endif
}
{
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
void KFileItemModel::slotClear()
{
#ifdef KFILEITEMMODEL_DEBUG
- kDebug() << "Clearing all items";
+ qCDebug(DolphinDebug) << "Clearing all items";
#endif
qDeleteAll(m_filteredItems.values());
m_expandedDirs.clear();
}
-void KFileItemModel::slotNaturalSortingChanged()
+void KFileItemModel::slotSortingChoiceChanged()
{
- m_naturalSorting = KGlobalSettings::naturalSorting();
+ loadSortingSettings();
resortAllItems();
}
#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();
sort(newItems.begin(), newItems.end());
#ifdef KFILEITEMMODEL_DEBUG
- kDebug() << "[TIME] Sorting:" << timer.elapsed();
+ qCDebug(DolphinDebug) << "[TIME] Sorting:" << timer.elapsed();
#endif
KItemRangeList itemRanges;
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) {
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
}
// (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;
}
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;
}
}
- result = sortRoleCompare(a, b);
+ result = sortRoleCompare(a, b, collator);
return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0;
}
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<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::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
}
}
-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;
case ImageSizeRole: {
// Alway use a natural comparing to interpret the numbers of a string like
// "1600 x 1200" for having a correct sorting.
- result = m_collator.compare(a->values.value("imageSize").toString(),
- b->values.value("imageSize").toString());
+ result = collator.compare(a->values.value("imageSize").toString(),
+ b->values.value("imageSize").toString());
break;
}
}
// 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(), itemB.name());
+ result = stringCompare(itemA.name(), itemB.name(), collator);
if (result != 0) {
return result;
}
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
{
if (m_naturalSorting) {
- return m_collator.compare(a, b);
+ return collator.compare(a, b);
}
- const int result = QString::compare(a, b, m_collator.caseSensitivity());
- if (result != 0 || m_collator.caseSensitivity() == Qt::CaseSensitive) {
+ 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.
const int daysDistance = modifiedDate.daysTo(currentDate);
QString newGroupValue;
- if (currentDate.year() == modifiedDate.year() && currentDate.month() == modifiedDate.month()) {
+ if (currentDate.year() == modifiedDate.year() &&
+ currentDate.month() == modifiedDate.month()) {
+
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);
}
break;
case 1:
}
} else {
const QDate lastMonthDate = currentDate.addMonths(-1);
- if (lastMonthDate.year() == modifiedDate.year() && lastMonthDate.month() == modifiedDate.month()) {
+ 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)"));
+ 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 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);
} 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)"));
+ 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: %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);
} 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)"));
+ 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: %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);
}
} 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"));
+ 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 },
- { "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 },
// 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;
}
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;
}
}