#include "kfileitemmodel.h"
-#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"
#include "private/kfileitemmodelsortalgorithm.h"
#include "private/kfileitemmodeldirlister.h"
-#include <QApplication>
+#include <QElapsedTimer>
#include <QMimeData>
#include <QTimer>
#include <QWidget>
-#include <algorithm>
-#include <vector>
-
// #define KFILEITEMMODEL_DEBUG
KFileItemModel::KFileItemModel(QObject* parent) :
KItemModelBase("text", parent),
- m_dirLister(0),
- m_naturalSorting(KGlobalSettings::naturalSorting()),
+ m_dirLister(nullptr),
m_sortDirsFirst(true),
m_sortRole(NameRole),
m_sortingProgressPercent(-1),
m_roles(),
- m_caseSensitivity(Qt::CaseInsensitive),
m_itemData(),
m_items(),
m_filter(),
m_filteredItems(),
m_requestRole(),
- m_maximumUpdateIntervalTimer(0),
- m_resortAllItemsTimer(0),
+ m_maximumUpdateIntervalTimer(nullptr),
+ m_resortAllItemsTimer(nullptr),
m_pendingItemsToInsert(),
m_groups(),
m_expandedDirs(),
m_urlsToExpand()
{
+ m_collator.setNumericMode(true);
+
+ loadSortingSettings();
+
m_dirLister = new KFileItemModelDirLister(this);
m_dirLister->setDelayedMimeTypes(true);
m_dirLister->setMainWindow(parentWidget->window());
}
- connect(m_dirLister, SIGNAL(started(KUrl)), this, SIGNAL(directoryLoadingStarted()));
- connect(m_dirLister, SIGNAL(canceled()), this, SLOT(slotCanceled()));
- connect(m_dirLister, SIGNAL(completed(KUrl)), this, SLOT(slotCompleted()));
- connect(m_dirLister, SIGNAL(itemsAdded(KUrl,KFileItemList)), this, SLOT(slotItemsAdded(KUrl,KFileItemList)));
- connect(m_dirLister, SIGNAL(itemsDeleted(KFileItemList)), this, SLOT(slotItemsDeleted(KFileItemList)));
- connect(m_dirLister, SIGNAL(refreshItems(QList<QPair<KFileItem,KFileItem> >)), this, SLOT(slotRefreshItems(QList<QPair<KFileItem,KFileItem> >)));
- connect(m_dirLister, SIGNAL(clear()), this, SLOT(slotClear()));
- connect(m_dirLister, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString)));
- connect(m_dirLister, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString)));
- connect(m_dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SIGNAL(directoryRedirection(KUrl,KUrl)));
- connect(m_dirLister, SIGNAL(urlIsFileError(KUrl)), this, SIGNAL(urlIsFileError(KUrl)));
+ connect(m_dirLister, &KFileItemModelDirLister::started, this, &KFileItemModel::directoryLoadingStarted);
+ connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)()>(&KFileItemModelDirLister::canceled), this, &KFileItemModel::slotCanceled);
+ connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)(const QUrl&)>(&KFileItemModelDirLister::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);
+ connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)()>(&KFileItemModelDirLister::clear), this, &KFileItemModel::slotClear);
+ connect(m_dirLister, &KFileItemModelDirLister::infoMessage, this, &KFileItemModel::infoMessage);
+ connect(m_dirLister, &KFileItemModelDirLister::errorMessage, this, &KFileItemModel::errorMessage);
+ connect(m_dirLister, &KFileItemModelDirLister::percent, this, &KFileItemModel::directoryLoadingProgress);
+ connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)(const QUrl&, const QUrl&)>(&KFileItemModelDirLister::redirection), this, &KFileItemModel::directoryRedirection);
+ connect(m_dirLister, &KFileItemModelDirLister::urlIsFileError, this, &KFileItemModel::urlIsFileError);
// Apply default roles that should be determined
resetRoles();
m_roles.insert("text");
m_roles.insert("isDir");
m_roles.insert("isLink");
+ m_roles.insert("isHidden");
// For slow KIO-slaves like used for searching it makes sense to show results periodically even
// before the completed() or canceled() signal has been emitted.
m_maximumUpdateIntervalTimer = new QTimer(this);
m_maximumUpdateIntervalTimer->setInterval(2000);
m_maximumUpdateIntervalTimer->setSingleShot(true);
- connect(m_maximumUpdateIntervalTimer, SIGNAL(timeout()), this, SLOT(dispatchPendingItemsToInsert()));
+ connect(m_maximumUpdateIntervalTimer, &QTimer::timeout, this, &KFileItemModel::dispatchPendingItemsToInsert);
// When changing the value of an item which represents the sort-role a resorting must be
// triggered. Especially in combination with KFileItemModelRolesUpdater this might be done
m_resortAllItemsTimer = new QTimer(this);
m_resortAllItemsTimer->setInterval(500);
m_resortAllItemsTimer->setSingleShot(true);
- connect(m_resortAllItemsTimer, SIGNAL(timeout()), this, SLOT(resortAllItems()));
+ connect(m_resortAllItemsTimer, &QTimer::timeout, this, &KFileItemModel::resortAllItems);
- connect(KGlobalSettings::self(), SIGNAL(naturalSortingChanged()), this, SLOT(slotNaturalSortingChanged()));
+ connect(GeneralSettings::self(), &GeneralSettings::sortingChoiceChanged, this, &KFileItemModel::slotSortingChoiceChanged);
}
KFileItemModel::~KFileItemModel()
{
qDeleteAll(m_itemData);
- qDeleteAll(m_filteredItems.values());
+ qDeleteAll(m_filteredItems);
qDeleteAll(m_pendingItemsToInsert);
}
-void KFileItemModel::loadDirectory(const KUrl& url)
+void KFileItemModel::loadDirectory(const QUrl &url)
{
m_dirLister->openUrl(url);
}
-void KFileItemModel::refreshDirectory(const KUrl& url)
+void KFileItemModel::refreshDirectory(const QUrl &url)
{
// Refresh all expanded directories first (Bug 295300)
- QHashIterator<KUrl, KUrl> expandedDirs(m_expandedDirs);
+ QHashIterator<QUrl, QUrl> expandedDirs(m_expandedDirs);
while (expandedDirs.hasNext()) {
expandedDirs.next();
m_dirLister->openUrl(expandedDirs.value(), KDirLister::Reload);
m_dirLister->openUrl(url, KDirLister::Reload);
}
-KUrl KFileItemModel::directory() const
+QUrl KFileItemModel::directory() const
{
return m_dirLister->url();
}
m_itemData[index]->values = currentValues;
if (changedRoles.contains("text")) {
- 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());
m_itemData[index]->item.setUrl(url);
}
m_dirLister->setShowingDotFiles(show);
m_dirLister->emitChanges();
if (show) {
- slotCompleted();
+ dispatchPendingItemsToInsert();
}
}
// The following code has been taken from KDirModel::mimeData()
// (kdelibs/kio/kio/kdirmodel.cpp)
// Copyright (C) 2006 David Faure <faure@kde.org>
- KUrl::List urls;
- KUrl::List mostLocalUrls;
+ QList<QUrl> urls;
+ QList<QUrl> mostLocalUrls;
bool canUseMostLocalUrls = true;
- const ItemData* lastAddedItem = 0;
+ const ItemData* lastAddedItem = nullptr;
- foreach (int index, indexes) {
+ for (int index : indexes) {
const ItemData* itemData = m_itemData.at(index);
const ItemData* parent = itemData->parent;
lastAddedItem = itemData;
const KFileItem& item = itemData->item;
if (!item.isNull()) {
- urls << item.targetUrl();
+ urls << item.url();
bool isLocal;
mostLocalUrls << item.mostLocalUrl(isLocal);
}
}
- const bool different = canUseMostLocalUrls && mostLocalUrls != urls;
- if (different) {
- urls.populateMimeData(mostLocalUrls, data);
- } else {
- urls.populateMimeData(data);
- }
-
+ KUrlMimeData::setUrls(urls, mostLocalUrls, data);
return data;
}
switch (typeForRole(sortRole())) {
case NameRole: m_groups = nameRoleGroups(); break;
case SizeRole: m_groups = sizeRoleGroups(); break;
- case DateRole: m_groups = dateRoleGroups(); break;
+ case ModificationTimeRole:
+ m_groups = timeRoleGroups([](const ItemData *item) {
+ return item->item.time(KFileItem::ModificationTime);
+ });
+ break;
+ case CreationTimeRole:
+ m_groups = timeRoleGroups([](const ItemData *item) {
+ return item->item.time(KFileItem::CreationTime);
+ });
+ break;
+ case AccessTimeRole:
+ m_groups = timeRoleGroups([](const ItemData *item) {
+ return item->item.time(KFileItem::AccessTime);
+ });
+ break;
+ case DeletionTimeRole:
+ m_groups = timeRoleGroups([](const ItemData *item) {
+ return item->values.value("deletiontime").toDateTime();
+ });
+ break;
case PermissionsRole: m_groups = permissionRoleGroups(); break;
case RatingRole: m_groups = ratingRoleGroups(); break;
default: m_groups = genericStringRoleGroups(sortRole()); break;
}
#ifdef KFILEITEMMODEL_DEBUG
- kDebug() << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed();
+ qCDebug(DolphinDebug) << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed();
#endif
}
return KFileItem();
}
-KFileItem KFileItemModel::fileItem(const KUrl& url) const
+KFileItem KFileItemModel::fileItem(const QUrl &url) const
{
const int indexForUrl = index(url);
if (indexForUrl >= 0) {
int KFileItemModel::index(const KFileItem& item) const
{
- return index(KUrl(item.url()));
+ return index(item.url());
}
-int KFileItemModel::index(const KUrl& url) const
+int KFileItemModel::index(const QUrl& url) const
{
- KUrl urlToFind = url;
- urlToFind.adjustPath(KUrl::RemoveTrailingSlash);
+ const QUrl urlToFind = url.adjusted(QUrl::StripTrailingSlash);
const int itemCount = m_itemData.count();
int itemsInHash = m_items.count();
const int blockSize = 1000;
const int currentBlockEnd = qMin(itemsInHash + blockSize, itemCount);
for (int i = itemsInHash; i < currentBlockEnd; ++i) {
- const KUrl nextUrl = m_itemData.at(i)->item.url();
+ const QUrl nextUrl = m_itemData.at(i)->item.url();
m_items.insert(nextUrl, i);
}
index = m_items.value(urlToFind, -1);
}
- Q_ASSERT(index >= 0 || m_items.count() == m_itemData.count());
+ if (index < 0) {
+ // The item could not be found, even though all items from m_itemData
+ // should be in m_items now. We print some diagnostic information which
+ // might help to find the cause of the problem, but only once. This
+ // prevents that obtaining and printing the debugging information
+ // wastes CPU cycles and floods the shell or .xsession-errors.
+ static bool printDebugInfo = true;
+
+ if (m_items.count() != m_itemData.count() && printDebugInfo) {
+ printDebugInfo = false;
+
+ 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;
+ for (int i = 0; i < m_itemData.count(); ++i) {
+ indexesForUrl.insert(m_itemData.at(i)->item.url(), i);
+ }
+
+ foreach (const QUrl& url, indexesForUrl.uniqueKeys()) {
+ if (indexesForUrl.count(url) > 1) {
+ qCWarning(DolphinDebug) << "Multiple items found with the URL" << url;
+
+ auto it = indexesForUrl.find(url);
+ while (it != indexesForUrl.end() && it.key() == url) {
+ const ItemData* data = m_itemData.at(it.value());
+ qCWarning(DolphinDebug) << "index" << it.value() << ":" << data->item;
+ if (data->parent) {
+ qCWarning(DolphinDebug) << "parent" << data->parent->item;
+ }
+ ++it;
+ }
+ }
+ }
+ }
+ }
return index;
}
if (m_roles == roles) {
return;
}
+
+ const QSet<QByteArray> changedRoles = (roles - m_roles) + (m_roles - roles);
m_roles = roles;
if (count() > 0) {
m_itemData[i]->values = retrieveData(m_itemData.at(i)->item, m_itemData.at(i)->parent);
}
- kWarning() << "TODO: Emitting itemsChanged() with no information what has changed!";
- emit itemsChanged(KItemRangeList() << KItemRange(0, count()), QSet<QByteArray>());
+ emit itemsChanged(KItemRangeList() << KItemRange(0, count()), changedRoles);
}
// Clear the 'values' of all filtered items. They will be re-populated with the
}
const KFileItem item = m_itemData.at(index)->item;
- const KUrl url = item.url();
- const KUrl targetUrl = item.targetUrl();
+ const QUrl url = item.url();
+ const QUrl targetUrl = item.targetUrl();
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());
}
} else {
+ // Note that there might be (indirect) children of the folder which is to be collapsed in
+ // m_pendingItemsToInsert. To prevent that they will be inserted into the model later,
+ // possibly without a parent, which might result in a crash, we insert all pending items
+ // right now. All new items which would be without a parent will then be removed.
+ dispatchPendingItemsToInsert();
+
+ // Check if the index of the collapsed folder has changed. If that is the case, then items
+ // were inserted before the collapsed folder, and its index needs to be updated.
+ if (m_itemData.at(index)->item != item) {
+ index = this->index(item);
+ }
+
m_expandedDirs.remove(targetUrl);
m_dirLister->stop(url);
const int itemCount = m_itemData.count();
const int firstChildIndex = index + 1;
- KUrl::List expandedChildren;
+ QVariantList expandedChildren;
int childIndex = firstChildIndex;
while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) {
ItemData* itemData = m_itemData.at(childIndex);
if (itemData->values.value("isExpanded").toBool()) {
- const KUrl targetUrl = itemData->item.targetUrl();
+ const QUrl targetUrl = itemData->item.targetUrl();
+ const QUrl url = itemData->item.url();
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);
}
++childIndex;
return 0;
}
-QSet<KUrl> KFileItemModel::expandedDirectories() const
+QSet<QUrl> KFileItemModel::expandedDirectories() const
{
- return m_expandedDirs.values().toSet();
+ QSet<QUrl> result;
+ const auto dirs = m_expandedDirs;
+ for (const auto &dir : dirs) {
+ result.insert(dir);
+ }
+ return result;
}
-void KFileItemModel::restoreExpandedDirectories(const QSet<KUrl>& urls)
+void KFileItemModel::restoreExpandedDirectories(const QSet<QUrl> &urls)
{
m_urlsToExpand = urls;
}
-void KFileItemModel::expandParentDirectories(const KUrl& url)
+void KFileItemModel::expandParentDirectories(const QUrl &url)
{
- const int pos = m_dirLister->url().path().length();
// Assure that each sub-path of the URL that should be
// expanded is added to m_urlsToExpand. KDirLister
// does not care whether the parent-URL has already been
// expanded.
- KUrl urlToExpand = m_dirLister->url();
- const QStringList subDirs = url.path().mid(pos).split(QDir::separator());
+ QUrl urlToExpand = m_dirLister->url();
+ const int pos = urlToExpand.path().length();
+
+ // first subdir can be empty, if m_dirLister->url().path() does not end with '/'
+ // this happens if baseUrl is not root but a home directory, see FoldersPanel,
+ // so using QString::SkipEmptyParts
+ const QStringList subDirs = url.path().mid(pos).split(QDir::separator(), QString::SkipEmptyParts);
for (int i = 0; i < subDirs.count() - 1; ++i) {
- urlToExpand.addPath(subDirs.at(i));
+ QString path = urlToExpand.path();
+ if (!path.endsWith(QLatin1Char('/'))) {
+ path.append(QLatin1Char('/'));
+ }
+ urlToExpand.setPath(path + subDirs.at(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().
- QSetIterator<KUrl> it2(m_urlsToExpand);
+ QSetIterator<QUrl> it2(m_urlsToExpand);
while (it2.hasNext()) {
const int idx = index(it2.next());
if (idx >= 0 && !isExpanded(idx)) {
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
// that it can be determined which indexes have
// been moved because of the resorting.
- QList<KUrl> oldUrls;
+ QList<QUrl> oldUrls;
oldUrls.reserve(itemCount);
foreach (const ItemData* itemData, m_itemData) {
oldUrls.append(itemData->item.url());
}
#ifdef KFILEITEMMODEL_DEBUG
- kDebug() << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed();
+ qCDebug(DolphinDebug) << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed();
#endif
}
// 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.
- foreach (const KUrl& url, m_urlsToExpand) {
+ foreach (const QUrl& url, m_urlsToExpand) {
const int indexForUrl = index(url);
if (indexForUrl >= 0) {
m_urlsToExpand.remove(url);
emit directoryLoadingCanceled();
}
-void KFileItemModel::slotItemsAdded(const KUrl& directoryUrl, const KFileItemList& items)
+void KFileItemModel::slotItemsAdded(const QUrl &directoryUrl, const KFileItemList& items)
{
Q_ASSERT(!items.isEmpty());
- KUrl parentUrl;
+ QUrl parentUrl;
if (m_expandedDirs.contains(directoryUrl)) {
parentUrl = m_expandedDirs.value(directoryUrl);
} else {
- parentUrl = directoryUrl;
- parentUrl.adjustPath(KUrl::RemoveTrailingSlash);
+ parentUrl = directoryUrl.adjusted(QUrl::StripTrailingSlash);
}
if (m_requestRole[ExpandedParentsCountRole]) {
// 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.
- if (index(KUrl(items.first().url())) >= 0) {
+ if (index(items.first().url()) >= 0) {
// The items are already part of the model.
return;
}
{
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());
+ qDeleteAll(m_filteredItems);
m_filteredItems.clear();
m_groups.clear();
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) {
}
// 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 KUrl&) is called.
+ // It will be re-populated with the updated indices if index(const QUrl&) is called.
m_items.clear();
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
}
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 KUrl&) is called.
+ // It will be re-populated with the updated indices if index(const QUrl&) is called.
m_items.clear();
emit itemsRemoved(itemRanges);
}
-QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const
+QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const QUrl& parentUrl, const KFileItemList& items) const
{
if (m_sortRole == TypeRole) {
// Try to resolve the MIME-types synchronously to prevent a reordering of
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.
foreach (ItemData* itemData, itemDataList) {
// (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;
}
// 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);
// 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");
data.insert(sharedValue("isLink"), true);
}
+ if (m_requestRole[IsHiddenRole] && item.isHidden()) {
+ data.insert(sharedValue("isHidden"), true);
+ }
+
if (m_requestRole[NameRole]) {
data.insert(sharedValue("text"), item.text());
}
data.insert(sharedValue("size"), item.size());
}
- if (m_requestRole[DateRole]) {
+ if (m_requestRole[ModificationTimeRole]) {
// Don't use KFileItem::timeString() as this is too expensive when
// having several thousands of items. Instead the formatting of the
// date-time will be done on-demand by the view when the date will be shown.
const QDateTime dateTime = item.time(KFileItem::ModificationTime);
- data.insert(sharedValue("date"), dateTime);
+ data.insert(sharedValue("modificationtime"), dateTime);
+ }
+
+ if (m_requestRole[CreationTimeRole]) {
+ // Don't use KFileItem::timeString() as this is too expensive when
+ // having several thousands of items. Instead the formatting of the
+ // date-time will be done on-demand by the view when the date will be shown.
+ const QDateTime dateTime = item.time(KFileItem::CreationTime);
+ data.insert(sharedValue("creationtime"), dateTime);
+ }
+
+ if (m_requestRole[AccessTimeRole]) {
+ // Don't use KFileItem::timeString() as this is too expensive when
+ // having several thousands of items. Instead the formatting of the
+ // date-time will be done on-demand by the view when the date will be shown.
+ const QDateTime dateTime = item.time(KFileItem::AccessTime);
+ data.insert(sharedValue("accesstime"), dateTime);
}
if (m_requestRole[PermissionsRole]) {
if (m_requestRole[DestinationRole]) {
QString destination = item.linkDest();
if (destination.isEmpty()) {
- destination = QLatin1String("-");
+ destination = QStringLiteral("-");
}
data.insert(sharedValue("destination"), destination);
}
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);
}
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());
+ }
+
+ ~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)
+ {
+ m_model = other.m_model;
+ m_collator = other.m_collator;
+ return *this;
+ }
+
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;
break;
}
- case DateRole: {
+ case ModificationTimeRole: {
const QDateTime dateTimeA = itemA.time(KFileItem::ModificationTime);
const QDateTime dateTimeB = itemB.time(KFileItem::ModificationTime);
if (dateTimeA < dateTimeB) {
break;
}
+ case CreationTimeRole: {
+ const QDateTime dateTimeA = itemA.time(KFileItem::CreationTime);
+ const QDateTime dateTimeB = itemB.time(KFileItem::CreationTime);
+ if (dateTimeA < dateTimeB) {
+ result = -1;
+ } else if (dateTimeA > dateTimeB) {
+ result = +1;
+ }
+ break;
+ }
+
+ case DeletionTimeRole: {
+ const QDateTime dateTimeA = a->values.value("deletiontime").toDateTime();
+ const QDateTime dateTimeB = b->values.value("deletiontime").toDateTime();
+ if (dateTimeA < dateTimeB) {
+ result = -1;
+ } else if (dateTimeA > dateTimeB) {
+ result = +1;
+ }
+ break;
+ }
+
case RatingRole: {
result = a->values.value("rating").toInt() - b->values.value("rating").toInt();
break;
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;
}
}
// 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;
}
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 <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);
}
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<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;
+ lettersAtoZ.reserve('Z' - 'A' + 1);
if (lettersAtoZ.empty()) {
for (char c = 'A'; c <= 'Z'; ++c) {
lettersAtoZ.push_back(QLatin1Char(c));
}
}
+ auto localeAwareLessThan = [this](QChar c1, QChar c2) -> bool {
+ return m_collator.compare(c1, c2) < 0;
+ };
+
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()) {
return groups;
}
-QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
+QList<QPair<int, QVariant> > KFileItemModel::timeRoleGroups(std::function<QDateTime(const ItemData *)> fileTimeCb) const
{
Q_ASSERT(!m_itemData.isEmpty());
const QDate currentDate = QDate::currentDate();
- QDate previousModifiedDate;
+ QDate previousFileDate;
QString groupValue;
for (int i = 0; i <= maxIndex; ++i) {
if (isChildItem(i)) {
continue;
}
- const QDateTime modifiedTime = m_itemData.at(i)->item.time(KFileItem::ModificationTime);
- const QDate modifiedDate = modifiedTime.date();
- if (modifiedDate == previousModifiedDate) {
+ const QDateTime fileTime = fileTimeCb(m_itemData.at(i));
+ const QDate fileDate = fileTime.date();
+ if (fileDate == previousFileDate) {
// The current item is in the same group as the previous item
continue;
}
- previousModifiedDate = modifiedDate;
+ previousFileDate = fileDate;
- const int daysDistance = modifiedDate.daysTo(currentDate);
+ const int daysDistance = fileDate.daysTo(currentDate);
QString newGroupValue;
- if (currentDate.year() == modifiedDate.year() && currentDate.month() == modifiedDate.month()) {
+ if (currentDate.year() == fileDate.year() &&
+ currentDate.month() == fileDate.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 = fileTime.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() == fileDate.year() &&
+ lastMonthDate.month() == fileDate.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 = fileTime.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 = 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",
+ "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 = fileTime.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 = fileTime.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 = fileTime.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 = fileTime.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 = fileTime.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);
}
}
{
static const RoleInfoMap rolesInfoMap[] = {
// | role | roleType | role translation | group translation | requires Baloo | requires indexer
- { 0, NoRole, 0, 0, 0, 0, false, false },
- { "text", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), 0, 0, false, false },
- { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), 0, 0, false, false },
- { "date", DateRole, I18N_NOOP2_NOSTRIP("@label", "Date"), 0, 0, false, false },
- { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), 0, 0, false, false },
- { "rating", RatingRole, I18N_NOOP2_NOSTRIP("@label", "Rating"), 0, 0, true, false },
- { "tags", TagsRole, I18N_NOOP2_NOSTRIP("@label", "Tags"), 0, 0, true, false },
- { "comment", CommentRole, I18N_NOOP2_NOSTRIP("@label", "Comment"), 0, 0, true, false },
+ { nullptr, NoRole, nullptr, nullptr, nullptr, nullptr, false, false },
+ { "text", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), nullptr, nullptr, false, false },
+ { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), nullptr, nullptr, false, false },
+ { "modificationtime", ModificationTimeRole, I18N_NOOP2_NOSTRIP("@label", "Modified"), nullptr, nullptr, false, false },
+ { "creationtime", CreationTimeRole, I18N_NOOP2_NOSTRIP("@label", "Created"), nullptr, nullptr, false, false },
+ { "accesstime", AccessTimeRole, I18N_NOOP2_NOSTRIP("@label", "Accessed"), nullptr, nullptr, false, false },
+ { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), nullptr, nullptr, false, false },
+ { "rating", RatingRole, I18N_NOOP2_NOSTRIP("@label", "Rating"), nullptr, nullptr, true, false },
+ { "tags", TagsRole, I18N_NOOP2_NOSTRIP("@label", "Tags"), nullptr, nullptr, true, false },
+ { "comment", CommentRole, I18N_NOOP2_NOSTRIP("@label", "Comment"), nullptr, nullptr, true, false },
+ { "title", TitleRole, I18N_NOOP2_NOSTRIP("@label", "Title"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true },
{ "wordCount", WordCountRole, I18N_NOOP2_NOSTRIP("@label", "Word Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true },
{ "lineCount", LineCountRole, I18N_NOOP2_NOSTRIP("@label", "Line Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true },
+ { "imageDateTime", ImageDateTimeRole, I18N_NOOP2_NOSTRIP("@label", "Date Photographed"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true },
{ "imageSize", ImageSizeRole, I18N_NOOP2_NOSTRIP("@label", "Image Size"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true },
{ "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true },
{ "artist", ArtistRole, I18N_NOOP2_NOSTRIP("@label", "Artist"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true },
+ { "genre", GenreRole, I18N_NOOP2_NOSTRIP("@label", "Genre"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true },
{ "album", AlbumRole, I18N_NOOP2_NOSTRIP("@label", "Album"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true },
{ "duration", DurationRole, I18N_NOOP2_NOSTRIP("@label", "Duration"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true },
+ { "bitrate", BitrateRole, I18N_NOOP2_NOSTRIP("@label", "Bitrate"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true },
{ "track", TrackRole, I18N_NOOP2_NOSTRIP("@label", "Track"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true },
+ { "releaseYear", ReleaseYearRole, I18N_NOOP2_NOSTRIP("@label", "Release Year"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true },
{ "path", PathRole, I18N_NOOP2_NOSTRIP("@label", "Path"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
+ { "deletiontime",DeletionTimeRole,I18N_NOOP2_NOSTRIP("@label", "Deletion Time"), 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 },
bool KFileItemModel::isConsistent() const
{
// m_items may contain less items than m_itemData because m_items
- // is populated lazily, see KFileItemModel::index(const KUrl& url).
+ // is populated lazily, see KFileItemModel::index(const QUrl& url).
if (m_items.count() > m_itemData.count()) {
return false;
}
- for (int i = 0; i < count(); ++i) {
+ for (int i = 0, iMax = count(); i < iMax; ++i) {
// 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;
}
}
return true;
}
-
-#include "kfileitemmodel.moc"