diff --git a/src/attica/atticaprovider.cpp b/src/attica/atticaprovider.cpp index c2ef51d1..55ce3e3c 100644 --- a/src/attica/atticaprovider.cpp +++ b/src/attica/atticaprovider.cpp @@ -1,491 +1,491 @@ /* Copyright (c) 2009-2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "atticaprovider_p.h" #include "question.h" #include #include #include #include #include #include #include #include #include using namespace Attica; namespace KNSCore { AtticaProvider::AtticaProvider(const QStringList &categories) - : mEntryJob(0) + : mEntryJob(nullptr) , mInitialized(false) { // init categories map with invalid categories foreach (const QString &category, categories) { mCategoryMap.insert(category, Attica::Category()); } connect(&m_providerManager, &ProviderManager::providerAdded, this, &AtticaProvider::providerLoaded); connect(&m_providerManager, SIGNAL(authenticationCredentialsMissing(Provider)), SLOT(authenticationCredentialsMissing(Provider))); } AtticaProvider::AtticaProvider(const Attica::Provider &provider, const QStringList &categories) - : mEntryJob(0) + : mEntryJob(nullptr) , mInitialized(false) { // init categories map with invalid categories foreach (const QString &category, categories) { mCategoryMap.insert(category, Attica::Category()); } providerLoaded(provider); } QString AtticaProvider::id() const { return m_provider.baseUrl().toString(); } void AtticaProvider::authenticationCredentialsMissing(const KNSCore::Provider &) { qCDebug(KNEWSTUFFCORE) << "Authentication missing!"; // FIXME Show autentication dialog } bool AtticaProvider::setProviderXML(const QDomElement &xmldata) { if (xmldata.tagName() != QLatin1String("provider")) { return false; } // FIXME this is quite ugly, repackaging the xml into a string QDomDocument doc(QStringLiteral("temp")); qCDebug(KNEWSTUFFCORE) << "setting provider xml" << doc.toString(); doc.appendChild(xmldata.cloneNode(true)); m_providerManager.addProviderFromXml(doc.toString()); if (!m_providerManager.providers().isEmpty()) { qCDebug(KNEWSTUFFCORE) << "base url of attica provider:" << m_providerManager.providers().last().baseUrl().toString(); } else { qCritical() << "Could not load provider."; return false; } return true; } void AtticaProvider::setCachedEntries(const KNSCore::EntryInternal::List &cachedEntries) { mCachedEntries = cachedEntries; } void AtticaProvider::providerLoaded(const Attica::Provider &provider) { mName = provider.name(); qCDebug(KNEWSTUFFCORE) << "Added provider: " << provider.name(); m_provider = provider; Attica::ListJob *job = m_provider.requestCategories(); connect(job, &BaseJob::finished, this, &AtticaProvider::listOfCategoriesLoaded); job->start(); } void AtticaProvider::listOfCategoriesLoaded(Attica::BaseJob *listJob) { if (!jobSuccess(listJob)) { return; } qCDebug(KNEWSTUFFCORE) << "loading categories: " << mCategoryMap.keys(); Attica::ListJob *job = static_cast*>(listJob); Category::List categoryList = job->itemList(); QList categoryMetadataList; foreach (const Category &category, categoryList) { if (mCategoryMap.contains(category.name())) { qCDebug(KNEWSTUFFCORE) << "Adding category: " << category.name() << category.displayName(); mCategoryMap[category.name()] = category; CategoryMetadata categoryMetadata; categoryMetadata.id = category.id(); categoryMetadata.name = category.name(); categoryMetadata.displayName = category.displayName(); categoryMetadataList << categoryMetadata; } } bool correct = false; for(auto it = mCategoryMap.cbegin(), itEnd = mCategoryMap.cend(); it!=itEnd; ++it) { if (!it.value().isValid()) { qCWarning(KNEWSTUFFCORE) << "Could not find category" << it.key(); } else { correct = true; } } if (correct) { mInitialized = true; emit providerInitialized(this); emit categoriesMetadataLoded(categoryMetadataList); } else { emit signalError(i18n("All categories are missing")); } } bool AtticaProvider::isInitialized() const { return mInitialized; } void AtticaProvider::loadEntries(const KNSCore::Provider::SearchRequest &request) { if (mEntryJob) { mEntryJob->abort(); - mEntryJob = 0; + mEntryJob = nullptr; } mCurrentRequest = request; switch (request.filter) { case None: break; case ExactEntryId: { ItemJob *job = m_provider.requestContent(request.searchTerm); connect(job, &BaseJob::finished, this, &AtticaProvider::detailsLoaded); job->start(); return; } case Installed: if (request.page == 0) { emit loadingFinished(request, installedEntries()); } else { emit loadingFinished(request, EntryInternal::List()); } return; case Updates: checkForUpdates(); return; } Attica::Provider::SortMode sorting = atticaSortMode(request.sortMode); Attica::Category::List categoriesToSearch; if (request.categories.isEmpty()) { // search in all categories categoriesToSearch = mCategoryMap.values(); } else { categoriesToSearch.reserve(request.categories.size()); foreach (const QString &categoryName, request.categories) { categoriesToSearch.append(mCategoryMap.value(categoryName)); } } ListJob *job = m_provider.searchContents(categoriesToSearch, request.searchTerm, sorting, request.page, request.pageSize); connect(job, &BaseJob::finished, this, &AtticaProvider::categoryContentsLoaded); mEntryJob = job; job->start(); } void AtticaProvider::checkForUpdates() { foreach (const EntryInternal &e, mCachedEntries) { ItemJob *job = m_provider.requestContent(e.uniqueId()); connect(job, &BaseJob::finished, this, &AtticaProvider::detailsLoaded); m_updateJobs.insert(job); job->start(); qCDebug(KNEWSTUFFCORE) << "Checking for update: " << e.name(); } } void AtticaProvider::loadEntryDetails(const KNSCore::EntryInternal &entry) { ItemJob *job = m_provider.requestContent(entry.uniqueId()); connect(job, &BaseJob::finished, this, &AtticaProvider::detailsLoaded); job->start(); } void AtticaProvider::detailsLoaded(BaseJob *job) { if (jobSuccess(job)) { ItemJob *contentJob = static_cast*>(job); Content content = contentJob->result(); EntryInternal entry = entryFromAtticaContent(content); emit entryDetailsLoaded(entry); qCDebug(KNEWSTUFFCORE) << "check update finished: " << entry.name(); } if (m_updateJobs.remove(job) && m_updateJobs.isEmpty()) { qCDebug(KNEWSTUFFCORE) << "check update finished."; QList updatable; foreach (const EntryInternal &entry, mCachedEntries) { if (entry.status() == KNS3::Entry::Updateable) { updatable.append(entry); } } emit loadingFinished(mCurrentRequest, updatable); } } void AtticaProvider::categoryContentsLoaded(BaseJob *job) { if (!jobSuccess(job)) { return; } ListJob *listJob = static_cast*>(job); Content::List contents = listJob->itemList(); EntryInternal::List entries; Q_FOREACH (const Content &content, contents) { mCachedContent.insert(content.id(), content); entries.append(entryFromAtticaContent(content)); } qCDebug(KNEWSTUFFCORE) << "loaded: " << mCurrentRequest.hashForRequest() << " count: " << entries.size(); emit loadingFinished(mCurrentRequest, entries); - mEntryJob = 0; + mEntryJob = nullptr; } Attica::Provider::SortMode AtticaProvider::atticaSortMode(const SortMode &sortMode) { switch(sortMode) { case Newest: return Attica::Provider::Newest; case Alphabetical: return Attica::Provider::Alphabetical; case Downloads: return Attica::Provider::Downloads; default: return Attica::Provider::Rating; } } void AtticaProvider::loadPayloadLink(const KNSCore::EntryInternal &entry, int linkId) { Attica::Content content = mCachedContent.value(entry.uniqueId()); const DownloadDescription desc = content.downloadUrlDescription(linkId); if (desc.hasPrice()) { // Ask for balance, then show information... ItemJob *job = m_provider.requestAccountBalance(); connect(job, &BaseJob::finished, this, &AtticaProvider::accountBalanceLoaded); mDownloadLinkJobs[job] = qMakePair(entry, linkId); job->start(); qCDebug(KNEWSTUFFCORE) << "get account balance"; } else { ItemJob *job = m_provider.downloadLink(entry.uniqueId(), QString::number(linkId)); connect(job, &BaseJob::finished, this, &AtticaProvider::downloadItemLoaded); mDownloadLinkJobs[job] = qMakePair(entry, linkId); job->start(); qCDebug(KNEWSTUFFCORE) << " link for " << entry.uniqueId(); } } void AtticaProvider::accountBalanceLoaded(Attica::BaseJob *baseJob) { if (!jobSuccess(baseJob)) { return; } ItemJob *job = static_cast*>(baseJob); AccountBalance item = job->result(); QPair pair = mDownloadLinkJobs.take(job); EntryInternal entry(pair.first); Content content = mCachedContent.value(entry.uniqueId()); if (content.downloadUrlDescription(pair.second).priceAmount() < item.balance()) { qCDebug(KNEWSTUFFCORE) << "Your balance is greater than the price." << content.downloadUrlDescription(pair.second).priceAmount() << " balance: " << item.balance(); Question question; question.setQuestion(i18nc("the price of a download item, parameter 1 is the currency, 2 is the price", "This item costs %1 %2.\nDo you want to buy it?", item.currency(), content.downloadUrlDescription(pair.second).priceAmount() )); if(question.ask() == Question::YesResponse) { ItemJob *job = m_provider.downloadLink(entry.uniqueId(), QString::number(pair.second)); connect(job, &BaseJob::finished, this, &AtticaProvider::downloadItemLoaded); mDownloadLinkJobs[job] = qMakePair(entry, pair.second); job->start(); } else { return; } } else { qCDebug(KNEWSTUFFCORE) << "You don't have enough money on your account!" << content.downloadUrlDescription(0).priceAmount() << " balance: " << item.balance(); emit signalInformation(i18n("Your account balance is too low:\nYour balance: %1\nPrice: %2", item.balance(), content.downloadUrlDescription(0).priceAmount())); } } void AtticaProvider::downloadItemLoaded(BaseJob *baseJob) { if (!jobSuccess(baseJob)) { return; } ItemJob *job = static_cast*>(baseJob); DownloadItem item = job->result(); EntryInternal entry = mDownloadLinkJobs.take(job).first; entry.setPayload(QString(item.url().toString())); emit payloadLinkLoaded(entry); } EntryInternal::List AtticaProvider::installedEntries() const { EntryInternal::List entries; foreach (const EntryInternal &entry, mCachedEntries) { if (entry.status() == KNS3::Entry::Installed || entry.status() == KNS3::Entry::Updateable) { entries.append(entry); } } return entries; } void AtticaProvider::vote(const EntryInternal &entry, uint rating) { PostJob *job = m_provider.voteForContent(entry.uniqueId(), rating); connect(job, &BaseJob::finished, this, &AtticaProvider::votingFinished); job->start(); } void AtticaProvider::votingFinished(Attica::BaseJob *job) { if (!jobSuccess(job)) { return; } emit signalInformation(i18nc("voting for an item (good/bad)", "Your vote was recorded.")); } void AtticaProvider::becomeFan(const EntryInternal &entry) { PostJob *job = m_provider.becomeFan(entry.uniqueId()); connect(job, &BaseJob::finished, this, &AtticaProvider::becomeFanFinished); job->start(); } void AtticaProvider::becomeFanFinished(Attica::BaseJob *job) { if (!jobSuccess(job)) { return; } emit signalInformation(i18n("You are now a fan.")); } bool AtticaProvider::jobSuccess(Attica::BaseJob *job) const { if (job->metadata().error() == Attica::Metadata::NoError) { return true; } qCDebug(KNEWSTUFFCORE) << "job error: " << job->metadata().error() << " status code: " << job->metadata().statusCode() << job->metadata().message(); if (job->metadata().error() == Attica::Metadata::NetworkError) { emit signalError(i18n("Network error. (%1)", job->metadata().statusCode())); } if (job->metadata().error() == Attica::Metadata::OcsError) { if (job->metadata().statusCode() == 200) { emit signalError(i18n("Too many requests to server. Please try again in a few minutes.")); } else { emit signalError(i18n("Unknown Open Collaboration Service API error. (%1)", job->metadata().statusCode())); } } return false; } EntryInternal AtticaProvider::entryFromAtticaContent(const Attica::Content &content) { EntryInternal entry; entry.setProviderId(id()); entry.setUniqueId(content.id()); entry.setStatus(KNS3::Entry::Downloadable); entry.setVersion(content.version()); entry.setReleaseDate(content.updated().date()); int index = mCachedEntries.indexOf(entry); if (index >= 0) { EntryInternal &cacheEntry = mCachedEntries[index]; // check if updateable if (((cacheEntry.status() == KNS3::Entry::Installed) || (cacheEntry.status() == KNS3::Entry::Updateable)) && ((cacheEntry.version() != entry.version()) || (cacheEntry.releaseDate() != entry.releaseDate()))) { cacheEntry.setStatus(KNS3::Entry::Updateable); cacheEntry.setUpdateVersion(entry.version()); cacheEntry.setUpdateReleaseDate(entry.releaseDate()); } entry = cacheEntry; } else { mCachedEntries.append(entry); } entry.setName(content.name()); entry.setHomepage(content.detailpage()); entry.setRating(content.rating()); entry.setNumberOfComments(content.numberOfComments()); entry.setDownloadCount(content.downloads()); entry.setNumberFans(content.attribute(QStringLiteral("fans")).toInt()); entry.setDonationLink(content.attribute(QStringLiteral("donationpage"))); entry.setKnowledgebaseLink(content.attribute(QStringLiteral("knowledgebasepage"))); entry.setNumberKnowledgebaseEntries(content.attribute(QStringLiteral("knowledgebaseentries")).toInt()); entry.setHomepage(content.detailpage()); entry.setPreviewUrl(content.smallPreviewPicture(QStringLiteral("1")), EntryInternal::PreviewSmall1); entry.setPreviewUrl(content.smallPreviewPicture(QStringLiteral("2")), EntryInternal::PreviewSmall2); entry.setPreviewUrl(content.smallPreviewPicture(QStringLiteral("3")), EntryInternal::PreviewSmall3); entry.setPreviewUrl(content.previewPicture(QStringLiteral("1")), EntryInternal::PreviewBig1); entry.setPreviewUrl(content.previewPicture(QStringLiteral("2")), EntryInternal::PreviewBig2); entry.setPreviewUrl(content.previewPicture(QStringLiteral("3")), EntryInternal::PreviewBig3); entry.setLicense(content.license()); Author author; author.setName(content.author()); author.setHomepage(content.attribute(QStringLiteral("profilepage"))); entry.setAuthor(author); entry.setSource(EntryInternal::Online); entry.setSummary(content.description()); entry.setShortSummary(content.summary()); entry.setChangelog(content.changelog()); entry.clearDownloadLinkInformation(); QList descs = content.downloadUrlDescriptions(); foreach (const Attica::DownloadDescription &desc, descs) { EntryInternal::DownloadLinkInformation info; info.name = desc.name(); info.priceAmount = desc.priceAmount(); info.distributionType = desc.distributionType(); info.descriptionLink = desc.link(); info.id = desc.id(); info.size = desc.size(); info.isDownloadtypeLink = desc.type() == Attica::DownloadDescription::LinkDownload; entry.appendDownloadLinkInformation(info); } return entry; } } // namespace diff --git a/src/core/cache.cpp b/src/core/cache.cpp index d0fee3b6..75528081 100644 --- a/src/core/cache.cpp +++ b/src/core/cache.cpp @@ -1,245 +1,245 @@ /* Copyright (c) 2009 Frederik Gladhorn Copyright (c) 2010 Matthias Fuchs This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "cache.h" #include #include #include #include #include #include using namespace KNSCore; typedef QHash > CacheHash; Q_GLOBAL_STATIC(CacheHash, s_caches) -Cache::Cache(const QString &appName): QObject(0) +Cache::Cache(const QString &appName): QObject(nullptr) { m_kns2ComponentName = appName; const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QLatin1String("knewstuff3/"); QDir().mkpath(path); registryFile = path + appName + ".knsregistry"; qCDebug(KNEWSTUFFCORE) << "Using registry file: " << registryFile; } QSharedPointer Cache::getCache(const QString &appName) { CacheHash::const_iterator it = s_caches()->constFind(appName); if ((it != s_caches()->constEnd()) && !(*it).isNull()) { return QSharedPointer(*it); } QSharedPointer p(new Cache(appName)); s_caches()->insert(appName, QWeakPointer(p)); return p; } Cache::~Cache() { } void Cache::readRegistry() { // read KNS2 registry first to migrate it readKns2MetaFiles(); QFile f(registryFile); if (!f.open(QIODevice::ReadOnly)) { qWarning() << "The file " << registryFile << " could not be opened."; return; } QDomDocument doc; if (!doc.setContent(&f)) { qWarning() << "The file could not be parsed."; return; } QDomElement root = doc.documentElement(); if (root.tagName() != QLatin1String("hotnewstuffregistry")) { qWarning() << "The file doesn't seem to be of interest."; return; } QDomElement stuff = root.firstChildElement(QStringLiteral("stuff")); while (!stuff.isNull()) { EntryInternal e; e.setEntryXML(stuff); e.setSource(EntryInternal::Cache); cache.insert(e); stuff = stuff.nextSiblingElement(QStringLiteral("stuff")); } qCDebug(KNEWSTUFFCORE) << "Cache read... entries: " << cache.size(); } void Cache::readKns2MetaFiles() { qCDebug(KNEWSTUFFCORE) << "Loading KNS2 registry of files for the component: " << m_kns2ComponentName; QString realAppName = m_kns2ComponentName.split(':')[0]; const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("knewstuff2-entries.registry"), QStandardPaths::LocateDirectory); for (QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) { qCDebug(KNEWSTUFFCORE) << " + Load from directory '" + (*it) + "'."; QDir dir((*it)); const QStringList files = dir.entryList(QDir::Files | QDir::Readable); for (QStringList::const_iterator fit = files.begin(); fit != files.end(); ++fit) { QString filepath = (*it) + '/' + (*fit); qCDebug(KNEWSTUFFCORE) << " Load from file '" + filepath + "'."; QFileInfo info(filepath); QFile f(filepath); // first see if this file is even for this app // because the registry contains entries for all apps // FIXMEE: should be able to do this with a filter on the entryList above probably QString thisAppName = QString::fromUtf8(QByteArray::fromBase64(info.baseName().toUtf8())); // NOTE: the ":" needs to always coincide with the separator character used in // the id(Entry*) method thisAppName = thisAppName.split(':')[0]; if (thisAppName != realAppName) { continue; } if (!f.open(QIODevice::ReadOnly)) { qWarning() << "The file: " << filepath << " could not be opened."; continue; } QDomDocument doc; if (!doc.setContent(&f)) { qWarning() << "The file could not be parsed."; return; } qCDebug(KNEWSTUFFCORE) << "found entry: " << doc.toString(); QDomElement root = doc.documentElement(); if (root.tagName() != QLatin1String("ghnsinstall")) { qWarning() << "The file doesn't seem to be of interest."; return; } // The .meta files only contain one entry QDomElement stuff = root.firstChildElement(QStringLiteral("stuff")); EntryInternal e; e.setEntryXML(stuff); e.setSource(EntryInternal::Cache); if (e.payload().startsWith(QLatin1String("http://download.kde.org/khotnewstuff"))) { // This is 99% sure a opendesktop file, make it a real one. e.setProviderId(QStringLiteral("https://api.opendesktop.org/v1/")); e.setHomepage(QUrl(QString(QLatin1String("http://opendesktop.org/content/show.php?content=") + e.uniqueId()))); } else if (e.payload().startsWith(QLatin1String("http://edu.kde.org/contrib/kvtml/"))) { // kvmtl-1 e.setProviderId(QStringLiteral("http://edu.kde.org/contrib/kvtml/kvtml.xml")); } else if (e.payload().startsWith(QLatin1String("http://edu.kde.org/contrib/kvtml2/"))) { // kvmtl-2 e.setProviderId(QStringLiteral("http://edu.kde.org/contrib/kvtml2/provider41.xml")); } else { // we failed, skip qWarning() << "Could not load entry: " << filepath; continue; } e.setStatus(KNS3::Entry::Installed); cache.insert(e); QDomDocument tmp(QStringLiteral("yay")); tmp.appendChild(e.entryXML()); qCDebug(KNEWSTUFFCORE) << "new entry: " << tmp.toString(); f.close(); QDir dir; if (!dir.remove(filepath)) { qWarning() << "could not delete old kns2 .meta file: " << filepath; } else { qCDebug(KNEWSTUFFCORE) << "Migrated KNS2 entry to KNS3."; } } } } EntryInternal::List Cache::registryForProvider(const QString &providerId) { EntryInternal::List entries; foreach (const EntryInternal &e, cache) { if (e.providerId() == providerId) { entries.append(e); } } return entries; } void Cache::writeRegistry() { qCDebug(KNEWSTUFFCORE) << "Write registry"; QFile f(registryFile); if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { qWarning() << "Cannot write meta information to '" << registryFile << "'." << endl; return; } QDomDocument doc(QStringLiteral("khotnewstuff3")); doc.appendChild(doc.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""))); QDomElement root = doc.createElement(QStringLiteral("hotnewstuffregistry")); doc.appendChild(root); foreach (const EntryInternal &entry, cache) { // Write the entry, unless the policy is CacheNever and the entry is not installed. if (entry.status() == KNS3::Entry::Installed || entry.status() == KNS3::Entry::Updateable) { QDomElement exml = entry.entryXML(); root.appendChild(exml); } } QTextStream metastream(&f); metastream << doc.toByteArray(); f.close(); } void Cache::registerChangedEntry(const KNSCore::EntryInternal &entry) { cache.insert(entry); } void Cache::insertRequest(const KNSCore::Provider::SearchRequest &request, const KNSCore::EntryInternal::List &entries) { // append new entries requestCache[request.hashForRequest()].append(entries); qCDebug(KNEWSTUFFCORE) << request.hashForRequest() << " add: " << entries.size() << " keys: " << requestCache.keys(); } EntryInternal::List Cache::requestFromCache(const KNSCore::Provider::SearchRequest &request) { qCDebug(KNEWSTUFFCORE) << request.hashForRequest(); return requestCache.value(request.hashForRequest()); } diff --git a/src/core/downloadmanager.h b/src/core/downloadmanager.h index e3d42bf4..c76913fe 100644 --- a/src/core/downloadmanager.h +++ b/src/core/downloadmanager.h @@ -1,153 +1,153 @@ /* Copyright (C) 2010 Frederik Gladhorn Copyright (C) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNSCORE_DownloadManager_H #define KNSCORE_DownloadManager_H #include "knewstuffcore_export.h" #include "entryinternal.h" namespace KNSCore { class DownloadManagerPrivate; /** * KNewStuff update checker. * This class can be used to search for KNewStuff items * without using the widgets and to look for updates of * already installed items without showing the dialog. */ class KNEWSTUFFCORE_EXPORT DownloadManager : public QObject { Q_OBJECT public: enum SortOrder { Newest, Alphabetical, Rating, Downloads }; /** * Create a DownloadManager * It will try to find a appname.knsrc file. * Appname is the name of your application as provided in the about data-> * * @param parent the parent of the dialog */ - explicit DownloadManager(QObject *parent = 0); + explicit DownloadManager(QObject *parent = nullptr); /** * Create a DownloadManager. Manually specifying the name of the .knsrc file. * * @param configFile the name of the configuration file * @param parent the parent of the dialog */ - explicit DownloadManager(const QString &configFile, QObject *parent = 0); + explicit DownloadManager(const QString &configFile, QObject *parent = nullptr); /** * destructor */ ~DownloadManager(); /** Search for a list of entries. searchResult will be emitted with the requested list. */ void search(int page = 0, int pageSize = 100); /** Check for available updates. Use searchResult to get notified as soon as an update has been found. */ void checkForUpdates(); /** Check for installed resources Use searchResult to get notified about installed entries. @since 5.28 */ void checkForInstalled(); /** Installs or updates an entry @param entry The entry you wish to install or update */ void installEntry(const EntryInternal &entry); /** * Uninstalls the given entry. * @param entry The entry which will be uninstalled. */ void uninstallEntry(const EntryInternal &entry); /** Sets the search term to filter the results on the server. Note that this function does not trigger a search. Use search after setting this. @param searchTerm The term you wish to search for */ void setSearchTerm(const QString &searchTerm); /** Set the sort order of the results. This depends on the server. Note that this function does not trigger a search. Use search after setting this. @see SortOrder @param order The way you want the results to be sorted */ void setSearchOrder(SortOrder order); /** * Triggers a search for an entry with @p id as its unique id * * @see searchResult * * @since 5.28 */ void fetchEntryById(const QString &id); Q_SIGNALS: /** Returns the search result. This can be the list of updates after checkForUpdates or the result of a search. @param entries the list of results. entries is empty when nothing was found. */ void searchResult(const EntryInternal::List &entries); /** The entry status has changed: emitted when the entry has been installed, updated or removed. Use EntryInternal::status() to check the current status. @param entry the item that has been updated. */ void entryStatusChanged(const EntryInternal &entry); /** * Notifies that the engine couldn't be loaded properly and won't be suitable */ void errorFound(const QString &errorMessage); public Q_SLOTS: void slotProvidersLoaded(); private: DownloadManagerPrivate *const d; Q_DISABLE_COPY(DownloadManager) }; } #endif diff --git a/src/core/engine.cpp b/src/core/engine.cpp index f3a38877..a56de7c2 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -1,609 +1,609 @@ /* knewstuff3/engine.cpp Copyright (c) 2007 Josef Spillner Copyright (C) 2007-2010 Frederik Gladhorn Copyright (c) 2009 Jeremy Whiting Copyright (c) 2010 Matthias Fuchs This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "engine.h" #include "../entry.h" #include "installation.h" #include "xmlloader.h" #include "imageloader_p.h" #include #include #include #include #include #include #include #include #include #if defined(Q_OS_WIN) #include #include #endif // libattica #include #include // own #include "../attica/atticaprovider_p.h" #include "cache.h" #include "../staticxml/staticxmlprovider_p.h" using namespace KNSCore; class EnginePrivate { public: QList categoriesMetadata; }; // BCI: Add a real d-pointer typedef QHash EnginePrivateHash; Q_GLOBAL_STATIC(EnginePrivateHash, d_func) static EnginePrivate *d(const Engine* engine) { EnginePrivate* ret = d_func()->value(engine); if (!ret) { ret = new EnginePrivate; d_func()->insert(engine, ret); } return ret; } static void delete_d(const Engine* engine) { EnginePrivate* ret = d_func()->value(engine); delete ret; d_func()->remove(engine); } Engine::Engine(QObject *parent) : QObject(parent) , m_installation(new Installation) - , m_cache(0) + , m_cache(nullptr) , m_searchTimer(new QTimer) - , m_atticaProviderManager(0) + , m_atticaProviderManager(nullptr) , m_currentPage(-1) , m_pageSize(20) , m_numDataJobs(0) , m_numPictureJobs(0) , m_numInstallJobs(0) , m_initialized(false) { m_searchTimer->setSingleShot(true); m_searchTimer->setInterval(1000); connect(m_searchTimer, &QTimer::timeout, this, &Engine::slotSearchTimerExpired); connect(m_installation, &Installation::signalInstallationFinished, this, &Engine::slotInstallationFinished); connect(m_installation, &Installation::signalInstallationFailed, this, &Engine::slotInstallationFailed); connect(m_installation, &Installation::signalInformation, this, &Engine::signalMessage); connect(m_installation, &Installation::signalError, this, &Engine::signalError); } Engine::~Engine() { if (m_cache) { m_cache->writeRegistry(); } delete m_atticaProviderManager; delete m_searchTimer; delete m_installation; delete_d(this); } bool Engine::init(const QString &configfile) { qCDebug(KNEWSTUFFCORE) << "Initializing KNSCore::Engine from '" << configfile << "'"; emit signalBusy(i18n("Initializing")); KConfig conf(configfile); if (conf.accessMode() == KConfig::NoAccess) { emit signalError(i18n("Configuration file not found: \"%1\"", configfile)); qCritical() << "No knsrc file named '" << configfile << "' was found." << endl; return false; } KConfigGroup group; if (conf.hasGroup("KNewStuff3")) { qCDebug(KNEWSTUFFCORE) << "Loading KNewStuff3 config: " << configfile; group = conf.group("KNewStuff3"); } else if (conf.hasGroup("KNewStuff2")) { qCDebug(KNEWSTUFFCORE) << "Loading KNewStuff2 config: " << configfile; group = conf.group("KNewStuff2"); } else { emit signalError(i18n("Configuration file is invalid: \"%1\"", configfile)); qCritical() << "A knsrc file was found but it doesn't contain a KNewStuff3 section." << endl; return false; } m_categories = group.readEntry("Categories", QStringList()); qCDebug(KNEWSTUFFCORE) << "Categories: " << m_categories; m_providerFileUrl = group.readEntry("ProvidersUrl", QString()); const QString configFileName = QFileInfo(QDir::isAbsolutePath(configfile) ? configfile : QStandardPaths::locate(QStandardPaths::GenericConfigLocation, configfile)).baseName(); // let installation read install specific config if (!m_installation->readConfig(group)) { return false; } connect(m_installation, &Installation::signalEntryChanged, this, &Engine::slotEntryChanged); m_cache = Cache::getCache(configFileName); connect(this, &Engine::signalEntryChanged, m_cache.data(), &Cache::registerChangedEntry); m_cache->readRegistry(); m_initialized = true; // load the providers loadProviders(); return true; } QStringList Engine::categories() const { return m_categories; } QStringList Engine::categoriesFilter() const { return m_currentRequest.categories; } QList Engine::categoriesMetadata() { return d(this)->categoriesMetadata; } void Engine::loadProviders() { if (m_providerFileUrl.isEmpty()) { // it would be nicer to move the attica stuff into its own class qCDebug(KNEWSTUFFCORE) << "Using OCS default providers"; delete m_atticaProviderManager; m_atticaProviderManager = new Attica::ProviderManager; connect(m_atticaProviderManager, &Attica::ProviderManager::providerAdded, this, &Engine::atticaProviderLoaded); m_atticaProviderManager->loadDefaultProviders(); } else { qCDebug(KNEWSTUFFCORE) << "loading providers from " << m_providerFileUrl; emit signalBusy(i18n("Loading provider information")); XmlLoader *loader = new XmlLoader(this); connect(loader, &XmlLoader::signalLoaded, this, &Engine::slotProviderFileLoaded); connect(loader, &XmlLoader::signalFailed, this, &Engine::slotProvidersFailed); loader->load(QUrl(m_providerFileUrl)); } } void Engine::slotProviderFileLoaded(const QDomDocument &doc) { qCDebug(KNEWSTUFFCORE) << "slotProvidersLoaded"; bool isAtticaProviderFile = false; // get each provider element, and create a provider object from it QDomElement providers = doc.documentElement(); if (providers.tagName() == QLatin1String("providers")) { isAtticaProviderFile = true; } else if (providers.tagName() != QLatin1String("ghnsproviders") && providers.tagName() != QLatin1String("knewstuffproviders")) { qWarning() << "No document in providers.xml."; emit signalError(i18n("Could not load get hot new stuff providers from file: %1", m_providerFileUrl)); return; } QDomElement n = providers.firstChildElement(QStringLiteral("provider")); while (!n.isNull()) { qCDebug(KNEWSTUFFCORE) << "Provider attributes: " << n.attribute(QStringLiteral("type")); QSharedPointer provider; if (isAtticaProviderFile || n.attribute(QStringLiteral("type")).toLower() == QLatin1String("rest")) { provider = QSharedPointer (new AtticaProvider(m_categories)); connect(provider.data(), &Provider::categoriesMetadataLoded, this, [this](const QList &categories){ d(this)->categoriesMetadata = categories; emit signalCategoriesMetadataLoded(categories); }); } else { provider = QSharedPointer (new StaticXmlProvider); } if (provider->setProviderXML(n)) { addProvider(provider); } else { emit signalError(i18n("Error initializing provider.")); } n = n.nextSiblingElement(); } emit signalBusy(i18n("Loading data")); } void Engine::atticaProviderLoaded(const Attica::Provider &atticaProvider) { qCDebug(KNEWSTUFFCORE) << "atticaProviderLoaded called"; if (!atticaProvider.hasContentService()) { qCDebug(KNEWSTUFFCORE) << "Found provider: " << atticaProvider.baseUrl() << " but it does not support content"; return; } QSharedPointer provider = QSharedPointer (new AtticaProvider(atticaProvider, m_categories)); connect(provider.data(), &Provider::categoriesMetadataLoded, this, [this](const QList &categories){ d(this)->categoriesMetadata = categories; emit signalCategoriesMetadataLoded(categories); }); addProvider(provider); } void Engine::addProvider(QSharedPointer provider) { qCDebug(KNEWSTUFFCORE) << "Engine addProvider called with provider with id " << provider->id(); m_providers.insert(provider->id(), provider); connect(provider.data(), &Provider::providerInitialized, this, &Engine::providerInitialized); connect(provider.data(), &Provider::loadingFinished, this, &Engine::slotEntriesLoaded); connect(provider.data(), &Provider::entryDetailsLoaded, this, &Engine::slotEntryDetailsLoaded); connect(provider.data(), &Provider::payloadLinkLoaded, this, &Engine::downloadLinkLoaded); connect(provider.data(), &Provider::signalError, this, &Engine::signalError); connect(provider.data(), &Provider::signalInformation, this, &Engine::signalIdle); } void Engine::providerJobStarted(KJob *job) { emit jobStarted(job, i18n("Loading data from provider")); } void Engine::slotProvidersFailed() { emit signalError(i18n("Loading of providers from file: %1 failed", m_providerFileUrl)); } void Engine::providerInitialized(Provider *p) { qCDebug(KNEWSTUFFCORE) << "providerInitialized" << p->name(); p->setCachedEntries(m_cache->registryForProvider(p->id())); updateStatus(); foreach (const QSharedPointer &p, m_providers) { if (!p->isInitialized()) { return; } } emit signalProvidersLoaded(); } void Engine::slotEntriesLoaded(const KNSCore::Provider::SearchRequest &request, KNSCore::EntryInternal::List entries) { m_currentPage = qMax(request.page, m_currentPage); qCDebug(KNEWSTUFFCORE) << "loaded page " << request.page << "current page" << m_currentPage; if (request.filter == Provider::Updates) { emit signalUpdateableEntriesLoaded(entries); } else { m_cache->insertRequest(request, entries); emit signalEntriesLoaded(entries); } --m_numDataJobs; updateStatus(); } void Engine::reloadEntries() { emit signalResetView(); m_currentPage = -1; m_currentRequest.page = 0; m_numDataJobs = 0; foreach (const QSharedPointer &p, m_providers) { if (p->isInitialized()) { if (m_currentRequest.filter == Provider::Installed) { // when asking for installed entries, never use the cache p->loadEntries(m_currentRequest); } else { // take entries from cache until there are no more EntryInternal::List cache = m_cache->requestFromCache(m_currentRequest); while (!cache.isEmpty()) { qCDebug(KNEWSTUFFCORE) << "From cache"; emit signalEntriesLoaded(cache); m_currentPage = m_currentRequest.page; ++m_currentRequest.page; cache = m_cache->requestFromCache(m_currentRequest); } // Since the cache has no more pages, reset the request's page if (m_currentPage >= 0) { m_currentRequest.page = m_currentPage; } // if the cache was empty, request data from provider if (m_currentPage == -1) { qCDebug(KNEWSTUFFCORE) << "From provider"; p->loadEntries(m_currentRequest); ++m_numDataJobs; updateStatus(); } } } } } void Engine::setCategoriesFilter(const QStringList &categories) { m_currentRequest.categories = categories; reloadEntries(); } void Engine::setSortMode(Provider::SortMode mode) { if (m_currentRequest.sortMode != mode) { m_currentRequest.page = -1; } m_currentRequest.sortMode = mode; reloadEntries(); } void KNSCore::Engine::setFilter(Provider::Filter filter) { if (m_currentRequest.filter != filter) { m_currentRequest.page = -1; } m_currentRequest.filter = filter; reloadEntries(); } void KNSCore::Engine::fetchEntryById(const QString& id) { m_searchTimer->stop(); m_currentRequest = KNSCore::Provider::SearchRequest(KNSCore::Provider::Newest, KNSCore::Provider::ExactEntryId, id); EntryInternal::List cache = m_cache->requestFromCache(m_currentRequest); if (!cache.isEmpty()) { reloadEntries(); } else { m_searchTimer->start(); } } void Engine::setSearchTerm(const QString &searchString) { m_searchTimer->stop(); m_currentRequest.searchTerm = searchString; EntryInternal::List cache = m_cache->requestFromCache(m_currentRequest); if (!cache.isEmpty()) { reloadEntries(); } else { m_searchTimer->start(); } } void Engine::slotSearchTimerExpired() { reloadEntries(); } void Engine::requestMoreData() { qCDebug(KNEWSTUFFCORE) << "Get more data! current page: " << m_currentPage << " requested: " << m_currentRequest.page; if (m_currentPage < m_currentRequest.page) { return; } m_currentRequest.page++; doRequest(); } void Engine::requestData(int page, int pageSize) { m_currentRequest.page = page; m_currentRequest.pageSize = pageSize; doRequest(); } void Engine::doRequest() { foreach (const QSharedPointer &p, m_providers) { if (p->isInitialized()) { p->loadEntries(m_currentRequest); ++m_numDataJobs; updateStatus(); } } } void Engine::install(KNSCore::EntryInternal entry, int linkId) { if (entry.status() == KNS3::Entry::Updateable) { entry.setStatus(KNS3::Entry::Updating); } else { entry.setStatus(KNS3::Entry::Installing); } emit signalEntryChanged(entry); qCDebug(KNEWSTUFFCORE) << "Install " << entry.name() << " from: " << entry.providerId(); QSharedPointer p = m_providers.value(entry.providerId()); if (p) { p->loadPayloadLink(entry, linkId); ++m_numInstallJobs; updateStatus(); } } void Engine::slotInstallationFinished() { --m_numInstallJobs; updateStatus(); } void Engine::slotInstallationFailed(const QString &message) { --m_numInstallJobs; emit signalError(message); } void Engine::slotEntryDetailsLoaded(const KNSCore::EntryInternal &entry) { emit signalEntryDetailsLoaded(entry); } void Engine::downloadLinkLoaded(const KNSCore::EntryInternal &entry) { m_installation->install(entry); } void Engine::uninstall(KNSCore::EntryInternal entry) { KNSCore::EntryInternal::List list = m_cache->registryForProvider(entry.providerId()); //we have to use the cached entry here, not the entry from the provider //since that does not contain the list of installed files KNSCore::EntryInternal actualEntryForUninstall; foreach (const KNSCore::EntryInternal &eInt, list) { if (eInt.uniqueId() == entry.uniqueId()) { actualEntryForUninstall = eInt; break; } } if (!actualEntryForUninstall.isValid()) { qCDebug(KNEWSTUFFCORE) << "could not find a cached entry with following id:" << entry.uniqueId() << " -> using the non-cached version"; return; } entry.setStatus(KNS3::Entry::Installing); actualEntryForUninstall.setStatus(KNS3::Entry::Installing); emit signalEntryChanged(entry); qCDebug(KNEWSTUFFCORE) << "about to uninstall entry " << entry.uniqueId(); // FIXME: change the status? m_installation->uninstall(actualEntryForUninstall); entry.setStatus(KNS3::Entry::Deleted); //status for actual entry gets set in m_installation->uninstall() emit signalEntryChanged(entry); } void Engine::loadDetails(const KNSCore::EntryInternal &entry) { QSharedPointer p = m_providers.value(entry.providerId()); p->loadEntryDetails(entry); } void Engine::loadPreview(const KNSCore::EntryInternal &entry, EntryInternal::PreviewType type) { qCDebug(KNEWSTUFFCORE) << "START preview: " << entry.name() << type; ImageLoader *l = new ImageLoader(entry, type, this); connect(l, &ImageLoader::signalPreviewLoaded, this, &Engine::slotPreviewLoaded); l->start(); ++m_numPictureJobs; updateStatus(); } void Engine::slotPreviewLoaded(const KNSCore::EntryInternal &entry, EntryInternal::PreviewType type) { qCDebug(KNEWSTUFFCORE) << "FINISH preview: " << entry.name() << type; emit signalEntryPreviewLoaded(entry, type); --m_numPictureJobs; updateStatus(); } void Engine::contactAuthor(const EntryInternal &entry) { if (!entry.author().email().isEmpty()) { // invoke mail with the address of the author QUrl mailUrl; mailUrl.setScheme(QStringLiteral("mailto")); mailUrl.setPath(entry.author().email()); QUrlQuery query; query.addQueryItem(QStringLiteral("subject"), i18n("Re: %1", entry.name())); mailUrl.setQuery(query); QDesktopServices::openUrl(mailUrl); } else if (!entry.author().homepage().isEmpty()) { QDesktopServices::openUrl(QUrl(entry.author().homepage())); } } void Engine::slotEntryChanged(const KNSCore::EntryInternal &entry) { emit signalEntryChanged(entry); } bool Engine::userCanVote(const EntryInternal &entry) { QSharedPointer p = m_providers.value(entry.providerId()); return p->userCanVote(); } void Engine::vote(const EntryInternal &entry, uint rating) { QSharedPointer p = m_providers.value(entry.providerId()); p->vote(entry, rating); } bool Engine::userCanBecomeFan(const EntryInternal &entry) { QSharedPointer p = m_providers.value(entry.providerId()); return p->userCanBecomeFan(); } void Engine::becomeFan(const EntryInternal &entry) { QSharedPointer p = m_providers.value(entry.providerId()); p->becomeFan(entry); } void Engine::updateStatus() { if (m_numDataJobs > 0) { emit signalBusy(i18n("Loading data")); } else if (m_numPictureJobs > 0) { emit signalBusy(i18np("Loading one preview", "Loading %1 previews", m_numPictureJobs)); } else if (m_numInstallJobs > 0) { emit signalBusy(i18n("Installing")); } else { emit signalIdle(QString()); } } void Engine::checkForUpdates() { foreach (QSharedPointer p, m_providers) { Provider::SearchRequest request(KNSCore::Provider::Newest, KNSCore::Provider::Updates); p->loadEntries(request); } } void KNSCore::Engine::checkForInstalled() { foreach (QSharedPointer p, m_providers) { Provider::SearchRequest request(KNSCore::Provider::Newest, KNSCore::Provider::Installed); request.page = 0; p->loadEntries(request); } } diff --git a/src/core/engine.h b/src/core/engine.h index b6dce7f7..5dcbe670 100644 --- a/src/core/engine.h +++ b/src/core/engine.h @@ -1,244 +1,244 @@ /* knewstuff3/engine.h. Copyright (c) 2007 Josef Spillner Copyright (C) 2007-2010 Frederik Gladhorn Copyright (c) 2009 Jeremy Whiting This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_ENGINE_P_H #define KNEWSTUFF3_ENGINE_P_H #include #include #include #include #include "provider.h" #include "entryinternal.h" #include "knewstuffcore_export.h" class QTimer; class KJob; namespace Attica { class ProviderManager; class Provider; } namespace KNSCore { class Cache; class Installation; /** * KNewStuff engine. * An engine keeps track of data which is available locally and remote * and offers high-level synchronization calls as well as upload and download * primitives using an underlying GHNS protocol. * * @internal */ class KNEWSTUFFCORE_EXPORT Engine : public QObject { Q_OBJECT public: /** * Constructor. */ - explicit Engine(QObject *parent = 0); + explicit Engine(QObject *parent = nullptr); /** * Destructor. Frees up all the memory again which might be taken * by cached entries and providers. */ ~Engine(); /** * Initializes the engine. This step is application-specific and relies * on an external configuration file, which determines all the details * about the initialization. * * @param configfile KNewStuff2 configuration file (*.knsrc) * @return \b true if any valid configuration was found, \b false otherwise */ bool init(const QString &configfile); /** * Installs an entry's payload file. This includes verification, if * necessary, as well as decompression and other steps according to the * application's *.knsrc file. * * @param entry Entry to be installed * * @see signalInstallationFinished * @see signalInstallationFailed */ void install(KNSCore::EntryInternal entry, int linkId = 1); /** * Uninstalls an entry. It reverses the steps which were performed * during the installation. * * @param entry The entry to deinstall */ void uninstall(KNSCore::EntryInternal entry); void loadPreview(const KNSCore::EntryInternal &entry, EntryInternal::PreviewType type); void loadDetails(const KNSCore::EntryInternal &entry); void setSortMode(Provider::SortMode mode); void setFilter(Provider::Filter filter); /** Set the categories that will be included in searches */ void setCategoriesFilter(const QStringList &categories); void setSearchTerm(const QString &searchString); void reloadEntries(); void requestMoreData(); void requestData(int page, int pageSize); void checkForUpdates(); void checkForInstalled(); void fetchEntryById(const QString &id); /** * Try to contact the author of the entry by email or showing their homepage. */ void contactAuthor(const EntryInternal &entry); bool userCanVote(const EntryInternal &entry); void vote(const EntryInternal &entry, uint rating); bool userCanBecomeFan(const EntryInternal &entry); void becomeFan(const EntryInternal &entry); QStringList categories() const; QStringList categoriesFilter() const; QList categoriesMetadata(); Q_SIGNALS: /** * Indicates a message to be added to the ui's log, or sent to a messagebox */ void signalMessage(const QString &message); void signalProvidersLoaded(); void signalEntriesLoaded(const KNSCore::EntryInternal::List &entries); void signalUpdateableEntriesLoaded(const KNSCore::EntryInternal::List &entries); void signalEntryChanged(const KNSCore::EntryInternal &entry); void signalEntryDetailsLoaded(const KNSCore::EntryInternal &entry); // a new search result is there, clear the list of items void signalResetView(); void signalEntryPreviewLoaded(const KNSCore::EntryInternal &, KNSCore::EntryInternal::PreviewType); void signalPreviewFailed(); void signalEntryUploadFinished(); void signalEntryUploadFailed(); void signalDownloadDialogDone(KNSCore::EntryInternal::List); void jobStarted(KJob *, const QString &); void signalError(const QString &); void signalBusy(const QString &); void signalIdle(const QString &); void signalCategoriesMetadataLoded(const QList &categories); private Q_SLOTS: // the .knsrc file was loaded void slotProviderFileLoaded(const QDomDocument &doc); // instead of getting providers from knsrc, use what was configured in ocs systemsettings void atticaProviderLoaded(const Attica::Provider &provider); // loading the .knsrc file failed void slotProvidersFailed(); // called when a provider is ready to work void providerInitialized(KNSCore::Provider *); void slotEntriesLoaded(const KNSCore::Provider::SearchRequest &, KNSCore::EntryInternal::List); void slotEntryDetailsLoaded(const KNSCore::EntryInternal &entry); void slotPreviewLoaded(const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::PreviewType type); void slotSearchTimerExpired(); void slotEntryChanged(const KNSCore::EntryInternal &entry); void slotInstallationFinished(); void slotInstallationFailed(const QString &message); void downloadLinkLoaded(const KNSCore::EntryInternal &entry); void providerJobStarted(KJob *); private: /** * load providers from the providersurl in the knsrc file * creates providers based on their type and adds them to the list of providers */ void loadProviders(); /** Add a provider and connect it to the right slots */ void addProvider(QSharedPointer provider); void updateStatus(); void doRequest(); //FIXME KF6: move all of this in EnginePrivate // handle installation of entries Installation *m_installation; // read/write cache of entries QSharedPointer m_cache; QTimer *m_searchTimer; // The url of the file containing information about content providers QString m_providerFileUrl; // Categories from knsrc file QStringList m_categories; QHash > m_providers; // TODO KF6: remove QString m_unused; // the current request from providers Provider::SearchRequest m_currentRequest; Attica::ProviderManager *m_atticaProviderManager; // the page that is currently displayed, so it is not requested repeatedly int m_currentPage; // when requesting entries from a provider, how many to ask for int m_pageSize; int m_numDataJobs; int m_numPictureJobs; int m_numInstallJobs; // If the provider is ready to be used bool m_initialized; Q_DISABLE_COPY(Engine) }; } #endif diff --git a/src/core/installation.h b/src/core/installation.h index ffe7cd30..e6e83bfc 100644 --- a/src/core/installation.h +++ b/src/core/installation.h @@ -1,172 +1,172 @@ /* This file is part of KNewStuff2. Copyright (c) 2007 Josef Spillner Copyright (C) 2009 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_INSTALLATION_P_H #define KNEWSTUFF3_INSTALLATION_P_H #include #include #include #include "entryinternal.h" #include "knewstuffcore_export.h" class KArchiveDirectory; class KJob; namespace KNSCore { /** * @short KNewStuff entry installation. * * The installation class stores all information related to an entry's * installation. * * @author Josef Spillner (spillner@kde.org) * * @internal */ class KNEWSTUFFCORE_EXPORT Installation : public QObject { Q_OBJECT public: /** * Constructor. */ - explicit Installation(QObject *parent = 0); + explicit Installation(QObject *parent = nullptr); enum Policy { CheckNever, CheckIfPossible, CheckAlways }; enum Scope { ScopeUser, ScopeSystem }; bool readConfig(const KConfigGroup &group); bool isRemote() const; public Q_SLOTS: /** * Downloads a payload file. The payload file matching most closely * the current user language preferences will be downloaded. * The file will not be installed set, for this \ref install must * be called. * * @param entry Entry to download payload file for * * @see signalPayloadLoaded * @see signalPayloadFailed */ void downloadPayload(const KNSCore::EntryInternal &entry); /** * Installs an entry's payload file. This includes verification, if * necessary, as well as decompression and other steps according to the * application's *.knsrc file. * Note that this method is asynchronous and thus the return value will * only report the successful start of the installation. * Note also that while entry is const at this point, it will change later * during the actual installation (the installedFiles list will change, as * will its status) * * @param entry Entry to be installed * * @see signalInstallationFinished * @see signalInstallationFailed */ void install(const KNSCore::EntryInternal &entry); /** * Uninstalls an entry. It reverses the steps which were performed * during the installation. * * The entry instance will be updated with any new information: *
    *
  • Status will be set to Deleted *
  • uninstalledFiles will list files which were removed during uninstallation *
  • installedFiles will become empty *
* * @param entry The entry to deinstall * * @note FIXME: I don't believe this works yet :) */ void uninstall(KNSCore::EntryInternal entry); void slotInstallationVerification(int result); void slotPayloadResult(KJob *job); Q_SIGNALS: void signalEntryChanged(const KNSCore::EntryInternal &entry); void signalInstallationFinished(); void signalInstallationFailed(const QString &message); void signalPayloadLoaded(QUrl payload); // FIXME: return Entry void signalInformation(const QString &) const; void signalError(const QString &) const; private: void install(KNSCore::EntryInternal entry, const QString &downloadedFile); QString targetInstallationPath(const QString &payloadfile); QStringList installDownloadedFileAndUncompress(const KNSCore::EntryInternal &entry, const QString &payloadfile, const QString installdir); void runPostInstallationCommand(const QString &installPath); static QStringList archiveEntries(const QString &path, const KArchiveDirectory *dir); // applications can set this if they want the installed files/directories to be piped into a shell command QString postInstallationCommand; // a custom command to run for the uninstall QString uninstallCommand; // compression policy QString uncompression; // only one of the five below can be set, that will be the target install path/file name // FIXME: check this when reading the config and make one path out of it if possible? QString standardResourceDirectory; QString targetDirectory; QString xdgTargetDirectory; QString installPath; QString absoluteInstallPath; // policies whether verification needs to be done Policy checksumPolicy; Policy signaturePolicy; // scope: install into user or system dirs Scope scope; // FIXME this throws together a file name from entry name and version - why would anyone want that? bool customName; bool acceptHtml; QMap entry_jobs; Q_DISABLE_COPY(Installation) }; } #endif diff --git a/src/core/itemsmodel.h b/src/core/itemsmodel.h index 3de6c85b..378b9307 100644 --- a/src/core/itemsmodel.h +++ b/src/core/itemsmodel.h @@ -1,70 +1,70 @@ /* knewstuff3/ui/itemsmodel.h. Copyright (C) 2008 Jeremy Whiting This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_ITEMSMODEL_P_H #define KNEWSTUFF3_ITEMSMODEL_P_H #include #include #include "entryinternal.h" #include "knewstuffcore_export.h" class KJob; namespace KNSCore { class Engine; class KNEWSTUFFCORE_EXPORT ItemsModel: public QAbstractListModel { Q_OBJECT public: - explicit ItemsModel(Engine *engine, QObject *parent = 0); + explicit ItemsModel(Engine *engine, QObject *parent = nullptr); ~ItemsModel(); int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; void addEntry(const EntryInternal &entry); void removeEntry(const EntryInternal &entry); bool hasPreviewImages() const; bool hasWebService() const; Q_SIGNALS: void jobStarted(KJob *, const QString &label); public Q_SLOTS: void slotEntryChanged(const KNSCore::EntryInternal &entry); void slotEntriesLoaded(const KNSCore::EntryInternal::List &entries); void clearEntries(); void slotEntryPreviewLoaded(const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::PreviewType type); private: Engine *m_engine; // the list of entries QList m_entries; bool m_hasPreviewImages; }; } // end KNS namespace Q_DECLARE_METATYPE(KNSCore::EntryInternal) #endif diff --git a/src/core/jobs/downloadjob.h b/src/core/jobs/downloadjob.h index d16b2ce4..045a9f0c 100644 --- a/src/core/jobs/downloadjob.h +++ b/src/core/jobs/downloadjob.h @@ -1,46 +1,46 @@ /* Copyright (C) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef DOWNLOADJOB_H #define DOWNLOADJOB_H #include "filecopyjob.h" namespace KNSCore { class DownloadJob : public FileCopyJob { Q_OBJECT public: - explicit DownloadJob(const QUrl& source, const QUrl& destination, int permissions=-1, JobFlags flags = DefaultFlags, QObject* parent = 0); - explicit DownloadJob(QObject* parent = 0); + explicit DownloadJob(const QUrl& source, const QUrl& destination, int permissions=-1, JobFlags flags = DefaultFlags, QObject* parent = nullptr); + explicit DownloadJob(QObject* parent = nullptr); virtual ~DownloadJob(); Q_SCRIPTABLE virtual void start() Q_DECL_OVERRIDE; protected Q_SLOTS: void handleWorkerCompleted(); void handleWorkerError(const QString& error); private: class Private; Private* d; }; } #endif//DOWNLOADJOB_H diff --git a/src/core/jobs/filecopyjob.cpp b/src/core/jobs/filecopyjob.cpp index d21a0e92..5b7115bc 100644 --- a/src/core/jobs/filecopyjob.cpp +++ b/src/core/jobs/filecopyjob.cpp @@ -1,113 +1,113 @@ /* Copyright (C) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "filecopyjob.h" #include "downloadjob.h" #include "filecopyworker.h" #include "knewstuffcore_debug.h" using namespace KNSCore; class FileCopyJob::Private { public: Private() : permissions(-1) , flags(DefaultFlags) - , worker(0) + , worker(nullptr) {} QUrl source; QUrl destination; int permissions; JobFlags flags; FileCopyWorker* worker; }; FileCopyJob::FileCopyJob(const QUrl& source, const QUrl& destination, int permissions, JobFlags flags, QObject* parent) : KJob(parent) , d(new Private) { d->source = source; d->destination = destination; d->permissions = permissions; d->flags = flags; } FileCopyJob::FileCopyJob(QObject* parent) : KJob(parent) , d(new Private) { } FileCopyJob::~FileCopyJob() { delete d; } void FileCopyJob::start() { if(d->worker) { // already started... return; } d->worker = new FileCopyWorker(d->source, d->destination, this); connect(d->worker, &FileCopyWorker::progress, this, &FileCopyJob::handleProgressUpdate); connect(d->worker, &FileCopyWorker::completed, this, &FileCopyJob::handleCompleted); d->worker->start(); } QUrl FileCopyJob::destUrl() const { return d->destination; } QUrl FileCopyJob::srcUrl() const { return d->source; } FileCopyJob* FileCopyJob::file_copy(const QUrl& source, const QUrl& destination, int permissions, JobFlags flags, QObject* parent) { - FileCopyJob* job = 0; + FileCopyJob* job = nullptr; if(source.isLocalFile() && destination.isLocalFile()) { qCDebug(KNEWSTUFFCORE) << "File copy job is local only"; job = new FileCopyJob(source, destination, permissions, flags, parent); } else { qCDebug(KNEWSTUFFCORE) << "File copy job is from (or to) a remote URL"; job = new DownloadJob(source, destination, permissions, flags, parent); } job->start(); return job; } void FileCopyJob::handleProgressUpdate(qlonglong current, qlonglong total) { setTotalAmount(KJob::Bytes, total); setProcessedAmount(KJob::Bytes, current); emitPercent(current, total); } void FileCopyJob::handleCompleted() { d->worker->deleteLater(); - d->worker = 0; + d->worker = nullptr; emitResult(); } diff --git a/src/core/jobs/filecopyjob.h b/src/core/jobs/filecopyjob.h index 96cdda67..01368373 100644 --- a/src/core/jobs/filecopyjob.h +++ b/src/core/jobs/filecopyjob.h @@ -1,58 +1,58 @@ /* Copyright (C) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef FILECOPYJOB_H #define FILECOPYJOB_H #include "jobbase.h" #include namespace KNSCore { class FileCopyJob : public KJob { Q_OBJECT public: - explicit FileCopyJob(const QUrl& source, const QUrl& destination, int permissions=-1, JobFlags flags = DefaultFlags, QObject* parent = 0); - explicit FileCopyJob(QObject* parent = 0); + explicit FileCopyJob(const QUrl& source, const QUrl& destination, int permissions=-1, JobFlags flags = DefaultFlags, QObject* parent = nullptr); + explicit FileCopyJob(QObject* parent = nullptr); virtual ~FileCopyJob(); Q_SCRIPTABLE virtual void start() Q_DECL_OVERRIDE; QUrl destUrl() const; QUrl srcUrl() const; // This will create either a FileCopyJob, or an instance of // a subclass, depending on the nature of the URLs passed to // it - static FileCopyJob* file_copy(const QUrl& source, const QUrl& destination, int permissions=-1, JobFlags flags = DefaultFlags, QObject* parent = 0); + static FileCopyJob* file_copy(const QUrl& source, const QUrl& destination, int permissions=-1, JobFlags flags = DefaultFlags, QObject* parent = nullptr); protected Q_SLOTS: void handleProgressUpdate(qlonglong current, qlonglong total); void handleCompleted(); private: class Private; Private* d; }; } #endif//FILECOPYJOB_H diff --git a/src/core/jobs/filecopyworker.h b/src/core/jobs/filecopyworker.h index ae6f8a65..b9cb4dc9 100644 --- a/src/core/jobs/filecopyworker.h +++ b/src/core/jobs/filecopyworker.h @@ -1,42 +1,42 @@ /* Copyright (C) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef FILECOPYWORKER_H #define FILECOPYWORKER_H #include #include namespace KNSCore { class FileCopyWorker : public QThread { Q_OBJECT public: - explicit FileCopyWorker(const QUrl& source, const QUrl& destination, QObject* parent = 0); + explicit FileCopyWorker(const QUrl& source, const QUrl& destination, QObject* parent = nullptr); virtual ~FileCopyWorker(); void run() Q_DECL_OVERRIDE; Q_SIGNAL void progress(qlonglong current, qlonglong total); Q_SIGNAL void completed(); private: class Private; Private* d; }; } #endif//FILECOPYWORKER_H diff --git a/src/core/jobs/httpjob.h b/src/core/jobs/httpjob.h index 4fd488c3..81f96c7e 100644 --- a/src/core/jobs/httpjob.h +++ b/src/core/jobs/httpjob.h @@ -1,63 +1,63 @@ /* Copyright (C) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef HTTPJOB_H #define HTTPJOB_H #include "jobbase.h" #include namespace KNSCore { class HTTPJob : public KJob { Q_OBJECT public: - explicit HTTPJob(const QUrl& source, LoadType loadType = Reload, JobFlags flags = DefaultFlags, QObject* parent = 0); - explicit HTTPJob(QObject* parent = 0); + explicit HTTPJob(const QUrl& source, LoadType loadType = Reload, JobFlags flags = DefaultFlags, QObject* parent = nullptr); + explicit HTTPJob(QObject* parent = nullptr); virtual ~HTTPJob(); Q_SLOT virtual void start() Q_DECL_OVERRIDE; - static HTTPJob* get(const QUrl& source, LoadType loadType = Reload, JobFlags flags = DefaultFlags, QObject* parent = 0); + static HTTPJob* get(const QUrl& source, LoadType loadType = Reload, JobFlags flags = DefaultFlags, QObject* parent = nullptr); Q_SIGNALS: /** * Data from the slave has arrived. * @param job the job that emitted this signal * @param data data received from the slave. * * End of data (EOD) has been reached if data.size() == 0, however, you * should not be certain of data.size() == 0 ever happening (e.g. in case * of an error), so you should rely on result() instead. */ void data(KJob *job, const QByteArray& data); protected Q_SLOTS: void handleWorkerData(const QByteArray& data); void handleWorkerCompleted(); void handleWorkerError(const QString& error); private: class Private; Private* d; }; } #endif//HTTPJOB_H diff --git a/src/core/jobs/httpworker.cpp b/src/core/jobs/httpworker.cpp index 424068d4..9df2e580 100644 --- a/src/core/jobs/httpworker.cpp +++ b/src/core/jobs/httpworker.cpp @@ -1,158 +1,158 @@ /* Copyright (C) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "httpworker.h" #include "knewstuffcore_debug.h" #include #include #include #include using namespace KNSCore; class HTTPWorker::Private { public: Private() : jobType(GetJob) - , qnam(0) - , reply(0) + , qnam(nullptr) + , reply(nullptr) {} JobType jobType; QUrl source; QUrl destination; QNetworkAccessManager* qnam; QNetworkReply* reply; QUrl redirectUrl; QFile dataFile; }; HTTPWorker::HTTPWorker(const QUrl& url, JobType jobType, QObject* parent) : QObject(parent) , d(new Private) { qCDebug(KNEWSTUFFCORE) << Q_FUNC_INFO; d->jobType = jobType; d->source = url; d->qnam = new QNetworkAccessManager(parent); connect(d->qnam, &QNetworkAccessManager::finished, this, &HTTPWorker::handleFinished); } HTTPWorker::HTTPWorker(const QUrl& source, const QUrl& destination, KNSCore::HTTPWorker::JobType jobType, QObject* parent) : QObject(parent) , d(new Private) { qCDebug(KNEWSTUFFCORE) << Q_FUNC_INFO; d->jobType = jobType; d->source = source; d->destination = destination; d->qnam = new QNetworkAccessManager(parent); connect(d->qnam, &QNetworkAccessManager::finished, this, &HTTPWorker::handleFinished); } HTTPWorker::~HTTPWorker() { delete d; } void HTTPWorker::setUrl(const QUrl& url) { d->source = url; } void HTTPWorker::startRequest() { if(d->reply) { // only run one request at a time... return; } QNetworkRequest request(d->source); d->reply = d->qnam->get(request); connect(d->reply, &QNetworkReply::readyRead, this, &HTTPWorker::handleReadyRead); if(d->jobType == DownloadJob) { d->dataFile.setFileName(d->destination.toLocalFile()); connect(this, &HTTPWorker::data, this, &HTTPWorker::handleData); } } void HTTPWorker::handleReadyRead() { // qCDebug(KNEWSTUFFCORE) << Q_FUNC_INFO; if (d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute).isNull()) { do { emit data(d->reply->read(32768)); } while(!d->reply->atEnd()); } } void HTTPWorker::handleFinished(QNetworkReply* reply) { qCDebug(KNEWSTUFFCORE) << Q_FUNC_INFO; if (reply->error() != QNetworkReply::NoError) { qCWarning(KNEWSTUFFCORE) << reply->errorString(); emit error(reply->errorString()); } // Handle redirections const QUrl possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != d->redirectUrl) { d->redirectUrl = reply->url().resolved(possibleRedirectUrl); if (d->redirectUrl.scheme().startsWith("http")) { qCInfo(KNEWSTUFFCORE) << "Redirected to " << d->redirectUrl.toDisplayString() << "..."; reply->deleteLater(); d->reply = d->qnam->get(QNetworkRequest(d->redirectUrl)); connect(d->reply, &QNetworkReply::readyRead, this, &HTTPWorker::handleReadyRead); return; } else { qCWarning(KNEWSTUFFCORE) << "Redirection to" << d->redirectUrl.toDisplayString() << "forbidden."; } } if(d->dataFile.isOpen() && d->redirectUrl.isEmpty()) { d->dataFile.close(); } d->redirectUrl.clear(); emit completed(); } void HTTPWorker::handleData(const QByteArray& data) { // It turns out that opening a file and then leaving it hanging without writing to it immediately will, at times // leave you with a file that suddenly (seemingly magically) no longer exists. Thanks for that. if(!d->dataFile.isOpen()) { if(d->dataFile.open(QIODevice::WriteOnly)) { qCDebug(KNEWSTUFFCORE) << "Opened file" << d->dataFile.fileName() << "for writing."; } else { qCWarning(KNEWSTUFFCORE) << "Failed to open file for writing!"; emit error(QString("Failed to open file %1 for writing!").arg(d->destination.toLocalFile())); } } qCDebug(KNEWSTUFFCORE) << "Writing" << data.length() << "bytes of data to" << d->dataFile.fileName(); quint64 written = d->dataFile.write(data); if(d->dataFile.error()) { qCDebug(KNEWSTUFFCORE) << "File has error" << d->dataFile.errorString(); } qCDebug(KNEWSTUFFCORE) << "Wrote" << written << "bytes. File is now size" << d->dataFile.size(); } diff --git a/src/core/jobs/httpworker.h b/src/core/jobs/httpworker.h index 98ed8fec..ce053193 100644 --- a/src/core/jobs/httpworker.h +++ b/src/core/jobs/httpworker.h @@ -1,58 +1,58 @@ /* Copyright (C) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef HTTPWORKER_H #define HTTPWORKER_H #include #include class QNetworkReply; namespace KNSCore { class HTTPWorker : public QObject { Q_OBJECT public: enum JobType { GetJob, DownloadJob // Much the same as a get... except with a filesystem destination, rather than outputting data }; - explicit HTTPWorker(const QUrl& url, JobType jobType = GetJob, QObject* parent = 0); - explicit HTTPWorker(const QUrl& source, const QUrl& destination, JobType jobType = DownloadJob, QObject* parent = 0); + explicit HTTPWorker(const QUrl& url, JobType jobType = GetJob, QObject* parent = nullptr); + explicit HTTPWorker(const QUrl& source, const QUrl& destination, JobType jobType = DownloadJob, QObject* parent = nullptr); virtual ~HTTPWorker(); void startRequest(); void setUrl(const QUrl& url); Q_SIGNAL void error(QString error); Q_SIGNAL void progress(qlonglong current, qlonglong total); Q_SIGNAL void completed(); Q_SIGNAL void data(const QByteArray& data); Q_SLOT void handleReadyRead(); Q_SLOT void handleFinished(QNetworkReply* reply); Q_SLOT void handleData(const QByteArray& data); private: class Private; Private* d; }; } #endif//HTTPWORKER_H diff --git a/src/core/question.h b/src/core/question.h index cd5d7d78..92b87a1f 100644 --- a/src/core/question.h +++ b/src/core/question.h @@ -1,108 +1,108 @@ /* This file is part of KNewStuffCore. Copyright (c) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNS3_QUESTION_H #define KNS3_QUESTION_H #include #include "knewstuffcore_export.h" namespace KNSCore { /** * @short A way to ask a user a question from insude a GUI-less library (like KNewStuffCore) * * Rather than using a message box (which is a UI thing), when you want to ask your user * a question, create an instance of this class and use that instead. The consuming library * (in most cases KNewStuff or KNewStuffQuick) will listen to any question being asked, * and act appropriately (that is, KNewStuff will show a dialog with an appropriate dialog * box, and KNewStuffQuick will either request a question be asked if the developer is using * the plugin directly, or ask the question using an appropriate method for Qt Quick based * applications) * * The following is an example of a question asking the user to select an item from a list. * * @code QStringList choices() << "foo" << "bar"; Question question(Question::SelectFromListQuestion); question.setTitle("Pick your option"); question.setQuestion("Please select which option you would like"); question.setList(choices); if(question.ask() == Question::OKResponse) { QString theChoice = question.response(); } @endcode */ class KNEWSTUFFCORE_EXPORT Question : public QObject { Q_OBJECT public: enum Response { InvalidResponse = 0, YesResponse = 1, NoResponse = 2, ContinueResponse = 3, CancelResponse = 4, OKResponse = YesResponse }; enum QuestionType { YesNoQuestion = 0, ContinueCancelQuestion = 1, InputTextQuestion = 2, SelectFromListQuestion = 3, PasswordQuestion = 4 }; - explicit Question(QuestionType = YesNoQuestion, QObject* parent = 0); + explicit Question(QuestionType = YesNoQuestion, QObject* parent = nullptr); virtual ~Question(); Response ask(); void setQuestionType(QuestionType newType = YesNoQuestion); QuestionType questionType() const; void setQuestion(QString newQuestion); QString question() const; void setTitle(QString newTitle); QString title() const; void setList(QStringList newList); QStringList list() const; /** * When the user makes a choice on a question, that is a response. This is the return value in ask(). * @param response This will set the response, and mark the question as answered */ void setResponse(Response response); /** * If the user has any way of inputting data to go along with the response above, consider this a part * of the response. As such, you can set, and later get, that response as well. This does NOT mark the * question as answered ( @see setResponse(Response) ). * @param response This sets the string response for the question */ void setResponse(QString response); QString response(); private: class Private; Private* d; }; } #endif//KNS3_QUESTION_H diff --git a/src/core/questionlistener.h b/src/core/questionlistener.h index 9e71ea62..b975930c 100644 --- a/src/core/questionlistener.h +++ b/src/core/questionlistener.h @@ -1,51 +1,51 @@ /* This file is part of KNewStuffCore. Copyright (c) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNS3_QUESTIONLISTENER_H #define KNS3_QUESTIONLISTENER_H #include #include "knewstuffcore_export.h" namespace KNSCore { class Question; /** * @short Implementation-side handler class for questions sent from KNewStuffCore * * When implementing anything on top of KNewStuffCore, you will need to be able * to react to questions asked from inside the framework. This is done by creating * an instance of a QuestionListener, and reacting to any calls to the askQuestion * slot, which you must extend and implement. Two examples of this exist, in the * form of the KNS3::WidgetQuestionListener and KNewStuffQuick::QuickQuestionListener * and should you need to create your own, take inspiration from them. */ class KNEWSTUFFCORE_EXPORT QuestionListener : public QObject { Q_OBJECT public: - explicit QuestionListener(QObject* parent = 0); + explicit QuestionListener(QObject* parent = nullptr); virtual ~QuestionListener(); Q_SLOT virtual void askQuestion(Question* question); }; } #endif//KNS3_QUESTIONLISTENER_H diff --git a/src/core/questionmanager.cpp b/src/core/questionmanager.cpp index b229e571..c67d374e 100644 --- a/src/core/questionmanager.cpp +++ b/src/core/questionmanager.cpp @@ -1,57 +1,57 @@ /* This file is part of KNewStuffCore. Copyright (c) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "questionmanager.h" using namespace KNSCore; class QuestionManagerHelper { public: - QuestionManagerHelper() : q(0) {} + QuestionManagerHelper() : q(nullptr) {} ~QuestionManagerHelper() { delete q; } QuestionManager *q; }; Q_GLOBAL_STATIC(QuestionManagerHelper, s_kns3_questionManager) class QuestionManager::Private { public: Private() {} }; QuestionManager* QuestionManager::instance() { if(!s_kns3_questionManager()->q) { new QuestionManager; } return s_kns3_questionManager()->q; } QuestionManager::QuestionManager() - : QObject(0) + : QObject(nullptr) , d(new Private) { s_kns3_questionManager()->q = this; } QuestionManager::~QuestionManager() { delete d; } diff --git a/src/core/security.cpp b/src/core/security.cpp index d86bd779..5eed68a6 100644 --- a/src/core/security.cpp +++ b/src/core/security.cpp @@ -1,383 +1,383 @@ /* This file is part of KNewStuff2. Copyright (c) 2004, 2005 Andras Mantia Copyright (c) 2007 Josef Spillner This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ //app includes #include "security.h" #include "question.h" //qt includes #include #include #include #include #include #include #include #include //kde includes #include using namespace KNSCore; static QString gpgExecutable() { QString gpgExe = QStandardPaths::findExecutable(QStringLiteral("gpg")); if (gpgExe.isEmpty()) { gpgExe = QStandardPaths::findExecutable(QStringLiteral("gpg2")); } if (gpgExe.isEmpty()) { return QStringLiteral("gpg"); } return gpgExe; } Security::Security() { m_keysRead = false; m_gpgRunning = false; readKeys(); readSecretKeys(); } Security::~Security() { } void Security::readKeys() { if (m_gpgRunning) { QTimer::singleShot(5, this, SLOT(readKeys())); return; } m_runMode = List; m_keys.clear(); m_process = new QProcess(); QStringList arguments; arguments << QStringLiteral("--no-secmem-warning") << QStringLiteral("--no-tty") << QStringLiteral("--with-colon") << QStringLiteral("--list-keys"); connect(m_process, static_cast(&QProcess::finished), this, &Security::slotFinished); connect(m_process, &QProcess::readyReadStandardOutput, this, &Security::slotReadyReadStandardOutput); m_process->start(gpgExecutable(), arguments); if (!m_process->waitForStarted()) { emit signalError(i18n("Cannot start gpg and retrieve the available keys. Make sure that gpg is installed, otherwise verification of downloaded resources will not be possible.")); delete m_process; - m_process = 0; + m_process = nullptr; } else { m_gpgRunning = true; } } void Security::readSecretKeys() { if (m_gpgRunning) { QTimer::singleShot(5, this, SLOT(readSecretKeys())); return; } m_runMode = ListSecret; m_process = new QProcess(); QStringList arguments; arguments << QStringLiteral("--no-secmem-warning") << QStringLiteral("--no-tty") << QStringLiteral("--with-colon") << QStringLiteral("--list-secret-keys"); connect(m_process, static_cast(&QProcess::finished), this, &Security::slotFinished); connect(m_process, &QProcess::readyReadStandardOutput, this, &Security::slotReadyReadStandardOutput); m_process->start(gpgExecutable(), arguments); if (!m_process->waitForStarted()) { delete m_process; - m_process = 0; + m_process = nullptr; } else { m_gpgRunning = true; } } void Security::slotFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitStatus != QProcess::NormalExit) { m_gpgRunning = false; delete m_process; - m_process = 0; + m_process = nullptr; return; } switch (m_runMode) { case ListSecret: m_keysRead = true; break; case Verify: emit validityResult(m_result); break; case Sign: emit fileSigned(m_result); break; } m_gpgRunning = false; delete m_process; - m_process = 0; + m_process = nullptr; Q_UNUSED(exitCode) } void Security::slotReadyReadStandardOutput() { QString data; while (m_process->canReadLine()) { data = QString::fromLocal8Bit(m_process->readLine()); switch (m_runMode) { case List: case ListSecret: if (data.startsWith(QLatin1String("pub")) || data.startsWith(QLatin1String("sec"))) { KeyStruct key; if (data.startsWith(QLatin1String("pub"))) { key.secret = false; } else { key.secret = true; } QStringList line = data.split(':', QString::KeepEmptyParts); key.id = line[4]; QString shortId = key.id.right(8); QString trustStr = line[1]; key.trusted = false; if (trustStr == QLatin1String("u") || trustStr == QLatin1String("f")) { key.trusted = true; } data = line[9]; key.mail = data.section('<', -1, -1); key.mail.truncate(key.mail.length() - 1); key.name = data.section('<', 0, 0); if (key.name.contains(QStringLiteral("("))) { key.name = key.name.section('(', 0, 0); } m_keys[shortId] = key; } break; case Verify: data = data.section(']', 1, -1).trimmed(); if (data.startsWith(QLatin1String("GOODSIG"))) { m_result &= SIGNED_BAD_CLEAR; m_result |= SIGNED_OK; QString id = data.section(' ', 1, 1).right(8); if (!m_keys.contains(id)) { m_result |= UNKNOWN; } else { m_signatureKey = m_keys[id]; } } else if (data.startsWith(QLatin1String("NO_PUBKEY"))) { m_result &= SIGNED_BAD_CLEAR; m_result |= UNKNOWN; } else if (data.startsWith(QLatin1String("BADSIG"))) { m_result |= SIGNED_BAD; QString id = data.section(' ', 1, 1).right(8); if (!m_keys.contains(id)) { m_result |= UNKNOWN; } else { m_signatureKey = m_keys[id]; } } else if (data.startsWith(QLatin1String("TRUST_ULTIMATE"))) { m_result &= SIGNED_BAD_CLEAR; m_result |= TRUSTED; } break; case Sign: if (data.contains(QStringLiteral("passphrase.enter"))) { KeyStruct key = m_keys[m_secretKey]; Question question(Question::PasswordQuestion); question.setQuestion(i18n("Enter passphrase for key 0x%1, belonging to
%2<%3>
:
", m_secretKey, key.name, key.mail)); if(question.ask() == Question::ContinueResponse) { m_process->write(question.response().toLocal8Bit() + '\n'); } else { m_result |= BAD_PASSPHRASE; m_process->kill(); return; } } else if (data.contains(QStringLiteral("BAD_PASSPHRASE"))) { m_result |= BAD_PASSPHRASE; } break; } } } void Security::checkValidity(const QString &filename) { m_fileName = filename; slotCheckValidity(); } void Security::slotCheckValidity() { if (!m_keysRead || m_gpgRunning) { QTimer::singleShot(5, this, SLOT(slotCheckValidity())); return; } if (m_keys.count() == 0) { emit validityResult(-1); return; } m_result = 0; m_runMode = Verify; QFileInfo f(m_fileName); //check the MD5 sum QString md5sum; QCryptographicHash context(QCryptographicHash::Md5); QFile file(m_fileName); if (!m_fileName.isEmpty() && file.open(QIODevice::ReadOnly)) { context.reset(); context.addData(&file); md5sum = context.result().toHex(); file.close(); } file.setFileName(f.path() + "/md5sum"); if (file.open(QIODevice::ReadOnly)) { QByteArray md5sum_file; file.readLine(md5sum_file.data(), 50); if (!md5sum_file.isEmpty() && QString(md5sum_file).startsWith(md5sum)) { m_result |= MD5_OK; } file.close(); } m_result |= SIGNED_BAD; m_signatureKey.id = QLatin1String(""); m_signatureKey.name = QLatin1String(""); m_signatureKey.mail = QLatin1String(""); m_signatureKey.trusted = false; //verify the signature m_process = new QProcess(); QStringList arguments; arguments << QStringLiteral("--no-secmem-warning") << QStringLiteral("--status-fd=2") << QStringLiteral("--command-fd=0") << QStringLiteral("--verify") << f.path() + "/signature" << m_fileName; connect(m_process, static_cast(&QProcess::finished), this, &Security::slotFinished); connect(m_process, &QProcess::readyReadStandardOutput, this, &Security::slotReadyReadStandardOutput); m_process->start(gpgExecutable(), arguments); if (m_process->waitForStarted()) { m_gpgRunning = true; } else { emit signalError(i18n("Cannot start gpg and check the validity of the file. Make sure that gpg is installed, otherwise verification of downloaded resources will not be possible.")); emit validityResult(0); delete m_process; - m_process = 0; + m_process = nullptr; } } void Security::signFile(const QString &fileName) { m_fileName = fileName; slotSignFile(); } void Security::slotSignFile() { if (!m_keysRead || m_gpgRunning) { QTimer::singleShot(5, this, SLOT(slotSignFile())); return; } QStringList secretKeys; for (QMap::Iterator it = m_keys.begin(); it != m_keys.end(); ++it) { if (it.value().secret) { secretKeys.append(it.key()); } } if (secretKeys.count() == 0) { emit fileSigned(-1); return; } m_result = 0; QFileInfo f(m_fileName); //create the MD5 sum QString md5sum; QCryptographicHash context(QCryptographicHash::Md5); QFile file(m_fileName); if (file.open(QIODevice::ReadOnly)) { context.reset(); context.addData(&file); md5sum = context.result().toHex(); file.close(); } file.setFileName(f.path() + "/md5sum"); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream << md5sum; m_result |= MD5_OK; file.close(); } if (secretKeys.count() > 1) { Question question(Question::SelectFromListQuestion); question.setQuestion(i18n("Key used for signing:")); question.setTitle(i18n("Select Signing Key")); question.setList(secretKeys); if(question.ask() == Question::OKResponse) { m_secretKey = question.response(); } else { // emit an error to be forwarded to the user for selecting a signing key... emit fileSigned(0); return; } } else { m_secretKey = secretKeys[0]; } //verify the signature m_process = new QProcess(); QStringList arguments; arguments << QStringLiteral("--no-secmem-warning") << QStringLiteral("--status-fd=2") << QStringLiteral("--command-fd=0") << QStringLiteral("--no-tty") << QStringLiteral("--detach-sign") << QStringLiteral("-u") << m_secretKey << QStringLiteral("-o") << f.path() + "/signature" << m_fileName; connect(m_process, static_cast(&QProcess::finished), this, &Security::slotFinished); connect(m_process, &QProcess::readyReadStandardOutput, this, &Security::slotReadyReadStandardOutput); m_runMode = Sign; m_process->start(gpgExecutable(), arguments); if (m_process->waitForStarted()) { m_gpgRunning = true; } else { emit signalError(i18n("Cannot start gpg and sign the file. Make sure that gpg is installed, otherwise signing of the resources will not be possible.")); emit fileSigned(0); delete m_process; - m_process = 0; + m_process = nullptr; } } diff --git a/src/downloaddialog.h b/src/downloaddialog.h index 1991b0b8..9a6be6c2 100644 --- a/src/downloaddialog.h +++ b/src/downloaddialog.h @@ -1,153 +1,153 @@ /* knewstuff3/ui/downloaddialog.h. Copyright (C) 2005 by Enrico Ros Copyright (C) 2005 - 2007 Josef Spillner Copyright (C) 2007 Dirk Mueller Copyright (C) 2007-2009 Jeremy Whiting Copyright (C) 2009-2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_UI_DOWNLOADDIALOG_H #define KNEWSTUFF3_UI_DOWNLOADDIALOG_H #include #include "knewstuff_export.h" #include "entry.h" namespace KNSCore { class Engine; } namespace KNS3 { class DownloadDialogPrivate; /** * KNewStuff download dialog. * * The download dialog will present items to the user * for installation, updates and removal. * Preview images as well as other meta information can be seen. * * \section knsrc knsrc Files * The Dialog is configured by a .knsrc file containing the KHotNewStuff configuration. * Your application should install a file called: $KDEDIR/share/config/appname.knsrc * * The file could look like this for wallpapers: *
    [KNewStuff3]
    ProvidersUrl=http://download.kde.org/ocs/providers.xml
    Categories=KDE Wallpaper 1920x1200,KDE Wallpaper 1600x1200
    XdgTargetDir=wallpapers
    Uncompress=archive
  * 
* * Uncompress can be one of: always, never or archive: *
    *
  1. always: assume all downloaded files are archives and need to be extracted
  2. *
  3. never: never try to extract the file
  4. *
  5. archive: if the file is an archive, uncompress it, otherwise just pass it on
  6. *
  7. subdir: logic as archive, but decompress into a subdirectory named after the payload filename
  8. *
* * You have different options to set the target install directory: *
  1. StandardResource: not available in KF5, use XdgTargetDir instead.
  2. *
  3. TargetDir: since KF5, this is equivalent to XdgTargetDir. *
  4. XdgTargetDir: a directory in the $XDG_DATA_HOME directory such as .local/share/wallpapers. * This is what QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + name will return.
  5. *
* * @since 4.4 */ class KNEWSTUFF_EXPORT DownloadDialog : public QDialog { Q_OBJECT public: /** * Create a DownloadDialog that lets the user install, update and uninstall * contents. It will try to find a appname.knsrc file with the configuration. * Appname is the name of your application as provided in the about data-> * * @param parent the parent of the dialog */ - explicit DownloadDialog(QWidget *parent = 0); + explicit DownloadDialog(QWidget *parent = nullptr); /** * Create a DownloadDialog that lets the user install, update and uninstall * contents. Manually specify the name of a .knsrc file where the * KHotNewStuff configuration can be found. * * @param configFile the name of the configuration file * @param parent the parent of the dialog */ - explicit DownloadDialog(const QString &configFile, QWidget *parent = 0); + explicit DownloadDialog(const QString &configFile, QWidget *parent = nullptr); /** * destructor */ ~DownloadDialog(); /** * The list of entries with changed status (installed/uninstalled) * @return the list of entries */ KNS3::Entry::List changedEntries(); /** * The list of entries that have been newly installed * @return the list of entries */ KNS3::Entry::List installedEntries(); /** * Set the title for display purposes in the widget's title. * @param title the title of the application (or category or whatever) */ void setTitle(const QString &title); /** * Get the current title * @return the current title */ QString title() const; /** * @return the engine used by this dialog * @since 5.30 */ KNSCore::Engine *engine(); public Q_SLOTS: // Override these slots so we can add KAuthorized checks to them. int exec() Q_DECL_OVERRIDE; void open() Q_DECL_OVERRIDE; protected: void showEvent(QShowEvent *event) Q_DECL_OVERRIDE; private: void init(const QString &configFile); DownloadDialogPrivate *const d; Q_DISABLE_COPY(DownloadDialog) }; } #endif diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 67509ee8..ee4df3fa 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -1,156 +1,156 @@ /* Copyright (C) 2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_UI_DownloadManager_H #define KNEWSTUFF3_UI_DownloadManager_H #include "knewstuff_export.h" #include "entry.h" namespace KNS3 { class DownloadManagerPrivate; /** * KNewStuff update checker. * This class can be used to search for KNewStuff items * without using the widgets and to look for updates of * already installed items without showing the dialog. * @since 4.5 * @deprecated Use KNSCore::DownloadManager instead */ class KNEWSTUFF_DEPRECATED_EXPORT DownloadManager : public QObject { Q_OBJECT public: enum SortOrder { Newest, Alphabetical, Rating, Downloads }; /** * Create a DownloadManager * It will try to find a appname.knsrc file. * Appname is the name of your application as provided in the about data-> * * @param parent the parent of the dialog */ - explicit DownloadManager(QObject *parent = 0); + explicit DownloadManager(QObject *parent = nullptr); /** * Create a DownloadManager. Manually specifying the name of the .knsrc file. * * @param configFile the name of the configuration file * @param parent */ - explicit DownloadManager(const QString &configFile, QObject *parent = 0); + explicit DownloadManager(const QString &configFile, QObject *parent = nullptr); /** * destructor */ ~DownloadManager(); /** Search for a list of entries. searchResult will be emitted with the requested list. */ void search(int page = 0, int pageSize = 100); /** Check for available updates. Use searchResult to get notified as soon as an update has been found. */ void checkForUpdates(); /** Check for installed resources Use searchResult to get notified about installed entries. @since 5.28 */ void checkForInstalled(); /** Installs or updates an entry @param entry */ void installEntry(const KNS3::Entry &entry); /** * Uninstalls the given entry. * @param entry The entry which will be uninstalled. * @since 4.7 */ void uninstallEntry(const KNS3::Entry &entry); /** Sets the search term to filter the results on the server. Note that this function does not trigger a search. Use search after setting this. @param searchTerm */ void setSearchTerm(const QString &searchTerm); /** Set the sort order of the results. This depends on the server. Note that this function does not trigger a search. Use search after setting this. @see SortOrder @param order */ void setSearchOrder(SortOrder order); /** * Triggers a search for an entry with @p id as its unique id * * @see searchResult * * @since 5.28 */ void fetchEntryById(const QString &id); Q_SIGNALS: /** Returns the search result. This can be the list of updates after checkForUpdates or the result of a search. @param entries the list of results. entries is empty when nothing was found. */ void searchResult(const KNS3::Entry::List &entries); /** The entry status has changed: emitted when the entry has been installed, updated or removed. Use KNS3::Entry::status() to check the current status. @param entry the item that has been updated. */ void entryStatusChanged(const KNS3::Entry &entry); /** * Notifies that the engine couldn't be loaded properly and won't be suitable */ void errorFound(const QString &errorMessage); private: Q_PRIVATE_SLOT(d, void _k_slotProvidersLoaded()) Q_PRIVATE_SLOT(d, void _k_slotEngineError(const QString &error)) Q_PRIVATE_SLOT(d, void _k_slotEntryStatusChanged(const KNSCore::EntryInternal &entry)) Q_PRIVATE_SLOT(d, void _k_slotEntriesLoaded(const KNSCore::EntryInternal::List &entries)) KNS3::DownloadManagerPrivate *const d; Q_DISABLE_COPY(DownloadManager) }; } #endif diff --git a/src/downloadwidget.cpp b/src/downloadwidget.cpp index ea5bbab5..e4ea7013 100644 --- a/src/downloadwidget.cpp +++ b/src/downloadwidget.cpp @@ -1,429 +1,429 @@ /* knewstuff3/ui/downloaddialog.cpp. Copyright (C) 2005 by Enrico Ros Copyright (C) 2005 - 2007 Josef Spillner Copyright (C) 2007 Dirk Mueller Copyright (C) 2007-2009 Jeremy Whiting Copyright (C) 2009-2010 Frederik Gladhorn Copyright (C) 2010 Reza Fatahilah Shah This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "downloadwidget.h" #include "downloadwidget_p.h" #include #include #include #include #include #include #include #include "core/itemsmodel.h" #include "ui/itemsviewdelegate_p.h" #include "ui/itemsgridviewdelegate_p.h" #include "ui/widgetquestionlistener.h" #include "entry_p.h" using namespace KNS3; DownloadWidget::DownloadWidget(QWidget *parent) : QWidget(parent) , d(new DownloadWidgetPrivate(this)) { const QString name = QCoreApplication::applicationName(); init(name + ".knsrc"); } DownloadWidget::DownloadWidget(const QString &configFile, QWidget *parent) : QWidget(parent) , d(new DownloadWidgetPrivate(this)) { init(configFile); } void DownloadWidget::init(const QString &configFile) { d->init(configFile); WidgetQuestionListener::instance(); } DownloadWidget::~DownloadWidget() { delete d; } void DownloadWidget::setTitle(const QString &title) { d->ui.m_titleWidget->setText(title); } QString DownloadWidget::title() const { return d->ui.m_titleWidget->text(); } KNSCore::Engine *DownloadWidget::engine() { return d->engine; } Entry::List DownloadWidget::changedEntries() { Entry::List entries; foreach (const KNSCore::EntryInternal &e, d->changedEntries) { entries.append(EntryPrivate::fromInternal(&e)); } return entries; } Entry::List DownloadWidget::installedEntries() { Entry::List entries; foreach (const KNSCore::EntryInternal &e, d->changedEntries) { if (e.status() == Entry::Installed) { entries.append(EntryPrivate::fromInternal(&e)); } } return entries; } DownloadWidgetPrivate::DownloadWidgetPrivate(DownloadWidget *q) : q(q) , engine(new KNSCore::Engine) , model(new KNSCore::ItemsModel(engine)) - , messageTimer(0) + , messageTimer(nullptr) , dialogMode(false) { } DownloadWidgetPrivate::~DownloadWidgetPrivate() { delete messageTimer; delete delegate; delete model; delete engine; } void DownloadWidgetPrivate::slotResetMessage() // SLOT { ui.m_titleWidget->setComment(QString()); } void DownloadWidgetPrivate::slotNetworkTimeout() // SLOT { displayMessage(i18n("Timeout. Check Internet connection."), KTitleWidget::ErrorMessage); } void DownloadWidgetPrivate::sortingChanged() { KNSCore::Provider::SortMode sortMode = KNSCore::Provider::Newest; KNSCore::Provider::Filter filter = KNSCore::Provider::None; if (ui.ratingRadio->isChecked()) { sortMode = KNSCore::Provider::Rating; } else if (ui.mostDownloadsRadio->isChecked()) { sortMode = KNSCore::Provider::Downloads; } else if (ui.installedRadio->isChecked()) { filter = KNSCore::Provider::Installed; } model->clearEntries(); if (filter == KNSCore::Provider::Installed) { ui.m_searchEdit->clear(); } ui.m_searchEdit->setEnabled(filter != KNSCore::Provider::Installed); engine->setSortMode(sortMode); engine->setFilter(filter); } void DownloadWidgetPrivate::slotUpdateSearch() { if (searchTerm == ui.m_searchEdit->text().trimmed()) { return; } searchTerm = ui.m_searchEdit->text().trimmed(); } void DownloadWidgetPrivate::slotSearchTextChanged() { if (searchTerm == ui.m_searchEdit->text().trimmed()) { return; } searchTerm = ui.m_searchEdit->text().trimmed(); engine->setSearchTerm(ui.m_searchEdit->text().trimmed()); } void DownloadWidgetPrivate::slotCategoryChanged(int idx) { if (idx == 0) { // All Categories item selected, reset filter engine->setCategoriesFilter(QStringList()); } else { QString category = ui.m_categoryCombo->currentData().toString(); if (!category.isEmpty()) { QStringList filter(category); engine->setCategoriesFilter(filter); } } } void DownloadWidgetPrivate::slotInfo(QString provider, QString server, QString version) { QString link = QStringLiteral("%1").arg(server); QString infostring = i18n("Server: %1", link); infostring += i18n("
Provider: %1", provider); infostring += i18n("
Version: %1", version); - KMessageBox::information(0, + KMessageBox::information(nullptr, infostring, i18n("Provider information")); } void DownloadWidgetPrivate::slotEntryChanged(const KNSCore::EntryInternal &entry) { changedEntries.insert(entry); model->slotEntryChanged(entry); } void DownloadWidgetPrivate::slotPayloadFailed(const KNSCore::EntryInternal &entry) { - KMessageBox::error(0, i18n("Could not install %1", entry.name()), + KMessageBox::error(nullptr, i18n("Could not install %1", entry.name()), i18n("Get Hot New Stuff!")); } void DownloadWidgetPrivate::slotPayloadLoaded(QUrl url) { Q_UNUSED(url) } void DownloadWidgetPrivate::slotError(const QString &message) { - KMessageBox::error(0, message, i18n("Get Hot New Stuff")); + KMessageBox::error(nullptr, message, i18n("Get Hot New Stuff")); } void DownloadWidgetPrivate::scrollbarValueChanged(int value) { if ((double)value / ui.m_listView->verticalScrollBar()->maximum() > 0.9) { engine->requestMoreData(); } } void DownloadWidgetPrivate::init(const QString &configFile) { m_configFile = configFile; ui.setupUi(q); ui.m_titleWidget->setVisible(false); ui.closeButton->setVisible(dialogMode); ui.backButton->setVisible(false); KStandardGuiItem::assign(ui.backButton, KStandardGuiItem::Back); q->connect(ui.backButton, &QPushButton::clicked, this, &DownloadWidgetPrivate::slotShowOverview); q->connect(engine, &KNSCore::Engine::signalMessage, this, &DownloadWidgetPrivate::slotShowMessage); q->connect(engine, &KNSCore::Engine::signalBusy, ui.progressIndicator, &ProgressIndicator::busy); q->connect(engine, &KNSCore::Engine::signalError, ui.progressIndicator, &ProgressIndicator::error); q->connect(engine, &KNSCore::Engine::signalIdle, ui.progressIndicator, &ProgressIndicator::idle); q->connect(engine, &KNSCore::Engine::signalProvidersLoaded, this, &DownloadWidgetPrivate::slotProvidersLoaded); // Entries have been fetched and should be shown: q->connect(engine, &KNSCore::Engine::signalEntriesLoaded, this, &DownloadWidgetPrivate::slotEntriesLoaded); // An entry has changes - eg because it was installed q->connect(engine, &KNSCore::Engine::signalEntryChanged, this, &DownloadWidgetPrivate::slotEntryChanged); q->connect(engine, &KNSCore::Engine::signalResetView, model, &KNSCore::ItemsModel::clearEntries); q->connect(engine, &KNSCore::Engine::signalEntryPreviewLoaded, model, &KNSCore::ItemsModel::slotEntryPreviewLoaded); engine->init(configFile); delegate = new ItemsViewDelegate(ui.m_listView, engine, q); ui.m_listView->setItemDelegate(delegate); ui.m_listView->setModel(model); ui.iconViewButton->setIcon(QIcon::fromTheme(QStringLiteral("view-list-icons"))); ui.iconViewButton->setToolTip(i18n("Icons view mode")); ui.listViewButton->setIcon(QIcon::fromTheme(QStringLiteral("view-list-details"))); ui.listViewButton->setToolTip(i18n("Details view mode")); q->connect(ui.listViewButton, &QPushButton::clicked, this, &DownloadWidgetPrivate::slotListViewListMode); q->connect(ui.iconViewButton, &QPushButton::clicked, this, &DownloadWidgetPrivate::slotListViewIconMode); q->connect(ui.newestRadio, &QRadioButton::clicked, this, &DownloadWidgetPrivate::sortingChanged); q->connect(ui.ratingRadio, &QRadioButton::clicked, this, &DownloadWidgetPrivate::sortingChanged); q->connect(ui.mostDownloadsRadio, &QRadioButton::clicked, this, &DownloadWidgetPrivate::sortingChanged); q->connect(ui.installedRadio, &QRadioButton::clicked, this, &DownloadWidgetPrivate::sortingChanged); q->connect(ui.m_searchEdit, &KLineEdit::textChanged, this, &DownloadWidgetPrivate::slotSearchTextChanged); q->connect(ui.m_searchEdit, &KLineEdit::editingFinished, this, &DownloadWidgetPrivate::slotUpdateSearch); ui.m_providerLabel->setVisible(false); ui.m_providerCombo->setVisible(false); ui.m_providerCombo->addItem(i18n("All Providers")); QStringList categories = engine->categories(); if (categories.size() < 2) { ui.m_categoryLabel->setVisible(false); ui.m_categoryCombo->setVisible(false); } else { ui.m_categoryCombo->addItem(i18n("All Categories")); //NOTE: categories will be populated when we will get metadata from the server } connect(engine, &KNSCore::Engine::signalCategoriesMetadataLoded, this, [this](const QList &categories) { for (auto data : categories) { if (!data.displayName.isEmpty()) { ui.m_categoryCombo->addItem(data.displayName, data.name); } else { ui.m_categoryCombo->addItem(data.name, data.name); } } }); ui.detailsStack->widget(0)->layout()->setMargin(0); ui.detailsStack->widget(1)->layout()->setMargin(0); q->connect(ui.m_categoryCombo, static_cast(&KComboBox::activated), this, &DownloadWidgetPrivate::slotCategoryChanged); // let the search line edit trap the enter key, otherwise it closes the dialog ui.m_searchEdit->setTrapReturnKey(true); q->connect(ui.m_listView->verticalScrollBar(), &QScrollBar::valueChanged, this, &DownloadWidgetPrivate::scrollbarValueChanged); q->connect(ui.m_listView, SIGNAL(doubleClicked(QModelIndex)), delegate, SLOT(slotDetailsClicked(QModelIndex))); details = new EntryDetails(engine, &ui); q->connect(delegate, &KNS3::ItemsViewBaseDelegate::signalShowDetails, this, &DownloadWidgetPrivate::slotShowDetails); slotShowOverview(); } void DownloadWidgetPrivate::slotListViewListMode() { ui.listViewButton->setChecked(true); ui.iconViewButton->setChecked(false); setListViewMode(QListView::ListMode); } void DownloadWidgetPrivate::slotListViewIconMode() { ui.listViewButton->setChecked(false); ui.iconViewButton->setChecked(true); setListViewMode(QListView::IconMode); } void DownloadWidgetPrivate::setListViewMode(QListView::ViewMode mode) { if (ui.m_listView->viewMode() == mode) { return; } ItemsViewBaseDelegate *oldDelegate = delegate; if (mode == QListView::ListMode) { delegate = new ItemsViewDelegate(ui.m_listView, engine, q); ui.m_listView->setViewMode(QListView::ListMode); ui.m_listView->setResizeMode(QListView::Fixed); } else { delegate = new ItemsGridViewDelegate(ui.m_listView, engine, q); ui.m_listView->setViewMode(QListView::IconMode); ui.m_listView->setResizeMode(QListView::Adjust); } ui.m_listView->setItemDelegate(delegate); delete oldDelegate; q->connect(ui.m_listView, SIGNAL(doubleClicked(QModelIndex)), delegate, SLOT(slotDetailsClicked(QModelIndex))); q->connect(delegate, &KNS3::ItemsViewBaseDelegate::signalShowDetails, this, &DownloadWidgetPrivate::slotShowDetails); } void DownloadWidgetPrivate::slotProvidersLoaded() { qCDebug(KNEWSTUFF) << "providers loaded"; engine->reloadEntries(); } void DownloadWidgetPrivate::slotEntriesLoaded(const KNSCore::EntryInternal::List &entries) { foreach (const KNSCore::EntryInternal &entry, entries) { if (!categories.contains(entry.category())) { qCDebug(KNEWSTUFF) << "Found category: " << entry.category(); categories.insert(entry.category()); } } model->slotEntriesLoaded(entries); } void DownloadWidgetPrivate::slotShowMessage(const QString& msg) { displayMessage(msg, KTitleWidget::InfoMessage); } void DownloadWidgetPrivate::displayMessage(const QString &msg, KTitleWidget::MessageType type, int timeOutMs) { if (!messageTimer) { messageTimer = new QTimer; messageTimer->setSingleShot(true); q->connect(messageTimer, &QTimer::timeout, this, &DownloadWidgetPrivate::slotResetMessage); } // stop the pending timer if present messageTimer->stop(); // set text to messageLabel ui.m_titleWidget->setComment(msg, type); // single shot the resetColors timer (and create it if null) if (timeOutMs > 0) { qCDebug(KNEWSTUFF) << "starting the message timer for " << timeOutMs; messageTimer->start(timeOutMs); } } void DownloadWidgetPrivate::slotShowDetails(const KNSCore::EntryInternal &entry) { if (!entry.isValid()) { qCDebug(KNEWSTUFF) << "invalid entry"; return; } titleText = ui.m_titleWidget->text(); ui.backButton->setVisible(true); ui.detailsStack->setCurrentIndex(1); ui.descriptionScrollArea->verticalScrollBar()->setValue(0); ui.preview1->setImage(QImage()); ui.preview2->setImage(QImage()); ui.preview3->setImage(QImage()); ui.previewBig->setImage(QImage()); details->setEntry(entry); } void DownloadWidgetPrivate::slotShowOverview() { ui.backButton->setVisible(false); ui.updateButton->setVisible(false); ui.installButton->setVisible(false); ui.becomeFanButton->setVisible(false); ui.uninstallButton->setVisible(false); ui.detailsStack->setCurrentIndex(0); ui.m_titleWidget->setText(titleText); } #include "moc_downloadwidget.cpp" diff --git a/src/downloadwidget.h b/src/downloadwidget.h index 78bc85ea..847a8d47 100644 --- a/src/downloadwidget.h +++ b/src/downloadwidget.h @@ -1,170 +1,170 @@ /* knewstuff3/ui/downloaddialog.h. Copyright (C) 2005 by Enrico Ros Copyright (C) 2005 - 2007 Josef Spillner Copyright (C) 2007 Dirk Mueller Copyright (C) 2007-2009 Jeremy Whiting Copyright (C) 2009-2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_UI_DOWNLOADWIDGET_H #define KNEWSTUFF3_UI_DOWNLOADWIDGET_H #include #include "knewstuff_export.h" #include "entry.h" namespace KNSCore { class Engine; } namespace KNS3 { class DownloadWidgetPrivate; /** * KNewStuff download widget. * * The download widget will present items to the user * for installation, updates and removal. * Preview images as well as other meta information can be seen. * * \section knsrc knsrc Files * The Dialog is configured by a .knsrc file containing the KHotNewStuff configuration. * Your application should install a file called: $KDEDIR/share/config/appname.knsrc * * The file could look like this for wallpapers: *
    [KNewStuff3]
    ProvidersUrl=http://download.kde.org/ocs/providers.xml
    Categories=KDE Wallpaper 1920x1200,KDE Wallpaper 1600x1200
    StandardResource=wallpaper
    Uncompress=archive
  * 
* * Uncompress can be one of: always, never or archive: *
    *
  1. always: assume all downloaded files are archives and need to be extracted
  2. *
  3. never: never try to extract the file
  4. *
  5. archive: if the file is an archive, uncompress it, otherwise just pass it on
  6. *
  7. subdir: logic as archive, but decompress into a subdirectory named after the payload filename
  8. *
* * You have different options to set the target install directory: *
  1. StandardResource: not available in KF5, use XdgTargetDir instead.
  2. *
  3. TargetDir: since KF5, this is equivalent to XdgTargetDir. *
  4. XdgTargetDir: a directory in the $XDG_DATA_HOME directory such as .local/share/wallpapers. * This is what QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + name will return.
  5. *
* * @since 4.5 */ class KNEWSTUFF_EXPORT DownloadWidget : public QWidget { Q_OBJECT public: /** * Create a download widget that lets the user install, update and uninstall * contents. It will try to find a appname.knsrc file with the configuration. * Appname is the name of your application as provided in the about data-> * * @param parent the parent of the dialog */ - explicit DownloadWidget(QWidget *parent = 0); + explicit DownloadWidget(QWidget *parent = nullptr); /** * Create a download widget that lets the user install, update and uninstall * contents. Manually specify the name of a .knsrc file where the * KHotNewStuff configuration can be found. * * @param configFile the name of the configuration file * @param parent the parent of the dialog */ - explicit DownloadWidget(const QString &configFile, QWidget *parent = 0); + explicit DownloadWidget(const QString &configFile, QWidget *parent = nullptr); /** * destructor */ ~DownloadWidget(); /** * The list of entries with changed status (installed/uninstalled) * @return the list of entries */ KNS3::Entry::List changedEntries(); /** * The list of entries that have been newly installed * @return the list of entries */ KNS3::Entry::List installedEntries(); /** * Set the title for display purposes in the widget's title. * @param title the title of the application (or category or whatever) */ void setTitle(const QString &title); /** * Get the current title * @return the current title */ QString title() const; /** * @return the engine used by this download widget * @since 5.30 */ KNSCore::Engine *engine(); private: void init(const QString &configFile); DownloadWidgetPrivate *const d; Q_DISABLE_COPY(DownloadWidget) Q_PRIVATE_SLOT(d, void slotListViewListMode()) Q_PRIVATE_SLOT(d, void slotListViewIconMode()) Q_PRIVATE_SLOT(d, void slotProvidersLoaded()) Q_PRIVATE_SLOT(d, void slotEntriesLoaded(const KNSCore::EntryInternal::List &entries)) Q_PRIVATE_SLOT(d, void slotEntryChanged(const KNSCore::EntryInternal &entry)) Q_PRIVATE_SLOT(d, void slotShowDetails(const KNSCore::EntryInternal &entry)) Q_PRIVATE_SLOT(d, void slotShowOverview()) Q_PRIVATE_SLOT(d, void slotPayloadFailed(const KNSCore::EntryInternal &entry)) Q_PRIVATE_SLOT(d, void slotPayloadLoaded(QUrl url)) Q_PRIVATE_SLOT(d, void slotResetMessage()) Q_PRIVATE_SLOT(d, void slotNetworkTimeout()) Q_PRIVATE_SLOT(d, void sortingChanged()) Q_PRIVATE_SLOT(d, void slotSearchTextChanged()) Q_PRIVATE_SLOT(d, void slotUpdateSearch()) Q_PRIVATE_SLOT(d, void slotCategoryChanged(int)) Q_PRIVATE_SLOT(d, void slotInfo(QString provider, QString server, QString version)) Q_PRIVATE_SLOT(d, void slotError(const QString &message)) Q_PRIVATE_SLOT(d, void scrollbarValueChanged(int value)) friend class DownloadDialog; }; } #endif diff --git a/src/kmoretools/kmoretoolsmenufactory.cpp b/src/kmoretools/kmoretoolsmenufactory.cpp index 5e7d3b98..5da332c4 100644 --- a/src/kmoretools/kmoretoolsmenufactory.cpp +++ b/src/kmoretools/kmoretoolsmenufactory.cpp @@ -1,311 +1,311 @@ /* Copyright 2015 by Gregor Mi This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "kmoretoolsmenufactory.h" #include "kmoretools_p.h" #include "kmoretoolspresets_p.h" #include #include #include #include #include #include #include class KMoreToolsMenuFactoryPrivate { public: // Note that this object must live long enough in case the user opens // the "Configure..." dialog KMoreTools* kmt = nullptr; QMenu* menu = nullptr; }; class KMoreToolsLazyMenu : public QMenu { private Q_SLOTS: void onAboutToShow() { //qDebug() << "onAboutToShow"; clear(); m_aboutToShowFunc(this); } public: - KMoreToolsLazyMenu(QWidget* parent = 0) : QMenu(parent) { + KMoreToolsLazyMenu(QWidget* parent = nullptr) : QMenu(parent) { connect(this, &QMenu::aboutToShow, this, &KMoreToolsLazyMenu::onAboutToShow); } void setAboutToShowAction(std::function aboutToShowFunc) { m_aboutToShowFunc = aboutToShowFunc; } private: std::function m_aboutToShowFunc; }; KMoreToolsMenuFactory::KMoreToolsMenuFactory(const QString& uniqueId) : d(new KMoreToolsMenuFactoryPrivate()) { d->kmt = new KMoreTools(uniqueId); } KMoreToolsMenuFactory::~KMoreToolsMenuFactory() { if (d->menu) { delete d->menu; } delete d->kmt; delete d; } // "file static" => no symbol will be exported static void addItemFromKmtService(KMoreToolsMenuBuilder* menuBuilder, QMenu* menu, KMoreToolsService* kmtService, const QUrl& url, bool isMoreSection ) { auto menuItem = menuBuilder->addMenuItem(kmtService, isMoreSection ? KMoreTools::MenuSection_More : KMoreTools::MenuSection_Main); if (kmtService->isInstalled()) { auto kService = kmtService->installedService(); if (!kService) { // if the corresponding desktop file is not installed // then the isInstalled was true because of the Exec line check // and we use the desktopfile provided by KMoreTools. // Otherwise *kService would crash. qDebug() << "Desktop file not installed:" << kmtService->desktopEntryName() << "=> Use desktop file provided by KMoreTools"; kService = kmtService->kmtProvidedService(); } if (!url.isEmpty() && kmtService->maxUrlArgCount() > 0) { menu->connect(menuItem->action(), &QAction::triggered, menu, [kService, url](bool) { KRun::runService(*kService, { url }, nullptr); }); } else { menu->connect(menuItem->action(), &QAction::triggered, menu, [kService](bool) { KRun::runService(*kService, { }, nullptr); }); } } } // "file static" => no symbol will be exported static void addItemsFromKmtServiceList(KMoreToolsMenuBuilder* menuBuilder, QMenu* menu, QList kmtServiceList, const QUrl& url, bool isMoreSection, QString firstMoreSectionDesktopEntryName ) { Q_FOREACH(auto kmtService, kmtServiceList) { // Check the pointer just in case a null pointer got in somewhere if (!kmtService) continue; if (kmtService->desktopEntryName() == firstMoreSectionDesktopEntryName) { // once we reach the potential first "more section desktop entry name" // all remaining services are added to the more section by default isMoreSection = true; } addItemFromKmtService(menuBuilder, menu, kmtService, url, isMoreSection); } } /** * "file static" => no symbol will be exported * @param isMoreSection: true => all items will be added into the more section * @param firstMoreSectionDesktopEntryName: only valid when @p isMoreSection is false: * see KMoreToolsPresets::registerServicesByGroupingNames */ static void addItemsForGroupingNameWithSpecialHandling(KMoreToolsMenuBuilder* menuBuilder, QMenu* menu, QList kmtServiceList, const QString& groupingName, const QUrl& url, bool isMoreSection, QString firstMoreSectionDesktopEntryName ) { // // special handlings // if (groupingName == QLatin1String("disk-usage") && !url.isEmpty()) { // // "disk-usage" plus a given URL. If no url is given there is no need // for special handling // auto filelightAppIter = std::find_if(kmtServiceList.begin(), kmtServiceList.end(), [](KMoreToolsService* s) { return s->desktopEntryName() == QLatin1String("org.kde.filelight"); }); if (filelightAppIter != kmtServiceList.end()) { auto filelightApp = *filelightAppIter; // because we later add all remaining items kmtServiceList.removeOne(filelightApp); if (url.isLocalFile()) { // 2015-01-12: Filelight can handle FTP connections // but KIO/kioexec cannot (bug or feature?), so we // don't offer it in this case const auto filelight1Item = menuBuilder->addMenuItem(filelightApp); if (filelightApp->isInstalled()) { const auto filelightService = filelightApp->installedService(); filelight1Item->action()->setText(filelightApp->formatString( i18nc("@action:inmenu", "$GenericName - current folder"))); menu->connect(filelight1Item->action(), &QAction::triggered, menu, [filelightService, url](bool) { KRun::runService(*filelightService, { url }, nullptr); }); const auto filelight2Item = menuBuilder->addMenuItem(filelightApp); filelight2Item->action()->setText(filelightApp->formatString( i18nc("@action:inmenu", "$GenericName - current device"))); menu->connect(filelight2Item->action(), &QAction::triggered, menu, [filelightService, url](bool) { KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByPath(url.toLocalFile()); KRun::runService(*filelightService, { QUrl::fromLocalFile(mountPoint->mountPoint()) }, nullptr); }); } } auto filelight3Item = menuBuilder->addMenuItem(filelightApp, KMoreTools::MenuSection_More); if (filelightApp->isInstalled()) { filelight3Item->action()->setText(filelightApp->formatString( i18nc("@action:inmenu", "$GenericName - all devices"))); const auto filelightService = filelightApp->installedService(); menu->connect(filelight3Item->action(), &QAction::triggered, menu, [filelightService](bool) { KRun::runService(*filelightService, { }, nullptr); }); } } else { qWarning() << "org.kde.filelight should be present in KMoreTools but it is not!"; } } else if (groupingName == QLatin1String("disk-partitions")) { // better because the Partition editors all have the same GenericName menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName ($Name)")); addItemsFromKmtServiceList(menuBuilder, menu, kmtServiceList, url, isMoreSection, firstMoreSectionDesktopEntryName); menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName")); // set back to default return; // skip processing remaining list (would result in duplicates) } else if (groupingName == QLatin1String("git-clients-and-actions")) { // Here we change the default item text and make sure that the url // argument is properly handled. // menuBuilder->setInitialItemTextTemplate(QStringLiteral("$Name")); // just use the application name Q_FOREACH(auto kmtService, kmtServiceList) { // Check the pointer just in case a null pointer got in somewhere if (!kmtService) continue; QUrl argUrl = url; if (url.isLocalFile()) { // this can only be done for local files, remote urls probably won't work for git clients anyway // by default we need an URL pointing to a directory // (this impl currently leads to wrong behaviour if the root dir of a git repo is chosen because it always goes one level up) argUrl = KmtUrlUtil::localFileAbsoluteDir(url); // needs local file if (kmtService->desktopEntryName() == _("git-cola-view-history.kmt-edition")) { // in this case we need the file because we would like to see its history argUrl = url; } } addItemFromKmtService(menuBuilder, menu, kmtService, argUrl, isMoreSection); } menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName")); // set back to default return; // skip processing remaining list (would result in duplicates) } // // default handling (or process remaining list) // menuBuilder->setInitialItemTextTemplate(QStringLiteral("$Name")); // just use the application name addItemsFromKmtServiceList(menuBuilder, menu, kmtServiceList, url, isMoreSection, firstMoreSectionDesktopEntryName); menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName")); // set back to default } QMenu* KMoreToolsMenuFactory::createMenuFromGroupingNames( const QStringList& groupingNames, const QUrl& url) { if (d->menu) { delete d->menu; } auto menu = new KMoreToolsLazyMenu(); menu->setAboutToShowAction([this, groupingNames, url](QMenu* m) { fillMenuFromGroupingNames(m, groupingNames, url); }); d->menu = menu; return d->menu; } void KMoreToolsMenuFactory::fillMenuFromGroupingNames(QMenu* menu, const QStringList& groupingNames, const QUrl& url) { const auto menuBuilder = d->kmt->menuBuilder(); menuBuilder->clear(); bool isMoreSection = false; Q_FOREACH(const auto &groupingName, groupingNames) { if (groupingName == QLatin1String("more:")) { isMoreSection = true; continue; } QString firstMoreSectionDesktopEntryName; auto kmtServiceList = KMoreToolsPresetsPrivate::registerServicesByGroupingNames( &firstMoreSectionDesktopEntryName, d->kmt, { groupingName }); addItemsForGroupingNameWithSpecialHandling(menuBuilder, menu, kmtServiceList, groupingName, url, isMoreSection, firstMoreSectionDesktopEntryName); } menuBuilder->buildByAppendingToMenu(menu); } diff --git a/src/knewstuffaction.h b/src/knewstuffaction.h index fb2a155e..5406ee69 100644 --- a/src/knewstuffaction.h +++ b/src/knewstuffaction.h @@ -1,71 +1,71 @@ /* This file is part of KNewStuff2. Copyright (c) 2002 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_KNEWSTUFFACTION_H #define KNEWSTUFF3_KNEWSTUFFACTION_H #include "knewstuff_export.h" #include class QObject; class QAction; class KActionCollection; namespace KNS3 { /** * @brief Standard action for the Hot New Stuff Download * * This action can be used to add KNewStuff support to menus and toolbars. * * @param what text describing what is being downloaded. For consistency, * set it to "Get New Foobar...". * Examples: "Get New Wallpapers...", "Get New Emoticons..." * @param receiver the QObject to connect the triggered(bool) signal to. * @param slot the slot to connect the triggered(bool) signal to. * @param parent the action's parent collection. * @param name The name by which the action will be retrieved again from the collection. * @since 4.4 */ KNEWSTUFF_EXPORT QAction *standardAction(const QString &what, const QObject *receiver, const char *slot, KActionCollection *parent, - const char *name = 0); + const char *name = nullptr); /** * @brief Standard action for Uploading files with Hot New Stuff * * This action can be used to add KNewStuff support to menus and toolbars. * * @param what text describing what is being downloaded. For consistency, * set it to "Upload Current Foobar...". * Examples: "Upload Current Wallpaper...", "Upload Current Document..." * @param receiver the QObject to connect the triggered(bool) signal to. * @param slot the slot to connect the triggered(bool) signal to. * @param parent the action's parent collection. * @param name The name by which the action will be retrieved again from the collection. * @since 4.5 */ KNEWSTUFF_EXPORT QAction *standardActionUpload(const QString &what, const QObject *receiver, const char *slot, KActionCollection *parent, - const char *name = 0); + const char *name = nullptr); } #endif // KNEWSTUFFACTION_H diff --git a/src/qtquick/downloadlinkinfo.h b/src/qtquick/downloadlinkinfo.h index f445dc80..cd7bf6d8 100644 --- a/src/qtquick/downloadlinkinfo.h +++ b/src/qtquick/downloadlinkinfo.h @@ -1,58 +1,58 @@ /* * Copyright (C) 2016 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #ifndef DOWNLOADLINKINFO_H #define DOWNLOADLINKINFO_H #include #include "entryinternal.h" class DownloadLinkInfo : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name NOTIFY dataChanged) Q_PROPERTY(QString priceAmount READ priceAmount NOTIFY dataChanged) Q_PROPERTY(QString distributionType READ distributionType NOTIFY dataChanged) Q_PROPERTY(QString descriptionLink READ descriptionLink NOTIFY dataChanged) Q_PROPERTY(int id READ id NOTIFY dataChanged) Q_PROPERTY(bool isDownloadtypeLink READ isDownloadtypeLink NOTIFY dataChanged) Q_PROPERTY(quint64 size READ size NOTIFY dataChanged) public: - explicit DownloadLinkInfo(QObject* parent = 0); + explicit DownloadLinkInfo(QObject* parent = nullptr); virtual ~DownloadLinkInfo(); void setData(const KNSCore::EntryInternal::DownloadLinkInformation& data); Q_SIGNAL void dataChanged(); QString name() const; QString priceAmount() const; QString distributionType() const; QString descriptionLink() const; int id() const; bool isDownloadtypeLink() const; quint64 size() const; private: class Private; Private* d; }; #endif//DOWNLOADLINKINFO_H diff --git a/src/qtquick/quickengine.cpp b/src/qtquick/quickengine.cpp index d76c0db5..69bcfa2c 100644 --- a/src/qtquick/quickengine.cpp +++ b/src/qtquick/quickengine.cpp @@ -1,71 +1,71 @@ /* * Copyright (C) 2016 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "quickengine.h" #include "engine.h" class Engine::Private { public: Private() - : engine(0) + : engine(nullptr) {} KNSCore::Engine* engine; QString configFile; }; Engine::Engine(QObject* parent) : QObject(parent) , d(new Private) { } Engine::~Engine() { delete d; } QString Engine::configFile() const { return d->configFile; } void Engine::setConfigFile(const QString& newFile) { d->configFile = newFile; emit configFileChanged(); if(!d->engine) { d->engine = new KNSCore::Engine(this); connect(d->engine, &KNSCore::Engine::signalMessage, this, &Engine::message); connect(d->engine, &KNSCore::Engine::signalIdle, this, &Engine::idleMessage); connect(d->engine, &KNSCore::Engine::signalBusy, this, &Engine::busyMessage); connect(d->engine, &KNSCore::Engine::signalError, this, &Engine::errorMessage); emit engineChanged(); } d->engine->init(d->configFile); } QObject * Engine::engine() const { return d->engine; } diff --git a/src/qtquick/quickengine.h b/src/qtquick/quickengine.h index afbd9e98..df3d55c4 100644 --- a/src/qtquick/quickengine.h +++ b/src/qtquick/quickengine.h @@ -1,54 +1,54 @@ /* * Copyright (C) 2016 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #ifndef ENGINE_H #define ENGINE_H #include class Engine : public QObject { Q_OBJECT Q_PROPERTY(QString configFile READ configFile WRITE setConfigFile NOTIFY configFileChanged) Q_PROPERTY(QObject* engine READ engine NOTIFY engineChanged) public: - explicit Engine(QObject* parent = 0); + explicit Engine(QObject* parent = nullptr); virtual ~Engine(); QString configFile() const; void setConfigFile(const QString& newFile); Q_SIGNAL void configFileChanged(); QObject* engine() const; Q_SIGNAL void engineChanged(); Q_SIGNALS: void message(const QString& message); void idleMessage(const QString& message); void busyMessage(const QString& message); void errorMessage(const QString& message); private: class Private; Private* d; }; #endif//ENGINE_H diff --git a/src/qtquick/quickitemsmodel.cpp b/src/qtquick/quickitemsmodel.cpp index c88c6aef..979d28ad 100644 --- a/src/qtquick/quickitemsmodel.cpp +++ b/src/qtquick/quickitemsmodel.cpp @@ -1,367 +1,367 @@ /* * Copyright (C) 2016 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "quickitemsmodel.h" #include "quickengine.h" #include "itemsmodel.h" #include "engine.h" #include "downloadlinkinfo.h" class ItemsModel::Private { public: Private(ItemsModel* qq) : q(qq) - , model(0) - , engine(0) + , model(nullptr) + , engine(nullptr) {} ItemsModel* q; KNSCore::ItemsModel* model; KNSCore::Engine* engine; bool initModel() { if(model) { return true; } if(!engine) { return false; } model = new KNSCore::ItemsModel(engine, q); q->connect(engine, &KNSCore::Engine::signalProvidersLoaded, engine, &KNSCore::Engine::reloadEntries); // Entries have been fetched and should be shown: q->connect(engine, &KNSCore::Engine::signalEntriesLoaded, model, &KNSCore::ItemsModel::slotEntriesLoaded); // An entry has changes - eg because it was installed q->connect(engine, &KNSCore::Engine::signalEntryChanged, model, &KNSCore::ItemsModel::slotEntryChanged); q->connect(engine, &KNSCore::Engine::signalResetView, model, &KNSCore::ItemsModel::clearEntries); q->connect(engine, &KNSCore::Engine::signalEntryPreviewLoaded, model, &KNSCore::ItemsModel::slotEntryPreviewLoaded); q->connect(model, &KNSCore::ItemsModel::rowsInserted, q, &ItemsModel::rowsInserted); q->connect(model, &KNSCore::ItemsModel::rowsRemoved, q, &ItemsModel::rowsRemoved); q->connect(model, &KNSCore::ItemsModel::dataChanged, q, &ItemsModel::dataChanged); q->connect(model, &KNSCore::ItemsModel::modelReset, q, &ItemsModel::modelReset); return true; } }; ItemsModel::ItemsModel(QObject* parent) : QAbstractListModel(parent) , d(new Private(this)) { } ItemsModel::~ItemsModel() { delete d; } QHash ItemsModel::roleNames() const { static const QHash roles = QHash{ {Qt::DisplayRole, "display"}, {NameRole, "name"}, {UniqueIdRole, "uniqueId"}, {CategoryRole, "category"}, {HomepageRole, "homepage"}, {AuthorRole, "author"}, {LicenseRole, "license"}, {ShortSummaryRole, "shortSummary"}, {SummaryRole, "summary"}, {ChangelogRole, "changelog"}, {VersionRole, "version"}, {ReleaseDateRole, "releaseDate"}, {UpdateVersionRole, "updateVersion"}, {UpdateReleaseDateRole, "updateReleaseDate"}, {PayloadRole, "payload"}, {Qt::DecorationRole, "decoration"}, {PreviewsSmallRole, "previewsSmall"}, {PreviewsRole, "previews"}, {InstalledFilesRole, "installedFiles"}, {UnInstalledFilesRole, "uninstalledFiles"}, {RatingRole, "rating"}, {NumberOfCommentsRole, "numberOfComments"}, {DownloadCountRole, "downloadCount"}, {NumberFansRole, "numberFans"}, {NumberKnowledgebaseEntriesRole, "numberKnowledgebaseEntries"}, {KnowledgebaseLinkRole, "knowledgebaseLink"}, {DownloadLinksRole, "downloadLinks"}, {DonationLinkRole, "donationLink"}, {ProviderIdRole, "providerId"}, {SourceRole, "source"}, {StatusRole, "status"} }; return roles; } int ItemsModel::rowCount(const QModelIndex& parent) const { if(parent.isValid()) return 0; if(d->initModel()) return d->model->rowCount(QModelIndex()); return 0; } QVariant ItemsModel::data(const QModelIndex& index, int role) const { QVariant data; if(index.isValid() && d->initModel()) { KNSCore::EntryInternal entry = d->model->data(d->model->index(index.row()), Qt::UserRole).value(); switch(role) { case NameRole: case Qt::DisplayRole: data.setValue(entry.name()); break; case UniqueIdRole: data.setValue(entry.uniqueId()); break; case CategoryRole: data.setValue(entry.category()); break; case HomepageRole: data.setValue(entry.homepage()); break; case AuthorRole: { KNSCore::Author author = entry.author(); QVariantMap returnAuthor; returnAuthor["name"] = author.name(); returnAuthor["email"] = author.email(); returnAuthor["homepage"] = author.homepage(); returnAuthor["jabber"] = author.jabber(); data.setValue<>(returnAuthor); } break; case LicenseRole: data.setValue(entry.license()); break; case ShortSummaryRole: data.setValue(entry.shortSummary()); break; case SummaryRole: data.setValue(entry.summary()); break; case ChangelogRole: data.setValue(entry.changelog()); break; case VersionRole: data.setValue(entry.version()); break; case ReleaseDateRole: data.setValue(entry.releaseDate()); break; case UpdateVersionRole: data.setValue(entry.updateVersion()); break; case UpdateReleaseDateRole: data.setValue(entry.updateReleaseDate()); break; case PayloadRole: data.setValue(entry.payload()); break; case Qt::DecorationRole: data.setValue(entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1)); break; case PreviewsSmallRole: { QStringList previews; previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1); previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall2); previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall3); while(previews.last().isEmpty()) { previews.takeLast(); } data.setValue(previews); } break; case PreviewsRole: { QStringList previews; previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig1); previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig2); previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig3); while(previews.last().isEmpty()) { previews.takeLast(); } data.setValue(previews); } break; case InstalledFilesRole: data.setValue(entry.installedFiles()); break; case UnInstalledFilesRole: data.setValue(entry.uninstalledFiles()); break; case RatingRole: data.setValue(entry.rating()); break; case NumberOfCommentsRole: data.setValue(entry.numberOfComments()); break; case DownloadCountRole: data.setValue(entry.downloadCount()); break; case NumberFansRole: data.setValue(entry.numberFans()); break; case NumberKnowledgebaseEntriesRole: data.setValue(entry.numberKnowledgebaseEntries()); break; case KnowledgebaseLinkRole: data.setValue(entry.knowledgebaseLink()); break; case DownloadLinksRole: { // This would be good to cache... but it also needs marking as dirty, somehow... QList dllinks = entry.downloadLinkInformationList(); QObjectList list; Q_FOREACH(const KNSCore::EntryInternal::DownloadLinkInformation& link, dllinks) { DownloadLinkInfo* info = new DownloadLinkInfo(); info->setData(link); list.append(info); } data.setValue(list); } break; case DonationLinkRole: data.setValue(entry.donationLink()); break; case ProviderIdRole: data.setValue(entry.providerId()); break; case SourceRole: { KNSCore::EntryInternal::Source src = entry.source(); switch(src) { case KNSCore::EntryInternal::Cache: data.setValue(QLatin1String("Cache")); break; case KNSCore::EntryInternal::Online: data.setValue(QLatin1String("Online")); break; case KNSCore::EntryInternal::Registry: data.setValue(QLatin1String("Registry")); break; default: data.setValue(QLatin1String("Unknown source - shouldn't be possible")); break; } } break; case StatusRole: { KNS3::Entry::Status status = entry.status(); switch(status) { case KNS3::Entry::Downloadable: data.setValue(ItemsModel::DownloadableStatus); break; case KNS3::Entry::Installed: data.setValue(ItemsModel::InstalledStatus); break; case KNS3::Entry::Updateable: data.setValue(ItemsModel::UpdateableStatus); break; case KNS3::Entry::Deleted: data.setValue(ItemsModel::DeletedStatus); break; case KNS3::Entry::Installing: data.setValue(ItemsModel::InstallingStatus); break; case KNS3::Entry::Updating: data.setValue(ItemsModel::UpdatingStatus); break; case KNS3::Entry::Invalid: default: data.setValue(ItemsModel::InvalidStatus); break; } } break; default: data.setValue(QLatin1String("Unknown role")); break; } } return data; } bool ItemsModel::canFetchMore(const QModelIndex& parent) const { if(parent.isValid()) { return false; } return true; } void ItemsModel::fetchMore(const QModelIndex& parent) { if(parent.isValid()) { return; } d->engine->requestMoreData(); } QObject * ItemsModel::engine() const { return d->engine; } void ItemsModel::setEngine(QObject* newEngine) { beginResetModel(); Engine* test = qobject_cast(newEngine); if(test) { d->engine = qobject_cast(test->engine()); } else { d->engine = qobject_cast(newEngine); } emit engineChanged(); endResetModel(); } void ItemsModel::installItem(int index) { if(d->engine) { KNSCore::EntryInternal entry = d->model->data(d->model->index(index), Qt::UserRole).value(); if(entry.isValid()) { d->engine->install(entry); } } } void ItemsModel::uninstallItem(int index) { if(d->engine) { KNSCore::EntryInternal entry = d->model->data(d->model->index(index), Qt::UserRole).value(); if(entry.isValid()) { d->engine->uninstall(entry); } } } diff --git a/src/qtquick/quickitemsmodel.h b/src/qtquick/quickitemsmodel.h index dd5680f9..26d3eeb5 100644 --- a/src/qtquick/quickitemsmodel.h +++ b/src/qtquick/quickitemsmodel.h @@ -1,113 +1,113 @@ /* * Copyright (C) 2016 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #ifndef ITEMSMODEL_H #define ITEMSMODEL_H #include class ItemsModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QObject* engine READ engine WRITE setEngine NOTIFY engineChanged) public: - explicit ItemsModel(QObject* parent = 0); + explicit ItemsModel(QObject* parent = nullptr); virtual ~ItemsModel(); enum Roles { NameRole = Qt::UserRole + 1, UniqueIdRole, CategoryRole, HomepageRole, AuthorRole, LicenseRole, ShortSummaryRole, SummaryRole, ChangelogRole, VersionRole, ReleaseDateRole, UpdateVersionRole, UpdateReleaseDateRole, PayloadRole, PreviewsSmallRole, ///@< this will return a list here, rather than be tied so tightly to the remote api PreviewsRole, ///@< this will return a list here, rather than be tied so tightly to the remote api InstalledFilesRole, UnInstalledFilesRole, RatingRole, NumberOfCommentsRole, DownloadCountRole, NumberFansRole, NumberKnowledgebaseEntriesRole, KnowledgebaseLinkRole, DownloadLinksRole, DonationLinkRole, ProviderIdRole, SourceRole, StatusRole }; enum ItemStatus { InvalidStatus, DownloadableStatus, InstalledStatus, UpdateableStatus, DeletedStatus, InstallingStatus, UpdatingStatus }; Q_ENUM(ItemStatus) virtual QHash< int, QByteArray > roleNames() const Q_DECL_OVERRIDE; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE; virtual bool canFetchMore(const QModelIndex & parent) const Q_DECL_OVERRIDE; virtual void fetchMore(const QModelIndex & parent) Q_DECL_OVERRIDE; QObject* engine() const; void setEngine(QObject* newEngine); Q_SIGNAL void engineChanged(); /** * @brief This will install (or update, if already installed) the item at the given index * * There are no side effects of this function if it is called on an item which cannot be * installed or updated (that is, if the status is not one such that these are possible, * the function will simply return without performing any actions) * * @param index The index of the item to install or update */ Q_INVOKABLE void installItem(int index); /** * @brief Uninstall an already installed item * * There are no side effects of this function if it is called on an item which cannot be * uninstalled (that is, if the status is not one such that this is possible, * the function will simply return without performing any actions) * * @param index The index of the item to be uninstalled */ Q_INVOKABLE void uninstallItem(int index); private: class Private; Private* d; }; Q_DECLARE_METATYPE(ItemsModel::ItemStatus) #endif//ITEMSMODEL_H diff --git a/src/ui/entrydetailsdialog.cpp b/src/ui/entrydetailsdialog.cpp index bbb4eace..b8b036b4 100644 --- a/src/ui/entrydetailsdialog.cpp +++ b/src/ui/entrydetailsdialog.cpp @@ -1,306 +1,306 @@ /* Copyright (C) 2009 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "entrydetailsdialog_p.h" #include #include #include #include "core/engine.h" #include "core/imageloader_p.h" #include using namespace KNS3; EntryDetails::EntryDetails(KNSCore::Engine *engine, Ui::DownloadWidget *widget) : QObject(widget->m_listView), m_engine(engine), ui(widget) { init(); } EntryDetails::~EntryDetails() { } void EntryDetails::init() { connect(ui->preview1, &ImagePreviewWidget::clicked, this, &EntryDetails::preview1Selected); connect(ui->preview2, &ImagePreviewWidget::clicked, this, &EntryDetails::preview2Selected); connect(ui->preview3, &ImagePreviewWidget::clicked, this, &EntryDetails::preview3Selected); ui->ratingWidget->setMaxRating(10); ui->ratingWidget->setHalfStepsEnabled(true); updateButtons(); connect(ui->installButton, &QAbstractButton::clicked, this, &EntryDetails::install); connect(ui->uninstallButton, &QAbstractButton::clicked, this, &EntryDetails::uninstall); // updating is the same as installing connect(ui->updateButton, &QAbstractButton::clicked, this, &EntryDetails::install); connect(ui->becomeFanButton, &QAbstractButton::clicked, this, &EntryDetails::becomeFan); ui->installButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok"))); ui->updateButton->setIcon(QIcon::fromTheme(QStringLiteral("system-software-update"))); ui->uninstallButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); connect(m_engine, &KNSCore::Engine::signalEntryDetailsLoaded, this, &EntryDetails::entryChanged); connect(m_engine, &KNSCore::Engine::signalEntryChanged, this, &EntryDetails::entryStatusChanged); connect(m_engine, &KNSCore::Engine::signalEntryPreviewLoaded, this, &EntryDetails::slotEntryPreviewLoaded); } void EntryDetails::setEntry(const KNSCore::EntryInternal &entry) { m_entry = entry; // immediately show something entryChanged(m_entry); // fetch more preview images m_engine->loadDetails(m_entry); } void EntryDetails::entryChanged(const KNSCore::EntryInternal &entry) { if (ui->detailsStack->currentIndex() == 0) { return; } m_entry = entry; // FIXME //ui->ratingWidget->setEditable(m_engine->userCanVote(m_entry)); if (!m_engine->userCanBecomeFan(m_entry)) { ui->becomeFanButton->setEnabled(false); } ui->m_titleWidget->setText(i18n("Details for %1", m_entry.name())); if (!m_entry.author().homepage().isEmpty()) { ui->authorLabel->setText("" + m_entry.author().name() + ""); } else if (!m_entry.author().email().isEmpty()) { ui->authorLabel->setText("" + m_entry.author().name() + ""); } else { ui->authorLabel->setText(m_entry.author().name()); } QString summary = KNSCore::replaceBBCode(m_entry.summary()).replace('\n', QLatin1String("
")); QString changelog = KNSCore::replaceBBCode(m_entry.changelog()).replace('\n', QLatin1String("
")); QString description = "" + summary; if (!changelog.isEmpty()) { description += "

" + i18n("Changelog:") + "
" + changelog + "

"; } description += QLatin1String(""); ui->descriptionLabel->setText(description); QString homepageText("" + i18nc("A link to the description of this Get Hot New Stuff item", "Homepage") + ""); if (!m_entry.donationLink().isEmpty()) { homepageText += "
" + i18nc("A link to make a donation for a Get Hot New Stuff item (opens a web browser)", "Make a donation") + ""; } if (!m_entry.knowledgebaseLink().isEmpty()) { homepageText += "
" + i18ncp("A link to the knowledgebase (like a forum) (opens a web browser)", "Knowledgebase (no entries)", "Knowledgebase (%1 entries)", m_entry.numberKnowledgebaseEntries()) + ""; } ui->homepageLabel->setText(homepageText); ui->homepageLabel->setToolTip(i18nc("Tooltip for a link in a dialog", "Opens in a browser window")); if (m_entry.rating() > 0) { ui->ratingWidget->setVisible(true); disconnect(ui->ratingWidget, static_cast(&KRatingWidget::ratingChanged), this, &EntryDetails::ratingChanged); // Most of the voting is 20 - 80, so rate 20 as 0 stars and 80 as 5 stars int rating = qMax(0, qMin(10, (m_entry.rating() - 20) / 6)); ui->ratingWidget->setRating(rating); connect(ui->ratingWidget, static_cast(&KRatingWidget::ratingChanged), this, &EntryDetails::ratingChanged); } else { ui->ratingWidget->setVisible(false); } bool hideSmallPreviews = m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall2).isEmpty() && m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall3).isEmpty(); ui->preview1->setVisible(!hideSmallPreviews); ui->preview2->setVisible(!hideSmallPreviews); ui->preview3->setVisible(!hideSmallPreviews); // in static xml we often only get a small preview, use that in details if (m_entry.previewUrl(KNSCore::EntryInternal::PreviewBig1).isEmpty() && !m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1).isEmpty()) { m_entry.setPreviewUrl(m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1), KNSCore::EntryInternal::PreviewBig1); m_entry.setPreviewImage(m_entry.previewImage(KNSCore::EntryInternal::PreviewSmall1), KNSCore::EntryInternal::PreviewBig1); } for (int type = KNSCore::EntryInternal::PreviewSmall1; type <= KNSCore::EntryInternal::PreviewBig3; ++type) { if (m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1).isEmpty()) { ui->previewBig->setVisible(false); } else if (!m_entry.previewUrl((KNSCore::EntryInternal::PreviewType)type).isEmpty()) { qCDebug(KNEWSTUFF) << "type: " << type << m_entry.previewUrl((KNSCore::EntryInternal::PreviewType)type); if (m_entry.previewImage((KNSCore::EntryInternal::PreviewType)type).isNull()) { m_engine->loadPreview(m_entry, (KNSCore::EntryInternal::PreviewType)type); } else { slotEntryPreviewLoaded(m_entry, (KNSCore::EntryInternal::PreviewType)type); } } } updateButtons(); } void EntryDetails::entryStatusChanged(const KNSCore::EntryInternal &entry) { Q_UNUSED(entry); updateButtons(); } void EntryDetails::updateButtons() { if (ui->detailsStack->currentIndex() == 0) { return; } qCDebug(KNEWSTUFF) << "update buttons: " << m_entry.status(); ui->installButton->setVisible(false); ui->uninstallButton->setVisible(false); ui->updateButton->setVisible(false); switch (m_entry.status()) { case Entry::Installed: ui->uninstallButton->setVisible(true); ui->uninstallButton->setEnabled(true); break; case Entry::Updateable: ui->updateButton->setVisible(true); ui->updateButton->setEnabled(true); ui->uninstallButton->setVisible(true); ui->uninstallButton->setEnabled(true); break; case Entry::Invalid: case Entry::Downloadable: ui->installButton->setVisible(true); ui->installButton->setEnabled(true); break; case Entry::Installing: ui->installButton->setVisible(true); ui->installButton->setEnabled(false); break; case Entry::Updating: ui->updateButton->setVisible(true); ui->updateButton->setEnabled(false); ui->uninstallButton->setVisible(true); ui->uninstallButton->setEnabled(false); break; case Entry::Deleted: ui->installButton->setVisible(true); ui->installButton->setEnabled(true); break; } if (ui->installButton->menu()) { QMenu *buttonMenu = ui->installButton->menu(); buttonMenu->clear(); - ui->installButton->setMenu(0); + ui->installButton->setMenu(nullptr); buttonMenu->deleteLater(); } if (ui->installButton->isVisible() && m_entry.downloadLinkCount() > 1) { QMenu *installMenu = new QMenu(ui->installButton); foreach (KNSCore::EntryInternal::DownloadLinkInformation info, m_entry.downloadLinkInformationList()) { QString text = info.name; if (!info.distributionType.trimmed().isEmpty()) { text + " (" + info.distributionType.trimmed() + ')'; } QAction *installAction = installMenu->addAction(QIcon::fromTheme(QStringLiteral("dialog-ok")), text); installAction->setData(info.id); } qCDebug(KNEWSTUFF) << "links: " << m_entry.downloadLinkInformationList().size(); ui->installButton->setMenu(installMenu); } } void EntryDetails::install() { m_engine->install(m_entry); } void EntryDetails::uninstall() { m_engine->uninstall(m_entry); } void EntryDetails::slotEntryPreviewLoaded(const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::PreviewType type) { if (!(entry == m_entry)) { return; } switch (type) { case KNSCore::EntryInternal::PreviewSmall1: ui->preview1->setImage(entry.previewImage(KNSCore::EntryInternal::PreviewSmall1)); break; case KNSCore::EntryInternal::PreviewSmall2: ui->preview2->setImage(entry.previewImage(KNSCore::EntryInternal::PreviewSmall2)); break; case KNSCore::EntryInternal::PreviewSmall3: ui->preview3->setImage(entry.previewImage(KNSCore::EntryInternal::PreviewSmall3)); break; case KNSCore::EntryInternal::PreviewBig1: m_currentPreview = entry.previewImage(KNSCore::EntryInternal::PreviewBig1); ui->previewBig->setImage(m_currentPreview); break; default: break; } } void EntryDetails::preview1Selected() { previewSelected(0); } void EntryDetails::preview2Selected() { previewSelected(1); } void EntryDetails::preview3Selected() { previewSelected(2); } void EntryDetails::previewSelected(int current) { KNSCore::EntryInternal::PreviewType type = static_cast(KNSCore::EntryInternal::PreviewBig1 + current); m_currentPreview = m_entry.previewImage(type); ui->previewBig->setImage(m_currentPreview); } void EntryDetails::ratingChanged(uint rating) { // engine expects values from 0..100 qCDebug(KNEWSTUFF) << "rating: " << rating << " -> " << rating*10; m_engine->vote(m_entry, rating * 10); } void EntryDetails::becomeFan() { m_engine->becomeFan(m_entry); } diff --git a/src/ui/imagepreviewwidget_p.h b/src/ui/imagepreviewwidget_p.h index 0ab70a18..64ab66ec 100644 --- a/src/ui/imagepreviewwidget_p.h +++ b/src/ui/imagepreviewwidget_p.h @@ -1,53 +1,53 @@ /* Copyright (C) 2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_IMAGEPREVIEWWIDGET_P_H #define KNEWSTUFF3_IMAGEPREVIEWWIDGET_P_H #include #include namespace KNS3 { class ImagePreviewWidget : public QWidget { Q_OBJECT public: - explicit ImagePreviewWidget(QWidget *parent = 0); + explicit ImagePreviewWidget(QWidget *parent = nullptr); void setImage(const QImage &preview); QSize sizeHint() const Q_DECL_OVERRIDE; Q_SIGNALS: void clicked(); protected: void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; private: QImage m_image; QImage m_scaledImage; QPixmap m_frameImage; }; } #endif // IMAGEPREVIEWWIDGET_H diff --git a/src/ui/itemsgridviewdelegate.cpp b/src/ui/itemsgridviewdelegate.cpp index 883b17ed..1a5a3ff0 100644 --- a/src/ui/itemsgridviewdelegate.cpp +++ b/src/ui/itemsgridviewdelegate.cpp @@ -1,378 +1,378 @@ /* Copyright (C) 2008 Jeremy Whiting Copyright (C) 2010 Reza Fatahilah Shah Copyright (C) 2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "itemsgridviewdelegate_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "core/itemsmodel.h" namespace KNS3 { enum { DelegateTitleLabel, DelegateAuthorLabel, DelegateDownloadCounterLabel, DelegateGridRatingWidget }; ItemsGridViewDelegate::ItemsGridViewDelegate(QAbstractItemView *itemView, KNSCore::Engine *engine, QObject *parent) : ItemsViewBaseDelegate(itemView, engine, parent) , m_elementYPos(0) { createOperationBar(); } ItemsGridViewDelegate::~ItemsGridViewDelegate() { } QList ItemsGridViewDelegate::createItemWidgets(const QModelIndex &index) const { Q_UNUSED(index) QList m_widgetList; KSqueezedTextLabel *titleLabel = new KSqueezedTextLabel(); titleLabel->setOpenExternalLinks(true); titleLabel->setTextElideMode(Qt::ElideRight); // not so nice - work around constness to install the event filter ItemsGridViewDelegate *delegate = const_cast(this); titleLabel->installEventFilter(delegate); m_widgetList << titleLabel; KSqueezedTextLabel *authorLabel = new KSqueezedTextLabel(); authorLabel->setTextElideMode(Qt::ElideRight); m_widgetList << authorLabel; KSqueezedTextLabel *downloadCounterLabel = new KSqueezedTextLabel(); downloadCounterLabel->setTextElideMode(Qt::ElideRight); m_widgetList << downloadCounterLabel; KRatingWidget *rating = new KRatingWidget(); rating->setMaxRating(10); rating->setHalfStepsEnabled(true); m_widgetList << rating; return m_widgetList; } void ItemsGridViewDelegate::updateItemWidgets(const QList widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const { const KNSCore::ItemsModel *model = qobject_cast(index.model()); if (!model) { qCDebug(KNEWSTUFF) << "WARNING - INVALID MODEL!"; return; } KNSCore::EntryInternal entry = index.data(Qt::UserRole).value(); int elementYPos = KNSCore::PreviewHeight + ItemMargin + FrameThickness * 2; //setup rating widget KRatingWidget *rating = qobject_cast(widgets.at(DelegateGridRatingWidget)); if (rating) { if (entry.rating() > 0) { rating->setToolTip(i18n("Rating: %1%", entry.rating())); // assume all entries come with rating 0..100 but most are in the range 20 - 80, so 20 is 0 stars, 80 is 5 stars rating->setRating((entry.rating() - 20) * 10 / 60); //make the rating widget smaller than the one at list view int newWidth = 68; QSize size(newWidth, 15); rating->resize(size); //put rating widget under image rectangle rating->move((ItemGridWidth - newWidth) / 2, elementYPos); elementYPos += rating->height(); } else { //is it better to stay visible? rating->setVisible(false); } } elementYPos += ItemMargin; //setup title label QLabel *titleLabel = qobject_cast(widgets.at(DelegateTitleLabel)); - if (titleLabel != NULL) { + if (titleLabel != nullptr) { titleLabel->setWordWrap(true); titleLabel->setAlignment(Qt::AlignHCenter); //titleLabel->setFrameStyle(QFrame::Panel); titleLabel->resize(QSize(option.rect.width() - (ItemMargin * 2), option.fontMetrics.height() * 2)); titleLabel->move((ItemGridWidth - titleLabel->width()) / 2, elementYPos); QString title; QUrl link = qvariant_cast(entry.homepage()); if (!link.isEmpty()) { title += "" + entry.name() + "\n"; } else { title += "" + entry.name() + ""; } titleLabel->setText(title); elementYPos += titleLabel->height(); } //setup author label QLabel *authorLabel = qobject_cast(widgets.at(DelegateAuthorLabel)); - if (authorLabel != NULL) { + if (authorLabel != nullptr) { authorLabel->setWordWrap(true); authorLabel->setAlignment(Qt::AlignHCenter); authorLabel->resize(QSize(option.rect.width() - (ItemMargin * 2), option.fontMetrics.height())); authorLabel->move((ItemGridWidth - authorLabel->width()) / 2, elementYPos); QString text; QString authorName = entry.author().name(); QString email = entry.author().email(); QString authorPage = entry.author().homepage(); if (!authorName.isEmpty()) { if (!authorPage.isEmpty()) { text += "

" + i18nc("Show the author of this item in a list", "By %1", " " + authorName + "") + "

\n"; } else if (!email.isEmpty()) { text += "

" + i18nc("Show the author of this item in a list", "By %1", authorName) + " " + email + "

\n"; } else { text += "

" + i18nc("Show the author of this item in a list", "By %1", authorName) + "

\n"; } } authorLabel->setText(text); elementYPos += authorLabel->height(); } elementYPos += ItemMargin; //setup download label QLabel *downloadLabel = qobject_cast(widgets.at(DelegateDownloadCounterLabel)); - if (downloadLabel != NULL) { + if (downloadLabel != nullptr) { downloadLabel->setWordWrap(true); downloadLabel->setAlignment(Qt::AlignHCenter); downloadLabel->resize(QSize(option.rect.width() - (ItemMargin * 2), option.fontMetrics.height())); downloadLabel->move((ItemGridWidth - downloadLabel->width()) / 2, elementYPos); unsigned int fans = entry.numberFans(); unsigned int downloads = entry.downloadCount(); QString text; QString fanString; QString downloadString; if (fans > 0) { fanString = i18ncp("fan as in supporter", "1 fan", "%1 fans", fans); } if (downloads > 0) { downloadString = i18np("1 download", "%1 downloads", downloads); } if (downloads > 0 || fans > 0) { text += "

" + downloadString; if (downloads > 0 && fans > 0) { text += QLatin1String(", "); } text += fanString + QLatin1String("

"); } downloadLabel->setText(text); elementYPos += downloadLabel->height(); } elementYPos += ItemMargin; m_elementYPos = elementYPos; } void ItemsGridViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (option.state & QStyle::State_MouseOver) { QModelIndex focIndex = focusedIndex(); if (m_oldIndex != focIndex || !m_operationBar->isVisible()) { ItemsGridViewDelegate *delegate = const_cast(this); delegate->displayOperationBar(option.rect, index); delegate->m_oldIndex = focIndex; } } else { QModelIndex focindex = focusedIndex(); if (!focindex.isValid()) { qCDebug(KNEWSTUFF) << "INVALID hide selection"; m_operationBar->hide(); } } QStyle *style = QApplication::style(); - style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, 0); + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, nullptr); painter->save(); if (option.state & QStyle::State_Selected) { painter->setPen(QPen(option.palette.highlightedText().color())); } else { painter->setPen(QPen(option.palette.text().color())); } const KNSCore::ItemsModel *realmodel = qobject_cast(index.model()); if (realmodel->hasPreviewImages()) { int width = option.rect.width(); KNSCore::EntryInternal entry = index.data(Qt::UserRole).value(); if (entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1).isEmpty()) { ; } else { QPoint centralPoint(option.rect.left() + width / 2, option.rect.top() + ItemMargin + FrameThickness + KNSCore::PreviewHeight / 2); QImage image = entry.previewImage(KNSCore::EntryInternal::PreviewSmall1); if (!image.isNull()) { QPoint previewPoint(centralPoint.x() - image.width() / 2, centralPoint.y() - image.height() / 2); painter->drawImage(previewPoint, image); QPixmap frameImageScaled = m_frameImage.scaled(image.width() + FrameThickness * 2, image.height() + FrameThickness * 2); QPoint framePoint(centralPoint.x() - frameImageScaled.width() / 2, centralPoint.y() - frameImageScaled.height() / 2); painter->drawPixmap(framePoint, frameImageScaled); } else { QPoint thumbnailPoint(option.rect.left() + ((width - KNSCore::PreviewWidth - FrameThickness * 2) / 2), option.rect.top() + ItemMargin); QRect rect(thumbnailPoint, QSize(KNSCore::PreviewWidth + FrameThickness * 2, KNSCore::PreviewHeight + FrameThickness * 2)); painter->drawText(rect, Qt::AlignCenter | Qt::TextWordWrap, i18n("Loading Preview")); } } } painter->restore(); } QSize ItemsGridViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); Q_UNUSED(index); QSize size; size.setWidth(ItemGridWidth); size.setHeight(qMax(option.fontMetrics.height() * 13, ItemGridHeight)); // up to 6 lines of text, and two margins return size; } void ItemsGridViewDelegate::createOperationBar() { m_operationBar = new QWidget(this->itemView()->viewport()); m_detailsButton = new QToolButton(); m_detailsButton->setToolButtonStyle(Qt::ToolButtonFollowStyle); m_detailsButton->setPopupMode(QToolButton::InstantPopup); m_detailsButton->setToolTip(i18n("Details")); m_detailsButton->setIcon(QIcon::fromTheme(QStringLiteral("documentinfo"))); setBlockedEventTypes(m_detailsButton, QList() << QEvent::MouseButtonPress << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick); connect(m_detailsButton, &QToolButton::clicked, this, static_cast(&ItemsGridViewDelegate::slotDetailsClicked)); m_installButton = new QToolButton(); m_installButton->setToolButtonStyle(Qt::ToolButtonFollowStyle); m_installButton->setPopupMode(QToolButton::InstantPopup); setBlockedEventTypes(m_installButton, QList() << QEvent::MouseButtonPress << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick); connect(m_installButton, &QAbstractButton::clicked, this, &ItemsGridViewDelegate::slotInstallClicked); connect(m_installButton, &QToolButton::triggered, this, &ItemsGridViewDelegate::slotInstallActionTriggered); if (m_installButton->menu()) { QMenu *buttonMenu = m_installButton->menu(); buttonMenu->clear(); - m_installButton->setMenu(0); + m_installButton->setMenu(nullptr); buttonMenu->deleteLater(); } QHBoxLayout *layout = new QHBoxLayout(m_operationBar); layout->setSpacing(1); layout->addWidget(m_installButton); layout->addWidget(m_detailsButton); m_operationBar->adjustSize(); m_operationBar->hide(); } void ItemsGridViewDelegate::displayOperationBar(const QRect &rect, const QModelIndex &index) { KNSCore::EntryInternal entry = index.data(Qt::UserRole).value(); - if (m_installButton != 0) { - if (m_installButton->menu() != 0) { + if (m_installButton != nullptr) { + if (m_installButton->menu() != nullptr) { QMenu *buttonMenu = m_installButton->menu(); buttonMenu->clear(); - m_installButton->setMenu(0); + m_installButton->setMenu(nullptr); buttonMenu->deleteLater(); } bool installable = false; bool enabled = true; QString text; QIcon icon; switch (entry.status()) { case Entry::Installed: text = i18n("Uninstall"); icon = m_iconDelete; break; case Entry::Updateable: text = i18n("Update"); icon = m_iconUpdate; installable = true; break; case Entry::Installing: text = i18n("Installing"); enabled = false; icon = m_iconUpdate; break; case Entry::Updating: text = i18n("Updating"); enabled = false; icon = m_iconUpdate; break; case Entry::Downloadable: text = i18n("Install"); icon = m_iconInstall; installable = true; break; case Entry::Deleted: text = i18n("Install Again"); icon = m_iconInstall; installable = true; break; default: text = i18n("Install"); } m_installButton->setToolTip(text); m_installButton->setIcon(icon); m_installButton->setEnabled(enabled); if (installable && entry.downloadLinkCount() > 1) { QMenu *installMenu = new QMenu(m_installButton); foreach (const KNSCore::EntryInternal::DownloadLinkInformation &info, entry.downloadLinkInformationList()) { QString text = info.name; if (!info.distributionType.trimmed().isEmpty()) { text + " (" + info.distributionType.trimmed() + ')'; } QAction *installAction = installMenu->addAction(m_iconInstall, text); installAction->setData(QPoint(index.row(), info.id)); } m_installButton->setMenu(installMenu); } m_operationBar->move(rect.left() + (ItemGridWidth - m_operationBar->width()) / 2, rect.top() + m_elementYPos); m_operationBar->show(); } } } diff --git a/src/ui/itemsgridviewdelegate_p.h b/src/ui/itemsgridviewdelegate_p.h index bb92b6d4..21671a85 100644 --- a/src/ui/itemsgridviewdelegate_p.h +++ b/src/ui/itemsgridviewdelegate_p.h @@ -1,66 +1,66 @@ /* Copyright (C) 2008 Jeremy Whiting Copyright (C) 2010 Reza Fatahilah Shah Copyright (C) 2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_ITEMSGRIDVIEWDELEGATE_P_H #define KNEWSTUFF3_ITEMSGRIDVIEWDELEGATE_P_H #include "itemsviewbasedelegate_p.h" class QToolButton; namespace KNS3 { static const int ItemGridHeight = 202; static const int ItemGridWidth = 158; static const int FrameThickness = 5; static const int ItemMargin = 2; class ItemsGridViewDelegate: public ItemsViewBaseDelegate { Q_OBJECT public: - explicit ItemsGridViewDelegate(QAbstractItemView *itemView, KNSCore::Engine *engine, QObject *parent = 0); + explicit ItemsGridViewDelegate(QAbstractItemView *itemView, KNSCore::Engine *engine, QObject *parent = nullptr); ~ItemsGridViewDelegate(); // paint the item at index with all its attributes shown void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; // get the list of widgets QList createItemWidgets(const QModelIndex &index) const Q_DECL_OVERRIDE; // update the widgets virtual void updateItemWidgets(const QList widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const Q_DECL_OVERRIDE; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; private: void createOperationBar(); void displayOperationBar(const QRect &rect, const QModelIndex &index); private: QWidget *m_operationBar; QToolButton *m_detailsButton; QToolButton *m_installButton; QModelIndex m_oldIndex; mutable int m_elementYPos; }; } #endif diff --git a/src/ui/itemsview_p.h b/src/ui/itemsview_p.h index 6bf9ba54..8fa906d9 100644 --- a/src/ui/itemsview_p.h +++ b/src/ui/itemsview_p.h @@ -1,36 +1,36 @@ /* Copyright (C) 2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_ITEMSVIEW_P_H #define KNEWSTUFF3_ITEMSVIEW_P_H #include namespace KNS3 { class ItemsView: public QListView { public: - explicit ItemsView(QWidget *parent = 0); + explicit ItemsView(QWidget *parent = nullptr); protected: void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; }; } // end KNS namespace #endif diff --git a/src/ui/itemsviewbasedelegate_p.h b/src/ui/itemsviewbasedelegate_p.h index 16a81967..1f6994b6 100644 --- a/src/ui/itemsviewbasedelegate_p.h +++ b/src/ui/itemsviewbasedelegate_p.h @@ -1,79 +1,79 @@ /* Copyright (C) 2008 Jeremy Whiting Copyright (C) 2010 Reza Fatahilah Shah Copyright (C) 2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_ITEMSVIEWBASEDELEGATE_P_H #define KNEWSTUFF3_ITEMSVIEWBASEDELEGATE_P_H #include #include #include #include #include #include "core/engine.h" #include "core/entryinternal.h" #include namespace KNS3 { class ItemsViewBaseDelegate: public KWidgetItemDelegate { Q_OBJECT public: - explicit ItemsViewBaseDelegate(QAbstractItemView *itemView, KNSCore::Engine *engine, QObject *parent = 0); + explicit ItemsViewBaseDelegate(QAbstractItemView *itemView, KNSCore::Engine *engine, QObject *parent = nullptr); virtual ~ItemsViewBaseDelegate(); // paint the item at index with all its attributes shown void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE = 0; // get the list of widgets QList createItemWidgets(const QModelIndex &index) const Q_DECL_OVERRIDE = 0; // update the widgets void updateItemWidgets(const QList widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const Q_DECL_OVERRIDE = 0; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE = 0; Q_SIGNALS: void signalShowDetails(const KNSCore::EntryInternal &entry); protected Q_SLOTS: bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE; void slotInstallClicked(); void slotInstallActionTriggered(QAction *action); void slotLinkClicked(const QString &url); void slotDetailsClicked(const QModelIndex &index); void slotDetailsClicked(); protected: KNSCore::Engine *m_engine; QAbstractItemView *m_itemView; QIcon m_iconInvalid; QIcon m_iconDownloadable; QIcon m_iconInstall; QIcon m_iconUpdate; QIcon m_iconDelete; QPixmap m_frameImage; QPixmap m_noImage; QSize m_buttonSize; }; } #endif diff --git a/src/ui/itemsviewdelegate.cpp b/src/ui/itemsviewdelegate.cpp index 75ad50b4..933d9136 100644 --- a/src/ui/itemsviewdelegate.cpp +++ b/src/ui/itemsviewdelegate.cpp @@ -1,332 +1,332 @@ /* This file is part of KNewStuff2. Copyright (C) 2008 Jeremy Whiting Copyright (C) 2010 Reza Fatahilah Shah Copyright (C) 2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "itemsviewdelegate_p.h" #include #include #include #include #include #include #include #include #include "core/itemsmodel.h" #include "entrydetailsdialog_p.h" namespace KNS3 { enum { DelegateLabel, DelegateInstallButton, DelegateDetailsButton, DelegateRatingWidget }; ItemsViewDelegate::ItemsViewDelegate(QAbstractItemView *itemView, KNSCore::Engine *engine, QObject *parent) : ItemsViewBaseDelegate(itemView, engine, parent) { } ItemsViewDelegate::~ItemsViewDelegate() { } QList ItemsViewDelegate::createItemWidgets(const QModelIndex &index) const { Q_UNUSED(index); QList list; QLabel *infoLabel = new QLabel(); infoLabel->setOpenExternalLinks(true); // not so nice - work around constness to install the event filter ItemsViewDelegate *delegate = const_cast(this); infoLabel->installEventFilter(delegate); list << infoLabel; QToolButton *installButton = new QToolButton(); installButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); installButton->setPopupMode(QToolButton::InstantPopup); list << installButton; setBlockedEventTypes(installButton, QList() << QEvent::MouseButtonPress << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick); connect(installButton, &QAbstractButton::clicked, this, &ItemsViewDelegate::slotInstallClicked); connect(installButton, &QToolButton::triggered, this, &ItemsViewDelegate::slotInstallActionTriggered); QToolButton *detailsButton = new QToolButton(); detailsButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); list << detailsButton; setBlockedEventTypes(detailsButton, QList() << QEvent::MouseButtonPress << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick); connect(detailsButton, &QToolButton::clicked, this, static_cast(&ItemsViewDelegate::slotDetailsClicked)); KRatingWidget *rating = new KRatingWidget(); rating->setMaxRating(10); rating->setHalfStepsEnabled(true); list << rating; //connect(rating, SIGNAL(ratingChanged(uint)), this, SLOT()); return list; } void ItemsViewDelegate::updateItemWidgets(const QList widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const { const KNSCore::ItemsModel *model = qobject_cast(index.model()); if (!model) { qCDebug(KNEWSTUFF) << "WARNING - INVALID MODEL!"; return; } KNSCore::EntryInternal entry = index.data(Qt::UserRole).value(); // setup the install button int margin = option.fontMetrics.height() / 2; int right = option.rect.width(); QToolButton *installButton = qobject_cast(widgets.at(DelegateInstallButton)); - if (installButton != 0) { + if (installButton != nullptr) { if (installButton->menu()) { QMenu *buttonMenu = installButton->menu(); buttonMenu->clear(); - installButton->setMenu(0); + installButton->setMenu(nullptr); buttonMenu->deleteLater(); } bool installable = false; bool enabled = true; QString text; QIcon icon; switch (entry.status()) { case Entry::Installed: text = i18n("Uninstall"); icon = m_iconDelete; break; case Entry::Updateable: text = i18n("Update"); icon = m_iconUpdate; installable = true; break; case Entry::Installing: text = i18n("Installing"); enabled = false; icon = m_iconUpdate; break; case Entry::Updating: text = i18n("Updating"); enabled = false; icon = m_iconUpdate; break; case Entry::Downloadable: text = i18n("Install"); icon = m_iconInstall; installable = true; break; case Entry::Deleted: text = i18n("Install Again"); icon = m_iconInstall; installable = true; break; default: text = i18n("Install"); } installButton->setText(text); installButton->setEnabled(enabled); installButton->setIcon(icon); if (installable && entry.downloadLinkCount() > 1) { QMenu *installMenu = new QMenu(installButton); foreach (const KNSCore::EntryInternal::DownloadLinkInformation &info, entry.downloadLinkInformationList()) { QString text = info.name; if (!info.distributionType.trimmed().isEmpty()) { text + " (" + info.distributionType.trimmed() + ')'; } QAction *installAction = installMenu->addAction(m_iconInstall, text); installAction->setData(QPoint(index.row(), info.id)); } installButton->setMenu(installMenu); } } QToolButton *detailsButton = qobject_cast(widgets.at(DelegateDetailsButton)); if (detailsButton) { detailsButton->setText(i18n("Details")); detailsButton->setIcon(QIcon::fromTheme(QStringLiteral("documentinfo"))); } if (installButton && detailsButton) { if (m_buttonSize.width() < installButton->sizeHint().width()) { const_cast(m_buttonSize) = QSize( qMax(option.fontMetrics.height() * 7, qMax(installButton->sizeHint().width(), detailsButton->sizeHint().width())), installButton->sizeHint().height()); } installButton->resize(m_buttonSize); installButton->move(right - installButton->width() - margin, option.rect.height() / 2 - installButton->height() * 1.5); detailsButton->resize(m_buttonSize); detailsButton->move(right - installButton->width() - margin, option.rect.height() / 2 - installButton->height() / 2); } QLabel *infoLabel = qobject_cast(widgets.at(DelegateLabel)); infoLabel->setWordWrap(true); - if (infoLabel != NULL) { + if (infoLabel != nullptr) { if (model->hasPreviewImages()) { // move the text right by kPreviewWidth + margin pixels to fit the preview infoLabel->move(KNSCore::PreviewWidth + margin * 2, 0); infoLabel->resize(QSize(option.rect.width() - KNSCore::PreviewWidth - (margin * 6) - m_buttonSize.width(), option.fontMetrics.height() * 7)); } else { infoLabel->move(margin, 0); infoLabel->resize(QSize(option.rect.width() - (margin * 4) - m_buttonSize.width(), option.fontMetrics.height() * 7)); } QString text = QLatin1String("\n" "

"); QUrl link = qvariant_cast(entry.homepage()); if (!link.isEmpty()) { text += "

" + entry.name() + "

\n"; } else { text += entry.name(); } text += QLatin1String("

\n"); QString authorName = entry.author().name(); QString email = entry.author().email(); QString authorPage = entry.author().homepage(); if (!authorName.isEmpty()) { if (!authorPage.isEmpty()) { text += "

" + i18nc("Show the author of this item in a list", "By %1", " " + authorName + "") + "

\n"; } else if (!email.isEmpty()) { text += "

" + i18nc("Show the author of this item in a list", "By %1", authorName) + " " + email + "

\n"; } else { text += "

" + i18nc("Show the author of this item in a list", "By %1", authorName) + "

\n"; } } QString summary = "

" + option.fontMetrics.elidedText(entry.summary(), Qt::ElideRight, infoLabel->width() * 3) + "

\n"; text += summary; unsigned int fans = entry.numberFans(); unsigned int downloads = entry.downloadCount(); QString fanString; QString downloadString; if (fans > 0) { fanString = i18ncp("fan as in supporter", "1 fan", "%1 fans", fans); } if (downloads > 0) { downloadString = i18np("1 download", "%1 downloads", downloads); } if (downloads > 0 || fans > 0) { text += "

" + downloadString; if (downloads > 0 && fans > 0) { text += QLatin1String(", "); } text += fanString + QLatin1String("

\n"); } text += QLatin1String(""); // use simplified to get rid of newlines etc text = KNSCore::replaceBBCode(text).simplified(); infoLabel->setText(text); } KRatingWidget *rating = qobject_cast(widgets.at(DelegateRatingWidget)); if (rating) { if (entry.rating() > 0) { rating->setToolTip(i18n("Rating: %1%", entry.rating())); // assume all entries come with rating 0..100 but most are in the range 20 - 80, so 20 is 0 stars, 80 is 5 stars rating->setRating((entry.rating() - 20) * 10 / 60); // put the rating label below the install button rating->move(right - installButton->width() - margin, option.rect.height() / 2 + installButton->height() / 2); rating->resize(m_buttonSize); } else { rating->setVisible(false); } } } // draws the preview void ItemsViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { int margin = option.fontMetrics.height() / 2; QStyle *style = QApplication::style(); - style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, 0); + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, nullptr); painter->save(); if (option.state & QStyle::State_Selected) { painter->setPen(QPen(option.palette.highlightedText().color())); } else { painter->setPen(QPen(option.palette.text().color())); } const KNSCore::ItemsModel *realmodel = qobject_cast(index.model()); if (realmodel->hasPreviewImages()) { int height = option.rect.height(); QPoint point(option.rect.left() + margin, option.rect.top() + ((height - KNSCore::PreviewHeight) / 2)); KNSCore::EntryInternal entry = index.data(Qt::UserRole).value(); if (entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1).isEmpty()) { // paint the no preview icon //point.setX((PreviewWidth - m_noImage.width())/2 + 5); //point.setY(option.rect.top() + ((height - m_noImage.height()) / 2)); //painter->drawPixmap(point, m_noImage); } else { QImage image = entry.previewImage(KNSCore::EntryInternal::PreviewSmall1); if (!image.isNull()) { point.setX((KNSCore::PreviewWidth - image.width()) / 2 + 5); point.setY(option.rect.top() + ((height - image.height()) / 2)); painter->drawImage(point, image); QPoint framePoint(point.x() - 5, point.y() - 5); painter->drawPixmap(framePoint, m_frameImage.scaled(image.width() + 10, image.height() + 10)); } else { QRect rect(point, QSize(KNSCore::PreviewWidth, KNSCore::PreviewHeight)); painter->drawText(rect, Qt::AlignCenter | Qt::TextWordWrap, i18n("Loading Preview")); } } } painter->restore(); } QSize ItemsViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); Q_UNUSED(index); QSize size; size.setWidth(option.fontMetrics.height() * 4); size.setHeight(qMax(option.fontMetrics.height() * 7, KNSCore::PreviewHeight)); // up to 6 lines of text, and two margins return size; } } // namespace diff --git a/src/ui/itemsviewdelegate_p.h b/src/ui/itemsviewdelegate_p.h index 16239596..e5924d05 100644 --- a/src/ui/itemsviewdelegate_p.h +++ b/src/ui/itemsviewdelegate_p.h @@ -1,51 +1,51 @@ /* This file is part of KNewStuff2. Copyright (C) 2008 Jeremy Whiting Copyright (C) 2010 Reza Fatahilah Shah Copyright (C) 2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_ITEMSVIEWDELEGATE_P_H #define KNEWSTUFF3_ITEMSVIEWDELEGATE_P_H #include "itemsviewbasedelegate_p.h" namespace KNS3 { class ItemsViewDelegate: public ItemsViewBaseDelegate { Q_OBJECT public: - explicit ItemsViewDelegate(QAbstractItemView *itemView, KNSCore::Engine *engine, QObject *parent = 0); + explicit ItemsViewDelegate(QAbstractItemView *itemView, KNSCore::Engine *engine, QObject *parent = nullptr); ~ItemsViewDelegate(); // paint the item at index with all its attributes shown void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; // get the list of widgets QList createItemWidgets(const QModelIndex &index) const Q_DECL_OVERRIDE; // update the widgets virtual void updateItemWidgets(const QList widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const Q_DECL_OVERRIDE; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; }; } #endif diff --git a/src/ui/widgetquestionlistener.cpp b/src/ui/widgetquestionlistener.cpp index 908843d5..b90a6b82 100644 --- a/src/ui/widgetquestionlistener.cpp +++ b/src/ui/widgetquestionlistener.cpp @@ -1,109 +1,109 @@ /* This file is part of KNewStuffCore. Copyright (c) 2016 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "widgetquestionlistener.h" #include "core/question.h" #include #include #include using namespace KNS3; class WidgetQuestionListenerHelper { public: - WidgetQuestionListenerHelper() : q(0) {} + WidgetQuestionListenerHelper() : q(nullptr) {} ~WidgetQuestionListenerHelper() { delete q; } WidgetQuestionListener *q; }; Q_GLOBAL_STATIC(WidgetQuestionListenerHelper, s_kns3_widgetQuestionListener) WidgetQuestionListener* WidgetQuestionListener::instance() { if(!s_kns3_widgetQuestionListener()->q) { new WidgetQuestionListener; } return s_kns3_widgetQuestionListener()->q; } WidgetQuestionListener::WidgetQuestionListener() - : KNSCore::QuestionListener(0) + : KNSCore::QuestionListener(nullptr) { s_kns3_widgetQuestionListener()->q = this; } WidgetQuestionListener::~WidgetQuestionListener() { } void KNS3::WidgetQuestionListener::askQuestion(KNSCore::Question* question) { switch(question->questionType()) { case KNSCore::Question::SelectFromListQuestion: { bool ok = false; - question->setResponse(QInputDialog::getItem(0, question->title(), question->question(), question->list(), 0, false, &ok)); + question->setResponse(QInputDialog::getItem(nullptr, question->title(), question->question(), question->list(), 0, false, &ok)); if(ok) { question->setResponse(KNSCore::Question::OKResponse); } else { question->setResponse(KNSCore::Question::CancelResponse); } } break; case KNSCore::Question::ContinueCancelQuestion: { - KMessageBox::ButtonCode response = KMessageBox::warningContinueCancel(0, question->question(), question->title()); + KMessageBox::ButtonCode response = KMessageBox::warningContinueCancel(nullptr, question->question(), question->title()); if(response == KMessageBox::Continue) { question->setResponse(KNSCore::Question::ContinueResponse); } else { question->setResponse(KNSCore::Question::CancelResponse); } } break; case KNSCore::Question::PasswordQuestion: { KPasswordDialog dlg; dlg.setPrompt(question->question()); if(dlg.exec()) { question->setResponse(dlg.password()); question->setResponse(KNSCore::Question::ContinueResponse); } else { question->setResponse(KNSCore::Question::CancelResponse); } } break; case KNSCore::Question::YesNoQuestion: default: { - KMessageBox::ButtonCode response = KMessageBox::questionYesNo(0, question->question(), question->title()); + KMessageBox::ButtonCode response = KMessageBox::questionYesNo(nullptr, question->question(), question->title()); if(response == KMessageBox::Yes) { question->setResponse(KNSCore::Question::YesResponse); } else { question->setResponse(KNSCore::Question::NoResponse); } } break; } } diff --git a/src/upload/atticahelper_p.h b/src/upload/atticahelper_p.h index 26e24e68..6cf03eea 100644 --- a/src/upload/atticahelper_p.h +++ b/src/upload/atticahelper_p.h @@ -1,100 +1,100 @@ /* Copyright (C) 2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_ATTICAHELPER_P_H #define KNEWSTUFF3_ATTICAHELPER_P_H #include #include #include #include #include #include #include "knewstuffcore_export.h" class KJob; namespace KNSCore { class HTTPJob; class KNEWSTUFFCORE_EXPORT AtticaHelper : public QObject { Q_OBJECT public: - explicit AtticaHelper(QObject *parent = 0); + explicit AtticaHelper(QObject *parent = nullptr); void init(); void setCurrentProvider(const QString &provider); void addProviderFile(const QUrl &file); Attica::Provider provider(); void checkLogin(const QString &name, const QString &password); bool loadCredentials(QString &name, QString &password); bool saveCredentials(const QString &name, const QString &password); void loadCategories(const QStringList &configuredCategories); void loadContentByCurrentUser(); void loadLicenses(); void loadDetailsLink(const QString &contentId); void loadContent(const QString &contentId); void loadCurrency(); void loadPreviews(const QString &contentId); Q_SIGNALS: void loginChecked(bool); void providersLoaded(const QStringList &); void categoriesLoaded(Attica::Category::List); void contentByCurrentUserLoaded(const Attica::Content::List &); void licensesLoaded(const Attica::License::List &); void detailsLinkLoaded(const QUrl &); void contentLoaded(const Attica::Content &); void currencyLoaded(const QString &); void previewLoaded(int index, const QImage &image); private Q_SLOTS: void checkLoginFinished(Attica::BaseJob *baseJob); void defaultProvidersLoaded(); void categoriesLoaded(Attica::BaseJob *baseJob); void contentByCurrentUserLoaded(Attica::BaseJob *baseJob); void licensesLoaded(Attica::BaseJob *baseJob); void detailsLinkLoaded(Attica::BaseJob *baseJob); void contentLoaded(Attica::BaseJob *baseJob); void currencyLoaded(Attica::BaseJob *baseJob); void slotPreviewData(KJob *job, const QByteArray &buf); void slotPreviewDownload(KJob *job); private: Attica::ProviderManager providerManager; Attica::Provider currentProvider; Attica::Category::List m_validCategories; QString m_username; QStringList m_configuredCategories; Attica::Content::List m_userCreatedContent; QByteArray m_previewBuffer[3]; HTTPJob *m_previewJob[3]; Q_DISABLE_COPY(AtticaHelper) }; } #endif diff --git a/src/uploaddialog.cpp b/src/uploaddialog.cpp index ffac2664..7b847e7e 100644 --- a/src/uploaddialog.cpp +++ b/src/uploaddialog.cpp @@ -1,854 +1,854 @@ /* knewstuff3/ui/uploaddialog.cpp. Copyright (c) 2002 Cornelius Schumacher Copyright (c) 2009 Jeremy Whiting Copyright (C) 2009-2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "uploaddialog.h" #include "uploaddialog_p.h" #include "ui/widgetquestionlistener.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KNS3; bool UploadDialogPrivate::init(const QString &configfile) { QVBoxLayout *layout = new QVBoxLayout; q->setLayout(layout); QWidget *_mainWidget = new QWidget(q); ui.setupUi(_mainWidget); layout->addWidget(_mainWidget); backButton = new QPushButton; KGuiItem::assign(backButton, KStandardGuiItem::back(KStandardGuiItem::UseRTL)); nextButton = new QPushButton; nextButton->setText(i18nc("Opposite to Back", "Next")); nextButton->setIcon(KStandardGuiItem::forward(KStandardGuiItem::UseRTL).icon()); nextButton->setDefault(true); finishButton = new QPushButton; finishButton->setText(i18n("Finish")); finishButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply"))); buttonBox = new QDialogButtonBox(q); buttonBox->addButton(backButton, QDialogButtonBox::ActionRole); buttonBox->addButton(nextButton, QDialogButtonBox::ActionRole); buttonBox->addButton(finishButton, QDialogButtonBox::AcceptRole); buttonBox->setStandardButtons(QDialogButtonBox::Cancel); layout->addWidget(buttonBox); atticaHelper = new KNSCore::AtticaHelper(q); bool success = true; KConfig conf(configfile); if (conf.accessMode() == KConfig::NoAccess) { qCritical() << "No knsrc file named '" << configfile << "' was found." << endl; success = false; } // KConfig does not actually tell us whether the config file exists, so // we check ourselves for better error messages. if (QStandardPaths::locate(QStandardPaths::GenericConfigLocation, configfile).isEmpty()) { qCritical() << "No knsrc file named '" << configfile << "' was found." << endl; success = false; } KConfigGroup group; if (conf.hasGroup("KNewStuff3")) { qCDebug(KNEWSTUFF) << "Loading KNewStuff3 config: " << configfile; group = conf.group("KNewStuff3"); } else { qCritical() << "A knsrc file was found but it doesn't contain a KNewStuff3 section." << endl; success = false; } if (success) { const QString providersFileUrl = group.readEntry("ProvidersUrl", QString()); categoryNames = group.readEntry("UploadCategories", QStringList()); // fall back to download categories if (categoryNames.isEmpty()) { categoryNames = group.readEntry("Categories", QStringList()); } atticaHelper->addProviderFile(QUrl(providersFileUrl)); } ui.mCategoryCombo->addItems(categoryNames); if (categoryNames.size() == 1) { ui.mCategoryLabel->setVisible(false); ui.mCategoryCombo->setVisible(false); } qCDebug(KNEWSTUFF) << "Categories: " << categoryNames; q->connect(atticaHelper, SIGNAL(providersLoaded(QStringList)), q, SLOT(_k_providersLoaded(QStringList))); q->connect(atticaHelper, SIGNAL(loginChecked(bool)), q, SLOT(_k_checkCredentialsFinished(bool))); q->connect(atticaHelper, SIGNAL(licensesLoaded(Attica::License::List)), q, SLOT(_k_licensesLoaded(Attica::License::List))); q->connect(atticaHelper, SIGNAL(categoriesLoaded(Attica::Category::List)), q, SLOT(_k_categoriesLoaded(Attica::Category::List))); q->connect(atticaHelper, SIGNAL(contentByCurrentUserLoaded(Attica::Content::List)), q, SLOT(_k_contentByCurrentUserLoaded(Attica::Content::List))); q->connect(atticaHelper, SIGNAL(contentLoaded(Attica::Content)), q, SLOT(_k_updatedContentFetched(Attica::Content))); q->connect(atticaHelper, SIGNAL(detailsLinkLoaded(QUrl)), q, SLOT(_k_detailsLinkLoaded(QUrl))); q->connect(atticaHelper, SIGNAL(currencyLoaded(QString)), q, SLOT(_k_currencyLoaded(QString))); q->connect(atticaHelper, SIGNAL(previewLoaded(int,QImage)), q, SLOT(_k_previewLoaded(int,QImage))); atticaHelper->init(); q->connect(ui.changePreview1Button, SIGNAL(clicked()), q, SLOT(_k_changePreview1())); q->connect(ui.changePreview2Button, SIGNAL(clicked()), q, SLOT(_k_changePreview2())); q->connect(ui.changePreview3Button, SIGNAL(clicked()), q, SLOT(_k_changePreview3())); q->connect(ui.providerComboBox, SIGNAL(currentIndexChanged(QString)), q, SLOT(_k_providerChanged(QString))); q->connect(ui.radioUpdate, SIGNAL(toggled(bool)), q, SLOT(_k_updateContentsToggled(bool))); q->connect(ui.registerNewAccountLabel, SIGNAL(linkActivated(QString)), q, SLOT(_k_openRegisterAccountWebpage(QString))); //Busy widget busyWidget = new KPixmapSequenceWidget(); busyWidget->setSequence(KPixmapSequence(QStringLiteral("process-working"), 22)); busyWidget->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); ui.busyWidget->setLayout(new QHBoxLayout()); ui.busyWidget->layout()->addWidget(busyWidget); busyWidget->setVisible(false); WidgetQuestionListener::instance(); return success; } void UploadDialogPrivate::setBusy(const QString &message) { ui.busyLabel->setText(message); busyWidget->setVisible(true); } void UploadDialogPrivate::setIdle(const QString &message) { ui.busyLabel->setText(message); busyWidget->setVisible(false); } void UploadDialogPrivate::_k_showPage(int page) { ui.stackedWidget->setCurrentIndex(page); setIdle(QString()); switch (ui.stackedWidget->currentIndex()) { case UserPasswordPage: ui.username->setFocus(); setBusy(i18n("Fetching provider information...")); break; case FileNewUpdatePage: atticaHelper->loadLicenses(); atticaHelper->loadCurrency(); ui.uploadButton->setFocus(); setBusy(i18n("Fetching license data from server...")); break; case Details1Page: if (ui.radioUpdate->isChecked()) { // Fetch atticaHelper->loadContent(ui.userContentList->currentItem()->data(Qt::UserRole).toString()); setBusy(i18n("Fetching content data from server...")); } ui.mNameEdit->setFocus(); break; case UploadFinalPage: if (previewFile1.isEmpty()) { ui.uploadPreview1ImageLabel->setVisible(false); ui.uploadPreview1Label->setVisible(false); } if (previewFile2.isEmpty()) { ui.uploadPreview2ImageLabel->setVisible(false); ui.uploadPreview2Label->setVisible(false); } if (previewFile3.isEmpty()) { ui.uploadPreview3ImageLabel->setVisible(false); ui.uploadPreview3Label->setVisible(false); } break; } _k_updatePage(); } void UploadDialogPrivate::_k_updatePage() { bool firstPage = ui.stackedWidget->currentIndex() == 0; backButton->setEnabled(!firstPage && !finished); bool nextEnabled = false; switch (ui.stackedWidget->currentIndex()) { case UserPasswordPage: if (ui.providerComboBox->count() > 0 && !ui.username->text().isEmpty() && !ui.password->text().isEmpty()) { nextEnabled = true; } break; case FileNewUpdatePage: // FIXME: check if the file requester contains a valid file if (!uploadFile.isEmpty() || ui.uploadFileRequester->url().isLocalFile()) { if (ui.radioNewUpload->isChecked() || ui.userContentList->currentRow() >= 0) { nextEnabled = true; } } break; case Details1Page: if (!ui.mNameEdit->text().isEmpty()) { nextEnabled = true; } break; case Details2Page: nextEnabled = true; break; case UploadFinalPage: break; } nextButton->setEnabled(nextEnabled); finishButton->setEnabled(finished); nextButton->setDefault(nextEnabled); finishButton->setDefault(!nextEnabled); if (nextEnabled && buttonBox->button(QDialogButtonBox::Cancel)->hasFocus()) { nextButton->setFocus(); } } void UploadDialogPrivate::_k_providersLoaded(const QStringList &providers) { if (providers.isEmpty()) { setIdle(i18n("Could not fetch provider information.")); ui.stackedWidget->setEnabled(false); qWarning() << "Could not load providers."; return; } setIdle(QString()); ui.providerComboBox->addItems(providers); ui.providerComboBox->setCurrentIndex(0); atticaHelper->setCurrentProvider(providers.at(0)); QString user; QString pass; if (atticaHelper->loadCredentials(user, pass)) { ui.username->setText(user); ui.password->setText(pass); } _k_updatePage(); } void UploadDialogPrivate::_k_providerChanged(const QString &providerName) { atticaHelper->setCurrentProvider(providerName); QString registerUrl = atticaHelper->provider().getRegisterAccountUrl(); if (! registerUrl.isEmpty()) { ui.registerNewAccountLabel->setText("" + i18n("Register a new account") + ""); } else { ui.registerNewAccountLabel->setText(QString()); } ui.username->clear(); ui.password->clear(); QString user; QString pass; if (atticaHelper->loadCredentials(user, pass)) { ui.username->setText(user); ui.password->setText(pass); } _k_updatePage(); } void UploadDialogPrivate::_k_backPage() { _k_showPage(ui.stackedWidget->currentIndex() - 1); } void UploadDialogPrivate::_k_nextPage() { // TODO: validate credentials after user name/password have been entered if (ui.stackedWidget->currentIndex() == UserPasswordPage) { setBusy(i18n("Checking login...")); nextButton->setEnabled(false); ui.providerComboBox->setEnabled(false); ui.username->setEnabled(false); ui.password->setEnabled(false); atticaHelper->checkLogin(ui.username->text(), ui.password->text()); } else { _k_showPage(ui.stackedWidget->currentIndex() + 1); } } void UploadDialogPrivate::_k_checkCredentialsFinished(bool success) { ui.providerComboBox->setEnabled(true); ui.username->setEnabled(true); ui.password->setEnabled(true); if (success) { atticaHelper->saveCredentials(ui.username->text(), ui.password->text()); _k_showPage(FileNewUpdatePage); atticaHelper->loadCategories(categoryNames); setBusy(i18n("Fetching your previously updated content...")); } else { // TODO check what the actual error is setIdle(i18n("Could not verify login, please try again.")); } } void UploadDialogPrivate::_k_licensesLoaded(const Attica::License::List &licenses) { ui.mLicenseCombo->clear(); foreach (const Attica::License &license, licenses) { ui.mLicenseCombo->addItem(license.name(), license.id()); } } void UploadDialogPrivate::_k_currencyLoaded(const QString ¤cy) { ui.priceCurrency->setText(currency); } void UploadDialogPrivate::_k_contentByCurrentUserLoaded(const Attica::Content::List &contentList) { setIdle(i18n("Fetching your previously updated content finished.")); foreach (const Attica::Content &content, contentList) { QListWidgetItem *contentItem = new QListWidgetItem(content.name()); contentItem->setData(Qt::UserRole, content.id()); ui.userContentList->addItem(contentItem); } if (ui.userContentList->count() > 0) { ui.userContentList->setCurrentRow(0); ui.radioUpdate->setEnabled(true); _k_updatePage(); } } void UploadDialogPrivate::_k_updatedContentFetched(const Attica::Content &content) { setIdle(i18n("Fetching content data from server finished.")); contentId = content.id(); // fill in ui ui.mNameEdit->setText(content.name()); ui.mSummaryEdit->setText(content.description()); ui.mVersionEdit->setText(content.version()); ui.changelog->setText(content.changelog()); ui.priceCheckBox->setChecked(content.attribute(QStringLiteral("downloadbuy1")) == QLatin1String("1")); ui.priceSpinBox->setValue(content.attribute(QStringLiteral("downloadbuyprice1")).toDouble()); ui.priceReasonLineEdit->setText(content.attribute(QStringLiteral("downloadbuyreason1"))); bool conversionOk = false; int licenseNumber = content.license().toInt(&conversionOk); if (conversionOk) { // check if that int is in list int row = ui.mLicenseCombo->findData(licenseNumber, Qt::UserRole); ui.mLicenseCombo->setCurrentIndex(row); } else { ui.mLicenseCombo->setEditText(content.license()); } ui.contentWebsiteLink->setText(QLatin1String("") + i18nc("A link to the website where the get hot new stuff upload can be seen", "Visit website") + QLatin1String("")); ui.fetchContentLinkImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); } void UploadDialogPrivate::_k_previewLoaded(int index, const QImage &image) { switch (index) { case 1: ui.previewImage1->setPixmap(QPixmap::fromImage(image)); break; case 2: ui.previewImage2->setPixmap(QPixmap::fromImage(image)); break; case 3: ui.previewImage3->setPixmap(QPixmap::fromImage(image)); break; } } void UploadDialogPrivate::_k_updateContentsToggled(bool update) { ui.userContentList->setEnabled(update); } UploadDialog::UploadDialog(QWidget *parent) : QDialog(parent), d(new UploadDialogPrivate(this)) { const QString name = QCoreApplication::applicationName(); init(name + ".knsrc"); } UploadDialog::UploadDialog(const QString &configFile, QWidget *parent) : QDialog(parent), d(new UploadDialogPrivate(this)) { init(configFile); } UploadDialog::~UploadDialog() { delete d; } bool UploadDialog::init(const QString &configfile) { bool success = d->init(configfile); setWindowTitle(i18n("Share Hot New Stuff")); d->_k_updatePage(); connect(d->ui.username, SIGNAL(textChanged(QString)), this, SLOT(_k_updatePage())); connect(d->ui.password, SIGNAL(textChanged(QString)), this, SLOT(_k_updatePage())); connect(d->ui.mNameEdit, SIGNAL(textChanged(QString)), this, SLOT(_k_updatePage())); connect(d->ui.uploadFileRequester, SIGNAL(textChanged(QString)), this, SLOT(_k_updatePage())); connect(d->ui.priceCheckBox, SIGNAL(toggled(bool)), this, SLOT(_k_priceToggled(bool))); connect(d->ui.uploadButton, SIGNAL(clicked()), this, SLOT(_k_startUpload())); connect(d->backButton, SIGNAL(clicked()), this, SLOT(_k_backPage())); connect(d->nextButton, SIGNAL(clicked()), this, SLOT(_k_nextPage())); connect(d->buttonBox, &QDialogButtonBox::accepted, this, &UploadDialog::accept); connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); QString displayName = QGuiApplication::applicationDisplayName(); if (displayName.isEmpty()) { displayName = QCoreApplication::applicationName(); } d->ui.mTitleWidget->setText(i18nc("Program name followed by 'Add On Uploader'", "%1 Add-On Uploader", displayName)); //d->ui.mTitleWidget->setPixmap(QIcon::fromTheme(KGlobal::activeComponent().aboutData()->programIconName())); if (success) { d->_k_showPage(0); } return success; } void UploadDialog::setUploadFile(const QUrl &payloadFile) { d->uploadFile = payloadFile; d->ui.uploadFileLabel->setVisible(false); d->ui.uploadFileRequester->setVisible(false); QFile file(d->uploadFile.toLocalFile()); if (!file.open(QIODevice::ReadOnly)) { KMessageBox::error(this, i18n("File not found: %1", d->uploadFile.url()), i18n("Upload Failed")); } } void UploadDialog::setUploadName(const QString &name) { d->ui.mNameEdit->setText(name); } void UploadDialog::selectCategory(const QString &category) { d->ui.mCategoryCombo->setCurrentIndex(d->ui.mCategoryCombo->findText(category, Qt::MatchFixedString)); } void UploadDialog::setChangelog(const QString &changelog) { d->ui.changelog->setText(changelog); } void UploadDialog::setDescription(const QString &description) { d->ui.mSummaryEdit->setText(description); } void UploadDialog::setPriceEnabled(bool enabled) { d->ui.priceCheckBox->setVisible(enabled); d->ui.priceGroupBox->setVisible(enabled); } void UploadDialog::setPrice(double price) { d->ui.priceCheckBox->setEnabled(true); d->ui.priceSpinBox->setValue(price); } void UploadDialog::setPriceReason(const QString &reason) { d->ui.priceReasonLineEdit->setText(reason); } void UploadDialog::setVersion(const QString &version) { d->ui.mVersionEdit->setText(version); } void UploadDialog::setPreviewImageFile(uint number, const QUrl &file) { QPixmap preview(file.toLocalFile()); switch (number) { case 0 : d->previewFile1 = file; d->ui.previewImage1->setPixmap(preview.scaled(d->ui.previewImage1->size())); break; case 1 : d->previewFile2 = file; d->ui.previewImage2->setPixmap(preview.scaled(d->ui.previewImage2->size())); break; case 2 : d->previewFile3 = file; d->ui.previewImage3->setPixmap(preview.scaled(d->ui.previewImage3->size())); break; default : qCritical() << "Wrong preview image file number"; break; } } void UploadDialogPrivate::_k_priceToggled(bool priceEnabled) { ui.priceGroupBox->setEnabled(priceEnabled); } void UploadDialogPrivate::_k_categoriesLoaded(const Attica::Category::List &loadedCategories) { categories = loadedCategories; // at least one category is needed if (categories.count() == 0) { KMessageBox::error(q, i18np("The server does not recognize the category %2 to which you are trying to upload.", "The server does not recognize any of the categories to which you are trying to upload: %2", categoryNames.size(), categoryNames.join(", ")), i18n("Error")); // close the dialog q->reject(); return; } foreach (const Attica::Category &c, categories) { ui.mCategoryCombo->addItem(c.name(), c.id()); } atticaHelper->loadContentByCurrentUser(); } void UploadDialog::accept() { QDialog::accept(); } void UploadDialogPrivate::_k_startUpload() { // FIXME: this only works if categories are set in the .knsrc file // TODO: ask for confirmation when closing the dialog backButton->setEnabled(false); buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); ui.uploadButton->setEnabled(false); // idle back and forth, we need a fix in attica to get at real progress values ui.uploadProgressBar->setMinimum(0); ui.uploadProgressBar->setMaximum(0); ui.uploadProgressBar->setValue(0); // check the category QString categoryName = ui.mCategoryCombo->currentText(); QList::const_iterator iter = categories.constBegin(); Attica::Category category; QList::const_iterator iterEnd = categories.constEnd(); while (iter != iterEnd) { if (iter->name() == categoryName) { category = *iter; break; } ++iter; } if (!category.isValid()) { KMessageBox::error(q, i18n("The selected category \"%1\" is invalid.", categoryName), i18n("Upload Failed")); return; } // fill in the content object Attica::Content content; content.setName(ui.mNameEdit->text()); QString summary = ui.mSummaryEdit->toPlainText(); content.addAttribute(QStringLiteral("description"), summary); content.addAttribute(QStringLiteral("version"), ui.mVersionEdit->text()); // for the license, if one of the licenses coming from the server was used, pass its id, otherwise the string QString licenseId = ui.mLicenseCombo->itemData(ui.mLicenseCombo->currentIndex()).toString(); if (licenseId.isEmpty()) { // use other as type and add the string as text content.addAttribute(QStringLiteral("licensetype"), QStringLiteral("0")); content.addAttribute(QStringLiteral("license"), ui.mLicenseCombo->currentText()); } else { content.addAttribute(QStringLiteral("licensetype"), licenseId); } content.addAttribute(QStringLiteral("changelog"), ui.changelog->toPlainText()); // TODO: add additional attributes //content.addAttribute("downloadlink1", ui.link1->text()); //content.addAttribute("downloadlink2", ui.link2->text()); //content.addAttribute("homepage1", ui.homepage->text()); //content.addAttribute("blog1", ui.blog->text()); content.addAttribute(QStringLiteral("downloadbuy1"), ui.priceCheckBox->isChecked() ? "1" : "0"); content.addAttribute(QStringLiteral("downloadbuyprice1"), QString::number(ui.priceSpinBox->value())); content.addAttribute(QStringLiteral("downloadbuyreason1"), ui.priceReasonLineEdit->text()); if (ui.radioNewUpload->isChecked()) { // upload a new content Attica::ItemPostJob *job = currentProvider().addNewContent(category, content); q->connect(job, SIGNAL(finished(Attica::BaseJob*)), q, SLOT(_k_contentAdded(Attica::BaseJob*))); job->start(); } else { // update old content Attica::ItemPostJob *job = currentProvider().editContent(category, ui.userContentList->currentItem()->data(Qt::UserRole).toString(), content); q->connect(job, SIGNAL(finished(Attica::BaseJob*)), q, SLOT(_k_contentAdded(Attica::BaseJob*))); job->start(); } } void UploadDialogPrivate::_k_changePreview1() { const QStringList filters = _supportedMimeTypes(); QPointer dialog = new QFileDialog(q, i18n("Select preview image")); dialog->setMimeTypeFilters(filters); if (dialog->exec() == QDialog::Accepted) { QUrl url = dialog->selectedUrls().first(); previewFile1 = url; qCDebug(KNEWSTUFF) << "preview is: " << url.url(); QPixmap preview(url.toLocalFile()); ui.previewImage1->setPixmap(preview.scaled(ui.previewImage1->size())); } delete dialog; } void UploadDialogPrivate::_k_changePreview2() { const QStringList filters = _supportedMimeTypes(); QPointer dialog = new QFileDialog(q, i18n("Select preview image")); dialog->setMimeTypeFilters(filters); if (dialog->exec() == QDialog::Accepted) { QUrl url = dialog->selectedUrls().first(); previewFile2 = url; QPixmap preview(url.toLocalFile()); ui.previewImage2->setPixmap(preview.scaled(ui.previewImage1->size())); } delete dialog; } void UploadDialogPrivate::_k_changePreview3() { const QStringList filters = _supportedMimeTypes(); QPointer dialog = new QFileDialog(q, i18n("Select preview image")); dialog->setMimeTypeFilters(filters); if (dialog->exec() == QDialog::Accepted) { QUrl url = dialog->selectedUrls().first(); previewFile3 = url; QPixmap preview(url.toLocalFile()); ui.previewImage3->setPixmap(preview.scaled(ui.previewImage1->size())); } delete dialog; } void UploadDialogPrivate::_k_contentAdded(Attica::BaseJob *baseJob) { if (baseJob->metadata().error()) { if (baseJob->metadata().error() == Attica::Metadata::NetworkError) { KMessageBox::error(q, i18n("There was a network error."), i18n("Uploading Failed")); return; } if (baseJob->metadata().error() == Attica::Metadata::OcsError) { if (baseJob->metadata().statusCode() == 102) { KMessageBox::error(q, i18n("Authentication error."), i18n("Uploading Failed")); } } return; } ui.createContentImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); Attica::ItemPostJob *job = static_cast *>(baseJob); if (job->metadata().error() != Attica::Metadata::NoError) { KMessageBox::error(q, i18n("Upload failed: %1", job->metadata().message())); return; } // only when adding new content we get an id returned, otherwise stick with the old one QString id = job->result().id(); if (!id.isEmpty()) { contentId = id; } if (!uploadFile.isEmpty()) { doUpload(QString(), uploadFile); } else { doUpload(QString(), ui.uploadFileRequester->url()); } // FIXME: status labels need to accommodate 3 previews if (!previewFile1.isEmpty()) { doUpload(QStringLiteral("1"), previewFile1); } if (!previewFile2.isEmpty()) { doUpload(QStringLiteral("2"), previewFile2); } if (!previewFile3.isEmpty()) { doUpload(QStringLiteral("3"), previewFile3); } if (ui.radioNewUpload->isChecked()) { atticaHelper->loadDetailsLink(contentId); } } void UploadDialogPrivate::_k_openRegisterAccountWebpage(QString) { KRun::runUrl(QUrl::fromUserInput(atticaHelper->provider().getRegisterAccountUrl()), QStringLiteral("text/html"), q); } void UploadDialogPrivate::doUpload(const QString &index, const QUrl &path) { QFile file(path.toLocalFile()); if (!file.open(QIODevice::ReadOnly)) { KMessageBox::error(q, i18n("File not found: %1", uploadFile.url()), i18n("Upload Failed")); q->reject(); return; } QByteArray fileContents; fileContents.append(file.readAll()); file.close(); QString fileName = QFileInfo(path.toLocalFile()).fileName(); - Attica::PostJob *job = 0; + Attica::PostJob *job = nullptr; if (index.isEmpty()) { job = currentProvider().setDownloadFile(contentId, fileName, fileContents); q->connect(job, SIGNAL(finished(Attica::BaseJob*)), q, SLOT(_k_fileUploadFinished(Attica::BaseJob*))); } else if (index == QLatin1String("1")) { job = currentProvider().setPreviewImage(contentId, index, fileName, fileContents); q->connect(job, SIGNAL(finished(Attica::BaseJob*)), q, SLOT(_k_preview1UploadFinished(Attica::BaseJob*))); } else if (index == QLatin1String("2")) { job = currentProvider().setPreviewImage(contentId, index, fileName, fileContents); q->connect(job, SIGNAL(finished(Attica::BaseJob*)), q, SLOT(_k_preview2UploadFinished(Attica::BaseJob*))); } else if (index == QLatin1String("3")) { job = currentProvider().setPreviewImage(contentId, index, fileName, fileContents); q->connect(job, SIGNAL(finished(Attica::BaseJob*)), q, SLOT(_k_preview3UploadFinished(Attica::BaseJob*))); } if (job) { job->start(); } } void UploadDialogPrivate::_k_fileUploadFinished(Attica::BaseJob *) { ui.uploadContentImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); finishedContents = true; uploadFileFinished(); } void UploadDialogPrivate::_k_preview1UploadFinished(Attica::BaseJob *) { ui.uploadPreview1ImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); finishedPreview1 = true; uploadFileFinished(); } void UploadDialogPrivate::_k_preview2UploadFinished(Attica::BaseJob *) { ui.uploadPreview2ImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); finishedPreview2 = true; uploadFileFinished(); } void UploadDialogPrivate::_k_preview3UploadFinished(Attica::BaseJob *) { ui.uploadPreview3ImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); finishedPreview3 = true; uploadFileFinished(); } void UploadDialogPrivate::uploadFileFinished() { // FIXME multiple previews if (finishedContents && (previewFile1.isEmpty() || finishedPreview1) && (previewFile2.isEmpty() || finishedPreview2) && (previewFile3.isEmpty() || finishedPreview3)) { finished = true; ui.uploadProgressBar->setMinimum(0); ui.uploadProgressBar->setMaximum(100); ui.uploadProgressBar->setValue(100); _k_updatePage(); } } void UploadDialogPrivate::_k_detailsLinkLoaded(const QUrl &url) { ui.contentWebsiteLink->setText(QLatin1String("") + i18nc("A link to the website where the get hot new stuff upload can be seen", "Visit website") + QLatin1String("")); ui.fetchContentLinkImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); } QStringList UploadDialogPrivate::_supportedMimeTypes() const { QStringList mimeTypes; QList supported = QImageReader::supportedMimeTypes(); foreach (const QByteArray &mimeType, supported) { mimeTypes.append(QString(mimeType)); } return mimeTypes; } #include "moc_uploaddialog.cpp" diff --git a/src/uploaddialog.h b/src/uploaddialog.h index 8697550b..798d0114 100644 --- a/src/uploaddialog.h +++ b/src/uploaddialog.h @@ -1,202 +1,202 @@ /* knewstuff3/ui/uploaddialog.h. Copyright (c) 2002 Cornelius Schumacher Copyright (C) 2007 Josef Spillner Copyright (C) 2009 Jeremy Whiting Copyright (C) 2009-2010 Frederik Gladhorn This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KNEWSTUFF3_UI_UPLOADDIALOG_H #define KNEWSTUFF3_UI_UPLOADDIALOG_H #include #include #include "knewstuff_export.h" namespace Attica { class BaseJob; class Provider; } // KDE5: this class should inherit from the wizard class - KAssistantDialog namespace KNS3 { class UploadDialogPrivate; /** * @short KNewStuff file upload dialog. * * Using this dialog, data can easily be uploaded to the Hotstuff servers. * * \par Maintainer: * Jeremy Whiting (jpwhiting@kde.org) * * @since 4.4 */ class KNEWSTUFF_EXPORT UploadDialog : public QDialog { Q_OBJECT public: /** Create a new upload dialog. @param parent the parent window */ - explicit UploadDialog(QWidget *parent = 0); + explicit UploadDialog(QWidget *parent = nullptr); /** Create a new upload dialog. @param parent the parent window */ - explicit UploadDialog(const QString &configFile, QWidget *parent = 0); + explicit UploadDialog(const QString &configFile, QWidget *parent = nullptr); /** Destructor. */ ~UploadDialog(); /** Set the file to be uploaded. This has to be set for the dialog to work, before displaying the dialog. @param payloadFile the payload data file */ void setUploadFile(const QUrl &payloadFile); /** Set the suggested title for the upload. The application can suggest a title which can then be edited by the user before uploading. The name field will be left empty if no title was set. @param name the suggested name for the upload */ void setUploadName(const QString &name); /** Set the suggested version displayed in the upload dialog. The user can still change this. @param version */ void setVersion(const QString &version); /** Set the suggested description displayed in the upload dialog. The user can still change this. @param description */ void setDescription(const QString &description); /** Set the suggested changelog displayed in the upload dialog. The user can still change this. @param version version */ void setChangelog(const QString &changelog); /* * Set the suggested license displayed in the upload dialog. The user can still change this. @param version version */ // enum License {}; // see fd.o api spec // void setLicense(License license); /** Set one of the threee preview images displayed in the upload dialog. The user can still change this. @param number The number of the preview image to set, either 1, 2, or 3. @param file A URL to the file to be used as preview image @since 4.6 */ void setPreviewImageFile(uint number, const QUrl &file); /** Enable the UI to let the user to set a price for the uploaded item. @param enabled enable the price option - it is enabled by default @since 4.5 */ void setPriceEnabled(bool enabled); /** Set the suggested price displayed in the upload dialog. The user can still change this. @param version version */ void setPrice(double price); /** Set the suggested rationale why this item costs something to download. The user can still change this. @param version version */ void setPriceReason(const QString &reason); /** Set the suggested category for the upload. The .knsrc file may contain multiple upload categories, this sets which one is pre-selected. It does not add any new category to the list of available categories. @param category the suggested category for the upload */ void selectCategory(const QString &category); public Q_SLOTS: void accept() Q_DECL_OVERRIDE; private: bool init(const QString &configfile); UploadDialogPrivate *const d; Q_PRIVATE_SLOT(d, void _k_nextPage()) Q_PRIVATE_SLOT(d, void _k_backPage()) Q_PRIVATE_SLOT(d, void _k_updatePage()) Q_PRIVATE_SLOT(d, void _k_providerChanged(QString)) Q_PRIVATE_SLOT(d, void _k_checkCredentialsFinished(bool)) Q_PRIVATE_SLOT(d, void _k_contentByCurrentUserLoaded(Attica::Content::List)) Q_PRIVATE_SLOT(d, void _k_providersLoaded(QStringList)) Q_PRIVATE_SLOT(d, void _k_categoriesLoaded(Attica::Category::List)) Q_PRIVATE_SLOT(d, void _k_licensesLoaded(Attica::License::List)) Q_PRIVATE_SLOT(d, void _k_currencyLoaded(QString)) Q_PRIVATE_SLOT(d, void _k_previewLoaded(int, QImage)) Q_PRIVATE_SLOT(d, void _k_changePreview1()) Q_PRIVATE_SLOT(d, void _k_changePreview2()) Q_PRIVATE_SLOT(d, void _k_changePreview3()) Q_PRIVATE_SLOT(d, void _k_priceToggled(bool)) Q_PRIVATE_SLOT(d, void _k_updateContentsToggled(bool update)) Q_PRIVATE_SLOT(d, void _k_startUpload()) Q_PRIVATE_SLOT(d, void _k_contentAdded(Attica::BaseJob *)) Q_PRIVATE_SLOT(d, void _k_fileUploadFinished(Attica::BaseJob *)) Q_PRIVATE_SLOT(d, void _k_preview1UploadFinished(Attica::BaseJob *)) Q_PRIVATE_SLOT(d, void _k_preview2UploadFinished(Attica::BaseJob *)) Q_PRIVATE_SLOT(d, void _k_preview3UploadFinished(Attica::BaseJob *)) Q_PRIVATE_SLOT(d, void _k_updatedContentFetched(Attica::Content)) Q_PRIVATE_SLOT(d, void _k_detailsLinkLoaded(QUrl)) Q_PRIVATE_SLOT(d, void _k_openRegisterAccountWebpage(QString)) Q_DISABLE_COPY(UploadDialog) }; } #endif