]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Count the items inside directories in another thread
authorFrank Reininghaus <frank78ac@googlemail.com>
Wed, 4 Sep 2013 19:49:01 +0000 (21:49 +0200)
committerFrank Reininghaus <frank78ac@googlemail.com>
Wed, 4 Sep 2013 19:50:16 +0000 (21:50 +0200)
This prevents that the GUI freezes if there are many files inside the
directory, or if the access to the directory is slow for some other
reason.

BUG: 318518
REVIEW: 111920
FIXED-IN: 4.12.0

src/CMakeLists.txt
src/kitemviews/kfileitemmodelrolesupdater.cpp
src/kitemviews/kfileitemmodelrolesupdater.h
src/kitemviews/private/kdirectorycontentscounter.cpp [new file with mode: 0644]
src/kitemviews/private/kdirectorycontentscounter.h [new file with mode: 0644]
src/kitemviews/private/kdirectorycontentscounterworker.cpp [new file with mode: 0644]
src/kitemviews/private/kdirectorycontentscounterworker.h [new file with mode: 0644]

index 3aac80bf8a0ebb9a750df908474a7e9da1aa001a..48ea14c18e9696267c60917eb9f3cc0edf9edd98 100644 (file)
@@ -58,6 +58,8 @@ set(dolphinprivate_LIB_SRCS
     kitemviews/kstandarditemlistwidget.cpp
     kitemviews/kstandarditemlistview.cpp
     kitemviews/kstandarditemmodel.cpp
+    kitemviews/private/kdirectorycontentscounter.cpp
+    kitemviews/private/kdirectorycontentscounterworker.cpp
     kitemviews/private/kfileitemclipboard.cpp
     kitemviews/private/kfileitemmodeldirlister.cpp
     kitemviews/private/kfileitemmodelfilter.cpp
index e9b69566d5b542ddb5234961b32bbd1652b85b61..d6445c981f72dcf5a0aa45851d169b056feff14b 100644 (file)
 #include <KConfig>
 #include <KConfigGroup>
 #include <KDebug>
-#include <KDirWatch>
 #include <KFileItem>
 #include <KGlobal>
 #include <KIO/JobUiDelegate>
 #include <KIO/PreviewJob>
 
 #include "private/kpixmapmodifier.h"
+#include "private/kdirectorycontentscounter.h"
 
 #include <QApplication>
 #include <QPainter>
     #include <Nepomuk2/ResourceManager>
 #endif
 
-// Required includes for subItemsCount():
-#ifdef Q_WS_WIN
-    #include <QDir>
-#else
-    #include <dirent.h>
-    #include <QFile>
-#endif
-
 // #define KFILEITEMMODELROLESUPDATER_DEBUG
 
 namespace {
@@ -95,8 +87,7 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO
     m_recentlyChangedItemsTimer(0),
     m_recentlyChangedItems(),
     m_changedItems(),
-    m_dirWatcher(0),
-    m_watchedDirs()
+    m_directoryContentsCounter(0)
   #ifdef HAVE_NEPOMUK
   , m_nepomukResourceWatcher(0),
     m_nepomukUriItems()
@@ -135,10 +126,9 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO
     m_resolvableRoles += KNepomukRolesProvider::instance().roles();
 #endif
 
-    // When folders are expandable or the item-count is shown for folders, it is necessary
-    // to watch the number of items of the sub-folder to be able to react on changes.
-    m_dirWatcher = new KDirWatch(this);
-    connect(m_dirWatcher, SIGNAL(dirty(QString)), this, SLOT(slotDirWatchDirty(QString)));
+    m_directoryContentsCounter = new KDirectoryContentsCounter(m_model, this);
+    connect(m_directoryContentsCounter, SIGNAL(result(QString,int)),
+            this,                       SLOT(slotDirectoryContentsCountReceived(QString,int)));
 }
 
 KFileItemModelRolesUpdater::~KFileItemModelRolesUpdater()
@@ -367,25 +357,6 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRang
 
     const bool allItemsRemoved = (m_model->count() == 0);
 
-    if (!m_watchedDirs.isEmpty()) {
-        // Don't let KDirWatch watch for removed items
-        if (allItemsRemoved) {
-            foreach (const QString& path, m_watchedDirs) {
-                m_dirWatcher->removeDir(path);
-            }
-            m_watchedDirs.clear();
-        } else {
-            QMutableSetIterator<QString> it(m_watchedDirs);
-            while (it.hasNext()) {
-                const QString& path = it.next();
-                if (m_model->index(KUrl(path)) < 0) {
-                    m_dirWatcher->removeDir(path);
-                    it.remove();
-                }
-            }
-        }
-    }
-
 #ifdef HAVE_NEPOMUK
     if (m_nepomukResourceWatcher) {
         // Don't let the ResourceWatcher watch for removed items
@@ -783,7 +754,7 @@ void KFileItemModelRolesUpdater::applyChangedNepomukRoles(const Nepomuk2::Resour
 #endif
 }
 
-void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path)
+void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count)
 {
     const bool getSizeRole = m_roles.contains("size");
     const bool getIsExpandableRole = m_roles.contains("isExpandable");
@@ -791,16 +762,9 @@ void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path)
     if (getSizeRole || getIsExpandableRole) {
         const int index = m_model->index(KUrl(path));
         if (index >= 0) {
-            if (!m_model->fileItem(index).isDir()) {
-                // If INotify is used, KDirWatch issues the dirty() signal
-                // also for changed files inside the directory, even if we
-                // don't enable this behavior explicitly (see bug 309740).
-                return;
-            }
 
             QHash<QByteArray, QVariant> data;
 
-            const int count = subItemsCount(path);
             if (getSizeRole) {
                 data.insert("size", count);
             }
@@ -808,9 +772,11 @@ void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path)
                 data.insert("isExpandable", count > 0);
             }
 
-            // Note that we do not block the itemsChanged signal here.
-            // This ensures that a new preview will be generated.
+            disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+                       this,    SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
             m_model->setData(index, data);
+            connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+                    this,    SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
         }
     }
 }
@@ -1037,7 +1003,7 @@ void KFileItemModelRolesUpdater::applySortRole(int index)
         data.insert("type", item.mimeComment());
     } else if (m_model->sortRole() == "size" && item.isLocalFile() && item.isDir()) {
         const QString path = item.localPath();
-        data.insert("size", subItemsCount(path));
+        data.insert("size", m_directoryContentsCounter->countDirectoryContentsSynchronously(path));
     } else {
         // Probably the sort role is a Nepomuk role - just determine all roles.
         data = rolesData(item);
@@ -1105,7 +1071,7 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(const KFileItem& item, Resol
     return false;
 }
 
-QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem& item) const
+QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem& item)
 {
     QHash<QByteArray, QVariant> data;
 
@@ -1114,19 +1080,10 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
 
     if ((getSizeRole || getIsExpandableRole) && item.isDir()) {
         if (item.isLocalFile()) {
+            // Tell m_directoryContentsCounter that we want to count the items
+            // inside the directory. The result will be received in slotDirectoryContentsCountReceived.
             const QString path = item.localPath();
-            const int count = subItemsCount(path);
-            if (getSizeRole) {
-                data.insert("size", count);
-            }
-            if (getIsExpandableRole) {
-                data.insert("isExpandable", count > 0);
-            }
-
-            if (!m_dirWatcher->contains(path)) {
-                m_dirWatcher->addDir(path);
-                m_watchedDirs.insert(path);
-            }
+            m_directoryContentsCounter->addDirectory(path);
         } else if (getSizeRole) {
             data.insert("size", -1); // -1 indicates an unknown number of items
         }
@@ -1170,61 +1127,6 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
     return data;
 }
 
-int KFileItemModelRolesUpdater::subItemsCount(const QString& path) const
-{
-    const bool countHiddenFiles = m_model->showHiddenFiles();
-    const bool showFoldersOnly  = m_model->showDirectoriesOnly();
-
-#ifdef Q_WS_WIN
-    QDir dir(path);
-    QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System;
-    if (countHiddenFiles) {
-        filters |= QDir::Hidden;
-    }
-    if (showFoldersOnly) {
-        filters |= QDir::Dirs;
-    } else {
-        filters |= QDir::AllEntries;
-    }
-    return dir.entryList(filters).count();
-#else
-    // Taken from kdelibs/kio/kio/kdirmodel.cpp
-    // Copyright (C) 2006 David Faure <faure@kde.org>
-
-    int count = -1;
-    DIR* dir = ::opendir(QFile::encodeName(path));
-    if (dir) {  // krazy:exclude=syscalls
-        count = 0;
-        struct dirent *dirEntry = 0;
-        while ((dirEntry = ::readdir(dir))) {
-            if (dirEntry->d_name[0] == '.') {
-                if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) {
-                    // Skip "." or hidden files
-                    continue;
-                }
-                if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') {
-                    // Skip ".."
-                    continue;
-                }
-            }
-
-            // If only directories are counted, consider an unknown file type and links also
-            // as directory instead of trying to do an expensive stat()
-            // (see bugs 292642 and 299997).
-            const bool countEntry = !showFoldersOnly ||
-                                    dirEntry->d_type == DT_DIR ||
-                                    dirEntry->d_type == DT_LNK ||
-                                    dirEntry->d_type == DT_UNKNOWN;
-            if (countEntry) {
-                ++count;
-            }
-        }
-        ::closedir(dir);
-    }
-    return count;
-#endif
-}
-
 void KFileItemModelRolesUpdater::updateAllPreviews()
 {
     if (m_state == Paused) {
index 409f098e8cd60df6e18bf241e230c25c15ba69a0..fced44a85da6be095899c01c7db50253a5b0e082 100644 (file)
@@ -32,7 +32,7 @@
 #include <QSize>
 #include <QStringList>
 
-class KDirWatch;
+class KDirectoryContentsCounter;
 class KFileItemModel;
 class KJob;
 class QPixmap;
@@ -218,12 +218,7 @@ private slots:
 
     void applyChangedNepomukRoles(const Nepomuk2::Resource& resource, const Nepomuk2::Types::Property& property);
 
-    /**
-     * Is invoked if a directory watched by KDirWatch got dirty. Updates
-     * the "isExpandable"- and "size"-roles of the item that matches to
-     * the given path.
-     */
-    void slotDirWatchDirty(const QString& path);
+    void slotDirectoryContentsCountReceived(const QString& path, int count);
 
 private:
     /**
@@ -267,7 +262,7 @@ private:
         ResolveAll
     };
     bool applyResolvedRoles(const KFileItem& item, ResolveHint hint);
-    QHash<QByteArray, QVariant> rolesData(const KFileItem& item) const;
+    QHash<QByteArray, QVariant> rolesData(const KFileItem& item);
 
     /**
      * @return The number of items of the path \a path.
@@ -349,9 +344,8 @@ private:
     // Items which have not been changed repeatedly recently.
     QSet<KFileItem> m_changedItems;
 
-    KDirWatch* m_dirWatcher;
-    mutable QSet<QString> m_watchedDirs; // Required as sadly KDirWatch does not offer a getter method
-                                         // to get all watched directories.
+    KDirectoryContentsCounter* m_directoryContentsCounter;
+
 #ifdef HAVE_NEPOMUK
     Nepomuk2::ResourceWatcher* m_nepomukResourceWatcher;
     mutable QHash<QUrl, KUrl> m_nepomukUriItems;
diff --git a/src/kitemviews/private/kdirectorycontentscounter.cpp b/src/kitemviews/private/kdirectorycontentscounter.cpp
new file mode 100644 (file)
index 0000000..fd8479f
--- /dev/null
@@ -0,0 +1,164 @@
+/***************************************************************************
+ *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
+ ***************************************************************************/
+
+#include "kdirectorycontentscounter.h"
+
+#include "kdirectorycontentscounterworker.h"
+#include <kitemviews/kfileitemmodel.h>
+
+#include <KDirWatch>
+#include <QThread>
+
+KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObject* parent) :
+    QObject(parent),
+    m_model(model),
+    m_queue(),
+    m_workerThread(0),
+    m_worker(0),
+    m_workerIsBusy(false),
+    m_dirWatcher(0),
+    m_watchedDirs()
+{
+    connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)),
+            this,    SLOT(slotItemsRemoved()));
+
+    m_workerThread = new QThread(this);
+    m_worker = new KDirectoryContentsCounterWorker();
+    m_worker->moveToThread(m_workerThread);
+
+    connect(this,     SIGNAL(requestDirectoryContentsCount(QString,KDirectoryContentsCounterWorker::Options)),
+            m_worker, SLOT(countDirectoryContents(QString,KDirectoryContentsCounterWorker::Options)));
+    connect(m_worker, SIGNAL(result(QString,int)),
+            this,     SLOT(slotResult(QString,int)));
+
+    m_workerThread->start();
+
+    m_dirWatcher = new KDirWatch(this);
+    connect(m_dirWatcher, SIGNAL(dirty(QString)), this, SLOT(slotDirWatchDirty(QString)));
+}
+
+KDirectoryContentsCounter::~KDirectoryContentsCounter()
+{
+    m_workerThread->quit();
+    m_workerThread->wait();
+
+    delete m_worker;
+}
+
+void KDirectoryContentsCounter::addDirectory(const QString& path)
+{
+    startWorker(path);
+}
+
+int KDirectoryContentsCounter::countDirectoryContentsSynchronously(const QString& path)
+{
+    if (!m_dirWatcher->contains(path)) {
+        m_dirWatcher->addDir(path);
+        m_watchedDirs.insert(path);
+    }
+
+    KDirectoryContentsCounterWorker::Options options;
+
+    if (m_model->showHiddenFiles()) {
+        options |= KDirectoryContentsCounterWorker::CountHiddenFiles;
+    }
+
+    if (m_model->showDirectoriesOnly()) {
+        options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly;
+    }
+
+    return KDirectoryContentsCounterWorker::subItemsCount(path, options);
+}
+
+void KDirectoryContentsCounter::slotResult(const QString& path, int count)
+{
+    m_workerIsBusy = false;
+
+    if (!m_dirWatcher->contains(path)) {
+        m_dirWatcher->addDir(path);
+        m_watchedDirs.insert(path);
+    }
+
+    if (!m_queue.isEmpty()) {
+        startWorker(m_queue.dequeue());
+    }
+
+    emit result(path, count);
+}
+
+void KDirectoryContentsCounter::slotDirWatchDirty(const QString& path)
+{
+    const int index = m_model->index(KUrl(path));
+    if (index >= 0) {
+        if (!m_model->fileItem(index).isDir()) {
+            // If INotify is used, KDirWatch issues the dirty() signal
+            // also for changed files inside the directory, even if we
+            // don't enable this behavior explicitly (see bug 309740).
+            return;
+        }
+
+        startWorker(path);
+    }
+}
+
+void KDirectoryContentsCounter::slotItemsRemoved()
+{
+    const bool allItemsRemoved = (m_model->count() == 0);
+
+    if (!m_watchedDirs.isEmpty()) {
+        // Don't let KDirWatch watch for removed items
+        if (allItemsRemoved) {
+            foreach (const QString& path, m_watchedDirs) {
+                m_dirWatcher->removeDir(path);
+            }
+            m_watchedDirs.clear();
+            m_queue.clear();
+        } else {
+            QMutableSetIterator<QString> it(m_watchedDirs);
+            while (it.hasNext()) {
+                const QString& path = it.next();
+                if (m_model->index(KUrl(path)) < 0) {
+                    m_dirWatcher->removeDir(path);
+                    it.remove();
+                }
+            }
+        }
+    }
+}
+
+void KDirectoryContentsCounter::startWorker(const QString& path)
+{
+    if (m_workerIsBusy) {
+        m_queue.enqueue(path);
+    } else {
+        KDirectoryContentsCounterWorker::Options options;
+
+        if (m_model->showHiddenFiles()) {
+            options |= KDirectoryContentsCounterWorker::CountHiddenFiles;
+        }
+
+        if (m_model->showDirectoriesOnly()) {
+            options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly;
+        }
+
+        emit requestDirectoryContentsCount(path, options);
+        m_workerIsBusy = true;
+    }
+}
diff --git a/src/kitemviews/private/kdirectorycontentscounter.h b/src/kitemviews/private/kdirectorycontentscounter.h
new file mode 100644 (file)
index 0000000..425c363
--- /dev/null
@@ -0,0 +1,90 @@
+/***************************************************************************
+ *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
+ ***************************************************************************/
+
+#ifndef KDIRECTORYCONTENTSCOUNTER_H
+#define KDIRECTORYCONTENTSCOUNTER_H
+
+#include "kdirectorycontentscounterworker.h"
+
+#include <QSet>
+#include <QQueue>
+
+class KDirWatch;
+class KFileItemModel;
+class QString;
+
+class KDirectoryContentsCounter : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit KDirectoryContentsCounter(KFileItemModel* model, QObject* parent = 0);
+    ~KDirectoryContentsCounter();
+
+    /**
+     * Requests the number of items inside the directory \a path. The actual
+     * counting is done asynchronously, and the result is announced via the
+     * signal \a result.
+     *
+     * The directory \a path is watched for changes, and the signal is emitted
+     * again if a change occurs.
+     */
+    void addDirectory(const QString& path);
+
+    /**
+     * In contrast to \a addDirectory, this function counts the items inside
+     * the directory \a path synchronously and returns the result.
+     *
+     * The directory is watched for changes, and the signal \a result is
+     * emitted if a change occurs.
+     */
+    int countDirectoryContentsSynchronously(const QString& path);
+
+signals:
+    /**
+     * Signals that the directory \a path contains \a count items.
+     */
+    void result(const QString& path, int count);
+
+    void requestDirectoryContentsCount(const QString& path, KDirectoryContentsCounterWorker::Options options);
+
+private slots:
+    void slotResult(const QString& path, int count);
+    void slotDirWatchDirty(const QString& path);
+    void slotItemsRemoved();
+
+private:
+    void startWorker(const QString& path);
+
+private:
+    KFileItemModel* m_model;
+
+    QQueue<QString> m_queue;
+
+    QThread* m_workerThread;
+    KDirectoryContentsCounterWorker* m_worker;
+    bool m_workerIsBusy;
+
+    KDirWatch* m_dirWatcher;
+    QSet<QString> m_watchedDirs;    // Required as sadly KDirWatch does not offer a getter method
+                                    // to get all watched directories.
+};
+
+#endif
\ No newline at end of file
diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.cpp b/src/kitemviews/private/kdirectorycontentscounterworker.cpp
new file mode 100644 (file)
index 0000000..e649c20
--- /dev/null
@@ -0,0 +1,95 @@
+/***************************************************************************
+ *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
+ ***************************************************************************/
+
+#include "kdirectorycontentscounterworker.h"
+
+// Required includes for subItemsCount():
+#ifdef Q_WS_WIN
+    #include <QDir>
+#else
+    #include <dirent.h>
+    #include <QFile>
+#endif
+
+KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) :
+    QObject(parent)
+{
+    qRegisterMetaType<KDirectoryContentsCounterWorker::Options>();
+}
+
+int KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options)
+{
+    const bool countHiddenFiles = options & CountHiddenFiles;
+    const bool countDirectoriesOnly = options & CountDirectoriesOnly;
+
+#ifdef Q_WS_WIN
+    QDir dir(path);
+    QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System;
+    if (countHiddenFiles) {
+        filters |= QDir::Hidden;
+    }
+    if (countDirectoriesOnly) {
+        filters |= QDir::Dirs;
+    } else {
+        filters |= QDir::AllEntries;
+    }
+    return dir.entryList(filters).count();
+#else
+    // Taken from kdelibs/kio/kio/kdirmodel.cpp
+    // Copyright (C) 2006 David Faure <faure@kde.org>
+
+    int count = -1;
+    DIR* dir = ::opendir(QFile::encodeName(path));
+    if (dir) {  // krazy:exclude=syscalls
+        count = 0;
+        struct dirent *dirEntry = 0;
+        while ((dirEntry = ::readdir(dir))) {
+            if (dirEntry->d_name[0] == '.') {
+                if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) {
+                    // Skip "." or hidden files
+                    continue;
+                }
+                if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') {
+                    // Skip ".."
+                    continue;
+                }
+            }
+
+            // If only directories are counted, consider an unknown file type and links also
+            // as directory instead of trying to do an expensive stat()
+            // (see bugs 292642 and 299997).
+            const bool countEntry = !countDirectoriesOnly ||
+                                    dirEntry->d_type == DT_DIR ||
+                                    dirEntry->d_type == DT_LNK ||
+                                    dirEntry->d_type == DT_UNKNOWN;
+            if (countEntry) {
+                ++count;
+            }
+        }
+        ::closedir(dir);
+    }
+    return count;
+#endif
+}
+
+void KDirectoryContentsCounterWorker::countDirectoryContents(const QString& path, Options options)
+{
+    emit result(path, subItemsCount(path, options));
+}
diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.h b/src/kitemviews/private/kdirectorycontentscounterworker.h
new file mode 100644 (file)
index 0000000..96831ef
--- /dev/null
@@ -0,0 +1,71 @@
+/***************************************************************************
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
+ ***************************************************************************/
+
+#ifndef KDIRECTORYCONTENTENTSCOUNTERWORKER_H
+#define KDIRECTORYCONTENTENTSCOUNTERWORKER_H
+
+#include <QFlags>
+#include <QMetaType>
+#include <QObject>
+
+class QString;
+
+class KDirectoryContentsCounterWorker : public QObject
+{
+    Q_OBJECT
+
+public:
+    enum Option {
+        NoOptions = 0x0,
+        CountHiddenFiles = 0x1,
+        CountDirectoriesOnly = 0x2
+    };
+    Q_DECLARE_FLAGS(Options, Option)
+
+    explicit KDirectoryContentsCounterWorker(QObject* parent = 0);
+
+    /**
+     * Counts the items inside the directory \a path using the options
+     * \a options.
+     *
+     * @return The number of items.
+     */
+    static int subItemsCount(const QString& path, Options options);
+
+signals:
+    /**
+     * Signals that the directory \a path contains \a count items.
+     */
+    void result(const QString& path, int count);
+
+public slots:
+    /**
+     * Requests the number of items inside the directory \a path using the
+     * options \a options. The result is announced via the signal \a result.
+     */
+    // Note that the full type name KDirectoryContentsCounterWorker::Options
+    // is needed here. Just using 'Options' is OK for the compiler, but
+    // confuses moc.
+    void countDirectoryContents(const QString& path, KDirectoryContentsCounterWorker::Options options);
+};
+
+Q_DECLARE_METATYPE(KDirectoryContentsCounterWorker::Options)
+Q_DECLARE_OPERATORS_FOR_FLAGS(KDirectoryContentsCounterWorker::Options)
+
+#endif