1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
|
/*
* SPDX-FileCopyrightText: 2011 Peter Penz <[email protected]>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KFILEITEMMODEL_H
#define KFILEITEMMODEL_H
#include "dolphin_export.h"
#include "kitemviews/kitemmodelbase.h"
#include "kitemviews/private/kfileitemmodelfilter.h"
#include <KFileItem>
#include <KLazyLocalizedString>
#include <QCollator>
#include <QHash>
#include <QSet>
#include <QUrl>
#include <functional>
class KDirLister;
class QTimer;
namespace KIO
{
class Job;
}
/**
* @brief KItemModelBase implementation for KFileItems.
*
* Allows to load items of a directory. Sorting and grouping of
* items are supported. Roles that are not part of KFileItem can
* be added with KFileItemModel::setData().
*
* Recursive expansion of sub-directories is supported by
* KFileItemModel::setExpanded().
*/
class DOLPHIN_EXPORT KFileItemModel : public KItemModelBase
{
Q_OBJECT
public:
explicit KFileItemModel(QObject *parent = nullptr);
~KFileItemModel() override;
/**
* Loads the directory specified by \a url. The signals
* directoryLoadingStarted(), directoryLoadingProgress() and directoryLoadingCompleted()
* indicate the current state of the loading process. The items
* of the directory are added after the loading has been completed.
*/
void loadDirectory(const QUrl &url);
/**
* Throws away all currently loaded items and refreshes the directory
* by reloading all items again.
*/
void refreshDirectory(const QUrl &url);
/**
* @return Parent directory of the items that are shown. In case
* if a directory tree is shown, KFileItemModel::dir() returns
* the root-parent of all items.
* @see rootItem()
*/
QUrl directory() const override;
/**
* Cancels the loading of a directory which has been started by either
* loadDirectory() or refreshDirectory().
*/
void cancelDirectoryLoading();
int count() const override;
QHash<QByteArray, QVariant> data(int index) const override;
bool setData(int index, const QHash<QByteArray, QVariant> &values) override;
/**
* Sets a separate sorting with directories first (true) or a mixed
* sorting of files and directories (false).
*/
void setSortDirectoriesFirst(bool dirsFirst);
bool sortDirectoriesFirst() const;
/**
* Sets a separate sorting with hidden files and folders last (true) or not (false).
*/
void setSortHiddenLast(bool hiddenLast);
bool sortHiddenLast() const;
void setShowHiddenFiles(bool show);
bool showHiddenFiles() const;
/**
* If set to true, only directories are shown as items of the model. Files
* are ignored.
*/
void setShowDirectoriesOnly(bool enabled);
bool showDirectoriesOnly() const;
QMimeData *createMimeData(const KItemSet &indexes) const override;
int indexForKeyboardSearch(const QString &text, int startFromIndex = 0) const override;
bool supportsDropping(int index) const override;
QString roleDescription(const QByteArray &role) const override;
QList<QPair<int, QVariant>> groups() const override;
/**
* @return The file-item for the index \a index. If the index is in a valid
* range it is assured that the file-item is not null. The runtime
* complexity of this call is O(1).
*/
KFileItem fileItem(int index) const;
/**
* @return The file-item for the url \a url. If no file-item with the given
* URL is found KFileItem::isNull() will be true for the returned
* file-item. The runtime complexity of this call is O(1).
*/
KFileItem fileItem(const QUrl &url) const;
/**
* @return The index for the file-item \a item. -1 is returned if no file-item
* is found or if the file-item is null. The amortized runtime
* complexity of this call is O(1).
*/
int index(const KFileItem &item) const;
/**
* @return The index for the URL \a url. -1 is returned if no file-item
* is found. The amortized runtime complexity of this call is O(1).
*/
int index(const QUrl &url) const;
/**
* @return Root item of all items representing the item
* for KFileItemModel::dir().
*/
KFileItem rootItem() const;
/**
* Clears all items of the model.
*/
void clear();
/**
* Sets the roles that should be shown for each item.
*/
void setRoles(const QSet<QByteArray> &roles);
QSet<QByteArray> roles() const;
bool setExpanded(int index, bool expanded) override;
bool isExpanded(int index) const override;
bool isExpandable(int index) const override;
int expandedParentsCount(int index) const override;
QSet<QUrl> expandedDirectories() const;
/**
* Marks the URLs in \a urls as sub-directories which were expanded previously.
* After calling loadDirectory() or refreshDirectory() the marked sub-directories
* will be expanded step-by-step.
*/
void restoreExpandedDirectories(const QSet<QUrl> &urls);
/**
* Expands all parent-directories of the item \a url.
*/
void expandParentDirectories(const QUrl &url);
void setNameFilter(const QString &nameFilter);
QString nameFilter() const;
void setMimeTypeFilters(const QStringList &filters);
QStringList mimeTypeFilters() const;
struct RoleInfo {
QByteArray role;
QString translation;
QString group;
QString tooltip;
bool requiresBaloo;
bool requiresIndexer;
};
/**
* @return Provides static information for all available roles that
* are supported by KFileItemModel. Some roles can only be
* determined if Baloo is enabled and/or the Baloo
* indexing is enabled.
*/
static QList<RoleInfo> rolesInformation();
Q_SIGNALS:
/**
* Is emitted if the loading of a directory has been started. It is
* assured that a signal directoryLoadingCompleted() will be send after
* the loading has been finished. For tracking the loading progress
* the signal directoryLoadingProgress() gets emitted in between.
*/
void directoryLoadingStarted();
/**
* Is emitted after the loading of a directory has been completed or new
* items have been inserted to an already loaded directory. Usually
* one or more itemsInserted() signals are emitted before loadingCompleted()
* (the only exception is loading an empty directory, where only a
* loadingCompleted() signal gets emitted).
*/
void directoryLoadingCompleted();
/**
* Is emitted after the loading of a directory has been canceled.
*/
void directoryLoadingCanceled();
/**
* Informs about the progress in percent when loading a directory. It is assured
* that the signal directoryLoadingStarted() has been emitted before.
*/
void directoryLoadingProgress(int percent);
/**
* Is emitted if the sort-role gets resolved asynchronously and provides
* the progress-information of the sorting in percent. It is assured
* that the last sortProgress-signal contains 100 as value.
*/
void directorySortingProgress(int percent);
/**
* Is emitted if an information message (e.g. "Connecting to host...")
* should be shown.
*/
void infoMessage(const QString &message);
/**
* Is emitted if an error message (e.g. "Unknown location")
* should be shown.
*/
void errorMessage(const QString &message);
/**
* Is emitted if a redirection from the current URL \a oldUrl
* to the new URL \a newUrl has been done.
*/
void directoryRedirection(const QUrl &oldUrl, const QUrl &newUrl);
/**
* Is emitted when the URL passed by KFileItemModel::setUrl() represents a file.
* In this case no signal errorMessage() will be emitted.
*/
void urlIsFileError(const QUrl &url);
/**
* It is emitted for files when they change and
* for dirs when files are added or removed.
*/
void fileItemsChanged(const KFileItemList &changedFileItems);
/**
* It is emitted when the parent directory was removed.
*/
void currentDirectoryRemoved();
protected:
void onGroupedSortingChanged(bool current) override;
void onSortRoleChanged(const QByteArray ¤t, const QByteArray &previous, bool resortItems = true) override;
void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) override;
private Q_SLOTS:
/**
* Resorts all items dependent on the set sortRole(), sortOrder()
* and foldersFirst() settings.
*/
void resortAllItems();
void slotCompleted();
void slotCanceled();
void slotItemsAdded(const QUrl &directoryUrl, const KFileItemList &items);
void slotItemsDeleted(const KFileItemList &items);
void slotRefreshItems(const QList<QPair<KFileItem, KFileItem>> &items);
void slotClear();
void slotSortingChoiceChanged();
void slotListerError(KIO::Job *job);
void dispatchPendingItemsToInsert();
private:
enum RoleType {
// User visible roles:
NoRole,
NameRole,
SizeRole,
ModificationTimeRole,
CreationTimeRole,
AccessTimeRole,
PermissionsRole,
OwnerRole,
GroupRole,
TypeRole,
ExtensionRole,
DestinationRole,
PathRole,
DeletionTimeRole,
// User visible roles available with Baloo:
CommentRole,
TagsRole,
RatingRole,
DimensionsRole,
WidthRole,
HeightRole,
ImageDateTimeRole,
OrientationRole,
PublisherRole,
PageCountRole,
WordCountRole,
TitleRole,
AuthorRole,
LineCountRole,
ArtistRole,
GenreRole,
AlbumRole,
DurationRole,
TrackRole,
ReleaseYearRole,
BitrateRole,
OriginUrlRole,
AspectRatioRole,
FrameRateRole,
// Non-visible roles:
IsDirRole,
IsLinkRole,
IsHiddenRole,
IsExpandedRole,
IsExpandableRole,
ExpandedParentsCountRole,
// Mandatory last entry:
RolesCount
};
struct ItemData {
KFileItem item;
QHash<QByteArray, QVariant> values;
ItemData *parent;
};
enum RemoveItemsBehavior { KeepItemData, DeleteItemData, DeleteItemDataIfUnfiltered };
void insertItems(QList<ItemData *> &items);
void removeItems(const KItemRangeList &itemRanges, RemoveItemsBehavior behavior);
/**
* Helper method for insertItems() and removeItems(): Creates
* a list of ItemData elements based on the given items.
* Note that the ItemData instances are created dynamically and
* must be deleted by the caller.
*/
QList<ItemData *> createItemDataList(const QUrl &parentUrl, const KFileItemList &items) const;
/**
* Prepares the items for sorting. Normally, the hash 'values' in ItemData is filled
* lazily to save time and memory, but for some sort roles, it is expected that the
* sort role data is stored in 'values'.
*/
void prepareItemsForSorting(QList<ItemData *> &itemDataList);
static int expandedParentsCount(const ItemData *data);
void removeExpandedItems();
/**
* This function is called by setData() and slotRefreshItems(). It emits
* the itemsChanged() signal, checks if the sort order is still correct,
* and starts m_resortAllItemsTimer if that is not the case.
*/
void emitItemsChangedAndTriggerResorting(const KItemRangeList &itemRanges, const QSet<QByteArray> &changedRoles);
/**
* Resets all values from m_requestRole to false.
*/
void resetRoles();
/**
* @return Role-type for the given role.
* Runtime complexity is O(1).
*/
RoleType typeForRole(const QByteArray &role) const;
/**
* @return Role-byte-array for the given role-type.
* Runtime complexity is O(1).
*/
QByteArray roleForType(RoleType roleType) const;
QHash<QByteArray, QVariant> retrieveData(const KFileItem &item, const ItemData *parent) const;
/**
* @return True if role values benefit from natural or case insensitive sorting.
*/
static bool isRoleValueNatural(const RoleType roleType);
/**
* @return True if \a a has a KFileItem whose text is 'less than' the one
* of \a b according to QString::operator<(const QString&).
*/
static bool nameLessThan(const ItemData *a, const ItemData *b);
/**
* @return True if the item-data \a a should be ordered before the item-data
* \b. The item-data may have different parent-items.
*/
bool lessThan(const ItemData *a, const ItemData *b, const QCollator &collator) const;
/**
* Sorts the items between \a begin and \a end using the comparison
* function lessThan().
*/
void sort(const QList<ItemData *>::iterator &begin, const QList<ItemData *>::iterator &end) const;
/**
* Helper method for lessThan() and expandedParentsCountCompare(): Compares
* the passed item-data using m_sortRole as criteria. Both items must
* have the same parent item, otherwise the comparison will be wrong.
*/
int sortRoleCompare(const ItemData *a, const ItemData *b, const QCollator &collator) const;
int stringCompare(const QString &a, const QString &b, const QCollator &collator) const;
QList<QPair<int, QVariant>> nameRoleGroups() const;
QList<QPair<int, QVariant>> sizeRoleGroups() const;
QList<QPair<int, QVariant>> timeRoleGroups(const std::function<QDateTime(const ItemData *)> &fileTimeCb) const;
QList<QPair<int, QVariant>> permissionRoleGroups() const;
QList<QPair<int, QVariant>> ratingRoleGroups() const;
QList<QPair<int, QVariant>> genericStringRoleGroups(const QByteArray &typeForRole) const;
/**
* Helper method for all xxxRoleGroups() methods to check whether the
* item with the given index is a child-item. A child-item is defined
* as item having an expansion-level > 0. All xxxRoleGroups() methods
* should skip the grouping if the item is a child-item (although
* KItemListView would be capable to show sub-groups in groups this
* results in visual clutter for most usecases).
*/
bool isChildItem(int index) const;
/**
* Is invoked by KFileItemModelRolesUpdater and results in emitting the
* sortProgress signal with a percent-value of the progress.
*/
void emitSortProgress(int resolvedCount);
/**
* Applies the filters set through @ref setNameFilter and @ref setMimeTypeFilters.
*/
void applyFilters();
/**
* Removes filtered items whose expanded parents have been deleted
* or collapsed via setExpanded(parentIndex, false).
*/
void removeFilteredChildren(const KItemRangeList &parents);
/**
* Loads the selected choice of sorting method from Dolphin General Settings
*/
void loadSortingSettings();
/**
* Maps the QByteArray-roles to RoleTypes and provides translation- and
* group-contexts.
*/
struct RoleInfoMap {
const char *const role;
const RoleType roleType;
const KLazyLocalizedString roleTranslation;
const KLazyLocalizedString groupTranslation;
const KLazyLocalizedString tooltipTranslation;
const bool requiresBaloo;
const bool requiresIndexer;
};
/**
* @return Map of user visible roles that are accessible by KFileItemModel::rolesInformation().
*/
static const RoleInfoMap *rolesInfoMap(int &count);
/**
* Determines the MIME-types of all items that can be done within
* the given timeout.
*/
static void determineMimeTypes(const KFileItemList &items, int timeout);
/**
* @return Returns a copy of \a value that is implicitly shared
* with other users to save memory.
*/
static QByteArray sharedValue(const QByteArray &value);
/**
* Checks if the model's internal data structures are consistent.
*/
bool isConsistent() const;
/**
* Filters out the expanded folders that don't pass the filter themselves and don't have any filter-passing children.
* Will update the removedItemRanges arguments to include the parents that have been filtered.
* @returns the number of parents that have been filtered.
* @param removedItemRanges The ranges of items being deleted/filtered, will get updated
* @param parentsToEnsureVisible Parents that must be visible no matter what due to being ancestors of newly visible items
*/
int filterChildlessParents(KItemRangeList &removedItemRanges, const QSet<ItemData *> &parentsToEnsureVisible = QSet<ItemData *>());
private:
KDirLister *m_dirLister = nullptr;
QCollator m_collator;
bool m_naturalSorting;
bool m_sortDirsFirst;
bool m_sortHiddenLast;
RoleType m_sortRole;
int m_sortingProgressPercent; // Value of directorySortingProgress() signal
QSet<QByteArray> m_roles;
QList<ItemData *> m_itemData;
// m_items is a cache for the method index(const QUrl&). If it contains N
// entries, it is guaranteed that these correspond to the first N items in
// the model, i.e., that (for every i between 0 and N - 1)
// m_items.value(fileItem(i).url()) == i
mutable QHash<QUrl, int> m_items;
KFileItemModelFilter m_filter;
QHash<KFileItem, ItemData *> m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter()
bool m_requestRole[RolesCount];
QTimer *m_maximumUpdateIntervalTimer;
QTimer *m_resortAllItemsTimer;
QList<ItemData *> m_pendingItemsToInsert;
// Cache for KFileItemModel::groups()
mutable QList<QPair<int, QVariant>> m_groups;
// Stores the URLs (key: target url, value: url) of the expanded directories.
QHash<QUrl, QUrl> m_expandedDirs;
// URLs that must be expanded. The expanding is initially triggered in setExpanded()
// and done step after step in slotCompleted().
QSet<QUrl> m_urlsToExpand;
friend class KFileItemModelRolesUpdater; // Accesses emitSortProgress() method
friend class KFileItemModelTest; // For unit testing
friend class KFileItemModelBenchmark; // For unit testing
friend class KFileItemListViewTest; // For unit testing
friend class DolphinPart; // Accesses m_dirLister
};
inline bool KFileItemModel::isRoleValueNatural(RoleType roleType)
{
return (roleType == TypeRole || roleType == ExtensionRole || roleType == TagsRole || roleType == CommentRole || roleType == TitleRole
|| roleType == ArtistRole || roleType == GenreRole || roleType == AlbumRole || roleType == PathRole || roleType == DestinationRole
|| roleType == OriginUrlRole || roleType == OwnerRole || roleType == GroupRole);
}
inline bool KFileItemModel::nameLessThan(const ItemData *a, const ItemData *b)
{
return a->item.text() < b->item.text();
}
inline bool KFileItemModel::isChildItem(int index) const
{
if (m_itemData.at(index)->parent) {
return true;
} else {
return false;
}
}
#endif
|