Changeset View
Changeset View
Standalone View
Standalone View
src/contentlist/FilesystemContentLister.cpp
Show All 24 Lines | |||||
25 | 25 | | |||
26 | #include <QCoreApplication> | 26 | #include <QCoreApplication> | ||
27 | #include <QDateTime> | 27 | #include <QDateTime> | ||
28 | #include <QDebug> | 28 | #include <QDebug> | ||
29 | #include <QDirIterator> | 29 | #include <QDirIterator> | ||
30 | #include <QMimeDatabase> | 30 | #include <QMimeDatabase> | ||
31 | #include <QTimer> | 31 | #include <QTimer> | ||
32 | #include <QVariantHash> | 32 | #include <QVariantHash> | ||
33 | #include <QThreadPool> | ||||
34 | #include <QRunnable> | ||||
35 | | ||||
36 | #include "ContentQuery.h" | ||||
37 | | ||||
38 | class FileSystemSearcher : public QObject, public QRunnable | ||||
39 | { | ||||
40 | Q_OBJECT | ||||
41 | public: | ||||
42 | FileSystemSearcher(ContentQuery* query) : QObject() { m_query = query; } | ||||
43 | | ||||
44 | void run() override | ||||
45 | { | ||||
46 | QMimeDatabase mimeDb; | ||||
47 | | ||||
48 | auto locations = m_query->locations(); | ||||
49 | if(locations.isEmpty()) | ||||
50 | locations.append(QDir::homePath()); | ||||
51 | | ||||
52 | for(const auto& location : m_query->locations()) | ||||
53 | { | ||||
54 | QDirIterator it(location, QDirIterator::Subdirectories); | ||||
55 | while (it.hasNext()) | ||||
56 | { | ||||
57 | auto filePath = it.next(); | ||||
58 | | ||||
59 | if(it.fileInfo().isDir()) | ||||
60 | continue; | ||||
61 | | ||||
62 | QString mimeType = mimeDb.mimeTypeForFile(filePath).name(); | ||||
63 | if(!m_query->mimeTypes().isEmpty() && !m_query->mimeTypes().contains(mimeType)) | ||||
64 | continue; | ||||
65 | | ||||
66 | auto metadata = ContentListerBase::metaDataForFile(filePath); | ||||
67 | | ||||
68 | emit fileFound(filePath, metadata); | ||||
69 | } | ||||
70 | } | ||||
71 | | ||||
72 | emit finished(this); | ||||
73 | } | ||||
74 | | ||||
75 | Q_SIGNALS: | ||||
76 | void fileFound(const QString& path, const QVariantMap& metaData); | ||||
77 | void finished(FileSystemSearcher* searcher); | ||||
78 | | ||||
79 | private: | ||||
80 | ContentQuery* m_query; | ||||
81 | }; | ||||
33 | 82 | | |||
34 | class FilesystemContentLister::Private | 83 | class FilesystemContentLister::Private | ||
35 | { | 84 | { | ||
36 | public: | 85 | public: | ||
37 | Private() {} | 86 | Private() { } | ||
38 | QString searchString; | 87 | | ||
39 | QStringList knownFiles; | 88 | QList<FileSystemSearcher*> runnables; | ||
40 | QStringList locations; | | |||
41 | QStringList mimetypes; | | |||
42 | }; | 89 | }; | ||
43 | 90 | | |||
44 | FilesystemContentLister::FilesystemContentLister(QObject* parent) | 91 | FilesystemContentLister::FilesystemContentLister(QObject* parent) | ||
45 | : ContentListerBase(parent) | 92 | : ContentListerBase(parent) | ||
46 | , d(new Private) | 93 | , d(new Private) | ||
47 | { | 94 | { | ||
95 | | ||||
48 | } | 96 | } | ||
49 | 97 | | |||
50 | FilesystemContentLister::~FilesystemContentLister() | 98 | FilesystemContentLister::~FilesystemContentLister() | ||
51 | { | 99 | { | ||
100 | QThreadPool::globalInstance()->waitForDone(); | ||||
52 | delete d; | 101 | delete d; | ||
53 | } | 102 | } | ||
54 | 103 | | |||
55 | void FilesystemContentLister::addLocation(QString path) | 104 | void FilesystemContentLister::startSearch(const QList<ContentQuery*>& queries) | ||
56 | { | 105 | { | ||
57 | d->locations.append(path); | 106 | for(const auto& query : queries) | ||
58 | } | | |||
59 | | ||||
60 | void FilesystemContentLister::addMimetype(QString mimetype) | | |||
61 | { | 107 | { | ||
62 | d->mimetypes.append(mimetype); | 108 | auto runnable = new FileSystemSearcher{query}; | ||
63 | } | 109 | connect(runnable, &FileSystemSearcher::fileFound, this, &FilesystemContentLister::fileFound); | ||
110 | connect(runnable, &FileSystemSearcher::finished, this, &FilesystemContentLister::queryFinished); | ||||
64 | 111 | | |||
65 | void FilesystemContentLister::setSearchString(const QString& searchString) | 112 | d->runnables.append(runnable); | ||
66 | { | | |||
67 | d->searchString = searchString; | | |||
68 | } | 113 | } | ||
69 | 114 | | |||
70 | void FilesystemContentLister::setKnownFiles(QStringList knownFiles) | 115 | if(!d->runnables.isEmpty()) | ||
71 | { | 116 | QThreadPool::globalInstance()->start(d->runnables.first()); | ||
72 | d->knownFiles = knownFiles; | | |||
73 | } | 117 | } | ||
74 | 118 | | |||
75 | void FilesystemContentLister::startSearch() | 119 | void FilesystemContentLister::queryFinished(QRunnable* runnable) | ||
76 | { | 120 | { | ||
77 | QMimeDatabase mimeDb; | 121 | d->runnables.removeAll(static_cast<FileSystemSearcher*>(runnable)); | ||
78 | bool useThis(false); | | |||
79 | | ||||
80 | qDebug() << "Searching in" << d->locations; | | |||
81 | Q_FOREACH(const QString& folder, d->locations) | | |||
82 | { | | |||
83 | QDirIterator it(folder, QDirIterator::Subdirectories); | | |||
84 | while (it.hasNext()) | | |||
85 | { | | |||
86 | QString filePath = it.next(); | | |||
87 | if(d->knownFiles.contains(filePath)) { | | |||
88 | continue; | | |||
89 | } | | |||
90 | 122 | | |||
91 | QFileInfo info(filePath); | 123 | if(!d->runnables.isEmpty()) | ||
92 | | ||||
93 | if(info.isDir()) | | |||
94 | { | 124 | { | ||
95 | qApp->processEvents(); | 125 | QThreadPool::globalInstance()->start(d->runnables.first()); | ||
96 | continue; | | |||
97 | } | 126 | } | ||
98 | useThis = false; | 127 | else | ||
99 | QString mimetype = mimeDb.mimeTypeForFile(filePath, QMimeDatabase::MatchExtension).name(); | | |||
100 | // qDebug() << useThis << mimetype << filePath; | | |||
101 | Q_FOREACH(const QString& type, d->mimetypes) | | |||
102 | { | 128 | { | ||
103 | if(type == mimetype) { | 129 | emit searchCompleted(); | ||
104 | useThis = true; | | |||
105 | break; | | |||
106 | } | | |||
107 | } | | |||
108 | | ||||
109 | if(useThis) | | |||
110 | { | | |||
111 | QVariantHash metadata; | | |||
112 | metadata["created"] = info.created(); | | |||
113 | | ||||
114 | KFileMetaData::UserMetaData data(filePath); | | |||
115 | if (data.hasAttribute("peruse.currentPage")) { | | |||
116 | int currentPage = data.attribute("peruse.currentPage").toInt(); | | |||
117 | metadata["currentPage"] = QVariant::fromValue<int>(currentPage); | | |||
118 | } | 130 | } | ||
119 | if (data.hasAttribute("peruse.totalPages")) { | | |||
120 | int totalPages = data.attribute("peruse.totalPages").toInt(); | | |||
121 | metadata["totalPages"] = QVariant::fromValue<int>(totalPages); | | |||
122 | } | 131 | } | ||
123 | 132 | | |||
124 | emit fileFound(filePath, metadata); | 133 | // This needs to be included since we define a QObject subclass here in the C++ file. | ||
125 | } | 134 | #include "FilesystemContentLister.moc" | ||
126 | qApp->processEvents(); | | |||
127 | } | | |||
128 | } | | |||
129 | // This ensures that, should we decide to search more stuff later, we can do so granularly | | |||
130 | d->locations.clear(); | | |||
131 | | ||||
132 | // Not entirely happy about this, but it makes things not break... | | |||
133 | // Previously, the welcome page in Peruse would end up unpopulated because a signal | | |||
134 | // was unreceived from the main window upon search completion (and consequently | | |||
135 | // application readiness) | | |||
136 | QTimer::singleShot(0, this, SIGNAL(searchCompleted())); | | |||
137 | } | |