File indexing completed on 2024-05-12 15:51:16
0001 // SPDX-FileCopyrightText: 2015 Dan Leinir Turthra Jensen <admin@leinir.dk> 0002 // SPDX-License-Identifier: LGPL-2.1-only or LGPL-3.0-only or LicenseRef-KDE-Accepted-LGPL 0003 0004 #include "contentlist.h" 0005 #ifdef HAVE_BALOO 0006 #include "baloocontentlister.h" 0007 #endif 0008 #include "filesystemcontentlister.h" 0009 #include "manualcontentlister.h" 0010 0011 #include <QDebug> 0012 #include <QMimeDatabase> 0013 #include <QSet> 0014 #include <QTimer> 0015 #include <QUrl> 0016 0017 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0018 using countsize = int; 0019 #else 0020 using countsize = qsizetype; 0021 #endif 0022 0023 struct ContentEntry { 0024 QString filename; 0025 QUrl filePath; 0026 QVariantMap metadata; 0027 }; 0028 0029 class ContentList::Private 0030 { 0031 public: 0032 typedef QQmlListProperty<ContentQuery> QueryListProperty; 0033 0034 Private() 0035 : actualContentList(nullptr) 0036 , manualContentLister(new ManualContentLister()) 0037 { 0038 } 0039 QList<ContentEntry *> entries; 0040 ContentListerBase *actualContentList; 0041 ManualContentLister *manualContentLister; 0042 0043 QList<ContentQuery *> queries; 0044 QueryListProperty listProperty; 0045 0046 QSet<QString> knownFiles; 0047 0048 bool autoSearch = false; 0049 bool cacheResults = false; 0050 bool completed = false; 0051 0052 static void appendToList(QueryListProperty *property, ContentQuery *value); 0053 static ContentQuery *listValueAt(QueryListProperty *property, countsize index); 0054 static void clearList(QueryListProperty *property); 0055 static countsize countList(QueryListProperty *property); 0056 static void removeLast(QueryListProperty *property); 0057 static void replace(QueryListProperty *property, countsize index, ContentQuery *value); 0058 0059 static QStringList cachedFiles; 0060 }; 0061 0062 QStringList ContentList::Private::cachedFiles; 0063 0064 ContentList::ContentList(QObject *parent) 0065 : QAbstractListModel(parent) 0066 , d(new Private) 0067 { 0068 #ifdef HAVE_BALOO 0069 auto baloo = new BalooContentLister(this); 0070 if (baloo->balooEnabled()) { 0071 d->actualContentList = baloo; 0072 } else { 0073 baloo->deleteLater(); 0074 d->actualContentList = new FilesystemContentLister(this); 0075 } 0076 #else 0077 d->actualContentList = new FilesystemContentLister(this); 0078 #endif 0079 0080 connect(d->actualContentList, &ContentListerBase::fileFound, this, &ContentList::fileFound); 0081 connect(d->actualContentList, &ContentListerBase::searchCompleted, this, &ContentList::searchCompleted); 0082 0083 connect(d->manualContentLister, &ContentListerBase::fileFound, this, &ContentList::fileFound); 0084 connect(d->manualContentLister, &ContentListerBase::searchCompleted, this, &ContentList::searchCompleted); 0085 0086 d->listProperty = QQmlListProperty<ContentQuery> 0087 { 0088 this, &d->queries, &ContentList::Private::appendToList, &ContentList::Private::countList, &ContentList::Private::listValueAt, 0089 &ContentList::Private::clearList, 0090 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0091 &ContentList::Private::replace, &ContentList::Private::removeLast, 0092 #endif 0093 }; 0094 } 0095 0096 ContentList::~ContentList() = default; 0097 0098 QQmlListProperty<ContentQuery> ContentList::queries() 0099 { 0100 return d->listProperty; 0101 } 0102 0103 bool ContentList::autoSearch() const 0104 { 0105 return d->autoSearch; 0106 } 0107 0108 bool ContentList::cacheResults() const 0109 { 0110 return d->cacheResults; 0111 } 0112 0113 QString ContentList::getMimetype(const QString &filePath) 0114 { 0115 QMimeDatabase db; 0116 const QMimeType mime = db.mimeTypeForFile(filePath); 0117 return mime.name(); 0118 } 0119 0120 void ContentList::startSearch() 0121 { 0122 QTimer::singleShot(1, this, [this]() { 0123 Q_EMIT searchStarted(); 0124 qWarning() << "search started"; 0125 d->actualContentList->knownFiles = d->knownFiles; 0126 d->actualContentList->startSearch(d->queries); 0127 d->manualContentLister->startSearch(d->queries); 0128 }); 0129 } 0130 0131 void ContentList::fileFound(const QString &filePath, const QVariantMap &metaData) 0132 { 0133 if (d->knownFiles.contains(filePath)) 0134 return; 0135 0136 auto fileUrl = QUrl::fromLocalFile(filePath); 0137 0138 ContentEntry *entry = new ContentEntry(); 0139 entry->filename = fileUrl.fileName(); 0140 entry->filePath = fileUrl; 0141 entry->metadata = metaData; 0142 0143 int newRow = d->entries.count(); 0144 beginInsertRows({}, newRow, newRow); 0145 d->entries.append(entry); 0146 d->knownFiles.insert(filePath); 0147 endInsertRows(); 0148 0149 if (d->cacheResults) { 0150 Private::cachedFiles.append(filePath); 0151 } 0152 } 0153 0154 void ContentList::setAutoSearch(bool autoSearch) 0155 { 0156 if (autoSearch == d->autoSearch) 0157 return; 0158 0159 d->autoSearch = autoSearch; 0160 Q_EMIT autoSearchChanged(); 0161 } 0162 0163 void ContentList::setCacheResults(bool cacheResults) 0164 { 0165 if (cacheResults == d->cacheResults) 0166 return; 0167 0168 d->cacheResults = cacheResults; 0169 0170 if (d->cacheResults && d->completed && !Private::cachedFiles.isEmpty()) { 0171 setKnownFiles(Private::cachedFiles); 0172 } 0173 0174 Q_EMIT cacheResultsChanged(); 0175 } 0176 0177 void ContentList::setKnownFiles(const QStringList &results) 0178 { 0179 beginResetModel(); 0180 d->entries.clear(); 0181 d->knownFiles.clear(); 0182 for (const auto &result : results) { 0183 auto entry = new ContentEntry{}; 0184 auto url = QUrl::fromLocalFile(result); 0185 0186 entry->filename = url.fileName(); 0187 entry->filePath = url; 0188 entry->metadata = ContentListerBase::metaDataForFile(result); 0189 0190 d->entries.append(entry); 0191 d->knownFiles.insert(result); 0192 } 0193 endResetModel(); 0194 } 0195 0196 QHash<int, QByteArray> ContentList::roleNames() const 0197 { 0198 return { 0199 {FilenameRole, "filename"}, 0200 {FilePathRole, "filePath"}, 0201 {MetadataRole, "metadata"}, 0202 }; 0203 } 0204 0205 QVariant ContentList::data(const QModelIndex &index, int role) const 0206 { 0207 if (!index.isValid() || index.row() <= -1 || index.row() >= d->entries.count()) { 0208 return {}; 0209 } 0210 0211 const ContentEntry *entry = d->entries[index.row()]; 0212 switch (role) { 0213 case FilenameRole: 0214 return entry->filename; 0215 case FilePathRole: 0216 return entry->filePath; 0217 case MetadataRole: 0218 return entry->metadata; 0219 } 0220 return {}; 0221 } 0222 0223 int ContentList::rowCount(const QModelIndex &parent) const 0224 { 0225 if (parent.isValid()) 0226 return 0; 0227 return d->entries.count(); 0228 } 0229 0230 void ContentList::classBegin() 0231 { 0232 } 0233 0234 void ContentList::componentComplete() 0235 { 0236 d->completed = true; 0237 0238 if (d->cacheResults && !Private::cachedFiles.isEmpty()) 0239 setKnownFiles(Private::cachedFiles); 0240 0241 if (d->autoSearch) 0242 d->actualContentList->startSearch(d->queries); 0243 } 0244 0245 bool ContentList::isComplete() const 0246 { 0247 return d->completed; 0248 } 0249 0250 void ContentList::Private::appendToList(Private::QueryListProperty *property, ContentQuery *value) 0251 { 0252 auto list = static_cast<QList<ContentQuery *> *>(property->data); 0253 auto model = static_cast<ContentList *>(property->object); 0254 list->append(value); 0255 if (model->autoSearch() && model->isComplete()) 0256 model->startSearch(); 0257 } 0258 0259 ContentQuery *ContentList::Private::listValueAt(Private::QueryListProperty *property, countsize index) 0260 { 0261 return static_cast<QList<ContentQuery *> *>(property->data)->at(index); 0262 } 0263 0264 countsize ContentList::Private::countList(Private::QueryListProperty *property) 0265 { 0266 return static_cast<QList<ContentQuery *> *>(property->data)->size(); 0267 } 0268 0269 void ContentList::Private::clearList(Private::QueryListProperty *property) 0270 { 0271 auto list = static_cast<QList<ContentQuery *> *>(property->data); 0272 auto model = static_cast<ContentList *>(property->object); 0273 model->beginResetModel(); 0274 list->clear(); 0275 model->endResetModel(); 0276 } 0277 0278 void ContentList::Private::removeLast(QueryListProperty *property) 0279 { 0280 auto list = static_cast<QList<ContentQuery *> *>(property->data); 0281 auto model = static_cast<ContentList *>(property->object); 0282 list->removeLast(); 0283 if (model->autoSearch() && model->isComplete()) { 0284 model->startSearch(); 0285 } 0286 } 0287 0288 void ContentList::Private::replace(QueryListProperty *property, countsize index, ContentQuery *value) 0289 { 0290 auto list = static_cast<QList<ContentQuery *> *>(property->data); 0291 auto model = static_cast<ContentList *>(property->object); 0292 list->replace(index, value); 0293 if (model->autoSearch() && model->isComplete()) { 0294 model->startSearch(); 0295 } 0296 } 0297 0298 void ContentList::addFile(const QUrl &filePath) 0299 { 0300 d->manualContentLister->addFile(filePath); 0301 } 0302 0303 #include "moc_contentlist.cpp"