File indexing completed on 2023-09-24 04:08:35
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2002-2006 Michael Brade <brade@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #ifndef KCOREDIRLISTER_P_H 0009 #define KCOREDIRLISTER_P_H 0010 0011 #include "kfileitem.h" 0012 0013 #include <QCache> 0014 #include <QCoreApplication> 0015 #include <QFileInfo> 0016 #include <QHash> 0017 #include <QMap> 0018 #include <QTimer> 0019 #include <QUrl> 0020 #include <QVector> 0021 0022 #include <KDirWatch> 0023 #include <kio/global.h> 0024 0025 #include <set> 0026 0027 class QRegularExpression; 0028 class KCoreDirLister; 0029 namespace KIO 0030 { 0031 class Job; 0032 class ListJob; 0033 } 0034 class OrgKdeKDirNotifyInterface; 0035 struct KCoreDirListerCacheDirectoryData; 0036 0037 class KCoreDirListerPrivate 0038 { 0039 public: 0040 explicit KCoreDirListerPrivate(KCoreDirLister *qq); 0041 0042 void emitCachedItems(const QUrl &, bool, bool); 0043 void slotInfoMessage(KJob *, const QString &); 0044 void slotPercent(KJob *, unsigned long); 0045 void slotTotalSize(KJob *, qulonglong); 0046 void slotProcessedSize(KJob *, qulonglong); 0047 void slotSpeed(KJob *, unsigned long); 0048 0049 bool doMimeExcludeFilter(const QString &mimeExclude, const QStringList &filters) const; 0050 void connectJob(KIO::ListJob *); 0051 void jobDone(KIO::ListJob *); 0052 uint numJobs(); 0053 void addNewItem(const QUrl &directoryUrl, const KFileItem &item); 0054 void addNewItems(const QUrl &directoryUrl, const QList<KFileItem> &items); 0055 void addRefreshItem(const QUrl &directoryUrl, const KFileItem &oldItem, const KFileItem &item); 0056 void emitItems(); 0057 void emitItemsDeleted(const KFileItemList &items); 0058 bool matchesFilter(const QString &name) const; 0059 bool matchesMimeFilter(const QString &mimeType) const; 0060 0061 /** 0062 * Redirect this dirlister from oldUrl to newUrl. 0063 * @param keepItems if true, keep the fileitems (e.g. when renaming an existing dir); 0064 * if false, clear out everything (e.g. when redirecting during listing). 0065 */ 0066 void redirect(const QUrl &oldUrl, const QUrl &newUrl, bool keepItems); 0067 0068 /** 0069 * Should this item be visible according to the current filter settings? 0070 */ 0071 bool isItemVisible(const KFileItem &item) const; 0072 0073 void prepareForSettingsChange() 0074 { 0075 if (!hasPendingChanges) { 0076 hasPendingChanges = true; 0077 oldSettings = settings; 0078 } 0079 } 0080 0081 void emitChanges(); 0082 0083 class CachedItemsJob; 0084 CachedItemsJob *cachedItemsJobForUrl(const QUrl &url) const; 0085 0086 KCoreDirLister *const q; 0087 0088 /** 0089 * List of dirs handled by this dirlister. The first entry is the base URL. 0090 * For a tree view, it contains all the dirs shown. 0091 */ 0092 QList<QUrl> lstDirs; 0093 0094 // toplevel URL 0095 QUrl url; 0096 0097 bool complete = false; 0098 bool autoUpdate = false; 0099 bool delayedMimeTypes = false; 0100 bool hasPendingChanges = false; // i.e. settings != oldSettings 0101 bool m_autoErrorHandling = true; 0102 bool requestMimeTypeWhileListing = false; 0103 0104 struct JobData { 0105 long unsigned int percent, speed; 0106 KIO::filesize_t processedSize, totalSize; 0107 }; 0108 0109 QMap<KIO::ListJob *, JobData> jobData; 0110 0111 // file item for the root itself (".") 0112 KFileItem rootFileItem; 0113 0114 typedef QHash<QUrl, KFileItemList> NewItemsHash; 0115 NewItemsHash lstNewItems; 0116 QList<QPair<KFileItem, KFileItem>> lstRefreshItems; 0117 KFileItemList lstMimeFilteredItems, lstRemoveItems; 0118 0119 QList<CachedItemsJob *> m_cachedItemsJobs; 0120 0121 QString nameFilter; // parsed into lstFilters 0122 0123 struct FilterSettings { 0124 FilterSettings() 0125 : isShowingDotFiles(false) 0126 , dirOnlyMode(false) 0127 { 0128 } 0129 bool isShowingDotFiles; 0130 bool dirOnlyMode; 0131 QVector<QRegularExpression> lstFilters; 0132 QStringList mimeFilter; 0133 QStringList mimeExcludeFilter; 0134 }; 0135 FilterSettings settings; 0136 FilterSettings oldSettings; 0137 0138 friend class KCoreDirListerCache; 0139 }; 0140 0141 /** 0142 * Design of the cache: 0143 * There is a single KCoreDirListerCache for the whole process. 0144 * It holds all the items used by the dir listers (itemsInUse) 0145 * as well as a cache of the recently used items (itemsCached). 0146 * Those items are grouped by directory (a DirItem represents a whole directory). 0147 * 0148 * KCoreDirListerCache also runs all the jobs for listing directories, whether they are for 0149 * normal listing or for updates. 0150 * For faster lookups, it also stores a hash table, which gives for a directory URL: 0151 * - the dirlisters holding that URL (listersCurrentlyHolding) 0152 * - the dirlisters currently listing that URL (listersCurrentlyListing) 0153 */ 0154 class KCoreDirListerCache : public QObject 0155 { 0156 Q_OBJECT 0157 public: 0158 KCoreDirListerCache(); // only called by K_GLOBAL_STATIC 0159 ~KCoreDirListerCache() override; 0160 0161 void updateDirectory(const QUrl &dir); 0162 0163 KFileItem itemForUrl(const QUrl &url) const; 0164 QList<KFileItem> *itemsForDir(const QUrl &dir) const; 0165 0166 bool listDir(KCoreDirLister *lister, const QUrl &_url, bool _keep, bool _reload); 0167 0168 // stop all running jobs for lister 0169 void stop(KCoreDirLister *lister, bool silent = false); 0170 // stop just the job listing url for lister 0171 void stopListingUrl(KCoreDirLister *lister, const QUrl &_url, bool silent = false); 0172 0173 void setAutoUpdate(KCoreDirLister *lister, bool enable); 0174 0175 void forgetDirs(KCoreDirLister *lister); 0176 void forgetDirs(KCoreDirLister *lister, const QUrl &_url, bool notify); 0177 0178 KFileItem findByName(const KCoreDirLister *lister, const QString &_name) const; 0179 // findByUrl returns a pointer so that it's possible to modify the item. 0180 // See itemForUrl for the version that returns a readonly kfileitem. 0181 // @param lister can be 0. If set, it is checked that the url is held by the lister 0182 KFileItem findByUrl(const KCoreDirLister *lister, const QUrl &url) const; 0183 0184 // Called by CachedItemsJob: 0185 // Emits the cached items, for this lister and this url 0186 void emitItemsFromCache(KCoreDirListerPrivate::CachedItemsJob *job, KCoreDirLister *lister, const QUrl &_url, bool _reload, bool _emitCompleted); 0187 // Called by CachedItemsJob: 0188 void forgetCachedItemsJob(KCoreDirListerPrivate::CachedItemsJob *job, KCoreDirLister *lister, const QUrl &url); 0189 0190 public Q_SLOTS: 0191 /** 0192 * Notify that files have been added in @p directory 0193 * The receiver will list that directory again to find 0194 * the new items (since it needs more than just the names anyway). 0195 * Connected to the DBus signal from the KDirNotify interface. 0196 */ 0197 void slotFilesAdded(const QString &urlDirectory); 0198 0199 /** 0200 * Notify that files have been deleted. 0201 * This call passes the exact urls of the deleted files 0202 * so that any view showing them can simply remove them 0203 * or be closed (if its current dir was deleted) 0204 * Connected to the DBus signal from the KDirNotify interface. 0205 */ 0206 void slotFilesRemoved(const QStringList &fileList); 0207 0208 /** 0209 * Notify that files have been changed. 0210 * At the moment, this is only used for new icon, but it could be 0211 * used for size etc. as well. 0212 * Connected to the DBus signal from the KDirNotify interface. 0213 */ 0214 void slotFilesChanged(const QStringList &fileList); 0215 void slotFileRenamed(const QString &srcUrl, const QString &dstUrl, const QString &dstPath); 0216 0217 private Q_SLOTS: 0218 void slotFileDirty(const QString &_file); 0219 void slotFileCreated(const QString &_file); 0220 void slotFileDeleted(const QString &_file); 0221 0222 void slotEntries(KIO::Job *job, const KIO::UDSEntryList &entries); 0223 void slotResult(KJob *j); 0224 void slotRedirection(KIO::Job *job, const QUrl &url); 0225 0226 void slotUpdateEntries(KIO::Job *job, const KIO::UDSEntryList &entries); 0227 void slotUpdateResult(KJob *job); 0228 void processPendingUpdates(); 0229 0230 private: 0231 void itemsAddedInDirectory(const QUrl &url); 0232 0233 class DirItem; 0234 DirItem *dirItemForUrl(const QUrl &dir) const; 0235 0236 void stopListJob(const QUrl &url, bool silent); 0237 0238 KIO::ListJob *jobForUrl(const QUrl &url, KIO::ListJob *not_job = nullptr); 0239 const QUrl &joburl(KIO::ListJob *job); 0240 0241 void killJob(KIO::ListJob *job); 0242 0243 // Called when something tells us that the directory @p url has changed. 0244 // Returns true if @p url is held by some lister (meaning: do the update now) 0245 // otherwise mark the cached item as not-up-to-date for later and return false 0246 bool checkUpdate(const QUrl &url); 0247 0248 // Helper method for slotFileDirty 0249 void handleFileDirty(const QUrl &url); 0250 void handleDirDirty(const QUrl &url); 0251 0252 // when there were items deleted from the filesystem all the listers holding 0253 // the parent directory need to be notified, the items have to be deleted 0254 // and removed from the cache including all the children. 0255 void deleteUnmarkedItems(const QList<KCoreDirLister *> &, QList<KFileItem> &lstItems, const QHash<QString, KFileItem> &itemsToDelete); 0256 0257 // Helper method called when we know that a list of items was deleted 0258 void itemsDeleted(const QList<KCoreDirLister *> &listers, const KFileItemList &deletedItems); 0259 void slotFilesRemoved(const QList<QUrl> &urls); 0260 // common for slotRedirection and slotFileRenamed 0261 void renameDir(const QUrl &oldUrl, const QUrl &url); 0262 // common for deleteUnmarkedItems and slotFilesRemoved 0263 void deleteDir(const QUrl &dirUrl); 0264 // remove directory from cache (itemsCached), including all child dirs 0265 void removeDirFromCache(const QUrl &dir); 0266 // helper for renameDir 0267 void emitRedirections(const QUrl &oldUrl, const QUrl &url); 0268 0269 /** 0270 * Emits refreshItem() in the directories that cared for oldItem. 0271 * The caller has to remember to call emitItems in the set of dirlisters returned 0272 * (but this allows to buffer change notifications) 0273 */ 0274 std::set<KCoreDirLister *> emitRefreshItem(const KFileItem &oldItem, const KFileItem &fileitem); 0275 0276 /** 0277 * Remove the item from the sorted by url list matching @p oldUrl, 0278 * that is in the wrong place (because its url has changed) and insert @p item in the right place. 0279 * @param oldUrl the previous url of the @p item 0280 * @param item the modified item to be inserted 0281 */ 0282 void reinsert(const KFileItem &item, const QUrl &oldUrl) 0283 { 0284 const QUrl parentDir = oldUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); 0285 DirItem *dirItem = dirItemForUrl(parentDir); 0286 if (dirItem) { 0287 auto it = std::lower_bound(dirItem->lstItems.begin(), dirItem->lstItems.end(), oldUrl); 0288 Q_ASSERT(it != dirItem->lstItems.end()); 0289 dirItem->lstItems.erase(it); 0290 dirItem->insert(item); 0291 } 0292 } 0293 0294 /** 0295 * When KDirWatch tells us that something changed in "dir", we need to 0296 * also notify the dirlisters that are listing a symlink to "dir" (#213799) 0297 */ 0298 QList<QUrl> directoriesForCanonicalPath(const QUrl &dir) const; 0299 0300 // Definition of the cache of ".hidden" files 0301 struct CacheHiddenFile { 0302 CacheHiddenFile(const QDateTime &mtime, std::set<QString> &&listedFilesParam) 0303 : mtime(mtime) 0304 , listedFiles(std::move(listedFilesParam)) 0305 { 0306 } 0307 QDateTime mtime; 0308 std::set<QString> listedFiles; 0309 }; 0310 0311 /** 0312 * Returns the names listed in dir's ".hidden" file, if it exists. 0313 * If a file named ".hidden" exists in the @p dir directory, this method 0314 * returns all the file names listed in that file. If it doesn't exist, an 0315 * empty set is returned. 0316 * @param dir path to the target directory. 0317 * @return names listed in the directory's ".hidden" file (empty if it doesn't exist). 0318 */ 0319 CacheHiddenFile *cachedDotHiddenForDir(const QString &dir); 0320 0321 #ifndef NDEBUG 0322 void printDebug(); 0323 #endif 0324 0325 class DirItem 0326 { 0327 public: 0328 DirItem(const QUrl &dir, const QString &canonicalPath) 0329 : url(dir) 0330 , m_canonicalPath(canonicalPath) 0331 { 0332 autoUpdates = 0; 0333 complete = false; 0334 watchedWhileInCache = false; 0335 } 0336 0337 ~DirItem() 0338 { 0339 if (autoUpdates) { 0340 if (KDirWatch::exists() && url.isLocalFile()) { 0341 KDirWatch::self()->removeDir(m_canonicalPath); 0342 } 0343 // Since sendSignal goes through D-Bus, QCoreApplication has to be available 0344 // which might not be the case anymore from a global static dtor like the 0345 // lister cache 0346 if (QCoreApplication::instance()) { 0347 sendSignal(false, url); 0348 } 0349 } 0350 lstItems.clear(); 0351 } 0352 0353 DirItem(const DirItem &) = delete; 0354 DirItem &operator=(const DirItem &) = delete; 0355 0356 void sendSignal(bool entering, const QUrl &url) 0357 { 0358 // Note that "entering" means "start watching", and "leaving" means "stop watching" 0359 // (i.e. it's not when the user leaves the directory, it's when the directory is removed from the cache) 0360 #ifndef KIO_ANDROID_STUB 0361 if (entering) { 0362 org::kde::KDirNotify::emitEnteredDirectory(url); 0363 } else { 0364 org::kde::KDirNotify::emitLeftDirectory(url); 0365 } 0366 #endif 0367 } 0368 0369 void redirect(const QUrl &newUrl) 0370 { 0371 if (autoUpdates) { 0372 if (url.isLocalFile()) { 0373 KDirWatch::self()->removeDir(m_canonicalPath); 0374 } 0375 sendSignal(false, url); 0376 0377 if (newUrl.isLocalFile()) { 0378 m_canonicalPath = QFileInfo(newUrl.toLocalFile()).canonicalFilePath(); 0379 KDirWatch::self()->addDir(m_canonicalPath); 0380 } 0381 sendSignal(true, newUrl); 0382 } 0383 0384 url = newUrl; 0385 0386 if (!rootItem.isNull()) { 0387 rootItem.setUrl(newUrl); 0388 } 0389 } 0390 0391 void incAutoUpdate() 0392 { 0393 if (autoUpdates++ == 0) { 0394 if (url.isLocalFile()) { 0395 KDirWatch::self()->addDir(m_canonicalPath); 0396 } 0397 sendSignal(true, url); 0398 } 0399 } 0400 0401 void decAutoUpdate() 0402 { 0403 if (--autoUpdates == 0) { 0404 if (url.isLocalFile()) { 0405 KDirWatch::self()->removeDir(m_canonicalPath); 0406 } 0407 sendSignal(false, url); 0408 } 0409 0410 else if (autoUpdates < 0) { 0411 autoUpdates = 0; 0412 } 0413 } 0414 0415 // Insert the item in the sorted list 0416 void insert(const KFileItem &item) 0417 { 0418 auto it = std::lower_bound(lstItems.begin(), lstItems.end(), item.url()); 0419 lstItems.insert(it, item); 0420 } 0421 0422 // Insert the already sorted items in the sorted list 0423 void insertSortedItems(const KFileItemList &items) 0424 { 0425 if (items.isEmpty()) { 0426 return; 0427 } 0428 lstItems.reserve(lstItems.size() + items.size()); 0429 auto it = lstItems.begin(); 0430 for (const auto &item : items) { 0431 it = std::lower_bound(it, lstItems.end(), item.url()); 0432 it = lstItems.insert(it, item); 0433 } 0434 } 0435 0436 // number of KCoreDirListers using autoUpdate for this dir 0437 short autoUpdates; 0438 0439 // this directory is up-to-date 0440 bool complete; 0441 0442 // the directory is watched while being in the cache (useful for proper incAutoUpdate/decAutoUpdate count) 0443 bool watchedWhileInCache; 0444 0445 // the complete url of this directory 0446 QUrl url; 0447 0448 // the local path, with symlinks resolved, so that KDirWatch works 0449 QString m_canonicalPath; 0450 0451 // KFileItem representing the root of this directory. 0452 // Remember that this is optional. FTP sites don't return '.' in 0453 // the list, so they give no root item 0454 KFileItem rootItem; 0455 QList<KFileItem> lstItems; 0456 }; 0457 0458 QMap<KIO::ListJob *, KIO::UDSEntryList> runningListJobs; 0459 0460 // an item is a complete directory 0461 QHash<QUrl, DirItem *> itemsInUse; 0462 QCache<QUrl, DirItem> itemsCached; 0463 0464 // cache of ".hidden" files 0465 QCache<QString /*dot hidden file*/, CacheHiddenFile> m_cacheHiddenFiles; 0466 0467 typedef QHash<QUrl, KCoreDirListerCacheDirectoryData> DirectoryDataHash; 0468 DirectoryDataHash directoryData; 0469 0470 // Symlink-to-directories are registered here so that we can 0471 // find the url that changed, when kdirwatch tells us about 0472 // changes in the canonical url. (#213799) 0473 QHash<QUrl /*canonical path*/, QList<QUrl> /*dirlister urls*/> canonicalUrls; 0474 0475 // Set of local files that we have changed recently (according to KDirWatch) 0476 // We temporize the notifications by keeping them 500ms in this list. 0477 std::set<QString /*path*/> pendingUpdates; 0478 std::set<QString /*path*/> pendingDirectoryUpdates; 0479 // The timer for doing the delayed updates 0480 QTimer pendingUpdateTimer; 0481 0482 // Set of remote files that have changed recently -- but we can't emit those 0483 // changes yet, we need to wait for the "update" directory listing. 0484 // The cmp() call can't differ MIME types since they are determined on demand, 0485 // this is why we need to remember those files here. 0486 std::set<KFileItem> pendingRemoteUpdates; 0487 0488 // the KDirNotify signals 0489 OrgKdeKDirNotifyInterface *kdirnotify; 0490 0491 struct ItemInUseChange; 0492 }; 0493 0494 // Data associated with a directory url 0495 // This could be in DirItem but only in the itemsInUse dict... 0496 struct KCoreDirListerCacheDirectoryData { 0497 // A lister can be EITHER in listersCurrentlyListing OR listersCurrentlyHolding 0498 // but NOT in both at the same time. 0499 // But both lists can have different listers at the same time; this 0500 // happens if more listers are requesting url at the same time and 0501 // one lister was stopped during the listing of files. 0502 0503 // Listers that are currently listing this url 0504 QList<KCoreDirLister *> listersCurrentlyListing; 0505 // Listers that are currently holding this url 0506 QList<KCoreDirLister *> listersCurrentlyHolding; 0507 0508 void moveListersWithoutCachedItemsJob(const QUrl &url); 0509 }; 0510 0511 // This job tells KCoreDirListerCache to emit cached items asynchronously from listDir() 0512 // to give the KCoreDirLister user enough time for connecting to its signals, and so 0513 // that KCoreDirListerCache behaves just like when a real KIO::Job is used: nothing 0514 // is emitted during the openUrl call itself. 0515 class KCoreDirListerPrivate::CachedItemsJob : public KJob 0516 { 0517 Q_OBJECT 0518 public: 0519 CachedItemsJob(KCoreDirLister *lister, const QUrl &url, bool reload); 0520 0521 /*reimp*/ void start() override 0522 { 0523 QMetaObject::invokeMethod(this, &KCoreDirListerPrivate::CachedItemsJob::done, Qt::QueuedConnection); 0524 } 0525 0526 // For updateDirectory() to cancel m_emitCompleted; 0527 void setEmitCompleted(bool b) 0528 { 0529 m_emitCompleted = b; 0530 } 0531 0532 QUrl url() const 0533 { 0534 return m_url; 0535 } 0536 0537 protected: 0538 bool doKill() override; 0539 0540 public Q_SLOTS: 0541 void done(); 0542 0543 private: 0544 KCoreDirLister *m_lister; 0545 QUrl m_url; 0546 bool m_reload; 0547 bool m_emitCompleted; 0548 }; 0549 0550 #endif