diff --git a/Apper/PackageDetails.cpp b/Apper/PackageDetails.cpp index 7acc0b5..6180d97 100644 --- a/Apper/PackageDetails.cpp +++ b/Apper/PackageDetails.cpp @@ -1,802 +1,802 @@ /*************************************************************************** * Copyright (C) 2009-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "PackageDetails.h" #include "ui_PackageDetails.h" #include "ScreenShotViewer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_APPSTREAM #include #endif #include "GraphicsOpacityDropShadowEffect.h" #define BLUR_RADIUS 15 #define FINAL_HEIGHT 210 using namespace PackageKit; Q_DECLARE_LOGGING_CATEGORY(APPER) Q_DECLARE_METATYPE(KPixmapSequenceOverlayPainter**) PackageDetails::PackageDetails(QWidget *parent) : QWidget(parent), ui(new Ui::PackageDetails), m_busySeq(0), m_display(false), m_hideVersion(false), m_hideArch(false), m_transaction(0), m_hasDetails(false), m_hasFileList(false) { ui->setupUi(this); ui->hideTB->setIcon(QIcon::fromTheme(QLatin1String("window-close"))); connect(ui->hideTB, SIGNAL(clicked()), this, SLOT(hide())); auto menu = new QMenu(i18n("Display"), this); m_actionGroup = new QActionGroup(this); // we check to see which roles are supported by the backend // if so we ask for information and create the containers descriptionAction = menu->addAction(i18n("Description")); descriptionAction->setCheckable(true); descriptionAction->setData(PackageKit::Transaction::RoleGetDetails); m_actionGroup->addAction(descriptionAction); ui->descriptionW->setWidgetResizable(true); dependsOnAction = menu->addAction(i18n("Depends On")); dependsOnAction->setCheckable(true); dependsOnAction->setData(PackageKit::Transaction::RoleDependsOn); m_actionGroup->addAction(dependsOnAction); // Sets a transparent background QWidget *dependsViewport = ui->dependsOnLV->viewport(); QPalette dependsPalette = dependsViewport->palette(); dependsPalette.setColor(dependsViewport->backgroundRole(), Qt::transparent); dependsPalette.setColor(dependsViewport->foregroundRole(), dependsPalette.color(QPalette::WindowText)); dependsViewport->setPalette(dependsPalette); m_dependsModel = new PackageModel(this); m_dependsProxy = new QSortFilterProxyModel(this); m_dependsProxy->setDynamicSortFilter(true); m_dependsProxy->setSortRole(PackageModel::SortRole); m_dependsProxy->setSourceModel(m_dependsModel); ui->dependsOnLV->setModel(m_dependsProxy); ui->dependsOnLV->sortByColumn(0, Qt::AscendingOrder); ui->dependsOnLV->header()->setDefaultAlignment(Qt::AlignCenter); ui->dependsOnLV->header()->setSectionResizeMode(PackageModel::NameCol, QHeaderView::ResizeToContents); ui->dependsOnLV->header()->setSectionResizeMode(PackageModel::VersionCol, QHeaderView::ResizeToContents); ui->dependsOnLV->header()->setSectionResizeMode(PackageModel::ArchCol, QHeaderView::Stretch); ui->dependsOnLV->header()->hideSection(PackageModel::ActionCol); ui->dependsOnLV->header()->hideSection(PackageModel::CurrentVersionCol); ui->dependsOnLV->header()->hideSection(PackageModel::OriginCol); ui->dependsOnLV->header()->hideSection(PackageModel::SizeCol); requiredByAction = menu->addAction(i18n("Required By")); requiredByAction->setCheckable(true); requiredByAction->setData(PackageKit::Transaction::RoleRequiredBy); m_actionGroup->addAction(requiredByAction); // Sets a transparent background QWidget *requiredViewport = ui->requiredByLV->viewport(); QPalette requiredPalette = requiredViewport->palette(); requiredPalette.setColor(requiredViewport->backgroundRole(), Qt::transparent); requiredPalette.setColor(requiredViewport->foregroundRole(), requiredPalette.color(QPalette::WindowText)); requiredViewport->setPalette(requiredPalette); m_requiresModel = new PackageModel(this); m_requiresProxy = new QSortFilterProxyModel(this); m_requiresProxy->setDynamicSortFilter(true); m_requiresProxy->setSortRole(PackageModel::SortRole); m_requiresProxy->setSourceModel(m_requiresModel); ui->requiredByLV->setModel(m_requiresProxy); ui->requiredByLV->sortByColumn(0, Qt::AscendingOrder); ui->requiredByLV->header()->setDefaultAlignment(Qt::AlignCenter); ui->requiredByLV->header()->setSectionResizeMode(PackageModel::NameCol, QHeaderView::ResizeToContents); ui->requiredByLV->header()->setSectionResizeMode(PackageModel::VersionCol, QHeaderView::ResizeToContents); ui->requiredByLV->header()->setSectionResizeMode(PackageModel::ArchCol, QHeaderView::Stretch); ui->requiredByLV->header()->hideSection(PackageModel::ActionCol); ui->requiredByLV->header()->hideSection(PackageModel::CurrentVersionCol); ui->requiredByLV->header()->hideSection(PackageModel::OriginCol); ui->requiredByLV->header()->hideSection(PackageModel::SizeCol); fileListAction = menu->addAction(i18n("File List")); fileListAction->setCheckable(true); fileListAction->setData(PackageKit::Transaction::RoleGetFiles); m_actionGroup->addAction(fileListAction); // Sets a transparent background QWidget *actionsViewport = ui->filesPTE->viewport(); QPalette palette = actionsViewport->palette(); palette.setColor(actionsViewport->backgroundRole(), Qt::transparent); palette.setColor(actionsViewport->foregroundRole(), palette.color(QPalette::WindowText)); actionsViewport->setPalette(palette); // Set the menu ui->menuTB->setMenu(menu); ui->menuTB->setIcon(QIcon::fromTheme(QLatin1String("help-about"))); connect(m_actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(actionActivated(QAction*))); m_busySeq = new KPixmapSequenceOverlayPainter(this); m_busySeq->setSequence(KIconLoader::global()->loadPixmapSequence(QLatin1String("process-working"), KIconLoader::SizeSmallMedium)); m_busySeq->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_busySeq->setWidget(ui->stackedWidget); // Setup the opacit effect that makes the descriptio transparent // after finished it checks in display() to see if it shouldn't show // up again. The property animation is always the same, the only different thing // is the Forward or Backward property - QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect(ui->stackedWidget); + auto effect = new QGraphicsOpacityEffect(ui->stackedWidget); effect->setOpacity(0); ui->stackedWidget->setGraphicsEffect(effect); m_fadeStacked = new QPropertyAnimation(effect, "opacity", this); m_fadeStacked->setDuration(500); m_fadeStacked->setStartValue(qreal(0)); m_fadeStacked->setEndValue(qreal(1)); connect(m_fadeStacked, SIGNAL(finished()), this, SLOT(display())); // It's is impossible due to some limitation in Qt to set two effects on the same // Widget m_fadeScreenshot = new QPropertyAnimation(effect, "opacity", this); - GraphicsOpacityDropShadowEffect *shadow = new GraphicsOpacityDropShadowEffect(ui->screenshotL); + auto shadow = new GraphicsOpacityDropShadowEffect(ui->screenshotL); shadow->setOpacity(0); shadow->setBlurRadius(BLUR_RADIUS); shadow->setOffset(2); shadow->setColor(QApplication::palette().dark().color()); ui->screenshotL->setGraphicsEffect(shadow); m_fadeScreenshot = new QPropertyAnimation(shadow, "opacity", this); m_fadeScreenshot->setDuration(500); m_fadeScreenshot->setStartValue(qreal(0)); m_fadeScreenshot->setEndValue(qreal(1)); connect(m_fadeScreenshot, SIGNAL(finished()), this, SLOT(display())); // This pannel expanding - QPropertyAnimation *anim1 = new QPropertyAnimation(this, "maximumSize", this); + auto anim1 = new QPropertyAnimation(this, "maximumSize", this); anim1->setDuration(500); anim1->setEasingCurve(QEasingCurve::OutQuart); anim1->setStartValue(QSize(QWIDGETSIZE_MAX, 0)); anim1->setEndValue(QSize(QWIDGETSIZE_MAX, FINAL_HEIGHT)); - QPropertyAnimation *anim2 = new QPropertyAnimation(this, "minimumSize", this); + auto anim2 = new QPropertyAnimation(this, "minimumSize", this); anim2->setDuration(500); anim2->setEasingCurve(QEasingCurve::OutQuart); anim2->setStartValue(QSize(QWIDGETSIZE_MAX, 0)); anim2->setEndValue(QSize(QWIDGETSIZE_MAX, FINAL_HEIGHT)); m_expandPanel = new QParallelAnimationGroup(this); m_expandPanel->addAnimation(anim1); m_expandPanel->addAnimation(anim2); connect(m_expandPanel, SIGNAL(finished()), this, SLOT(display())); } void PackageDetails::init(PackageKit::Transaction::Roles roles) { // qCDebug(); bool setChecked = true; if (roles & PackageKit::Transaction::RoleGetDetails) { descriptionAction->setEnabled(true); descriptionAction->setChecked(setChecked); setChecked = false; } else { descriptionAction->setEnabled(false); descriptionAction->setChecked(false); } if (roles & PackageKit::Transaction::RoleDependsOn) { dependsOnAction->setEnabled(true); dependsOnAction->setChecked(setChecked); setChecked = false; } else { dependsOnAction->setEnabled(false); dependsOnAction->setChecked(false); } if (roles & PackageKit::Transaction::RoleRequiredBy) { requiredByAction->setEnabled(true); requiredByAction->setChecked(setChecked); setChecked = false; } else { requiredByAction->setEnabled(false); requiredByAction->setChecked(false); } if (roles & PackageKit::Transaction::RoleGetFiles) { fileListAction->setEnabled(true); fileListAction->setChecked(setChecked); setChecked = false; } else { fileListAction->setEnabled(false); fileListAction->setChecked(false); } } PackageDetails::~PackageDetails() { delete ui; } void PackageDetails::setPackage(const QModelIndex &index) { qCDebug(APPER) << index; QString appId = index.data(PackageModel::ApplicationId).toString(); QString packageID = index.data(PackageModel::IdRole).toString(); // if it's the same package and the same application, return if (packageID == m_packageID && appId == m_appId) { return; } else if (maximumSize().height() == 0) { // Expand the panel m_display = true; m_expandPanel->setDirection(QAbstractAnimation::Forward); m_expandPanel->start(); } else { // Hide the old description fadeOut(PackageDetails::FadeScreenshot | PackageDetails::FadeStacked); } m_index = index; m_appId = appId; m_packageID = packageID; m_hasDetails = false; m_hasFileList = false; m_hasRequires = false; m_hasDepends = false; qCDebug(APPER) << "appId" << appId << "m_package" << m_packageID; QString pkgIconPath = index.data(PackageModel::IconRole).toString(); m_currentIcon = PkIcons::getIcon(pkgIconPath, QString()).pixmap(64, 64); m_appName = index.data(PackageModel::NameRole).toString(); m_currentScreenshot = thumbnail(Transaction::packageName(m_packageID)); qCDebug(APPER) << "current screenshot" << m_currentScreenshot; - if (!m_currentScreenshot.isNull()) { + if (!m_currentScreenshot.isEmpty()) { if (m_screenshotPath.contains(m_currentScreenshot)) { display(); } else { auto tempFile = new QTemporaryFile; tempFile->open(); - KIO::FileCopyJob *job = KIO::file_copy(QUrl(m_currentScreenshot), + KIO::FileCopyJob *job = KIO::file_copy(m_currentScreenshot, QUrl(tempFile->fileName()), -1, KIO::Overwrite | KIO::HideProgressInfo); connect(job, &KIO::FileCopyJob::result, this, &PackageDetails::resultJob); } } if (m_actionGroup->checkedAction()) { actionActivated(m_actionGroup->checkedAction()); } } void PackageDetails::on_screenshotL_clicked() { const QUrl url = screenshot(Transaction::packageName(m_packageID)); if (!url.isEmpty()) { auto view = new ScreenShotViewer(url); view->setWindowTitle(m_appName); view->show(); } } void PackageDetails::hidePackageVersion(bool hide) { m_hideVersion = hide; } void PackageDetails::hidePackageArch(bool hide) { m_hideArch = hide; } void PackageDetails::actionActivated(QAction *action) { // don't fade the screenshot // if the package changed setPackage() fades both fadeOut(FadeStacked); qCDebug(APPER); // disconnect the transaction // so that we don't get old data if (m_transaction) { disconnect(m_transaction, SIGNAL(details(PackageKit::Details)), this, SLOT(description(PackageKit::Details))); disconnect(m_transaction, SIGNAL(package(PackageKit::Transaction::Info,QString,QString)), m_dependsModel, SLOT(addPackage(PackageKit::Transaction::Info,QString,QString))); disconnect(m_transaction, SIGNAL(package(PackageKit::Transaction::Info,QString,QString)), m_requiresModel, SLOT(addPackage(PackageKit::Transaction::Info,QString,QString))); disconnect(m_transaction, SIGNAL(files(QString,QStringList)), this, SLOT(files(QString,QStringList))); disconnect(m_transaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), this, SLOT(finished())); m_transaction = 0; } // Check to see if we don't already have the required data uint role = action->data().toUInt(); switch (role) { case PackageKit::Transaction::RoleGetDetails: if (m_hasDetails) { description(m_details); display(); return; } break; case PackageKit::Transaction::RoleDependsOn: if (m_hasDepends) { display(); return; } break; case PackageKit::Transaction::RoleRequiredBy: if (m_hasRequires) { display(); return; } break; case PackageKit::Transaction::RoleGetFiles: if (m_hasFileList) { display(); return; } break; } // we don't have the data qCDebug(APPER) << "New transaction"; switch (role) { case PackageKit::Transaction::RoleGetDetails: m_transaction = Daemon::getDetails(m_packageID); connect(m_transaction, SIGNAL(details(PackageKit::Details)), SLOT(description(PackageKit::Details))); break; case PackageKit::Transaction::RoleDependsOn: m_dependsModel->clear(); m_transaction = Daemon::dependsOn(m_packageID, PackageKit::Transaction::FilterNone, false); connect(m_transaction, SIGNAL(package(PackageKit::Transaction::Info,QString,QString)), m_dependsModel, SLOT(addPackage(PackageKit::Transaction::Info,QString,QString))); connect(m_transaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), m_dependsModel, SLOT(finished())); break; case PackageKit::Transaction::RoleRequiredBy: m_requiresModel->clear(); m_transaction = Daemon::requiredBy(m_packageID, PackageKit::Transaction::FilterNone, false); connect(m_transaction, SIGNAL(package(PackageKit::Transaction::Info,QString,QString)), m_requiresModel, SLOT(addPackage(PackageKit::Transaction::Info,QString,QString))); connect(m_transaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), m_requiresModel, SLOT(finished())); break; case PackageKit::Transaction::RoleGetFiles: m_currentFileList.clear(); m_transaction = Daemon::getFiles(m_packageID); connect(m_transaction, SIGNAL(files(QString,QStringList)), this, SLOT(files(QString,QStringList))); break; default: qWarning() << Q_FUNC_INFO << "Oops, unhandled role, please report" << role; return; } connect(m_transaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), this, SLOT(finished())); qCDebug(APPER) <<"transaction running"; m_busySeq->start(); } void PackageDetails::resultJob(KJob *job) { KIO::FileCopyJob *fJob = qobject_cast(job); if (!fJob->error()) { - m_screenshotPath[fJob->srcUrl().url()] = fJob->destUrl().toLocalFile(); + m_screenshotPath[fJob->srcUrl()] = fJob->destUrl().toLocalFile(); display(); } } void PackageDetails::hide() { m_display = false; // Clean the old description otherwise if the user selects the same // package the pannel won't expand m_packageID.clear(); m_appId.clear(); if (maximumSize().height() == FINAL_HEIGHT) { if (m_fadeStacked->currentValue().toReal() == 0 && m_fadeScreenshot->currentValue().toReal() == 0) { // Screen shot and description faded let's shrink the pannel m_expandPanel->setDirection(QAbstractAnimation::Backward); m_expandPanel->start(); } else { // Hide current description fadeOut(PackageDetails::FadeScreenshot | PackageDetails::FadeStacked); } } } void PackageDetails::fadeOut(FadeWidgets widgets) { // Fade out only if needed if ((widgets & FadeStacked) && m_fadeStacked->currentValue().toReal() != 0) { m_fadeStacked->setDirection(QAbstractAnimation::Backward); m_fadeStacked->start(); } // Fade out the screenshot only if needed if ((widgets & FadeScreenshot) && m_fadeScreenshot->currentValue().toReal() != 0) { ui->screenshotL->unsetCursor(); m_fadeScreenshot->setDirection(QAbstractAnimation::Backward); m_fadeScreenshot->start(); } } void PackageDetails::display() { // If we shouldn't be showing hide the pannel if (!m_display) { hide(); } else if (maximumSize().height() == FINAL_HEIGHT) { emit ensureVisible(m_index); // Check to see if the stacked widget is transparent if (m_fadeStacked->currentValue().toReal() == 0 && m_actionGroup->checkedAction()) { bool fadeIn = false; switch (m_actionGroup->checkedAction()->data().toUInt()) { case PackageKit::Transaction::RoleGetDetails: if (m_hasDetails) { setupDescription(); fadeIn = true; } break; case PackageKit::Transaction::RoleDependsOn: if (m_hasDepends) { if (ui->stackedWidget->currentWidget() != ui->pageDepends) { ui->stackedWidget->setCurrentWidget(ui->pageDepends); } fadeIn = true; } break; case PackageKit::Transaction::RoleRequiredBy: if (m_hasRequires) { if (ui->stackedWidget->currentWidget() != ui->pageRequired) { ui->stackedWidget->setCurrentWidget(ui->pageRequired); } fadeIn = true; } break; case PackageKit::Transaction::RoleGetFiles: if (m_hasFileList) { ui->filesPTE->clear(); if (m_currentFileList.isEmpty()) { ui->filesPTE->insertPlainText(i18n("No files were found.")); } else { m_currentFileList.sort(); ui->filesPTE->insertPlainText(m_currentFileList.join(QLatin1Char('\n'))); } if (ui->stackedWidget->currentWidget() != ui->pageFiles) { ui->stackedWidget->setCurrentWidget(ui->pageFiles); } ui->filesPTE->verticalScrollBar()->setValue(0); fadeIn = true; } break; } if (fadeIn) { // Fade In m_fadeStacked->setDirection(QAbstractAnimation::Forward); m_fadeStacked->start(); } } // Check to see if we have a screen shot and if we are // transparent, and make sure the details are going // to be shown if (m_fadeScreenshot->currentValue().toReal() == 0 && m_screenshotPath.contains(m_currentScreenshot) && m_fadeStacked->direction() == QAbstractAnimation::Forward) { QPixmap pixmap; pixmap = QPixmap(m_screenshotPath[m_currentScreenshot]) .scaled(160,120, Qt::KeepAspectRatio, Qt::SmoothTransformation); ui->screenshotL->setPixmap(pixmap); ui->screenshotL->setCursor(Qt::PointingHandCursor); // Fade In m_fadeScreenshot->setDirection(QAbstractAnimation::Forward); m_fadeScreenshot->start(); } } } void PackageDetails::setupDescription() { if (ui->stackedWidget->currentWidget() != ui->pageDescription) { ui->stackedWidget->setCurrentWidget(ui->pageDescription); } if (!m_hasDetails) { // Oops we don't have any details ui->descriptionL->setText(i18n("Could not fetch software details")); ui->descriptionL->show(); // Hide stuff so we don't display outdated data ui->homepageL->hide(); ui->pathL->hide(); ui->licenseL->hide(); ui->sizeL->hide(); ui->iconL->clear(); } if (!m_detailsDescription.isEmpty()) { ui->descriptionL->setText(m_detailsDescription.replace(QLatin1Char('\n'), QLatin1String("
"))); ui->descriptionL->show(); } else { ui->descriptionL->clear(); } if (!m_details.url().isEmpty()) { ui->homepageL->setText(QLatin1String("") + m_details.url() + QLatin1String("")); ui->homepageL->show(); } else { ui->homepageL->hide(); } // Let's try to find the application's path in human user // readable easiest form :D KService::Ptr service = KService::serviceByDesktopName(m_appId); QVector > ret; if (service) { ret = locateApplication(QString(), service->menuId()); } if (ret.isEmpty()) { ui->pathL->hide(); } else { QString path; path.append(QString(QLatin1String("")) .arg(KIconLoader::global()->iconPath(QLatin1String("kde"), KIconLoader::Small))); path.append(QString(QLatin1String(" %1  %3")) .arg(QString::fromUtf8("➜")) .arg(KIconLoader::global()->iconPath(QLatin1String("applications-other"), KIconLoader::Small)) .arg(i18n("Applications"))); for (int i = 0; i < ret.size(); i++) { path.append(QString(QLatin1String(" %1  %3")) .arg(QString::fromUtf8("➜")) .arg(KIconLoader::global()->iconPath(ret.at(i).second, KIconLoader::Small)) .arg(ret.at(i).first)); } ui->pathL->setText(path); ui->pathL->show(); } // if (details->group() != Package::UnknownGroup) { // // description += "" + i18nc("Group of the package", "Group") + ":" // // + PkStrings::groups(details->group()) // // + ""; // } if (!m_details.license().isEmpty() && m_details.license() != QLatin1String("unknown")) { // We have a license, check if we have and should show show package version if (!m_hideVersion && !Transaction::packageVersion(m_details.packageId()).isEmpty()) { ui->licenseL->setText(Transaction::packageVersion(m_details.packageId()) + QLatin1String(" - ") + m_details.license()); } else { ui->licenseL->setText(m_details.license()); } ui->licenseL->show(); } else if (!m_hideVersion) { ui->licenseL->setText(Transaction::packageVersion(m_details.packageId())); ui->licenseL->show(); } else { ui->licenseL->hide(); } if (m_details.size() > 0) { QString size = KFormat().formatByteSize(m_details.size()); if (!m_hideArch && !Transaction::packageArch(m_details.packageId()).isEmpty()) { ui->sizeL->setText(size % QLatin1String(" (") % Transaction::packageArch(m_details.packageId()) % QLatin1Char(')')); } else { ui->sizeL->setText(size); } ui->sizeL->show(); } else if (!m_hideArch && !Transaction::packageArch(m_details.packageId()).isEmpty()) { ui->sizeL->setText(Transaction::packageArch(m_details.packageId())); } else { ui->sizeL->hide(); } if (m_currentIcon.isNull()) { ui->iconL->clear(); } else { ui->iconL->setPixmap(m_currentIcon); } } QVector > PackageDetails::locateApplication(const QString &_relPath, const QString &menuId) const { QVector > ret; KServiceGroup::Ptr root = KServiceGroup::group(_relPath); if (!root || !root->isValid()) { return ret; } KServiceGroup::List list = root->entries(false /* sorted */, true /* exclude no display entries */, false /* allow separators */); //! TODO: Port to KF5 properly Q_UNUSED(menuId) #if 0 for (KServiceGroup::List::ConstIterator it = list.begin(); it != list.end(); it++) { KSycocaEntry::Ptr = (*it); if (p->isType(KST_KService)) { KService *service = static_cast(p.get()); if (service->noDisplay()) { continue; } // qCDebug(APPER) << menuId << service->menuId(); if (service->menuId() == menuId) { QPair pair; pair.first = service->name(); pair.second = service->icon(); ret << pair; // qCDebug(APPER) << "FOUND!"; return ret; } } else if (p->isType(KST_KServiceGroup)) { KServiceGroup *serviceGroup = static_cast(p.get()); if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0) { continue; } QVector > found; found = locateApplication(serviceGroup->relPath(), menuId); if (!found.isEmpty()) { QPair pair; pair.first = serviceGroup->caption(); pair.second = serviceGroup->icon(); ret << pair; ret << found; return ret; } } else { kWarning(250) << "KServiceGroup: Unexpected object in list!"; continue; } } #endif return ret; } -QString PackageDetails::thumbnail(const QString &pkgName) const +QUrl PackageDetails::thumbnail(const QString &pkgName) const { #ifndef HAVE_APPSTREAM Q_UNUSED(pkgName) - return QString(); + return QUrl(); #else - return QString();//AppStream::instance()->thumbnail(pkgName); + return AppStreamHelper::instance()->thumbnail(pkgName); #endif } QUrl PackageDetails::screenshot(const QString &pkgName) const { #ifndef HAVE_APPSTREAM Q_UNUSED(pkgName) return QUrl(); #else return AppStreamHelper::instance()->screenshot(pkgName); #endif } void PackageDetails::description(const PackageKit::Details &details) { qCDebug(APPER) << details; m_details = details; m_detailsDescription = details.description(); m_hasDetails = true; #ifdef HAVE_APPSTREAM // check if we have application details from Appstream data // FIXME: The whole AppStream handling sucks badly, since it was added later // and on to of the package-based model. So we can't respect the "multiple apps // in one package" case here. const QList apps = AppStreamHelper::instance()->applications(Transaction::packageName(m_packageID)); for (const AppStream::Component &app : apps) { if (!app.description().isEmpty()) { m_detailsDescription = app.description(); break; } } #endif } void PackageDetails::finished() { if (m_busySeq) { m_busySeq->stop(); } m_transaction = 0; auto transaction = qobject_cast(sender()); qCDebug(APPER); if (transaction) { qCDebug(APPER) << transaction->role() << PackageKit::Transaction::RoleGetDetails; if (transaction->role() == PackageKit::Transaction::RoleGetDetails) { m_hasDetails = true; } else if (transaction->role() == PackageKit::Transaction::RoleGetFiles) { m_hasFileList = true; } else if (transaction->role() == PackageKit::Transaction::RoleRequiredBy) { m_hasRequires = true; } else if (transaction->role() == PackageKit::Transaction::RoleDependsOn) { m_hasDepends = true; } else { return; } display(); } } void PackageDetails::files(const QString &packageID, const QStringList &files) { Q_UNUSED(packageID) m_currentFileList = files; } #include "moc_PackageDetails.cpp" diff --git a/Apper/PackageDetails.h b/Apper/PackageDetails.h index a900499..95d8f57 100644 --- a/Apper/PackageDetails.h +++ b/Apper/PackageDetails.h @@ -1,137 +1,138 @@ /*************************************************************************** * Copyright (C) 2009-2010 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef PACKAGE_DETAILS_H #define PACKAGE_DETAILS_H #include #include
#include #include +#include #include #include #include #include #include namespace Ui { class PackageDetails; } class PackageModel; class PackageDetails : public QWidget { Q_OBJECT public: enum FadeWidget { FadeNone = 0x0, FadeStacked = 0x1, FadeScreenshot = 0x2 }; Q_DECLARE_FLAGS(FadeWidgets, FadeWidget) explicit PackageDetails(QWidget *parent = 0); ~PackageDetails(); void init(PackageKit::Transaction::Roles roles); void setPackage(const QModelIndex &index); void hidePackageVersion(bool hide); void hidePackageArch(bool hide); public Q_SLOTS: void hide(); Q_SIGNALS: void ensureVisible(const QModelIndex &index); private Q_SLOTS: void on_screenshotL_clicked(); void actionActivated(QAction *action); void description(const PackageKit::Details &details); void files(const QString &packageID, const QStringList &files); void finished(); void resultJob(KJob *); void display(); private: void fadeOut(FadeWidgets widgets); void setupDescription(); QVector > locateApplication(const QString &_relPath, const QString &menuId) const; - QString thumbnail(const QString &pkgName) const; + QUrl thumbnail(const QString &pkgName) const; QUrl screenshot(const QString &pkgName) const; Ui::PackageDetails *ui; QActionGroup *m_actionGroup; QModelIndex m_index; QString m_packageID; PackageKit::Details m_details; QString m_detailsDescription; QAction *descriptionAction; QAction *dependsOnAction; QAction *requiredByAction; QAction *fileListAction; QString m_appName; QParallelAnimationGroup *m_expandPanel; KPixmapSequenceOverlayPainter *m_busySeq; QPropertyAnimation *m_fadeStacked; QPropertyAnimation *m_fadeScreenshot; bool m_display; bool m_hideVersion; bool m_hideArch; // We need a copy of prety much every thing // we have, so that we update only when we are // totaly transparent this way the user // does not see the ui flicker PackageKit::Transaction *m_transaction; bool m_hasDetails; QString m_currentText; QPixmap m_currentIcon; QString m_appId; // file list buffer bool m_hasFileList; QStringList m_currentFileList; // GetDepends buffer bool m_hasDepends; PackageModel *m_dependsModel; QSortFilterProxyModel *m_dependsProxy; // GetRequires buffer bool m_hasRequires; PackageModel *m_requiresModel; QSortFilterProxyModel *m_requiresProxy; // Screen shot buffer - QString m_currentScreenshot; - QHash m_screenshotPath; + QUrl m_currentScreenshot; + QHash m_screenshotPath; }; Q_DECLARE_OPERATORS_FOR_FLAGS(PackageDetails::FadeWidgets) #endif diff --git a/libapper/AppStream.cpp b/libapper/AppStream.cpp index fb94a28..b75e316 100644 --- a/libapper/AppStream.cpp +++ b/libapper/AppStream.cpp @@ -1,240 +1,252 @@ /*************************************************************************** * Copyright (C) 2010 by Daniel Nicoletti * * Copyright (C) 2012-2013 by Matthias Klumpp * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include #include #include #include #include #include "AppStream.h" #include #include Q_DECLARE_LOGGING_CATEGORY(APPER_LIB) AppStreamHelper* AppStreamHelper::m_instance = 0; AppStreamHelper* AppStreamHelper::instance() { if(!m_instance) { m_instance = new AppStreamHelper(qApp); m_instance->open(); } return m_instance; } AppStreamHelper::AppStreamHelper(QObject *parent) : QObject(parent) { #ifdef HAVE_APPSTREAM // create new AppStream metadata pool m_pool = new AppStream::Pool(this); #endif //HAVE_APPSTREAM } AppStreamHelper::~AppStreamHelper() { } bool AppStreamHelper::open() { #ifdef HAVE_APPSTREAM QString error; if (!m_pool->load(&error)) { qCWarning(APPER_LIB) << "Unable to open AppStream metadata pool:" << error; return false; } // // cache application data (we should actually search the data directly via as_pool_search()...) // auto cptArray = as_pool_get_components(m_pool); // if (cptArray == NULL) { // qWarning("AppStream application array way NULL! (This should never happen)"); // return false; // } // for (uint i = 0; i < cptArray->len; i++) { // auto cpt = AS_COMPONENT (g_ptr_array_index(cptArray, i)); // // we only want apps at time // auto cptKind = as_component_get_kind (cpt); // if ((cptKind != AS_COMPONENT_KIND_DESKTOP_APP) && // (cptKind != AS_COMPONENT_KIND_CONSOLE_APP) && // (cptKind != AS_COMPONENT_KIND_WEB_APP)) // continue; // Application app; // // Application name // app.name = QString::fromUtf8(as_component_get_name(cpt)); // // Package name // auto pkgnameC = as_component_get_pkgname(cpt); // QString pkgName; // if (pkgnameC != NULL) // pkgName = QString::fromUtf8(pkgnameC); // // Desktop file // app.id = QString::fromUtf8(as_component_get_id(cpt)); // // Summary // app.summary = QString::fromUtf8(as_component_get_summary(cpt)); // // Description // app.description = QString::fromUtf8(as_component_get_description(cpt)); // // Application stock icon // auto icons = as_component_get_icons(cpt); // for (uint i = 0; i < icons->len; i++) { // auto icon = AS_ICON (g_ptr_array_index (icons, i)); // if (as_icon_get_kind (icon) != AS_ICON_KIND_STOCK) // app.icon_url = QString::fromUtf8(as_icon_get_filename (icon)); // } // // Application categories // app.categories = QStringList(); // auto cats = as_component_get_categories(cpt); // for (uint i = 0; i < cats->len; i++) { // auto category = (const gchar*) g_ptr_array_index (cats, i); // app.categories << QString::fromUtf8(category); // } // // add default screenshot urls // auto scrs = as_component_get_screenshots (cpt); // // find default screenshot, if possible (otherwise we get a random one) // AsScreenshot *scr = NULL; // for (uint i = 0; i < scrs->len; i++) { // scr = AS_SCREENSHOT (g_ptr_array_index (scrs, i)); // if (as_screenshot_get_kind (scr) == AS_SCREENSHOT_KIND_DEFAULT) // break; // } // if (scr != NULL) { // auto imgs = as_screenshot_get_images (scr); // for (uint i = 0; i < imgs->len; i++) { // auto img = AS_IMAGE (g_ptr_array_index (imgs, i)); // if ((as_image_get_kind (img) == AS_IMAGE_KIND_SOURCE) && (app.screenshot.isEmpty())) { // app.screenshot = QString::fromUtf8(as_image_get_url (img)); // } else if ((as_image_get_kind (img) == AS_IMAGE_KIND_THUMBNAIL) && (app.thumbnail.isEmpty())) { // app.thumbnail = QString::fromUtf8(as_image_get_url (img)); // } // if ((!app.screenshot.isEmpty()) && (!app.thumbnail.isEmpty())) // break; // } // } // m_appInfo.insertMulti(pkgName, app); // } const QList apps = m_pool->componentsByKind(AppStream::Component::KindDesktopApp); for (const AppStream::Component &app : apps) { const QStringList pkgNames = app.packageNames(); for (const QString &pkgName : pkgNames) { m_appInfo.insertMulti(pkgName, app); } } return true; #else return false; #endif } QList AppStreamHelper::applications(const QString &pkgName) const { return m_appInfo.values(pkgName); } QString AppStreamHelper::genericIcon(const QString &pkgName) const { if (m_appInfo.contains(pkgName)) { // const QList apps = applications(pkgName); // for (const AppStream::Component &app : apps) { // if (!app.icon_url.isEmpty()) { // return app.icon_url; // } // } } return QString(); } QStringList AppStreamHelper::findPkgNames(const CategoryMatcher &parser) const { QStringList packages; // QHash::const_iterator i = m_appInfo.constBegin(); // while (i != m_appInfo.constEnd()) { // if (parser.match(i.value().categories)) { //// kDebug() << i.key() << categories; // packages << i.key(); // } // ++i; // } return packages; } -QString AppStreamHelper::thumbnail(const QString &pkgName) const +QUrl AppStreamHelper::thumbnail(const QString &pkgName) const { + QUrl url; #ifdef HAVE_APPSTREAM - QString url = QLatin1String(""); if (m_appInfo.contains(pkgName)) { AppStream::Component app = m_appInfo.value(pkgName); -// ) -// url = app.icon(); + const QList screenshots = app.screenshots(); + for (const AppStream::Screenshot &screenshot : screenshots) { + const QList images = screenshot.images(); + for (const AppStream::Image &image : images) { + if (image.kind() == AppStream::Image::KindThumbnail) { + url = image.url(); + break; + } + } + + if (screenshot.isDefault() && !url.isEmpty()) { + break; + } + } } return url; #else Q_UNUSED(pkgName) - return QString(); + return url; #endif //HAVE_APPSTREAM } QUrl AppStreamHelper::screenshot(const QString &pkgName) const { QUrl url; #ifdef HAVE_APPSTREAM if (m_appInfo.contains(pkgName)) { AppStream::Component app = m_appInfo.value(pkgName); const QList screenshots = app.screenshots(); for (const AppStream::Screenshot &screenshot : screenshots) { const QList images = screenshot.images(); for (const AppStream::Image &image : images) { url = image.url(); break; } if (screenshot.isDefault() && !url.isEmpty()) { break; } } } #else Q_UNUSED(pkgName) #endif //HAVE_APPSTREAM return url; } diff --git a/libapper/AppStream.h b/libapper/AppStream.h index 0f87ca4..7492e88 100644 --- a/libapper/AppStream.h +++ b/libapper/AppStream.h @@ -1,59 +1,59 @@ /*************************************************************************** * Copyright (C) 2010 by Daniel Nicoletti * * Copyright (C) 2012-2013 by Matthias Klumpp * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifndef APPSTREAM_H #define APPSTREAM_H #include "CategoryMatcher.h" #include #include #include namespace AppStream { class Pool; } struct _AsScreenshotService; typedef struct _AsScreenshotService AsScreenshotService; class Q_DECL_EXPORT AppStreamHelper : public QObject { public: static AppStreamHelper* instance(); virtual ~AppStreamHelper(); bool open(); QList applications(const QString &pkgName) const; QString genericIcon(const QString &pkgName) const; QStringList findPkgNames(const CategoryMatcher &parser) const; - QString thumbnail(const QString &pkgName) const; + QUrl thumbnail(const QString &pkgName) const; QUrl screenshot(const QString &pkgName) const; private: explicit AppStreamHelper(QObject *parent = 0); AppStream::Pool *m_pool; QHash m_appInfo; static AppStreamHelper *m_instance; }; #endif // APPSTREAM_H