feat: EmailTreeModel grouping modes + MailListView integration

- EmailTreeModel: 4 grouping modes
  * NoGrouping - flat list
  * GroupByDate - chronological groups (Hoy, Ayer, Esta semana...)
  * GroupByThread - conversations with expandable threads (ThreadId from References/In-Reply-To/Message-ID)
  * GroupBySender - groups by sender email/name

- EmailTreeModel thread support:
  * ThreadItem type with expand/collapse
  * ThreadId extraction via ThreadUtils (References > In-Reply-To > Message-ID)
  * Double-click thread node toggles expansion
  * Threads sorted by newest email first, emails within thread oldest first

- EmailTreeModel sender grouping:
  * Groups by sender name/email
  * Senders sorted by newest email first
  * Emails within group newest first

- MailListView: Added 'Agrupar por remitente' to group-by combo
- MailListView: Double-click thread node toggles expansion (not open mail)
- ThreadUtils: parseReferencesHeader, extractThreadId, generateThreadId
This commit is contained in:
2026-08-25 18:53:51 +02:00
parent fa699b5399
commit 6e6fdf9c21
75 changed files with 25631 additions and 12574 deletions
+19 -3
View File
@@ -25,7 +25,10 @@ public:
IsGroupRole = Qt::UserRole + 2,
GroupNameRole = Qt::UserRole + 3,
GroupIndexRole = Qt::UserRole + 4,
MailIndexRole = Qt::UserRole + 5
MailIndexRole = Qt::UserRole + 5,
IsThreadRole = Qt::UserRole + 6,
ThreadIdRole = Qt::UserRole + 7,
IsThreadExpandedRole = Qt::UserRole + 8
};
explicit EmailTreeModel(QObject *parent = nullptr);
@@ -47,20 +50,28 @@ public:
// Grouping
enum GroupMode {
NoGrouping = 0,
GroupByDate = 1
GroupByDate = 1,
GroupByThread = 2,
GroupBySender = 3
};
void setGroupMode(GroupMode mode);
GroupMode groupMode() const { return m_groupMode; }
// Thread expansion
void setThreadExpanded(const QString& threadId, bool expanded);
bool isThreadExpanded(const QString& threadId) const;
private:
struct TreeItem {
enum Type { Group, Mail } type;
enum Type { Group, Mail, Thread } type;
QString groupName; // for Group type
QString threadId; // for Thread type
int groupIndex = -1; // for Group type
int mailIndex = -1; // for Mail type (index in m_emails)
QVector<TreeItem*> children;
TreeItem* parent = nullptr;
int row = -1;
bool expanded = true; // for Thread type
TreeItem(Type t, TreeItem* p = nullptr) : type(t), parent(p) {}
~TreeItem() { qDeleteAll(children); }
@@ -69,10 +80,15 @@ private:
GroupMode m_groupMode = NoGrouping;
QVector<MailItem> m_emails;
TreeItem* m_rootItem = nullptr;
QSet<QString> m_expandedThreads;
void setupModelData();
void buildGroups();
void buildThreads();
void buildSenderGroups();
QString groupNameForDate(const QDateTime &date) const;
int groupIndexForDate(const QDateTime &date) const;
void deleteTree(TreeItem* item);
void buildThreadTree();
QString extractThreadId(const MailItem& mail) const;
};