File patch-r890057.diff of Package kdelibs4

Subject: fixes for toggling hidden files
From: wstephenson@suse.de
Bug: kde#174788
Patch-upstream: 890057
--- kio/kio/kdirlister_p.h	(revision 890056)
+++ kio/kio/kdirlister_p.h	(revision 890057)
@@ -47,8 +47,6 @@ public:
     complete = false;
 
     autoUpdate = false;
-    isShowingDotFiles = false;
-    dirOnlyMode = false;
 
     autoErrorHandling = false;
     errorParent = 0;
@@ -63,8 +61,7 @@ public:
     lstRemoveItems = 0;
 
     refreshItemWasFiltered = false;
-
-    changes = NONE;
+    hasPendingChanges = false;
 
     window = 0;
     m_cachedItemsJob = 0;
@@ -91,6 +88,21 @@ public:
   void emitItemsDeleted(const KFileItemList &items);
   void redirect( const KUrl& oldUrl, const KUrl& newUrl );
 
+    /**
+     * Should this item be visible according to the current filter settings?
+     */
+    bool isItemVisible(const KFileItem& item) const;
+
+    void prepareForSettingsChange() {
+        if (!hasPendingChanges) {
+            hasPendingChanges = true;
+            oldSettings = settings;
+        }
+    }
+
+    void emitChanges();
+
+
   KDirLister *m_parent;
 
   /**
@@ -105,12 +117,11 @@ public:
   bool complete:1;
 
   bool autoUpdate:1;
-  bool isShowingDotFiles:1;
-  bool dirOnlyMode:1;
 
   bool delayedMimeTypes:1;
 
   bool refreshItemWasFiltered:1;
+    bool hasPendingChanges:1; // i.e. settings != oldSettings
 
   bool autoErrorHandling:2;
   QWidget *errorParent;
@@ -129,16 +140,24 @@ public:
   QList<QPair<KFileItem,KFileItem> > *lstRefreshItems;
   KFileItemList *lstMimeFilteredItems, *lstRemoveItems;
 
-  int changes;
-
     QWidget *window; // Main window this lister is associated with
     class CachedItemsJob;
     CachedItemsJob* m_cachedItemsJob;
 
-  QString nameFilter;
-  QList<QRegExp> lstFilters, oldFilters;
-  QStringList mimeFilter, oldMimeFilter;
-  QStringList mimeExcludeFilter, oldMimeExcludeFilter;
+    QString nameFilter; // parsed into lstFilters
+
+    struct FilterSettings {
+        FilterSettings() : isShowingDotFiles(false), dirOnlyMode(false) {}
+        bool isShowingDotFiles;
+        bool dirOnlyMode;
+        QList<QRegExp> lstFilters;
+        QStringList mimeFilter;
+        QStringList mimeExcludeFilter;
+    };
+    FilterSettings settings;
+    FilterSettings oldSettings;
+
+    friend class KDirListerCache;
 };
 
 /**
--- kio/kio/kdirlister.h	(revision 890056)
+++ kio/kio/kdirlister.h	(revision 890057)
@@ -579,10 +579,6 @@ Q_SIGNALS:
   void speed( int bytes_per_second );
 
 protected:
-  enum Changes {
-    NONE=0, NAME_FILTER=1, MIME_FILTER=2, DOT_FILES=4, DIR_ONLY_MODE=8
-  };
-
   /**
    * Called for every new item before emitting newItems().
    * You may reimplement this method in a subclass to implement your own
--- kio/kio/kdirlister.cpp	(revision 890056)
+++ kio/kio/kdirlister.cpp	(revision 890057)
@@ -524,8 +524,8 @@ void KDirListerCache::forgetDirs( KDirLi
                     // Look for a manually-mounted directory inside
                     // If there's one, we can't keep a watch either, FAM would prevent unmounting the CDROM
                     // I hope this isn't too slow
-                    KFileItemList::const_iterator kit = item->lstItems.begin();
-                    const KFileItemList::const_iterator kend = item->lstItems.end();
+                    KFileItemList::const_iterator kit = item->lstItems.constBegin();
+                    KFileItemList::const_iterator kend = item->lstItems.constEnd();
                     for ( ; kit != kend && !containsManuallyMounted; ++kit )
                         if ( (*kit).isDir() && manually_mounted((*kit).url().path(), possibleMountPoints) )
                             containsManuallyMounted = true;
@@ -771,12 +771,12 @@ void KDirListerCache::slotFilesRemoved(
     }
     }
 
-    QMap<QString, KFileItemList>::const_iterator rit = removedItemsByDir.begin();
-    for(; rit != removedItemsByDir.end(); ++rit) {
+    QMap<QString, KFileItemList>::const_iterator rit = removedItemsByDir.constBegin();
+    for(; rit != removedItemsByDir.constEnd(); ++rit) {
         // Tell the views about it before calling deleteDir.
         // They might need the subdirs' file items (see the dirtree).
-        DirectoryDataHash::const_iterator dit = directoryData.find(rit.key());
-        if (dit != directoryData.end()) {
+        DirectoryDataHash::const_iterator dit = directoryData.constFind(rit.key());
+        if (dit != directoryData.constEnd()) {
             itemsDeleted((*dit).listersCurrentlyHolding, rit.value());
         }
     }
@@ -816,8 +816,8 @@ void KDirListerCache::slotFilesChanged(
         }
     }
 
-    KUrl::List::const_iterator itdir = dirsToUpdate.begin();
-    for (; itdir != dirsToUpdate.end() ; ++itdir)
+    KUrl::List::const_iterator itdir = dirsToUpdate.constBegin();
+    for (; itdir != dirsToUpdate.constEnd() ; ++itdir)
         updateDirectory( *itdir );
     // ## TODO problems with current jobs listing/updating that dir
     // ( see kde-2.2.2's kdirlister )
@@ -1481,8 +1481,8 @@ void KDirListerCache::slotUpdateResult(
     }
 
     KIO::UDSEntryList buf = jobs.value( job );
-    KIO::UDSEntryList::const_iterator it = buf.begin();
-    const KIO::UDSEntryList::const_iterator end = buf.end();
+    KIO::UDSEntryList::const_iterator it = buf.constBegin();
+    const KIO::UDSEntryList::const_iterator end = buf.constEnd();
     for ( ; it != end; ++it )
     {
         // Form the complete url
@@ -1574,11 +1574,10 @@ void KDirListerCache::slotUpdateResult(
 
 KIO::ListJob *KDirListerCache::jobForUrl( const QString& url, KIO::ListJob *not_job )
 {
-  KIO::ListJob *job;
-  QMap< KIO::ListJob *, KIO::UDSEntryList >::const_iterator it = jobs.begin();
-  while ( it != jobs.end() )
+  QMap< KIO::ListJob *, KIO::UDSEntryList >::const_iterator it = jobs.constBegin();
+  while ( it != jobs.constEnd() )
   {
-    job = it.key();
+    KIO::ListJob *job = it.key();
     if ( joburl( job ).url(KUrl::RemoveTrailingSlash) == url && job != not_job )
        return job;
     ++it;
@@ -1724,8 +1723,8 @@ void KDirListerCache::processPendingUpda
 void KDirListerCache::printDebug()
 {
     kDebug(7004) << "Items in use:";
-    QHash<QString, DirItem *>::const_iterator itu = itemsInUse.begin();
-    const QHash<QString, DirItem *>::const_iterator ituend = itemsInUse.end();
+    QHash<QString, DirItem *>::const_iterator itu = itemsInUse.constBegin();
+    const QHash<QString, DirItem *>::const_iterator ituend = itemsInUse.constEnd();
     for ( ; itu != ituend ; ++itu ) {
         kDebug(7004) << "   " << itu.key() << "URL:" << itu.value()->url
                      << "rootItem:" << ( !itu.value()->rootItem.isNull() ? itu.value()->rootItem.url() : KUrl() )
@@ -1735,8 +1734,8 @@ void KDirListerCache::printDebug()
     }
 
     kDebug(7004) << "Directory data:";
-    DirectoryDataHash::const_iterator dit = directoryData.begin();
-    for ( ; dit != directoryData.end(); ++dit )
+    DirectoryDataHash::const_iterator dit = directoryData.constBegin();
+    for ( ; dit != directoryData.constEnd(); ++dit )
     {
         QString list;
         foreach ( KDirLister* listit, (*dit).listersCurrentlyListing )
@@ -1803,10 +1802,10 @@ KDirLister::~KDirLister()
 bool KDirLister::openUrl( const KUrl& _url, OpenUrlFlags _flags )
 {
     // emit the current changes made to avoid an inconsistent treeview
-    if ( d->changes != NONE && ( _flags & Keep ) )
+    if (d->hasPendingChanges && (_flags & Keep))
         emitChanges();
 
-    d->changes = NONE;
+    d->hasPendingChanges = false;
 
     return kDirListerCache->listDir( this, _url, _flags & Keep, _flags & Reload );
 }
@@ -1837,30 +1836,30 @@ void KDirLister::setAutoUpdate( bool _en
 
 bool KDirLister::showingDotFiles() const
 {
-  return d->isShowingDotFiles;
+  return d->settings.isShowingDotFiles;
 }
 
 void KDirLister::setShowingDotFiles( bool _showDotFiles )
 {
-  if ( d->isShowingDotFiles == _showDotFiles )
+  if ( d->settings.isShowingDotFiles == _showDotFiles )
     return;
 
-  d->isShowingDotFiles = _showDotFiles;
-  d->changes ^= DOT_FILES;
+  d->prepareForSettingsChange();
+  d->settings.isShowingDotFiles = _showDotFiles;
 }
 
 bool KDirLister::dirOnlyMode() const
 {
-  return d->dirOnlyMode;
+  return d->settings.dirOnlyMode;
 }
 
 void KDirLister::setDirOnlyMode( bool _dirsOnly )
 {
-  if ( d->dirOnlyMode == _dirsOnly )
+  if ( d->settings.dirOnlyMode == _dirsOnly )
     return;
 
-  d->dirOnlyMode = _dirsOnly;
-  d->changes ^= DIR_ONLY_MODE;
+  d->prepareForSettingsChange();
+  d->settings.dirOnlyMode = _dirsOnly;
 }
 
 bool KDirLister::autoErrorHandlingEnabled() const
@@ -1886,97 +1885,62 @@ KUrl::List KDirLister::directories() con
 
 void KDirLister::emitChanges()
 {
-  if ( d->changes == NONE )
-    return;
-
-  const int changes = d->changes;
-  d->changes = NONE; // in case of recursion (testcase: enabling recursive scan in ktorrent, #174920)
-
-  Q_FOREACH(const KUrl& dir, d->lstDirs) {
-    KFileItemList deletedItems;
-
-    const KFileItemList* itemList = kDirListerCache->itemsForDir(dir);
-    KFileItemList::const_iterator kit = itemList->begin();
-    const KFileItemList::const_iterator kend = itemList->end();
-    for ( ; kit != kend; ++kit )
-    {
-      if ( (*kit).text() == "." || (*kit).text() == ".." )
-        continue;
-
-      bool oldMime = true, newMime = true;
-
-      if (changes & MIME_FILTER) {
-        const QString mimetype = (*kit).mimetype();
-        oldMime = doMimeFilter( mimetype, d->oldMimeFilter )
-                && d->doMimeExcludeFilter( mimetype, d->oldMimeExcludeFilter );
-        newMime = doMimeFilter( mimetype, d->mimeFilter )
-                && d->doMimeExcludeFilter( mimetype, d->mimeExcludeFilter );
+    d->emitChanges();
+}
 
-        if ( oldMime && !newMime )
-        {
-          deletedItems.append(*kit);
-          continue;
-        }
-      }
+void KDirLister::Private::emitChanges()
+{
+    if (!hasPendingChanges)
+        return;
 
-      if (changes & DIR_ONLY_MODE) {
-        // the lister switched to dirOnlyMode
-        if ( d->dirOnlyMode )
-        {
-          if ( !(*kit).isDir() )
-          {
-              deletedItems.append(*kit);
-          }
+    // reset 'hasPendingChanges' now, in case of recursion
+    // (testcase: enabling recursive scan in ktorrent, #174920)
+    hasPendingChanges = false;
+
+    const Private::FilterSettings newSettings = settings;
+    settings = oldSettings; // temporarily
+
+    // Mark all items that are currently visible
+    Q_FOREACH(const KUrl& dir, lstDirs) {
+        KFileItemList* itemList = kDirListerCache->itemsForDir(dir);
+        KFileItemList::iterator kit = itemList->begin();
+        const KFileItemList::iterator kend = itemList->end();
+        for (; kit != kend; ++kit) {
+            if (isItemVisible(*kit) && m_parent->matchesMimeFilter(*kit))
+                (*kit).mark();
+            else
+                (*kit).unmark();
         }
-        else if ( !(*kit).isDir() )
-          d->addNewItem( *kit );
+    }
 
-        continue;
-      }
+    settings = newSettings;
 
-      if ( (*kit).isHidden() )
-      {
-        if (changes & DOT_FILES) {
-          // the lister switched to dot files mode
-          if ( d->isShowingDotFiles )
-            d->addNewItem( *kit );
-          else
-          {
-              deletedItems.append(*kit);
-          }
+    Q_FOREACH(const KUrl& dir, lstDirs) {
+        KFileItemList deletedItems;
 
-          continue;
+        KFileItemList* itemList = kDirListerCache->itemsForDir(dir);
+        KFileItemList::iterator kit = itemList->begin();
+        const KFileItemList::iterator kend = itemList->end();
+        for (; kit != kend; ++kit) {
+            KFileItem& item = *kit;
+            const QString text = item.text();
+            if (text == "." || text == "..")
+                continue;
+            const bool nowVisible = isItemVisible(item) && m_parent->matchesMimeFilter(item);
+            if (nowVisible && !item.isMarked())
+                addNewItem(item); // takes care of emitting newItem or itemsFilteredByMime
+            else if (!nowVisible && item.isMarked())
+                deletedItems.append(*kit);
         }
-      } else if (changes & NAME_FILTER) {
-        bool oldName = (*kit).isDir() ||
-                       d->oldFilters.isEmpty() ||
-                       doNameFilter( (*kit).text(), d->oldFilters );
-
-        bool newName = (*kit).isDir() ||
-                       d->lstFilters.isEmpty() ||
-                       doNameFilter( (*kit).text(), d->lstFilters );
-
-        if ( oldName && !newName )
-        {
-          deletedItems.append(*kit);
-          continue;
+        if (!deletedItems.isEmpty()) {
+            emit m_parent->itemsDeleted(deletedItems);
+            // for compat
+            Q_FOREACH(const KFileItem& item, deletedItems)
+                emit m_parent->deleteItem(item);
         }
-        else if ( !oldName && newName )
-          d->addNewItem( *kit );
-      }
-
-      if ((changes & MIME_FILTER) && !oldMime && newMime)
-        d->addNewItem( *kit );
+        emitItems();
     }
-
-    if (!deletedItems.isEmpty()) {
-        emit itemsDeleted(deletedItems);
-        // for compat
-        Q_FOREACH(const KFileItem& item, deletedItems)
-            emit deleteItem(item);
-    }
-    d->emitItems();
-  }
+    oldSettings = settings;
 }
 
 void KDirLister::updateDirectory( const KUrl& _u )
@@ -2014,21 +1978,17 @@ KFileItem KDirLister::findByName( const
 
 void KDirLister::setNameFilter( const QString& nameFilter )
 {
-  if ( !(d->changes & NAME_FILTER) )
-  {
-    d->oldFilters = d->lstFilters;
-  }
-
-  d->lstFilters.clear();
-
-  d->nameFilter = nameFilter;
+    if (d->nameFilter == nameFilter)
+        return;
 
-  // Split on white space
-  const QStringList list = nameFilter.split( ' ', QString::SkipEmptyParts );
-  for ( QStringList::const_iterator it = list.begin(); it != list.end(); ++it )
-    d->lstFilters.append( QRegExp(*it, Qt::CaseInsensitive, QRegExp::Wildcard ) );
+    d->prepareForSettingsChange();
 
-  d->changes |= NAME_FILTER;
+    d->settings.lstFilters.clear();
+    d->nameFilter = nameFilter;
+    // Split on white space
+    const QStringList list = nameFilter.split( ' ', QString::SkipEmptyParts );
+    for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it)
+        d->settings.lstFilters.append(QRegExp(*it, Qt::CaseInsensitive, QRegExp::Wildcard));
 }
 
 QString KDirLister::nameFilter() const
@@ -2038,52 +1998,47 @@ QString KDirLister::nameFilter() const
 
 void KDirLister::setMimeFilter( const QStringList& mimeFilter )
 {
-  if ( !(d->changes & MIME_FILTER) )
-    d->oldMimeFilter = d->mimeFilter;
-
-  if ( mimeFilter.contains("application/octet-stream") ) // all files
-    d->mimeFilter.clear();
-  else
-    d->mimeFilter = mimeFilter;
+    if (d->settings.mimeFilter == mimeFilter)
+        return;
 
-  d->changes |= MIME_FILTER;
+    d->prepareForSettingsChange();
+    if (mimeFilter.contains("application/octet-stream")) // all files
+        d->settings.mimeFilter.clear();
+    else
+        d->settings.mimeFilter = mimeFilter;
 }
 
 void KDirLister::setMimeExcludeFilter( const QStringList& mimeExcludeFilter )
 {
-  if ( !(d->changes & MIME_FILTER) )
-    d->oldMimeExcludeFilter = d->mimeExcludeFilter;
+    if (d->settings.mimeExcludeFilter == mimeExcludeFilter)
+        return;
 
-  d->mimeExcludeFilter = mimeExcludeFilter;
-  d->changes |= MIME_FILTER;
+    d->prepareForSettingsChange();
+    d->settings.mimeExcludeFilter = mimeExcludeFilter;
 }
 
 
 void KDirLister::clearMimeFilter()
 {
-  if ( !(d->changes & MIME_FILTER) )
-  {
-    d->oldMimeFilter = d->mimeFilter;
-    d->oldMimeExcludeFilter = d->mimeExcludeFilter;
-  }
-  d->mimeFilter.clear();
-  d->mimeExcludeFilter.clear();
-  d->changes |= MIME_FILTER;
+    d->prepareForSettingsChange();
+    d->settings.mimeFilter.clear();
+    d->settings.mimeExcludeFilter.clear();
 }
 
 QStringList KDirLister::mimeFilters() const
 {
-  return d->mimeFilter;
+  return d->settings.mimeFilter;
 }
 
 bool KDirLister::matchesFilter( const QString& name ) const
 {
-  return doNameFilter( name, d->lstFilters );
+    return doNameFilter(name, d->settings.lstFilters);
 }
 
 bool KDirLister::matchesMimeFilter( const QString& mime ) const
 {
-  return doMimeFilter( mime, d->mimeFilter ) && d->doMimeExcludeFilter(mime,d->mimeExcludeFilter);
+    return doMimeFilter(mime, d->settings.mimeFilter) &&
+        d->doMimeExcludeFilter(mime, d->settings.mimeExcludeFilter);
 }
 
 // ================ protected methods ================ //
@@ -2095,10 +2050,10 @@ bool KDirLister::matchesFilter( const KF
   if ( item.text() == ".." )
     return false;
 
-  if ( !d->isShowingDotFiles && item.isHidden() )
+  if ( !d->settings.isShowingDotFiles && item.isHidden() )
     return false;
 
-  if ( item.isDir() || d->lstFilters.isEmpty() )
+  if ( item.isDir() || d->settings.lstFilters.isEmpty() )
     return true;
 
   return matchesFilter( item.text() );
@@ -2106,11 +2061,11 @@ bool KDirLister::matchesFilter( const KF
 
 bool KDirLister::matchesMimeFilter( const KFileItem& item ) const
 {
-  Q_ASSERT( !item.isNull() );
-  // Don't lose time determining the mimetype if there is no filter
-  if ( d->mimeFilter.isEmpty() && d->mimeExcludeFilter.isEmpty() )
-      return true;
-  return matchesMimeFilter( item.mimetype() );
+    Q_ASSERT(!item.isNull());
+    // Don't lose time determining the mimetype if there is no filter
+    if (d->settings.mimeFilter.isEmpty() && d->settings.mimeExcludeFilter.isEmpty())
+        return true;
+    return matchesMimeFilter(item.mimetype());
 }
 
 bool KDirLister::doNameFilter( const QString& name, const QList<QRegExp>& filters ) const
@@ -2165,8 +2120,8 @@ void KDirLister::handleError( KIO::Job *
 
 void KDirLister::Private::addNewItem( const KFileItem &item )
 {
-  if ( ( dirOnlyMode && !item.isDir() ) || !m_parent->matchesFilter( item ) )
-    return; // No reason to continue... bailing out here prevents a mimetype scan.
+    if (!isItemVisible(item))
+        return; // No reason to continue... bailing out here prevents a mimetype scan.
 
   if ( m_parent->matchesMimeFilter( item ) )
   {
@@ -2202,21 +2157,12 @@ void KDirLister::Private::addNewItems( c
 
 void KDirLister::Private::aboutToRefreshItem( const KFileItem &item )
 {
-  // The code here follows the logic in addNewItem
-  if ( ( dirOnlyMode && !item.isDir() ) || !m_parent->matchesFilter( item ) )
-    refreshItemWasFiltered = true;
-  else if ( !m_parent->matchesMimeFilter( item ) )
-    refreshItemWasFiltered = true;
-  else
-    refreshItemWasFiltered = false;
+    refreshItemWasFiltered = !isItemVisible(item) || !m_parent->matchesMimeFilter(item);
 }
 
 void KDirLister::Private::addRefreshItem( const KFileItem& oldItem, const KFileItem& item )
 {
-  bool isExcluded = (dirOnlyMode && !item.isDir()) || !m_parent->matchesFilter( item );
-
-  if ( !isExcluded && m_parent->matchesMimeFilter( item ) )
-  {
+  if (isItemVisible(item) && m_parent->matchesMimeFilter(item)) {
     if ( refreshItemWasFiltered )
     {
       if ( !lstNewItems ) {
@@ -2291,12 +2237,18 @@ void KDirLister::Private::emitItems()
 
 void KDirLister::Private::emitDeleteItem( const KFileItem &item )
 {
-  if ( ( dirOnlyMode && !item.isDir() ) || !m_parent->matchesFilter( item ) )
-    return; // No reason to continue... bailing out here prevents a mimetype scan.
-  if ( m_parent->matchesMimeFilter( item ) )
-  {
-    emit m_parent->deleteItem( item );
-  }
+    if (isItemVisible(item) && m_parent->matchesMimeFilter(item)) {
+        emit m_parent->deleteItem(item);
+    }
+}
+
+bool KDirLister::Private::isItemVisible(const KFileItem& item) const
+{
+    // Note that this doesn't include mime filters, because
+    // of the itemsFilteredByMime signal. Filtered-by-mime items are
+    // considered "visible", they are just visible via a different signal...
+    return (!settings.dirOnlyMode || item.isDir())
+        && m_parent->matchesFilter(item);
 }
 
 void KDirLister::Private::emitItemsDeleted(const KFileItemList &_items)
@@ -2305,13 +2257,11 @@ void KDirLister::Private::emitItemsDelet
     QMutableListIterator<KFileItem> it(items);
     while (it.hasNext()) {
         const KFileItem& item = it.next();
-        if ((dirOnlyMode && !item.isDir())
-            || !m_parent->matchesFilter(item)
-            || !m_parent->matchesMimeFilter(item) ) { // do this one last
-            it.remove();
-        } else {
+        if (isItemVisible(item) && m_parent->matchesMimeFilter(item)) {
             // for compat
             emit m_parent->deleteItem(item);
+        } else {
+            it.remove();
         }
     }
     if (!items.isEmpty())
@@ -2466,14 +2416,14 @@ KFileItemList KDirLister::itemsForDir( c
     else // only items passing the filters
     {
         KFileItemList result;
-        KFileItemList::const_iterator kit = allItems->begin();
-        const KFileItemList::const_iterator kend = allItems->end();
+        KFileItemList::const_iterator kit = allItems->constBegin();
+        const KFileItemList::const_iterator kend = allItems->constEnd();
         for ( ; kit != kend; ++kit )
         {
-            KFileItem item = *kit;
-            bool isExcluded = (d->dirOnlyMode && !item.isDir()) || !matchesFilter( item );
-            if ( !isExcluded && matchesMimeFilter( item ) )
-                result.append( item );
+            const KFileItem& item = *kit;
+            if (d->isItemVisible(item) && matchesMimeFilter(item)) {
+                result.append(item);
+            }
         }
         return result;
     }
@@ -2497,7 +2447,13 @@ void KDirLister::Private::redirect( cons
         url = newUrl;
     }
 
-    lstDirs[ lstDirs.indexOf( oldUrl ) ] = newUrl;
+    const int idx = lstDirs.indexOf( oldUrl );
+    if (idx == -1) {
+        kWarning(7004) << "Unexpected redirection from" << oldUrl << "to" << newUrl
+                       << "but this dirlister is currently listing" << lstDirs;
+    } else {
+        lstDirs[ idx ] = newUrl;
+    }
 
     if ( lstDirs.count() == 1 ) {
         emit m_parent->clear();
--- kio/tests/kdirmodeltest.cpp	(revision 890056)
+++ kio/tests/kdirmodeltest.cpp	(revision 890057)
@@ -40,15 +40,20 @@
 QTEST_KDEMAIN( KDirModelTest, NoGUI )
 
 #ifndef Q_WS_WIN
-#define SPECIALCHARS "specialchars%:"
+#define SPECIALCHARS "specialchars%:.pdf"
 #else
-#define SPECIALCHARS "specialchars%"
+#define SPECIALCHARS "specialchars%.pdf"
 #endif
 
+Q_DECLARE_METATYPE(KFileItemList)
+
 void KDirModelTest::initTestCase()
 {
     qRegisterMetaType<QModelIndex>("QModelIndex"); // beats me why Qt doesn't do that
 
+    qRegisterMetaType<KFileItemList>("KFileItemList");
+
+    m_dirModelForExpand = 0;
     s_referenceTimeStamp = QDateTime::currentDateTime().addSecs( -30 ); // 30 seconds ago
     m_tempDir = 0;
     m_topLevelFileNames << "toplevelfile_1"
@@ -70,7 +75,9 @@ void KDirModelTest::recreateTestData()
      * PATH/toplevelfile_1
      * PATH/toplevelfile_2
      * PATH/toplevelfile_3
-     * PATH/specialchars%:
+     * PATH/specialchars%:.pdf
+     * PATH/.hidden
+     * PATH/.hidden2
      * PATH/subdir
      * PATH/subdir/testfile
      * PATH/subdir/subsubdir
@@ -80,6 +87,8 @@ void KDirModelTest::recreateTestData()
     foreach(const QString &f, m_topLevelFileNames) {
         createTestFile(path+f);
     }
+    createTestFile(path+".hidden");
+    createTestFile(path+".hidden2");
     createTestDirectory(path+"subdir");
     createTestDirectory(path+"subdir/subsubdir", NoSymlink);
 
@@ -112,6 +121,9 @@ void KDirModelTest::cleanup()
 {
     disconnect(&m_dirModel, 0, &m_eventLoop, 0);
     disconnect(m_dirModel.dirLister(), 0, this, 0);
+    m_dirModel.dirLister()->setNameFilter(QString());
+    m_dirModel.dirLister()->setMimeFilter(QStringList());
+    m_dirModel.dirLister()->emitChanges();
 }
 
 void KDirModelTest::collectKnownIndexes()
@@ -534,26 +546,25 @@ void KDirModelTest::testFilter()
     QVERIFY(m_dirIndex.isValid());
     const int oldTopLevelRowCount = m_dirModel.rowCount();
     const int oldSubdirRowCount = m_dirModel.rowCount(m_dirIndex);
+    QSignalSpy spyItemsFilteredByMime(m_dirModel.dirLister(), SIGNAL(itemsFilteredByMime(KFileItemList)));
+    QSignalSpy spyItemsDeleted(m_dirModel.dirLister(), SIGNAL(itemsDeleted(KFileItemList)));
     QSignalSpy spyRowsRemoved(&m_dirModel, SIGNAL(rowsRemoved(QModelIndex,int,int)));
     m_dirModel.dirLister()->setNameFilter("toplevel*");
     QCOMPARE(m_dirModel.rowCount(), oldTopLevelRowCount); // no change yet
     QCOMPARE(m_dirModel.rowCount(m_dirIndex), oldSubdirRowCount); // no change yet
     m_dirModel.dirLister()->emitChanges();
 
-    const int expectedTopLevelRowCount = 4; // 3 toplevel* files, one subdir
-    const int expectedSubdirRowCount = 1; // the files get filtered out, the subdir remains
+    QCOMPARE(m_dirModel.rowCount(), 4); // 3 toplevel* files, one subdir
+    QCOMPARE(m_dirModel.rowCount(m_dirIndex), 1); // the files get filtered out, the subdir remains
 
-    //while (m_dirModel.rowCount() > expectedTopLevelRowCount) {
-    //    QTest::qWait(20);
-    //    kDebug() << "rowCount=" << m_dirModel.rowCount();
-    //}
-    QCOMPARE(m_dirModel.rowCount(), expectedTopLevelRowCount);
-
-    //while (m_dirModel.rowCount(m_dirIndex) > expectedSubdirRowCount) {
-    //    QTest::qWait(20);
-    //    kDebug() << "rowCount in subdir=" << m_dirModel.rowCount(m_dirIndex);
-    //}
-    QCOMPARE(m_dirModel.rowCount(m_dirIndex), expectedSubdirRowCount);
+    QCOMPARE(spyRowsRemoved.count(), 3); // once for every dir
+    QCOMPARE(spyItemsDeleted.count(), 3); // once for every dir
+    QCOMPARE(spyItemsDeleted[0][0].value<KFileItemList>().count(), 1); // one from toplevel ('specialchars')
+    QCOMPARE(spyItemsDeleted[1][0].value<KFileItemList>().count(), 2); // two from subdir
+    QCOMPARE(spyItemsDeleted[2][0].value<KFileItemList>().count(), 1); // one from subsubdir
+    QCOMPARE(spyItemsFilteredByMime.count(), 0);
+    spyItemsDeleted.clear();
+    spyItemsFilteredByMime.clear();
 
     // Reset the filter
     kDebug() << "reset to no filter";
@@ -562,12 +573,78 @@ void KDirModelTest::testFilter()
 
     QCOMPARE(m_dirModel.rowCount(), oldTopLevelRowCount);
     QCOMPARE(m_dirModel.rowCount(m_dirIndex), oldSubdirRowCount);
+    QCOMPARE(spyItemsDeleted.count(), 0);
+    QCOMPARE(spyItemsFilteredByMime.count(), 0);
+
+    // The order of things changed because of filtering.
+    // Fill again, so that m_fileIndex etc. are correct again.
+    fillModel(true);
+}
+
+void KDirModelTest::testMimeFilter()
+{
+    QVERIFY(m_dirIndex.isValid());
+    const int oldTopLevelRowCount = m_dirModel.rowCount();
+    const int oldSubdirRowCount = m_dirModel.rowCount(m_dirIndex);
+    QSignalSpy spyItemsFilteredByMime(m_dirModel.dirLister(), SIGNAL(itemsFilteredByMime(KFileItemList)));
+    QSignalSpy spyItemsDeleted(m_dirModel.dirLister(), SIGNAL(itemsDeleted(KFileItemList)));
+    QSignalSpy spyRowsRemoved(&m_dirModel, SIGNAL(rowsRemoved(QModelIndex,int,int)));
+    m_dirModel.dirLister()->setMimeFilter(QStringList() << "application/pdf");
+    QCOMPARE(m_dirModel.rowCount(), oldTopLevelRowCount); // no change yet
+    QCOMPARE(m_dirModel.rowCount(m_dirIndex), oldSubdirRowCount); // no change yet
+    m_dirModel.dirLister()->emitChanges();
+
+    QCOMPARE(m_dirModel.rowCount(), 1); // 1 pdf files, no subdir anymore
+
+    QVERIFY(spyRowsRemoved.count() >= 1); // depends on contiguity...
+    QVERIFY(spyItemsDeleted.count() >= 1); // once for every dir
+    // Maybe it would make sense to have those items in itemsFilteredByMime,
+    // but well, for the only existing use of that signal (mime filter plugin),
+    // it's not really necessary, the plugin has seen those files before anyway.
+    // The signal is mostly useful for the case of listing a dir with a mime filter set.
+    //QCOMPARE(spyItemsFilteredByMime.count(), 1);
+    //QCOMPARE(spyItemsFilteredByMime[0][0].value<KFileItemList>().count(), 4);
+    spyItemsDeleted.clear();
+    spyItemsFilteredByMime.clear();
+
+    // Reset the filter
+    kDebug() << "reset to no filter";
+    m_dirModel.dirLister()->setMimeFilter(QStringList());
+    m_dirModel.dirLister()->emitChanges();
+
+    QCOMPARE(m_dirModel.rowCount(), oldTopLevelRowCount);
+    QCOMPARE(spyItemsDeleted.count(), 0);
+    QCOMPARE(spyItemsFilteredByMime.count(), 0);
 
     // The order of things changed because of filtering.
     // Fill again, so that m_fileIndex etc. are correct again.
     fillModel(true);
 }
 
+void KDirModelTest::testShowHiddenFiles() // #174788
+{
+    KDirLister* dirLister = m_dirModel.dirLister();
+
+    QSignalSpy spyRowsRemoved(&m_dirModel, SIGNAL(rowsRemoved(QModelIndex, int, int)));
+    QSignalSpy spyNewItems(dirLister, SIGNAL(newItems(KFileItemList)));
+    QSignalSpy spyRowsInserted(&m_dirModel, SIGNAL(rowsInserted(QModelIndex,int,int)));
+    dirLister->setShowingDotFiles(true);
+    dirLister->emitChanges();
+    const int numberOfDotFiles = 2;
+    QCOMPARE(spyNewItems.count(), 1);
+    QCOMPARE(spyNewItems[0][0].value<KFileItemList>().count(), numberOfDotFiles);
+    QCOMPARE(spyRowsInserted.count(), 1);
+    QCOMPARE(spyRowsRemoved.count(), 0);
+    spyNewItems.clear();
+    spyRowsInserted.clear();
+
+    dirLister->setShowingDotFiles(false);
+    dirLister->emitChanges();
+    QCOMPARE(spyNewItems.count(), 0);
+    QCOMPARE(spyRowsInserted.count(), 0);
+    QCOMPARE(spyRowsRemoved.count(), 1);
+}
+
 void KDirModelTest::testMultipleSlashes()
 {
     const QString path = m_tempDir->name();
--- kio/tests/kdirmodeltest.h	(revision 890056)
+++ kio/tests/kdirmodeltest.h	(revision 890057)
@@ -46,6 +46,8 @@ private Q_SLOTS:
     void testExpandToUrl_data();
     void testExpandToUrl();
     void testFilter();
+    void testMimeFilter();
+    void testShowHiddenFiles();
     void testMultipleSlashes();
     void testUrlWithRef();
     void testUrlWithHost();
Index: kio/kio/kdirlister_p.h
===================================================================
Index: kio/kio/kdirlister.h
===================================================================
Index: kio/kio/kdirlister.cpp
===================================================================
Index: kio/tests/kdirmodeltest.cpp
===================================================================
Index: kio/tests/kdirmodeltest.h
===================================================================
openSUSE Build Service is sponsored by