File indexing completed on 2024-04-21 15:02:25

0001 /*
0002     SPDX-FileCopyrightText: 2016 Dan Leinir Turthra Jensen <admin@leinir.dk>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "quickengine.h"
0008 #include "quicksettings.h"
0009 
0010 #if KNEWSTUFFQUICK_BUILD_DEPRECATED_SINCE(5, 81)
0011 #include <KAuthorized>
0012 #endif
0013 #include <KLocalizedString>
0014 
0015 #include "categoriesmodel.h"
0016 #include "quickquestionlistener.h"
0017 #include "searchpresetmodel.h"
0018 
0019 #include "engine.h"
0020 
0021 class EnginePrivate
0022 {
0023 public:
0024     EnginePrivate()
0025         : engine(nullptr)
0026         , categoriesModel(nullptr)
0027         , searchPresetModel(nullptr)
0028     {
0029     }
0030     KNSCore::Engine *engine;
0031     bool isLoading{false};
0032     bool isValid{false};
0033     CategoriesModel *categoriesModel;
0034     SearchPresetModel *searchPresetModel;
0035     QString configFile;
0036 
0037 #if KNEWSTUFF_BUILD_DEPRECATED_SINCE(5, 82)
0038     KNSCore::EntryInternal::List changedEntries;
0039     static KNSCore::EntryWrapper *getChangedEntry(QQmlListProperty<KNSCore::EntryWrapper> *property, int i)
0040     {
0041         KNSCore::EntryWrapper *entry{nullptr};
0042         if (property) {
0043             auto d = static_cast<EnginePrivate *>(property->data);
0044             if (d) {
0045                 if (i >= 0 && i < d->changedEntries.count()) {
0046                     // Lifetime management for these objects should be done by the consumer,
0047                     // but are also parented for auto-delete on application shutdown
0048                     entry = new KNSCore::EntryWrapper(d->changedEntries[i], property->object);
0049                 }
0050             }
0051         }
0052         return entry;
0053     }
0054     static int getChangedEntriesCount(QQmlListProperty<KNSCore::EntryWrapper> *property)
0055     {
0056         int count{0};
0057         if (property) {
0058             auto d = static_cast<EnginePrivate *>(property->data);
0059             if (d) {
0060                 count = d->changedEntries.count();
0061             }
0062         }
0063         return count;
0064     }
0065 #endif
0066 };
0067 
0068 Engine::Engine(QObject *parent)
0069     : QObject(parent)
0070     , d(new EnginePrivate)
0071 {
0072 }
0073 
0074 Engine::~Engine() = default;
0075 
0076 #if KNEWSTUFFQUICK_BUILD_DEPRECATED_SINCE(5, 81)
0077 bool Engine::allowedByKiosk() const
0078 {
0079     return KAuthorized::authorize(KAuthorized::GHNS);
0080 }
0081 #endif
0082 
0083 QString Engine::configFile() const
0084 {
0085     return d->configFile;
0086 }
0087 
0088 void Engine::setConfigFile(const QString &newFile)
0089 {
0090     if (d->configFile != newFile) {
0091         d->isLoading = true;
0092         Q_EMIT isLoadingChanged();
0093         d->configFile = newFile;
0094         Q_EMIT configFileChanged();
0095 
0096         if (KNewStuffQuick::Settings::instance()->allowedByKiosk()) {
0097             if (!d->engine) {
0098                 d->engine = new KNSCore::Engine(this);
0099                 connect(d->engine, &KNSCore::Engine::signalProvidersLoaded, this, [=]() {
0100                     d->isLoading = false;
0101                     Q_EMIT isLoadingChanged();
0102                 });
0103                 connect(d->engine, &KNSCore::Engine::signalMessage, this, &Engine::message);
0104                 connect(d->engine, &KNSCore::Engine::busyStateChanged, this, [this]() {
0105                     if (!d->engine->busyState()) {
0106                         idleMessage(QString());
0107                     } else {
0108                         busyMessage(d->engine->busyMessage());
0109                     }
0110                 });
0111                 connect(d->engine,
0112                         &KNSCore::Engine::signalErrorCode,
0113                         this,
0114                         [=](const KNSCore::ErrorCode &theErrorCode, const QString &message, const QVariant &metadata) {
0115                             Q_EMIT errorCode(static_cast<ErrorCode>(theErrorCode), message, metadata);
0116                             if (theErrorCode == KNSCore::ProviderError) {
0117                                 // This means loading the providers file failed entirely and we cannot complete the
0118                                 // initialisation. It also means the engine is done loading, but that nothing will
0119                                 // work, and we need to inform the user of this.
0120                                 d->isLoading = false;
0121                                 Q_EMIT isLoadingChanged();
0122                             }
0123                             Q_EMIT errorMessage(message);
0124                         });
0125                 connect(d->engine,
0126                         &KNSCore::Engine::signalEntryEvent,
0127                         this,
0128                         [this](const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::EntryEvent event) {
0129                             KNSCore::EntryWrapper *wrappedEntry = new KNSCore::EntryWrapper(entry, this);
0130                             // Just forward the event but not do anything more
0131                             if (event != KNSCore::EntryInternal::StatusChangedEvent) {
0132                                 Q_EMIT entryEvent(wrappedEntry, (EntryEvent)event);
0133                                 return;
0134                             }
0135 
0136                             // We do not want to emit the entries changed signal for intermediate changed
0137                             // this would cause the KCMs to reload their view unnecessarily, BUG: 431568
0138                             if (entry.status() == KNS3::Entry::Installing || entry.status() == KNS3::Entry::Updating) {
0139                                 return;
0140                             }
0141                             Q_EMIT entryEvent(wrappedEntry, (EntryEvent)event);
0142 #if KNEWSTUFF_BUILD_DEPRECATED_SINCE(5, 82)
0143                             if (d->changedEntries.contains(entry)) {
0144                                 d->changedEntries.removeAll(entry);
0145                             }
0146                             d->changedEntries << entry;
0147                             Q_EMIT changedEntriesChanged();
0148 #endif
0149                         });
0150                 Q_EMIT engineChanged();
0151                 KNewStuffQuick::QuickQuestionListener::instance();
0152                 d->categoriesModel = new CategoriesModel(this);
0153                 Q_EMIT categoriesChanged();
0154                 d->searchPresetModel = new SearchPresetModel(this);
0155                 Q_EMIT searchPresetModelChanged();
0156                 // And finally, let's just make sure we don't miss out the various things here getting changed
0157                 // In other words, when we're asked to reset the view, actually do that
0158                 connect(d->engine, &KNSCore::Engine::signalResetView, this, &Engine::categoriesFilterChanged);
0159                 connect(d->engine, &KNSCore::Engine::signalResetView, this, &Engine::filterChanged);
0160                 connect(d->engine, &KNSCore::Engine::signalResetView, this, &Engine::sortOrderChanged);
0161                 connect(d->engine, &KNSCore::Engine::signalResetView, this, &Engine::searchTermChanged);
0162                 Q_EMIT categoriesFilterChanged();
0163                 Q_EMIT filterChanged();
0164                 Q_EMIT sortOrderChanged();
0165                 Q_EMIT searchTermChanged();
0166             }
0167             d->isValid = d->engine->init(d->configFile);
0168             Q_EMIT engineInitialized();
0169         } else {
0170             // This is not an error message in the proper sense, and the message is not intended to look like an error (as there is really
0171             // nothing the user can do to fix it, and we just tell them so they're not wondering what's wrong)
0172             Q_EMIT message(
0173                 i18nc("An informational message which is shown to inform the user they are not authorized to use GetHotNewStuff functionality",
0174                       "You are not authorized to Get Hot New Stuff. If you think this is in error, please contact the person in charge of your permissions."));
0175         }
0176     }
0177 }
0178 
0179 QObject *Engine::engine() const
0180 {
0181     return d->engine;
0182 }
0183 
0184 bool Engine::isLoading() const
0185 {
0186     return d->isLoading;
0187 }
0188 
0189 bool Engine::hasAdoptionCommand() const
0190 {
0191     if (d->engine) {
0192         return d->engine->hasAdoptionCommand();
0193     }
0194     return false;
0195 }
0196 
0197 QString Engine::name() const
0198 {
0199     if (d->engine) {
0200         return d->engine->name();
0201     }
0202     return QString{};
0203 }
0204 
0205 QObject *Engine::categories() const
0206 {
0207     return d->categoriesModel;
0208 }
0209 
0210 QStringList Engine::categoriesFilter() const
0211 {
0212     if (d->engine) {
0213         return d->engine->categoriesFilter();
0214     }
0215     return QStringList{};
0216 }
0217 
0218 void Engine::setCategoriesFilter(const QStringList &newCategoriesFilter)
0219 {
0220     if (d->engine) {
0221         // This ensures that if we somehow end up with any empty entries (such as the default
0222         // option in the categories dropdowns), our list will remain empty.
0223         QStringList filter{newCategoriesFilter};
0224         filter.removeAll({});
0225         if (d->engine->categoriesFilter() != filter) {
0226             d->engine->setCategoriesFilter(filter);
0227             Q_EMIT categoriesFilterChanged();
0228         }
0229     }
0230 }
0231 
0232 void Engine::resetCategoriesFilter()
0233 {
0234     if (d->engine) {
0235         d->engine->setCategoriesFilter(d->engine->categories());
0236     }
0237 }
0238 
0239 int Engine::filter() const
0240 {
0241     if (d->engine) {
0242         return d->engine->filter();
0243     }
0244     return 0;
0245 }
0246 
0247 void Engine::setFilter(int newFilter)
0248 {
0249     if (d->engine && d->engine->filter() != newFilter) {
0250         d->engine->setFilter(static_cast<KNSCore::Provider::Filter>(newFilter));
0251         Q_EMIT filterChanged();
0252     }
0253 }
0254 
0255 int Engine::sortOrder() const
0256 {
0257     if (d->engine) {
0258         return d->engine->sortMode();
0259     }
0260     return 0;
0261 }
0262 
0263 void Engine::setSortOrder(int newSortOrder)
0264 {
0265     if (d->engine && d->engine->sortMode() != newSortOrder) {
0266         d->engine->setSortMode(static_cast<KNSCore::Provider::SortMode>(newSortOrder));
0267         Q_EMIT sortOrderChanged();
0268     }
0269 }
0270 
0271 QString Engine::searchTerm() const
0272 {
0273     if (d->engine) {
0274         return d->engine->searchTerm();
0275     }
0276     return QString{};
0277 }
0278 
0279 void Engine::setSearchTerm(const QString &newSearchTerm)
0280 {
0281     if (d->engine && d->isValid && d->engine->searchTerm() != newSearchTerm) {
0282         d->engine->setSearchTerm(newSearchTerm);
0283         Q_EMIT searchTermChanged();
0284     }
0285 }
0286 
0287 QObject *Engine::searchPresetModel() const
0288 {
0289     return d->searchPresetModel;
0290 }
0291 
0292 void Engine::resetSearchTerm()
0293 {
0294     setSearchTerm(QString{});
0295 }
0296 
0297 #if KNEWSTUFF_BUILD_DEPRECATED_SINCE(5, 82)
0298 QQmlListProperty<KNSCore::EntryWrapper> Engine::changedEntries()
0299 {
0300     return QQmlListProperty<KNSCore::EntryWrapper>(this, d.get(), &EnginePrivate::getChangedEntriesCount, &EnginePrivate::getChangedEntry);
0301 }
0302 
0303 int Engine::changedEntriesCount() const
0304 {
0305     return d->changedEntries.count();
0306 }
0307 
0308 void Engine::resetChangedEntries()
0309 {
0310     if (!d->changedEntries.isEmpty()) {
0311         d->changedEntries.clear();
0312         Q_EMIT changedEntriesChanged();
0313     }
0314 }
0315 #endif
0316 
0317 bool Engine::isValid()
0318 {
0319     return d->isValid;
0320 }
0321 
0322 #include "moc_quickengine.cpp"