fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kio / kio / kdirlister_p.h
blob8c4c97d033245a4126fc5bcd33554fdf27202ff9
1 /* This file is part of the KDE project
2 Copyright (C) 2002-2006 Michael Brade <brade@kde.org>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #ifndef kdirlister_p_h
21 #define kdirlister_p_h
23 #include "kfileitem.h"
25 #include <QtCore/QMap>
26 #include <QtCore/QHash>
27 #include <QtCore/QCache>
28 #include <QtCore/QSet>
29 #include <QtCore/QTimer>
30 #include <QtGui/QWidget>
32 #include <kurl.h>
33 #include <kio/global.h>
34 #include <kdirwatch.h>
36 class KDirLister;
37 namespace KIO { class Job; class ListJob; }
38 class OrgKdeKDirNotifyInterface;
39 struct KDirListerCacheDirectoryData;
41 class KDirLister::Private
43 public:
44 Private(KDirLister *parent)
45 : m_parent(parent)
47 complete = false;
49 autoUpdate = false;
51 autoErrorHandling = false;
52 errorParent = 0;
54 delayedMimeTypes = false;
56 rootFileItem = KFileItem();
58 lstNewItems = 0;
59 lstRefreshItems = 0;
60 lstMimeFilteredItems = 0;
61 lstRemoveItems = 0;
63 refreshItemWasFiltered = false;
64 hasPendingChanges = false;
66 window = 0;
67 m_cachedItemsJob = 0;
70 void _k_emitCachedItems(const KUrl&, bool, bool);
71 void _k_slotInfoMessage( KJob*, const QString& );
72 void _k_slotPercent( KJob*, unsigned long );
73 void _k_slotTotalSize( KJob*, qulonglong );
74 void _k_slotProcessedSize( KJob*, qulonglong );
75 void _k_slotSpeed( KJob*, unsigned long );
77 bool doMimeExcludeFilter( const QString& mimeExclude, const QStringList& filters ) const;
78 void jobStarted( KIO::ListJob * );
79 void connectJob( KIO::ListJob * );
80 void jobDone( KIO::ListJob * );
81 uint numJobs();
82 void addNewItem(const KUrl& directoryUrl, const KFileItem& item);
83 void addNewItems(const KUrl& directoryUrl, const KFileItemList& items);
84 void aboutToRefreshItem(const KFileItem& item);
85 void addRefreshItem(const KUrl& directoryUrl, const KFileItem& oldItem, const KFileItem& item);
86 void emitItems();
87 void emitItemsDeleted(const KFileItemList &items);
89 /**
90 * Redirect this dirlister from oldUrl to newUrl.
91 * @param keepItems if true, keep the fileitems (e.g. when renaming an existing dir);
92 * if false, clear out everything (e.g. when redirecting during listing).
94 void redirect(const KUrl& oldUrl, const KUrl& newUrl, bool keepItems);
96 /**
97 * Should this item be visible according to the current filter settings?
99 bool isItemVisible(const KFileItem& item) const;
101 void prepareForSettingsChange() {
102 if (!hasPendingChanges) {
103 hasPendingChanges = true;
104 oldSettings = settings;
108 void emitChanges();
111 KDirLister *m_parent;
114 * List of dirs handled by this dirlister. The first entry is the base URL.
115 * For a tree view, it contains all the dirs shown.
117 KUrl::List lstDirs;
119 // toplevel URL
120 KUrl url;
122 bool complete:1;
124 bool autoUpdate:1;
126 bool delayedMimeTypes:1;
128 bool refreshItemWasFiltered:1;
129 bool hasPendingChanges:1; // i.e. settings != oldSettings
131 bool autoErrorHandling:2;
132 QWidget *errorParent;
134 struct JobData {
135 long unsigned int percent, speed;
136 KIO::filesize_t processedSize, totalSize;
139 QMap<KIO::ListJob *, JobData> jobData;
141 // file item for the root itself (".")
142 KFileItem rootFileItem;
144 typedef QHash<KUrl, KFileItemList> NewItemsHash;
145 NewItemsHash *lstNewItems;
146 QList<QPair<KFileItem,KFileItem> > *lstRefreshItems;
147 KFileItemList *lstMimeFilteredItems, *lstRemoveItems;
149 QWidget *window; // Main window this lister is associated with
150 class CachedItemsJob;
151 CachedItemsJob* m_cachedItemsJob;
153 QString nameFilter; // parsed into lstFilters
155 struct FilterSettings {
156 FilterSettings() : isShowingDotFiles(false), dirOnlyMode(false) {}
157 bool isShowingDotFiles;
158 bool dirOnlyMode;
159 QList<QRegExp> lstFilters;
160 QStringList mimeFilter;
161 QStringList mimeExcludeFilter;
163 FilterSettings settings;
164 FilterSettings oldSettings;
166 friend class KDirListerCache;
170 * Design of the cache:
171 * There is a single KDirListerCache for the whole process.
172 * It holds all the items used by the dir listers (itemsInUse)
173 * as well as a cache of the recently used items (itemsCached).
174 * Those items are grouped by directory (a DirItem represents a whole directory).
176 * KDirListerCache also runs all the jobs for listing directories, whether they are for
177 * normal listing or for updates.
178 * For faster lookups, it also stores a hash table, which gives for a directory URL:
179 * - the dirlisters holding that URL (listersCurrentlyHolding)
180 * - the dirlisters currently listing that URL (listersCurrentlyListing)
182 class KDirListerCache : public QObject
184 Q_OBJECT
185 public:
186 KDirListerCache(); // only called by K_GLOBAL_STATIC
187 ~KDirListerCache();
189 void updateDirectory( const KUrl& dir );
191 KFileItem itemForUrl( const KUrl& url ) const;
192 KFileItemList *itemsForDir(const KUrl& dir) const;
194 bool listDir( KDirLister *lister, const KUrl& _url, bool _keep, bool _reload );
196 // stop all running jobs for lister
197 void stop( KDirLister *lister, bool silent = false );
198 // stop just the job listing url for lister
199 void stop( KDirLister *lister, const KUrl &_url, bool silent = false );
201 void setAutoUpdate( KDirLister *lister, bool enable );
203 void forgetDirs( KDirLister *lister );
204 void forgetDirs( KDirLister *lister, const KUrl &_url, bool notify );
206 KFileItem findByName( const KDirLister *lister, const QString &_name ) const;
207 // findByUrl returns a pointer so that it's possible to modify the item.
208 // See itemForUrl for the version that returns a readonly kfileitem.
209 // @param lister can be 0. If set, it is checked that the url is held by the lister
210 KFileItem *findByUrl(const KDirLister *lister, const KUrl &url) const;
212 // Called by CachedItemsJob:
213 // Emits those items, for this lister and this url
214 void emitItemsFromCache(KDirLister* lister, const KFileItemList& lst, const KFileItem& rootItem,
215 const KUrl& _url, bool _reload, bool _emitCompleted);
217 public Q_SLOTS:
219 * Notify that files have been added in @p directory
220 * The receiver will list that directory again to find
221 * the new items (since it needs more than just the names anyway).
222 * Connected to the DBus signal from the KDirNotify interface.
224 void slotFilesAdded( const QString& urlDirectory );
227 * Notify that files have been deleted.
228 * This call passes the exact urls of the deleted files
229 * so that any view showing them can simply remove them
230 * or be closed (if its current dir was deleted)
231 * Connected to the DBus signal from the KDirNotify interface.
233 void slotFilesRemoved( const QStringList& fileList );
236 * Notify that files have been changed.
237 * At the moment, this is only used for new icon, but it could be
238 * used for size etc. as well.
239 * Connected to the DBus signal from the KDirNotify interface.
241 void slotFilesChanged( const QStringList& fileList );
242 void slotFileRenamed( const QString& srcUrl, const QString& dstUrl );
244 private Q_SLOTS:
245 void slotFileDirty( const QString &_file );
246 void slotFileCreated( const QString &_file );
247 void slotFileDeleted( const QString &_file );
249 void slotEntries( KIO::Job *job, const KIO::UDSEntryList &entries );
250 void slotResult( KJob *j );
251 void slotRedirection( KIO::Job *job, const KUrl &url );
253 void slotUpdateEntries( KIO::Job *job, const KIO::UDSEntryList &entries );
254 void slotUpdateResult( KJob *job );
255 void processPendingUpdates();
257 private:
258 class DirItem;
259 DirItem* dirItemForUrl(const KUrl& dir) const;
261 bool validUrl( const KDirLister *lister, const KUrl& _url ) const;
263 // helper for both stop methods
264 void stopLister(KDirLister* lister, const QString& url, KDirListerCacheDirectoryData& dirData, bool silent);
266 KIO::ListJob *jobForUrl( const QString& url, KIO::ListJob *not_job = 0 );
267 const KUrl& joburl( KIO::ListJob *job );
269 void killJob( KIO::ListJob *job );
271 // Called when something tells us that the directory @p url has changed.
272 // Returns true if @p url is held by some lister (meaning: do the update now)
273 // otherwise mark the cached item as not-up-to-date for later and return false
274 bool checkUpdate( const QString& url );
276 // when there were items deleted from the filesystem all the listers holding
277 // the parent directory need to be notified, the unmarked items have to be deleted
278 // and removed from the cache including all the children.
279 void deleteUnmarkedItems( const QList<KDirLister *>&, KFileItemList & );
280 // Helper method called when we know that a list of items was deleted
281 void itemsDeleted(const QList<KDirLister *>& listers, const KFileItemList& deletedItems);
282 void slotFilesRemoved(const KUrl::List& urls);
283 // common for slotRedirection and slotFileRenamed
284 void renameDir( const KUrl &oldUrl, const KUrl &url );
285 // common for deleteUnmarkedItems and slotFilesRemoved
286 void deleteDir( const KUrl& dirUrl );
287 // remove directory from cache (itemsCached), including all child dirs
288 void removeDirFromCache( const KUrl& dir );
289 // helper for renameDir
290 void emitRedirections( const KUrl &oldUrl, const KUrl &url );
292 void aboutToRefreshItem( const KFileItem& fileitem );
295 * Emits refreshItem() in the directories that cared for oldItem.
297 void emitRefreshItem(const KFileItem& oldItem, const KFileItem& fileitem);
299 #ifndef NDEBUG
300 void printDebug();
301 #endif
303 class DirItem
305 public:
306 DirItem( const KUrl &dir )
307 : url(dir)
309 autoUpdates = 0;
310 complete = false;
313 ~DirItem()
315 if ( autoUpdates )
317 if ( KDirWatch::exists() && url.isLocalFile() )
318 KDirWatch::self()->removeDir( url.path() );
319 sendSignal( false, url );
321 lstItems.clear();
324 void sendSignal( bool entering, const KUrl& url )
326 // Note that "entering" means "start watching", and "leaving" means "stop watching"
327 // (i.e. it's not when the user leaves the directory, it's when the directory is removed from the cache)
328 if (entering)
329 org::kde::KDirNotify::emitEnteredDirectory( url.url() );
330 else
331 org::kde::KDirNotify::emitLeftDirectory( url.url() );
334 void redirect( const KUrl& newUrl )
336 if ( autoUpdates )
338 if ( url.isLocalFile() )
339 KDirWatch::self()->removeDir( url.path() );
340 sendSignal( false, url );
342 if ( newUrl.isLocalFile() )
343 KDirWatch::self()->addDir( newUrl.path() );
344 sendSignal( true, newUrl );
347 url = newUrl;
349 if ( !rootItem.isNull() )
350 rootItem.setUrl( newUrl );
353 void incAutoUpdate()
355 if ( autoUpdates++ == 0 )
357 if ( url.isLocalFile() )
358 KDirWatch::self()->addDir( url.path() );
359 sendSignal( true, url );
363 void decAutoUpdate()
365 if ( --autoUpdates == 0 )
367 if ( url.isLocalFile() )
368 KDirWatch::self()->removeDir( url.path() );
369 sendSignal( false, url );
372 else if ( autoUpdates < 0 )
373 autoUpdates = 0;
376 // number of KDirListers using autoUpdate for this dir
377 short autoUpdates;
379 // this directory is up-to-date
380 bool complete;
382 // the complete url of this directory
383 KUrl url;
385 // KFileItem representing the root of this directory.
386 // Remember that this is optional. FTP sites don't return '.' in
387 // the list, so they give no root item
388 KFileItem rootItem;
389 KFileItemList lstItems;
392 //static const unsigned short MAX_JOBS_PER_LISTER;
394 QMap<KIO::ListJob *, KIO::UDSEntryList> runningListJobs;
396 // an item is a complete directory
397 QHash<QString /*url*/, DirItem*> itemsInUse;
398 QCache<QString /*url*/, DirItem> itemsCached;
400 typedef QHash<QString /*url*/, KDirListerCacheDirectoryData> DirectoryDataHash;
401 DirectoryDataHash directoryData;
403 // Set of local files that we have changed recently (according to KDirWatch)
404 // We temporize the notifications by keeping them 500ms in this list.
405 QSet<QString /*url*/> pendingUpdates;
406 // The timer for doing the delayed updates
407 QTimer pendingUpdateTimer;
409 // Set of remote files that have changed recently -- but we can't emit those
410 // changes yet, we need to wait for the "update" directory listing.
411 // The cmp() call can't differ mimetypes since they are determined on demand,
412 // this is why we need to remember those files here.
413 QSet<KFileItem*> pendingRemoteUpdates;
415 // the KDirNotify signals
416 OrgKdeKDirNotifyInterface *kdirnotify;
418 struct ItemInUseChange;
421 // Data associated with a directory url
422 // This could be in DirItem but only in the itemsInUse dict...
423 struct KDirListerCacheDirectoryData
425 // A lister can be EITHER in listersCurrentlyListing OR listersCurrentlyHolding
426 // but NOT in both at the same time.
427 // But both lists can have different listers at the same time; this
428 // happens if more listers are requesting url at the same time and
429 // one lister was stopped during the listing of files.
431 // Listers that are currently listing this url
432 QList<KDirLister *> listersCurrentlyListing;
433 // Listers that are currently holding this url
434 QList<KDirLister *> listersCurrentlyHolding;
436 void moveListersWithoutCachedItemsJob();
439 //const unsigned short KDirListerCache::MAX_JOBS_PER_LISTER = 5;
441 // This job tells KDirListerCache to emit cached items asynchronously from listDir()
442 // to give the KDirLister user enough time for connecting to its signals, and so
443 // that KDirListerCache behaves just like when a real KIO::Job is used: nothing
444 // is emitted during the openUrl call itself.
445 class KDirLister::Private::CachedItemsJob : public KJob {
446 Q_OBJECT
447 public:
448 CachedItemsJob(KDirLister* lister, const KFileItemList& items, const KFileItem& rootItem,
449 const KUrl& url, bool reload)
450 : KJob(lister),
451 m_lister(lister), m_url(url),
452 m_items(items), m_rootItem(rootItem),
453 m_reload(reload), m_emitCompleted(true) {
454 Q_ASSERT(lister->d->m_cachedItemsJob == 0);
455 lister->d->m_cachedItemsJob = this;
456 setAutoDelete(true);
457 start();
460 /*reimp*/ void start() { QMetaObject::invokeMethod(this, "done", Qt::QueuedConnection); }
462 // For updateDirectory() to cancel m_emitCompleted;
463 void setEmitCompleted(bool b) { m_emitCompleted = b; }
465 KUrl url() const { return m_url; }
467 public Q_SLOTS:
468 void done();
470 private:
471 KDirLister* m_lister;
472 KUrl m_url;
473 KFileItemList m_items;
474 KFileItem m_rootItem;
475 bool m_reload;
476 bool m_emitCompleted;
479 #endif