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"