File indexing completed on 2024-04-28 15:29:04

0001 /*
0002     SPDX-FileCopyrightText: 2009 Frederik Gladhorn <gladhorn@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "entrydetailsdialog_p.h"
0008 
0009 #include <KLocalizedString>
0010 #include <QMenu>
0011 #include <knewstuff_debug.h>
0012 
0013 #include "core/engine.h"
0014 #include "core/imageloader_p.h"
0015 #include <attica/provider.h>
0016 
0017 using namespace KNS3;
0018 
0019 EntryDetails::EntryDetails(KNSCore::Engine *engine, Ui::DownloadWidget *widget)
0020     : QObject(widget->m_listView)
0021     , m_engine(engine)
0022     , ui(widget)
0023 {
0024     init();
0025 }
0026 
0027 EntryDetails::~EntryDetails()
0028 {
0029 }
0030 
0031 void EntryDetails::init()
0032 {
0033     connect(ui->preview1, &ImagePreviewWidget::clicked, this, &EntryDetails::preview1Selected);
0034     connect(ui->preview2, &ImagePreviewWidget::clicked, this, &EntryDetails::preview2Selected);
0035     connect(ui->preview3, &ImagePreviewWidget::clicked, this, &EntryDetails::preview3Selected);
0036 
0037     ui->ratingWidget->setMaxRating(10);
0038     ui->ratingWidget->setHalfStepsEnabled(true);
0039 
0040     updateButtons();
0041     connect(ui->installButton, &QAbstractButton::clicked, this, &EntryDetails::install);
0042     connect(ui->uninstallButton, &QAbstractButton::clicked, this, &EntryDetails::uninstall);
0043     // updating is the same as installing
0044     connect(ui->updateButton, &QAbstractButton::clicked, this, &EntryDetails::install);
0045     connect(ui->becomeFanButton, &QAbstractButton::clicked, this, &EntryDetails::becomeFan);
0046 
0047     ui->installButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok")));
0048     ui->updateButton->setIcon(QIcon::fromTheme(QStringLiteral("system-software-update")));
0049     ui->uninstallButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0050 
0051     connect(m_engine, &KNSCore::Engine::signalEntryEvent, this, [this](const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::EntryEvent event) {
0052         if (event == KNSCore::EntryInternal::DetailsLoadedEvent) {
0053             Q_EMIT entryChanged(entry);
0054         } else if (event == KNSCore::EntryInternal::StatusChangedEvent) {
0055             updateButtons();
0056         }
0057     });
0058     connect(m_engine, &KNSCore::Engine::signalEntryPreviewLoaded, this, &EntryDetails::slotEntryPreviewLoaded);
0059 }
0060 
0061 void EntryDetails::setEntry(const KNSCore::EntryInternal &entry)
0062 {
0063     m_entry = entry;
0064     // immediately show something
0065     entryChanged(m_entry);
0066     // fetch more preview images
0067     m_engine->loadDetails(m_entry);
0068 }
0069 
0070 void EntryDetails::entryChanged(const KNSCore::EntryInternal &entry)
0071 {
0072     if (ui->detailsStack->currentIndex() == 0) {
0073         return;
0074     }
0075     m_entry = entry;
0076 
0077     // FIXME
0078     // ui->ratingWidget->setEditable(m_engine->userCanVote(m_entry));
0079 
0080     if (!m_engine->userCanBecomeFan(m_entry)) {
0081         ui->becomeFanButton->setEnabled(false);
0082     }
0083 
0084     ui->m_titleWidget->setText(i18n("Details for %1", m_entry.name()));
0085     if (!m_entry.author().homepage().isEmpty()) {
0086         ui->authorLabel->setText(QLatin1String("<a href=\"") + m_entry.author().homepage() + QLatin1String("\">") + m_entry.author().name()
0087                                  + QLatin1String("</a>"));
0088     } else if (!m_entry.author().email().isEmpty()) {
0089         ui->authorLabel->setText(QLatin1String("<a href=\"mailto:") + m_entry.author().email() + QLatin1String("\">") + m_entry.author().name()
0090                                  + QLatin1String("</a>"));
0091     } else {
0092         ui->authorLabel->setText(m_entry.author().name());
0093     }
0094 
0095     QString summary = KNSCore::replaceBBCode(m_entry.summary()).replace(QLatin1Char('\n'), QLatin1String("<br/>"));
0096     QString changelog = KNSCore::replaceBBCode(m_entry.changelog()).replace(QLatin1Char('\n'), QLatin1String("<br/>"));
0097 
0098     QString description = QLatin1String("<html><body>") + summary;
0099     if (!changelog.isEmpty()) {
0100         description += QLatin1String("<br/><p><b>") + i18n("Changelog:") + QLatin1String("</b><br/>") + changelog + QLatin1String("</p>");
0101     }
0102     description += QLatin1String("</body></html>");
0103     ui->descriptionLabel->setText(description);
0104 
0105     QString homepageText(QLatin1String("<a href=\"") + m_entry.homepage().url() + QLatin1String("\">")
0106                          + i18nc("A link to the description of this Get Hot New Stuff item", "Homepage") + QStringLiteral("</a>"));
0107 
0108     if (!m_entry.donationLink().isEmpty()) {
0109         homepageText += QLatin1String("<br><a href=\"") + m_entry.donationLink() + QLatin1String("\">")
0110             + i18nc("A link to make a donation for a Get Hot New Stuff item (opens a web browser)", "Make a donation") + QLatin1String("</a>");
0111     }
0112     if (!m_entry.knowledgebaseLink().isEmpty()) {
0113         homepageText += QLatin1String("<br><a href=\"") + m_entry.knowledgebaseLink() + QLatin1String("\">")
0114             + i18ncp("A link to the knowledgebase (like a forum) (opens a web browser)",
0115                      "Knowledgebase (no entries)",
0116                      "Knowledgebase (%1 entries)",
0117                      m_entry.numberKnowledgebaseEntries())
0118             + QStringLiteral("</a>");
0119     }
0120     ui->homepageLabel->setText(homepageText);
0121     ui->homepageLabel->setToolTip(i18nc("Tooltip for a link in a dialog", "Opens in a browser window"));
0122 
0123     if (m_entry.rating() > 0) {
0124         ui->ratingWidget->setVisible(true);
0125         disconnect(ui->ratingWidget, &KRatingWidget::ratingChanged, this, &EntryDetails::ratingChanged);
0126         // Most of the voting is 20 - 80, so rate 20 as 0 stars and 80 as 5 stars
0127         int rating = qMax(0, qMin(10, (m_entry.rating() - 20) / 6));
0128         ui->ratingWidget->setRating(rating);
0129         connect(ui->ratingWidget, &KRatingWidget::ratingChanged, this, &EntryDetails::ratingChanged);
0130     } else {
0131         ui->ratingWidget->setVisible(false);
0132     }
0133 
0134     bool hideSmallPreviews =
0135         m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall2).isEmpty() && m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall3).isEmpty();
0136 
0137     ui->preview1->setVisible(!hideSmallPreviews);
0138     ui->preview2->setVisible(!hideSmallPreviews);
0139     ui->preview3->setVisible(!hideSmallPreviews);
0140 
0141     // in static xml we often only get a small preview, use that in details
0142     if (m_entry.previewUrl(KNSCore::EntryInternal::PreviewBig1).isEmpty() && !m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1).isEmpty()) {
0143         m_entry.setPreviewUrl(m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1), KNSCore::EntryInternal::PreviewBig1);
0144         m_entry.setPreviewImage(m_entry.previewImage(KNSCore::EntryInternal::PreviewSmall1), KNSCore::EntryInternal::PreviewBig1);
0145     }
0146 
0147     for (int type = KNSCore::EntryInternal::PreviewSmall1; type <= KNSCore::EntryInternal::PreviewBig3; ++type) {
0148         if (m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1).isEmpty()) {
0149             ui->previewBig->setVisible(false);
0150         } else
0151 
0152             if (!m_entry.previewUrl((KNSCore::EntryInternal::PreviewType)type).isEmpty()) {
0153             qCDebug(KNEWSTUFF) << "type: " << type << m_entry.previewUrl((KNSCore::EntryInternal::PreviewType)type);
0154             if (m_entry.previewImage((KNSCore::EntryInternal::PreviewType)type).isNull()) {
0155                 m_engine->loadPreview(m_entry, (KNSCore::EntryInternal::PreviewType)type);
0156             } else {
0157                 slotEntryPreviewLoaded(m_entry, (KNSCore::EntryInternal::PreviewType)type);
0158             }
0159         }
0160     }
0161 
0162     updateButtons();
0163 }
0164 
0165 void EntryDetails::updateButtons()
0166 {
0167     if (ui->detailsStack->currentIndex() == 0) {
0168         return;
0169     }
0170     qCDebug(KNEWSTUFF) << "update buttons: " << m_entry.status();
0171     ui->installButton->setVisible(false);
0172     ui->uninstallButton->setVisible(false);
0173     ui->updateButton->setVisible(false);
0174 
0175     switch (m_entry.status()) {
0176     case Entry::Installed:
0177         ui->uninstallButton->setVisible(true);
0178         ui->uninstallButton->setEnabled(true);
0179         break;
0180     case Entry::Updateable:
0181         ui->updateButton->setVisible(true);
0182         ui->updateButton->setEnabled(true);
0183         ui->uninstallButton->setVisible(true);
0184         ui->uninstallButton->setEnabled(true);
0185         break;
0186 
0187     case Entry::Invalid:
0188     case Entry::Downloadable:
0189         ui->installButton->setVisible(true);
0190         ui->installButton->setEnabled(true);
0191         break;
0192 
0193     case Entry::Installing:
0194         ui->installButton->setVisible(true);
0195         ui->installButton->setEnabled(false);
0196         break;
0197     case Entry::Updating:
0198         ui->updateButton->setVisible(true);
0199         ui->updateButton->setEnabled(false);
0200         ui->uninstallButton->setVisible(true);
0201         ui->uninstallButton->setEnabled(false);
0202         break;
0203     case Entry::Deleted:
0204         ui->installButton->setVisible(true);
0205         ui->installButton->setEnabled(true);
0206         break;
0207     }
0208 
0209     if (QMenu *buttonMenu = ui->installButton->menu()) {
0210         buttonMenu->clear();
0211         ui->installButton->setMenu(nullptr);
0212         buttonMenu->deleteLater();
0213     }
0214     if (ui->installButton->isVisible() && m_entry.downloadLinkCount() > 1) {
0215         QMenu *installMenu = new QMenu(ui->installButton);
0216         const auto lst = m_entry.downloadLinkInformationList();
0217         for (const KNSCore::EntryInternal::DownloadLinkInformation &info : lst) {
0218             QString text = info.name;
0219             if (!info.distributionType.trimmed().isEmpty()) {
0220                 text + QStringLiteral(" (") + info.distributionType.trimmed() + QLatin1Char(')');
0221             }
0222             QAction *installMenuAction = installMenu->addAction(QIcon::fromTheme(QStringLiteral("dialog-ok")), text);
0223             installMenuAction->setData(info.id);
0224             connect(installMenuAction, &QAction::triggered, this, [this, installMenuAction]() {
0225                 installAction(installMenuAction);
0226             });
0227         }
0228         qCDebug(KNEWSTUFF) << "links: " << m_entry.downloadLinkInformationList().size();
0229         ui->installButton->setMenu(installMenu);
0230     }
0231 }
0232 
0233 void EntryDetails::install()
0234 {
0235     m_engine->install(m_entry);
0236 }
0237 
0238 void EntryDetails::uninstall()
0239 {
0240     m_engine->uninstall(m_entry);
0241 }
0242 
0243 void EntryDetails::slotEntryPreviewLoaded(const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::PreviewType type)
0244 {
0245     if (!(entry == m_entry)) {
0246         return;
0247     }
0248 
0249     switch (type) {
0250     case KNSCore::EntryInternal::PreviewSmall1:
0251         ui->preview1->setImage(entry.previewImage(KNSCore::EntryInternal::PreviewSmall1));
0252         break;
0253     case KNSCore::EntryInternal::PreviewSmall2:
0254         ui->preview2->setImage(entry.previewImage(KNSCore::EntryInternal::PreviewSmall2));
0255         break;
0256     case KNSCore::EntryInternal::PreviewSmall3:
0257         ui->preview3->setImage(entry.previewImage(KNSCore::EntryInternal::PreviewSmall3));
0258         break;
0259     case KNSCore::EntryInternal::PreviewBig1:
0260         m_currentPreview = entry.previewImage(KNSCore::EntryInternal::PreviewBig1);
0261         ui->previewBig->setImage(m_currentPreview);
0262         break;
0263     default:
0264         break;
0265     }
0266 }
0267 
0268 void EntryDetails::preview1Selected()
0269 {
0270     previewSelected(0);
0271 }
0272 
0273 void EntryDetails::preview2Selected()
0274 {
0275     previewSelected(1);
0276 }
0277 
0278 void EntryDetails::preview3Selected()
0279 {
0280     previewSelected(2);
0281 }
0282 
0283 void EntryDetails::previewSelected(int current)
0284 {
0285     KNSCore::EntryInternal::PreviewType type = static_cast<KNSCore::EntryInternal::PreviewType>(KNSCore::EntryInternal::PreviewBig1 + current);
0286     m_currentPreview = m_entry.previewImage(type);
0287     ui->previewBig->setImage(m_currentPreview);
0288 }
0289 
0290 void EntryDetails::ratingChanged(int rating)
0291 {
0292     // engine expects values from 0..100
0293     qCDebug(KNEWSTUFF) << "rating: " << rating << " -> " << rating * 10;
0294     m_engine->vote(m_entry, rating * 10);
0295 }
0296 
0297 void EntryDetails::becomeFan()
0298 {
0299     m_engine->becomeFan(m_entry);
0300 }
0301 
0302 void EntryDetails::installAction(QAction *action)
0303 {
0304     m_engine->install(m_entry, action->data().toInt());
0305 }
0306 
0307 #include "moc_entrydetailsdialog_p.cpp"