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 5310 : 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 : SuggestionConfRole = Qt::UserRole + 10, // double: suggestion confidence
32 : };
33 :
34 : explicit MailListModel(QObject *parent = nullptr);
35 :
36 : // QAbstractTableModel interface
37 : int rowCount(const QModelIndex &parent = {}) const override;
38 : int columnCount(const QModelIndex &parent = {}) const override;
39 : QVariant data(const QModelIndex &index,
40 : int role = Qt::DisplayRole) const override;
41 : QVariant headerData(int section, Qt::Orientation orientation,
42 : int role = Qt::DisplayRole) const override;
43 :
44 : // Sprint 76 (T-76.B3): re-emit header data so translated column headers
45 : // (Subject/From/Date/Size/Has attachment) refresh on a live language switch.
46 : void retranslateUi();
47 :
48 : // T-102: Drag & Drop support
49 : Qt::ItemFlags flags(const QModelIndex &index) const override;
50 : QStringList mimeTypes() const override;
51 : QMimeData *mimeData(const QModelIndexList &indexes) const override;
52 : Qt::DropActions supportedDragActions() const override;
53 :
54 : // --- Data management ---
55 :
56 : // Replace all headers (initial load from cache).
57 : void setHeaders(const QList<MailHeader> &headers);
58 :
59 : // Append new headers (incremental sync from IMAP).
60 : void appendHeaders(const QList<MailHeader> &headers);
61 :
62 : // Clear all data.
63 : void clear();
64 :
65 : // Update flags for a specific UID. Emits dataChanged for the row.
66 : // T-514: folderId required for composite key lookup.
67 : void updateFlags(qint64 uid, quint32 newFlags, qint64 folderId);
68 :
69 : // T-71: Update attachment status for a row (called after storeAttachments).
70 : void setHasAttachments(qint64 uid, qint64 folderId, bool has);
71 :
72 : // Remove a mail by UID. Uses beginRemoveRows/endRemoveRows.
73 : // T-514: folderId required for composite key lookup.
74 : void removeByUid(qint64 uid, qint64 folderId);
75 :
76 : // T-525/Cx8: Batch remove — single rebuild instead of O(N²)
77 : // T-514: folderId required (all UIDs must be from the same folder).
78 : void removeByUids(const QList<qint64> &uids, qint64 folderId);
79 :
80 : // Get the header at a given row. Returns nullptr if out of range.
81 : const MailHeader *headerAt(int row) const;
82 : MailHeader *mutableHeaderAt(int row);
83 :
84 : // Get the UID at a given row. Returns -1 if out of range.
85 : qint64 uidAt(int row) const;
86 :
87 : // Find the row index for a given UID. Returns -1 if not found.
88 : // T-514: folderId required for composite key lookup.
89 : int rowForUid(qint64 uid, qint64 folderId) const;
90 :
91 : // Count of unseen (unread) mails in the current model.
92 : int unreadCount() const;
93 :
94 : // T-099: Access the full header list for thread model population.
95 64 : const QList<MailHeader> &allHeaders() const { return m_headers; }
96 :
97 : // T-232: Set/clear folder suggestion for a specific UID.
98 : // T-514: folderId required for composite key lookup.
99 : void setSuggestion(qint64 uid, qint64 folderId, const QString &text, double confidence);
100 : void clearSuggestions();
101 :
102 : public:
103 : // 67.B4: humanized date for the Date column (shared with
104 : // MailThreadModel). Today "14:32" · yesterday "Yesterday" · this week
105 : // "Mon" · this year "12. May" · older "12.05.2024".
106 : static QString formatDate(const QDateTime &dt);
107 :
108 : private:
109 :
110 : // Format size for display: "1.2 KB", "3.4 MB"
111 : static QString formatSize(qint64 bytes);
112 :
113 : // T-115: Rebuild the UID→row index from m_headers.
114 : void rebuildUidIndex();
115 :
116 : QList<MailHeader> m_headers;
117 :
118 : // T-514: O(1) (folderId,UID)→row lookup index.
119 : // Maintained in sync with m_headers by all mutating methods.
120 : QHash<MailKey, int> m_uidIndex;
121 :
122 : // T-116: Cached unread (unseen) count — avoids O(n) scan on each query.
123 : // Maintained by all mutating methods (setHeaders, appendHeaders,
124 : // updateFlags, removeByUid, clear).
125 : int m_unreadCount = 0;
126 :
127 : // T-232: Suggestion cache — (folderId,UID) → display text + confidence
128 : struct SuggestionData {
129 : QString text; // e.g. "→ Projekte 96%"
130 : double confidence; // for color coding
131 : };
132 : QHash<MailKey, SuggestionData> m_suggestionCache;
133 : };
|