• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdelibs API Reference
  • KDE Home
  • Contact Us
 

KIO

  • sources
  • kde-4.14
  • kdelibs
  • kio
  • kio
kdirlister.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE project
2  Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
3  2000 Carsten Pfeiffer <pfeiffer@kde.org>
4  2003-2005 David Faure <faure@kde.org>
5  2001-2006 Michael Brade <brade@kde.org>
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Library General Public
9  License as published by the Free Software Foundation; either
10  version 2 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Library General Public License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 */
22 
23 #include "kdirlister.h"
24 #include "kdirlister_p.h"
25 
26 #include <QtCore/QRegExp>
27 
28 #include <kdebug.h>
29 #include <kde_file.h>
30 #include <klocale.h>
31 #include <kio/job.h>
32 #include <kio/jobuidelegate.h>
33 #include <kmessagebox.h>
34 #include "kprotocolmanager.h"
35 #include "kmountpoint.h"
36 
37 #include <QFile>
38 
39 // Enable this to get printDebug() called often, to see the contents of the cache
40 //#define DEBUG_CACHE
41 
42 // Make really sure it doesn't get activated in the final build
43 #ifdef NDEBUG
44 #undef DEBUG_CACHE
45 #endif
46 
47 K_GLOBAL_STATIC(KDirListerCache, kDirListerCache)
48 
49 KDirListerCache::KDirListerCache()
50  : itemsCached( 10 ) // keep the last 10 directories around
51 {
52  //kDebug(7004);
53 
54  connect( &pendingUpdateTimer, SIGNAL(timeout()), this, SLOT(processPendingUpdates()) );
55  pendingUpdateTimer.setSingleShot( true );
56 
57  connect( KDirWatch::self(), SIGNAL(dirty(QString)),
58  this, SLOT(slotFileDirty(QString)) );
59  connect( KDirWatch::self(), SIGNAL(created(QString)),
60  this, SLOT(slotFileCreated(QString)) );
61  connect( KDirWatch::self(), SIGNAL(deleted(QString)),
62  this, SLOT(slotFileDeleted(QString)) );
63 
64  kdirnotify = new org::kde::KDirNotify(QString(), QString(), QDBusConnection::sessionBus(), this);
65  connect(kdirnotify, SIGNAL(FileRenamed(QString,QString)), SLOT(slotFileRenamed(QString,QString)));
66  connect(kdirnotify, SIGNAL(FilesAdded(QString)), SLOT(slotFilesAdded(QString)));
67  connect(kdirnotify, SIGNAL(FilesChanged(QStringList)), SLOT(slotFilesChanged(QStringList)));
68  connect(kdirnotify, SIGNAL(FilesRemoved(QStringList)), SLOT(slotFilesRemoved(QStringList)));
69 
70  // The use of KUrl::url() in ~DirItem (sendSignal) crashes if the static for QRegExpEngine got deleted already,
71  // so we need to destroy the KDirListerCache before that.
72  qAddPostRoutine(kDirListerCache.destroy);
73 }
74 
75 KDirListerCache::~KDirListerCache()
76 {
77  //kDebug(7004);
78 
79  qDeleteAll(itemsInUse);
80  itemsInUse.clear();
81 
82  itemsCached.clear();
83  directoryData.clear();
84 
85  if ( KDirWatch::exists() )
86  KDirWatch::self()->disconnect( this );
87 }
88 
89 // setting _reload to true will emit the old files and
90 // call updateDirectory
91 bool KDirListerCache::listDir( KDirLister *lister, const KUrl& _u,
92  bool _keep, bool _reload )
93 {
94  KUrl _url(_u);
95  _url.cleanPath(); // kill consecutive slashes
96 
97  if (!_url.host().isEmpty() && KProtocolInfo::protocolClass(_url.protocol()) == ":local"
98  && _url.protocol() != "file") {
99  // ":local" protocols ignore the hostname, so strip it out preventively - #160057
100  // kio_file is special cased since it does honor the hostname (by redirecting to e.g. smb)
101  _url.setHost(QString());
102  if (_keep == false)
103  emit lister->redirection(_url);
104  }
105 
106  // like this we don't have to worry about trailing slashes any further
107  _url.adjustPath(KUrl::RemoveTrailingSlash);
108 
109  const QString urlStr = _url.url();
110 
111  QString resolved;
112  if (_url.isLocalFile()) {
113  // Resolve symlinks (#213799)
114  const QString local = _url.toLocalFile();
115  resolved = QFileInfo(local).canonicalFilePath();
116  if (local != resolved)
117  canonicalUrls[resolved].append(urlStr);
118  // TODO: remove entry from canonicalUrls again in forgetDirs
119  // Note: this is why we use a QStringList value in there rather than a QSet:
120  // we can just remove one entry and not have to worry about other dirlisters
121  // (the non-unicity of the stringlist gives us the refcounting, basically).
122  }
123 
124  if (!validUrl(lister, _url)) {
125  kDebug(7004) << lister << "url=" << _url << "not a valid url";
126  return false;
127  }
128 
129  //kDebug(7004) << lister << "url=" << _url << "keep=" << _keep << "reload=" << _reload;
130 #ifdef DEBUG_CACHE
131  printDebug();
132 #endif
133 
134  if (!_keep) {
135  // stop any running jobs for lister
136  stop(lister, true /*silent*/);
137 
138  // clear our internal list for lister
139  forgetDirs(lister);
140 
141  lister->d->rootFileItem = KFileItem();
142  } else if (lister->d->lstDirs.contains(_url)) {
143  // stop the job listing _url for this lister
144  stopListingUrl(lister, _url, true /*silent*/);
145 
146  // remove the _url as well, it will be added in a couple of lines again!
147  // forgetDirs with three args does not do this
148  // TODO: think about moving this into forgetDirs
149  lister->d->lstDirs.removeAll(_url);
150 
151  // clear _url for lister
152  forgetDirs(lister, _url, true);
153 
154  if (lister->d->url == _url)
155  lister->d->rootFileItem = KFileItem();
156  }
157 
158  lister->d->complete = false;
159 
160  lister->d->lstDirs.append(_url);
161 
162  if (lister->d->url.isEmpty() || !_keep) // set toplevel URL only if not set yet
163  lister->d->url = _url;
164 
165  DirItem *itemU = itemsInUse.value(urlStr);
166 
167  KDirListerCacheDirectoryData& dirData = directoryData[urlStr]; // find or insert
168 
169  if (dirData.listersCurrentlyListing.isEmpty()) {
170  // if there is an update running for _url already we get into
171  // the following case - it will just be restarted by updateDirectory().
172 
173  dirData.listersCurrentlyListing.append(lister);
174 
175  DirItem *itemFromCache = 0;
176  if (itemU || (!_reload && (itemFromCache = itemsCached.take(urlStr)) ) ) {
177  if (itemU) {
178  kDebug(7004) << "Entry already in use:" << _url;
179  // if _reload is set, then we'll emit cached items and then updateDirectory.
180  } else {
181  kDebug(7004) << "Entry in cache:" << _url;
182  itemsInUse.insert(urlStr, itemFromCache);
183  itemU = itemFromCache;
184  }
185  if (lister->d->autoUpdate) {
186  itemU->incAutoUpdate();
187  }
188  if (itemFromCache && itemFromCache->watchedWhileInCache) {
189  itemFromCache->watchedWhileInCache = false;;
190  itemFromCache->decAutoUpdate();
191  }
192 
193  emit lister->started(_url);
194 
195  // List items from the cache in a delayed manner, just like things would happen
196  // if we were not using the cache.
197  new KDirLister::Private::CachedItemsJob(lister, _url, _reload);
198 
199  } else {
200  // dir not in cache or _reload is true
201  if (_reload) {
202  kDebug(7004) << "Reloading directory:" << _url;
203  itemsCached.remove(urlStr);
204  } else {
205  kDebug(7004) << "Listing directory:" << _url;
206  }
207 
208  itemU = new DirItem(_url, resolved);
209  itemsInUse.insert(urlStr, itemU);
210  if (lister->d->autoUpdate)
211  itemU->incAutoUpdate();
212 
213 // // we have a limit of MAX_JOBS_PER_LISTER concurrently running jobs
214 // if ( lister->d->numJobs() >= MAX_JOBS_PER_LISTER )
215 // {
216 // pendingUpdates.insert( _url );
217 // }
218 // else
219  {
220  KIO::ListJob* job = KIO::listDir(_url, KIO::HideProgressInfo);
221  runningListJobs.insert(job, KIO::UDSEntryList());
222 
223  lister->d->jobStarted(job);
224  lister->d->connectJob(job);
225 
226  if (lister->d->window)
227  job->ui()->setWindow(lister->d->window);
228 
229  connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
230  this, SLOT(slotEntries(KIO::Job*,KIO::UDSEntryList)));
231  connect(job, SIGNAL(result(KJob*)),
232  this, SLOT(slotResult(KJob*)));
233  connect(job, SIGNAL(redirection(KIO::Job*,KUrl)),
234  this, SLOT(slotRedirection(KIO::Job*,KUrl)));
235 
236  emit lister->started(_url);
237  }
238  //kDebug(7004) << "Entry now being listed by" << dirData.listersCurrentlyListing;
239  }
240  } else {
241 
242  kDebug(7004) << "Entry currently being listed:" << _url << "by" << dirData.listersCurrentlyListing;
243 #ifdef DEBUG_CACHE
244  printDebug();
245 #endif
246 
247  emit lister->started( _url );
248 
249  // Maybe listersCurrentlyListing/listersCurrentlyHolding should be QSets?
250  Q_ASSERT(!dirData.listersCurrentlyListing.contains(lister));
251  dirData.listersCurrentlyListing.append( lister );
252 
253  KIO::ListJob *job = jobForUrl( urlStr );
254  // job will be 0 if we were listing from cache rather than listing from a kio job.
255  if( job ) {
256  lister->d->jobStarted( job );
257  lister->d->connectJob( job );
258  }
259  Q_ASSERT( itemU );
260 
261  // List existing items in a delayed manner, just like things would happen
262  // if we were not using the cache.
263  kDebug(7004) << "Listing" << itemU->lstItems.count() << "cached items soon";
264  KDirLister::Private::CachedItemsJob *cachedItemsJob = new KDirLister::Private::CachedItemsJob(lister, _url, _reload);
265 
266  if (job) {
267  // The ListJob will take care of emitting completed.
268  // ### If it finishes before the CachedItemsJob, then we'll emit cached items after completed(), not sure how bad this is.
269  cachedItemsJob->setEmitCompleted(false);
270  }
271 
272 
273 #ifdef DEBUG_CACHE
274  printDebug();
275 #endif
276  }
277 
278  return true;
279 }
280 
281 KDirLister::Private::CachedItemsJob* KDirLister::Private::cachedItemsJobForUrl(const KUrl& url) const
282 {
283  Q_FOREACH(CachedItemsJob* job, m_cachedItemsJobs) {
284  if (job->url() == url)
285  return job;
286  }
287  return 0;
288 }
289 
290 KDirLister::Private::CachedItemsJob::CachedItemsJob(KDirLister* lister, const KUrl& url, bool reload)
291  : KJob(lister),
292  m_lister(lister), m_url(url),
293  m_reload(reload), m_emitCompleted(true)
294 {
295  //kDebug() << "Creating CachedItemsJob" << this << "for lister" << lister << url;
296  if (lister->d->cachedItemsJobForUrl(url)) {
297  kWarning(7004) << "Lister" << lister << "has a cached items job already for" << url;
298  }
299  lister->d->m_cachedItemsJobs.append(this);
300  setAutoDelete(true);
301  start();
302 }
303 
304 // Called by start() via QueuedConnection
305 void KDirLister::Private::CachedItemsJob::done()
306 {
307  if (!m_lister) // job was already killed, but waiting deletion due to deleteLater
308  return;
309  kDirListerCache->emitItemsFromCache(this, m_lister, m_url, m_reload, m_emitCompleted);
310  emitResult();
311 }
312 
313 bool KDirLister::Private::CachedItemsJob::doKill()
314 {
315  //kDebug(7004) << this;
316  kDirListerCache->forgetCachedItemsJob(this, m_lister, m_url);
317  if (!property("_kdlc_silent").toBool()) {
318  emit m_lister->canceled(m_url);
319  emit m_lister->canceled();
320  }
321  m_lister = 0;
322  return true;
323 }
324 
325 void KDirListerCache::emitItemsFromCache(KDirLister::Private::CachedItemsJob* cachedItemsJob, KDirLister* lister, const KUrl& _url, bool _reload, bool _emitCompleted)
326 {
327  const QString urlStr = _url.url();
328  KDirLister::Private* kdl = lister->d;
329  kdl->complete = false;
330 
331  DirItem *itemU = kDirListerCache->itemsInUse.value(urlStr);
332  if (!itemU) {
333  kWarning(7004) << "Can't find item for directory" << urlStr << "anymore";
334  } else {
335  const KFileItemList items = itemU->lstItems;
336  const KFileItem rootItem = itemU->rootItem;
337  _reload = _reload || !itemU->complete;
338 
339  if (kdl->rootFileItem.isNull() && !rootItem.isNull() && kdl->url == _url) {
340  kdl->rootFileItem = rootItem;
341  }
342  if (!items.isEmpty()) {
343  //kDebug(7004) << "emitting" << items.count() << "for lister" << lister;
344  kdl->addNewItems(_url, items);
345  kdl->emitItems();
346  }
347  }
348 
349  forgetCachedItemsJob(cachedItemsJob, lister, _url);
350 
351  // Emit completed, unless we were told not to,
352  // or if listDir() was called while another directory listing for this dir was happening,
353  // so we "joined" it. We detect that using jobForUrl to ensure it's a real ListJob,
354  // not just a lister-specific CachedItemsJob (which wouldn't emit completed for us).
355  if (_emitCompleted) {
356 
357  kdl->complete = true;
358  emit lister->completed( _url );
359  emit lister->completed();
360 
361  if ( _reload ) {
362  updateDirectory( _url );
363  }
364  }
365 }
366 
367 void KDirListerCache::forgetCachedItemsJob(KDirLister::Private::CachedItemsJob* cachedItemsJob, KDirLister* lister, const KUrl& _url)
368 {
369  // Modifications to data structures only below this point;
370  // so that addNewItems is called with a consistent state
371 
372  const QString urlStr = _url.url();
373  lister->d->m_cachedItemsJobs.removeAll(cachedItemsJob);
374 
375  KDirListerCacheDirectoryData& dirData = directoryData[urlStr];
376  Q_ASSERT(dirData.listersCurrentlyListing.contains(lister));
377 
378  KIO::ListJob *listJob = jobForUrl(urlStr);
379  if (!listJob) {
380  Q_ASSERT(!dirData.listersCurrentlyHolding.contains(lister));
381  //kDebug(7004) << "Moving from listing to holding, because no more job" << lister << urlStr;
382  dirData.listersCurrentlyHolding.append( lister );
383  dirData.listersCurrentlyListing.removeAll( lister );
384  } else {
385  //kDebug(7004) << "Still having a listjob" << listJob << ", so not moving to currently-holding.";
386  }
387 }
388 
389 bool KDirListerCache::validUrl( const KDirLister *lister, const KUrl& url ) const
390 {
391  if ( !url.isValid() )
392  {
393  if ( lister->d->autoErrorHandling )
394  {
395  QString tmp = i18n("Malformed URL\n%1", url.prettyUrl() );
396  KMessageBox::error( lister->d->errorParent, tmp );
397  }
398  return false;
399  }
400 
401  if ( !KProtocolManager::supportsListing( url ) )
402  {
403  if ( lister->d->autoErrorHandling )
404  {
405  QString tmp = i18n("URL cannot be listed\n%1", url.prettyUrl() );
406  KMessageBox::error( lister->d->errorParent, tmp );
407  }
408  return false;
409  }
410 
411  return true;
412 }
413 
414 void KDirListerCache::stop( KDirLister *lister, bool silent )
415 {
416 #ifdef DEBUG_CACHE
417  //printDebug();
418 #endif
419  //kDebug(7004) << "lister:" << lister << "silent=" << silent;
420 
421  const KUrl::List urls = lister->d->lstDirs;
422  Q_FOREACH(const KUrl& url, urls) {
423  stopListingUrl(lister, url, silent);
424  }
425 
426 #if 0 // test code
427  QHash<QString,KDirListerCacheDirectoryData>::iterator dirit = directoryData.begin();
428  const QHash<QString,KDirListerCacheDirectoryData>::iterator dirend = directoryData.end();
429  for( ; dirit != dirend ; ++dirit ) {
430  KDirListerCacheDirectoryData& dirData = dirit.value();
431  if (dirData.listersCurrentlyListing.contains(lister)) {
432  kDebug(7004) << "ERROR: found lister" << lister << "in list - for" << dirit.key();
433  Q_ASSERT(false);
434  }
435  }
436 #endif
437 }
438 
439 void KDirListerCache::stopListingUrl(KDirLister *lister, const KUrl& _u, bool silent)
440 {
441  KUrl url(_u);
442  url.adjustPath( KUrl::RemoveTrailingSlash );
443  const QString urlStr = url.url();
444 
445  KDirLister::Private::CachedItemsJob* cachedItemsJob = lister->d->cachedItemsJobForUrl(url);
446  if (cachedItemsJob) {
447  if (silent) {
448  cachedItemsJob->setProperty("_kdlc_silent", true);
449  }
450  cachedItemsJob->kill(); // removes job from list, too
451  }
452 
453  // TODO: consider to stop all the "child jobs" of url as well
454  kDebug(7004) << lister << " url=" << url;
455 
456  QHash<QString,KDirListerCacheDirectoryData>::iterator dirit = directoryData.find(urlStr);
457  if (dirit == directoryData.end())
458  return;
459  KDirListerCacheDirectoryData& dirData = dirit.value();
460  if (dirData.listersCurrentlyListing.contains(lister)) {
461  //kDebug(7004) << " found lister" << lister << "in list - for" << urlStr;
462  if (dirData.listersCurrentlyListing.count() == 1) {
463  // This was the only dirlister interested in the list job -> kill the job
464  stopListJob(urlStr, silent);
465  } else {
466  // Leave the job running for the other dirlisters, just unsubscribe us.
467  dirData.listersCurrentlyListing.removeAll(lister);
468  if (!silent) {
469  emit lister->canceled();
470  emit lister->canceled(url);
471  }
472  }
473  }
474 }
475 
476 // Helper for stop() and stopListingUrl()
477 void KDirListerCache::stopListJob(const QString& url, bool silent)
478 {
479  // Old idea: if it's an update job, let's just leave the job running.
480  // After all, update jobs do run for "listersCurrentlyHolding",
481  // so there's no reason to kill them just because @p lister is now a holder.
482 
483  // However it could be a long-running non-local job (e.g. filenamesearch), which
484  // the user wants to abort, and which will never be used for updating...
485  // And in any case slotEntries/slotResult is not meant to be called by update jobs.
486  // So, change of plan, let's kill it after all, in a way that triggers slotResult/slotUpdateResult.
487 
488  KIO::ListJob *job = jobForUrl(url);
489  if (job) {
490  //kDebug() << "Killing list job" << job << "for" << url;
491  if (silent) {
492  job->setProperty("_kdlc_silent", true);
493  }
494  job->kill(KJob::EmitResult);
495  }
496 }
497 
498 void KDirListerCache::setAutoUpdate( KDirLister *lister, bool enable )
499 {
500  // IMPORTANT: this method does not check for the current autoUpdate state!
501 
502  for ( KUrl::List::const_iterator it = lister->d->lstDirs.constBegin();
503  it != lister->d->lstDirs.constEnd(); ++it ) {
504  DirItem* dirItem = itemsInUse.value((*it).url());
505  Q_ASSERT(dirItem);
506  if ( enable )
507  dirItem->incAutoUpdate();
508  else
509  dirItem->decAutoUpdate();
510  }
511 }
512 
513 void KDirListerCache::forgetDirs( KDirLister *lister )
514 {
515  //kDebug(7004) << lister;
516 
517  emit lister->clear();
518  // clear lister->d->lstDirs before calling forgetDirs(), so that
519  // it doesn't contain things that itemsInUse doesn't. When emitting
520  // the canceled signals, lstDirs must not contain anything that
521  // itemsInUse does not contain. (otherwise it might crash in findByName()).
522  const KUrl::List lstDirsCopy = lister->d->lstDirs;
523  lister->d->lstDirs.clear();
524 
525  //kDebug() << "Iterating over dirs" << lstDirsCopy;
526  for ( KUrl::List::const_iterator it = lstDirsCopy.begin();
527  it != lstDirsCopy.end(); ++it ) {
528  forgetDirs( lister, *it, false );
529  }
530 }
531 
532 static bool manually_mounted(const QString& path, const KMountPoint::List& possibleMountPoints)
533 {
534  KMountPoint::Ptr mp = possibleMountPoints.findByPath(path);
535  if (!mp) { // not listed in fstab -> yes, manually mounted
536  if (possibleMountPoints.isEmpty()) // no fstab at all -> don't assume anything
537  return false;
538  return true;
539  }
540  const bool supermount = mp->mountType() == "supermount";
541  if (supermount) {
542  return true;
543  }
544  // noauto -> manually mounted. Otherwise, mounted at boot time, won't be unmounted any time soon hopefully.
545  return mp->mountOptions().contains("noauto");
546 }
547 
548 
549 void KDirListerCache::forgetDirs( KDirLister *lister, const KUrl& _url, bool notify )
550 {
551  //kDebug(7004) << lister << " _url: " << _url;
552 
553  KUrl url( _url );
554  url.adjustPath( KUrl::RemoveTrailingSlash );
555  const QString urlStr = url.url();
556 
557  DirectoryDataHash::iterator dit = directoryData.find(urlStr);
558  if (dit == directoryData.end())
559  return;
560  KDirListerCacheDirectoryData& dirData = *dit;
561  dirData.listersCurrentlyHolding.removeAll(lister);
562 
563  // This lister doesn't care for updates running in <url> anymore
564  KIO::ListJob *job = jobForUrl(urlStr);
565  if (job)
566  lister->d->jobDone(job);
567 
568  DirItem *item = itemsInUse.value(urlStr);
569  Q_ASSERT(item);
570  bool insertIntoCache = false;
571 
572  if ( dirData.listersCurrentlyHolding.isEmpty() && dirData.listersCurrentlyListing.isEmpty() ) {
573  // item not in use anymore -> move into cache if complete
574  directoryData.erase(dit);
575  itemsInUse.remove( urlStr );
576 
577  // this job is a running update which nobody cares about anymore
578  if ( job ) {
579  killJob( job );
580  kDebug(7004) << "Killing update job for " << urlStr;
581 
582  // Well, the user of KDirLister doesn't really care that we're stopping
583  // a background-running job from a previous URL (in listDir) -> commented out.
584  // stop() already emitted canceled.
585  //emit lister->canceled( url );
586  if ( lister->d->numJobs() == 0 ) {
587  lister->d->complete = true;
588  //emit lister->canceled();
589  }
590  }
591 
592  if ( notify ) {
593  lister->d->lstDirs.removeAll( url );
594  emit lister->clear( url );
595  }
596 
597  insertIntoCache = item->complete;
598  if (insertIntoCache) {
599  // TODO(afiestas): remove use of KMountPoint+manually_mounted and port to Solid:
600  // 1) find Volume for the local path "item->url.toLocalFile()" (which could be anywhere
601  // under the mount point) -- probably needs a new operator in libsolid query parser
602  // 2) [**] becomes: if (Drive is hotpluggable or Volume is removable) "set to dirty" else "keep watch"
603  const KMountPoint::List possibleMountPoints = KMountPoint::possibleMountPoints(KMountPoint::NeedMountOptions);
604 
605  // Should we forget the dir for good, or keep a watch on it?
606  // Generally keep a watch, except when it would prevent
607  // unmounting a removable device (#37780)
608  const bool isLocal = item->url.isLocalFile();
609  bool isManuallyMounted = false;
610  bool containsManuallyMounted = false;
611  if (isLocal) {
612  isManuallyMounted = manually_mounted( item->url.toLocalFile(), possibleMountPoints );
613  if ( !isManuallyMounted ) {
614  // Look for a manually-mounted directory inside
615  // If there's one, we can't keep a watch either, FAM would prevent unmounting the CDROM
616  // I hope this isn't too slow
617  KFileItemList::const_iterator kit = item->lstItems.constBegin();
618  KFileItemList::const_iterator kend = item->lstItems.constEnd();
619  for ( ; kit != kend && !containsManuallyMounted; ++kit )
620  if ( (*kit).isDir() && manually_mounted((*kit).url().toLocalFile(), possibleMountPoints) )
621  containsManuallyMounted = true;
622  }
623  }
624 
625  if ( isManuallyMounted || containsManuallyMounted ) // [**]
626  {
627  kDebug(7004) << "Not adding a watch on " << item->url << " because it " <<
628  ( isManuallyMounted ? "is manually mounted" : "contains a manually mounted subdir" );
629  item->complete = false; // set to "dirty"
630  } else {
631  item->incAutoUpdate(); // keep watch
632  item->watchedWhileInCache = true;
633  }
634  }
635  else
636  {
637  delete item;
638  item = 0;
639  }
640  }
641 
642  if ( item && lister->d->autoUpdate )
643  item->decAutoUpdate();
644 
645  // Inserting into QCache must be done last, since it might delete the item
646  if (item && insertIntoCache) {
647  kDebug(7004) << lister << "item moved into cache:" << url;
648  itemsCached.insert(urlStr, item);
649  }
650 }
651 
652 void KDirListerCache::updateDirectory( const KUrl& _dir )
653 {
654  kDebug(7004) << _dir;
655 
656  QString urlStr = _dir.url(KUrl::RemoveTrailingSlash);
657  if (!checkUpdate(urlStr)) {
658  if (_dir.isLocalFile() && findByUrl(0, _dir)) {
659  pendingUpdates.insert(_dir.toLocalFile());
660  if (!pendingUpdateTimer.isActive())
661  pendingUpdateTimer.start(500);
662  }
663  return;
664  }
665 
666  // A job can be running to
667  // - only list a new directory: the listers are in listersCurrentlyListing
668  // - only update a directory: the listers are in listersCurrentlyHolding
669  // - update a currently running listing: the listers are in both
670 
671  KDirListerCacheDirectoryData& dirData = directoryData[urlStr];
672  QList<KDirLister *> listers = dirData.listersCurrentlyListing;
673  QList<KDirLister *> holders = dirData.listersCurrentlyHolding;
674 
675  //kDebug(7004) << urlStr << "listers=" << listers << "holders=" << holders;
676 
677  // restart the job for _dir if it is running already
678  bool killed = false;
679  QWidget *window = 0;
680  KIO::ListJob *job = jobForUrl( urlStr );
681  if (job) {
682  window = job->ui()->window();
683 
684  killJob( job );
685  killed = true;
686 
687  foreach ( KDirLister *kdl, listers )
688  kdl->d->jobDone( job );
689 
690  foreach ( KDirLister *kdl, holders )
691  kdl->d->jobDone( job );
692  } else {
693  // Emit any cached items.
694  // updateDirectory() is about the diff compared to the cached items...
695  Q_FOREACH(KDirLister *kdl, listers) {
696  KDirLister::Private::CachedItemsJob* cachedItemsJob = kdl->d->cachedItemsJobForUrl(_dir);
697  if (cachedItemsJob) {
698  cachedItemsJob->setEmitCompleted(false);
699  cachedItemsJob->done(); // removes from cachedItemsJobs list
700  delete cachedItemsJob;
701  killed = true;
702  }
703  }
704  }
705  //kDebug(7004) << "Killed=" << killed;
706 
707  // we don't need to emit canceled signals since we only replaced the job,
708  // the listing is continuing.
709 
710  if (!(listers.isEmpty() || killed)) {
711  kWarning() << "The unexpected happened.";
712  kWarning() << "listers for" << _dir << "=" << listers;
713  kWarning() << "job=" << job;
714  Q_FOREACH(KDirLister *kdl, listers) {
715  kDebug() << "lister" << kdl << "m_cachedItemsJobs=" << kdl->d->m_cachedItemsJobs;
716  }
717 #ifndef NDEBUG
718  printDebug();
719 #endif
720  }
721  Q_ASSERT( listers.isEmpty() || killed );
722 
723  job = KIO::listDir( _dir, KIO::HideProgressInfo );
724  runningListJobs.insert( job, KIO::UDSEntryList() );
725 
726  connect( job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
727  this, SLOT(slotUpdateEntries(KIO::Job*,KIO::UDSEntryList)) );
728  connect( job, SIGNAL(result(KJob*)),
729  this, SLOT(slotUpdateResult(KJob*)) );
730 
731  kDebug(7004) << "update started in" << _dir;
732 
733  foreach ( KDirLister *kdl, listers ) {
734  kdl->d->jobStarted( job );
735  }
736 
737  if ( !holders.isEmpty() ) {
738  if ( !killed ) {
739  bool first = true;
740  foreach ( KDirLister *kdl, holders ) {
741  kdl->d->jobStarted( job );
742  if ( first && kdl->d->window ) {
743  first = false;
744  job->ui()->setWindow( kdl->d->window );
745  }
746  emit kdl->started( _dir );
747  }
748  } else {
749  job->ui()->setWindow( window );
750 
751  foreach ( KDirLister *kdl, holders ) {
752  kdl->d->jobStarted( job );
753  }
754  }
755  }
756 }
757 
758 bool KDirListerCache::checkUpdate( const QString& _dir )
759 {
760  if ( !itemsInUse.contains(_dir) )
761  {
762  DirItem *item = itemsCached[_dir];
763  if ( item && item->complete )
764  {
765  // Stop watching items once they are only in the cache and not used anymore.
766  // We'll trigger an update when listing that dir again later.
767  item->complete = false;
768  item->watchedWhileInCache = false;
769  item->decAutoUpdate();
770  // Hmm, this debug output might include login/password from the _dir URL.
771  //kDebug(7004) << "directory " << _dir << " not in use, marked dirty.";
772  }
773  //else
774  //kDebug(7004) << "aborted, directory " << _dir << " not in cache.";
775 
776  return false;
777  }
778  else
779  return true;
780 }
781 
782 KFileItem KDirListerCache::itemForUrl( const KUrl& url ) const
783 {
784  KFileItem *item = findByUrl( 0, url );
785  if (item) {
786  return *item;
787  } else {
788  return KFileItem();
789  }
790 }
791 
792 KDirListerCache::DirItem *KDirListerCache::dirItemForUrl(const KUrl& dir) const
793 {
794  const QString urlStr = dir.url(KUrl::RemoveTrailingSlash);
795  DirItem *item = itemsInUse.value(urlStr);
796  if ( !item )
797  item = itemsCached[urlStr];
798  return item;
799 }
800 
801 KFileItemList *KDirListerCache::itemsForDir(const KUrl& dir) const
802 {
803  DirItem *item = dirItemForUrl(dir);
804  return item ? &item->lstItems : 0;
805 }
806 
807 KFileItem KDirListerCache::findByName( const KDirLister *lister, const QString& _name ) const
808 {
809  Q_ASSERT(lister);
810 
811  for (KUrl::List::const_iterator it = lister->d->lstDirs.constBegin();
812  it != lister->d->lstDirs.constEnd(); ++it) {
813  DirItem* dirItem = itemsInUse.value((*it).url());
814  Q_ASSERT(dirItem);
815  const KFileItem item = dirItem->lstItems.findByName(_name);
816  if (!item.isNull())
817  return item;
818  }
819 
820  return KFileItem();
821 }
822 
823 KFileItem *KDirListerCache::findByUrl( const KDirLister *lister, const KUrl& _u ) const
824 {
825  KUrl url(_u);
826  url.adjustPath(KUrl::RemoveTrailingSlash);
827 
828  KUrl parentDir(url);
829  parentDir.setPath( parentDir.directory() );
830 
831  DirItem* dirItem = dirItemForUrl(parentDir);
832  if (dirItem) {
833  // If lister is set, check that it contains this dir
834  if (!lister || lister->d->lstDirs.contains(parentDir)) {
835  KFileItemList::iterator it = dirItem->lstItems.begin();
836  const KFileItemList::iterator end = dirItem->lstItems.end();
837  for (; it != end ; ++it) {
838  if ((*it).url() == url) {
839  return &*it;
840  }
841  }
842  }
843  }
844 
845  // Maybe _u is a directory itself? (see KDirModelTest::testChmodDirectory)
846  // We check this last, though, we prefer returning a kfileitem with an actual
847  // name if possible (and we make it '.' for root items later).
848  dirItem = dirItemForUrl(url);
849  if (dirItem && !dirItem->rootItem.isNull() && dirItem->rootItem.url() == url) {
850  // If lister is set, check that it contains this dir
851  if (!lister || lister->d->lstDirs.contains(url)) {
852  return &dirItem->rootItem;
853  }
854  }
855 
856  return 0;
857 }
858 
859 void KDirListerCache::slotFilesAdded( const QString &dir /*url*/ ) // from KDirNotify signals
860 {
861  KUrl urlDir(dir);
862  kDebug(7004) << urlDir; // output urls, not qstrings, since they might contain a password
863  if (urlDir.isLocalFile()) {
864  Q_FOREACH(const QString& u, directoriesForCanonicalPath(urlDir.toLocalFile())) {
865  updateDirectory(KUrl(u));
866  }
867  } else {
868  updateDirectory(urlDir);
869  }
870 }
871 
872 void KDirListerCache::slotFilesRemoved( const QStringList &fileList ) // from KDirNotify signals
873 {
874  // TODO: handling of symlinks-to-directories isn't done here,
875  // because I'm not sure how to do it and keep the performance ok...
876  slotFilesRemoved(KUrl::List(fileList));
877 }
878 
879 void KDirListerCache::slotFilesRemoved(const KUrl::List& fileList)
880 {
881  //kDebug(7004) << fileList.count();
882  // Group notifications by parent dirs (usually there would be only one parent dir)
883  QMap<QString, KFileItemList> removedItemsByDir;
884  KUrl::List deletedSubdirs;
885 
886  for (KUrl::List::const_iterator it = fileList.begin(); it != fileList.end() ; ++it) {
887  const KUrl url(*it);
888  DirItem* dirItem = dirItemForUrl(url); // is it a listed directory?
889  if (dirItem) {
890  deletedSubdirs.append(url);
891  if (!dirItem->rootItem.isNull()) {
892  removedItemsByDir[url.url()].append(dirItem->rootItem);
893  }
894  }
895 
896  KUrl parentDir(url);
897  parentDir.setPath(parentDir.directory());
898  dirItem = dirItemForUrl(parentDir);
899  if (!dirItem)
900  continue;
901  for (KFileItemList::iterator fit = dirItem->lstItems.begin(), fend = dirItem->lstItems.end(); fit != fend ; ++fit) {
902  if ((*fit).url() == url) {
903  const KFileItem fileitem = *fit;
904  removedItemsByDir[parentDir.url()].append(fileitem);
905  // If we found a fileitem, we can test if it's a dir. If not, we'll go to deleteDir just in case.
906  if (fileitem.isNull() || fileitem.isDir()) {
907  deletedSubdirs.append(url);
908  }
909  dirItem->lstItems.erase(fit); // remove fileitem from list
910  break;
911  }
912  }
913  }
914 
915  QMap<QString, KFileItemList>::const_iterator rit = removedItemsByDir.constBegin();
916  for(; rit != removedItemsByDir.constEnd(); ++rit) {
917  // Tell the views about it before calling deleteDir.
918  // They might need the subdirs' file items (see the dirtree).
919  DirectoryDataHash::const_iterator dit = directoryData.constFind(rit.key());
920  if (dit != directoryData.constEnd()) {
921  itemsDeleted((*dit).listersCurrentlyHolding, rit.value());
922  }
923  }
924 
925  Q_FOREACH(const KUrl& url, deletedSubdirs) {
926  // in case of a dir, check if we have any known children, there's much to do in that case
927  // (stopping jobs, removing dirs from cache etc.)
928  deleteDir(url);
929  }
930 }
931 
932 void KDirListerCache::slotFilesChanged( const QStringList &fileList ) // from KDirNotify signals
933 {
934  //kDebug(7004) << fileList;
935  KUrl::List dirsToUpdate;
936  QStringList::const_iterator it = fileList.begin();
937  for (; it != fileList.end() ; ++it) {
938  KUrl url( *it );
939  KFileItem *fileitem = findByUrl(0, url);
940  if (!fileitem) {
941  kDebug(7004) << "item not found for" << url;
942  continue;
943  }
944  if (url.isLocalFile()) {
945  pendingUpdates.insert(url.toLocalFile()); // delegate the work to processPendingUpdates
946  } else {
947  pendingRemoteUpdates.insert(fileitem);
948  // For remote files, we won't be able to figure out the new information,
949  // we have to do a update (directory listing)
950  KUrl dir(url);
951  dir.setPath(dir.directory());
952  if (!dirsToUpdate.contains(dir))
953  dirsToUpdate.prepend(dir);
954  }
955  }
956 
957  KUrl::List::const_iterator itdir = dirsToUpdate.constBegin();
958  for (; itdir != dirsToUpdate.constEnd() ; ++itdir)
959  updateDirectory( *itdir );
960  // ## TODO problems with current jobs listing/updating that dir
961  // ( see kde-2.2.2's kdirlister )
962 
963  processPendingUpdates();
964 }
965 
966 void KDirListerCache::slotFileRenamed( const QString &_src, const QString &_dst ) // from KDirNotify signals
967 {
968  KUrl src( _src );
969  KUrl dst( _dst );
970  kDebug(7004) << src << "->" << dst;
971 #ifdef DEBUG_CACHE
972  printDebug();
973 #endif
974 
975  KUrl oldurl(src);
976  oldurl.adjustPath( KUrl::RemoveTrailingSlash );
977  KFileItem *fileitem = findByUrl(0, oldurl);
978  if (!fileitem) {
979  kDebug(7004) << "Item not found:" << oldurl;
980  return;
981  }
982 
983  const KFileItem oldItem = *fileitem;
984 
985  // Dest already exists? Was overwritten then (testcase: #151851)
986  // We better emit it as deleted -before- doing the renaming, otherwise
987  // the "update" mechanism will emit the old one as deleted and
988  // kdirmodel will delete the new (renamed) one!
989  KFileItem* existingDestItem = findByUrl(0, dst);
990  if (existingDestItem) {
991  //kDebug() << dst << "already existed, let's delete it";
992  slotFilesRemoved(dst);
993  }
994 
995  // If the item had a UDS_URL as well as UDS_NAME set, the user probably wants
996  // to be updating the name only (since they can't see the URL).
997  // Check to see if a URL exists, and if so, if only the file part has changed,
998  // only update the name and not the underlying URL.
999  bool nameOnly = !fileitem->entry().stringValue( KIO::UDSEntry::UDS_URL ).isEmpty();
1000  nameOnly &= src.directory( KUrl::IgnoreTrailingSlash | KUrl::AppendTrailingSlash ) ==
1001  dst.directory( KUrl::IgnoreTrailingSlash | KUrl::AppendTrailingSlash );
1002 
1003  if (!nameOnly && fileitem->isDir()) {
1004  renameDir( src, dst );
1005  // #172945 - if the fileitem was the root item of a DirItem that was just removed from the cache,
1006  // then it's a dangling pointer now...
1007  fileitem = findByUrl(0, oldurl);
1008  if (!fileitem) //deleted from cache altogether, #188807
1009  return;
1010  }
1011 
1012  // Now update the KFileItem representing that file or dir (not exclusive with the above!)
1013  if (!oldItem.isLocalFile() && !oldItem.localPath().isEmpty()) { // it uses UDS_LOCAL_PATH? ouch, needs an update then
1014  slotFilesChanged( QStringList() << src.url() );
1015  } else {
1016  if( nameOnly )
1017  fileitem->setName( dst.fileName() );
1018  else
1019  fileitem->setUrl( dst );
1020  fileitem->refreshMimeType();
1021  fileitem->determineMimeType();
1022  QSet<KDirLister*> listers = emitRefreshItem( oldItem, *fileitem );
1023  Q_FOREACH(KDirLister * kdl, listers) {
1024  kdl->d->emitItems();
1025  }
1026  }
1027 
1028 #ifdef DEBUG_CACHE
1029  printDebug();
1030 #endif
1031 }
1032 
1033 QSet<KDirLister*> KDirListerCache::emitRefreshItem(const KFileItem& oldItem, const KFileItem& fileitem)
1034 {
1035  //kDebug(7004) << "old:" << oldItem.name() << oldItem.url()
1036  // << "new:" << fileitem.name() << fileitem.url();
1037  // Look whether this item was shown in any view, i.e. held by any dirlister
1038  KUrl parentDir( oldItem.url() );
1039  parentDir.setPath( parentDir.directory() );
1040  const QString parentDirURL = parentDir.url();
1041  DirectoryDataHash::iterator dit = directoryData.find(parentDirURL);
1042  QList<KDirLister *> listers;
1043  // Also look in listersCurrentlyListing, in case the user manages to rename during a listing
1044  if (dit != directoryData.end())
1045  listers += (*dit).listersCurrentlyHolding + (*dit).listersCurrentlyListing;
1046  if (oldItem.isDir()) {
1047  // For a directory, look for dirlisters where it's the root item.
1048  dit = directoryData.find(oldItem.url().url());
1049  if (dit != directoryData.end())
1050  listers += (*dit).listersCurrentlyHolding + (*dit).listersCurrentlyListing;
1051  }
1052  QSet<KDirLister*> listersToRefresh;
1053  Q_FOREACH(KDirLister *kdl, listers) {
1054  // For a directory, look for dirlisters where it's the root item.
1055  KUrl directoryUrl(oldItem.url());
1056  if (oldItem.isDir() && kdl->d->rootFileItem == oldItem) {
1057  const KFileItem oldRootItem = kdl->d->rootFileItem;
1058  kdl->d->rootFileItem = fileitem;
1059  kdl->d->addRefreshItem(directoryUrl, oldRootItem, fileitem);
1060  } else {
1061  directoryUrl.setPath(directoryUrl.directory());
1062  kdl->d->addRefreshItem(directoryUrl, oldItem, fileitem);
1063  }
1064  listersToRefresh.insert(kdl);
1065  }
1066  return listersToRefresh;
1067 }
1068 
1069 QStringList KDirListerCache::directoriesForCanonicalPath(const QString& dir) const
1070 {
1071  QStringList dirs;
1072  dirs << dir;
1073  dirs << canonicalUrls.value(dir).toSet().toList(); /* make unique; there are faster ways, but this is really small anyway */
1074 
1075  if (dirs.count() > 1)
1076  kDebug() << dir << "known as" << dirs;
1077 
1078  return dirs;
1079 }
1080 
1081 // private slots
1082 
1083 // Called by KDirWatch - usually when a dir we're watching has been modified,
1084 // but it can also be called for a file.
1085 void KDirListerCache::slotFileDirty( const QString& path )
1086 {
1087  // kDebug(7004) << path;
1088 
1089  KUrl url(path);
1090  url.adjustPath(KUrl::RemoveTrailingSlash);
1091 
1092  bool isDir;
1093  const KFileItem item = itemForUrl(url);
1094 
1095  if (!item.isNull()) {
1096  isDir = item.isDir();
1097  } else {
1098  KDE_struct_stat buff;
1099  kDebug(7004) << "Doing stat on:" << path;
1100  if ( KDE::stat( path, &buff ) != 0 )
1101  return; // error
1102  isDir = S_ISDIR(buff.st_mode);
1103  }
1104 
1105  if (isDir) {
1106  Q_FOREACH(const QString& dir, directoriesForCanonicalPath(url.toLocalFile())) {
1107  handleDirDirty(dir);
1108  }
1109  } else {
1110  Q_FOREACH(const QString& dir, directoriesForCanonicalPath(url.directory())) {
1111  KUrl aliasUrl(dir);
1112  aliasUrl.addPath(url.fileName());
1113  handleFileDirty(aliasUrl);
1114  }
1115  }
1116 }
1117 
1118 // Called by slotFileDirty
1119 void KDirListerCache::handleDirDirty(const KUrl& url)
1120 {
1121  // A dir: launch an update job if anyone cares about it
1122 
1123  // This also means we can forget about pending updates to individual files in that dir
1124  const QString dirPath = url.toLocalFile(KUrl::AddTrailingSlash);
1125  QMutableSetIterator<QString> pendingIt(pendingUpdates);
1126  while (pendingIt.hasNext()) {
1127  const QString updPath = pendingIt.next();
1128  //kDebug(7004) << "had pending update" << updPath;
1129  if (updPath.startsWith(dirPath) &&
1130  updPath.indexOf('/', dirPath.length()) == -1) { // direct child item
1131  kDebug(7004) << "forgetting about individual update to" << updPath;
1132  pendingIt.remove();
1133  }
1134  }
1135 
1136  updateDirectory(url);
1137 }
1138 
1139 // Called by slotFileDirty
1140 void KDirListerCache::handleFileDirty(const KUrl& url)
1141 {
1142  // A file: do we know about it already?
1143  KFileItem* existingItem = findByUrl(0, url);
1144  if (!existingItem) {
1145  // No - update the parent dir then
1146  KUrl dir(url);
1147  dir.setPath(url.directory());
1148  updateDirectory(dir);
1149  } else {
1150  // A known file: delay updating it, FAM is flooding us with events
1151  const QString filePath = url.toLocalFile();
1152  if (!pendingUpdates.contains(filePath)) {
1153  KUrl dir(url);
1154  dir.setPath(dir.directory());
1155  if (checkUpdate(dir.url())) {
1156  pendingUpdates.insert(filePath);
1157  if (!pendingUpdateTimer.isActive())
1158  pendingUpdateTimer.start(500);
1159  }
1160  }
1161  }
1162 }
1163 
1164 void KDirListerCache::slotFileCreated( const QString& path ) // from KDirWatch
1165 {
1166  kDebug(7004) << path;
1167  // XXX: how to avoid a complete rescan here?
1168  // We'd need to stat that one file separately and refresh the item(s) for it.
1169  KUrl fileUrl(path);
1170  slotFilesAdded(fileUrl.directory());
1171 }
1172 
1173 void KDirListerCache::slotFileDeleted( const QString& path ) // from KDirWatch
1174 {
1175  kDebug(7004) << path;
1176  KUrl u( path );
1177  QStringList fileUrls;
1178  Q_FOREACH(KUrl url, directoriesForCanonicalPath(u.directory())) {
1179  url.addPath(u.fileName());
1180  fileUrls << url.url();
1181  }
1182  slotFilesRemoved(fileUrls);
1183 }
1184 
1185 void KDirListerCache::slotEntries( KIO::Job *job, const KIO::UDSEntryList &entries )
1186 {
1187  KUrl url(joburl( static_cast<KIO::ListJob *>(job) ));
1188  url.adjustPath(KUrl::RemoveTrailingSlash);
1189  QString urlStr = url.url();
1190 
1191  //kDebug(7004) << "new entries for " << url;
1192 
1193  DirItem *dir = itemsInUse.value(urlStr);
1194  if (!dir) {
1195  kError(7004) << "Internal error: job is listing" << url << "but itemsInUse only knows about" << itemsInUse.keys();
1196  Q_ASSERT( dir );
1197  return;
1198  }
1199 
1200  DirectoryDataHash::iterator dit = directoryData.find(urlStr);
1201  if (dit == directoryData.end()) {
1202  kError(7004) << "Internal error: job is listing" << url << "but directoryData doesn't know about that url, only about:" << directoryData.keys();
1203  Q_ASSERT(dit != directoryData.end());
1204  return;
1205  }
1206  KDirListerCacheDirectoryData& dirData = *dit;
1207  if (dirData.listersCurrentlyListing.isEmpty()) {
1208  kError(7004) << "Internal error: job is listing" << url << "but directoryData says no listers are currently listing " << urlStr;
1209 #ifndef NDEBUG
1210  printDebug();
1211 #endif
1212  Q_ASSERT( !dirData.listersCurrentlyListing.isEmpty() );
1213  return;
1214  }
1215 
1216  // check if anyone wants the mimetypes immediately
1217  bool delayedMimeTypes = true;
1218  foreach ( KDirLister *kdl, dirData.listersCurrentlyListing )
1219  delayedMimeTypes &= kdl->d->delayedMimeTypes;
1220 
1221  KIO::UDSEntryList::const_iterator it = entries.begin();
1222  const KIO::UDSEntryList::const_iterator end = entries.end();
1223  for ( ; it != end; ++it )
1224  {
1225  const QString name = (*it).stringValue( KIO::UDSEntry::UDS_NAME );
1226 
1227  Q_ASSERT( !name.isEmpty() );
1228  if ( name.isEmpty() )
1229  continue;
1230 
1231  if ( name == "." )
1232  {
1233  Q_ASSERT( dir->rootItem.isNull() );
1234  // Try to reuse an existing KFileItem (if we listed the parent dir)
1235  // rather than creating a new one. There are many reasons:
1236  // 1) renames and permission changes to the item would have to emit the signals
1237  // twice, otherwise, so that both views manage to recognize the item.
1238  // 2) with kio_ftp we can only know that something is a symlink when
1239  // listing the parent, so prefer that item, which has more info.
1240  // Note that it gives a funky name() to the root item, rather than "." ;)
1241  dir->rootItem = itemForUrl(url);
1242  if (dir->rootItem.isNull())
1243  dir->rootItem = KFileItem( *it, url, delayedMimeTypes, true );
1244 
1245  foreach ( KDirLister *kdl, dirData.listersCurrentlyListing )
1246  if ( kdl->d->rootFileItem.isNull() && kdl->d->url == url )
1247  kdl->d->rootFileItem = dir->rootItem;
1248  }
1249  else if ( name != ".." )
1250  {
1251  KFileItem item( *it, url, delayedMimeTypes, true );
1252 
1253  //kDebug(7004)<< "Adding item: " << item.url();
1254  dir->lstItems.append( item );
1255 
1256  foreach ( KDirLister *kdl, dirData.listersCurrentlyListing )
1257  kdl->d->addNewItem(url, item);
1258  }
1259  }
1260 
1261  foreach ( KDirLister *kdl, dirData.listersCurrentlyListing )
1262  kdl->d->emitItems();
1263 }
1264 
1265 void KDirListerCache::slotResult( KJob *j )
1266 {
1267 #ifdef DEBUG_CACHE
1268  //printDebug();
1269 #endif
1270 
1271  Q_ASSERT( j );
1272  KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
1273  runningListJobs.remove( job );
1274 
1275  KUrl jobUrl(joburl( job ));
1276  jobUrl.adjustPath(KUrl::RemoveTrailingSlash); // need remove trailing slashes again, in case of redirections
1277  QString jobUrlStr = jobUrl.url();
1278 
1279  kDebug(7004) << "finished listing" << jobUrl;
1280 
1281  DirectoryDataHash::iterator dit = directoryData.find(jobUrlStr);
1282  if (dit == directoryData.end()) {
1283  kError() << "Nothing found in directoryData for URL" << jobUrlStr;
1284 #ifndef NDEBUG
1285  printDebug();
1286 #endif
1287  Q_ASSERT(dit != directoryData.end());
1288  return;
1289  }
1290  KDirListerCacheDirectoryData& dirData = *dit;
1291  if ( dirData.listersCurrentlyListing.isEmpty() ) {
1292  kError() << "OOOOPS, nothing in directoryData.listersCurrentlyListing for" << jobUrlStr;
1293  // We're about to assert; dump the current state...
1294 #ifndef NDEBUG
1295  printDebug();
1296 #endif
1297  Q_ASSERT( !dirData.listersCurrentlyListing.isEmpty() );
1298  }
1299  QList<KDirLister *> listers = dirData.listersCurrentlyListing;
1300 
1301  // move all listers to the holding list, do it before emitting
1302  // the signals to make sure it exists in KDirListerCache in case someone
1303  // calls listDir during the signal emission
1304  Q_ASSERT( dirData.listersCurrentlyHolding.isEmpty() );
1305  dirData.moveListersWithoutCachedItemsJob(jobUrl);
1306 
1307  if ( job->error() )
1308  {
1309  foreach ( KDirLister *kdl, listers )
1310  {
1311  kdl->d->jobDone( job );
1312  if (job->error() != KJob::KilledJobError) {
1313  kdl->handleError( job );
1314  }
1315  const bool silent = job->property("_kdlc_silent").toBool();
1316  if (!silent) {
1317  emit kdl->canceled( jobUrl );
1318  }
1319 
1320  if (kdl->d->numJobs() == 0) {
1321  kdl->d->complete = true;
1322  if (!silent) {
1323  emit kdl->canceled();
1324  }
1325  }
1326  }
1327  }
1328  else
1329  {
1330  DirItem *dir = itemsInUse.value(jobUrlStr);
1331  Q_ASSERT( dir );
1332  dir->complete = true;
1333 
1334  foreach ( KDirLister* kdl, listers )
1335  {
1336  kdl->d->jobDone( job );
1337  emit kdl->completed( jobUrl );
1338  if ( kdl->d->numJobs() == 0 )
1339  {
1340  kdl->d->complete = true;
1341  emit kdl->completed();
1342  }
1343  }
1344  }
1345 
1346  // TODO: hmm, if there was an error and job is a parent of one or more
1347  // of the pending urls we should cancel it/them as well
1348  processPendingUpdates();
1349 
1350 #ifdef DEBUG_CACHE
1351  printDebug();
1352 #endif
1353 }
1354 
1355 void KDirListerCache::slotRedirection( KIO::Job *j, const KUrl& url )
1356 {
1357  Q_ASSERT( j );
1358  KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
1359 
1360  KUrl oldUrl(job->url()); // here we really need the old url!
1361  KUrl newUrl(url);
1362 
1363  // strip trailing slashes
1364  oldUrl.adjustPath(KUrl::RemoveTrailingSlash);
1365  newUrl.adjustPath(KUrl::RemoveTrailingSlash);
1366 
1367  if ( oldUrl == newUrl ) {
1368  kDebug(7004) << "New redirection url same as old, giving up.";
1369  return;
1370  } else if (newUrl.isEmpty()) {
1371  kDebug(7004) << "New redirection url is empty, giving up.";
1372  return;
1373  }
1374 
1375  const QString oldUrlStr = oldUrl.url();
1376  const QString newUrlStr = newUrl.url();
1377 
1378  kDebug(7004) << oldUrl << "->" << newUrl;
1379 
1380 #ifdef DEBUG_CACHE
1381  // Can't do that here. KDirListerCache::joburl() will use the new url already,
1382  // while our data structures haven't been updated yet -> assert fail.
1383  //printDebug();
1384 #endif
1385 
1386  // I don't think there can be dirItems that are children of oldUrl.
1387  // Am I wrong here? And even if so, we don't need to delete them, right?
1388  // DF: redirection happens before listDir emits any item. Makes little sense otherwise.
1389 
1390  // oldUrl cannot be in itemsCached because only completed items are moved there
1391  DirItem *dir = itemsInUse.take(oldUrlStr);
1392  Q_ASSERT( dir );
1393 
1394  DirectoryDataHash::iterator dit = directoryData.find(oldUrlStr);
1395  Q_ASSERT(dit != directoryData.end());
1396  KDirListerCacheDirectoryData oldDirData = *dit;
1397  directoryData.erase(dit);
1398  Q_ASSERT( !oldDirData.listersCurrentlyListing.isEmpty() );
1399  const QList<KDirLister *> listers = oldDirData.listersCurrentlyListing;
1400  Q_ASSERT( !listers.isEmpty() );
1401 
1402  foreach ( KDirLister *kdl, listers ) {
1403  kdl->d->redirect(oldUrlStr, newUrl, false /*clear items*/);
1404  }
1405 
1406  // when a lister was stopped before the job emits the redirection signal, the old url will
1407  // also be in listersCurrentlyHolding
1408  const QList<KDirLister *> holders = oldDirData.listersCurrentlyHolding;
1409  foreach ( KDirLister *kdl, holders ) {
1410  kdl->d->jobStarted( job );
1411  // do it like when starting a new list-job that will redirect later
1412  // TODO: maybe don't emit started if there's an update running for newUrl already?
1413  emit kdl->started( oldUrl );
1414 
1415  kdl->d->redirect(oldUrl, newUrl, false /*clear items*/);
1416  }
1417 
1418  DirItem *newDir = itemsInUse.value(newUrlStr);
1419  if ( newDir ) {
1420  kDebug(7004) << newUrl << "already in use";
1421 
1422  // only in this case there can newUrl already be in listersCurrentlyListing or listersCurrentlyHolding
1423  delete dir;
1424 
1425  // get the job if one's running for newUrl already (can be a list-job or an update-job), but
1426  // do not return this 'job', which would happen because of the use of redirectionURL()
1427  KIO::ListJob *oldJob = jobForUrl( newUrlStr, job );
1428 
1429  // listers of newUrl with oldJob: forget about the oldJob and use the already running one
1430  // which will be converted to an updateJob
1431  KDirListerCacheDirectoryData& newDirData = directoryData[newUrlStr];
1432 
1433  QList<KDirLister *>& curListers = newDirData.listersCurrentlyListing;
1434  if ( !curListers.isEmpty() ) {
1435  kDebug(7004) << "and it is currently listed";
1436 
1437  Q_ASSERT( oldJob ); // ?!
1438 
1439  foreach ( KDirLister *kdl, curListers ) { // listers of newUrl
1440  kdl->d->jobDone( oldJob );
1441 
1442  kdl->d->jobStarted( job );
1443  kdl->d->connectJob( job );
1444  }
1445 
1446  // append listers of oldUrl with newJob to listers of newUrl with oldJob
1447  foreach ( KDirLister *kdl, listers )
1448  curListers.append( kdl );
1449  } else {
1450  curListers = listers;
1451  }
1452 
1453  if ( oldJob ) // kill the old job, be it a list-job or an update-job
1454  killJob( oldJob );
1455 
1456  // holders of newUrl: use the already running job which will be converted to an updateJob
1457  QList<KDirLister *>& curHolders = newDirData.listersCurrentlyHolding;
1458  if ( !curHolders.isEmpty() ) {
1459  kDebug(7004) << "and it is currently held.";
1460 
1461  foreach ( KDirLister *kdl, curHolders ) { // holders of newUrl
1462  kdl->d->jobStarted( job );
1463  emit kdl->started( newUrl );
1464  }
1465 
1466  // append holders of oldUrl to holders of newUrl
1467  foreach ( KDirLister *kdl, holders )
1468  curHolders.append( kdl );
1469  } else {
1470  curHolders = holders;
1471  }
1472 
1473 
1474  // emit old items: listers, holders. NOT: newUrlListers/newUrlHolders, they already have them listed
1475  // TODO: make this a separate method?
1476  foreach ( KDirLister *kdl, listers + holders ) {
1477  if ( kdl->d->rootFileItem.isNull() && kdl->d->url == newUrl )
1478  kdl->d->rootFileItem = newDir->rootItem;
1479 
1480  kdl->d->addNewItems(newUrl, newDir->lstItems);
1481  kdl->d->emitItems();
1482  }
1483  } else if ( (newDir = itemsCached.take( newUrlStr )) ) {
1484  kDebug(7004) << newUrl << "is unused, but already in the cache.";
1485 
1486  delete dir;
1487  itemsInUse.insert( newUrlStr, newDir );
1488  KDirListerCacheDirectoryData& newDirData = directoryData[newUrlStr];
1489  newDirData.listersCurrentlyListing = listers;
1490  newDirData.listersCurrentlyHolding = holders;
1491 
1492  // emit old items: listers, holders
1493  foreach ( KDirLister *kdl, listers + holders ) {
1494  if ( kdl->d->rootFileItem.isNull() && kdl->d->url == newUrl )
1495  kdl->d->rootFileItem = newDir->rootItem;
1496 
1497  kdl->d->addNewItems(newUrl, newDir->lstItems);
1498  kdl->d->emitItems();
1499  }
1500  } else {
1501  kDebug(7004) << newUrl << "has not been listed yet.";
1502 
1503  dir->rootItem = KFileItem();
1504  dir->lstItems.clear();
1505  dir->redirect( newUrl );
1506  itemsInUse.insert( newUrlStr, dir );
1507  KDirListerCacheDirectoryData& newDirData = directoryData[newUrlStr];
1508  newDirData.listersCurrentlyListing = listers;
1509  newDirData.listersCurrentlyHolding = holders;
1510 
1511  if ( holders.isEmpty() ) {
1512 #ifdef DEBUG_CACHE
1513  printDebug();
1514 #endif
1515  return; // only in this case the job doesn't need to be converted,
1516  }
1517  }
1518 
1519  // make the job an update job
1520  job->disconnect( this );
1521 
1522  connect( job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
1523  this, SLOT(slotUpdateEntries(KIO::Job*,KIO::UDSEntryList)) );
1524  connect( job, SIGNAL(result(KJob*)),
1525  this, SLOT(slotUpdateResult(KJob*)) );
1526 
1527  // FIXME: autoUpdate-Counts!!
1528 
1529 #ifdef DEBUG_CACHE
1530  printDebug();
1531 #endif
1532 }
1533 
1534 struct KDirListerCache::ItemInUseChange
1535 {
1536  ItemInUseChange(const QString& old, const QString& newU, DirItem* di)
1537  : oldUrl(old), newUrl(newU), dirItem(di) {}
1538  QString oldUrl;
1539  QString newUrl;
1540  DirItem* dirItem;
1541 };
1542 
1543 void KDirListerCache::renameDir( const KUrl &oldUrl, const KUrl &newUrl )
1544 {
1545  kDebug(7004) << oldUrl << "->" << newUrl;
1546  const QString oldUrlStr = oldUrl.url(KUrl::RemoveTrailingSlash);
1547  const QString newUrlStr = newUrl.url(KUrl::RemoveTrailingSlash);
1548 
1549  // Not enough. Also need to look at any child dir, even sub-sub-sub-dir.
1550  //DirItem *dir = itemsInUse.take( oldUrlStr );
1551  //emitRedirections( oldUrl, url );
1552 
1553  QLinkedList<ItemInUseChange> itemsToChange;
1554  QSet<KDirLister *> listers;
1555 
1556  // Look at all dirs being listed/shown
1557  QHash<QString, DirItem *>::iterator itu = itemsInUse.begin();
1558  const QHash<QString, DirItem *>::iterator ituend = itemsInUse.end();
1559  for (; itu != ituend ; ++itu) {
1560  DirItem *dir = itu.value();
1561  KUrl oldDirUrl ( itu.key() );
1562  //kDebug(7004) << "itemInUse:" << oldDirUrl;
1563  // Check if this dir is oldUrl, or a subfolder of it
1564  if ( oldUrl.isParentOf( oldDirUrl ) ) {
1565  // TODO should use KUrl::cleanpath like isParentOf does
1566  QString relPath = oldDirUrl.path().mid( oldUrl.path().length() );
1567 
1568  KUrl newDirUrl( newUrl ); // take new base
1569  if ( !relPath.isEmpty() )
1570  newDirUrl.addPath( relPath ); // add unchanged relative path
1571  //kDebug(7004) << "new url=" << newDirUrl;
1572 
1573  // Update URL in dir item and in itemsInUse
1574  dir->redirect( newDirUrl );
1575 
1576  itemsToChange.append(ItemInUseChange(oldDirUrl.url(KUrl::RemoveTrailingSlash),
1577  newDirUrl.url(KUrl::RemoveTrailingSlash),
1578  dir));
1579  // Rename all items under that dir
1580 
1581  for ( KFileItemList::iterator kit = dir->lstItems.begin(), kend = dir->lstItems.end();
1582  kit != kend ; ++kit )
1583  {
1584  const KFileItem oldItem = *kit;
1585 
1586  const KUrl oldItemUrl ((*kit).url());
1587  const QString oldItemUrlStr( oldItemUrl.url(KUrl::RemoveTrailingSlash) );
1588  KUrl newItemUrl( oldItemUrl );
1589  newItemUrl.setPath( newDirUrl.path() );
1590  newItemUrl.addPath( oldItemUrl.fileName() );
1591  kDebug(7004) << "renaming" << oldItemUrl << "to" << newItemUrl;
1592  (*kit).setUrl(newItemUrl);
1593 
1594  listers |= emitRefreshItem(oldItem, *kit);
1595  }
1596  }
1597  }
1598 
1599  Q_FOREACH(KDirLister * kdl, listers) {
1600  kdl->d->emitItems();
1601  }
1602 
1603  // Do the changes to itemsInUse out of the loop to avoid messing up iterators,
1604  // and so that emitRefreshItem can find the stuff in the hash.
1605  foreach(const ItemInUseChange& i, itemsToChange) {
1606  itemsInUse.remove(i.oldUrl);
1607  itemsInUse.insert(i.newUrl, i.dirItem);
1608  }
1609  //Now that all the caches are updated and consistent, emit the redirection.
1610  foreach(const ItemInUseChange& i, itemsToChange) {
1611  emitRedirections(i.oldUrl, i.newUrl);
1612  }
1613 
1614  // Is oldUrl a directory in the cache?
1615  // Remove any child of oldUrl from the cache - even if the renamed dir itself isn't in it!
1616  removeDirFromCache( oldUrl );
1617  // TODO rename, instead.
1618 }
1619 
1620 // helper for renameDir, not used for redirections from KIO::listDir().
1621 void KDirListerCache::emitRedirections( const KUrl &oldUrl, const KUrl &newUrl )
1622 {
1623  kDebug(7004) << oldUrl << "->" << newUrl;
1624  const QString oldUrlStr = oldUrl.url(KUrl::RemoveTrailingSlash);
1625  const QString newUrlStr = newUrl.url(KUrl::RemoveTrailingSlash);
1626 
1627  KIO::ListJob *job = jobForUrl( oldUrlStr );
1628  if ( job )
1629  killJob( job );
1630 
1631  // Check if we were listing this dir. Need to abort and restart with new name in that case.
1632  DirectoryDataHash::iterator dit = directoryData.find(oldUrlStr);
1633  if ( dit == directoryData.end() )
1634  return;
1635  const QList<KDirLister *> listers = (*dit).listersCurrentlyListing;
1636  const QList<KDirLister *> holders = (*dit).listersCurrentlyHolding;
1637 
1638  KDirListerCacheDirectoryData& newDirData = directoryData[newUrlStr];
1639 
1640  // Tell the world that the job listing the old url is dead.
1641  foreach ( KDirLister *kdl, listers ) {
1642  if ( job )
1643  kdl->d->jobDone( job );
1644 
1645  emit kdl->canceled( oldUrl );
1646  }
1647  newDirData.listersCurrentlyListing += listers;
1648 
1649  // Check if we are currently displaying this directory (odds opposite wrt above)
1650  foreach ( KDirLister *kdl, holders ) {
1651  if ( job )
1652  kdl->d->jobDone( job );
1653  }
1654  newDirData.listersCurrentlyHolding += holders;
1655  directoryData.erase(dit);
1656 
1657  if ( !listers.isEmpty() ) {
1658  updateDirectory( newUrl );
1659 
1660  // Tell the world about the new url
1661  foreach ( KDirLister *kdl, listers )
1662  emit kdl->started( newUrl );
1663  }
1664 
1665  // And notify the dirlisters of the redirection
1666  foreach ( KDirLister *kdl, holders ) {
1667  kdl->d->redirect(oldUrl, newUrl, true /*keep items*/);
1668  }
1669 }
1670 
1671 void KDirListerCache::removeDirFromCache( const KUrl& dir )
1672 {
1673  kDebug(7004) << dir;
1674  const QList<QString> cachedDirs = itemsCached.keys(); // seems slow, but there's no qcache iterator...
1675  foreach(const QString& cachedDir, cachedDirs) {
1676  if ( dir.isParentOf( KUrl( cachedDir ) ) )
1677  itemsCached.remove( cachedDir );
1678  }
1679 }
1680 
1681 void KDirListerCache::slotUpdateEntries( KIO::Job* job, const KIO::UDSEntryList& list )
1682 {
1683  runningListJobs[static_cast<KIO::ListJob*>(job)] += list;
1684 }
1685 
1686 void KDirListerCache::slotUpdateResult( KJob * j )
1687 {
1688  Q_ASSERT( j );
1689  KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
1690 
1691  KUrl jobUrl (joburl( job ));
1692  jobUrl.adjustPath(KUrl::RemoveTrailingSlash); // need remove trailing slashes again, in case of redirections
1693  QString jobUrlStr (jobUrl.url());
1694 
1695  kDebug(7004) << "finished update" << jobUrl;
1696 
1697  KDirListerCacheDirectoryData& dirData = directoryData[jobUrlStr];
1698  // Collect the dirlisters which were listing the URL using that ListJob
1699  // plus those that were already holding that URL - they all get updated.
1700  dirData.moveListersWithoutCachedItemsJob(jobUrl);
1701  QList<KDirLister *> listers = dirData.listersCurrentlyHolding;
1702  listers += dirData.listersCurrentlyListing;
1703 
1704  // once we are updating dirs that are only in the cache this will fail!
1705  Q_ASSERT( !listers.isEmpty() );
1706 
1707  if ( job->error() ) {
1708  foreach ( KDirLister* kdl, listers ) {
1709  kdl->d->jobDone( job );
1710 
1711  //don't bother the user
1712  //kdl->handleError( job );
1713 
1714  const bool silent = job->property("_kdlc_silent").toBool();
1715  if (!silent) {
1716  emit kdl->canceled( jobUrl );
1717  }
1718  if ( kdl->d->numJobs() == 0 ) {
1719  kdl->d->complete = true;
1720  if (!silent) {
1721  emit kdl->canceled();
1722  }
1723  }
1724  }
1725 
1726  runningListJobs.remove( job );
1727 
1728  // TODO: if job is a parent of one or more
1729  // of the pending urls we should cancel them
1730  processPendingUpdates();
1731  return;
1732  }
1733 
1734  DirItem *dir = itemsInUse.value(jobUrlStr, 0);
1735  if (!dir) {
1736  kError(7004) << "Internal error: itemsInUse did not contain" << jobUrlStr;
1737 #ifndef NDEBUG
1738  printDebug();
1739 #endif
1740  Q_ASSERT(dir);
1741  } else {
1742  dir->complete = true;
1743  }
1744 
1745  // check if anyone wants the mimetypes immediately
1746  bool delayedMimeTypes = true;
1747  foreach ( KDirLister *kdl, listers )
1748  delayedMimeTypes &= kdl->d->delayedMimeTypes;
1749 
1750  QHash<QString, KFileItem*> fileItems; // fileName -> KFileItem*
1751 
1752  // Unmark all items in url
1753  for ( KFileItemList::iterator kit = dir->lstItems.begin(), kend = dir->lstItems.end() ; kit != kend ; ++kit )
1754  {
1755  (*kit).unmark();
1756  fileItems.insert( (*kit).name(), &*kit );
1757  }
1758 
1759  const KIO::UDSEntryList& buf = runningListJobs.value( job );
1760  KIO::UDSEntryList::const_iterator it = buf.constBegin();
1761  const KIO::UDSEntryList::const_iterator end = buf.constEnd();
1762  for ( ; it != end; ++it )
1763  {
1764  // Form the complete url
1765  KFileItem item( *it, jobUrl, delayedMimeTypes, true );
1766 
1767  const QString name = item.name();
1768  Q_ASSERT( !name.isEmpty() );
1769 
1770  // we duplicate the check for dotdot here, to avoid iterating over
1771  // all items again and checking in matchesFilter() that way.
1772  if ( name.isEmpty() || name == ".." )
1773  continue;
1774 
1775  if ( name == "." )
1776  {
1777  // if the update was started before finishing the original listing
1778  // there is no root item yet
1779  if ( dir->rootItem.isNull() )
1780  {
1781  dir->rootItem = item;
1782 
1783  foreach ( KDirLister *kdl, listers )
1784  if ( kdl->d->rootFileItem.isNull() && kdl->d->url == jobUrl )
1785  kdl->d->rootFileItem = dir->rootItem;
1786  }
1787  continue;
1788  }
1789 
1790  // Find this item
1791  if (KFileItem* tmp = fileItems.value(item.name()))
1792  {
1793  QSet<KFileItem*>::iterator pru_it = pendingRemoteUpdates.find(tmp);
1794  const bool inPendingRemoteUpdates = (pru_it != pendingRemoteUpdates.end());
1795 
1796  // check if something changed for this file, using KFileItem::cmp()
1797  if (!tmp->cmp( item ) || inPendingRemoteUpdates) {
1798 
1799  if (inPendingRemoteUpdates) {
1800  pendingRemoteUpdates.erase(pru_it);
1801  }
1802 
1803  //kDebug(7004) << "file changed:" << tmp->name();
1804 
1805  const KFileItem oldItem = *tmp;
1806  *tmp = item;
1807  foreach ( KDirLister *kdl, listers )
1808  kdl->d->addRefreshItem(jobUrl, oldItem, *tmp);
1809  }
1810  //kDebug(7004) << "marking" << tmp;
1811  tmp->mark();
1812  }
1813  else // this is a new file
1814  {
1815  //kDebug(7004) << "new file:" << name;
1816 
1817  KFileItem pitem(item);
1818  pitem.mark();
1819  dir->lstItems.append( pitem );
1820 
1821  foreach ( KDirLister *kdl, listers )
1822  kdl->d->addNewItem(jobUrl, pitem);
1823  }
1824  }
1825 
1826  runningListJobs.remove( job );
1827 
1828  deleteUnmarkedItems( listers, dir->lstItems );
1829 
1830  foreach ( KDirLister *kdl, listers ) {
1831  kdl->d->emitItems();
1832 
1833  kdl->d->jobDone( job );
1834 
1835  emit kdl->completed( jobUrl );
1836  if ( kdl->d->numJobs() == 0 )
1837  {
1838  kdl->d->complete = true;
1839  emit kdl->completed();
1840  }
1841  }
1842 
1843  // TODO: hmm, if there was an error and job is a parent of one or more
1844  // of the pending urls we should cancel it/them as well
1845  processPendingUpdates();
1846 }
1847 
1848 // private
1849 
1850 KIO::ListJob *KDirListerCache::jobForUrl( const QString& url, KIO::ListJob *not_job )
1851 {
1852  QMap< KIO::ListJob *, KIO::UDSEntryList >::const_iterator it = runningListJobs.constBegin();
1853  while ( it != runningListJobs.constEnd() )
1854  {
1855  KIO::ListJob *job = it.key();
1856  if ( joburl( job ).url(KUrl::RemoveTrailingSlash) == url && job != not_job )
1857  return job;
1858  ++it;
1859  }
1860  return 0;
1861 }
1862 
1863 const KUrl& KDirListerCache::joburl( KIO::ListJob *job )
1864 {
1865  if ( job->redirectionUrl().isValid() )
1866  return job->redirectionUrl();
1867  else
1868  return job->url();
1869 }
1870 
1871 void KDirListerCache::killJob( KIO::ListJob *job )
1872 {
1873  runningListJobs.remove( job );
1874  job->disconnect( this );
1875  job->kill();
1876 }
1877 
1878 void KDirListerCache::deleteUnmarkedItems( const QList<KDirLister *>& listers, KFileItemList &lstItems )
1879 {
1880  KFileItemList deletedItems;
1881  // Find all unmarked items and delete them
1882  QMutableListIterator<KFileItem> kit(lstItems);
1883  while (kit.hasNext()) {
1884  const KFileItem& item = kit.next();
1885  if (!item.isMarked()) {
1886  //kDebug(7004) << "deleted:" << item.name() << &item;
1887  deletedItems.append(item);
1888  kit.remove();
1889  }
1890  }
1891  if (!deletedItems.isEmpty())
1892  itemsDeleted(listers, deletedItems);
1893 }
1894 
1895 void KDirListerCache::itemsDeleted(const QList<KDirLister *>& listers, const KFileItemList& deletedItems)
1896 {
1897  Q_FOREACH(KDirLister *kdl, listers) {
1898  kdl->d->emitItemsDeleted(deletedItems);
1899  }
1900 
1901  Q_FOREACH(const KFileItem& item, deletedItems) {
1902  if (item.isDir())
1903  deleteDir(item.url());
1904  }
1905 }
1906 
1907 void KDirListerCache::deleteDir( const KUrl& dirUrl )
1908 {
1909  //kDebug() << dirUrl;
1910  // unregister and remove the children of the deleted item.
1911  // Idea: tell all the KDirListers that they should forget the dir
1912  // and then remove it from the cache.
1913 
1914  // Separate itemsInUse iteration and calls to forgetDirs (which modify itemsInUse)
1915  KUrl::List affectedItems;
1916 
1917  QHash<QString, DirItem *>::iterator itu = itemsInUse.begin();
1918  const QHash<QString, DirItem *>::iterator ituend = itemsInUse.end();
1919  for ( ; itu != ituend; ++itu ) {
1920  const KUrl deletedUrl( itu.key() );
1921  if ( dirUrl.isParentOf( deletedUrl ) ) {
1922  affectedItems.append(deletedUrl);
1923  }
1924  }
1925 
1926  foreach(const KUrl& deletedUrl, affectedItems) {
1927  const QString deletedUrlStr = deletedUrl.url();
1928  // stop all jobs for deletedUrlStr
1929  DirectoryDataHash::iterator dit = directoryData.find(deletedUrlStr);
1930  if (dit != directoryData.end()) {
1931  // we need a copy because stop modifies the list
1932  QList<KDirLister *> listers = (*dit).listersCurrentlyListing;
1933  foreach ( KDirLister *kdl, listers )
1934  stopListingUrl( kdl, deletedUrl );
1935  // tell listers holding deletedUrl to forget about it
1936  // this will stop running updates for deletedUrl as well
1937 
1938  // we need a copy because forgetDirs modifies the list
1939  QList<KDirLister *> holders = (*dit).listersCurrentlyHolding;
1940  foreach ( KDirLister *kdl, holders ) {
1941  // lister's root is the deleted item
1942  if ( kdl->d->url == deletedUrl )
1943  {
1944  // tell the view first. It might need the subdirs' items (which forgetDirs will delete)
1945  if ( !kdl->d->rootFileItem.isNull() ) {
1946  emit kdl->deleteItem( kdl->d->rootFileItem );
1947  emit kdl->itemsDeleted(KFileItemList() << kdl->d->rootFileItem);
1948  }
1949  forgetDirs( kdl );
1950  kdl->d->rootFileItem = KFileItem();
1951  }
1952  else
1953  {
1954  const bool treeview = kdl->d->lstDirs.count() > 1;
1955  if ( !treeview )
1956  {
1957  emit kdl->clear();
1958  kdl->d->lstDirs.clear();
1959  }
1960  else
1961  kdl->d->lstDirs.removeAll( deletedUrl );
1962 
1963  forgetDirs( kdl, deletedUrl, treeview );
1964  }
1965  }
1966  }
1967 
1968  // delete the entry for deletedUrl - should not be needed, it's in
1969  // items cached now
1970  int count = itemsInUse.remove( deletedUrlStr );
1971  Q_ASSERT( count == 0 );
1972  Q_UNUSED( count ); //keep gcc "unused variable" complaining quiet when in release mode
1973  }
1974 
1975  // remove the children from the cache
1976  removeDirFromCache( dirUrl );
1977 }
1978 
1979 // delayed updating of files, FAM is flooding us with events
1980 void KDirListerCache::processPendingUpdates()
1981 {
1982  QSet<KDirLister *> listers;
1983  foreach(const QString& file, pendingUpdates) { // always a local path
1984  kDebug(7004) << file;
1985  KUrl u(file);
1986  KFileItem *item = findByUrl( 0, u ); // search all items
1987  if ( item ) {
1988  // we need to refresh the item, because e.g. the permissions can have changed.
1989  KFileItem oldItem = *item;
1990  item->refresh();
1991  listers |= emitRefreshItem( oldItem, *item );
1992  }
1993  }
1994  pendingUpdates.clear();
1995  Q_FOREACH(KDirLister * kdl, listers) {
1996  kdl->d->emitItems();
1997  }
1998 }
1999 
2000 #ifndef NDEBUG
2001 void KDirListerCache::printDebug()
2002 {
2003  kDebug(7004) << "Items in use:";
2004  QHash<QString, DirItem *>::const_iterator itu = itemsInUse.constBegin();
2005  const QHash<QString, DirItem *>::const_iterator ituend = itemsInUse.constEnd();
2006  for ( ; itu != ituend ; ++itu ) {
2007  kDebug(7004) << " " << itu.key() << "URL:" << itu.value()->url
2008  << "rootItem:" << ( !itu.value()->rootItem.isNull() ? itu.value()->rootItem.url() : KUrl() )
2009  << "autoUpdates refcount:" << itu.value()->autoUpdates
2010  << "complete:" << itu.value()->complete
2011  << QString("with %1 items.").arg(itu.value()->lstItems.count());
2012  }
2013 
2014  QList<KDirLister*> listersWithoutJob;
2015  kDebug(7004) << "Directory data:";
2016  DirectoryDataHash::const_iterator dit = directoryData.constBegin();
2017  for ( ; dit != directoryData.constEnd(); ++dit )
2018  {
2019  QString list;
2020  foreach ( KDirLister* listit, (*dit).listersCurrentlyListing )
2021  list += " 0x" + QString::number( (qlonglong)listit, 16 );
2022  kDebug(7004) << " " << dit.key() << (*dit).listersCurrentlyListing.count() << "listers:" << list;
2023  foreach ( KDirLister* listit, (*dit).listersCurrentlyListing ) {
2024  if (!listit->d->m_cachedItemsJobs.isEmpty()) {
2025  kDebug(7004) << " Lister" << listit << "has CachedItemsJobs" << listit->d->m_cachedItemsJobs;
2026  } else if (KIO::ListJob* listJob = jobForUrl(dit.key())) {
2027  kDebug(7004) << " Lister" << listit << "has ListJob" << listJob;
2028  } else {
2029  listersWithoutJob.append(listit);
2030  }
2031  }
2032 
2033  list.clear();
2034  foreach ( KDirLister* listit, (*dit).listersCurrentlyHolding )
2035  list += " 0x" + QString::number( (qlonglong)listit, 16 );
2036  kDebug(7004) << " " << dit.key() << (*dit).listersCurrentlyHolding.count() << "holders:" << list;
2037  }
2038 
2039  QMap< KIO::ListJob *, KIO::UDSEntryList >::Iterator jit = runningListJobs.begin();
2040  kDebug(7004) << "Jobs:";
2041  for ( ; jit != runningListJobs.end() ; ++jit )
2042  kDebug(7004) << " " << jit.key() << "listing" << joburl( jit.key() ) << ":" << (*jit).count() << "entries.";
2043 
2044  kDebug(7004) << "Items in cache:";
2045  const QList<QString> cachedDirs = itemsCached.keys();
2046  foreach(const QString& cachedDir, cachedDirs) {
2047  DirItem* dirItem = itemsCached.object(cachedDir);
2048  kDebug(7004) << " " << cachedDir << "rootItem:"
2049  << (!dirItem->rootItem.isNull() ? dirItem->rootItem.url().prettyUrl() : QString("NULL") )
2050  << "with" << dirItem->lstItems.count() << "items.";
2051  }
2052 
2053  // Abort on listers without jobs -after- showing the full dump. Easier debugging.
2054  Q_FOREACH(KDirLister* listit, listersWithoutJob) {
2055  kFatal() << "HUH? Lister" << listit << "is supposed to be listing, but has no job!";
2056  }
2057 }
2058 #endif
2059 
2060 
2061 KDirLister::KDirLister( QObject* parent )
2062  : QObject(parent), d(new Private(this))
2063 {
2064  //kDebug(7003) << "+KDirLister";
2065 
2066  d->complete = true;
2067 
2068  setAutoUpdate( true );
2069  setDirOnlyMode( false );
2070  setShowingDotFiles( false );
2071 
2072  setAutoErrorHandlingEnabled( true, 0 );
2073 }
2074 
2075 KDirLister::~KDirLister()
2076 {
2077  //kDebug(7003) << "~KDirLister" << this;
2078 
2079  // Stop all running jobs, remove lister from lists
2080  if (!kDirListerCache.isDestroyed()) {
2081  stop();
2082  kDirListerCache->forgetDirs( this );
2083  }
2084 
2085  delete d;
2086 }
2087 
2088 bool KDirLister::openUrl( const KUrl& _url, OpenUrlFlags _flags )
2089 {
2090  // emit the current changes made to avoid an inconsistent treeview
2091  if (d->hasPendingChanges && (_flags & Keep))
2092  emitChanges();
2093 
2094  d->hasPendingChanges = false;
2095 
2096  return kDirListerCache->listDir( this, _url, _flags & Keep, _flags & Reload );
2097 }
2098 
2099 void KDirLister::stop()
2100 {
2101  kDirListerCache->stop( this );
2102 }
2103 
2104 void KDirLister::stop( const KUrl& _url )
2105 {
2106  kDirListerCache->stopListingUrl( this, _url );
2107 }
2108 
2109 bool KDirLister::autoUpdate() const
2110 {
2111  return d->autoUpdate;
2112 }
2113 
2114 void KDirLister::setAutoUpdate( bool _enable )
2115 {
2116  if ( d->autoUpdate == _enable )
2117  return;
2118 
2119  d->autoUpdate = _enable;
2120  kDirListerCache->setAutoUpdate( this, _enable );
2121 }
2122 
2123 bool KDirLister::showingDotFiles() const
2124 {
2125  return d->settings.isShowingDotFiles;
2126 }
2127 
2128 void KDirLister::setShowingDotFiles( bool _showDotFiles )
2129 {
2130  if ( d->settings.isShowingDotFiles == _showDotFiles )
2131  return;
2132 
2133  d->prepareForSettingsChange();
2134  d->settings.isShowingDotFiles = _showDotFiles;
2135 }
2136 
2137 bool KDirLister::dirOnlyMode() const
2138 {
2139  return d->settings.dirOnlyMode;
2140 }
2141 
2142 void KDirLister::setDirOnlyMode( bool _dirsOnly )
2143 {
2144  if ( d->settings.dirOnlyMode == _dirsOnly )
2145  return;
2146 
2147  d->prepareForSettingsChange();
2148  d->settings.dirOnlyMode = _dirsOnly;
2149 }
2150 
2151 bool KDirLister::autoErrorHandlingEnabled() const
2152 {
2153  return d->autoErrorHandling;
2154 }
2155 
2156 void KDirLister::setAutoErrorHandlingEnabled( bool enable, QWidget* parent )
2157 {
2158  d->autoErrorHandling = enable;
2159  d->errorParent = parent;
2160 }
2161 
2162 KUrl KDirLister::url() const
2163 {
2164  return d->url;
2165 }
2166 
2167 KUrl::List KDirLister::directories() const
2168 {
2169  return d->lstDirs;
2170 }
2171 
2172 void KDirLister::emitChanges()
2173 {
2174  d->emitChanges();
2175 }
2176 
2177 void KDirLister::Private::emitChanges()
2178 {
2179  if (!hasPendingChanges)
2180  return;
2181 
2182  // reset 'hasPendingChanges' now, in case of recursion
2183  // (testcase: enabling recursive scan in ktorrent, #174920)
2184  hasPendingChanges = false;
2185 
2186  const Private::FilterSettings newSettings = settings;
2187  settings = oldSettings; // temporarily
2188 
2189  // Mark all items that are currently visible
2190  Q_FOREACH(const KUrl& dir, lstDirs) {
2191  KFileItemList* itemList = kDirListerCache->itemsForDir(dir);
2192  if (!itemList) {
2193  continue;
2194  }
2195 
2196  KFileItemList::iterator kit = itemList->begin();
2197  const KFileItemList::iterator kend = itemList->end();
2198  for (; kit != kend; ++kit) {
2199  if (isItemVisible(*kit) && m_parent->matchesMimeFilter(*kit))
2200  (*kit).mark();
2201  else
2202  (*kit).unmark();
2203  }
2204  }
2205 
2206  settings = newSettings;
2207 
2208  Q_FOREACH(const KUrl& dir, lstDirs) {
2209  KFileItemList deletedItems;
2210 
2211  KFileItemList* itemList = kDirListerCache->itemsForDir(dir);
2212  if (!itemList) {
2213  continue;
2214  }
2215 
2216  KFileItemList::iterator kit = itemList->begin();
2217  const KFileItemList::iterator kend = itemList->end();
2218  for (; kit != kend; ++kit) {
2219  KFileItem& item = *kit;
2220  const QString text = item.text();
2221  if (text == "." || text == "..")
2222  continue;
2223  const bool nowVisible = isItemVisible(item) && m_parent->matchesMimeFilter(item);
2224  if (nowVisible && !item.isMarked())
2225  addNewItem(dir, item); // takes care of emitting newItem or itemsFilteredByMime
2226  else if (!nowVisible && item.isMarked())
2227  deletedItems.append(*kit);
2228  }
2229  if (!deletedItems.isEmpty()) {
2230  emit m_parent->itemsDeleted(deletedItems);
2231  // for compat
2232  Q_FOREACH(const KFileItem& item, deletedItems)
2233  emit m_parent->deleteItem(item);
2234  }
2235  emitItems();
2236  }
2237  oldSettings = settings;
2238 }
2239 
2240 void KDirLister::updateDirectory( const KUrl& _u )
2241 {
2242  kDirListerCache->updateDirectory( _u );
2243 }
2244 
2245 bool KDirLister::isFinished() const
2246 {
2247  return d->complete;
2248 }
2249 
2250 KFileItem KDirLister::rootItem() const
2251 {
2252  return d->rootFileItem;
2253 }
2254 
2255 KFileItem KDirLister::findByUrl( const KUrl& _url ) const
2256 {
2257  KFileItem *item = kDirListerCache->findByUrl( this, _url );
2258  if (item) {
2259  return *item;
2260  } else {
2261  return KFileItem();
2262  }
2263 }
2264 
2265 KFileItem KDirLister::findByName( const QString& _name ) const
2266 {
2267  return kDirListerCache->findByName( this, _name );
2268 }
2269 
2270 
2271 // ================ public filter methods ================ //
2272 
2273 void KDirLister::setNameFilter( const QString& nameFilter )
2274 {
2275  if (d->nameFilter == nameFilter)
2276  return;
2277 
2278  d->prepareForSettingsChange();
2279 
2280  d->settings.lstFilters.clear();
2281  d->nameFilter = nameFilter;
2282  // Split on white space
2283  const QStringList list = nameFilter.split( ' ', QString::SkipEmptyParts );
2284  for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it)
2285  d->settings.lstFilters.append(QRegExp(*it, Qt::CaseInsensitive, QRegExp::Wildcard));
2286 }
2287 
2288 QString KDirLister::nameFilter() const
2289 {
2290  return d->nameFilter;
2291 }
2292 
2293 void KDirLister::setMimeFilter( const QStringList& mimeFilter )
2294 {
2295  if (d->settings.mimeFilter == mimeFilter)
2296  return;
2297 
2298  d->prepareForSettingsChange();
2299  if (mimeFilter.contains(QLatin1String("application/octet-stream")) || mimeFilter.contains(QLatin1String("all/allfiles"))) // all files
2300  d->settings.mimeFilter.clear();
2301  else
2302  d->settings.mimeFilter = mimeFilter;
2303 }
2304 
2305 void KDirLister::setMimeExcludeFilter( const QStringList& mimeExcludeFilter )
2306 {
2307  if (d->settings.mimeExcludeFilter == mimeExcludeFilter)
2308  return;
2309 
2310  d->prepareForSettingsChange();
2311  d->settings.mimeExcludeFilter = mimeExcludeFilter;
2312 }
2313 
2314 
2315 void KDirLister::clearMimeFilter()
2316 {
2317  d->prepareForSettingsChange();
2318  d->settings.mimeFilter.clear();
2319  d->settings.mimeExcludeFilter.clear();
2320 }
2321 
2322 QStringList KDirLister::mimeFilters() const
2323 {
2324  return d->settings.mimeFilter;
2325 }
2326 
2327 bool KDirLister::matchesFilter( const QString& name ) const
2328 {
2329  return doNameFilter(name, d->settings.lstFilters);
2330 }
2331 
2332 bool KDirLister::matchesMimeFilter( const QString& mime ) const
2333 {
2334  return doMimeFilter(mime, d->settings.mimeFilter) &&
2335  d->doMimeExcludeFilter(mime, d->settings.mimeExcludeFilter);
2336 }
2337 
2338 // ================ protected methods ================ //
2339 
2340 bool KDirLister::matchesFilter( const KFileItem& item ) const
2341 {
2342  Q_ASSERT( !item.isNull() );
2343 
2344  if ( item.text() == ".." )
2345  return false;
2346 
2347  if ( !d->settings.isShowingDotFiles && item.isHidden() )
2348  return false;
2349 
2350  if ( item.isDir() || d->settings.lstFilters.isEmpty() )
2351  return true;
2352 
2353  return matchesFilter( item.text() );
2354 }
2355 
2356 bool KDirLister::matchesMimeFilter( const KFileItem& item ) const
2357 {
2358  Q_ASSERT(!item.isNull());
2359  // Don't lose time determining the mimetype if there is no filter
2360  if (d->settings.mimeFilter.isEmpty() && d->settings.mimeExcludeFilter.isEmpty())
2361  return true;
2362  return matchesMimeFilter(item.mimetype());
2363 }
2364 
2365 bool KDirLister::doNameFilter( const QString& name, const QList<QRegExp>& filters ) const
2366 {
2367  for ( QList<QRegExp>::const_iterator it = filters.begin(); it != filters.end(); ++it )
2368  if ( (*it).exactMatch( name ) )
2369  return true;
2370 
2371  return false;
2372 }
2373 
2374 bool KDirLister::doMimeFilter( const QString& mime, const QStringList& filters ) const
2375 {
2376  if ( filters.isEmpty() )
2377  return true;
2378 
2379  const KMimeType::Ptr mimeptr = KMimeType::mimeType(mime);
2380  if ( !mimeptr )
2381  return false;
2382 
2383  //kDebug(7004) << "doMimeFilter: investigating: "<<mimeptr->name();
2384  QStringList::const_iterator it = filters.begin();
2385  for ( ; it != filters.end(); ++it )
2386  if ( mimeptr->is(*it) )
2387  return true;
2388  //else kDebug(7004) << "doMimeFilter: compared without result to "<<*it;
2389 
2390  return false;
2391 }
2392 
2393 bool KDirLister::Private::doMimeExcludeFilter( const QString& mime, const QStringList& filters ) const
2394 {
2395  if ( filters.isEmpty() )
2396  return true;
2397 
2398  QStringList::const_iterator it = filters.begin();
2399  for ( ; it != filters.end(); ++it )
2400  if ( (*it) == mime )
2401  return false;
2402 
2403  return true;
2404 }
2405 
2406 void KDirLister::handleError( KIO::Job *job )
2407 {
2408  if ( d->autoErrorHandling )
2409  job->uiDelegate()->showErrorMessage();
2410 }
2411 
2412 
2413 // ================= private methods ================= //
2414 
2415 void KDirLister::Private::addNewItem(const KUrl& directoryUrl, const KFileItem &item)
2416 {
2417  if (!isItemVisible(item))
2418  return; // No reason to continue... bailing out here prevents a mimetype scan.
2419 
2420  //kDebug(7004) << "in" << directoryUrl << "item:" << item.url();
2421 
2422  if ( m_parent->matchesMimeFilter( item ) )
2423  {
2424  if ( !lstNewItems )
2425  {
2426  lstNewItems = new NewItemsHash;
2427  }
2428 
2429  Q_ASSERT( !item.isNull() );
2430  (*lstNewItems)[directoryUrl].append( item ); // items not filtered
2431  }
2432  else
2433  {
2434  if ( !lstMimeFilteredItems ) {
2435  lstMimeFilteredItems = new KFileItemList;
2436  }
2437 
2438  Q_ASSERT( !item.isNull() );
2439  lstMimeFilteredItems->append( item ); // only filtered by mime
2440  }
2441 }
2442 
2443 void KDirLister::Private::addNewItems(const KUrl& directoryUrl, const KFileItemList& items)
2444 {
2445  // TODO: make this faster - test if we have a filter at all first
2446  // DF: was this profiled? The matchesFoo() functions should be fast, w/o filters...
2447  // Of course if there is no filter and we can do a range-insertion instead of a loop, that might be good.
2448  KFileItemList::const_iterator kit = items.begin();
2449  const KFileItemList::const_iterator kend = items.end();
2450  for ( ; kit != kend; ++kit )
2451  addNewItem(directoryUrl, *kit);
2452 }
2453 
2454 void KDirLister::Private::addRefreshItem(const KUrl& directoryUrl, const KFileItem& oldItem, const KFileItem& item)
2455 {
2456  const bool refreshItemWasFiltered = !isItemVisible(oldItem) ||
2457  !m_parent->matchesMimeFilter(oldItem);
2458  if (isItemVisible(item) && m_parent->matchesMimeFilter(item)) {
2459  if ( refreshItemWasFiltered )
2460  {
2461  if ( !lstNewItems ) {
2462  lstNewItems = new NewItemsHash;
2463  }
2464 
2465  Q_ASSERT( !item.isNull() );
2466  (*lstNewItems)[directoryUrl].append( item );
2467  }
2468  else
2469  {
2470  if ( !lstRefreshItems ) {
2471  lstRefreshItems = new QList<QPair<KFileItem,KFileItem> >;
2472  }
2473 
2474  Q_ASSERT( !item.isNull() );
2475  lstRefreshItems->append( qMakePair(oldItem, item) );
2476  }
2477  }
2478  else if ( !refreshItemWasFiltered )
2479  {
2480  if ( !lstRemoveItems ) {
2481  lstRemoveItems = new KFileItemList;
2482  }
2483 
2484  // notify the user that the mimetype of a file changed that doesn't match
2485  // a filter or does match an exclude filter
2486  // This also happens when renaming foo to .foo and dot files are hidden (#174721)
2487  Q_ASSERT(!oldItem.isNull());
2488  lstRemoveItems->append(oldItem);
2489  }
2490 }
2491 
2492 void KDirLister::Private::emitItems()
2493 {
2494  NewItemsHash *tmpNew = lstNewItems;
2495  lstNewItems = 0;
2496 
2497  KFileItemList *tmpMime = lstMimeFilteredItems;
2498  lstMimeFilteredItems = 0;
2499 
2500  QList<QPair<KFileItem, KFileItem> > *tmpRefresh = lstRefreshItems;
2501  lstRefreshItems = 0;
2502 
2503  KFileItemList *tmpRemove = lstRemoveItems;
2504  lstRemoveItems = 0;
2505 
2506  if (tmpNew) {
2507  QHashIterator<KUrl, KFileItemList> it(*tmpNew);
2508  while (it.hasNext()) {
2509  it.next();
2510  emit m_parent->itemsAdded(it.key(), it.value());
2511  emit m_parent->newItems(it.value()); // compat
2512  }
2513  delete tmpNew;
2514  }
2515 
2516  if ( tmpMime )
2517  {
2518  emit m_parent->itemsFilteredByMime( *tmpMime );
2519  delete tmpMime;
2520  }
2521 
2522  if ( tmpRefresh )
2523  {
2524  emit m_parent->refreshItems( *tmpRefresh );
2525  delete tmpRefresh;
2526  }
2527 
2528  if ( tmpRemove )
2529  {
2530  emit m_parent->itemsDeleted( *tmpRemove );
2531  delete tmpRemove;
2532  }
2533 }
2534 
2535 bool KDirLister::Private::isItemVisible(const KFileItem& item) const
2536 {
2537  // Note that this doesn't include mime filters, because
2538  // of the itemsFilteredByMime signal. Filtered-by-mime items are
2539  // considered "visible", they are just visible via a different signal...
2540  return (!settings.dirOnlyMode || item.isDir())
2541  && m_parent->matchesFilter(item);
2542 }
2543 
2544 void KDirLister::Private::emitItemsDeleted(const KFileItemList &_items)
2545 {
2546  KFileItemList items = _items;
2547  QMutableListIterator<KFileItem> it(items);
2548  while (it.hasNext()) {
2549  const KFileItem& item = it.next();
2550  if (isItemVisible(item) && m_parent->matchesMimeFilter(item)) {
2551  // for compat
2552  emit m_parent->deleteItem(item);
2553  } else {
2554  it.remove();
2555  }
2556  }
2557  if (!items.isEmpty())
2558  emit m_parent->itemsDeleted(items);
2559 }
2560 
2561 // ================ private slots ================ //
2562 
2563 void KDirLister::Private::_k_slotInfoMessage( KJob *, const QString& message )
2564 {
2565  emit m_parent->infoMessage( message );
2566 }
2567 
2568 void KDirLister::Private::_k_slotPercent( KJob *job, unsigned long pcnt )
2569 {
2570  jobData[static_cast<KIO::ListJob *>(job)].percent = pcnt;
2571 
2572  int result = 0;
2573 
2574  KIO::filesize_t size = 0;
2575 
2576  QMap< KIO::ListJob *, Private::JobData >::Iterator dataIt = jobData.begin();
2577  while ( dataIt != jobData.end() )
2578  {
2579  result += (*dataIt).percent * (*dataIt).totalSize;
2580  size += (*dataIt).totalSize;
2581  ++dataIt;
2582  }
2583 
2584  if ( size != 0 )
2585  result /= size;
2586  else
2587  result = 100;
2588  emit m_parent->percent( result );
2589 }
2590 
2591 void KDirLister::Private::_k_slotTotalSize( KJob *job, qulonglong size )
2592 {
2593  jobData[static_cast<KIO::ListJob *>(job)].totalSize = size;
2594 
2595  KIO::filesize_t result = 0;
2596  QMap< KIO::ListJob *, Private::JobData >::Iterator dataIt = jobData.begin();
2597  while ( dataIt != jobData.end() )
2598  {
2599  result += (*dataIt).totalSize;
2600  ++dataIt;
2601  }
2602 
2603  emit m_parent->totalSize( result );
2604 }
2605 
2606 void KDirLister::Private::_k_slotProcessedSize( KJob *job, qulonglong size )
2607 {
2608  jobData[static_cast<KIO::ListJob *>(job)].processedSize = size;
2609 
2610  KIO::filesize_t result = 0;
2611  QMap< KIO::ListJob *, Private::JobData >::Iterator dataIt = jobData.begin();
2612  while ( dataIt != jobData.end() )
2613  {
2614  result += (*dataIt).processedSize;
2615  ++dataIt;
2616  }
2617 
2618  emit m_parent->processedSize( result );
2619 }
2620 
2621 void KDirLister::Private::_k_slotSpeed( KJob *job, unsigned long spd )
2622 {
2623  jobData[static_cast<KIO::ListJob *>(job)].speed = spd;
2624 
2625  int result = 0;
2626  QMap< KIO::ListJob *, Private::JobData >::Iterator dataIt = jobData.begin();
2627  while ( dataIt != jobData.end() )
2628  {
2629  result += (*dataIt).speed;
2630  ++dataIt;
2631  }
2632 
2633  emit m_parent->speed( result );
2634 }
2635 
2636 uint KDirLister::Private::numJobs()
2637 {
2638 #ifdef DEBUG_CACHE
2639  // This code helps detecting stale entries in the jobData map.
2640  qDebug() << m_parent << "numJobs:" << jobData.count();
2641  QMapIterator<KIO::ListJob *, JobData> it(jobData);
2642  while (it.hasNext()) {
2643  it.next();
2644  qDebug() << (void*)it.key();
2645  qDebug() << it.key();
2646  }
2647 #endif
2648 
2649  return jobData.count();
2650 }
2651 
2652 void KDirLister::Private::jobDone( KIO::ListJob *job )
2653 {
2654  jobData.remove( job );
2655 }
2656 
2657 void KDirLister::Private::jobStarted( KIO::ListJob *job )
2658 {
2659  Private::JobData data;
2660  data.speed = 0;
2661  data.percent = 0;
2662  data.processedSize = 0;
2663  data.totalSize = 0;
2664 
2665  jobData.insert( job, data );
2666  complete = false;
2667 }
2668 
2669 void KDirLister::Private::connectJob( KIO::ListJob *job )
2670 {
2671  m_parent->connect( job, SIGNAL(infoMessage(KJob*,QString,QString)),
2672  m_parent, SLOT(_k_slotInfoMessage(KJob*,QString)) );
2673  m_parent->connect( job, SIGNAL(percent(KJob*,ulong)),
2674  m_parent, SLOT(_k_slotPercent(KJob*,ulong)) );
2675  m_parent->connect( job, SIGNAL(totalSize(KJob*,qulonglong)),
2676  m_parent, SLOT(_k_slotTotalSize(KJob*,qulonglong)) );
2677  m_parent->connect( job, SIGNAL(processedSize(KJob*,qulonglong)),
2678  m_parent, SLOT(_k_slotProcessedSize(KJob*,qulonglong)) );
2679  m_parent->connect( job, SIGNAL(speed(KJob*,ulong)),
2680  m_parent, SLOT(_k_slotSpeed(KJob*,ulong)) );
2681 }
2682 
2683 void KDirLister::setMainWindow( QWidget *window )
2684 {
2685  d->window = window;
2686 }
2687 
2688 QWidget *KDirLister::mainWindow()
2689 {
2690  return d->window;
2691 }
2692 
2693 KFileItemList KDirLister::items( WhichItems which ) const
2694 {
2695  return itemsForDir( url(), which );
2696 }
2697 
2698 KFileItemList KDirLister::itemsForDir( const KUrl& dir, WhichItems which ) const
2699 {
2700  KFileItemList *allItems = kDirListerCache->itemsForDir( dir );
2701  if ( !allItems )
2702  return KFileItemList();
2703 
2704  if ( which == AllItems )
2705  return *allItems;
2706  else // only items passing the filters
2707  {
2708  KFileItemList result;
2709  KFileItemList::const_iterator kit = allItems->constBegin();
2710  const KFileItemList::const_iterator kend = allItems->constEnd();
2711  for ( ; kit != kend; ++kit )
2712  {
2713  const KFileItem& item = *kit;
2714  if (d->isItemVisible(item) && matchesMimeFilter(item)) {
2715  result.append(item);
2716  }
2717  }
2718  return result;
2719  }
2720 }
2721 
2722 bool KDirLister::delayedMimeTypes() const
2723 {
2724  return d->delayedMimeTypes;
2725 }
2726 
2727 void KDirLister::setDelayedMimeTypes( bool delayedMimeTypes )
2728 {
2729  d->delayedMimeTypes = delayedMimeTypes;
2730 }
2731 
2732 // called by KDirListerCache::slotRedirection
2733 void KDirLister::Private::redirect(const KUrl& oldUrl, const KUrl& newUrl, bool keepItems)
2734 {
2735  if ( url.equals( oldUrl, KUrl::CompareWithoutTrailingSlash ) ) {
2736  if (!keepItems) {
2737  rootFileItem = KFileItem();
2738  } else {
2739  rootFileItem.setUrl(newUrl);
2740  }
2741  url = newUrl;
2742  }
2743 
2744  const int idx = lstDirs.indexOf( oldUrl );
2745  if (idx == -1) {
2746  kWarning(7004) << "Unexpected redirection from" << oldUrl << "to" << newUrl
2747  << "but this dirlister is currently listing/holding" << lstDirs;
2748  } else {
2749  lstDirs[ idx ] = newUrl;
2750  }
2751 
2752  if ( lstDirs.count() == 1 ) {
2753  if (!keepItems)
2754  emit m_parent->clear();
2755  emit m_parent->redirection( newUrl );
2756  } else {
2757  if (!keepItems)
2758  emit m_parent->clear( oldUrl );
2759  }
2760  emit m_parent->redirection( oldUrl, newUrl );
2761 }
2762 
2763 void KDirListerCacheDirectoryData::moveListersWithoutCachedItemsJob(const KUrl& url)
2764 {
2765  // Move dirlisters from listersCurrentlyListing to listersCurrentlyHolding,
2766  // but not those that are still waiting on a CachedItemsJob...
2767  // Unit-testing note:
2768  // Run kdirmodeltest in valgrind to hit the case where an update
2769  // is triggered while a lister has a CachedItemsJob (different timing...)
2770  QMutableListIterator<KDirLister *> lister_it(listersCurrentlyListing);
2771  while (lister_it.hasNext()) {
2772  KDirLister* kdl = lister_it.next();
2773  if (!kdl->d->cachedItemsJobForUrl(url)) {
2774  // OK, move this lister from "currently listing" to "currently holding".
2775 
2776  // Huh? The KDirLister was present twice in listersCurrentlyListing, or was in both lists?
2777  Q_ASSERT(!listersCurrentlyHolding.contains(kdl));
2778  if (!listersCurrentlyHolding.contains(kdl)) {
2779  listersCurrentlyHolding.append(kdl);
2780  }
2781  lister_it.remove();
2782  } else {
2783  //kDebug(7004) << "Not moving" << kdl << "to listersCurrentlyHolding because it still has job" << kdl->d->m_cachedItemsJobs;
2784  }
2785  }
2786 }
2787 
2788 KFileItem KDirLister::cachedItemForUrl(const KUrl& url)
2789 {
2790  return kDirListerCache->itemForUrl(url);
2791 }
2792 
2793 #include "kdirlister.moc"
2794 #include "kdirlister_p.moc"
KIO::JobUiDelegate::setWindow
virtual void setWindow(QWidget *window)
Associate this job with a window given by window.
Definition: jobuidelegate.cpp:58
KIO::UDSEntry::UDS_URL
An alternative URL (If different from the caption).
Definition: udsentry.h:190
KDirLister::Private::CachedItemsJob::doKill
virtual bool doKill()
Definition: kdirlister.cpp:313
kdirlister.h
KDirListerCache::slotFilesRemoved
void slotFilesRemoved(const QStringList &fileList)
Notify that files have been deleted.
Definition: kdirlister.cpp:872
KDirLister::Private::JobData::speed
long unsigned int speed
Definition: kdirlister_p.h:135
i18n
QString i18n(const char *text)
KCompositeJob::kill
bool kill(KillVerbosity verbosity=Quietly)
QList::clear
void clear()
KDirLister::percent
void percent(int percent)
Progress signal showing the overall progress of the KDirLister.
KDirLister::~KDirLister
virtual ~KDirLister()
Destroy the directory lister.
Definition: kdirlister.cpp:2075
KDirLister::Private::rootFileItem
KFileItem rootFileItem
Definition: kdirlister_p.h:142
KSharedPtr
Definition: kprotocolmanager.h:31
KUrl::adjustPath
void adjustPath(AdjustPathOption trailing)
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
KDirLister::deleteItem
QT_MOC_COMPAT void deleteItem(const KFileItem &_fileItem)
Signals that an item has been deleted.
QWidget
KDirListerCacheDirectoryData
Definition: kdirlister_p.h:448
QUrl::clear
void clear()
KIO::filesize_t
qulonglong filesize_t
64-bit file size
Definition: global.h:57
QHash::insert
iterator insert(const Key &key, const T &value)
KUrl::directory
QString directory(const DirectoryOptions &options=IgnoreTrailingSlash) const
KUrl::RemoveTrailingSlash
KFileItem::determineMimeType
KMimeType::Ptr determineMimeType() const
Returns the mimetype of the file item.
Definition: kfileitem.cpp:779
KDirWatch::self
static KDirWatch * self()
QList::remove
iterator remove(iterator pos)
KDirLister::setShowingDotFiles
virtual void setShowingDotFiles(bool _showDotFiles)
Changes the "is viewing dot files" setting.
Definition: kdirlister.cpp:2128
QMutableSetIterator
KDirLister::findByName
virtual KFileItem findByName(const QString &name) const
Find an item by its name.
Definition: kdirlister.cpp:2265
KDirLister::Private::autoErrorHandling
bool autoErrorHandling
Definition: kdirlister_p.h:131
KFileItem::isDir
bool isDir() const
Returns true if this item represents a directory.
Definition: kfileitem.cpp:1141
KDirLister::matchesMimeFilter
bool matchesMimeFilter(const QString &mime) const
Checks whether mime matches a filter in the list of mime types.
Definition: kdirlister.cpp:2332
kdebug.h
QHash::key
const Key key(const T &value) const
KDirLister::Private::_k_slotInfoMessage
void _k_slotInfoMessage(KJob *, const QString &)
Definition: kdirlister.cpp:2563
KUrl::AddTrailingSlash
KDirLister::Private::m_parent
KDirLister * m_parent
Definition: kdirlister_p.h:112
KDirLister::setAutoUpdate
virtual void setAutoUpdate(bool enable)
Enable/disable automatic directory updating, when a directory changes (using KDirWatch).
Definition: kdirlister.cpp:2114
KDirLister::directories
KUrl::List directories() const
Returns all URLs that are listed by this KDirLister.
Definition: kdirlister.cpp:2167
QHashIterator::key
const Key & key() const
KFileItem::mimetype
QString mimetype() const
Returns the mimetype of the file item.
Definition: kfileitem.cpp:770
kmountpoint.h
KDirLister::Private::oldSettings
FilterSettings oldSettings
Definition: kdirlister_p.h:163
QString::split
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
timeout
int timeout
KIO::ListJob
A ListJob is allows you to get the get the content of a directory.
Definition: jobclasses.h:936
KFileItem::isNull
bool isNull() const
Return true if default-constructed.
Definition: kfileitem.cpp:1714
QHashIterator::hasNext
bool hasNext() const
KDirLister::delayedMimeTypes
bool delayedMimeTypes() const
KDirLister::Private::emitChanges
void emitChanges()
Definition: kdirlister.cpp:2177
K_GLOBAL_STATIC
#define K_GLOBAL_STATIC(TYPE, NAME)
KDirLister::handleError
virtual void handleError(KIO::Job *)
Reimplement to customize error handling.
Definition: kdirlister.cpp:2406
KIO::HideProgressInfo
Hide progress information dialog, i.e.
Definition: jobclasses.h:51
QMap::constBegin
const_iterator constBegin() const
QMap
Definition: netaccess.h:36
KDirLister::Keep
Previous directories aren't forgotten (they are still watched by kdirwatch and their items are kept f...
Definition: kdirlister.h:76
KFileItem::refresh
void refresh()
Throw away and re-read (for local files) all information about the file.
Definition: kfileitem.cpp:512
QUrl::host
QString host() const
KUrl::cleanPath
void cleanPath(const CleanPathOption &options=SimplifyDirSeparators)
dirs
KStandardDirs * dirs()
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const
KDE::stat
int stat(const QString &path, KDE_struct_stat *buf)
KDirLister::openUrl
virtual bool openUrl(const KUrl &_url, OpenUrlFlags _flags=NoFlags)
Run the directory lister on the given url.
Definition: kdirlister.cpp:2088
KIO::SimpleJob::url
const KUrl & url() const
Returns the SimpleJob's URL.
Definition: job.cpp:341
kError
static QDebug kError(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
KDirLister::clearMimeFilter
virtual void clearMimeFilter()
Clears the mime based filter.
Definition: kdirlister.cpp:2315
KDirLister::findByUrl
virtual KFileItem findByUrl(const KUrl &_url) const
Find an item by its URL.
Definition: kdirlister.cpp:2255
KFileItem::setUrl
void setUrl(const KUrl &url)
Sets the item's URL.
Definition: kfileitem.cpp:543
KDirLister::Private::CachedItemsJob::done
void done()
Definition: kdirlister.cpp:305
QList::erase
iterator erase(iterator pos)
KDirLister::cachedItemForUrl
static KFileItem cachedItemForUrl(const KUrl &url)
Return the KFileItem for the given URL, if we listed it recently and it's still in the cache - which ...
Definition: kdirlister.cpp:2788
QDBusConnection::sessionBus
QDBusConnection sessionBus()
KUrl::toLocalFile
QString toLocalFile(AdjustPathOption trailing=LeaveTrailingSlash) const
KFileItem::setName
void setName(const QString &name)
Sets the item's name (i.e.
Definition: kfileitem.cpp:554
KDirLister::Private::nameFilter
QString nameFilter
Definition: kdirlister_p.h:152
KDirLister::Private::FilterSettings::mimeExcludeFilter
QStringList mimeExcludeFilter
Definition: kdirlister_p.h:160
KDirListerCacheDirectoryData::listersCurrentlyListing
QList< KDirLister * > listersCurrentlyListing
Definition: kdirlister_p.h:457
KDirLister::rootItem
KFileItem rootItem() const
Returns the file item of the URL.
Definition: kdirlister.cpp:2250
KDirListerCache::forgetCachedItemsJob
void forgetCachedItemsJob(KDirLister::Private::CachedItemsJob *job, KDirLister *lister, const KUrl &url)
Definition: kdirlister.cpp:367
KUrl::CompareWithoutTrailingSlash
KDirLister::setMainWindow
void setMainWindow(QWidget *window)
Pass the main window this object is associated with this is used for caching authentication data...
Definition: kdirlister.cpp:2683
QSet::insert
const_iterator insert(const T &value)
QString::remove
QString & remove(int position, int n)
KDirLister::nameFilter
QString nameFilter() const
Returns the current name filter, as set via setNameFilter()
KDirLister::dirOnlyMode
bool dirOnlyMode() const
Checks whether the KDirLister only lists directories or all files.
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
KDirListerCache::stopListingUrl
void stopListingUrl(KDirLister *lister, const KUrl &_url, bool silent=false)
Definition: kdirlister.cpp:439
KUrl::isParentOf
bool isParentOf(const KUrl &u) const
KDirLister::Private::addNewItem
void addNewItem(const KUrl &directoryUrl, const KFileItem &item)
Definition: kdirlister.cpp:2415
klocale.h
manually_mounted
static bool manually_mounted(const QString &path, const KMountPoint::List &possibleMountPoints)
Definition: kdirlister.cpp:532
QObject::disconnect
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QUrl::isEmpty
bool isEmpty() const
KDirLister::Private::_k_slotPercent
void _k_slotPercent(KJob *, unsigned long)
Definition: kdirlister.cpp:2568
KMountPoint::NeedMountOptions
QList::const_iterator
KDirLister::Private::connectJob
void connectJob(KIO::ListJob *)
Definition: kdirlister.cpp:2669
KDirListerCache::slotFileRenamed
void slotFileRenamed(const QString &srcUrl, const QString &dstUrl)
Definition: kdirlister.cpp:966
KUrl
KDirLister::Private::complete
bool complete
Definition: kdirlister_p.h:123
QCache::take
T * take(const Key &key)
KDirLister::totalSize
void totalSize(KIO::filesize_t size)
Emitted when we know the size of the jobs.
kprotocolmanager.h
KUrl::setPath
void setPath(const QString &path)
KDirListerCache::itemForUrl
KFileItem itemForUrl(const KUrl &url) const
Definition: kdirlister.cpp:782
KDirLister::Private::autoUpdate
bool autoUpdate
Definition: kdirlister_p.h:125
KIO::Job::ui
JobUiDelegate * ui() const
Retrieves the UI delegate of this job.
Definition: job.cpp:90
QList::value
T value(int i) const
KDirLister::Private::FilterSettings::dirOnlyMode
bool dirOnlyMode
Definition: kdirlister_p.h:157
QString::clear
void clear()
KIO::listDir
ListJob * listDir(const KUrl &url, JobFlags flags=DefaultFlags, bool includeHidden=true)
List the contents of url, which is assumed to be a directory.
Definition: job.cpp:2735
KFileItem::isLocalFile
bool isLocalFile() const
Returns true if the file is a local file.
Definition: kfileitem.cpp:1575
KDirLister::autoErrorHandlingEnabled
bool autoErrorHandlingEnabled() const
Check whether auto error handling is enabled.
KDirListerCache::stop
void stop(KDirLister *lister, bool silent=false)
Definition: kdirlister.cpp:414
KDirListerCacheDirectoryData::listersCurrentlyHolding
QList< KDirLister * > listersCurrentlyHolding
Definition: kdirlister_p.h:459
KDirLister::Private::isItemVisible
bool isItemVisible(const KFileItem &item) const
Should this item be visible according to the current filter settings?
Definition: kdirlister.cpp:2535
QLinkedList
QRegExp
KDirLister::stop
virtual void stop()
Stop listing all directories currently being listed.
Definition: kdirlister.cpp:2099
QObject::name
const char * name() const
KDirLister::Private::FilterSettings::isShowingDotFiles
bool isShowingDotFiles
Definition: kdirlister_p.h:156
KDirLister::setNameFilter
virtual void setNameFilter(const QString &filter)
Set a name filter to only list items matching this name, e.g.
Definition: kdirlister.cpp:2273
KDirListerCache::updateDirectory
void updateDirectory(const KUrl &dir)
Definition: kdirlister.cpp:652
QFileInfo::canonicalFilePath
QString canonicalFilePath() const
KUrl::addPath
void addPath(const QString &txt)
QString::number
QString number(int n, int base)
KDirLister::Private::_k_slotProcessedSize
void _k_slotProcessedSize(KJob *, qulonglong)
Definition: kdirlister.cpp:2606
QList::count
int count(const T &value) const
KDirLister::doNameFilter
virtual bool doNameFilter(const QString &name, const QList< QRegExp > &filters) const
Called by the public matchesFilter() to do the actual filtering.
Definition: kdirlister.cpp:2365
QList::append
void append(const T &value)
KDirLister::Reload
Indicates whether to use the cache or to reread the directory from the disk.
Definition: kdirlister.h:81
QMapIterator
KDirLister::Private::JobData::percent
long unsigned int percent
Definition: kdirlister_p.h:135
QString::insert
QString & insert(int position, QChar ch)
QObject::property
QVariant property(const char *name) const
QHash::constEnd
const_iterator constEnd() const
KDirLister::setDirOnlyMode
virtual void setDirOnlyMode(bool dirsOnly)
Call this to list only directories.
Definition: kdirlister.cpp:2142
KDirLister::Private::prepareForSettingsChange
void prepareForSettingsChange()
Definition: kdirlister_p.h:99
QHash
KDirLister::Private::hasPendingChanges
bool hasPendingChanges
Definition: kdirlister_p.h:129
KDirLister::Private::FilterSettings
Definition: kdirlister_p.h:154
KDirLister::Private::CachedItemsJob::start
void start()
Definition: kdirlister_p.h:475
KDirLister::Private::window
QWidget * window
Definition: kdirlister_p.h:149
QMapIterator::next
Item next()
QObject
KDirLister::Private::emitItemsDeleted
void emitItemsDeleted(const KFileItemList &items)
Definition: kdirlister.cpp:2544
KDirLister::Private::CachedItemsJob::CachedItemsJob
CachedItemsJob(KDirLister *lister, const KUrl &url, bool reload)
Definition: kdirlister.cpp:290
KDirLister::items
KFileItemList items(WhichItems which=FilteredItems) const
Returns the items listed for the current url().
Definition: kdirlister.cpp:2693
KDirLister::speed
void speed(int bytes_per_second)
Emitted to display information about the speed of the jobs.
KUrl::protocol
QString protocol() const
KDirLister::mainWindow
QWidget * mainWindow()
Returns the main window associated with this object.
Definition: kdirlister.cpp:2688
KDirLister::Private::errorParent
QWidget * errorParent
Definition: kdirlister_p.h:132
QList::isEmpty
bool isEmpty() const
kdirlister_p.h
KDirLister::infoMessage
void infoMessage(const QString &msg)
Emitted to display information about running jobs.
QHashIterator
KFileItem::localPath
QString localPath() const
Returns the local path if isLocalFile() == true or the KIO item has a UDS_LOCAL_PATH atom...
Definition: kfileitem.cpp:602
QString::isEmpty
bool isEmpty() const
KDirLister::setAutoErrorHandlingEnabled
void setAutoErrorHandlingEnabled(bool enable, QWidget *parent)
Enable or disable auto error handling is enabled.
Definition: kdirlister.cpp:2156
QList::removeAll
int removeAll(const T &value)
QMap::constEnd
const_iterator constEnd() const
KDirLister::started
void started(const KUrl &_url)
Tell the view that we started to list _url.
QHash::begin
iterator begin()
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
KFileItemList
List of KFileItems, which adds a few helper methods to QList.
Definition: kfileitem.h:674
QMap::const_iterator
KIO::UDSEntry::stringValue
QString stringValue(uint field) const
Definition: udsentry.cpp:73
KDirLister::isFinished
bool isFinished() const
Returns true if no io operation is currently in progress.
Definition: kdirlister.cpp:2245
KDirLister::canceled
void canceled()
Tell the view that the user canceled the listing.
KDirLister::completed
void completed()
Tell the view that listing is finished.
KDirListerCache::slotFilesChanged
void slotFilesChanged(const QStringList &fileList)
Notify that files have been changed.
Definition: kdirlister.cpp:932
KDirLister::Private::FilterSettings::lstFilters
QList< QRegExp > lstFilters
Definition: kdirlister_p.h:158
QSet
KDirLister::setDelayedMimeTypes
void setDelayedMimeTypes(bool delayedMimeTypes)
Delayed mimetypes feature: If enabled, mime types will be fetched on demand, which leads to a faster ...
Definition: kdirlister.cpp:2727
QMutableListIterator::remove
void remove()
KDirLister::setMimeFilter
virtual void setMimeFilter(const QStringList &mimeList)
Set mime-based filter to only list items matching the given mimetypes.
Definition: kdirlister.cpp:2293
KDirLister::Private::jobStarted
void jobStarted(KIO::ListJob *)
Definition: kdirlister.cpp:2657
QString
QList< UDSEntry >
QMap::end
iterator end()
KProtocolInfo::protocolClass
static QString protocolClass(const QString &protocol)
QMapIterator::key
const Key & key() const
KUrl::path
QString path(AdjustPathOption trailing=LeaveTrailingSlash) const
KDirLister::Private::FilterSettings::mimeFilter
QStringList mimeFilter
Definition: kdirlister_p.h:159
KDirLister::Private::jobDone
void jobDone(KIO::ListJob *)
Definition: kdirlister.cpp:2652
KDirLister::AllItems
Definition: kdirlister.h:393
KCompositeJob::uiDelegate
KJobUiDelegate * uiDelegate() const
KFileItem::refreshMimeType
void refreshMimeType()
Re-reads mimetype information.
Definition: kfileitem.cpp:533
QMap::begin
iterator begin()
QStringList
KDirListerCache::listDir
bool listDir(KDirLister *lister, const KUrl &_url, bool _keep, bool _reload)
Definition: kdirlister.cpp:91
KDirLister::showingDotFiles
bool showingDotFiles() const
Checks whether hidden files (files beginning with a dot) will be shown.
KDirLister::Private::m_cachedItemsJobs
QList< CachedItemsJob * > m_cachedItemsJobs
Definition: kdirlister_p.h:150
KDirListerCache::findByUrl
KFileItem * findByUrl(const KDirLister *lister, const KUrl &url) const
Definition: kdirlister.cpp:823
KDirLister::Private
Definition: kdirlister_p.h:42
KFileItem::text
QString text() const
Returns the text of the file item.
Definition: kfileitem.cpp:1583
jobuidelegate.h
QFileInfo
QHash::clear
void clear()
QList::end
iterator end()
QHash::value
const T value(const Key &key) const
KDirLister::url
KUrl url() const
Returns the top level URL that is listed by this KDirLister.
Definition: kdirlister.cpp:2162
QHash::find
iterator find(const Key &key)
QHashIterator::next
Item next()
KDirLister::Private::JobData::totalSize
KIO::filesize_t totalSize
Definition: kdirlister_p.h:136
QMutableListIterator::hasNext
bool hasNext() const
QList::contains
bool contains(const T &value) const
KDirLister::clear
void clear()
Signal to clear all items.
KDirLister::WhichItems
WhichItems
Used by items() and itemsForDir() to specify whether you want all items for a directory or just the f...
Definition: kdirlister.h:391
KDirLister::Private::cachedItemsJobForUrl
CachedItemsJob * cachedItemsJobForUrl(const KUrl &url) const
Definition: kdirlister.cpp:281
KDirLister::doMimeFilter
virtual bool doMimeFilter(const QString &mime, const QStringList &filters) const
Called by the public matchesMimeFilter() to do the actual filtering.
Definition: kdirlister.cpp:2374
KDirLister::Private::emitItems
void emitItems()
Definition: kdirlister.cpp:2492
KDirListerCache::forgetDirs
void forgetDirs(KDirLister *lister)
Definition: kdirlister.cpp:513
QMap::key
const Key key(const T &value) const
KDirLister::processedSize
void processedSize(KIO::filesize_t size)
Regularly emitted to show the progress of this KDirLister.
job.h
KDirLister::Private::delayedMimeTypes
bool delayedMimeTypes
Definition: kdirlister_p.h:127
KUrl::List
KDirLister::Private::redirect
void redirect(const KUrl &oldUrl, const KUrl &newUrl, bool keepItems)
Redirect this dirlister from oldUrl to newUrl.
Definition: kdirlister.cpp:2733
KDirListerCache
Design of the cache: There is a single KDirListerCache for the whole process.
Definition: kdirlister_p.h:181
QHash::constBegin
const_iterator constBegin() const
KDirListerCache::setAutoUpdate
void setAutoUpdate(KDirLister *lister, bool enable)
Definition: kdirlister.cpp:498
KDirListerCache::findByName
KFileItem findByName(const KDirLister *lister, const QString &_name) const
Definition: kdirlister.cpp:807
QUrl::isValid
bool isValid() const
QString::mid
QString mid(int position, int n) const
OrgKdeKDirNotifyInterface
Proxy class for interface org.kde.KDirNotify.
Definition: kdirnotify.h:47
KRecentDirs::dir
QString dir(const QString &fileClass)
Returns the most recently used directory accociated with this file-class.
Definition: krecentdirs.cpp:68
KJobUiDelegate::showErrorMessage
virtual void showErrorMessage()
QLatin1String
KDirLister::Private::CachedItemsJob::setEmitCompleted
void setEmitCompleted(bool b)
Definition: kdirlister_p.h:478
KDirLister::redirection
void redirection(const KUrl &_url)
Signal a redirection.
KMountPoint::possibleMountPoints
static List possibleMountPoints(DetailsNeededFlags infoNeeded=BasicInfoNeeded)
KDirLister::autoUpdate
bool autoUpdate() const
Checks whether KDirWatch will automatically update directories.
QMutableListIterator::next
T & next()
QMutableListIterator
KDirLister::Private::lstDirs
KUrl::List lstDirs
List of dirs handled by this dirlister.
Definition: kdirlister_p.h:118
KMountPoint::List::findByPath
Ptr findByPath(const QString &path) const
QString::count
int count() const
KFileItem::isMarked
bool isMarked() const
Used when updating a directory.
Definition: kfileitem.cpp:1680
QSet::end
iterator end()
KFileItem::name
QString name(bool lowerCase=false) const
Return the name of the file item (without a path).
Definition: kfileitem.cpp:1591
KIO::UDSEntry::UDS_NAME
Filename - as displayed in directory listings etc.
Definition: udsentry.h:163
KDirLister::Private::_k_slotTotalSize
void _k_slotTotalSize(KJob *, qulonglong)
Definition: kdirlister.cpp:2591
KDirLister::Private::JobData::processedSize
KIO::filesize_t processedSize
Definition: kdirlister_p.h:136
KUrl::IgnoreTrailingSlash
KDirListerCache::emitItemsFromCache
void emitItemsFromCache(KDirLister::Private::CachedItemsJob *job, KDirLister *lister, const KUrl &_url, bool _reload, bool _emitCompleted)
Definition: kdirlister.cpp:325
KDirListerCache::slotFilesAdded
void slotFilesAdded(const QString &urlDirectory)
Notify that files have been added in directory The receiver will list that directory again to find th...
Definition: kdirlister.cpp:859
KIO::Job
The base class for all jobs.
Definition: jobclasses.h:94
KDirLister::Private::JobData
Definition: kdirlister_p.h:134
KFileItem::isHidden
bool isHidden() const
Checks whether the file is hidden.
Definition: kfileitem.cpp:1125
KUrl::AppendTrailingSlash
QString::length
int length() const
QVariant::toBool
bool toBool() const
kWarning
static QDebug kWarning(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
KDirLister::Private::numJobs
uint numJobs()
Definition: kdirlister.cpp:2636
KMountPoint::List
KDirLister::Private::_k_slotSpeed
void _k_slotSpeed(KJob *, unsigned long)
Definition: kdirlister.cpp:2621
QList::prepend
void prepend(const T &value)
QObject::setProperty
bool setProperty(const char *name, const QVariant &value)
QMap::insert
iterator insert(const Key &key, const T &value)
QUrl::setHost
void setHost(const QString &host)
KUrl::url
QString url(AdjustPathOption trailing=LeaveTrailingSlash) const
KDirLister::Private::doMimeExcludeFilter
bool doMimeExcludeFilter(const QString &mimeExclude, const QStringList &filters) const
Definition: kdirlister.cpp:2393
KDirWatch::exists
static bool exists()
QString::find
int find(QChar c, int i, bool cs) const
KProtocolManager::supportsListing
static bool supportsListing(const KUrl &url)
Returns whether the protocol can list files/objects.
Definition: kprotocolmanager.cpp:1032
KDirLister
Helper class for the kiojob used to list and update a directory.
Definition: kdirlister.h:57
KIO::ListJob::redirectionUrl
const KUrl & redirectionUrl() const
Returns the ListJob's redirection URL.
Definition: job.cpp:2775
QHash::end
iterator end()
QHashIterator::value
const T & value() const
KDirListerCacheDirectoryData::moveListersWithoutCachedItemsJob
void moveListersWithoutCachedItemsJob(const KUrl &url)
Definition: kdirlister.cpp:2763
QList::constEnd
const_iterator constEnd() const
QList::constBegin
const_iterator constBegin() const
KDirLister::Private::settings
FilterSettings settings
Definition: kdirlister_p.h:162
KFileItem::entry
KIO::UDSEntry entry() const
Returns the UDS entry.
Definition: kfileitem.cpp:1672
KDirLister::Private::CachedItemsJob::url
KUrl url() const
Definition: kdirlister_p.h:480
KUrl::isLocalFile
bool isLocalFile() const
kmessagebox.h
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::parent
QObject * parent() const
KDirLister::mimeFilter
QStringList mimeFilter
Definition: kdirlister.h:69
KDirLister::Private::CachedItemsJob
Definition: kdirlister_p.h:470
end
const KShortcut & end()
QSet::find
iterator find(const T &value)
KDirLister::setMimeExcludeFilter
void setMimeExcludeFilter(const QStringList &mimeList)
Filtering should be done with KFileFilter.
Definition: kdirlister.cpp:2305
KDirLister::Private::addRefreshItem
void addRefreshItem(const KUrl &directoryUrl, const KFileItem &oldItem, const KFileItem &item)
Definition: kdirlister.cpp:2454
QCache::remove
bool remove(const Key &key)
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
KDirListerCache::~KDirListerCache
~KDirListerCache()
Definition: kdirlister.cpp:75
KDirLister::mimeFilters
QStringList mimeFilters() const
Returns the list of mime based filters, as set via setMimeFilter().
Definition: kdirlister.cpp:2322
KJob
kFatal
static QDebug kFatal(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
KDirLister::itemsDeleted
void itemsDeleted(const KFileItemList &items)
Signal that items have been deleted.
QList::begin
iterator begin()
QList::find
iterator find(const T &t)
KDirListerCache::itemsForDir
KFileItemList * itemsForDir(const KUrl &dir) const
Definition: kdirlister.cpp:801
KUrl::prettyUrl
QString prettyUrl(AdjustPathOption trailing=LeaveTrailingSlash) const
KDirLister::Private::addNewItems
void addNewItems(const KUrl &directoryUrl, const KFileItemList &items)
Definition: kdirlister.cpp:2443
KFileItem::url
KUrl url() const
Returns the url of the file.
Definition: kfileitem.cpp:1543
KDirLister::emitChanges
virtual void emitChanges()
Actually emit the changes made with setShowingDotFiles, setDirOnlyMode, setNameFilter and setMimeFilt...
Definition: kdirlister.cpp:2172
KMessageBox::error
static void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
KDirLister::matchesFilter
bool matchesFilter(const QString &name) const
Checks whether name matches a filter in the list of name filters.
Definition: kdirlister.cpp:2327
KFileItem
A KFileItem is a generic class to handle a file, local or remote.
Definition: kfileitem.h:45
KDialogJobUiDelegate::window
QWidget * window() const
QMapIterator::hasNext
bool hasNext() const
QLinkedList::append
void append(const T &value)
KDirLister::KDirLister
KDirLister(QObject *parent=0)
Create a directory lister.
Definition: kdirlister.cpp:2061
KCompositeJob::error
int error() const
KRecentDirs::list
QStringList list(const QString &fileClass)
Returns a list of directories associated with this file-class.
Definition: krecentdirs.cpp:60
QCache::clear
void clear()
KDirLister::Private::url
KUrl url
Definition: kdirlister_p.h:121
KDirLister::itemsForDir
KFileItemList itemsForDir(const KUrl &dir, WhichItems which=FilteredItems) const
Returns the items listed for the given dir.
Definition: kdirlister.cpp:2698
KDirLister::updateDirectory
virtual void updateDirectory(const KUrl &_dir)
Update the directory _dir.
Definition: kdirlister.cpp:2240
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:24:53 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal