From 74bc3cac2530031c0ec78fc5039342ac0f077eb7 Mon Sep 17 00:00:00 2001 From: Peter Penz Date: Sat, 29 Oct 2011 18:05:37 +0200 Subject: [PATCH] Implement grouping for all roles --- src/kitemviews/kfileitemmodel.cpp | 246 +++++++++++++++++++++++++----- src/kitemviews/kfileitemmodel.h | 21 ++- src/kitemviews/kitemlistview.cpp | 6 +- 3 files changed, 231 insertions(+), 42 deletions(-) diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 272654872..083b6421c 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -236,11 +236,11 @@ QList > KFileItemModel::groups() const case SizeRole: m_groups = sizeRoleGroups(); break; case DateRole: m_groups = dateRoleGroups(); break; case PermissionsRole: m_groups = permissionRoleGroups(); break; - case OwnerRole: m_groups = ownerRoleGroups(); break; - case GroupRole: m_groups = groupRoleGroups(); break; - case TypeRole: m_groups = typeRoleGroups(); break; - case DestinationRole: m_groups = destinationRoleGroups(); break; - case PathRole: m_groups = pathRoleGroups(); break; + case OwnerRole: m_groups = genericStringRoleGroups("owner"); break; + case GroupRole: m_groups = genericStringRoleGroups("group"); break; + case TypeRole: m_groups = genericStringRoleGroups("type"); break; + case DestinationRole: m_groups = genericStringRoleGroups("destination"); break; + case PathRole: m_groups = genericStringRoleGroups("path"); break; case NoRole: break; case IsDirRole: break; case IsExpandedRole: break; @@ -762,12 +762,11 @@ void KFileItemModel::resortAllItems() return; } - m_groups.clear(); - const KFileItemList oldSortedItems = m_sortedItems; const QHash oldItems = m_items; const QList > oldData = m_data; + m_groups.clear(); m_items.clear(); m_data.clear(); @@ -1168,10 +1167,7 @@ QList > KFileItemModel::nameRoleGroups() const QChar firstChar; bool isLetter = false; for (int i = 0; i <= maxIndex; ++i) { - if (m_requestRole[ExpansionLevelRole] && m_data.at(i).value("expansionLevel").toInt() > 0) { - // KItemListView would be capable to show sub-groups in groups but - // in typical usecases this results in visual clutter, hence we - // just ignore sub-groups. + if (isChildItem(i)) { continue; } @@ -1227,49 +1223,227 @@ QList > KFileItemModel::sizeRoleGroups() const { Q_ASSERT(!m_data.isEmpty()); - return QList >(); + const int maxIndex = count() - 1; + QList > groups; + + QString groupValue; + for (int i = 0; i <= maxIndex; ++i) { + if (isChildItem(i)) { + continue; + } + + const KFileItem& item = m_sortedItems.at(i); + const KIO::filesize_t fileSize = !item.isNull() ? item.size() : ~0U; + QString newGroupValue; + if (!item.isNull() && item.isDir()) { + newGroupValue = i18nc("@title:group Size", "Folders"); + } else if (fileSize < 5 * 1024 * 1024) { + newGroupValue = i18nc("@title:group Size", "Small"); + } else if (fileSize < 10 * 1024 * 1024) { + newGroupValue = i18nc("@title:group Size", "Medium"); + } else { + newGroupValue = i18nc("@title:group Size", "Big"); + } + + if (newGroupValue != groupValue) { + groupValue = newGroupValue; + groups.append(QPair(i, newGroupValue)); + } + } + + return groups; } QList > KFileItemModel::dateRoleGroups() const { Q_ASSERT(!m_data.isEmpty()); - return QList >(); + + const int maxIndex = count() - 1; + QList > groups; + + const QDate currentDate = KDateTime::currentLocalDateTime().date(); + + int yearForCurrentWeek = 0; + int currentWeek = currentDate.weekNumber(&yearForCurrentWeek); + if (yearForCurrentWeek == currentDate.year() + 1) { + currentWeek = 53; + } + + QDate previousModifiedDate; + QString groupValue; + for (int i = 0; i <= maxIndex; ++i) { + if (isChildItem(i)) { + continue; + } + + const KDateTime modifiedTime = m_sortedItems.at(i).time(KFileItem::ModificationTime); + const QDate modifiedDate = modifiedTime.date(); + if (modifiedDate == previousModifiedDate) { + // The current item is in the same group as the previous item + continue; + } + previousModifiedDate = modifiedDate; + + const int daysDistance = modifiedDate.daysTo(currentDate); + + int yearForModifiedWeek = 0; + int modifiedWeek = modifiedDate.weekNumber(&yearForModifiedWeek); + if (yearForModifiedWeek == modifiedDate.year() + 1) { + modifiedWeek = 53; + } + + QString newGroupValue; + if (currentDate.year() == modifiedDate.year() && currentDate.month() == modifiedDate.month()) { + if (modifiedWeek > currentWeek) { + // Usecase: modified date = 2010-01-01, current date = 2010-01-22 + // modified week = 53, current week = 3 + modifiedWeek = 0; + } + switch (currentWeek - modifiedWeek) { + 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")); + } + break; + case 1: + newGroupValue = i18nc("@title:group Date", "Last Week"); + break; + case 2: + newGroupValue = i18nc("@title:group Date", "Two Weeks Ago"); + break; + case 3: + newGroupValue = i18nc("@title:group Date", "Three Weeks Ago"); + break; + case 4: + case 5: + newGroupValue = i18nc("@title:group Date", "Earlier this Month"); + break; + default: + Q_ASSERT(false); + } + } else { + const QDate lastMonthDate = currentDate.addMonths(-1); + 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)")); + } 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)")); + } 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", "Last Week (%B, %Y)")); + } 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)")); + } 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)")); + } 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")); + } + } 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")); + } + } + + if (newGroupValue != groupValue) { + groupValue = newGroupValue; + groups.append(QPair(i, newGroupValue)); + } + } + + return groups; } QList > KFileItemModel::permissionRoleGroups() const { Q_ASSERT(!m_data.isEmpty()); - return QList >(); -} -QList > KFileItemModel::ownerRoleGroups() const -{ - Q_ASSERT(!m_data.isEmpty()); - return QList >(); -} + const int maxIndex = count() - 1; + QList > groups; -QList > KFileItemModel::groupRoleGroups() const -{ - Q_ASSERT(!m_data.isEmpty()); - return QList >(); -} + QString permissionsString; + QString groupValue; + for (int i = 0; i <= maxIndex; ++i) { + if (isChildItem(i)) { + continue; + } -QList > KFileItemModel::typeRoleGroups() const -{ - Q_ASSERT(!m_data.isEmpty()); - return QList >(); -} + const QString newPermissionsString = m_data.at(i).value("permissions").toString(); + if (newPermissionsString == permissionsString) { + continue; + } + permissionsString = newPermissionsString; -QList > KFileItemModel::destinationRoleGroups() const -{ - Q_ASSERT(!m_data.isEmpty()); - return QList >(); + const QFileInfo info(m_sortedItems.at(i).url().pathOrUrl()); + + // Set user string + QString user; + if (info.permission(QFile::ReadUser)) { + user = i18nc("@item:intext Access permission, concatenated", "Read, "); + } + if (info.permission(QFile::WriteUser)) { + user += i18nc("@item:intext Access permission, concatenated", "Write, "); + } + if (info.permission(QFile::ExeUser)) { + user += i18nc("@item:intext Access permission, concatenated", "Execute, "); + } + user = user.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : user.mid(0, user.count() - 2); + + // Set group string + QString group; + if (info.permission(QFile::ReadGroup)) { + group = i18nc("@item:intext Access permission, concatenated", "Read, "); + } + if (info.permission(QFile::WriteGroup)) { + group += i18nc("@item:intext Access permission, concatenated", "Write, "); + } + if (info.permission(QFile::ExeGroup)) { + group += i18nc("@item:intext Access permission, concatenated", "Execute, "); + } + group = group.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : group.mid(0, group.count() - 2); + + // Set others string + QString others; + if (info.permission(QFile::ReadOther)) { + others = i18nc("@item:intext Access permission, concatenated", "Read, "); + } + if (info.permission(QFile::WriteOther)) { + others += i18nc("@item:intext Access permission, concatenated", "Write, "); + } + if (info.permission(QFile::ExeOther)) { + others += i18nc("@item:intext Access permission, concatenated", "Execute, "); + } + others = others.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : others.mid(0, others.count() - 2); + + const QString newGroupValue = i18nc("@title:group Files and folders by permissions", "User: %1 | Group: %2 | Others: %3", user, group, others); + if (newGroupValue != groupValue) { + groupValue = newGroupValue; + groups.append(QPair(i, newGroupValue)); + } + } + + return groups; } -QList > KFileItemModel::pathRoleGroups() const +QList > KFileItemModel::genericStringRoleGroups(const QByteArray& role) const { Q_ASSERT(!m_data.isEmpty()); - return QList >(); + + const int maxIndex = count() - 1; + QList > groups; + + QString groupValue; + for (int i = 0; i <= maxIndex; ++i) { + if (isChildItem(i)) { + continue; + } + const QString newGroupValue = m_data.at(i).value(role).toString(); + if (newGroupValue != groupValue) { + groupValue = newGroupValue; + groups.append(QPair(i, newGroupValue)); + } + } + + return groups; } #include "kfileitemmodel.moc" diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index 8ccff95e8..f2e783f46 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -198,11 +198,17 @@ private: QList > sizeRoleGroups() const; QList > dateRoleGroups() const; QList > permissionRoleGroups() const; - QList > ownerRoleGroups() const; - QList > groupRoleGroups() const; - QList > typeRoleGroups() const; - QList > destinationRoleGroups() const; - QList > pathRoleGroups() const; + QList > genericStringRoleGroups(const QByteArray& role) const; + + /** + * Helper method for all xxxRoleGroups() methods to check whether the + * item with the given index is a child-item. A child-item is defined + * as item having an expansion-level > 0. All xxxRoleGroups() methods + * should skip the grouping if the item is a child-item (although + * KItemListView would be capable to show sub-groups in groups this + * results in visual clutter for most usecases). + */ + bool isChildItem(int index) const; private: QWeakPointer m_dirLister; @@ -241,6 +247,11 @@ private: friend class KFileItemModelTest; // For unit testing }; +inline bool KFileItemModel::isChildItem(int index) const +{ + return m_requestRole[ExpansionLevelRole] && m_data.at(index).value("expansionLevel").toInt() > 0; +} + #endif diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index 478bf6260..eff48cf09 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -1466,6 +1466,11 @@ void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget) return; } + const QList > groups = model()->groups(); + if (groups.isEmpty()) { + return; + } + KItemListGroupHeader* header = m_visibleGroups.value(widget); if (!header) { header = m_groupHeaderCreator->create(this); @@ -1476,7 +1481,6 @@ void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget) // Determine the shown data for the header by doing a binary // search in the groups-list - const QList > groups = model()->groups(); int min = 0; int max = groups.count() - 1; int mid = 0; -- 2.47.3