diff --git a/src/attica/atticaprovider.cpp b/src/attica/atticaprovider.cpp index 66ffb986..ee3cb856 100644 --- a/src/attica/atticaprovider.cpp +++ b/src/attica/atticaprovider.cpp @@ -1,531 +1,533 @@ /* 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 "tagsfilterchecker.h" #include #include #include #include #include #include #include #include #include #include using namespace Attica; namespace KNSCore { AtticaProvider::AtticaProvider(const QStringList &categories) : mEntryJob(nullptr) , mInitialized(false) { // init categories map with invalid categories for (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(nullptr) , mInitialized(false) { // init categories map with invalid categories for (const QString &category : categories) { mCategoryMap.insert(category, Attica::Category()); } providerLoaded(provider); } QString AtticaProvider::id() const { return m_providerId; } 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().constLast().baseUrl().toString(); } else { qCCritical(KNEWSTUFFCORE) << "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; m_providerId = provider.baseUrl().toString(); 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); const Category::List categoryList = job->itemList(); QList categoryMetadataList; for (const Category &category : categoryList) { if (mCategoryMap.contains(category.name())) { qCDebug(KNEWSTUFFCORE) << "Adding category: " << category.name() << category.displayName(); //If there is only the placeholder category, replace it if (mCategoryMap.contains(category.name()) && !mCategoryMap.value(category.name()).isValid()) { mCategoryMap.insert(category.name(), category); } else { mCategoryMap.insertMulti(category.name(), category); } CategoryMetadata categoryMetadata; categoryMetadata.id = category.id(); categoryMetadata.name = category.name(); categoryMetadata.displayName = category.displayName(); categoryMetadataList << categoryMetadata; } } std::sort(categoryMetadataList.begin(), categoryMetadataList.end(), [](const AtticaProvider::CategoryMetadata &i, const AtticaProvider::CategoryMetadata &j) -> bool { const QString a(i.displayName.isEmpty() ? i.name : i.displayName); const QString b(j.displayName.isEmpty() ? j.name : j.displayName); return (QCollator().compare(a, b) < 0); }); 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 signalErrorCode(KNSCore::ConfigFileError, i18n("All categories are missing"), QVariant()); } } bool AtticaProvider::isInitialized() const { return mInitialized; } void AtticaProvider::loadEntries(const KNSCore::Provider::SearchRequest &request) { if (mEntryJob) { mEntryJob->abort(); 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()); for (const QString &categoryName : qAsConst(request.categories)) { categoriesToSearch.append(mCategoryMap.values(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() { for (const EntryInternal &e : qAsConst(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; for (const EntryInternal &entry : qAsConst(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; TagsFilterChecker checker(tagFilter()); TagsFilterChecker downloadschecker(downloadTagFilter()); for (const Content &content : contents) { if (!content.isValid()) { qCDebug(KNEWSTUFFCORE) << "Filtered out an invalid entry. This suggests something is not right on the originating server. Please contact the administrators of" << name() << "and inform them there is an issue with content in the category or categories" << mCurrentRequest.categories; continue; } if (checker.filterAccepts(content.tags())) { bool filterAcceptsDownloads = true; if (content.downloads() > 0) { filterAcceptsDownloads = false; for (const Attica::DownloadDescription &dli : content.downloadUrlDescriptions()) { if (downloadschecker.filterAccepts(dli.tags())) { filterAcceptsDownloads = true; break; } } } if (filterAcceptsDownloads) { mCachedContent.insert(content.id(), content); entries.append(entryFromAtticaContent(content)); } else { qCDebug(KNEWSTUFFCORE) << "Filter has excluded" << content.name() << "on download filter" << downloadTagFilter(); } } else { qCDebug(KNEWSTUFFCORE) << "Filter has excluded" << content.name() << "on entry filter" << tagFilter(); } } qCDebug(KNEWSTUFFCORE) << "loaded: " << mCurrentRequest.hashForRequest() << " count: " << entries.size(); emit loadingFinished(mCurrentRequest, entries); 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; for (const EntryInternal &entry : qAsConst(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 signalErrorCode(KNSCore::NetworkError, i18n("Network error %1: %2", job->metadata().statusCode(), job->metadata().statusString()), job->metadata().statusCode()); } if (job->metadata().error() == Attica::Metadata::OcsError) { if (job->metadata().statusCode() == 200) { emit signalErrorCode(KNSCore::OcsError, i18n("Too many requests to server. Please try again in a few minutes."), job->metadata().statusCode()); + } else if (job->metadata().statusCode() == 405) { + emit signalErrorCode(KNSCore::OcsError, i18n("The Open Collaboration Services instance %1 does not support the attempted function.").arg(name()), job->metadata().statusCode()); } else { emit signalErrorCode(KNSCore::OcsError, i18n("Unknown Open Collaboration Service API error. (%1)", job->metadata().statusCode()), 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.setTags(content.tags()); entry.clearDownloadLinkInformation(); const QList descs = content.downloadUrlDescriptions(); for (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; info.tags = desc.tags(); entry.appendDownloadLinkInformation(info); } return entry; } } // namespace diff --git a/src/ui/itemsviewdelegate.cpp b/src/ui/itemsviewdelegate.cpp index 61603fe8..8fbfc6db 100644 --- a/src/ui/itemsviewdelegate.cpp +++ b/src/ui/itemsviewdelegate.cpp @@ -1,354 +1,357 @@ /* 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 #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); 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; const KNSCore::EntryInternal entry = index.data(Qt::UserRole).value(); connect(rating, static_cast(&KRatingWidget::ratingChanged), this, [this, entry](unsigned int newRating){m_engine->vote(entry, newRating * 10);}); 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; } const 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) { if (installButton->menu()) { QMenu *buttonMenu = installButton->menu(); buttonMenu->clear(); 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); installButton->setPopupMode(QToolButton::InstantPopup); if (installable && entry.downloadLinkCount() > 1) { QMenu *installMenu = new QMenu(installButton); const auto lst = entry.downloadLinkInformationList(); for (const KNSCore::EntryInternal::DownloadLinkInformation &info : lst) { QString text = info.name; if (!info.distributionType.trimmed().isEmpty()) { text += QStringLiteral(" (") + info.distributionType.trimmed() + QLatin1Char(')'); } QAction *installAction = installMenu->addAction(m_iconInstall, text); installAction->setData(QPoint(index.row(), info.id)); } installButton->setMenu(installMenu); } else if (entry.status() == Entry::Installed && m_engine->hasAdoptionCommand()) { QMenu* m = new QMenu(installButton); m->addAction(i18n("Use"), m, [this, entry](){ QStringList args = KShell::splitArgs(m_engine->adoptionCommand(entry)); qCDebug(KNEWSTUFF) << "executing AdoptionCommand" << args; QProcess::startDetached(args.takeFirst(), args); }); installButton->setPopupMode(QToolButton::MenuButtonPopup); installButton->setMenu(m); } } 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)); if (infoLabel != nullptr) { infoLabel->setWordWrap(true); 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 = QStringLiteral("\n" "

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

") + entry.name() + QStringLiteral("

\n"); } else { text += entry.name(); } const auto downloadInfo = entry.downloadLinkInformationList(); if (!downloadInfo.isEmpty() && downloadInfo.at(0).size > 0) { QString sizeString = KFormat().formatByteSize(downloadInfo.at(0).size * 1000); text += i18nc("Show the size of the file in a list", "

Size: %1

", sizeString); } 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 += QStringLiteral("

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

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

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

\n"); } else { text += QStringLiteral("

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

\n"); } } QString summary = QStringLiteral("

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

\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 += QStringLiteral("

") + 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())); + // Don't attempt to send a rating to the server if we're just updating the UI + rating->blockSignals(true); // 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); + rating->blockSignals(false); // 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, 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/progressindicator.cpp b/src/ui/progressindicator.cpp index ebb13f6d..fba981c2 100644 --- a/src/ui/progressindicator.cpp +++ b/src/ui/progressindicator.cpp @@ -1,74 +1,78 @@ /* This file is part of KNewStuff2. Copyright (c) 2007 Josef Spillner Copyright (c) 2007 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 "progressindicator_p.h" #include #include #include #include +#include #include "kjob.h" #include #include using namespace KNS3; ProgressIndicator::ProgressIndicator(QWidget *parent) : QFrame(parent) , m_busyPixmap(KIconLoader::global()->loadPixmapSequence(QStringLiteral("process-working"), 22)) , m_errorPixmap(KIconLoader::global()->loadPixmapSequence(QStringLiteral("dialog-error"), 22)) { setFrameStyle(QFrame::NoFrame); QHBoxLayout *hbox = new QHBoxLayout(this); hbox->setContentsMargins(0, 0, 0, 0); //Busy widget busyWidget = new KPixmapSequenceWidget(this); busyWidget->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); busyWidget->setVisible(false); hbox->addWidget(busyWidget); m_statusLabel = new QLabel(); hbox->addWidget(m_statusLabel); } void ProgressIndicator::busy(const QString &message) { m_statusLabel->setText(message); busyWidget->setVisible(true); busyWidget->setSequence(m_busyPixmap); } -void KNS3::ProgressIndicator::error(const KNSCore::ErrorCode& /*errorCode*/, const QString& message, const QVariant& /*metadata*/) +void KNS3::ProgressIndicator::error(const KNSCore::ErrorCode& errorCode, const QString& message, const QVariant& metadata) { + if(errorCode == KNSCore::OcsError && metadata.value() == 405) { + return; + } m_statusLabel->setText(message); busyWidget->setVisible(true); busyWidget->setSequence(m_errorPixmap); } void ProgressIndicator::idle(const QString &message) { m_statusLabel->setText(message); busyWidget->setVisible(false); }