1 | /* |
2 | This file is part of the KDE project |
3 | SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org> |
4 | SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org> |
5 | SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org> |
6 | |
7 | SPDX-License-Identifier: LGPL-2.0-only |
8 | */ |
9 | |
10 | #ifndef KFILEPLACESMODEL_H |
11 | #define KFILEPLACESMODEL_H |
12 | |
13 | #include "kiofilewidgets_export.h" |
14 | |
15 | #include <KBookmark> |
16 | #include <QAbstractItemModel> |
17 | #include <QUrl> |
18 | |
19 | #include <solid/device.h> |
20 | #include <solid/solidnamespace.h> |
21 | |
22 | #include <memory> |
23 | |
24 | class KFilePlacesModelPrivate; |
25 | class KBookmarkManager; |
26 | |
27 | class QMimeData; |
28 | class QAction; |
29 | |
30 | /** |
31 | * @class KFilePlacesModel kfileplacesmodel.h <KFilePlacesModel> |
32 | * |
33 | * This class is a list view model. Each entry represents a "place" |
34 | * where user can access files. Only relevant when |
35 | * used with QListView or QTableView. |
36 | * @note This class is since 6.0 re-entrant |
37 | */ |
38 | class KIOFILEWIDGETS_EXPORT KFilePlacesModel : public QAbstractItemModel |
39 | { |
40 | Q_OBJECT |
41 | |
42 | Q_PROPERTY(QStringList supportedSchemes READ supportedSchemes WRITE setSupportedSchemes NOTIFY supportedSchemesChanged) |
43 | |
44 | public: |
45 | // Note: run printf "0x%08X\n" $(($RANDOM*$RANDOM)) |
46 | // to define additional roles. |
47 | enum AdditionalRoles { |
48 | /** roleName is "url". @see url() */ |
49 | UrlRole = 0x069CD12B, |
50 | |
51 | /** roleName is "isHidden". @see isHidden() */ |
52 | HiddenRole = 0x0741CAAC, |
53 | |
54 | /** roleName is "isSetupNeeded". @see setupNeeded() */ |
55 | SetupNeededRole = 0x059A935D, |
56 | |
57 | /** |
58 | * Whether the place is a fixed device (neither hotpluggable nor removable). |
59 | * roleName is "isFixedDevice". |
60 | */ |
61 | FixedDeviceRole = 0x332896C1, |
62 | |
63 | /** |
64 | * Whether the place should have its free space displayed in a capacity bar. |
65 | * roleName is "isCapacityBarRecommended". |
66 | */ |
67 | CapacityBarRecommendedRole = 0x1548C5C4, |
68 | |
69 | /** |
70 | * The name of the group, for example "Remote" or "Devices". roleName is "group". |
71 | * @since 5.40 |
72 | */ |
73 | GroupRole = 0x0a5b64ee, |
74 | |
75 | /** |
76 | * roleName is "iconName". |
77 | * @see icon() |
78 | * @since 5.41 |
79 | */ |
80 | IconNameRole = 0x00a45c00, |
81 | |
82 | /** roleName is "isGroupHidden". |
83 | * @see isGroupHidden() |
84 | * @since 5.42 |
85 | */ |
86 | GroupHiddenRole = 0x21a4b936, |
87 | |
88 | /** roleName is "isTeardownAllowed". |
89 | * @see isTeardownAllowed(). |
90 | * @since 5.91 |
91 | */ |
92 | TeardownAllowedRole = 0x02533364, |
93 | |
94 | /** roleName is "isEjectAllowed". |
95 | * @since 5.94. |
96 | */ |
97 | EjectAllowedRole = 0x0A16AC5B, |
98 | |
99 | /** |
100 | * roleName is "isTeardownOverlayRecommended". |
101 | * @see isTeardownOverlayRecommended() |
102 | * @since 5.95 |
103 | */ |
104 | TeardownOverlayRecommendedRole = 0x032EDCCE, |
105 | |
106 | /** |
107 | * roleName is "deviceAccessibility". |
108 | * @see deviceAccessibility() |
109 | * @since 5.99 |
110 | */ |
111 | DeviceAccessibilityRole = 0x023FFD93, |
112 | }; |
113 | |
114 | /** |
115 | * Describes the available group types used in this model. |
116 | * @since 5.42 |
117 | */ |
118 | enum GroupType { |
119 | PlacesType, ///< "Places" section |
120 | RemoteType, ///< "Remote" section |
121 | RecentlySavedType, ///< "Recent" section |
122 | SearchForType, ///< "Search for" section |
123 | DevicesType, ///< "Devices" section |
124 | RemovableDevicesType, ///< "Removable Devices" section |
125 | UnknownType, ///< Unknown GroupType |
126 | TagsType, ///< "Tags" section. @since 5.54 |
127 | }; |
128 | Q_ENUM(GroupType) |
129 | |
130 | enum DeviceAccessibility { SetupNeeded, SetupInProgress, Accessible, TeardownInProgress }; |
131 | Q_ENUM(DeviceAccessibility) |
132 | |
133 | explicit KFilePlacesModel(QObject *parent = nullptr); |
134 | ~KFilePlacesModel() override; |
135 | |
136 | /** |
137 | * @return The URL of the place at index @p index. |
138 | */ |
139 | Q_INVOKABLE QUrl url(const QModelIndex &index) const; |
140 | |
141 | /** |
142 | * @return Whether the place at index @p index needs to be mounted before it can be used. |
143 | */ |
144 | Q_INVOKABLE bool setupNeeded(const QModelIndex &index) const; |
145 | |
146 | /** |
147 | * @return Whether the place is a device that can be unmounted, e.g. it is |
148 | * mounted but does not point at system Root or the user's Home directory. |
149 | * |
150 | * It does not indicate whether the teardown can succeed. |
151 | * @since 5.91 |
152 | */ |
153 | Q_INVOKABLE bool isTeardownAllowed(const QModelIndex &index) const; |
154 | |
155 | /** |
156 | * @return Whether the place is a device that can be ejected, e.g. it is |
157 | * a CD, DVD, etc. |
158 | * |
159 | * It does not indicate whether the eject can succeed. |
160 | * @since 5.94 |
161 | */ |
162 | Q_INVOKABLE bool isEjectAllowed(const QModelIndex &index) const; |
163 | |
164 | /** |
165 | * @return Whether showing an inline teardown button is recommended, |
166 | * e.g. when it is a removable drive. |
167 | * |
168 | * @since 5.95 |
169 | **/ |
170 | Q_INVOKABLE bool isTeardownOverlayRecommended(const QModelIndex &index) const; |
171 | |
172 | /** |
173 | * @return Whether this device is currently accessible or being (un)mounted. |
174 | * |
175 | * @since 5.99 |
176 | */ |
177 | Q_INVOKABLE KFilePlacesModel::DeviceAccessibility deviceAccessibility(const QModelIndex &index) const; |
178 | |
179 | /** |
180 | * @return The icon of the place at index @p index. |
181 | */ |
182 | Q_INVOKABLE QIcon icon(const QModelIndex &index) const; |
183 | |
184 | /** |
185 | * @return The user-visible text of the place at index @p index. |
186 | */ |
187 | Q_INVOKABLE QString text(const QModelIndex &index) const; |
188 | |
189 | /** |
190 | * @return Whether the place at index @p index is hidden or is inside an hidden group. |
191 | */ |
192 | Q_INVOKABLE bool isHidden(const QModelIndex &index) const; |
193 | |
194 | /** |
195 | * @return Whether the group type @p type is hidden. |
196 | * @since 5.42 |
197 | */ |
198 | Q_INVOKABLE bool isGroupHidden(const GroupType type) const; |
199 | |
200 | /** |
201 | * @return Whether the group of the place at index @p index is hidden. |
202 | * @since 5.42 |
203 | */ |
204 | Q_INVOKABLE bool isGroupHidden(const QModelIndex &index) const; |
205 | |
206 | /** |
207 | * @return Whether the place at index @p index is a device handled by Solid. |
208 | * @see deviceForIndex() |
209 | */ |
210 | Q_INVOKABLE bool isDevice(const QModelIndex &index) const; |
211 | |
212 | /** |
213 | * @return The solid device of the place at index @p index, if it is a device. Otherwise a default Solid::Device() instance is returned. |
214 | * @see isDevice() |
215 | */ |
216 | Solid::Device deviceForIndex(const QModelIndex &index) const; |
217 | |
218 | /** |
219 | * @return The KBookmark instance of the place at index @p index. |
220 | * If the index is not valid, a default KBookmark instance is returned. |
221 | */ |
222 | KBookmark bookmarkForIndex(const QModelIndex &index) const; |
223 | |
224 | /** |
225 | * @return The KBookmark instance of the place with url @p searchUrl. |
226 | * If the bookmark corresponding to searchUrl is not found, a default KBookmark instance is returned. |
227 | * @since 5.63 |
228 | */ |
229 | KBookmark bookmarkForUrl(const QUrl &searchUrl) const; |
230 | |
231 | /** |
232 | * @return The group type of the place at index @p index. |
233 | * @since 5.42 |
234 | */ |
235 | Q_INVOKABLE GroupType groupType(const QModelIndex &index) const; |
236 | |
237 | /** |
238 | * @return The list of model indexes that have @ type as their group type. |
239 | * @see groupType() |
240 | * @since 5.42 |
241 | */ |
242 | Q_INVOKABLE QModelIndexList groupIndexes(const GroupType type) const; |
243 | |
244 | /** |
245 | * @return A QAction with a proper translated label that can be used to trigger the requestTeardown() |
246 | * method for the place at index @p index. |
247 | * @see requestTeardown() |
248 | */ |
249 | Q_INVOKABLE QAction *teardownActionForIndex(const QModelIndex &index) const; |
250 | |
251 | /** |
252 | * @return A QAction with a proper translated label that can be used to trigger the requestEject() |
253 | * method for the place at index @p index. |
254 | * @see requestEject() |
255 | */ |
256 | Q_INVOKABLE QAction *ejectActionForIndex(const QModelIndex &index) const; |
257 | |
258 | /** |
259 | * @return A QAction with a proper translated label that can be used to open a partitioning menu for the device. nullptr if not a device. |
260 | */ |
261 | Q_INVOKABLE QAction *partitionActionForIndex(const QModelIndex &index) const; |
262 | |
263 | /** |
264 | * Unmounts the place at index @p index by triggering the teardown functionality of its Solid device. |
265 | * @see deviceForIndex() |
266 | */ |
267 | Q_INVOKABLE void requestTeardown(const QModelIndex &index); |
268 | |
269 | /** |
270 | * Ejects the place at index @p index by triggering the eject functionality of its Solid device. |
271 | * @see deviceForIndex() |
272 | */ |
273 | Q_INVOKABLE void requestEject(const QModelIndex &index); |
274 | |
275 | /** |
276 | * Mounts the place at index @p index by triggering the setup functionality of its Solid device. |
277 | * @see deviceForIndex() |
278 | */ |
279 | Q_INVOKABLE void requestSetup(const QModelIndex &index); |
280 | |
281 | /** |
282 | * Adds a new place to the model. |
283 | * @param text The user-visible text for the place |
284 | * @param url The URL of the place. It will be stored in its QUrl::FullyEncoded string format. |
285 | * @param iconName The icon of the place |
286 | * @param appName If set as the value of QCoreApplication::applicationName(), will make the place visible only in this application. |
287 | */ |
288 | Q_INVOKABLE void addPlace(const QString &text, const QUrl &url, const QString &iconName = QString(), const QString &appName = QString()); |
289 | |
290 | /** |
291 | * Adds a new place to the model. |
292 | * @param text The user-visible text for the place |
293 | * @param url The URL of the place. It will be stored in its QUrl::FullyEncoded string format. |
294 | * @param iconName The icon of the place |
295 | * @param appName If set as the value of QCoreApplication::applicationName(), will make the place visible only in this application. |
296 | * @param after The index after which the new place will be added. |
297 | */ |
298 | Q_INVOKABLE void addPlace(const QString &text, const QUrl &url, const QString &iconName, const QString &appName, const QModelIndex &after); |
299 | |
300 | /** |
301 | * Edits the place with index @p index. |
302 | * @param text The new user-visible text for the place |
303 | * @param url The new URL of the place |
304 | * @param iconName The new icon of the place |
305 | * @param appName The new application-local filter for the place (@see addPlace()). |
306 | */ |
307 | Q_INVOKABLE void |
308 | editPlace(const QModelIndex &index, const QString &text, const QUrl &url, const QString &iconName = QString(), const QString &appName = QString()); |
309 | |
310 | /** |
311 | * Deletes the place with index @p index from the model. |
312 | */ |
313 | Q_INVOKABLE void removePlace(const QModelIndex &index) const; |
314 | |
315 | /** |
316 | * Changes the visibility of the place with index @p index, but only if the place is not inside an hidden group. |
317 | * @param hidden Whether the place should be hidden or visible. |
318 | * @see isGroupHidden() |
319 | */ |
320 | Q_INVOKABLE void setPlaceHidden(const QModelIndex &index, bool hidden); |
321 | |
322 | /** |
323 | * Changes the visibility of the group with type @p type. |
324 | * @param hidden Whether the group should be hidden or visible. |
325 | * @see isGroupHidden() |
326 | * @since 5.42 |
327 | */ |
328 | Q_INVOKABLE void setGroupHidden(const GroupType type, bool hidden); |
329 | |
330 | /** |
331 | * @brief Move place at @p itemRow to a position before @p row |
332 | * @return Whether the place has been moved. |
333 | * @since 5.41 |
334 | */ |
335 | Q_INVOKABLE bool movePlace(int itemRow, int row); |
336 | |
337 | /** |
338 | * @return The number of hidden places in the model. |
339 | * @see isHidden() |
340 | */ |
341 | Q_INVOKABLE int hiddenCount() const; |
342 | |
343 | /** |
344 | * @brief Get a visible data based on Qt role for the given index. |
345 | * Return the device information for the give index. |
346 | * |
347 | * @param index The QModelIndex which contains the row, column to fetch the data. |
348 | * @param role The Interview data role(ex: Qt::DisplayRole). |
349 | * |
350 | * @return the data for the given index and role. |
351 | */ |
352 | QVariant data(const QModelIndex &index, int role) const override; |
353 | |
354 | /** |
355 | * @brief Get the children model index for the given row and column. |
356 | */ |
357 | QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; |
358 | |
359 | /** |
360 | * @brief Get the parent QModelIndex for the given model child. |
361 | */ |
362 | QModelIndex parent(const QModelIndex &child) const override; |
363 | |
364 | /// Reimplemented from QAbstractItemModel. |
365 | /// @see AdditionalRoles |
366 | QHash<int, QByteArray> roleNames() const override; |
367 | |
368 | /** |
369 | * @brief Get the number of rows for a model index. |
370 | */ |
371 | int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
372 | |
373 | /** |
374 | * @brief Get the number of columns for a model index. |
375 | */ |
376 | int columnCount(const QModelIndex &parent = QModelIndex()) const override; |
377 | |
378 | /** |
379 | * Returns the closest item for the URL \a url. |
380 | * The closest item is defined as item which is equal to |
381 | * the URL or at least is a parent URL. If there are more than |
382 | * one possible parent URL candidates, the item which covers |
383 | * the bigger range of the URL is returned. |
384 | * |
385 | * Example: the url is '/home/peter/Documents/Music'. |
386 | * Available items are: |
387 | * - /home/peter |
388 | * - /home/peter/Documents |
389 | * |
390 | * The returned item will the one for '/home/peter/Documents'. |
391 | */ |
392 | QModelIndex closestItem(const QUrl &url) const; |
393 | |
394 | Qt::DropActions supportedDropActions() const override; |
395 | Qt::ItemFlags flags(const QModelIndex &index) const override; |
396 | QStringList mimeTypes() const override; |
397 | QMimeData *mimeData(const QModelIndexList &indexes) const override; |
398 | bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; |
399 | |
400 | /** |
401 | * @brief Reload bookmark information |
402 | * @since 5.41 |
403 | */ |
404 | Q_INVOKABLE void refresh() const; |
405 | |
406 | /** |
407 | * @brief Converts the URL, which contains "virtual" URLs for system-items like |
408 | * "timeline:/lastmonth" into a Query-URL "timeline:/2017-10" |
409 | * that will be handled by the corresponding KIO worker. |
410 | * Virtual URLs for bookmarks are used to be independent from |
411 | * internal format changes. |
412 | * @param an url |
413 | * @return the converted URL, which can be handled by a KIO worker |
414 | * @since 5.41 |
415 | */ |
416 | static QUrl convertedUrl(const QUrl &url); |
417 | |
418 | /** |
419 | * Set the URL schemes that the file widget should allow navigating to. |
420 | * |
421 | * If the returned list is empty, all schemes are supported. Examples for |
422 | * schemes are @c "file" or @c "ftp". |
423 | * |
424 | * @sa QFileDialog::setSupportedSchemes |
425 | * @since 5.43 |
426 | */ |
427 | void setSupportedSchemes(const QStringList &schemes); |
428 | |
429 | /** |
430 | * Returns the URL schemes that the file widget should allow navigating to. |
431 | * |
432 | * If the returned list is empty, all schemes are supported. |
433 | * |
434 | * @sa QFileDialog::supportedSchemes |
435 | * @since 5.43 |
436 | */ |
437 | QStringList supportedSchemes() const; |
438 | |
439 | Q_SIGNALS: |
440 | /** |
441 | * @p message An error message explaining what went wrong. |
442 | */ |
443 | void errorMessage(const QString &message); |
444 | |
445 | /** |
446 | * Emitted after the Solid setup ends. |
447 | * @param success Whether the Solid setup has been successful. |
448 | * @see requestSetup() |
449 | */ |
450 | void setupDone(const QModelIndex &index, bool success); |
451 | |
452 | /** |
453 | * Emitted after the teardown of a device ends. |
454 | * |
455 | * @note In case of an error, the @p errorMessage signal |
456 | * will also be emitted with a message describing the error. |
457 | * |
458 | * @param error Type of error that occurred, if any. |
459 | * @param errorData More information about the error, if any. |
460 | * @since 5.100 |
461 | */ |
462 | void teardownDone(const QModelIndex &index, Solid::ErrorType error, const QVariant &errorData); |
463 | |
464 | /** |
465 | * Emitted whenever the visibility of the group @p group changes. |
466 | * @param hidden The new visibility of the group. |
467 | * @see setGroupHidden() |
468 | * @since 5.42 |
469 | */ |
470 | void groupHiddenChanged(KFilePlacesModel::GroupType group, bool hidden); |
471 | |
472 | /** |
473 | * Called once the model has been reloaded |
474 | * |
475 | * @since 5.71 |
476 | */ |
477 | void reloaded(); |
478 | |
479 | /** |
480 | * Emitted whenever the list of supported schemes has been changed |
481 | * |
482 | * @since 5.94 |
483 | */ |
484 | void supportedSchemesChanged(); |
485 | |
486 | private: |
487 | friend class KFilePlacesModelPrivate; |
488 | std::unique_ptr<KFilePlacesModelPrivate> d; |
489 | }; |
490 | |
491 | #endif |
492 | |