]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kfileitemmodel.cpp
"Group by - Type" now respects "Folders first" setting. Minor code cleanup
[dolphin.git] / src / kitemviews / kfileitemmodel.cpp
index 1ab9cd0e41cfe147af9b6f9c8ff7d642e1f72944..7d5b0f51847f0c25f4f7bf20ebf49f516b4d89ed 100644 (file)
@@ -34,7 +34,7 @@ Q_GLOBAL_STATIC(QRecursiveMutex, s_collatorMutex)
 // #define KFILEITEMMODEL_DEBUG
 
 KFileItemModel::KFileItemModel(QObject *parent)
-    : KItemModelBase("text", "text", parent)
+    : KItemModelBase("text", "none", parent)
     , m_dirLister(nullptr)
     , m_sortDirsFirst(true)
     , m_sortHiddenLast(false)
@@ -387,7 +387,14 @@ QList<QPair<int, QVariant>> KFileItemModel::groups() const
         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;
@@ -424,7 +431,7 @@ QList<QPair<int, QVariant>> KFileItemModel::groups() const
             m_groups = ratingRoleGroups();
             break;
         default:
-            m_groups = genericStringRoleGroups(groupRole());
+            m_groups = genericStringRoleGroups(role);
             break;
         }
 
@@ -949,6 +956,13 @@ void KFileItemModel::onSortRoleChanged(const QByteArray &current, const QByteArr
 {
     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;
@@ -975,8 +989,15 @@ void KFileItemModel::onGroupRoleChanged(const QByteArray &current, const QByteAr
 {
     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);
@@ -1669,7 +1690,7 @@ void KFileItemModel::removeItems(const KItemRangeList &itemRanges, RemoveItemsBe
 
 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).
@@ -1732,6 +1753,35 @@ void KFileItemModel::prepareItemsForSorting(QList<ItemData *> &itemDataList)
         // 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)
@@ -2290,101 +2340,99 @@ int KFileItemModel::groupRoleCompare(const ItemData *a, const ItemData *b, const
     // 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);
+        groupB = permissionRoleGroup(b, false);
+        break;
+    case RatingRole:
+        groupA = ratingRoleGroup(a, false);
+        groupB = ratingRoleGroup(b, false);
+        break;
+    case TypeRole:
+        groupA = typeRoleGroup(a);
+        groupB = typeRoleGroup(b);
         break;
-    // case PermissionsRole:
-    //  case RatingRole:
     default: {
-        QString strGroupA = genericStringRoleGroup(groupRole(), a);
-        QString strGroupB = genericStringRoleGroup(groupRole(), b);
-        if (strGroupA < strGroupB) {
-            result = -1;
-        } else if (strGroupA > strGroupB) {
-            result = 1;
-        }
+        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;
 }
@@ -2410,11 +2458,15 @@ int KFileItemModel::stringCompare(const QString &a, const QString &b, const QCol
 
 KFileItemModel::ItemGroupInfo KFileItemModel::nameRoleGroup(const ItemData *itemData, bool withString) const
 {
-    static ItemGroupInfo oldGroupInfo, groupInfo;
-    static QChar oldFirstChar, firstChar;
+    static ItemGroupInfo oldGroupInfo;
+    static QChar oldFirstChar;
+    ItemGroupInfo groupInfo;
+    QChar firstChar;
 
     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();
 
@@ -2480,15 +2532,14 @@ KFileItemModel::ItemGroupInfo KFileItemModel::nameRoleGroup(const ItemData *item
 
 KFileItemModel::ItemGroupInfo KFileItemModel::sizeRoleGroup(const ItemData *itemData, bool withString) const
 {
-    static ItemGroupInfo oldGroupInfo, groupInfo;
-    static KIO::filesize_t oldFileSize, fileSize;
+    static ItemGroupInfo oldGroupInfo;
+    static KIO::filesize_t oldFileSize;
+    ItemGroupInfo groupInfo;
+    KIO::filesize_t fileSize;
 
     const KFileItem item = itemData->item;
     fileSize = !item.isNull() ? item.size() : ~0U;
 
-    // Use the first character of the name as group indication
-    fileSize = item.size();
-
     groupInfo.comparable = -1; // None
     if (!item.isNull() && item.isDir()) {
         if (ContentDisplaySettings::directorySizeMode() == ContentDisplaySettings::EnumDirectorySizeMode::ContentCount || m_sortDirsFirst) {
@@ -2498,7 +2549,7 @@ KFileItemModel::ItemGroupInfo KFileItemModel::sizeRoleGroup(const ItemData *item
         }
     }
     if (fileSize == oldFileSize) {
-        groupInfo = oldGroupInfo;
+        return oldGroupInfo;
     }
     if (groupInfo.comparable < 0) {
         if (fileSize < 5 * 1024 * 1024) { // < 5 MB
@@ -2522,17 +2573,15 @@ KFileItemModel::ItemGroupInfo KFileItemModel::sizeRoleGroup(const ItemData *item
 KFileItemModel::ItemGroupInfo
 KFileItemModel::timeRoleGroup(const std::function<QDateTime(const ItemData *)> &fileTimeCb, const ItemData *itemData, bool withString) const
 {
-    static ItemGroupInfo oldGroupInfo, groupInfo;
+    static ItemGroupInfo oldGroupInfo;
     static QDate oldFileDate;
+    ItemGroupInfo groupInfo;
 
     const QDate currentDate = QDate::currentDate();
     const QDateTime fileTime = fileTimeCb(itemData);
     const QDate fileDate = fileTime.date();
     const int daysDistance = fileDate.daysTo(currentDate);
 
-    int intGroupInfo;
-    QString strGroupInfo;
-
     // Simplified grouping algorithm, preserving dates
     // but not taking "pretty printing" into account
     if (currentDate.year() == fileDate.year() && currentDate.month() == fileDate.month()) {
@@ -2553,13 +2602,13 @@ KFileItemModel::timeRoleGroup(const std::function<QDateTime(const ItemData *)> &
             if (daysDistance < 7) {
                 groupInfo.comparable = daysDistance; // Today, Yesterday and week days (Month, Year)
             } else if (daysDistance < 14) {
-                groupInfo.comparable = 9; // One Week Ago (Month, Year)
+                groupInfo.comparable = 11; // One Week Ago (Month, Year)
             } else if (daysDistance < 21) {
-                groupInfo.comparable = 19; // Two Weeks Ago (Month, Year)
+                groupInfo.comparable = 21; // Two Weeks Ago (Month, Year)
             } else if (daysDistance < 28) {
-                groupInfo.comparable = 29; // Three Weeks Ago (Month, Year)
+                groupInfo.comparable = 31; // Three Weeks Ago (Month, Year)
             } else {
-                groupInfo.comparable = 39; // Earlier on Month, Year
+                groupInfo.comparable = 41; // Earlier on Month, Year
             }
         } else {
             // The trick will fail for dates past April, 178956967 or before 1 AD.
@@ -2626,7 +2675,7 @@ KFileItemModel::timeRoleGroup(const std::function<QDateTime(const ItemData *)> &
                         const QString untranslatedFormat = format.toString({QLatin1String("en_US")});
                         groupInfo.text = fileTime.toString(untranslatedFormat);
                     }
-                } else if (daysDistance <= 7) {
+                } else if (daysDistance < 7) {
                     groupInfo.text =
                         fileTime.toString(i18nc("@title:group Date: "
                                                 "The week day name: dddd, MMMM is full month name "
@@ -2637,7 +2686,7 @@ KFileItemModel::timeRoleGroup(const std::function<QDateTime(const ItemData *)> &
                         "\"dddd (MMMM, yyyy)\" with context @title:group Date",
                         "%1",
                         groupInfo.text);
-                } else if (daysDistance <= 7 * 2) {
+                } else if (daysDistance < 7 * 2) {
                     const KLocalizedString format = ki18nc(
                         "@title:group Date: "
                         "MMMM is full month name in current locale, and yyyy is "
@@ -2658,7 +2707,7 @@ KFileItemModel::timeRoleGroup(const std::function<QDateTime(const ItemData *)> &
                         const QString untranslatedFormat = format.toString({QLatin1String("en_US")});
                         groupInfo.text = fileTime.toString(untranslatedFormat);
                     }
-                } else if (daysDistance <= 7 * 3) {
+                } else if (daysDistance < 7 * 3) {
                     const KLocalizedString format = ki18nc(
                         "@title:group Date: "
                         "MMMM is full month name in current locale, and yyyy is "
@@ -2679,7 +2728,7 @@ KFileItemModel::timeRoleGroup(const std::function<QDateTime(const ItemData *)> &
                         const QString untranslatedFormat = format.toString({QLatin1String("en_US")});
                         groupInfo.text = fileTime.toString(untranslatedFormat);
                     }
-                } else if (daysDistance <= 7 * 4) {
+                } else if (daysDistance < 7 * 4) {
                     const KLocalizedString format = ki18nc(
                         "@title:group Date: "
                         "MMMM is full month name in current locale, and yyyy is "
@@ -2741,9 +2790,80 @@ KFileItemModel::timeRoleGroup(const std::function<QDateTime(const ItemData *)> &
     return groupInfo;
 }
 
-QString KFileItemModel::genericStringRoleGroup(const QByteArray &role, const ItemData *itemData) const
+KFileItemModel::ItemGroupInfo KFileItemModel::permissionRoleGroup(const ItemData *itemData, bool withString) const
+{
+    static ItemGroupInfo oldGroupInfo;
+    static QFileDevice::Permissions oldPermissions;
+    ItemGroupInfo groupInfo;
+
+    const QFileInfo info(itemData->item.url().toLocalFile());
+    const QFileDevice::Permissions permissions = info.permissions();
+    if (permissions == oldPermissions) {
+        return oldGroupInfo;
+    }
+    groupInfo.comparable = (int)permissions;
+
+    if (withString) {
+        // Set user string
+        QString user;
+        if (permissions & QFile::ReadUser) {
+            user = i18nc("@item:intext Access permission, concatenated", "Read, ");
+        }
+        if (permissions & QFile::WriteUser) {
+            user += i18nc("@item:intext Access permission, concatenated", "Write, ");
+        }
+        if (permissions & QFile::ExeUser) {
+            user += i18nc("@item:intext Access permission, concatenated", "Execute, ");
+        }
+        user = user.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : user.mid(0, user.length() - 2);
+
+        // Set group string
+        QString group;
+        if (permissions & QFile::ReadGroup) {
+            group = i18nc("@item:intext Access permission, concatenated", "Read, ");
+        }
+        if (permissions & QFile::WriteGroup) {
+            group += i18nc("@item:intext Access permission, concatenated", "Write, ");
+        }
+        if (permissions & QFile::ExeGroup) {
+            group += i18nc("@item:intext Access permission, concatenated", "Execute, ");
+        }
+        group = group.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : group.mid(0, group.length() - 2);
+
+        // Set others string
+        QString others;
+        if (permissions & QFile::ReadOther) {
+            others = i18nc("@item:intext Access permission, concatenated", "Read, ");
+        }
+        if (permissions & QFile::WriteOther) {
+            others += i18nc("@item:intext Access permission, concatenated", "Write, ");
+        }
+        if (permissions & QFile::ExeOther) {
+            others += i18nc("@item:intext Access permission, concatenated", "Execute, ");
+        }
+        others = others.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : others.mid(0, others.length() - 2);
+        groupInfo.text = i18nc("@title:group Files and folders by permissions", "User: %1 | Group: %2 | Others: %3", user, group, others);
+    }
+    oldPermissions = permissions;
+    oldGroupInfo = groupInfo;
+    return groupInfo;
+}
+
+KFileItemModel::ItemGroupInfo KFileItemModel::ratingRoleGroup(const ItemData *itemData, bool withString) const
+{
+    ItemGroupInfo groupInfo;
+    groupInfo.comparable = itemData->values.value("rating", 0).toInt();
+    if (withString) {
+        // Dolphin does not currently use string representation of star rating
+        // as stars are rendered as graphics in group headers.
+        groupInfo.text = i18nc("@item:intext Rated N (stars)", "Rated ") + QString::number(groupInfo.comparable);
+    }
+    return groupInfo;
+}
+
+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
@@ -2792,6 +2912,20 @@ QList<QPair<int, QVariant>> KFileItemModel::sizeRoleGroups() 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());
@@ -2822,69 +2956,19 @@ QList<QPair<int, QVariant>> KFileItemModel::permissionRoleGroups() const
     const int maxIndex = count() - 1;
     QList<QPair<int, QVariant>> groups;
 
-    /*QString permissionsString;
     ItemGroupInfo groupInfo;
     for (int i = 0; i <= maxIndex; ++i) {
         if (isChildItem(i)) {
             continue;
         }
 
-        const ItemData *itemData = m_itemData.at(i);
-        const QString newPermissionsString = itemData->values.value("permissions").toString();
-        if (newPermissionsString == permissionsString) {
-            continue;
-        }
-        permissionsString = newPermissionsString;
+        ItemGroupInfo newGroupInfo = permissionRoleGroup(m_itemData.at(i));
 
-        const QFileInfo info(itemData->item.url().toLocalFile());
-
-        // 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.length() - 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.length() - 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.length() - 2);
-
-        const ItemListGroup newGroupInfo = {0,
-                                            i18nc("@title:group Files and folders by permissions", "User: %1 | Group: %2 | Others: %3", user, group, others)};
         if (newGroupInfo != groupInfo) {
             groupInfo = newGroupInfo;
-            groups.append(QPair<int, QVariant>(i, newGroupInfo));
+            groups.append(QPair<int, QVariant>(i, newGroupInfo.text));
         }
-    }*/
-
+    }
     return groups;
 }
 
@@ -2895,19 +2979,21 @@ QList<QPair<int, QVariant>> KFileItemModel::ratingRoleGroups() const
     const int maxIndex = count() - 1;
     QList<QPair<int, QVariant>> groups;
 
-    /*ItemGroupInfo groupInfo;
+    ItemGroupInfo groupInfo;
     for (int i = 0; i <= maxIndex; ++i) {
         if (isChildItem(i)) {
             continue;
         }
 
-        ItemGroupInfo newGroupInfo = nameRoleGroup(fileTimeCb, m_itemData.at(i)).toString();
+        ItemGroupInfo newGroupInfo = ratingRoleGroup(m_itemData.at(i));
 
         if (newGroupInfo != groupInfo) {
             groupInfo = newGroupInfo;
-            groups.append(QPair<int, QVariant>(i, newGroupInfo));
+            // Using the numeric representation because Dolphin has a special
+            // case for drawing stars.
+            groups.append(QPair<int, QVariant>(i, newGroupInfo.comparable));
         }
-    }*/
+    }
     return groups;
 }
 
@@ -2918,17 +3004,17 @@ QList<QPair<int, QVariant>> KFileItemModel::genericStringRoleGroups(const QByteA
     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;