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