Line data Source code
1 : // SPDX-FileCopyrightText: 2014 Anne Jan Brouwer
2 : // SPDX-License-Identifier: GPL-3.0-or-later
3 : /**
4 : * @file
5 : * @brief StoreModel, the filtering/sorting proxy over the store's
6 : * QFileSystemModel, and StoreDragItem, the payload of a tree drag.
7 : */
8 : #ifndef SRC_STOREMODEL_H_
9 : #define SRC_STOREMODEL_H_
10 :
11 : #include <QFileInfo>
12 : #include <QSortFilterProxyModel>
13 : #include <optional>
14 :
15 : class Pass;
16 : class QFileSystemModel;
17 :
18 : /**
19 : * @struct StoreDragItem
20 : * @ingroup store
21 : * @brief What a drag from the store tree carries: the kind of the dragged
22 : * item and its path.
23 : *
24 : * Travels as the `kStoreDragMimeType` payload
25 : * (`application/vnd.qtpass.store-item`), serialised by operator<<() and read
26 : * back by operator>>().
27 : */
28 61 : struct StoreDragItem {
29 : /**
30 : * @brief Type of the dragged item.
31 : */
32 : enum class ItemKind {
33 : Unknown, /**< Item type has not been determined yet */
34 : Directory, /**< Dragged item is a directory */
35 : File /**< Dragged item is a file */
36 : };
37 :
38 : ItemKind kind{ItemKind::Unknown}; /**< Type of dragged item */
39 : QString path; /**< Full path to the dragged item */
40 : };
41 :
42 : class QDataStream;
43 :
44 : /**
45 : * @brief Serialize a StoreDragItem for the QtPass MIME type.
46 : * @param out Stream to write to.
47 : * @param info Drag/drop info to serialize.
48 : * @return The same stream, for chaining.
49 : */
50 : auto operator<<(QDataStream &out, const StoreDragItem &info) -> QDataStream &;
51 :
52 : /**
53 : * @brief Deserialize a StoreDragItem from the QtPass MIME type.
54 : *
55 : * Unrecognised kind bytes resolve to ItemKind::Unknown rather than being
56 : * rejected, so a future protocol bump remains decode-safe.
57 : *
58 : * @param in Stream to read from.
59 : * @param info Drag/drop info to populate.
60 : * @return The same stream, for chaining.
61 : */
62 : auto operator>>(QDataStream &in, StoreDragItem &info) -> QDataStream &;
63 :
64 : /// @cond DOXYGEN_MISPARSES_CONSTEXPR_INIT
65 : // The one MIME type the store tree drags: a serialised StoreDragItem (see the
66 : // struct's documentation). Hidden from Doxygen, which reads the
67 : // constructor-style initialiser as a function taking a string literal.
68 : inline constexpr QLatin1StringView
69 : kStoreDragMimeType("application/vnd.qtpass.store-item");
70 : /// @endcond
71 :
72 : /**
73 : * @class StoreModel
74 : * @ingroup store
75 : * @brief QSortFilterProxyModel for filtering and displaying password store.
76 : *
77 : * StoreModel wraps QFileSystemModel to provide password store-specific
78 : * filtering and display functionality. It handles:
79 : * - Hiding the `.git` directory from display
80 : * - Removing .gpg extension from file names
81 : * - Supporting drag and drop operations
82 : * - Custom sorting (directories before files)
83 : */
84 : class StoreModel : public QSortFilterProxyModel {
85 0 : Q_OBJECT
86 :
87 : private:
88 : /**
89 : * @brief The wrapped file system model, set by setModelAndStore(); null
90 : * until then, which makes showThis() hide everything.
91 : */
92 : QFileSystemModel *fs;
93 : /**
94 : * @brief Store root used to make paths relative for filtering and for the
95 : * drop boundary check.
96 : */
97 : QString store;
98 : /**
99 : * @brief Backend that performs drag-and-drop Move/Copy; not owned, null
100 : * until setPass(), in which case drops are refused.
101 : */
102 : Pass *m_pass{nullptr};
103 :
104 : /**
105 : * @brief Decode the drag payload from @p data.
106 : * @param data MIME data of the drop; may be null.
107 : * @return The payload, or nothing when @p data is null, carries no payload
108 : * of ours, or the payload does not deserialise cleanly.
109 : */
110 : static auto parseDropData(const QMimeData *data)
111 : -> std::optional<StoreDragItem>;
112 :
113 : /**
114 : * @brief Resolve a validated drop into a Move or Copy.
115 : *
116 : * Both endpoints must resolve inside the store after symlink resolution
117 : * (the payload could be crafted). A directory is dropped into the target
118 : * folder; a file onto a folder goes into it, a file onto a file asks the
119 : * user before overwriting.
120 : * @param info The decoded drag payload.
121 : * @param action Qt::MoveAction or Qt::CopyAction.
122 : * @param parent Proxy index of the drop target.
123 : * @return true once the operation has been dispatched to the backend;
124 : * false for an escaping path, an unknown kind or a bad target.
125 : */
126 : auto executeDropAction(const StoreDragItem &info, Qt::DropAction action,
127 : const QModelIndex &parent) -> bool;
128 : /**
129 : * @brief Perform the actual Move/Copy for a validated drop.
130 : * @param cleanedSrc Canonical source path (inside the store).
131 : * @param cleanedDest Canonical destination path (inside the store).
132 : * @param action Qt::MoveAction or Qt::CopyAction.
133 : * @param force Overwrite an existing destination.
134 : * @return true once the operation has been dispatched.
135 : */
136 : auto performDrop(const QString &cleanedSrc, const QString &cleanedDest,
137 : Qt::DropAction action, bool force) -> bool;
138 :
139 : public:
140 : /**
141 : * @brief Construct a StoreModel.
142 : */
143 : StoreModel();
144 :
145 : /**
146 : * @brief Inject the Pass backend for drag-and-drop Move/Copy operations.
147 : * @param pass Pass backend instance; must outlive this StoreModel.
148 : */
149 : void setPass(Pass *pass);
150 :
151 : /**
152 : * @brief Filter whether a row should be displayed.
153 : * @param source_row Row index in source model.
154 : * @param source_parent Parent index in source model.
155 : * @return true if row should be displayed.
156 : */
157 : [[nodiscard]] auto filterAcceptsRow(int source_row,
158 : const QModelIndex &source_parent) const
159 : -> bool override;
160 :
161 : /**
162 : * @brief Check if a specific index should be shown.
163 : * @param index Model index to check.
164 : * @return true if index should be visible.
165 : */
166 : [[nodiscard]] auto showThis(const QModelIndex &index) const -> bool;
167 :
168 : /**
169 : * @brief Initialize model with source model and store path.
170 : * @param sourceModel Filesystem model to wrap.
171 : * @param passStore Root path of password store.
172 : */
173 : void setModelAndStore(QFileSystemModel *sourceModel,
174 : const QString &passStore);
175 :
176 : /**
177 : * @brief Set the source filesystem root and map it to a proxy index.
178 : *
179 : * Wraps the recurring `mapFromSource(fs->setRootPath(path))` dance used to
180 : * point the tree view at a directory, so callers don't reach through to the
181 : * wrapped QFileSystemModel.
182 : * @param path Directory to make the source root.
183 : * @return Proxy index for `path`, or an invalid index if no source model is
184 : * set.
185 : */
186 : auto rootIndexFor(const QString &path) -> QModelIndex;
187 :
188 : /**
189 : * @brief Update the store path used for filtering without changing the source
190 : * model.
191 : * @param passStore New root path of password store.
192 : */
193 : void setStore(const QString &passStore);
194 :
195 : /**
196 : * @brief Get display data for index.
197 : * @param index Model index.
198 : * @param role Data role (e.g., Qt::DisplayRole).
199 : * @return Display data for the role.
200 : */
201 : [[nodiscard]] auto data(const QModelIndex &index, int role) const
202 : -> QVariant override;
203 :
204 : /**
205 : * @brief Compare two indices for sorting.
206 : * @param source_left Left index to compare.
207 : * @param source_right Right index to compare.
208 : * @return true if left < right.
209 : */
210 : [[nodiscard]] auto lessThan(const QModelIndex &source_left,
211 : const QModelIndex &source_right) const
212 : -> bool override;
213 :
214 : /**
215 : * @brief Get the password store root path.
216 : * @return Store path.
217 : */
218 : [[nodiscard]] auto getStore() const -> QString { return store; }
219 :
220 : // QAbstractItemModel interface
221 : public:
222 : /**
223 : * @brief Get supported drop actions.
224 : * @return Supported Qt::DropActions.
225 : */
226 : [[nodiscard]] auto supportedDropActions() const -> Qt::DropActions override;
227 :
228 : /**
229 : * @brief Get supported drag actions.
230 : * @return Supported Qt::DropActions.
231 : */
232 : [[nodiscard]] auto supportedDragActions() const -> Qt::DropActions override;
233 :
234 : /**
235 : * @brief Get item flags for index.
236 : * @param index Model index.
237 : * @return Item flags.
238 : */
239 : [[nodiscard]] auto flags(const QModelIndex &index) const
240 : -> Qt::ItemFlags override;
241 :
242 : /**
243 : * @brief Get supported MIME types for drag/drop.
244 : * @return List of MIME type strings.
245 : */
246 : [[nodiscard]] auto mimeTypes() const -> QStringList override;
247 :
248 : /**
249 : * @brief Create MIME data from indexes.
250 : * @param indexes List of selected indexes.
251 : * @return MIME data object.
252 : */
253 : [[nodiscard]] auto mimeData(const QModelIndexList &indexes) const
254 : -> QMimeData * override;
255 :
256 : /**
257 : * @brief Check if drop is possible.
258 : * @param data MIME data to drop.
259 : * @param action Drop action.
260 : * @param row Target row.
261 : * @param column Target column.
262 : * @param parent Target parent index.
263 : * @return true if drop is possible.
264 : */
265 : auto canDropMimeData(const QMimeData *data, Qt::DropAction action, int row,
266 : int column, const QModelIndex &parent) const
267 : -> bool override;
268 :
269 : /**
270 : * @brief Handle dropped MIME data.
271 : * @param data MIME data to drop.
272 : * @param action Drop action.
273 : * @param row Target row.
274 : * @param column Target column.
275 : * @param parent Target parent index.
276 : * @return true if drop was handled.
277 : */
278 : auto dropMimeData(const QMimeData *data, Qt::DropAction action, int row,
279 : int column, const QModelIndex &parent) -> bool override;
280 : };
281 :
282 : #endif // SRC_STOREMODEL_H_
|