diff --git a/libdiscover/backends/DummyBackend/DummyResource.h b/libdiscover/backends/DummyBackend/DummyResource.h index 6e5a5761..76c90187 100644 --- a/libdiscover/backends/DummyBackend/DummyResource.h +++ b/libdiscover/backends/DummyBackend/DummyResource.h @@ -1,74 +1,76 @@ /*************************************************************************** * Copyright © 2013 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifndef DUMMYRESOURCE_H #define DUMMYRESOURCE_H #include class AddonList; class DummyResource : public AbstractResource { Q_OBJECT public: explicit DummyResource(QString name, bool isTechnical, AbstractResourcesBackend* parent); QList addonsInformation() override; QString section() override; QString origin() const override; QString longDescription() override; QString availableVersion() const override; QString installedVersion() const override; QString license() override; int size() override; QUrl homepage() override; QUrl helpURL() override; QUrl bugURL() override; QUrl donationURL() override; QStringList categories() override; AbstractResource::State state() override; QVariant icon() const override; QString comment() override; QString name() override; QString packageName() const override; bool isTechnical() const override { return m_isTechnical; } bool canExecute() const override { return true; } void invokeApplication() const override; void fetchChangelog() override; void fetchScreenshots() override; QUrl url() const override; void setState(State state); void setSize(int size) { m_size = size; } void setAddons(const AddonList& addons); void setAddonInstalled(const QString& addon, bool installed); + QString sourceIcon() const override { return QStringLiteral("player-time"); } + public: QString m_name; AbstractResource::State m_state; QList m_screenshots; QList m_screenshotThumbnails; QString m_iconName; QList m_addons; bool m_isTechnical; int m_size; }; #endif // DUMMYRESOURCE_H diff --git a/libdiscover/backends/FlatpakBackend/FlatpakResource.h b/libdiscover/backends/FlatpakBackend/FlatpakResource.h index 98b29bce..ac5af438 100644 --- a/libdiscover/backends/FlatpakBackend/FlatpakResource.h +++ b/libdiscover/backends/FlatpakBackend/FlatpakResource.h @@ -1,160 +1,161 @@ /*************************************************************************** * Copyright © 2013 Aleix Pol Gonzalez * * Copyright © 2017 Jan Grulich * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifndef FLATPAKRESOURCE_H #define FLATPAKRESOURCE_H #include extern "C" { #include } #include #include class AddonList; class FlatpakBackend; class FlatpakResource : public AbstractResource { Q_OBJECT public: explicit FlatpakResource(const AppStream::Component &component, FlatpakInstallation* installation, FlatpakBackend *parent); enum PropertyKind { DownloadSize = 0, InstalledSize, RequiredRuntime }; enum PropertyState { NotKnownYet = 0, AlreadyKnown, UnknownOrFailed, }; enum ResourceType { DesktopApp = 0, Runtime, Source }; static QString typeAsString(ResourceType type) { if (type == DesktopApp) { return QLatin1String("app"); } return QLatin1String("runtime"); } QString installationPath() const; static QString installationPath(FlatpakInstallation* installation); AppStream::Component appstreamComponent() const; QList addonsInformation() override; QString availableVersion() const override; QString appstreamId() const override; QString arch() const; QString branch() const; bool canExecute() const override; QStringList categories() override; QString comment() override; QString commit() const; int downloadSize() const; QVariant icon() const override; QString installedVersion() const override; int installedSize() const; bool isTechnical() const override; QUrl homepage() override; QUrl helpURL() override; QUrl bugURL() override; QUrl donationURL() override; QString flatpakFileType() const; QString flatpakName() const; QString license() override; QString longDescription() override; QString name() override; QString origin() const override; QString packageName() const override; PropertyState propertyState(PropertyKind kind) const; QUrl resourceFile() const; QString runtime() const; QString section() override; int size() override; QString sizeDescription() override; AbstractResource::State state() override; ResourceType type() const; QString typeAsString() const; QString uniqueId() const; QUrl url() const override; FlatpakInstallation* installation() const { return m_installation; } void invokeApplication() const override; void fetchChangelog() override; void fetchScreenshots() override; void setArch(const QString &arch); void setBranch(const QString &branch); void setBundledIcon(const QPixmap &pixmap); void setCommit(const QString &commit); void setDownloadSize(int size); void setIconPath(const QString &path); void setInstalledSize(int size); void setFlatpakFileType(const QString &fileType); void setFlatpakName(const QString &name); void setOrigin(const QString &origin); void setPropertyState(PropertyKind kind, PropertyState state); void setResourceFile(const QUrl &url); void setRuntime(const QString &runtime); void setState(State state); void setType(ResourceType type); // void setAddons(const AddonList& addons); // void setAddonInstalled(const QString& addon, bool installed); void updateFromRef(FlatpakRef* ref); + QString sourceIcon() const override { return QStringLiteral("https://flatpak.org/img/logo.svg"); } Q_SIGNALS: void propertyStateChanged(PropertyKind kind, PropertyState state); public: QList m_addons; const AppStream::Component m_appdata; FlatpakRefKind m_flatpakRefKind; QString m_arch; QString m_branch; QPixmap m_bundledIcon; QString m_commit; int m_downloadSize; QString m_flatpakFileType; QString m_flatpakName; QString m_iconPath; int m_installedSize; QString m_origin; QHash m_propertyStates; QUrl m_resourceFile; QString m_runtime; FlatpakInstallation* const m_installation; AbstractResource::State m_state; ResourceType m_type; }; #endif // FLATPAKRESOURCE_H diff --git a/libdiscover/backends/KNSBackend/KNSResource.h b/libdiscover/backends/KNSBackend/KNSResource.h index 159d1ebd..37e66846 100644 --- a/libdiscover/backends/KNSBackend/KNSResource.h +++ b/libdiscover/backends/KNSBackend/KNSResource.h @@ -1,75 +1,76 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifndef KNSRESOURCE_H #define KNSRESOURCE_H #include #include #include #include "discovercommon_export.h" class KNSBackend; class DISCOVERCOMMON_EXPORT KNSResource : public AbstractResource { Q_OBJECT public: explicit KNSResource(const KNSCore::EntryInternal & c, QStringList categories, KNSBackend* parent); ~KNSResource() override; AbstractResource::State state() override; QVariant icon() const override; QString comment() override; QString name() override; QString packageName() const override; QStringList categories() override; QUrl homepage() override; QString license() override; QString longDescription() override; QList addonsInformation() override { return QList(); } QString availableVersion() const override; QString installedVersion() const override; QString origin() const override; QString section() override; void fetchScreenshots() override; int size() override; void fetchChangelog() override; QStringList extends() const override; KNSBackend* knsBackend() const; void setEntry(const KNSCore::EntryInternal& entry); KNSCore::EntryInternal entry() const; bool canExecute() const override { return !executables().isEmpty(); } QStringList executables() const; void invokeApplication() const override; QUrl url() const override; QString executeLabel() const override; + QString sourceIcon() const override { return QStringLiteral("get-hot-new-stuff"); } private: const QStringList m_categories; KNSCore::EntryInternal m_entry; KNS3::Entry::Status m_lastStatus; }; #endif // KNSRESOURCE_H diff --git a/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp b/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp index e2bdfde0..4757b4ea 100644 --- a/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp +++ b/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp @@ -1,261 +1,266 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * Copyright © 2013 Lukas Appelhans * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "PackageKitResource.h" #include "PackageKitBackend.h" #include "PackageKitMessages.h" #include #include #include #include PackageKitResource::PackageKitResource(QString packageName, QString summary, PackageKitBackend* parent) : AbstractResource(parent) , m_summary(std::move(summary)) , m_name(std::move(packageName)) , m_dependenciesCount(0) { setObjectName(m_name); } QString PackageKitResource::name() { return m_name; } QString PackageKitResource::packageName() const { return m_name; } QStringList PackageKitResource::allPackageNames() const { return { m_name }; } QString PackageKitResource::availablePackageId() const { //First we check if it's upgradeable and use this version to display const QString pkgid = backend()->upgradeablePackageId(this); if (!pkgid.isEmpty()) return pkgid; QMap::const_iterator it = m_packages.constFind(PackageKit::Transaction::InfoAvailable); if (it != m_packages.constEnd()) return it->last(); return installedPackageId(); } QString PackageKitResource::installedPackageId() const { const auto installed = m_packages[PackageKit::Transaction::InfoInstalled]; return installed.isEmpty() ? QString() : installed.last(); } QString PackageKitResource::comment() { return m_summary; } QString PackageKitResource::longDescription() { fetchDetails(); return m_details.description(); } QUrl PackageKitResource::homepage() { fetchDetails(); return QUrl(m_details.url()); } QVariant PackageKitResource::icon() const { return QStringLiteral("applications-other"); } QString PackageKitResource::license() { fetchDetails(); return m_details.license().isEmpty() ? i18n("Unknown") : m_details.license(); } QList PackageKitResource::addonsInformation() { return QList(); } QString PackageKitResource::availableVersion() const { return PackageKit::Daemon::packageVersion(availablePackageId()); } QString PackageKitResource::installedVersion() const { return PackageKit::Daemon::packageVersion(installedPackageId()); } int PackageKitResource::size() { fetchDetails(); return m_details.size(); } QString PackageKitResource::origin() const { auto pkgid = availablePackageId(); return PackageKit::Daemon::packageData(pkgid); } QString PackageKitResource::section() { return QString(); } AbstractResource::State PackageKitResource::state() { if (backend()->isPackageNameUpgradeable(this)) return Upgradeable; else if(m_packages.contains(PackageKit::Transaction::InfoInstalled)) return Installed; else if(m_packages.contains(PackageKit::Transaction::InfoAvailable)) return None; else return Broken; } void PackageKitResource::addPackageId(PackageKit::Transaction::Info info, const QString &packageId, bool arch) { if (arch) m_packages[info].append(packageId); else m_packages[info].prepend(packageId); emit stateChanged(); } QStringList PackageKitResource::categories() { return { QStringLiteral("Unknown") }; } bool PackageKitResource::isTechnical() const { return true; } void PackageKitResource::fetchDetails() { const QString pkgid = availablePackageId(); if (!m_details.isEmpty() || pkgid.isEmpty()) return; m_details.insert(QStringLiteral("fetching"), true);//we add an entry so it's not re-fetched. backend()->fetchDetails(pkgid); } void PackageKitResource::failedFetchingDetails(PackageKit::Transaction::Error, const QString& msg) { qWarning() << "error fetching details" << msg; } void PackageKitResource::setDependenciesCount(uint deps) { if (deps != m_dependenciesCount) { m_dependenciesCount = deps; Q_EMIT sizeChanged(); } } void PackageKitResource::setDetails(const PackageKit::Details & details) { const bool ourDetails = details.packageId() == availablePackageId(); if (!ourDetails) return; if (m_details != details) { m_details = details; emit stateChanged(); if (!backend()->isFetching()) backend()->resourcesChanged(this, {"size", "homepage", "license"}); } } void PackageKitResource::fetchChangelog() { PackageKit::Transaction* t = PackageKit::Daemon::getUpdateDetail(availablePackageId()); connect(t, &PackageKit::Transaction::updateDetail, this, &PackageKitResource::updateDetail); connect(t, &PackageKit::Transaction::errorCode, this, [this](PackageKit::Transaction::Error err, const QString & error) { qWarning() << "error fetching updates:" << err << error; emit changelogFetched(QString()); }); } static void addIfNotEmpty(const QString& title, const QString& content, QString& where) { if (!content.isEmpty()) where += QStringLiteral("

") + title + QStringLiteral(" ") + QString(content).replace(QStringLiteral("\n"), QStringLiteral("
")) + QStringLiteral("

"); } QString PackageKitResource::joinPackages(const QStringList& pkgids, const QString &_sep) { QStringList ret; foreach(const QString& pkgid, pkgids) { ret += i18nc("package-name (version)", "%1 (%2)", PackageKit::Daemon::packageName(pkgid), PackageKit::Daemon::packageVersion(pkgid)); } const QString sep = _sep.isEmpty() ? i18nc("comma separating package names", ", ") : _sep; return ret.join(sep); } static QStringList urlToLinks(const QStringList& urls) { QStringList ret; foreach(const QString& in, urls) ret += QStringLiteral("%1").arg(in); return ret; } void PackageKitResource::updateDetail(const QString& /*packageID*/, const QStringList& updates, const QStringList& obsoletes, const QStringList& vendorUrls, const QStringList& /*bugzillaUrls*/, const QStringList& /*cveUrls*/, PackageKit::Transaction::Restart restart, const QString& updateText, const QString& changelog, PackageKit::Transaction::UpdateState state, const QDateTime& /*issued*/, const QDateTime& /*updated*/) { QString info; addIfNotEmpty(i18n("Reason:"), updateText, info); addIfNotEmpty(i18n("Obsoletes:"), joinPackages(obsoletes), info); addIfNotEmpty(i18n("Updates:"), joinPackages(updates), info); addIfNotEmpty(i18n("Update State:"), PackageKitMessages::updateStateMessage(state), info); addIfNotEmpty(i18n("Restart:"), PackageKitMessages::restartMessage(restart), info); if (!vendorUrls.isEmpty()) addIfNotEmpty(i18n("Vendor:"), urlToLinks(vendorUrls).join(QStringLiteral(", ")), info); emit changelogFetched(info); } PackageKitBackend* PackageKitResource::backend() const { return qobject_cast(parent()); } QString PackageKitResource::sizeDescription() { if (m_dependenciesCount == 0) return AbstractResource::sizeDescription(); else return i18np("%2 (plus %1 dependency)", "%2 (plus %1 dependencies)", m_dependenciesCount, AbstractResource::sizeDescription()); } + +QString PackageKitResource::sourceIcon() const +{ + return QStringLiteral("package-available"); +} diff --git a/libdiscover/backends/PackageKitBackend/PackageKitResource.h b/libdiscover/backends/PackageKitBackend/PackageKitResource.h index 181bc6f6..8871d438 100644 --- a/libdiscover/backends/PackageKitBackend/PackageKitResource.h +++ b/libdiscover/backends/PackageKitBackend/PackageKitResource.h @@ -1,103 +1,105 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifndef PACKAGEKITRESOURCE_H #define PACKAGEKITRESOURCE_H #include #include #include class PackageKitBackend; class PackageKitResource : public AbstractResource { Q_OBJECT public: explicit PackageKitResource(QString packageName, QString summary, PackageKitBackend* parent); QString packageName() const override; QString name() override; QString comment() override; QString longDescription() override; QUrl homepage() override; QVariant icon() const override; QStringList categories() override; QString license() override; QString origin() const override; QString section() override; bool isTechnical() const override; int size() override; void fetchChangelog() override; QList addonsInformation() override; State state() override; QString installedVersion() const override; QString availableVersion() const override; virtual QStringList allPackageNames() const; QString installedPackageId() const; QString availablePackageId() const; void clearPackageIds() { m_packages.clear(); } QMap packages() const { return m_packages; } PackageKitBackend* backend() const; static QString joinPackages(const QStringList& pkgids, const QString &_sep = {}); void invokeApplication() const override {} bool canExecute() const override { return false; } QString sizeDescription() override; void setDependenciesCount(uint count); + QString sourceIcon() const override; + public Q_SLOTS: void addPackageId(PackageKit::Transaction::Info info, const QString &packageId, bool arch); void setDetails(const PackageKit::Details& details); void updateDetail(const QString &packageID, const QStringList &updates, const QStringList &obsoletes, const QStringList &vendorUrls, const QStringList &bugzillaUrls, const QStringList &cveUrls, PackageKit::Transaction::Restart restart, const QString &updateText, const QString &changelog, PackageKit::Transaction::UpdateState state, const QDateTime &issued, const QDateTime &updated); private Q_SLOTS: void failedFetchingDetails(PackageKit::Transaction::Error, const QString& msg); private: /** fetches details individually, it's better if done in batch, like for updates */ void fetchDetails(); QMap m_packages; const QString m_summary; const QString m_name; PackageKit::Details m_details; uint m_dependenciesCount; }; #endif // PACKAGEKITRESOURCE_H diff --git a/libdiscover/backends/SnapBackend/SnapResource.h b/libdiscover/backends/SnapBackend/SnapResource.h index 97d8a842..4f5dd5dd 100644 --- a/libdiscover/backends/SnapBackend/SnapResource.h +++ b/libdiscover/backends/SnapBackend/SnapResource.h @@ -1,70 +1,71 @@ /*************************************************************************** * Copyright © 2013 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifndef SNAPRESOURCE_H #define SNAPRESOURCE_H #include #include #include #include class SnapBackend; class SnapResource : public AbstractResource { Q_OBJECT public: explicit SnapResource(QSharedPointer snap, AbstractResource::State state, SnapBackend* parent); ~SnapResource() override = default; QString section() override; QString origin() const override; QString longDescription() override; QString availableVersion() const override; QString installedVersion() const override; QString license() override; int size() override; QStringList categories() override; AbstractResource::State state() override; QVariant icon() const override; QString comment() override; QString name() override; QString packageName() const override; bool isTechnical() const override; bool canExecute() const override { return true; } void invokeApplication() const override; void fetchChangelog() override; void fetchScreenshots() override; QList addonsInformation() override { return {}; } QUrl url() const override; void setSnap(const QSharedPointer &snap); void setState(AbstractResource::State state); + QString sourceIcon() const override { return QStringLiteral("snap"); } public: void gotIcon(); AbstractResource::State m_state; QSharedPointer m_snap; mutable QVariant m_icon; }; #endif // SNAPRESOURCE_H diff --git a/libdiscover/resources/AbstractResource.h b/libdiscover/resources/AbstractResource.h index ef4ad6e2..da294da2 100644 --- a/libdiscover/resources/AbstractResource.h +++ b/libdiscover/resources/AbstractResource.h @@ -1,229 +1,231 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifndef ABSTRACTRESOURCE_H #define ABSTRACTRESOURCE_H #include #include #include #include #include #include #include #include "discovercommon_export.h" #include "PackageState.h" class Category; class Rating; class AbstractResourcesBackend; /** * \class AbstractResource AbstractResource.h "AbstractResource.h" * * \brief This is the base class of all resources. * * Each backend must reimplement its own resource class which needs to derive from this one. */ class DISCOVERCOMMON_EXPORT AbstractResource : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString packageName READ packageName CONSTANT) Q_PROPERTY(QString comment READ comment CONSTANT) Q_PROPERTY(QVariant icon READ icon NOTIFY iconChanged) Q_PROPERTY(bool canExecute READ canExecute CONSTANT) Q_PROPERTY(State state READ state NOTIFY stateChanged) Q_PROPERTY(QString status READ status NOTIFY stateChanged) Q_PROPERTY(QStringList category READ categories CONSTANT) Q_PROPERTY(bool isTechnical READ isTechnical CONSTANT) Q_PROPERTY(QUrl homepage READ homepage CONSTANT) Q_PROPERTY(QUrl helpURL READ helpURL CONSTANT) Q_PROPERTY(QUrl bugURL READ bugURL CONSTANT) Q_PROPERTY(QUrl donationURL READ donationURL CONSTANT) Q_PROPERTY(bool canUpgrade READ canUpgrade NOTIFY stateChanged) Q_PROPERTY(bool isInstalled READ isInstalled NOTIFY stateChanged) Q_PROPERTY(QString license READ license CONSTANT) Q_PROPERTY(QString longDescription READ longDescription CONSTANT) Q_PROPERTY(QString origin READ origin CONSTANT) Q_PROPERTY(QString displayOrigin READ displayOrigin CONSTANT) Q_PROPERTY(int size READ size NOTIFY sizeChanged) Q_PROPERTY(QString sizeDescription READ sizeDescription NOTIFY sizeChanged) Q_PROPERTY(QString installedVersion READ installedVersion NOTIFY stateChanged) Q_PROPERTY(QString availableVersion READ availableVersion NOTIFY stateChanged) Q_PROPERTY(QString section READ section CONSTANT) Q_PROPERTY(QStringList mimetypes READ mimetypes CONSTANT) Q_PROPERTY(AbstractResourcesBackend* backend READ backend CONSTANT) Q_PROPERTY(Rating* rating READ rating NOTIFY ratingFetched) Q_PROPERTY(QString appstreamId READ appstreamId CONSTANT) Q_PROPERTY(QString categoryDisplay READ categoryDisplay CONSTANT) Q_PROPERTY(QUrl url READ url CONSTANT) Q_PROPERTY(QString executeLabel READ executeLabel CONSTANT) + Q_PROPERTY(QString sourceIcon READ sourceIcon CONSTANT) public: /** * This describes the state of the resource */ enum State { /** * When the resource is somehow broken */ Broken, /** * This means that the resource is neither installed nor broken */ None, /** * The resource is installed and up-to-date */ Installed, /** * The resource is installed and an update is available */ Upgradeable }; Q_ENUM(State) /** * Constructs the AbstractResource with its corresponding backend */ explicit AbstractResource(AbstractResourcesBackend* parent); ~AbstractResource() override; ///used as internal identification of a resource virtual QString packageName() const = 0; ///resource name to be displayed virtual QString name() = 0; ///short description of the resource virtual QString comment() = 0; ///xdg-compatible icon name to represent the resource, url or QIcon virtual QVariant icon() const = 0; ///@returns whether invokeApplication makes something /// false if not overridden virtual bool canExecute() const = 0; ///executes the resource, if applies. Q_SCRIPTABLE virtual void invokeApplication() const = 0; virtual State state() = 0; virtual QStringList categories() = 0; ///@returns a URL that points to the app's website virtual QUrl homepage(); ///@returns a URL that points to the app's online documentation virtual QUrl helpURL(); ///@returns a URL that points to the place where you can file a bug virtual QUrl bugURL(); ///@returns a URL that points to the place where you can donate money to the app developer virtual QUrl donationURL(); virtual bool isTechnical() const; virtual int size() = 0; virtual QString sizeDescription(); virtual QString license() = 0; virtual QString installedVersion() const = 0; virtual QString availableVersion() const = 0; virtual QString longDescription() = 0; virtual QString origin() const = 0; QString displayOrigin() const; virtual QString section() = 0; ///@returns what kind of mime types the resource can consume virtual QStringList mimetypes() const; virtual QList addonsInformation() = 0; virtual QStringList extends() const; virtual QString appstreamId() const; void addMetadata(const QString &key, const QJsonValue &value); QJsonValue getMetadata(const QString &key); bool canUpgrade(); bool isInstalled(); ///@returns a user-readable explaination of the resource status ///by default, it will specify what state() is returning virtual QString status(); AbstractResourcesBackend* backend() const; /** * @returns a name sort key for faster sorting */ QCollatorSortKey nameSortKey(); /** * Convenience method to fetch the resource's rating * * @returns the rating for the resource or null if not available */ Rating* rating() const; /** * @returns a string defining the categories the resource belongs to */ QString categoryDisplay() const; bool categoryMatches(Category* cat); QSet categoryObjects(const QVector& cats) const; /** * @returns a url that uniquely identifies the application */ virtual QUrl url() const; virtual QString executeLabel() const; + virtual QString sourceIcon() const = 0; public Q_SLOTS: virtual void fetchScreenshots(); virtual void fetchChangelog() = 0; Q_SIGNALS: void iconChanged(); void sizeChanged(); void stateChanged(); void ratingFetched(); ///response to the fetchScreenshots method ///@p thumbnails and @p screenshots should have the same number of elements void screenshotsFetched(const QList& thumbnails, const QList& screenshots); void changelogFetched(const QString& changelog); private: void reportNewState(); // TODO: make it std::optional or make QCollatorSortKey() QScopedPointer m_collatorKey; QJsonObject m_metadata; }; Q_DECLARE_METATYPE(QVector) #endif // ABSTRACTRESOURCE_H diff --git a/libdiscover/resources/ResourcesProxyModel.cpp b/libdiscover/resources/ResourcesProxyModel.cpp index 1723e8ae..17b6701e 100644 --- a/libdiscover/resources/ResourcesProxyModel.cpp +++ b/libdiscover/resources/ResourcesProxyModel.cpp @@ -1,555 +1,556 @@ /*************************************************************************** * Copyright © 2010 Jonathan Thomas * * Copyright © 2012 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "ResourcesProxyModel.h" #include #include #include #include "ResourcesModel.h" #include "AbstractResource.h" #include "AbstractResourcesBackend.h" #include #include #include ResourcesProxyModel::ResourcesProxyModel(QObject *parent) : QAbstractListModel(parent) , m_sortRole(NameRole) , m_sortOrder(Qt::AscendingOrder) , m_sortByRelevancy(false) , m_roles({ { NameRole, "name" }, { IconRole, "icon" }, { CommentRole, "comment" }, { StateRole, "state" }, { RatingRole, "rating" }, { RatingPointsRole, "ratingPoints" }, { RatingCountRole, "ratingCount" }, { SortableRatingRole, "sortableRating" }, { InstalledRole, "isInstalled" }, { ApplicationRole, "application" }, { OriginRole, "origin" }, { DisplayOriginRole, "displayOrigin" }, { CanUpgrade, "canUpgrade" }, { PackageNameRole, "packageName" }, { IsTechnicalRole, "isTechnical" }, { CategoryRole, "category" }, { CategoryDisplayRole, "categoryDisplay" }, { SectionRole, "section" }, { MimeTypes, "mimetypes" }, { LongDescriptionRole, "longDescription" }, + { SourceIconRole, "sourceIcon" }, { SizeRole, "size" } }) , m_currentStream(nullptr) { // new ModelTest(this, this); connect(ResourcesModel::global(), &ResourcesModel::backendsChanged, this, &ResourcesProxyModel::invalidateFilter); connect(ResourcesModel::global(), &ResourcesModel::backendDataChanged, this, &ResourcesProxyModel::refreshBackend); connect(ResourcesModel::global(), &ResourcesModel::resourceDataChanged, this, &ResourcesProxyModel::refreshResource); connect(ResourcesModel::global(), &ResourcesModel::resourceRemoved, this, &ResourcesProxyModel::removeResource); } void ResourcesProxyModel::componentComplete() { m_setup = true; invalidateFilter(); } QHash ResourcesProxyModel::roleNames() const { return m_roles; } void ResourcesProxyModel::setSortRole(Roles sortRole) { if (sortRole != m_sortRole) { Q_ASSERT(roleNames().contains(sortRole)); m_sortRole = sortRole; Q_EMIT sortRoleChanged(sortRole); invalidateSorting(); } } void ResourcesProxyModel::setSortOrder(Qt::SortOrder sortOrder) { if (sortOrder != m_sortOrder) { m_sortOrder = sortOrder; Q_EMIT sortRoleChanged(sortOrder); invalidateSorting(); } } void ResourcesProxyModel::setSearch(const QString &_searchText) { // 1-character searches are painfully slow. >= 2 chars are fine, though const QString searchText = _searchText.count() <= 1 ? QString() : _searchText; const bool diff = searchText != m_filters.search; if (diff) { m_filters.search = searchText; m_sortByRelevancy = !searchText.isEmpty(); invalidateFilter(); Q_EMIT searchChanged(m_filters.search); } } void ResourcesProxyModel::removeDuplicates(QVector& resources) { const auto cab = ResourcesModel::global()->currentApplicationBackend(); QHash::iterator> storedIds; for(auto it = m_displayedResources.begin(); it != m_displayedResources.end(); ++it) { const auto appstreamid = (*it)->appstreamId(); if (appstreamid.isEmpty()) { continue; } auto at = storedIds.find(appstreamid); if (at == storedIds.end()) { storedIds[appstreamid] = it; } else { qWarning() << "We should have sanitized the displayed resources. There is a bug"; Q_UNREACHABLE(); } } QHash::iterator> ids; for(auto it = resources.begin(); it != resources.end(); ) { const auto appstreamid = (*it)->appstreamId(); if (appstreamid.isEmpty()) { ++it; continue; } auto at = storedIds.find(appstreamid); if (at == storedIds.end()) { auto at = ids.find(appstreamid); if (at == ids.end()) { ids[appstreamid] = it; ++it; } else { if ((*it)->backend() == cab) { qSwap(*it, **at); } it = resources.erase(it); } } else { if ((*it)->backend() == cab) { **at = *it; auto pos = index(*at - m_displayedResources.begin(), 0); dataChanged(pos, pos); } it = resources.erase(it); } } } void ResourcesProxyModel::addResources(const QVector& _res) { auto res = _res; m_filters.filterJustInCase(res); if (res.isEmpty()) return; if (!m_filters.allBackends) { removeDuplicates(res); } if (!m_sortByRelevancy) qSort(res.begin(), res.end(), [this](AbstractResource* res, AbstractResource* res2){ return lessThan(res, res2); }); sortedInsertion(res); fetchSubcategories(); } void ResourcesProxyModel::invalidateSorting() { if (m_displayedResources.isEmpty()) return; if (!m_sortByRelevancy) { beginResetModel(); qSort(m_displayedResources.begin(), m_displayedResources.end(), [this](AbstractResource* res, AbstractResource* res2){ return lessThan(res, res2); }); endResetModel(); } } QString ResourcesProxyModel::lastSearch() const { return m_filters.search; } void ResourcesProxyModel::setOriginFilter(const QString &origin) { if (origin == m_filters.origin) return; m_filters.origin = origin; invalidateFilter(); } QString ResourcesProxyModel::originFilter() const { return m_filters.origin; } void ResourcesProxyModel::setFiltersFromCategory(Category *category) { if(category==m_filters.category) return; m_filters.category = category; invalidateFilter(); emit categoryChanged(); } void ResourcesProxyModel::fetchSubcategories() { auto cats = (m_filters.category ? m_filters.category->subCategories() : CategoryModel::global()->rootCategories()).toList().toSet(); const int count = rowCount(); QSet done; for (int i=0; icategoryObjects(cats.toList().toVector()); done.unite(found); cats.subtract(found); } const QVariantList ret = kTransform(done, [](Category* cat) { return QVariant::fromValue(cat); }); if (ret != m_subcategories) { m_subcategories = ret; Q_EMIT subcategoriesChanged(m_subcategories); } } QVariantList ResourcesProxyModel::subcategories() const { return m_subcategories; } void ResourcesProxyModel::invalidateFilter() { if (!m_setup || ResourcesModel::global()->backends().isEmpty()) { return; } if (m_currentStream) { qWarning() << "last stream isn't over yet" << m_filters << this; delete m_currentStream; } m_currentStream = ResourcesModel::global()->search(m_filters); Q_EMIT busyChanged(true); if (!m_displayedResources.isEmpty()) { beginResetModel(); m_displayedResources.clear(); endResetModel(); } connect(m_currentStream, &AggregatedResultsStream::resourcesFound, this, &ResourcesProxyModel::addResources); connect(m_currentStream, &AggregatedResultsStream::finished, this, [this]() { m_currentStream = nullptr; Q_EMIT busyChanged(false); }); } int ResourcesProxyModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_displayedResources.count(); } bool ResourcesProxyModel::lessThan(AbstractResource* leftPackage, AbstractResource* rightPackage) const { auto role = m_sortRole; Qt::SortOrder order = m_sortOrder; QVariant leftValue; QVariant rightValue; //if we're comparing two equal values, we want the model sorted by application name if(role != NameRole) { leftValue = roleToValue(leftPackage, role); rightValue = roleToValue(rightPackage, role); if (leftValue == rightValue) { role = NameRole; order = Qt::DescendingOrder; } } bool ret; if(role == NameRole) { ret = leftPackage->nameSortKey().compare(rightPackage->nameSortKey()) < 0; } else if(role == CanUpgrade) { ret = leftValue.toBool(); } else { ret = leftValue < rightValue; } return ret != (order != Qt::AscendingOrder); } Category* ResourcesProxyModel::filteredCategory() const { return m_filters.category; } void ResourcesProxyModel::setStateFilter(AbstractResource::State s) { if (s != m_filters.state) { m_filters.state = s; invalidateFilter(); emit stateFilterChanged(); } } AbstractResource::State ResourcesProxyModel::stateFilter() const { return m_filters.state; } QString ResourcesProxyModel::mimeTypeFilter() const { return m_filters.mimetype; } void ResourcesProxyModel::setMimeTypeFilter(const QString& mime) { if (m_filters.mimetype != mime) { m_filters.mimetype = mime; invalidateFilter(); } } QString ResourcesProxyModel::extends() const { return m_filters.extends; } void ResourcesProxyModel::setExtends(const QString& extends) { if (m_filters.extends != extends) { m_filters.extends = extends; invalidateFilter(); } } QUrl ResourcesProxyModel::resourcesUrl() const { return m_filters.resourceUrl; } void ResourcesProxyModel::setResourcesUrl(const QUrl& resourcesUrl) { if (m_filters.resourceUrl != resourcesUrl) { m_filters.resourceUrl = resourcesUrl; invalidateFilter(); } } bool ResourcesProxyModel::allBackends() const { return m_filters.allBackends; } void ResourcesProxyModel::setAllBackends(bool allBackends) { m_filters.allBackends = allBackends; } QVariant ResourcesProxyModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } AbstractResource* const resource = m_displayedResources[index.row()]; return roleToValue(resource, role); } QVariant ResourcesProxyModel::roleToValue(AbstractResource* resource, int role) const { switch(role) { case ApplicationRole: return qVariantFromValue(resource); case RatingPointsRole: case RatingRole: case RatingCountRole: case SortableRatingRole: { Rating* const rating = resource->rating(); const int idx = Rating::staticMetaObject.indexOfProperty(roleNames().value(role).constData()); Q_ASSERT(idx >= 0); auto prop = Rating::staticMetaObject.property(idx); if (rating) return prop.read(rating); else { QVariant val(0); val.convert(prop.type()); return val; } } case Qt::DecorationRole: case Qt::DisplayRole: case Qt::StatusTipRole: case Qt::ToolTipRole: return QVariant(); default: { QByteArray roleText = roleNames().value(role); if(Q_UNLIKELY(roleText.isEmpty())) { qDebug() << "unsupported role" << role; return {}; } static const QMetaObject* m = &AbstractResource::staticMetaObject; int propidx = roleText.isEmpty() ? -1 : m->indexOfProperty(roleText.constData()); if(Q_UNLIKELY(propidx < 0)) { qWarning() << "unknown role:" << role << roleText; return QVariant(); } else return m->property(propidx).read(resource); } } } bool ResourcesProxyModel::isSorted(const QVector & resources) { auto last = resources.constFirst(); for(auto it = resources.constBegin()+1, itEnd = resources.constEnd(); it != itEnd; ++it) { if(!lessThan(last, *it)) { return false; } last = *it; } return true; } void ResourcesProxyModel::sortedInsertion(const QVector & resources) { Q_ASSERT(!resources.isEmpty()); if (m_sortByRelevancy || m_displayedResources.isEmpty()) { // Q_ASSERT(m_sortByRelevancy || isSorted(resources)); int rows = rowCount(); beginInsertRows({}, rows, rows+resources.count()-1); m_displayedResources += resources; endInsertRows(); return; } for(auto resource: resources) { const auto finder = [this](AbstractResource* resource, AbstractResource* res){ return lessThan(resource, res); }; const auto it = std::upper_bound(m_displayedResources.constBegin(), m_displayedResources.constEnd(), resource, finder); const auto newIdx = it == m_displayedResources.constEnd() ? m_displayedResources.count() : (it - m_displayedResources.constBegin()); if ((it-1) != m_displayedResources.constEnd() && *(it-1) == resource) continue; beginInsertRows({}, newIdx, newIdx); m_displayedResources.insert(newIdx, resource); endInsertRows(); // Q_ASSERT(isSorted(resources)); } } void ResourcesProxyModel::refreshResource(AbstractResource* resource, const QVector& properties) { const auto residx = m_displayedResources.indexOf(resource); if (residx<0) { if (!m_sortByRelevancy && m_filters.shouldFilter(resource)) { sortedInsertion({resource}); } return; } if (!m_filters.shouldFilter(resource)) { beginRemoveRows({}, residx, residx); m_displayedResources.removeAt(residx); endRemoveRows(); return; } const QModelIndex idx = index(residx, 0); Q_ASSERT(idx.isValid()); const auto roles = propertiesToRoles(properties); if (!m_sortByRelevancy && roles.contains(m_sortRole)) { beginRemoveRows({}, residx, residx); m_displayedResources.removeAt(residx); endRemoveRows(); sortedInsertion({resource}); } else emit dataChanged(idx, idx, roles); } void ResourcesProxyModel::removeResource(AbstractResource* resource) { const auto residx = m_displayedResources.indexOf(resource); if (residx < 0) return; beginRemoveRows({}, residx, residx); m_displayedResources.removeAt(residx); endRemoveRows(); } void ResourcesProxyModel::refreshBackend(AbstractResourcesBackend* backend, const QVector& properties) { auto roles = propertiesToRoles(properties); const int count = m_displayedResources.count(); bool found = false; for(int i = 0; ibackend()) continue; int j = i+1; for(; jbackend(); ++j) {} Q_EMIT dataChanged(index(i, 0), index(j-1, 0), roles); i = j; found = true; } if (found && properties.contains(m_roles.value(m_sortRole))) { invalidateSorting(); } } QVector ResourcesProxyModel::propertiesToRoles(const QVector& properties) const { QVector roles = kTransform>(properties, [this](const QByteArray& arr) { return roleNames().key(arr, -1); }); roles.removeAll(-1); return roles; } int ResourcesProxyModel::indexOf(AbstractResource* res) { return m_displayedResources.indexOf(res); } AbstractResource * ResourcesProxyModel::resourceAt(int row) const { return m_displayedResources[row]; } diff --git a/libdiscover/resources/ResourcesProxyModel.h b/libdiscover/resources/ResourcesProxyModel.h index 1f9004d5..dc715cac 100644 --- a/libdiscover/resources/ResourcesProxyModel.h +++ b/libdiscover/resources/ResourcesProxyModel.h @@ -1,165 +1,166 @@ /*************************************************************************** * Copyright © 2010 Jonathan Thomas * * Copyright © 2012 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifndef RESOURCESPROXYMODEL_H #define RESOURCESPROXYMODEL_H #include #include #include #include #include #include "discovercommon_export.h" #include "AbstractResource.h" #include "AbstractResourcesBackend.h" class Transaction; class AggregatedResultsStream; class DISCOVERCOMMON_EXPORT ResourcesProxyModel : public QAbstractListModel, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(Roles sortRole READ sortRole WRITE setSortRole NOTIFY sortRoleChanged) Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged) Q_PROPERTY(Category* filteredCategory READ filteredCategory WRITE setFiltersFromCategory NOTIFY categoryChanged) Q_PROPERTY(QString originFilter READ originFilter WRITE setOriginFilter) Q_PROPERTY(AbstractResource::State stateFilter READ stateFilter WRITE setStateFilter NOTIFY stateFilterChanged) Q_PROPERTY(QString mimeTypeFilter READ mimeTypeFilter WRITE setMimeTypeFilter) Q_PROPERTY(QString search READ lastSearch WRITE setSearch NOTIFY searchChanged) Q_PROPERTY(QUrl resourcesUrl READ resourcesUrl WRITE setResourcesUrl NOTIFY resourcesUrlChanged) Q_PROPERTY(QString extending READ extends WRITE setExtends) Q_PROPERTY(bool allBackends READ allBackends WRITE setAllBackends) Q_PROPERTY(QVariantList subcategories READ subcategories NOTIFY subcategoriesChanged) Q_PROPERTY(bool isBusy READ isBusy NOTIFY busyChanged) public: explicit ResourcesProxyModel(QObject* parent = nullptr); enum Roles { NameRole = Qt::UserRole, IconRole, CommentRole, StateRole, RatingRole, RatingPointsRole, RatingCountRole, SortableRatingRole, InstalledRole, ApplicationRole, OriginRole, DisplayOriginRole, CanUpgrade, PackageNameRole, IsTechnicalRole, CategoryRole, CategoryDisplayRole, SectionRole, MimeTypes, SizeRole, - LongDescriptionRole + LongDescriptionRole, + SourceIconRole }; Q_ENUM(Roles) QHash roleNames() const override; void setSearch(const QString &text); QString lastSearch() const; void setOriginFilter(const QString &origin); QString originFilter() const; void setFiltersFromCategory(Category *category); void setStateFilter(AbstractResource::State s); AbstractResource::State stateFilter() const; void setSortRole(Roles sortRole); Roles sortRole() const { return m_sortRole; } void setSortOrder(Qt::SortOrder sortOrder); Qt::SortOrder sortOrder() const { return m_sortOrder; } Category* filteredCategory() const; QString mimeTypeFilter() const; void setMimeTypeFilter(const QString& mime); QString extends() const; void setExtends(const QString &extends); QUrl resourcesUrl() const; void setResourcesUrl(const QUrl& resourcesUrl); bool allBackends() const; void setAllBackends(bool allBackends); QVariantList subcategories() const; QVariant data(const QModelIndex & index, int role) const override; int rowCount(const QModelIndex & parent = {}) const override; Q_SCRIPTABLE int indexOf(AbstractResource* res); Q_SCRIPTABLE AbstractResource* resourceAt(int row) const; bool isBusy() const { return m_currentStream != nullptr; } bool lessThan(AbstractResource* rl, AbstractResource* rr) const; Q_SCRIPTABLE void invalidateFilter(); void invalidateSorting(); void classBegin() override {} void componentComplete() override; private Q_SLOTS: void refreshBackend(AbstractResourcesBackend* backend, const QVector& properties); void refreshResource(AbstractResource* resource, const QVector& properties); void removeResource(AbstractResource* resource); private: void sortedInsertion(const QVector &res); QVariant roleToValue(AbstractResource* res, int role) const; QVector propertiesToRoles(const QVector& properties) const; void addResources(const QVector &res); void fetchSubcategories(); void removeDuplicates(QVector& newResources); bool isSorted(const QVector & resources); Roles m_sortRole; Qt::SortOrder m_sortOrder; bool m_sortByRelevancy; bool m_setup = false; AbstractResourcesBackend::Filters m_filters; QVariantList m_subcategories; QVector m_displayedResources; const QHash m_roles; AggregatedResultsStream* m_currentStream; Q_SIGNALS: void busyChanged(bool isBusy); void sortRoleChanged(int sortRole); void sortOrderChanged(Qt::SortOrder order); void categoryChanged(); void stateFilterChanged(); void searchChanged(const QString &search); void subcategoriesChanged(const QVariantList &subcategories); void resourcesUrlChanged(const QUrl &url); }; #endif