Line data Source code
1 : #pragma once
2 :
3 : #include <QAbstractTableModel>
4 : #include <QHash>
5 : #include <QList>
6 : #include <QMimeData>
7 : #include <QPair>
8 :
9 : #include "data/Models.h"
10 :
11 : // MailListModel displays mail headers in a table format (Star, Subject, From,
12 : // Date, Size). Designed for use with QSortFilterProxyModel for sorting. Unread
13 : // (unseen) mails are rendered in bold (FontRole). Star column shows ⭐ for
14 : // flagged mails.
15 : class MailListModel : public QAbstractTableModel {
16 5558 : Q_OBJECT
17 :
18 : public:
19 : // T-514: Composite identity key (folderId, uid) — UIDs are only unique per folder
20 : using MailKey = QPair<qint64, qint64>;
21 : // Column order: Star/status column first (narrow icon), then attachment
22 : // indicator, then content.
23 : enum Column { Star = 0, Attachment, Subject, Suggestion, From, Date, Size, ColumnCount };
24 :
25 : // Custom roles for filtering/sorting beyond Qt::UserRole
26 : enum Role {
27 : SortRole = Qt::UserRole, // Raw values for proper sorting
28 : FlagsRole = Qt::UserRole + 1, // quint32 flags bitmask
29 : HasAttachmentsRole = Qt::UserRole + 2, // bool: has attachments
30 : LabelsRole = Qt::UserRole + 3, // QStringList: IMAP keywords/labels
31 : // T-79.E5/M16: search-mode folder badge (display-only, per folderId)
32 : FolderBadgeRole = Qt::UserRole + 4,
33 : SuggestionConfRole = Qt::UserRole + 10, // double: suggestion confidence
34 : };
35 :
36 : explicit MailListModel(QObject *parent = nullptr);
37 :
38 : // QAbstractTableModel interface
39 : int rowCount(const QModelIndex &parent = {}) const override;
40 : int columnCount(const QModelIndex &parent = {}) const override;
41 : QVariant data(const QModelIndex &index,
42 : int role = Qt::DisplayRole) const override;
43 : QVariant headerData(int section, Qt::Orientation orientation,
44 : int role = Qt::DisplayRole) const override;
45 :
46 : // Sprint 76 (T-76.B3): re-emit header data so translated column headers
47 : // (Subject/From/Date/Size/Has attachment) refresh on a live language switch.
48 : void retranslateUi();
49 :
50 : // T-102: Drag & Drop support
51 : Qt::ItemFlags flags(const QModelIndex &index) const override;
52 : QStringList mimeTypes() const override;
53 : QMimeData *mimeData(const QModelIndexList &indexes) const override;
54 : Qt::DropActions supportedDragActions() const override;
55 :
56 : // --- Data management ---
57 :
58 : // Replace all headers (initial load from cache).
59 : void setHeaders(const QList<MailHeader> &headers);
60 :
61 : // Append new headers (incremental sync from IMAP).
62 : void appendHeaders(const QList<MailHeader> &headers);
63 :
64 : // Clear all data.
65 : void clear();
66 :
67 : // Update flags for a specific UID. Emits dataChanged for the row.
68 : // T-514: folderId required for composite key lookup.
69 : void updateFlags(qint64 uid, quint32 newFlags, qint64 folderId);
70 :
71 : // T-71: Update attachment status for a row (called after storeAttachments).
72 : void setHasAttachments(qint64 uid, qint64 folderId, bool has);
73 :
74 : // Remove a mail by UID. Uses beginRemoveRows/endRemoveRows.
75 : // T-514: folderId required for composite key lookup.
76 : void removeByUid(qint64 uid, qint64 folderId);
77 :
78 : // T-525/Cx8: Batch remove — single rebuild instead of O(N²)
79 : // T-514: folderId required (all UIDs must be from the same folder).
80 : void removeByUids(const QList<qint64> &uids, qint64 folderId);
81 :
82 : // Get the header at a given row. Returns nullptr if out of range.
83 : const MailHeader *headerAt(int row) const;
84 : MailHeader *mutableHeaderAt(int row);
85 :
86 : // Get the UID at a given row. Returns -1 if out of range.
87 : qint64 uidAt(int row) const;
88 :
89 : // Find the row index for a given UID. Returns -1 if not found.
90 : // T-514: folderId required for composite key lookup.
91 : int rowForUid(qint64 uid, qint64 folderId) const;
92 :
93 : // Count of unseen (unread) mails in the current model.
94 : int unreadCount() const;
95 :
96 : // T-099: Access the full header list for thread model population.
97 24 : const QList<MailHeader> &allHeaders() const { return m_headers; }
98 :
99 : // T-232: Set/clear folder suggestion for a specific UID.
100 : // T-514: folderId required for composite key lookup.
101 : void setSuggestion(qint64 uid, qint64 folderId, const QString &text, double confidence);
102 : void clearSuggestions();
103 :
104 : // T-79.E5/M16: search-mode folder badges. Register before populating the
105 : // model with search results; clear when leaving search mode.
106 : void setFolderBadge(qint64 folderId, const QString &folderName);
107 : void clearFolderBadges();
108 :
109 : public:
110 : // 67.B4: humanized date for the Date column (shared with
111 : // MailThreadModel). Today "14:32" · yesterday "Yesterday" · this week
112 : // "Mon" · this year "12. May" · older "12.05.2024".
113 : static QString formatDate(const QDateTime &dt);
114 :
115 : private:
116 :
117 : // Format size for display: "1.2 KB", "3.4 MB"
118 : static QString formatSize(qint64 bytes);
119 :
120 : // T-115: Rebuild the UID→row index from m_headers.
121 : void rebuildUidIndex();
122 :
123 : QList<MailHeader> m_headers;
124 :
125 : // T-514: O(1) (folderId,UID)→row lookup index.
126 : // Maintained in sync with m_headers by all mutating methods.
127 : QHash<MailKey, int> m_uidIndex;
128 :
129 : // T-116: Cached unread (unseen) count — avoids O(n) scan on each query.
130 : // Maintained by all mutating methods (setHeaders, appendHeaders,
131 : // updateFlags, removeByUid, clear).
132 : int m_unreadCount = 0;
133 :
134 : // T-232: Suggestion cache — (folderId,UID) → display text + confidence
135 : struct SuggestionData {
136 : QString text; // e.g. "→ Projekte 96%"
137 : double confidence; // for color coding
138 : };
139 : QHash<MailKey, SuggestionData> m_suggestionCache;
140 :
141 : // T-79.E5/M16: search-mode folder badges (folderId → display name).
142 : // Painted as a "[folder] " prefix in the Subject column; the underlying
143 : // MailHeader::subject stays clean so replies/forwards never carry it.
144 : QHash<qint64, QString> m_folderBadges;
145 : };
|