┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/src/kitemviews/kfileitemmodelrolesupdater.h
blob: 56e28ce72587dbdae53096c1b7cbc3da57df9079 (plain)
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
/*
 * SPDX-FileCopyrightText: 2011 Peter Penz <[email protected]>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#ifndef KFILEITEMMODELROLESUPDATER_H
#define KFILEITEMMODELROLESUPDATER_H

#include "dolphin_export.h"
#include "kitemviews/kitemmodelbase.h"

#include <list>

#include "config-dolphin.h"
#include <KFileItem>

#include <QObject>
#include <QSet>
#include <QSize>
#include <QStringList>

class KDirectoryContentsCounter;
class KFileItemModel;
class QPixmap;
class QTimer;
class KOverlayIconPlugin;

namespace KIO
{
class PreviewJob;
}

#if HAVE_BALOO
namespace Baloo
{
class FileMonitor;
}
#include <Baloo/IndexerConfig>
#endif

/**
 * @brief Resolves expensive roles asynchronously and applies them to the KFileItemModel.
 *
 * KFileItemModel only resolves roles that are inexpensive like e.g. the file name or
 * the permissions. Creating previews or determining the MIME-type can be quite expensive
 * and KFileItemModelRolesUpdater takes care to update such roles asynchronously.
 *
 * To prevent a huge CPU and I/O load, these roles are not updated for all
 * items, but only for the visible items, some items around the visible area,
 * and the items on the first and last pages of the view. This is a compromise
 * that aims to minimize the risk that the user sees items with unknown icons
 * in the view when scrolling or pressing Home or End.
 *
 * Determining the roles is done in several phases:
 *
 * 1.   If the sort role is "slow", it is determined for all items. If this
 *      cannot be finished synchronously in 200 ms, the remaining items are
 *      handled asynchronously by \a resolveNextSortRole().
 *
 * 2.   The function startUpdating(), which is called if either the sort role
 *      has been successfully determined for all items, or items are inserted
 *      in the view, or the visible items might have changed because items
 *      were removed or moved, tries to determine the icons for all visible
 *      items synchronously for 200 ms. Then:
 *
 *      (a) If previews are disabled, icons and all other roles are determined
 *          asynchronously for the interesting items. This is done by the
 *          function \a resolveNextPendingRoles().
 *
 *      (b) If previews are enabled, a \a KIO::PreviewJob is started that loads
 *          the previews for the interesting items. At the same time, the icons
 *          for these items are determined asynchronously as fast as possible
 *          by \a resolveNextPendingRoles(). This minimizes the risk that the
 *          user sees "unknown" icons when scrolling before the previews have
 *          arrived.
 *
 * 3.   Finally, the entire process is repeated for any items that might have
 *      changed in the mean time.
 */
class DOLPHIN_EXPORT KFileItemModelRolesUpdater : public QObject
{
    Q_OBJECT

public:
    explicit KFileItemModelRolesUpdater(KFileItemModel *model, QObject *parent = nullptr);
    ~KFileItemModelRolesUpdater() override;

    void setIconSize(const QSize &size);
    QSize iconSize() const;

    void setDevicePixelRatio(qreal devicePixelRatio);
    qreal devicePixelRatio() const;

    /**
     * Sets the range of items that are visible currently. The roles
     * of visible items are resolved first.
     */
    void setVisibleIndexRange(int index, int count);

    void setMaximumVisibleItems(int count);

    /**
     * If \a show is set to true, the "iconPixmap" role will be filled with a preview
     * of the file. If \a show is false the MIME type icon will be used for the "iconPixmap"
     * role.
     */
    void setPreviewsShown(bool show);
    bool previewsShown() const;

    /**
     * If enabled a small preview gets upscaled to the icon size in case where
     * the icon size is larger than the preview. Per default enlarging is
     * enabled.
     */
    void setEnlargeSmallPreviews(bool enlarge);
    bool enlargeSmallPreviews() const;

    /**
     * If \a paused is set to true the asynchronous resolving of roles will be paused.
     * State changes during pauses like changing the icon size or the preview-shown
     * will be remembered and handled after unpausing.
     */
    void setPaused(bool paused);
    bool isPaused() const;

    /**
     * Sets the roles that should be resolved asynchronously.
     */
    void setRoles(const QSet<QByteArray> &roles);
    QSet<QByteArray> roles() const;

    /**
     * Sets the list of enabled thumbnail plugins that are used for previews.
     * Per default all plugins enabled in the KConfigGroup "PreviewSettings"
     * are used.
     *
     * For a list of available plugins, call KIO::PreviewJob::availableThumbnailerPlugins().
     *
     * @see enabledPlugins
     */
    void setEnabledPlugins(const QStringList &list);

    /**
     * Returns the list of enabled thumbnail plugins.
     * @see setEnabledPlugins
     */
    QStringList enabledPlugins() const;

    /**
     * Sets the maximum file size of local files for which
     * previews will be generated (if enabled). A value of 0
     * indicates no file size limit.
     * Per default the value from KConfigGroup "PreviewSettings"
     * MaximumSize is used, 0 otherwise.
     * @param size
     */
    void setLocalFileSizePreviewLimit(qlonglong size);
    qlonglong localFileSizePreviewLimit() const;

    /**
     * If set to true, directories contents are scanned to determine their size
     * Default true
     */
    void setScanDirectories(bool enabled);
    bool scanDirectories() const;

    /**
     * Notifies the updater of a change in the hover state on an item.
     *
     * This will trigger asynchronous loading of the next few thumb sequence images
     * using a PreviewJob.
     *
     * @param item URL of the item that is hovered, or an empty URL if no item is hovered.
     * @param seqIdx The current hover sequence index. While an item is hovered,
     *               this method will be called repeatedly with increasing values
     *               for this parameter.
     */
    void setHoverSequenceState(const QUrl &itemUrl, int seqIdx);

Q_SIGNALS:
    void previewJobFinished(); // For unit testing

private Q_SLOTS:
    void slotItemsInserted(const KItemRangeList &itemRanges);
    void slotItemsRemoved(const KItemRangeList &itemRanges);
    void slotItemsMoved(KItemRange itemRange, const QList<int> &movedToIndexes);
    void slotItemsChanged(const KItemRangeList &itemRanges, const QSet<QByteArray> &roles);
    void slotSortRoleChanged(const QByteArray &current, const QByteArray &previous);

    /**
     * Is invoked after a preview has been received successfully.
     *
     * Note that this is not called for hover sequence previews.
     *
     * @see startPreviewJob()
     */
    void slotGotPreview(const KFileItem &item, const QPixmap &pixmap);

    /**
     * Is invoked after generating a preview has failed.
     *
     * Note that this is not called for hover sequence previews.
     *
     * @see startPreviewJob()
     */
    void slotPreviewFailed(const KFileItem &item);

    /**
     * Is invoked when the preview job has been finished. Starts a new preview
     * job if there are any interesting items without previews left, or updates
     * the changed items otherwise.
     *
     * Note that this is not called for hover sequence previews.
     *
     * @see startPreviewJob()
     */
    void slotPreviewJobFinished();

    /**
     * Is invoked after a hover sequence preview has been received successfully.
     */
    void slotHoverSequenceGotPreview(const KFileItem &item, const QPixmap &pixmap);

    /**
     * Is invoked after generating a hover sequence preview has failed.
     */
    void slotHoverSequencePreviewFailed(const KFileItem &item);

    /**
     * Is invoked when a hover sequence preview job is finished. May start another
     * job for the next sequence index right away by calling
     * \a loadNextHoverSequencePreview().
     *
     * Note that a PreviewJob will only ever generate a single sequence image, due
     * to limitations of the PreviewJob API.
     */
    void slotHoverSequencePreviewJobFinished();

    /**
     * Is invoked when one of the KOverlayIconPlugin emit the signal that an overlay has changed
     */
    void slotOverlaysChanged(const QUrl &url, const QStringList &);

    /**
     * Resolves the sort role of the next item in m_pendingSortRole, applies it
     * to the model, and invokes itself if there are any pending items left. If
     * that is not the case, \a startUpdating() is called.
     */
    void resolveNextSortRole();

    /**
     * Resolves the icon name and (if previews are disabled) all other roles
     * for the next interesting item. If there are no pending items left, any
     * changed items are updated.
     */
    void resolveNextPendingRoles();

    /**
     * Resolves items that have not been resolved yet after the change has been
     * notified by slotItemsChanged(). Is invoked if the m_changedItemsTimer
     * expires.
     */
    void resolveRecentlyChangedItems();

    void applyChangedBalooRoles(const QString &file);
    void applyChangedBalooRolesForItem(const KFileItem &file);

    void slotDirectoryContentsCountReceived(const QString &path, int count, long long size);

private:
    /**
     * Starts the updating of all roles. The visible items are handled first.
     */
    void startUpdating();

    /**
     * Loads the icons for the visible items. After 200 ms, the function
     * stops determining mime types and only loads preliminary icons.
     * This is a compromise that prevents that
     * (a) the GUI is blocked for more than 200 ms, and
     * (b) "unknown" icons could be shown in the view.
     */
    void updateVisibleIcons();

    /**
     * Creates previews for the items starting from the first item in
     * m_pendingPreviewItems.
     * @see slotGotPreview()
     * @see slotPreviewFailed()
     * @see slotPreviewJobFinished()
     */
    void startPreviewJob();

    /**
     * Transforms a raw preview image, applying scale and frame.
     *
     * @param pixmap A raw preview image from a PreviewJob.
     * @param overlays the overlays to add to the pixmap
     * @return The scaled and decorated preview image.
     */
    QPixmap transformPreviewPixmap(const QPixmap &pixmap);

    /**
     * Starts a PreviewJob for loading the next hover sequence image.
     */
    void loadNextHoverSequencePreview();

    /**
     * Aborts the currently running hover sequence PreviewJob (if any).
     */
    void killHoverSequencePreviewJob();

    /**
     * Ensures that icons, previews, and other roles are determined for any
     * items that have been changed.
     */
    void updateChangedItems();

    /**
     * Resolves the sort role of the item and applies it to the model.
     */
    void applySortRole(int index);

    void applySortProgressToModel();

    enum ResolveHint { ResolveFast, ResolveAll };
    bool applyResolvedRoles(int index, ResolveHint hint);
    QHash<QByteArray, QVariant> rolesData(const KFileItem &item, int index);

    /**
     * Must be invoked if a property has been changed that affects
     * the look of the preview. Takes care to update all previews.
     */
    void updateAllPreviews();

    void killPreviewJob();

    QList<int> indexesToResolve() const;

    void trimHoverSequenceLoadedItems();

    void resetSizeData(const int index, const int size = 0);

    void recountDirectoryItems(const QList<QUrl> directories);

private:
    QSize cacheSize();
    /**
     * enqueue directory size counting for KFileItem item at index
     */
    void startDirectorySizeCounting(const KFileItem &item, int index);

    enum State { Idle, Paused, ResolvingSortRole, ResolvingAllRoles, PreviewJobRunning };

    State m_state;

    // Property changes during pausing must be remembered to be able
    // to react when unpausing again:
    bool m_previewChangedDuringPausing;
    bool m_iconSizeChangedDuringPausing;
    bool m_rolesChangedDuringPausing;

    // Property for setPreviewsShown()/previewsShown().
    bool m_previewShown;

    // Property for setEnlargeSmallPreviews()/enlargeSmallPreviews()
    bool m_enlargeSmallPreviews;

    // True if the role "iconPixmap" should be cleared when resolving the next
    // role with resolveRole(). Is necessary if the preview gets disabled
    // during the roles-updater has been paused by setPaused().
    bool m_clearPreviews;

    // Remembers which items have been handled already, to prevent that
    // previews and other expensive roles are determined again.
    QSet<KFileItem> m_finishedItems;

    KFileItemModel *m_model;
    QSize m_iconSize;
    qreal m_devicePixelRatio;
    int m_firstVisibleIndex;
    int m_lastVisibleIndex;
    int m_maximumVisibleItems;
    QSet<QByteArray> m_roles;
    QSet<QByteArray> m_resolvableRoles;
    QStringList m_enabledPlugins;
    qulonglong m_localFileSizePreviewLimit;

    // Items for which the sort role still has to be determined.
    QSet<KFileItem> m_pendingSortRoleItems;

    // Indexes of items which still have to be handled by
    // resolveNextPendingRoles().
    QList<int> m_pendingIndexes;

    // Items which have been left over from the last call of startPreviewJob().
    // A new preview job will be started from them once the first one finishes.
    KFileItemList m_pendingPreviewItems;

    KIO::PreviewJob *m_previewJob;

    // Info about the item that the user currently hovers, and the current sequence
    // index for thumb generation.
    KFileItem m_hoverSequenceItem;
    int m_hoverSequenceIndex;
    KIO::PreviewJob *m_hoverSequencePreviewJob;
    int m_hoverSequenceNumSuccessiveFailures;
    std::list<KFileItem> m_hoverSequenceLoadedItems;

    // When downloading or copying large files, the slot slotItemsChanged()
    // will be called periodically within a quite short delay. To prevent
    // a high CPU-load by generating e.g. previews for each notification, the update
    // will be postponed until no file change has been done within a longer period
    // of time.
    QTimer *m_recentlyChangedItemsTimer;
    QSet<KFileItem> m_recentlyChangedItems;

    // Items which have not been changed repeatedly recently.
    QSet<KFileItem> m_changedItems;

    KDirectoryContentsCounter *m_directoryContentsCounter;

    QList<KOverlayIconPlugin *> m_overlayIconsPlugin;

#if HAVE_BALOO
    Baloo::FileMonitor *m_balooFileMonitor;
    Baloo::IndexerConfig m_balooConfig;
#endif
};

#endif