File indexing completed on 2024-04-28 05:45:05

0001 /*
0002  * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef KFILEITEMMODELROLESUPDATER_H
0008 #define KFILEITEMMODELROLESUPDATER_H
0009 
0010 #include "dolphin_export.h"
0011 #include "kitemviews/kitemmodelbase.h"
0012 
0013 #include <list>
0014 
0015 #include "config-dolphin.h"
0016 #include <KFileItem>
0017 
0018 #include <QObject>
0019 #include <QSet>
0020 #include <QSize>
0021 #include <QStringList>
0022 
0023 class KDirectoryContentsCounter;
0024 class KFileItemModel;
0025 class QPixmap;
0026 class QTimer;
0027 class KOverlayIconPlugin;
0028 
0029 namespace KIO
0030 {
0031 class PreviewJob;
0032 }
0033 
0034 #if HAVE_BALOO
0035 namespace Baloo
0036 {
0037 class FileMonitor;
0038 }
0039 #include <Baloo/IndexerConfig>
0040 #endif
0041 
0042 /**
0043  * @brief Resolves expensive roles asynchronously and applies them to the KFileItemModel.
0044  *
0045  * KFileItemModel only resolves roles that are inexpensive like e.g. the file name or
0046  * the permissions. Creating previews or determining the MIME-type can be quite expensive
0047  * and KFileItemModelRolesUpdater takes care to update such roles asynchronously.
0048  *
0049  * To prevent a huge CPU and I/O load, these roles are not updated for all
0050  * items, but only for the visible items, some items around the visible area,
0051  * and the items on the first and last pages of the view. This is a compromise
0052  * that aims to minimize the risk that the user sees items with unknown icons
0053  * in the view when scrolling or pressing Home or End.
0054  *
0055  * Determining the roles is done in several phases:
0056  *
0057  * 1.   If the sort role is "slow", it is determined for all items. If this
0058  *      cannot be finished synchronously in 200 ms, the remaining items are
0059  *      handled asynchronously by \a resolveNextSortRole().
0060  *
0061  * 2.   The function startUpdating(), which is called if either the sort role
0062  *      has been successfully determined for all items, or items are inserted
0063  *      in the view, or the visible items might have changed because items
0064  *      were removed or moved, tries to determine the icons for all visible
0065  *      items synchronously for 200 ms. Then:
0066  *
0067  *      (a) If previews are disabled, icons and all other roles are determined
0068  *          asynchronously for the interesting items. This is done by the
0069  *          function \a resolveNextPendingRoles().
0070  *
0071  *      (b) If previews are enabled, a \a KIO::PreviewJob is started that loads
0072  *          the previews for the interesting items. At the same time, the icons
0073  *          for these items are determined asynchronously as fast as possible
0074  *          by \a resolveNextPendingRoles(). This minimizes the risk that the
0075  *          user sees "unknown" icons when scrolling before the previews have
0076  *          arrived.
0077  *
0078  * 3.   Finally, the entire process is repeated for any items that might have
0079  *      changed in the mean time.
0080  */
0081 class DOLPHIN_EXPORT KFileItemModelRolesUpdater : public QObject
0082 {
0083     Q_OBJECT
0084 
0085 public:
0086     explicit KFileItemModelRolesUpdater(KFileItemModel *model, QObject *parent = nullptr);
0087     ~KFileItemModelRolesUpdater() override;
0088 
0089     void setIconSize(const QSize &size);
0090     QSize iconSize() const;
0091 
0092     void setDevicePixelRatio(qreal devicePixelRatio);
0093     qreal devicePixelRatio() const;
0094 
0095     /**
0096      * Sets the range of items that are visible currently. The roles
0097      * of visible items are resolved first.
0098      */
0099     void setVisibleIndexRange(int index, int count);
0100 
0101     void setMaximumVisibleItems(int count);
0102 
0103     /**
0104      * If \a show is set to true, the "iconPixmap" role will be filled with a preview
0105      * of the file. If \a show is false the MIME type icon will be used for the "iconPixmap"
0106      * role.
0107      */
0108     void setPreviewsShown(bool show);
0109     bool previewsShown() const;
0110 
0111     /**
0112      * If enabled a small preview gets upscaled to the icon size in case where
0113      * the icon size is larger than the preview. Per default enlarging is
0114      * enabled.
0115      */
0116     void setEnlargeSmallPreviews(bool enlarge);
0117     bool enlargeSmallPreviews() const;
0118 
0119     /**
0120      * If \a paused is set to true the asynchronous resolving of roles will be paused.
0121      * State changes during pauses like changing the icon size or the preview-shown
0122      * will be remembered and handled after unpausing.
0123      */
0124     void setPaused(bool paused);
0125     bool isPaused() const;
0126 
0127     /**
0128      * Sets the roles that should be resolved asynchronously.
0129      */
0130     void setRoles(const QSet<QByteArray> &roles);
0131     QSet<QByteArray> roles() const;
0132 
0133     /**
0134      * Sets the list of enabled thumbnail plugins that are used for previews.
0135      * Per default all plugins enabled in the KConfigGroup "PreviewSettings"
0136      * are used.
0137      *
0138      * For a list of available plugins, call KIO::PreviewJob::availableThumbnailerPlugins().
0139      *
0140      * @see enabledPlugins
0141      */
0142     void setEnabledPlugins(const QStringList &list);
0143 
0144     /**
0145      * Returns the list of enabled thumbnail plugins.
0146      * @see setEnabledPlugins
0147      */
0148     QStringList enabledPlugins() const;
0149 
0150     /**
0151      * Sets the maximum file size of local files for which
0152      * previews will be generated (if enabled). A value of 0
0153      * indicates no file size limit.
0154      * Per default the value from KConfigGroup "PreviewSettings"
0155      * MaximumSize is used, 0 otherwise.
0156      * @param size
0157      */
0158     void setLocalFileSizePreviewLimit(qlonglong size);
0159     qlonglong localFileSizePreviewLimit() const;
0160 
0161     /**
0162      * If set to true, directories contents are scanned to determine their size
0163      * Default true
0164      */
0165     void setScanDirectories(bool enabled);
0166     bool scanDirectories() const;
0167 
0168     /**
0169      * Notifies the updater of a change in the hover state on an item.
0170      *
0171      * This will trigger asynchronous loading of the next few thumb sequence images
0172      * using a PreviewJob.
0173      *
0174      * @param item URL of the item that is hovered, or an empty URL if no item is hovered.
0175      * @param seqIdx The current hover sequence index. While an item is hovered,
0176      *               this method will be called repeatedly with increasing values
0177      *               for this parameter.
0178      */
0179     void setHoverSequenceState(const QUrl &itemUrl, int seqIdx);
0180 
0181 private Q_SLOTS:
0182     void slotItemsInserted(const KItemRangeList &itemRanges);
0183     void slotItemsRemoved(const KItemRangeList &itemRanges);
0184     void slotItemsMoved(KItemRange itemRange, const QList<int> &movedToIndexes);
0185     void slotItemsChanged(const KItemRangeList &itemRanges, const QSet<QByteArray> &roles);
0186     void slotSortRoleChanged(const QByteArray &current, const QByteArray &previous);
0187 
0188     /**
0189      * Is invoked after a preview has been received successfully.
0190      *
0191      * Note that this is not called for hover sequence previews.
0192      *
0193      * @see startPreviewJob()
0194      */
0195     void slotGotPreview(const KFileItem &item, const QPixmap &pixmap);
0196 
0197     /**
0198      * Is invoked after generating a preview has failed.
0199      *
0200      * Note that this is not called for hover sequence previews.
0201      *
0202      * @see startPreviewJob()
0203      */
0204     void slotPreviewFailed(const KFileItem &item);
0205 
0206     /**
0207      * Is invoked when the preview job has been finished. Starts a new preview
0208      * job if there are any interesting items without previews left, or updates
0209      * the changed items otherwise.
0210      *
0211      * Note that this is not called for hover sequence previews.
0212      *
0213      * @see startPreviewJob()
0214      */
0215     void slotPreviewJobFinished();
0216 
0217     /**
0218      * Is invoked after a hover sequence preview has been received successfully.
0219      */
0220     void slotHoverSequenceGotPreview(const KFileItem &item, const QPixmap &pixmap);
0221 
0222     /**
0223      * Is invoked after generating a hover sequence preview has failed.
0224      */
0225     void slotHoverSequencePreviewFailed(const KFileItem &item);
0226 
0227     /**
0228      * Is invoked when a hover sequence preview job is finished. May start another
0229      * job for the next sequence index right away by calling
0230      * \a loadNextHoverSequencePreview().
0231      *
0232      * Note that a PreviewJob will only ever generate a single sequence image, due
0233      * to limitations of the PreviewJob API.
0234      */
0235     void slotHoverSequencePreviewJobFinished();
0236 
0237     /**
0238      * Is invoked when one of the KOverlayIconPlugin emit the signal that an overlay has changed
0239      */
0240     void slotOverlaysChanged(const QUrl &url, const QStringList &);
0241 
0242     /**
0243      * Resolves the sort role of the next item in m_pendingSortRole, applies it
0244      * to the model, and invokes itself if there are any pending items left. If
0245      * that is not the case, \a startUpdating() is called.
0246      */
0247     void resolveNextSortRole();
0248 
0249     /**
0250      * Resolves the icon name and (if previews are disabled) all other roles
0251      * for the next interesting item. If there are no pending items left, any
0252      * changed items are updated.
0253      */
0254     void resolveNextPendingRoles();
0255 
0256     /**
0257      * Resolves items that have not been resolved yet after the change has been
0258      * notified by slotItemsChanged(). Is invoked if the m_changedItemsTimer
0259      * expires.
0260      */
0261     void resolveRecentlyChangedItems();
0262 
0263     void applyChangedBalooRoles(const QString &file);
0264     void applyChangedBalooRolesForItem(const KFileItem &file);
0265 
0266     void slotDirectoryContentsCountReceived(const QString &path, int count, long long size);
0267 
0268 private:
0269     /**
0270      * Starts the updating of all roles. The visible items are handled first.
0271      */
0272     void startUpdating();
0273 
0274     /**
0275      * Loads the icons for the visible items. After 200 ms, the function
0276      * stops determining mime types and only loads preliminary icons.
0277      * This is a compromise that prevents that
0278      * (a) the GUI is blocked for more than 200 ms, and
0279      * (b) "unknown" icons could be shown in the view.
0280      */
0281     void updateVisibleIcons();
0282 
0283     /**
0284      * Creates previews for the items starting from the first item in
0285      * m_pendingPreviewItems.
0286      * @see slotGotPreview()
0287      * @see slotPreviewFailed()
0288      * @see slotPreviewJobFinished()
0289      */
0290     void startPreviewJob();
0291 
0292     /**
0293      * Transforms a raw preview image, applying scale and frame.
0294      *
0295      * @param pixmap A raw preview image from a PreviewJob.
0296      * @return The scaled and decorated preview image.
0297      */
0298     QPixmap transformPreviewPixmap(const QPixmap &pixmap);
0299 
0300     /**
0301      * Starts a PreviewJob for loading the next hover sequence image.
0302      */
0303     void loadNextHoverSequencePreview();
0304 
0305     /**
0306      * Aborts the currently running hover sequence PreviewJob (if any).
0307      */
0308     void killHoverSequencePreviewJob();
0309 
0310     /**
0311      * Ensures that icons, previews, and other roles are determined for any
0312      * items that have been changed.
0313      */
0314     void updateChangedItems();
0315 
0316     /**
0317      * Resolves the sort role of the item and applies it to the model.
0318      */
0319     void applySortRole(int index);
0320 
0321     void applySortProgressToModel();
0322 
0323     enum ResolveHint { ResolveFast, ResolveAll };
0324     bool applyResolvedRoles(int index, ResolveHint hint);
0325     QHash<QByteArray, QVariant> rolesData(const KFileItem &item, int index);
0326 
0327     /**
0328      * Must be invoked if a property has been changed that affects
0329      * the look of the preview. Takes care to update all previews.
0330      */
0331     void updateAllPreviews();
0332 
0333     void killPreviewJob();
0334 
0335     QList<int> indexesToResolve() const;
0336 
0337     void trimHoverSequenceLoadedItems();
0338 
0339 private:
0340     /**
0341      * enqueue directory size counting for KFileItem item at index
0342      */
0343     void startDirectorySizeCounting(const KFileItem &item, int index);
0344 
0345     enum State { Idle, Paused, ResolvingSortRole, ResolvingAllRoles, PreviewJobRunning };
0346 
0347     State m_state;
0348 
0349     // Property changes during pausing must be remembered to be able
0350     // to react when unpausing again:
0351     bool m_previewChangedDuringPausing;
0352     bool m_iconSizeChangedDuringPausing;
0353     bool m_rolesChangedDuringPausing;
0354 
0355     // Property for setPreviewsShown()/previewsShown().
0356     bool m_previewShown;
0357 
0358     // Property for setEnlargeSmallPreviews()/enlargeSmallPreviews()
0359     bool m_enlargeSmallPreviews;
0360 
0361     // True if the role "iconPixmap" should be cleared when resolving the next
0362     // role with resolveRole(). Is necessary if the preview gets disabled
0363     // during the roles-updater has been paused by setPaused().
0364     bool m_clearPreviews;
0365 
0366     // Remembers which items have been handled already, to prevent that
0367     // previews and other expensive roles are determined again.
0368     QSet<KFileItem> m_finishedItems;
0369 
0370     KFileItemModel *m_model;
0371     QSize m_iconSize;
0372     qreal m_devicePixelRatio;
0373     int m_firstVisibleIndex;
0374     int m_lastVisibleIndex;
0375     int m_maximumVisibleItems;
0376     QSet<QByteArray> m_roles;
0377     QSet<QByteArray> m_resolvableRoles;
0378     QStringList m_enabledPlugins;
0379     qulonglong m_localFileSizePreviewLimit;
0380 
0381     // Items for which the sort role still has to be determined.
0382     QSet<KFileItem> m_pendingSortRoleItems;
0383 
0384     // Indexes of items which still have to be handled by
0385     // resolveNextPendingRoles().
0386     QList<int> m_pendingIndexes;
0387 
0388     // Items which have been left over from the last call of startPreviewJob().
0389     // A new preview job will be started from them once the first one finishes.
0390     KFileItemList m_pendingPreviewItems;
0391 
0392     KIO::PreviewJob *m_previewJob;
0393 
0394     // Info about the item that the user currently hovers, and the current sequence
0395     // index for thumb generation.
0396     KFileItem m_hoverSequenceItem;
0397     int m_hoverSequenceIndex;
0398     KIO::PreviewJob *m_hoverSequencePreviewJob;
0399     int m_hoverSequenceNumSuccessiveFailures;
0400     std::list<KFileItem> m_hoverSequenceLoadedItems;
0401 
0402     // When downloading or copying large files, the slot slotItemsChanged()
0403     // will be called periodically within a quite short delay. To prevent
0404     // a high CPU-load by generating e.g. previews for each notification, the update
0405     // will be postponed until no file change has been done within a longer period
0406     // of time.
0407     QTimer *m_recentlyChangedItemsTimer;
0408     QSet<KFileItem> m_recentlyChangedItems;
0409 
0410     // Items which have not been changed repeatedly recently.
0411     QSet<KFileItem> m_changedItems;
0412 
0413     KDirectoryContentsCounter *m_directoryContentsCounter;
0414 
0415     QList<KOverlayIconPlugin *> m_overlayIconsPlugin;
0416 
0417 #if HAVE_BALOO
0418     Baloo::FileMonitor *m_balooFileMonitor;
0419     Baloo::IndexerConfig m_balooConfig;
0420 #endif
0421 };
0422 
0423 #endif