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

KIO

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

Skip menu "kdelibs-4.14.8 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
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