// #define KFILEITEMMODEL_DEBUG
KFileItemModel::KFileItemModel(QObject *parent)
- : KItemModelBase("text", "text", parent)
+ : KItemModelBase("text", "none", parent)
, m_dirLister(nullptr)
, m_sortDirsFirst(true)
, m_sortHiddenLast(false)
QElapsedTimer timer;
timer.start();
#endif
- switch (typeForRole(groupRole())) {
+ QByteArray role = groupRole();
+ if (typeForRole(role) == NoRole) {
+ // Handle extra grouping information
+ if (m_groupExtraInfo == "followSort") {
+ role = sortRole();
+ }
+ }
+ switch (typeForRole(role)) {
case NoRole:
m_groups.clear();
break;
m_groups = ratingRoleGroups();
break;
default:
- m_groups = genericStringRoleGroups(groupRole());
+ m_groups = genericStringRoleGroups(role);
break;
}
{
Q_UNUSED(previous)
m_sortRole = typeForRole(current);
+ if (m_sortRole == NoRole) {
+ // Requested role not in list of roles. This could
+ // be used for indicating non-trivial sorting behavior
+ m_sortExtraInfo = current;
+ } else {
+ m_sortExtraInfo.clear();
+ }
if (!m_requestRole[m_sortRole]) {
QSet<QByteArray> newRoles = m_roles;
{
Q_UNUSED(previous)
m_groupRole = typeForRole(current);
+ if (m_groupRole == NoRole) {
+ // Requested role not in list of roles. This could
+ // be used for indicating non-trivial grouping behavior
+ m_groupExtraInfo = current;
+ } else {
+ m_groupExtraInfo.clear();
+ }
- if (!m_requestRole[m_sortRole]) {
+ if (!m_requestRole[m_groupRole]) {
QSet<QByteArray> newRoles = m_roles;
newRoles << current;
setRoles(newRoles);
// 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());
+ m_dirSizeMode = ContentDisplaySettings::directorySizeMode();
}
void KFileItemModel::resortAllItems()
}
Q_EMIT itemsMoved(KItemRange(firstMovedIndex, movedItemsCount), movedToIndexes);
- } else if (groupedSorting()) {
+ }
+ if (groupedSorting()) {
// The groups might have changed even if the order of the items has not.
const QList<QPair<int, QVariant>> oldGroups = m_groups;
m_groups.clear();
QList<KFileItemModel::ItemData *> KFileItemModel::createItemDataList(const QUrl &parentUrl, const KFileItemList &items) const
{
- if (m_sortRole == TypeRole) {
+ if (m_sortRole == TypeRole || m_groupRole == TypeRole) {
// Try to resolve the MIME-types synchronously to prevent a reordering of
// the items when sorting by type (per default MIME-types are resolved
// asynchronously by KFileItemModelRolesUpdater).
// DateRole).
break;
}
+ switch (m_groupRole) {
+ case ExtensionRole:
+ case PermissionsRole:
+ case OwnerRole:
+ case GroupRole:
+ case DestinationRole:
+ case PathRole:
+ case DeletionTimeRole:
+ for (ItemData *itemData : std::as_const(itemDataList)) {
+ if (itemData->values.isEmpty()) {
+ itemData->values = retrieveData(itemData->item, itemData->parent);
+ }
+ }
+ break;
+
+ case TypeRole:
+ for (ItemData *itemData : std::as_const(itemDataList)) {
+ if (itemData->values.isEmpty()) {
+ const KFileItem item = itemData->item;
+ if (item.isDir() || item.isMimeTypeKnown()) {
+ itemData->values = retrieveData(itemData->item, itemData->parent);
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
}
int KFileItemModel::expandedParentsCount(const ItemData *data)
return true;
}
}
- if (m_sortDirsFirst
- || (ContentDisplaySettings::directorySizeMode() == ContentDisplaySettings::EnumDirectorySizeMode::ContentCount && m_sortRole == SizeRole)) {
+ if (m_sortDirsFirst || (m_dirSizeMode == ContentDisplaySettings::EnumDirectorySizeMode::ContentCount && m_sortRole == SizeRole)) {
const bool isDirA = a->item.isDir();
const bool isDirB = b->item.isDir();
if (isDirA && !isDirB) {
break;
case SizeRole: {
- if (ContentDisplaySettings::directorySizeMode() == ContentDisplaySettings::EnumDirectorySizeMode::ContentCount && itemA.isDir()) {
+ if (m_dirSizeMode == ContentDisplaySettings::EnumDirectorySizeMode::ContentCount && itemA.isDir()) {
// folders first then
// items A and B are folders thanks to lessThan checks
auto valueA = a->values.value("count");
// Unlike sortRoleCompare, this function can and often will return 0.
int result = 0;
- int groupA, groupB;
+ ItemGroupInfo groupA, groupB;
switch (m_groupRole) {
case NoRole:
- break;
+ // Non-trivial grouping behavior might be handled there in the future.
+ return 0;
case NameRole:
- groupA = nameRoleGroup(a, false).comparable;
- groupB = nameRoleGroup(b, false).comparable;
+ groupA = nameRoleGroup(a, false);
+ groupB = nameRoleGroup(b, false);
break;
case SizeRole:
- groupA = sizeRoleGroup(a, false).comparable;
- groupB = sizeRoleGroup(b, false).comparable;
+ groupA = sizeRoleGroup(a, false);
+ groupB = sizeRoleGroup(b, false);
break;
case ModificationTimeRole:
groupA = timeRoleGroup(
- [](const ItemData *item) {
- return item->item.time(KFileItem::ModificationTime);
- },
- a,
- false)
- .comparable;
+ [](const ItemData *item) {
+ return item->item.time(KFileItem::ModificationTime);
+ },
+ a,
+ false);
groupB = timeRoleGroup(
- [](const ItemData *item) {
- return item->item.time(KFileItem::ModificationTime);
- },
- b,
- false)
- .comparable;
+ [](const ItemData *item) {
+ return item->item.time(KFileItem::ModificationTime);
+ },
+ b,
+ false);
break;
case CreationTimeRole:
groupA = timeRoleGroup(
- [](const ItemData *item) {
- return item->item.time(KFileItem::CreationTime);
- },
- a,
- false)
- .comparable;
+ [](const ItemData *item) {
+ return item->item.time(KFileItem::CreationTime);
+ },
+ a,
+ false);
groupB = timeRoleGroup(
- [](const ItemData *item) {
- return item->item.time(KFileItem::CreationTime);
- },
- b,
- false)
- .comparable;
+ [](const ItemData *item) {
+ return item->item.time(KFileItem::CreationTime);
+ },
+ b,
+ false);
break;
case AccessTimeRole:
groupA = timeRoleGroup(
- [](const ItemData *item) {
- return item->item.time(KFileItem::AccessTime);
- },
- a,
- false)
- .comparable;
+ [](const ItemData *item) {
+ return item->item.time(KFileItem::AccessTime);
+ },
+ a,
+ false);
groupB = timeRoleGroup(
- [](const ItemData *item) {
- return item->item.time(KFileItem::AccessTime);
- },
- b,
- false)
- .comparable;
+ [](const ItemData *item) {
+ return item->item.time(KFileItem::AccessTime);
+ },
+ b,
+ false);
break;
case DeletionTimeRole:
groupA = timeRoleGroup(
- [](const ItemData *item) {
- return item->values.value("deletiontime").toDateTime();
- },
- a,
- false)
- .comparable;
+ [](const ItemData *item) {
+ return item->values.value("deletiontime").toDateTime();
+ },
+ a,
+ false);
groupB = timeRoleGroup(
- [](const ItemData *item) {
- return item->values.value("deletiontime").toDateTime();
- },
- b,
- false)
- .comparable;
+ [](const ItemData *item) {
+ return item->values.value("deletiontime").toDateTime();
+ },
+ b,
+ false);
break;
case PermissionsRole:
- groupA = permissionRoleGroup(a, false).comparable;
- groupB = permissionRoleGroup(b, false).comparable;
+ groupA = permissionRoleGroup(a, false);
+ groupB = permissionRoleGroup(b, false);
break;
case RatingRole:
- groupA = ratingRoleGroup(a, false).comparable;
- groupB = ratingRoleGroup(b, false).comparable;
+ groupA = ratingRoleGroup(a, false);
+ groupB = ratingRoleGroup(b, false);
+ break;
+ case TypeRole:
+ groupA = typeRoleGroup(a);
+ groupB = typeRoleGroup(b);
break;
default: {
- QString strGroupA = genericStringRoleGroup(groupRole(), a);
- QString strGroupB = genericStringRoleGroup(groupRole(), b);
- result = stringCompare(strGroupA, strGroupB, collator);
+ groupA = genericStringRoleGroup(groupRole(), a);
+ groupB = genericStringRoleGroup(groupRole(), b);
break;
}
}
- if (result == 0) {
- if (groupA < groupB) {
- result = -1;
- } else if (groupA > groupB) {
- result = 1;
- }
+ if (groupA.comparable < groupB.comparable) {
+ result = -1;
+ } else if (groupA.comparable > groupB.comparable) {
+ result = 1;
+ } else {
+ result = stringCompare(groupA.text, groupB.text, collator);
}
return result;
}
const QString name = itemData->item.text();
+ QMutexLocker collatorLock(s_collatorMutex());
+
// Use the first character of the name as group indication
firstChar = name.at(0).toUpper();
groupInfo.comparable = -1; // None
if (!item.isNull() && item.isDir()) {
- if (ContentDisplaySettings::directorySizeMode() == ContentDisplaySettings::EnumDirectorySizeMode::ContentCount || m_sortDirsFirst) {
+ if (m_dirSizeMode != ContentDisplaySettings::EnumDirectorySizeMode::ContentSize) {
groupInfo.comparable = 0; // Folders
} else {
fileSize = itemData->values.value("size").toULongLong();
return groupInfo;
}
-QString KFileItemModel::genericStringRoleGroup(const QByteArray &role, const ItemData *itemData) const
+KFileItemModel::ItemGroupInfo KFileItemModel::genericStringRoleGroup(const QByteArray &role, const ItemData *itemData) const
{
- return itemData->values.value(role).toString();
+ return {0, itemData->values.value(role).toString()};
}
QList<QPair<int, QVariant>> KFileItemModel::nameRoleGroups() const
return groups;
}
+KFileItemModel::ItemGroupInfo KFileItemModel::typeRoleGroup(const ItemData *itemData) const
+{
+ int priority = 0;
+ if (itemData->item.isDir() && m_sortDirsFirst) {
+ // Ensure folders stay first regardless of grouping order
+ if (groupOrder() == Qt::AscendingOrder) {
+ priority = -1;
+ } else {
+ priority = 1;
+ }
+ }
+ return {priority, itemData->values.value("type").toString()};
+}
+
QList<QPair<int, QVariant>> KFileItemModel::timeRoleGroups(const std::function<QDateTime(const ItemData *)> &fileTimeCb) const
{
Q_ASSERT(!m_itemData.isEmpty());
const int maxIndex = count() - 1;
QList<QPair<int, QVariant>> groups;
- QString groupText;
+ ItemGroupInfo groupInfo;
for (int i = 0; i <= maxIndex; ++i) {
if (isChildItem(i)) {
continue;
}
- QString newGroupText = genericStringRoleGroup(role, m_itemData.at(i));
+ ItemGroupInfo newGroupInfo = genericStringRoleGroup(role, m_itemData.at(i));
- if (newGroupText != groupText) {
- groupText = newGroupText;
- groups.append(QPair<int, QVariant>(i, newGroupText));
+ if (newGroupInfo != groupInfo) {
+ groupInfo = newGroupInfo;
+ groups.append(QPair<int, QVariant>(i, newGroupInfo.text));
}
}
return groups;