From 8ca90a070a22dc0c9d020ef252438b542d93266c Mon Sep 17 00:00:00 2001
From: Marco Langer <langer.m86@gmail.com>
Date: Sat, 1 Feb 2025 17:59:53 +0100
Subject: [PATCH] Fix sorting of advanced attribute list (#10091)

Sort advanced attribute list using locale aware sort.

Fixes #6175
---
 src/gui/entry/EntryAttributesModel.cpp |  6 ++++--
 src/gui/entry/EntryAttributesModel.h   |  2 ++
 tests/TestEntryModel.cpp               | 13 +++++++++++++
 3 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/src/gui/entry/EntryAttributesModel.cpp b/src/gui/entry/EntryAttributesModel.cpp
index 9ce9ed251..604f9af64 100644
--- a/src/gui/entry/EntryAttributesModel.cpp
+++ b/src/gui/entry/EntryAttributesModel.cpp
@@ -24,6 +24,7 @@ EntryAttributesModel::EntryAttributesModel(QObject* parent)
     , m_entryAttributes(nullptr)
     , m_nextRenameDataChange(false)
 {
+    m_collator.setNumericMode(true);
 }
 
 void EntryAttributesModel::setEntryAttributes(EntryAttributes* entryAttributes)
@@ -150,7 +151,7 @@ void EntryAttributesModel::attributeAboutToAdd(const QString& key)
 {
     QList<QString> rows = m_attributes;
     rows.append(key);
-    std::sort(rows.begin(), rows.end());
+    std::sort(rows.begin(), rows.end(), m_collator);
     int row = rows.indexOf(key);
     beginInsertRows(QModelIndex(), row, row);
 }
@@ -180,7 +181,7 @@ void EntryAttributesModel::attributeAboutToRename(const QString& oldKey, const Q
     QList<QString> rows = m_attributes;
     rows.removeOne(oldKey);
     rows.append(newKey);
-    std::sort(rows.begin(), rows.end());
+    std::sort(rows.begin(), rows.end(), m_collator);
     int newRow = rows.indexOf(newKey);
     if (newRow > oldRow) {
         newRow++;
@@ -232,4 +233,5 @@ void EntryAttributesModel::updateAttributes()
             m_attributes.append(key);
         }
     }
+    std::sort(m_attributes.begin(), m_attributes.end(), m_collator);
 }
diff --git a/src/gui/entry/EntryAttributesModel.h b/src/gui/entry/EntryAttributesModel.h
index 7d613c1f0..650426c32 100644
--- a/src/gui/entry/EntryAttributesModel.h
+++ b/src/gui/entry/EntryAttributesModel.h
@@ -19,6 +19,7 @@
 #define KEEPASSX_ENTRYATTRIBUTESMODEL_H
 
 #include <QAbstractListModel>
+#include <QCollator>
 
 class EntryAttributes;
 
@@ -55,6 +56,7 @@ private:
     EntryAttributes* m_entryAttributes;
     QList<QString> m_attributes;
     bool m_nextRenameDataChange;
+    QCollator m_collator;
 };
 
 #endif // KEEPASSX_ENTRYATTRIBUTESMODEL_H
diff --git a/tests/TestEntryModel.cpp b/tests/TestEntryModel.cpp
index 350642618..e3cdb4a46 100644
--- a/tests/TestEntryModel.cpp
+++ b/tests/TestEntryModel.cpp
@@ -201,6 +201,9 @@ void TestEntryModel::testAttributesModel()
 
     // make sure these don't generate messages
     entryAttributes->set("Title", "test");
+    entryAttributes->set("UserName", "test");
+    entryAttributes->set("Password", "test");
+    entryAttributes->set("URL", "test");
     entryAttributes->set("Notes", "test");
 
     QCOMPARE(spyDataChanged.count(), 1);
@@ -214,6 +217,16 @@ void TestEntryModel::testAttributesModel()
     entryAttributes->set("2nd", value, true);
     QVERIFY(entryAttributes->isProtected("2nd"));
     QCOMPARE(entryAttributes->value("2nd"), value);
+    entryAttributes->clear();
+
+    // test attribute sorting
+    entryAttributes->set("Test1", "1");
+    entryAttributes->set("Test11", "11");
+    entryAttributes->set("Test2", "2");
+    QCOMPARE(model->rowCount(), 3);
+    QCOMPARE(model->data(model->index(0, 0)).toString(), QString("Test1"));
+    QCOMPARE(model->data(model->index(1, 0)).toString(), QString("Test2"));
+    QCOMPARE(model->data(model->index(2, 0)).toString(), QString("Test11"));
 
     QSignalSpy spyReset(model, SIGNAL(modelReset()));
     entryAttributes->clear();