#include "kfileitemmodel.h"
#include "dolphin_generalsettings.h"
+#include "dolphindebug.h"
+#include "private/kfileitemmodeldirlister.h"
+#include "private/kfileitemmodelsortalgorithm.h"
#include <KLocalizedString>
#include <KUrlMimeData>
-#include "dolphindebug.h"
-
-#include "private/kfileitemmodelsortalgorithm.h"
-#include "private/kfileitemmodeldirlister.h"
-
+#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_dirLister(nullptr),
m_sortDirsFirst(true),
m_sortRole(NameRole),
m_sortingProgressPercent(-1),
m_filter(),
m_filteredItems(),
m_requestRole(),
- m_maximumUpdateIntervalTimer(0),
- m_resortAllItemsTimer(0),
+ m_maximumUpdateIntervalTimer(nullptr),
+ m_resortAllItemsTimer(nullptr),
m_pendingItemsToInsert(),
m_groups(),
m_expandedDirs(),
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);
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.
// Copyright (C) 2006 David Faure <faure@kde.org>
QList<QUrl> urls;
QList<QUrl> mostLocalUrls;
- bool canUseMostLocalUrls = true;
- const ItemData* lastAddedItem = 0;
+ const ItemData* lastAddedItem = nullptr;
for (int index : indexes) {
const ItemData* itemData = m_itemData.at(index);
bool isLocal;
mostLocalUrls << item.mostLocalUrl(isLocal);
- if (!isLocal) {
- canUseMostLocalUrls = false;
- }
}
}
int count = 0;
const RoleInfoMap* map = rolesInfoMap(count);
for (int i = 0; i < count; ++i) {
+ if (!map[i].roleTranslation) {
+ continue;
+ }
description.insert(map[i].role, i18nc(map[i].roleTranslationContext, map[i].roleTranslation));
}
}
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;
foreach (const QUrl& url, indexesForUrl.uniqueKeys()) {
if (indexesForUrl.count(url) > 1) {
qCWarning(DolphinDebug) << "Multiple items found with the URL" << url;
- foreach (int index, indexesForUrl.values(url)) {
- const ItemData* data = m_itemData.at(index);
- qCWarning(DolphinDebug) << "index" << index << ":" << data->item;
+
+ 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;
}
}
}
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<QUrl> &urls)
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.
QUrl urlToExpand = m_dirLister->url();
- const QStringList subDirs = url.path().mid(pos).split(QDir::separator());
+ 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.setPath(urlToExpand.path() + '/' + subDirs.at(i));
+ QString path = urlToExpand.path();
+ if (!path.endsWith(QLatin1Char('/'))) {
+ path.append(QLatin1Char('/'));
+ }
+ urlToExpand.setPath(path + subDirs.at(i));
m_urlsToExpand.insert(urlToExpand);
}
m_groups.clear();
}
-void KFileItemModel::onSortRoleChanged(const QByteArray& current, const QByteArray& previous)
+void KFileItemModel::onSortRoleChanged(const QByteArray& current, const QByteArray& previous, bool resortItems)
{
Q_UNUSED(previous);
m_sortRole = typeForRole(current);
setRoles(newRoles);
}
- resortAllItems();
+ if (resortItems) {
+ resortAllItems();
+ }
}
void KFileItemModel::onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous)
default:
Q_UNREACHABLE();
}
+ // Workaround for bug https://bugreports.qt.io/browse/QTBUG-69361
+ // Force the clean state of QCollator in single thread to avoid thread safety problems in sort
+ m_collator.compare(QString(), QString());
}
void KFileItemModel::resortAllItems()
}
// Extract the item-ranges out of the changed indexes
- qSort(indexes);
+ std::sort(indexes.begin(), indexes.end());
const KItemRangeList itemRangeList = KItemRangeList::fromSortedContainer(indexes);
emitItemsChangedAndTriggerResorting(itemRangeList, changedRoles);
}
} else {
m_itemData.reserve(totalItemCount);
for (int i = existingItemCount; i < totalItemCount; ++i) {
- m_itemData.append(0);
+ m_itemData.append(nullptr);
}
// We build the new list m_itemData in reverse order to minimize
delete m_itemData.at(index);
}
- m_itemData[index] = 0;
+ m_itemData[index] = nullptr;
}
}
}
const int parentIndex = index(parentUrl);
- ItemData* parentItem = parentIndex < 0 ? 0 : m_itemData.at(parentIndex);
+ ItemData* parentItem = parentIndex < 0 ? nullptr : m_itemData.at(parentIndex);
QList<ItemData*> itemDataList;
itemDataList.reserve(items.count());
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) {
// 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]) {
+ data.insert(sharedValue("isHidden"), item.isHidden());
+ }
+
if (m_requestRole[NameRole]) {
data.insert(sharedValue("text"), item.text());
}
data.insert(sharedValue("size"), item.size());
}
- if (m_requestRole[DateRole]) {
- // 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);
+ if (m_requestRole[ModificationTimeRole]) {
+ // Don't use KFileItem::timeString() or KFileItem::time() as this is too expensive when
+ // having several thousands of items. Instead read the raw number from UDSEntry directly
+ // and the formatting of the date-time will be done on-demand by the view when the date will be shown.
+ const long long dateTime = item.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
+ data.insert(sharedValue("modificationtime"), dateTime);
+ }
+
+ if (m_requestRole[CreationTimeRole]) {
+ // Don't use KFileItem::timeString() or KFileItem::time() as this is too expensive when
+ // having several thousands of items. Instead read the raw number from UDSEntry directly
+ // and the formatting of the date-time will be done on-demand by the view when the date will be shown.
+ const long long dateTime = item.entry().numberValue(KIO::UDSEntry::UDS_CREATION_TIME, -1);
+ data.insert(sharedValue("creationtime"), dateTime);
+ }
+
+ if (m_requestRole[AccessTimeRole]) {
+ // Don't use KFileItem::timeString() or KFileItem::time() as this is too expensive when
+ // having several thousands of items. Instead read the raw number from UDSEntry directly
+ // and the formatting of the date-time will be done on-demand by the view when the date will be shown.
+ const long long dateTime = item.entry().numberValue(KIO::UDSEntry::UDS_ACCESS_TIME, -1);
+ data.insert(sharedValue("accesstime"), dateTime);
}
if (m_requestRole[PermissionsRole]) {
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 (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0;
}
-/**
- * Helper class for KFileItemModel::sort().
- */
-class KFileItemModelLessThan
+void KFileItemModel::sort(const QList<KFileItemModel::ItemData*>::iterator &begin,
+ const QList<KFileItemModel::ItemData*>::iterator &end) const
{
-public:
- 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)
+ auto lambdaLessThan = [&] (const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b)
{
- 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, 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, m_collator);
+ return lessThan(a, b, m_collator);
+ };
if (m_sortRole == NameRole) {
// Sorting by name can be expensive, in particular if natural sorting is
// enabled. Use all CPU cores to speed up the sorting process.
static const int numberOfThreads = QThread::idealThreadCount();
- parallelMergeSort(begin, end, lessThan, numberOfThreads);
+ parallelMergeSort(begin, end, lambdaLessThan, numberOfThreads);
} else {
// Sorting by other roles is quite fast. Use only one thread to prevent
// problems caused by non-reentrant comparison functions, see
// https://bugs.kde.org/show_bug.cgi?id=312679
- mergeSort(begin, end, lessThan);
+ mergeSort(begin, end, lambdaLessThan);
}
}
break;
}
- case DateRole: {
- const QDateTime dateTimeA = itemA.time(KFileItem::ModificationTime);
- const QDateTime dateTimeB = itemB.time(KFileItem::ModificationTime);
+ case ModificationTimeRole: {
+ const long long dateTimeA = itemA.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
+ const long long dateTimeB = itemB.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
if (dateTimeA < dateTimeB) {
result = -1;
} else if (dateTimeA > dateTimeB) {
break;
}
- case RatingRole: {
- result = a->values.value("rating").toInt() - b->values.value("rating").toInt();
+ case CreationTimeRole: {
+ const long long dateTimeA = itemA.entry().numberValue(KIO::UDSEntry::UDS_CREATION_TIME, -1);
+ const long long dateTimeB = itemB.entry().numberValue(KIO::UDSEntry::UDS_CREATION_TIME, -1);
+ if (dateTimeA < dateTimeB) {
+ result = -1;
+ } else if (dateTimeA > dateTimeB) {
+ result = +1;
+ }
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 = collator.compare(a->values.value("imageSize").toString(),
- b->values.value("imageSize").toString());
+ 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:
+ case WidthRole:
+ case HeightRole:
+ case WordCountRole:
+ case LineCountRole:
+ case TrackRole:
+ case ReleaseYearRole: {
+ result = a->values.value(roleForType(m_sortRole)).toInt() - b->values.value(roleForType(m_sortRole)).toInt();
break;
}
return groups;
}
-QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
+QList<QPair<int, QVariant> > KFileItemModel::timeRoleGroups(const 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:
case 0: newGroupValue = i18nc("@title:group Date", "Today"); break;
case 1: newGroupValue = i18nc("@title:group Date", "Yesterday"); break;
default:
- newGroupValue = modifiedTime.toString(
+ 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);
}
} 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: "
- "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);
+ const KLocalizedString format = ki18nc("@title:group Date: "
+ "MMMM is full month name in current locale, and yyyy is "
+ "full year number", "'Yesterday' (MMMM, yyyy)");
+ const QString translatedFormat = format.toString();
+ if (translatedFormat.count(QLatin1Char('\'')) == 2) {
+ newGroupValue = fileTime.toString(translatedFormat);
+ newGroupValue = i18nc("Can be used to script translation of "
+ "\"'Yesterday' (MMMM, yyyy)\" with context @title:group Date",
+ "%1", newGroupValue);
+ } else {
+ qCWarning(DolphinDebug).nospace() << "A wrong translation was found: " << translatedFormat << ". Please file a bug report at bugs.kde.org";
+ const QString untranslatedFormat = format.toString({ QLatin1String("en_US") });
+ newGroupValue = fileTime.toString(untranslatedFormat);
+ }
} else if (daysDistance <= 7) {
- newGroupValue = modifiedTime.toString(i18nc("@title:group Date: "
+ 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)"));
"\"dddd (MMMM, yyyy)\" with context @title:group Date",
"%1", newGroupValue);
} else if (daysDistance <= 7 * 2) {
- 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);
+ const KLocalizedString format = ki18nc("@title:group Date: "
+ "MMMM is full month name in current locale, and yyyy is "
+ "full year number", "'One Week Ago' (MMMM, yyyy)");
+ const QString translatedFormat = format.toString();
+ if (translatedFormat.count(QLatin1Char('\'')) == 2) {
+ newGroupValue = fileTime.toString(translatedFormat);
+ newGroupValue = i18nc("Can be used to script translation of "
+ "\"'One Week Ago' (MMMM, yyyy)\" with context @title:group Date",
+ "%1", newGroupValue);
+ } else {
+ qCWarning(DolphinDebug).nospace() << "A wrong translation was found: " << translatedFormat << ". Please file a bug report at bugs.kde.org";
+ const QString untranslatedFormat = format.toString({ QLatin1String("en_US") });
+ newGroupValue = fileTime.toString(untranslatedFormat);
+ }
} else if (daysDistance <= 7 * 3) {
- 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);
+ const KLocalizedString format = ki18nc("@title:group Date: "
+ "MMMM is full month name in current locale, and yyyy is "
+ "full year number", "'Two Weeks Ago' (MMMM, yyyy)");
+ const QString translatedFormat = format.toString();
+ if (translatedFormat.count(QLatin1Char('\'')) == 2) {
+ newGroupValue = fileTime.toString(translatedFormat);
+ newGroupValue = i18nc("Can be used to script translation of "
+ "\"'Two Weeks Ago' (MMMM, yyyy)\" with context @title:group Date",
+ "%1", newGroupValue);
+ } else {
+ qCWarning(DolphinDebug).nospace() << "A wrong translation was found: " << translatedFormat << ". Please file a bug report at bugs.kde.org";
+ const QString untranslatedFormat = format.toString({ QLatin1String("en_US") });
+ newGroupValue = fileTime.toString(untranslatedFormat);
+ }
} else if (daysDistance <= 7 * 4) {
- 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);
+ const KLocalizedString format = ki18nc("@title:group Date: "
+ "MMMM is full month name in current locale, and yyyy is "
+ "full year number", "'Three Weeks Ago' (MMMM, yyyy)");
+ const QString translatedFormat = format.toString();
+ if (translatedFormat.count(QLatin1Char('\'')) == 2) {
+ newGroupValue = fileTime.toString(translatedFormat);
+ newGroupValue = i18nc("Can be used to script translation of "
+ "\"'Three Weeks Ago' (MMMM, yyyy)\" with context @title:group Date",
+ "%1", newGroupValue);
+ } else {
+ qCWarning(DolphinDebug).nospace() << "A wrong translation was found: " << translatedFormat << ". Please file a bug report at bugs.kde.org";
+ const QString untranslatedFormat = format.toString({ QLatin1String("en_US") });
+ newGroupValue = fileTime.toString(untranslatedFormat);
+ }
} else {
- 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);
+ const KLocalizedString format = ki18nc("@title:group Date: "
+ "MMMM is full month name in current locale, and yyyy is "
+ "full year number", "'Earlier on' MMMM, yyyy");
+ const QString translatedFormat = format.toString();
+ if (translatedFormat.count(QLatin1Char('\'')) == 2) {
+ newGroupValue = fileTime.toString(translatedFormat);
+ newGroupValue = i18nc("Can be used to script translation of "
+ "\"'Earlier on' MMMM, yyyy\" with context @title:group Date",
+ "%1", newGroupValue);
+ } else {
+ qCWarning(DolphinDebug).nospace() << "A wrong translation was found: " << translatedFormat << ". Please file a bug report at bugs.kde.org";
+ const QString untranslatedFormat = format.toString({ QLatin1String("en_US") });
+ newGroupValue = fileTime.toString(untranslatedFormat);
+ }
}
} else {
- newGroupValue = modifiedTime.toString(i18nc("@title:group "
+ 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 "
{
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 },
- { "imageSize", ImageSizeRole, I18N_NOOP2_NOSTRIP("@label", "Image Size"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true },
+ { "imageDateTime", ImageDateTimeRole, I18N_NOOP2_NOSTRIP("@label", "Date Photographed"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true },
+ { "width", WidthRole, I18N_NOOP2_NOSTRIP("@label", "Width"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true },
+ { "height", HeightRole, I18N_NOOP2_NOSTRIP("@label", "Height"), 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 },
+ { "aspectRatio", AspectRatioRole, I18N_NOOP2_NOSTRIP("@label", "Aspect Ratio"), I18N_NOOP2_NOSTRIP("@label", "Video"), true, true },
+ { "frameRate", FrameRateRole, I18N_NOOP2_NOSTRIP("@label", "Frame Rate"), I18N_NOOP2_NOSTRIP("@label", "Video"), 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 },
{ "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 },
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()) {