File indexing completed on 2024-05-05 05:29:11
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "AbstractAppsModel.h" 0008 0009 #include "discover_debug.h" 0010 #include <KConfigGroup> 0011 #include <KIO/StoredTransferJob> 0012 #include <KSharedConfig> 0013 #include <QDir> 0014 #include <QFile> 0015 #include <QJsonArray> 0016 #include <QJsonDocument> 0017 #include <QStandardPaths> 0018 #include <QtGlobal> 0019 0020 #include <libdiscover_debug.h> 0021 #include <resources/ResourcesModel.h> 0022 #include <resources/StoredResultsStream.h> 0023 #include <utils.h> 0024 0025 class BestInResultsStream : public QObject 0026 { 0027 Q_OBJECT 0028 public: 0029 BestInResultsStream(const QSet<ResultsStream *> &streams) 0030 : QObject() 0031 { 0032 connect(this, &BestInResultsStream::finished, this, &QObject::deleteLater); 0033 Q_ASSERT(!streams.contains(nullptr)); 0034 if (streams.isEmpty()) { 0035 QTimer::singleShot(0, this, &BestInResultsStream::clear); 0036 } 0037 0038 for (auto stream : streams) { 0039 m_streams.insert(stream); 0040 connect(stream, &ResultsStream::resourcesFound, this, [this](const QVector<StreamResult> &resources) { 0041 m_resources.append(resources.constFirst()); 0042 }); 0043 connect(stream, &QObject::destroyed, this, &BestInResultsStream::streamDestruction); 0044 } 0045 } 0046 0047 void streamDestruction(QObject *obj) 0048 { 0049 m_streams.remove(obj); 0050 clear(); 0051 } 0052 0053 void clear() 0054 { 0055 if (m_streams.isEmpty()) { 0056 Q_EMIT finished(m_resources); 0057 } 0058 } 0059 0060 Q_SIGNALS: 0061 void finished(QVector<StreamResult> resources); 0062 0063 private: 0064 QVector<StreamResult> m_resources; 0065 QSet<QObject *> m_streams; 0066 }; 0067 0068 AbstractAppsModel::AbstractAppsModel() 0069 { 0070 connect(ResourcesModel::global(), &ResourcesModel::currentApplicationBackendChanged, this, &AbstractAppsModel::refreshCurrentApplicationBackend); 0071 refreshCurrentApplicationBackend(); 0072 } 0073 0074 void AbstractAppsModel::refreshCurrentApplicationBackend() 0075 { 0076 auto backend = ResourcesModel::global()->currentApplicationBackend(); 0077 if (m_backend == backend) 0078 return; 0079 0080 if (m_backend) { 0081 disconnect(m_backend, &AbstractResourcesBackend::fetchingChanged, this, &AbstractAppsModel::refresh); 0082 disconnect(m_backend, &AbstractResourcesBackend::resourceRemoved, this, &AbstractAppsModel::removeResource); 0083 } 0084 0085 m_backend = backend; 0086 0087 if (backend) { 0088 connect(backend, &AbstractResourcesBackend::fetchingChanged, this, &AbstractAppsModel::refresh); 0089 connect(backend, &AbstractResourcesBackend::resourceRemoved, this, &AbstractAppsModel::removeResource); 0090 } 0091 0092 Q_EMIT currentApplicationBackendChanged(m_backend); 0093 } 0094 0095 void AbstractAppsModel::setUris(const QVector<QUrl> &uris) 0096 { 0097 if (!m_backend) 0098 return; 0099 0100 if (m_uris == uris) { 0101 return; 0102 } 0103 m_uris = uris; 0104 0105 QSet<ResultsStream *> streams; 0106 for (const auto &uri : uris) { 0107 AbstractResourcesBackend::Filters filter; 0108 filter.resourceUrl = uri; 0109 streams << m_backend->search(filter); 0110 } 0111 if (!streams.isEmpty()) { 0112 auto stream = new BestInResultsStream(streams); 0113 acquireFetching(true); 0114 connect(stream, &BestInResultsStream::finished, this, &AbstractAppsModel::setResources); 0115 } 0116 } 0117 0118 static void filterDupes(QVector<StreamResult> &resources) 0119 { 0120 QSet<QString> found; 0121 for (auto it = resources.begin(); it != resources.end();) { 0122 auto id = it->resource->appstreamId(); 0123 if (found.contains(id)) { 0124 it = resources.erase(it); 0125 } else { 0126 found.insert(id); 0127 ++it; 0128 } 0129 } 0130 } 0131 0132 void AbstractAppsModel::acquireFetching(bool f) 0133 { 0134 if (f) 0135 m_isFetching++; 0136 else 0137 m_isFetching--; 0138 0139 if ((!f && m_isFetching == 0) || (f && m_isFetching == 1)) { 0140 Q_EMIT isFetchingChanged(); 0141 } 0142 Q_ASSERT(m_isFetching >= 0); 0143 } 0144 0145 void AbstractAppsModel::setResources(const QVector<StreamResult> &_resources) 0146 { 0147 auto resources = _resources; 0148 filterDupes(resources); 0149 0150 if (m_resources != resources) { 0151 // TODO: sort like in the json files 0152 beginResetModel(); 0153 m_resources = resources; 0154 endResetModel(); 0155 Q_EMIT appsCountChanged(); 0156 } 0157 0158 acquireFetching(false); 0159 } 0160 0161 void AbstractAppsModel::removeResource(AbstractResource *resource) 0162 { 0163 int index = m_resources.indexOf(resource); 0164 if (index < 0) 0165 return; 0166 0167 beginRemoveRows({}, index, index); 0168 m_resources.removeAt(index); 0169 endRemoveRows(); 0170 } 0171 0172 QVariant AbstractAppsModel::data(const QModelIndex &index, int role) const 0173 { 0174 if (!index.isValid() || role != Qt::UserRole) 0175 return {}; 0176 0177 auto res = m_resources.value(index.row()).resource; 0178 if (!res) 0179 return {}; 0180 0181 return QVariant::fromValue<QObject *>(res); 0182 } 0183 0184 int AbstractAppsModel::rowCount(const QModelIndex &parent) const 0185 { 0186 return parent.isValid() ? 0 : m_resources.count(); 0187 } 0188 0189 QHash<int, QByteArray> AbstractAppsModel::roleNames() const 0190 { 0191 return {{Qt::UserRole, "application"}}; 0192 } 0193 0194 #include "AbstractAppsModel.moc"