Line data Source code
1 : #pragma once
2 :
3 : #include <QList>
4 : #include <QSet>
5 : #include <QStringList>
6 : #include <QTreeView>
7 :
8 : #include "data/Models.h"
9 :
10 : class QDragEnterEvent;
11 : class QDragMoveEvent;
12 : class QDropEvent;
13 :
14 : class QStandardItemModel;
15 : class QStandardItem;
16 :
17 : // FolderTree displays the IMAP folder hierarchy in the left panel.
18 : // Renders special folders (INBOX, Sent, Drafts, Trash, Archive) with
19 : // distinct theme icons and sorts them to the top.
20 : //
21 : // Sprint 05: Added section separators, theme icons (no emojis),
22 : // selectFolder/selectedFolderPath for session restore,
23 : // expandedFolderPaths/restoreExpandedFolders for state persistence.
24 : class FolderTree : public QTreeView {
25 16 : Q_OBJECT
26 :
27 : public:
28 : explicit FolderTree(QWidget *parent = nullptr);
29 :
30 : // Populate the tree from a flat list of IMAP folders.
31 : // Builds a hierarchy based on each folder's delimiter.
32 : void setFolders(const QList<FolderInfo> &folders);
33 :
34 : // Remove all items from the tree
35 : void clear();
36 :
37 : // Programmatically select a folder by its IMAP path.
38 : // Used for session state restoration.
39 : void selectFolder(const QString &folderPath);
40 :
41 : // Returns the currently selected folder's IMAP path, or empty string.
42 : QString selectedFolderPath() const;
43 :
44 : // Set the unread count badge for a folder.
45 : // count > 0 → shows bold name + count suffix; count == 0 → resets.
46 : void setUnreadCount(const QString &folderPath, int count);
47 :
48 : // T-197: Virtual search folder
49 : void showSearchNode(int resultCount);
50 : void updateSearchCount(int count);
51 : void hideSearchNode();
52 : bool isSearchNodeSelected() const;
53 :
54 : // T-069: Set hidden folders (will be filtered from the tree).
55 13 : void setHiddenFolders(const QStringList &hidden) { m_hiddenFolders = hidden; }
56 :
57 : // Returns list of IMAP paths for all currently expanded items.
58 : QStringList expandedFolderPaths() const;
59 :
60 : // Restores expanded state from a list of IMAP paths.
61 : void restoreExpandedFolders(const QStringList &paths);
62 :
63 : // Returns the model (for testing)
64 57 : QStandardItemModel *folderModel() const { return m_model; }
65 :
66 : // T-289: Check if a folder is a special system folder (INBOX, Sent, etc.)
67 : static bool isSpecialFolder(const QString &path, const QStringList &flags);
68 :
69 : signals:
70 : // Emitted when the user selects a folder in the tree
71 : void folderSelected(const QString &folderPath);
72 : // Emitted when the user selects the virtual "Suche" (search results) node,
73 : // so the previous search view can be restored.
74 : void searchNodeSelected();
75 : // Emitted when the user closes the "Suche" node (context menu), so a running
76 : // search can be cancelled and the search view dismissed.
77 : void searchNodeCloseRequested();
78 : // T-069: Emitted when user requests hiding a folder via context menu
79 : void folderHideRequested(const QString &folderPath);
80 : // T-134: Emitted when user wants to edit folder properties
81 : void folderPropertiesRequested(const QString &folderPath);
82 : // T-103: Emitted when mails are dropped onto a folder
83 : void moveRequested(const QList<qint64> &uids, const QString &targetFolder);
84 : void moveMailIdsRequested(const QList<MailIdentity> &mails,
85 : const QString &targetFolder);
86 : // T-200: Emitted when user wants to mark all mails as read
87 : void markAllReadRequested(const QString &folderPath);
88 :
89 : // T-289: Folder management signals
90 : void createFolderRequested(const QString &parentPath);
91 : void deleteFolderRequested(const QString &folderPath);
92 : void renameFolderRequested(const QString &folderPath);
93 : void moveFolderRequested(const QString &folderPath);
94 :
95 : protected:
96 : // T-103: Drop target events
97 : void dragEnterEvent(QDragEnterEvent *event) override;
98 : void dragMoveEvent(QDragMoveEvent *event) override;
99 : void dropEvent(QDropEvent *event) override;
100 : void dragLeaveEvent(QDragLeaveEvent *event) override;
101 : void paintEvent(QPaintEvent *event) override;
102 :
103 : public:
104 : // 67.B2: Re-tint all folder icons with the current theme colors.
105 : // Connected to ThemeManager::themeChanged in the constructor.
106 : void refreshFolderIcons();
107 :
108 : private:
109 : void setupUi();
110 : QStandardItem *findOrCreateParent(const QString &path,
111 : const QString &delimiter);
112 :
113 : // Returns a QIcon from the system theme for a given folder.
114 : static QIcon iconForFolder(const FolderInfo &folder);
115 :
116 : // 67.B2: Resolve the (possibly custom) icon for one tree item,
117 : // tinted with the current theme — shared by setFolders() and
118 : // refreshFolderIcons().
119 : static QIcon themedItemIcon(const QString &path, const FolderInfo &folder);
120 :
121 : // T-126: Tint an icon with a solid color (for custom folder colors)
122 : static QIcon tintedIcon(const QIcon &icon, const QColor &color);
123 :
124 : // Find an item by its folder path (recursive).
125 : QStandardItem *findItemByPath(const QString &folderPath) const;
126 : QStandardItem *findItemByPathRecursive(QStandardItem *root,
127 : const QString &path) const;
128 :
129 : // Role to store the IMAP folder path on each item
130 : static constexpr int FolderPathRole = Qt::UserRole + 1;
131 : // Role to mark separator items
132 : static constexpr int SeparatorRole = Qt::UserRole + 2;
133 : // Role to store the original display name (e.g. "UCC" not "Arbeit/UCC")
134 : static constexpr int DisplayNameRole = Qt::UserRole + 3;
135 : // Role to store the hierarchy delimiter (e.g. "/" or ".")
136 : static constexpr int DelimiterRole = Qt::UserRole + 4;
137 : // T-289: Role to store IMAP flags (e.g. "\Sent", "\Trash") as QStringList
138 : static constexpr int FolderFlagsRole = Qt::UserRole + 5;
139 :
140 : public:
141 : // 67.B4: Role storing the unread count (painted as a badge pill by
142 : // FolderBadgeDelegate; the item text stays the plain folder name)
143 : static constexpr int UnreadCountRole = Qt::UserRole + 6;
144 :
145 : private:
146 :
147 : QStandardItemModel *m_model = nullptr;
148 : QStringList m_hiddenFolders; // T-069
149 : QSet<QString> m_hiddenSet; // T-078: computed from m_hiddenFolders
150 :
151 : // T-525: O(1) path→item cache (replaces recursive tree search)
152 : QHash<QString, QStandardItem *> m_pathCache;
153 :
154 : // DnD visual highlight (avoids triggering folderSelected during drag)
155 : QPersistentModelIndex m_dragHighlightIndex;
156 : QPersistentModelIndex m_preDragIndex;
157 :
158 : // T-197: Virtual search node
159 : QStandardItem *m_searchItem = nullptr;
160 : static constexpr auto SearchNodePath = "__SEARCH__";
161 : };
|