File indexing completed on 2024-04-21 16:29:32
0001 /*************************************************************************** 0002 * Copyright (C) 2009-2018 by Daniel Nicoletti * 0003 * dantti12@gmail.com * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; see the file COPYING. If not, write to * 0017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0018 * Boston, MA 02110-1301, USA. * 0019 ***************************************************************************/ 0020 0021 #include "PackageDetails.h" 0022 #include "ui_PackageDetails.h" 0023 0024 #include "ScreenShotViewer.h" 0025 0026 #include <PackageModel.h> 0027 #include <PkStrings.h> 0028 #include <PkIcons.h> 0029 0030 #include <KMessageBox> 0031 0032 #include <KSycocaEntry> 0033 #include <KIconLoader> 0034 #include <KService> 0035 #include <KServiceGroup> 0036 #include <KDesktopFile> 0037 #include <QTemporaryFile> 0038 #include <KPixmapSequence> 0039 #include <QTextDocument> 0040 #include <QPlainTextEdit> 0041 #include <QScrollBar> 0042 #include <QPainter> 0043 #include <QAbstractAnimation> 0044 #include <QStringBuilder> 0045 0046 #include <KFormat> 0047 #include <KIO/Job> 0048 #include <QMenu> 0049 #include <QDir> 0050 0051 #include <QLoggingCategory> 0052 0053 #include <Daemon> 0054 #include <Transaction> 0055 0056 #include <config.h> 0057 0058 #ifdef HAVE_APPSTREAM 0059 #include <AppStream.h> 0060 #endif 0061 0062 #include "GraphicsOpacityDropShadowEffect.h" 0063 0064 #define BLUR_RADIUS 15 0065 #define FINAL_HEIGHT 210 0066 0067 using namespace PackageKit; 0068 0069 Q_DECLARE_LOGGING_CATEGORY(APPER) 0070 0071 Q_DECLARE_METATYPE(KPixmapSequenceOverlayPainter**) 0072 0073 PackageDetails::PackageDetails(QWidget *parent) 0074 : QWidget(parent), 0075 ui(new Ui::PackageDetails), 0076 m_busySeq(nullptr), 0077 m_display(false), 0078 m_hideVersion(false), 0079 m_hideArch(false), 0080 m_transaction(nullptr), 0081 m_hasDetails(false), 0082 m_hasFileList(false) 0083 { 0084 ui->setupUi(this); 0085 ui->hideTB->setIcon(QIcon::fromTheme(QLatin1String("window-close"))); 0086 connect(ui->hideTB, SIGNAL(clicked()), this, SLOT(hide())); 0087 0088 auto menu = new QMenu(i18n("Display"), this); 0089 m_actionGroup = new QActionGroup(this); 0090 0091 // we check to see which roles are supported by the backend 0092 // if so we ask for information and create the containers 0093 descriptionAction = menu->addAction(i18n("Description")); 0094 descriptionAction->setCheckable(true); 0095 descriptionAction->setData(PackageKit::Transaction::RoleGetDetails); 0096 m_actionGroup->addAction(descriptionAction); 0097 ui->descriptionW->setWidgetResizable(true); 0098 0099 dependsOnAction = menu->addAction(i18n("Depends On")); 0100 dependsOnAction->setCheckable(true); 0101 dependsOnAction->setData(PackageKit::Transaction::RoleDependsOn); 0102 m_actionGroup->addAction(dependsOnAction); 0103 // Sets a transparent background 0104 QWidget *dependsViewport = ui->dependsOnLV->viewport(); 0105 QPalette dependsPalette = dependsViewport->palette(); 0106 dependsPalette.setColor(dependsViewport->backgroundRole(), Qt::transparent); 0107 dependsPalette.setColor(dependsViewport->foregroundRole(), dependsPalette.color(QPalette::WindowText)); 0108 dependsViewport->setPalette(dependsPalette); 0109 0110 m_dependsModel = new PackageModel(this); 0111 m_dependsProxy = new QSortFilterProxyModel(this); 0112 m_dependsProxy->setDynamicSortFilter(true); 0113 m_dependsProxy->setSortRole(PackageModel::SortRole); 0114 m_dependsProxy->setSourceModel(m_dependsModel); 0115 ui->dependsOnLV->setModel(m_dependsProxy); 0116 ui->dependsOnLV->sortByColumn(0, Qt::AscendingOrder); 0117 ui->dependsOnLV->header()->setDefaultAlignment(Qt::AlignCenter); 0118 ui->dependsOnLV->header()->setSectionResizeMode(PackageModel::NameCol, QHeaderView::ResizeToContents); 0119 ui->dependsOnLV->header()->setSectionResizeMode(PackageModel::VersionCol, QHeaderView::ResizeToContents); 0120 ui->dependsOnLV->header()->setSectionResizeMode(PackageModel::ArchCol, QHeaderView::Stretch); 0121 ui->dependsOnLV->header()->hideSection(PackageModel::ActionCol); 0122 ui->dependsOnLV->header()->hideSection(PackageModel::CurrentVersionCol); 0123 ui->dependsOnLV->header()->hideSection(PackageModel::OriginCol); 0124 ui->dependsOnLV->header()->hideSection(PackageModel::SizeCol); 0125 0126 requiredByAction = menu->addAction(i18n("Required By")); 0127 requiredByAction->setCheckable(true); 0128 requiredByAction->setData(PackageKit::Transaction::RoleRequiredBy); 0129 m_actionGroup->addAction(requiredByAction); 0130 // Sets a transparent background 0131 QWidget *requiredViewport = ui->requiredByLV->viewport(); 0132 QPalette requiredPalette = requiredViewport->palette(); 0133 requiredPalette.setColor(requiredViewport->backgroundRole(), Qt::transparent); 0134 requiredPalette.setColor(requiredViewport->foregroundRole(), requiredPalette.color(QPalette::WindowText)); 0135 requiredViewport->setPalette(requiredPalette); 0136 0137 m_requiresModel = new PackageModel(this); 0138 m_requiresProxy = new QSortFilterProxyModel(this); 0139 m_requiresProxy->setDynamicSortFilter(true); 0140 m_requiresProxy->setSortRole(PackageModel::SortRole); 0141 m_requiresProxy->setSourceModel(m_requiresModel); 0142 ui->requiredByLV->setModel(m_requiresProxy); 0143 ui->requiredByLV->sortByColumn(0, Qt::AscendingOrder); 0144 ui->requiredByLV->header()->setDefaultAlignment(Qt::AlignCenter); 0145 ui->requiredByLV->header()->setSectionResizeMode(PackageModel::NameCol, QHeaderView::ResizeToContents); 0146 ui->requiredByLV->header()->setSectionResizeMode(PackageModel::VersionCol, QHeaderView::ResizeToContents); 0147 ui->requiredByLV->header()->setSectionResizeMode(PackageModel::ArchCol, QHeaderView::Stretch); 0148 ui->requiredByLV->header()->hideSection(PackageModel::ActionCol); 0149 ui->requiredByLV->header()->hideSection(PackageModel::CurrentVersionCol); 0150 ui->requiredByLV->header()->hideSection(PackageModel::OriginCol); 0151 ui->requiredByLV->header()->hideSection(PackageModel::SizeCol); 0152 0153 fileListAction = menu->addAction(i18n("File List")); 0154 fileListAction->setCheckable(true); 0155 fileListAction->setData(PackageKit::Transaction::RoleGetFiles); 0156 m_actionGroup->addAction(fileListAction); 0157 // Sets a transparent background 0158 QWidget *actionsViewport = ui->filesPTE->viewport(); 0159 QPalette palette = actionsViewport->palette(); 0160 palette.setColor(actionsViewport->backgroundRole(), Qt::transparent); 0161 palette.setColor(actionsViewport->foregroundRole(), palette.color(QPalette::WindowText)); 0162 actionsViewport->setPalette(palette); 0163 0164 // Set the menu 0165 ui->menuTB->setMenu(menu); 0166 ui->menuTB->setIcon(QIcon::fromTheme(QLatin1String("help-about"))); 0167 connect(m_actionGroup, SIGNAL(triggered(QAction*)), 0168 this, SLOT(actionActivated(QAction*))); 0169 0170 m_busySeq = new KPixmapSequenceOverlayPainter(this); 0171 m_busySeq->setSequence(KIconLoader::global()->loadPixmapSequence(QLatin1String("process-working"), KIconLoader::SizeSmallMedium)); 0172 m_busySeq->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); 0173 m_busySeq->setWidget(ui->stackedWidget); 0174 0175 // Setup the opacit effect that makes the descriptio transparent 0176 // after finished it checks in display() to see if it shouldn't show 0177 // up again. The property animation is always the same, the only different thing 0178 // is the Forward or Backward property 0179 auto effect = new QGraphicsOpacityEffect(ui->stackedWidget); 0180 effect->setOpacity(0); 0181 ui->stackedWidget->setGraphicsEffect(effect); 0182 m_fadeStacked = new QPropertyAnimation(effect, "opacity", this); 0183 m_fadeStacked->setDuration(500); 0184 m_fadeStacked->setStartValue(qreal(0)); 0185 m_fadeStacked->setEndValue(qreal(1)); 0186 connect(m_fadeStacked, SIGNAL(finished()), this, SLOT(display())); 0187 0188 // It's is impossible due to some limitation in Qt to set two effects on the same 0189 // Widget 0190 m_fadeScreenshot = new QPropertyAnimation(effect, "opacity", this); 0191 auto shadow = new GraphicsOpacityDropShadowEffect(ui->screenshotL); 0192 shadow->setOpacity(0); 0193 shadow->setBlurRadius(BLUR_RADIUS); 0194 shadow->setOffset(2); 0195 shadow->setColor(QApplication::palette().dark().color()); 0196 ui->screenshotL->setGraphicsEffect(shadow); 0197 0198 m_fadeScreenshot = new QPropertyAnimation(shadow, "opacity", this); 0199 m_fadeScreenshot->setDuration(500); 0200 m_fadeScreenshot->setStartValue(qreal(0)); 0201 m_fadeScreenshot->setEndValue(qreal(1)); 0202 connect(m_fadeScreenshot, SIGNAL(finished()), this, SLOT(display())); 0203 0204 // This pannel expanding 0205 auto anim1 = new QPropertyAnimation(this, "maximumSize", this); 0206 anim1->setDuration(500); 0207 anim1->setEasingCurve(QEasingCurve::OutQuart); 0208 anim1->setStartValue(QSize(QWIDGETSIZE_MAX, 0)); 0209 anim1->setEndValue(QSize(QWIDGETSIZE_MAX, FINAL_HEIGHT)); 0210 auto anim2 = new QPropertyAnimation(this, "minimumSize", this); 0211 anim2->setDuration(500); 0212 anim2->setEasingCurve(QEasingCurve::OutQuart); 0213 anim2->setStartValue(QSize(QWIDGETSIZE_MAX, 0)); 0214 anim2->setEndValue(QSize(QWIDGETSIZE_MAX, FINAL_HEIGHT)); 0215 0216 m_expandPanel = new QParallelAnimationGroup(this); 0217 m_expandPanel->addAnimation(anim1); 0218 m_expandPanel->addAnimation(anim2); 0219 connect(m_expandPanel, SIGNAL(finished()), this, SLOT(display())); 0220 } 0221 0222 void PackageDetails::init(PackageKit::Transaction::Roles roles) 0223 { 0224 // qCDebug(); 0225 0226 bool setChecked = true; 0227 if (roles & PackageKit::Transaction::RoleGetDetails) { 0228 descriptionAction->setEnabled(true); 0229 descriptionAction->setChecked(setChecked); 0230 setChecked = false; 0231 } else { 0232 descriptionAction->setEnabled(false); 0233 descriptionAction->setChecked(false); 0234 } 0235 0236 if (roles & PackageKit::Transaction::RoleDependsOn) { 0237 dependsOnAction->setEnabled(true); 0238 dependsOnAction->setChecked(setChecked); 0239 setChecked = false; 0240 } else { 0241 dependsOnAction->setEnabled(false); 0242 dependsOnAction->setChecked(false); 0243 } 0244 0245 if (roles & PackageKit::Transaction::RoleRequiredBy) { 0246 requiredByAction->setEnabled(true); 0247 requiredByAction->setChecked(setChecked); 0248 setChecked = false; 0249 } else { 0250 requiredByAction->setEnabled(false); 0251 requiredByAction->setChecked(false); 0252 } 0253 0254 if (roles & PackageKit::Transaction::RoleGetFiles) { 0255 fileListAction->setEnabled(true); 0256 fileListAction->setChecked(setChecked); 0257 setChecked = false; 0258 } else { 0259 fileListAction->setEnabled(false); 0260 fileListAction->setChecked(false); 0261 } 0262 0263 } 0264 0265 PackageDetails::~PackageDetails() 0266 { 0267 delete ui; 0268 } 0269 0270 void PackageDetails::setPackage(const QModelIndex &index) 0271 { 0272 qCDebug(APPER) << index; 0273 QString appId = index.data(PackageModel::ApplicationId).toString(); 0274 QString packageID = index.data(PackageModel::IdRole).toString(); 0275 0276 // if it's the same package and the same application, return 0277 if (packageID == m_packageID && appId == m_appId) { 0278 return; 0279 } else if (maximumSize().height() == 0) { 0280 // Expand the panel 0281 m_display = true; 0282 m_expandPanel->setDirection(QAbstractAnimation::Forward); 0283 m_expandPanel->start(); 0284 } else { 0285 // Hide the old description 0286 fadeOut(PackageDetails::FadeScreenshot | PackageDetails::FadeStacked); 0287 } 0288 0289 m_index = index; 0290 m_appId = appId; 0291 m_packageID = packageID; 0292 m_hasDetails = false; 0293 m_hasFileList = false; 0294 m_hasRequires = false; 0295 m_hasDepends = false; 0296 qCDebug(APPER) << "appId" << appId << "m_package" << m_packageID; 0297 0298 QString pkgIconPath = index.data(PackageModel::IconRole).toString(); 0299 m_currentIcon = PkIcons::getIcon(pkgIconPath, QString()).pixmap(64, 64); 0300 m_appName = index.data(PackageModel::NameRole).toString(); 0301 0302 m_currentScreenshot = thumbnail(Transaction::packageName(m_packageID)); 0303 qCDebug(APPER) << "current thumbnail" << m_currentScreenshot; 0304 if (!m_currentScreenshot.isEmpty()) { 0305 if (m_screenshotPath.contains(m_currentScreenshot)) { 0306 display(); 0307 } else { 0308 auto tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/apper.XXXXXX.png")); 0309 tempFile->open(); 0310 KIO::FileCopyJob *job = KIO::file_copy(m_currentScreenshot, 0311 QUrl::fromLocalFile(tempFile->fileName()), 0312 -1, 0313 KIO::Overwrite | KIO::HideProgressInfo); 0314 connect(job, &KIO::FileCopyJob::result, this, &PackageDetails::resultJob); 0315 } 0316 } 0317 0318 if (m_actionGroup->checkedAction()) { 0319 actionActivated(m_actionGroup->checkedAction()); 0320 } 0321 } 0322 0323 void PackageDetails::on_screenshotL_clicked() 0324 { 0325 const QUrl url = screenshot(Transaction::packageName(m_packageID)); 0326 if (!url.isEmpty()) { 0327 auto view = new ScreenShotViewer(url); 0328 view->setWindowTitle(m_appName); 0329 view->show(); 0330 } 0331 } 0332 0333 void PackageDetails::hidePackageVersion(bool hide) 0334 { 0335 m_hideVersion = hide; 0336 } 0337 0338 void PackageDetails::hidePackageArch(bool hide) 0339 { 0340 m_hideArch = hide; 0341 } 0342 0343 void PackageDetails::actionActivated(QAction *action) 0344 { 0345 // don't fade the screenshot 0346 // if the package changed setPackage() fades both 0347 fadeOut(FadeStacked); 0348 qCDebug(APPER); 0349 0350 // disconnect the transaction 0351 // so that we don't get old data 0352 if (m_transaction) { 0353 disconnect(m_transaction, SIGNAL(details(PackageKit::Details)), 0354 this, SLOT(description(PackageKit::Details))); 0355 disconnect(m_transaction, SIGNAL(package(PackageKit::Transaction::Info,QString,QString)), 0356 m_dependsModel, SLOT(addPackage(PackageKit::Transaction::Info,QString,QString))); 0357 disconnect(m_transaction, SIGNAL(package(PackageKit::Transaction::Info,QString,QString)), 0358 m_requiresModel, SLOT(addPackage(PackageKit::Transaction::Info,QString,QString))); 0359 disconnect(m_transaction, SIGNAL(files(QString,QStringList)), 0360 this, SLOT(files(QString,QStringList))); 0361 disconnect(m_transaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), 0362 this, SLOT(finished())); 0363 m_transaction = nullptr; 0364 } 0365 0366 // Check to see if we don't already have the required data 0367 uint role = action->data().toUInt(); 0368 switch (role) { 0369 case PackageKit::Transaction::RoleGetDetails: 0370 if (m_hasDetails) { 0371 description(m_details); 0372 display(); 0373 return; 0374 } 0375 break; 0376 case PackageKit::Transaction::RoleDependsOn: 0377 if (m_hasDepends) { 0378 display(); 0379 return; 0380 } 0381 break; 0382 case PackageKit::Transaction::RoleRequiredBy: 0383 if (m_hasRequires) { 0384 display(); 0385 return; 0386 } 0387 break; 0388 case PackageKit::Transaction::RoleGetFiles: 0389 if (m_hasFileList) { 0390 display(); 0391 return; 0392 } 0393 break; 0394 } 0395 0396 // we don't have the data 0397 qCDebug(APPER) << "New transaction"; 0398 switch (role) { 0399 case PackageKit::Transaction::RoleGetDetails: 0400 m_transaction = Daemon::getDetails(m_packageID); 0401 connect(m_transaction, SIGNAL(details(PackageKit::Details)), 0402 SLOT(description(PackageKit::Details))); 0403 break; 0404 case PackageKit::Transaction::RoleDependsOn: 0405 m_dependsModel->clear(); 0406 m_transaction = Daemon::dependsOn(m_packageID, PackageKit::Transaction::FilterNone, false); 0407 connect(m_transaction, SIGNAL(package(PackageKit::Transaction::Info,QString,QString)), 0408 m_dependsModel, SLOT(addPackage(PackageKit::Transaction::Info,QString,QString))); 0409 connect(m_transaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), 0410 m_dependsModel, SLOT(finished())); 0411 break; 0412 case PackageKit::Transaction::RoleRequiredBy: 0413 m_requiresModel->clear(); 0414 m_transaction = Daemon::requiredBy(m_packageID, PackageKit::Transaction::FilterNone, false); 0415 connect(m_transaction, SIGNAL(package(PackageKit::Transaction::Info,QString,QString)), 0416 m_requiresModel, SLOT(addPackage(PackageKit::Transaction::Info,QString,QString))); 0417 connect(m_transaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), 0418 m_requiresModel, SLOT(finished())); 0419 break; 0420 case PackageKit::Transaction::RoleGetFiles: 0421 m_currentFileList.clear(); 0422 m_transaction = Daemon::getFiles(m_packageID); 0423 connect(m_transaction, SIGNAL(files(QString,QStringList)), 0424 this, SLOT(files(QString,QStringList))); 0425 break; 0426 default: 0427 qWarning() << Q_FUNC_INFO << "Oops, unhandled role, please report" << role; 0428 return; 0429 } 0430 connect(m_transaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), 0431 this, SLOT(finished())); 0432 qCDebug(APPER) <<"transaction running"; 0433 0434 m_busySeq->start(); 0435 } 0436 0437 void PackageDetails::resultJob(KJob *job) 0438 { 0439 KIO::FileCopyJob *fJob = qobject_cast<KIO::FileCopyJob*>(job); 0440 if (!fJob->error()) { 0441 m_screenshotPath[fJob->srcUrl()] = fJob->destUrl().toLocalFile(); 0442 display(); 0443 } 0444 } 0445 0446 void PackageDetails::hide() 0447 { 0448 m_display = false; 0449 // Clean the old description otherwise if the user selects the same 0450 // package the pannel won't expand 0451 m_packageID.clear(); 0452 m_appId.clear(); 0453 0454 if (maximumSize().height() == FINAL_HEIGHT) { 0455 if (m_fadeStacked->currentValue().toReal() == 0 && 0456 m_fadeScreenshot->currentValue().toReal() == 0) { 0457 // Screen shot and description faded let's shrink the pannel 0458 m_expandPanel->setDirection(QAbstractAnimation::Backward); 0459 m_expandPanel->start(); 0460 } else { 0461 // Hide current description 0462 fadeOut(PackageDetails::FadeScreenshot | PackageDetails::FadeStacked); 0463 } 0464 } 0465 } 0466 0467 void PackageDetails::fadeOut(FadeWidgets widgets) 0468 { 0469 // Fade out only if needed 0470 if ((widgets & FadeStacked) && m_fadeStacked->currentValue().toReal() != 0) { 0471 m_fadeStacked->setDirection(QAbstractAnimation::Backward); 0472 m_fadeStacked->start(); 0473 } 0474 0475 // Fade out the screenshot only if needed 0476 if ((widgets & FadeScreenshot) && m_fadeScreenshot->currentValue().toReal() != 0) { 0477 ui->screenshotL->unsetCursor(); 0478 m_fadeScreenshot->setDirection(QAbstractAnimation::Backward); 0479 m_fadeScreenshot->start(); 0480 } 0481 } 0482 0483 void PackageDetails::display() 0484 { 0485 // If we shouldn't be showing hide the pannel 0486 if (!m_display) { 0487 hide(); 0488 } else if (maximumSize().height() == FINAL_HEIGHT) { 0489 emit ensureVisible(m_index); 0490 0491 // Check to see if the stacked widget is transparent 0492 if (m_fadeStacked->currentValue().toReal() == 0 && 0493 m_actionGroup->checkedAction()) 0494 { 0495 bool fadeIn = false; 0496 switch (m_actionGroup->checkedAction()->data().toUInt()) { 0497 case PackageKit::Transaction::RoleGetDetails: 0498 if (m_hasDetails) { 0499 setupDescription(); 0500 fadeIn = true; 0501 } 0502 break; 0503 case PackageKit::Transaction::RoleDependsOn: 0504 if (m_hasDepends) { 0505 if (ui->stackedWidget->currentWidget() != ui->pageDepends) { 0506 ui->stackedWidget->setCurrentWidget(ui->pageDepends); 0507 } 0508 fadeIn = true; 0509 } 0510 break; 0511 case PackageKit::Transaction::RoleRequiredBy: 0512 if (m_hasRequires) { 0513 if (ui->stackedWidget->currentWidget() != ui->pageRequired) { 0514 ui->stackedWidget->setCurrentWidget(ui->pageRequired); 0515 } 0516 fadeIn = true; 0517 } 0518 break; 0519 case PackageKit::Transaction::RoleGetFiles: 0520 if (m_hasFileList) { 0521 ui->filesPTE->clear(); 0522 if (m_currentFileList.isEmpty()) { 0523 ui->filesPTE->insertPlainText(i18n("No files were found.")); 0524 } else { 0525 m_currentFileList.sort(); 0526 ui->filesPTE->insertPlainText(m_currentFileList.join(QLatin1Char('\n'))); 0527 } 0528 0529 if (ui->stackedWidget->currentWidget() != ui->pageFiles) { 0530 ui->stackedWidget->setCurrentWidget(ui->pageFiles); 0531 } 0532 ui->filesPTE->verticalScrollBar()->setValue(0); 0533 fadeIn = true; 0534 } 0535 break; 0536 } 0537 0538 if (fadeIn) { 0539 // Fade In 0540 m_fadeStacked->setDirection(QAbstractAnimation::Forward); 0541 m_fadeStacked->start(); 0542 } 0543 } 0544 0545 // Check to see if we have a screen shot and if we are 0546 // transparent, and make sure the details are going 0547 // to be shown 0548 if (m_fadeScreenshot->currentValue().toReal() == 0 && 0549 m_screenshotPath.contains(m_currentScreenshot) && 0550 m_fadeStacked->direction() == QAbstractAnimation::Forward) { 0551 QPixmap pixmap; 0552 pixmap = QPixmap(m_screenshotPath[m_currentScreenshot]) 0553 .scaled(160,120, Qt::KeepAspectRatio, Qt::SmoothTransformation); 0554 ui->screenshotL->setPixmap(pixmap); 0555 ui->screenshotL->setCursor(Qt::PointingHandCursor); 0556 // Fade In 0557 m_fadeScreenshot->setDirection(QAbstractAnimation::Forward); 0558 m_fadeScreenshot->start(); 0559 } 0560 } 0561 } 0562 0563 void PackageDetails::setupDescription() 0564 { 0565 if (ui->stackedWidget->currentWidget() != ui->pageDescription) { 0566 ui->stackedWidget->setCurrentWidget(ui->pageDescription); 0567 } 0568 0569 if (!m_hasDetails) { 0570 // Oops we don't have any details 0571 ui->descriptionL->setText(i18n("Could not fetch software details")); 0572 ui->descriptionL->show(); 0573 0574 // Hide stuff so we don't display outdated data 0575 ui->homepageL->hide(); 0576 ui->pathL->hide(); 0577 ui->licenseL->hide(); 0578 ui->sizeL->hide(); 0579 ui->iconL->clear(); 0580 } 0581 0582 if (!m_detailsDescription.isEmpty()) { 0583 ui->descriptionL->setText(m_detailsDescription.replace(QLatin1Char('\n'), QLatin1String("<br>"))); 0584 ui->descriptionL->show(); 0585 } else { 0586 ui->descriptionL->clear(); 0587 } 0588 0589 if (!m_details.url().isEmpty()) { 0590 ui->homepageL->setText(QLatin1String("<a href=\"") + m_details.url() + QLatin1String("\">") + 0591 m_details.url() + QLatin1String("</a>")); 0592 ui->homepageL->show(); 0593 } else { 0594 ui->homepageL->hide(); 0595 } 0596 0597 // Let's try to find the application's path in human user 0598 // readable easiest form :D 0599 KService::Ptr service = KService::serviceByDesktopName(m_appId); 0600 QVector<QPair<QString, QString> > ret; 0601 if (service) { 0602 ret = locateApplication(QString(), service->menuId()); 0603 } 0604 if (ret.isEmpty()) { 0605 ui->pathL->hide(); 0606 } else { 0607 QString path; 0608 path.append(QString(QLatin1String("<img width=\"16\" heigh=\"16\"src=\"%1\"/>")) 0609 .arg(KIconLoader::global()->iconPath(QLatin1String("kde"), KIconLoader::Small))); 0610 path.append(QString(QLatin1String(" %1 <img width=\"16\" heigh=\"16\" src=\"%2\"/> %3")) 0611 .arg(QString::fromUtf8("➜")) 0612 .arg(KIconLoader::global()->iconPath(QLatin1String("applications-other"), KIconLoader::Small)) 0613 .arg(i18n("Applications"))); 0614 for (int i = 0; i < ret.size(); i++) { 0615 path.append(QString(QLatin1String(" %1 <img width=\"16\" heigh=\"16\" src=\"%2\"/> %3")) 0616 .arg(QString::fromUtf8("➜")) 0617 .arg(KIconLoader::global()->iconPath(ret.at(i).second, KIconLoader::Small)) 0618 .arg(ret.at(i).first)); 0619 } 0620 ui->pathL->setText(path); 0621 ui->pathL->show(); 0622 } 0623 0624 // if (details->group() != Package::UnknownGroup) { 0625 // // description += "<tr><td align=\"right\"><b>" + i18nc("Group of the package", "Group") + ":</b></td><td>" 0626 // // + PkStrings::groups(details->group()) 0627 // // + "</td></tr>"; 0628 // } 0629 0630 if (!m_details.license().isEmpty() && m_details.license() != QLatin1String("unknown")) { 0631 // We have a license, check if we have and should show show package version 0632 if (!m_hideVersion && !Transaction::packageVersion(m_details.packageId()).isEmpty()) { 0633 ui->licenseL->setText(Transaction::packageVersion(m_details.packageId()) + QLatin1String(" - ") + m_details.license()); 0634 } else { 0635 ui->licenseL->setText(m_details.license()); 0636 } 0637 ui->licenseL->show(); 0638 } else if (!m_hideVersion) { 0639 ui->licenseL->setText(Transaction::packageVersion(m_details.packageId())); 0640 ui->licenseL->show(); 0641 } else { 0642 ui->licenseL->hide(); 0643 } 0644 0645 if (m_details.size() > 0) { 0646 QString size = KFormat().formatByteSize(m_details.size()); 0647 if (!m_hideArch && !Transaction::packageArch(m_details.packageId()).isEmpty()) { 0648 ui->sizeL->setText(size % QLatin1String(" (") % Transaction::packageArch(m_details.packageId()) % QLatin1Char(')')); 0649 } else { 0650 ui->sizeL->setText(size); 0651 } 0652 ui->sizeL->show(); 0653 } else if (!m_hideArch && !Transaction::packageArch(m_details.packageId()).isEmpty()) { 0654 ui->sizeL->setText(Transaction::packageArch(m_details.packageId())); 0655 } else { 0656 ui->sizeL->hide(); 0657 } 0658 0659 if (m_currentIcon.isNull()) { 0660 ui->iconL->clear(); 0661 } else { 0662 ui->iconL->setPixmap(m_currentIcon); 0663 } 0664 } 0665 0666 QVector<QPair<QString, QString> > PackageDetails::locateApplication(const QString &_relPath, const QString &menuId) const 0667 { 0668 QVector<QPair<QString, QString> > ret; 0669 KServiceGroup::Ptr root = KServiceGroup::group(_relPath); 0670 0671 if (!root || !root->isValid()) { 0672 return ret; 0673 } 0674 0675 KServiceGroup::List list = root->entries(false /* sorted */, 0676 true /* exclude no display entries */, 0677 false /* allow separators */); 0678 0679 //! TODO: Port to KF5 properly 0680 Q_UNUSED(menuId) 0681 #if 0 0682 for (KServiceGroup::List::ConstIterator it = list.begin(); it != list.end(); it++) { 0683 KSycocaEntry::Ptr = (*it); 0684 0685 if (p->isType(KST_KService)) { 0686 KService *service = static_cast<KService *>(p.get()); 0687 0688 if (service->noDisplay()) { 0689 continue; 0690 } 0691 0692 // qCDebug(APPER) << menuId << service->menuId(); 0693 if (service->menuId() == menuId) { 0694 QPair<QString, QString> pair; 0695 pair.first = service->name(); 0696 pair.second = service->icon(); 0697 ret << pair; 0698 // qCDebug(APPER) << "FOUND!"; 0699 return ret; 0700 } 0701 } else if (p->isType(KST_KServiceGroup)) { 0702 KServiceGroup *serviceGroup = static_cast<KServiceGroup *>(p.get()); 0703 0704 if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0) { 0705 continue; 0706 } 0707 0708 QVector<QPair<QString, QString> > found; 0709 found = locateApplication(serviceGroup->relPath(), menuId); 0710 if (!found.isEmpty()) { 0711 QPair<QString, QString> pair; 0712 pair.first = serviceGroup->caption(); 0713 pair.second = serviceGroup->icon(); 0714 ret << pair; 0715 ret << found; 0716 return ret; 0717 } 0718 } else { 0719 kWarning(250) << "KServiceGroup: Unexpected object in list!"; 0720 continue; 0721 } 0722 } 0723 #endif 0724 0725 return ret; 0726 } 0727 0728 QUrl PackageDetails::thumbnail(const QString &pkgName) const 0729 { 0730 #ifndef HAVE_APPSTREAM 0731 Q_UNUSED(pkgName) 0732 return QUrl(); 0733 #else 0734 return AppStreamHelper::instance()->thumbnail(pkgName); 0735 #endif 0736 } 0737 0738 QUrl PackageDetails::screenshot(const QString &pkgName) const 0739 { 0740 #ifndef HAVE_APPSTREAM 0741 Q_UNUSED(pkgName) 0742 return QUrl(); 0743 #else 0744 return AppStreamHelper::instance()->screenshot(pkgName); 0745 #endif 0746 } 0747 0748 void PackageDetails::description(const PackageKit::Details &details) 0749 { 0750 qCDebug(APPER) << details; 0751 m_details = details; 0752 m_detailsDescription = details.description(); 0753 m_hasDetails = true; 0754 0755 #ifdef HAVE_APPSTREAM 0756 // check if we have application details from Appstream data 0757 // FIXME: The whole AppStream handling sucks badly, since it was added later 0758 // and on to of the package-based model. So we can't respect the "multiple apps 0759 // in one package" case here. 0760 const QList<AppStream::Component> apps = AppStreamHelper::instance()->applications(Transaction::packageName(m_packageID)); 0761 for (const AppStream::Component &app : apps) { 0762 if (!app.description().isEmpty()) { 0763 m_detailsDescription = app.description(); 0764 break; 0765 } 0766 } 0767 #endif 0768 } 0769 0770 void PackageDetails::finished() 0771 { 0772 if (m_busySeq) { 0773 m_busySeq->stop(); 0774 } 0775 m_transaction = nullptr; 0776 0777 auto transaction = qobject_cast<PackageKit::Transaction*>(sender()); 0778 qCDebug(APPER); 0779 if (transaction) { 0780 qCDebug(APPER) << transaction->role() << PackageKit::Transaction::RoleGetDetails; 0781 if (transaction->role() == PackageKit::Transaction::RoleGetDetails) { 0782 m_hasDetails = true; 0783 } else if (transaction->role() == PackageKit::Transaction::RoleGetFiles) { 0784 m_hasFileList = true; 0785 } else if (transaction->role() == PackageKit::Transaction::RoleRequiredBy) { 0786 m_hasRequires = true; 0787 } else if (transaction->role() == PackageKit::Transaction::RoleDependsOn) { 0788 m_hasDepends = true; 0789 } else { 0790 return; 0791 } 0792 0793 display(); 0794 } 0795 } 0796 0797 void PackageDetails::files(const QString &packageID, const QStringList &files) 0798 { 0799 Q_UNUSED(packageID) 0800 m_currentFileList = files; 0801 } 0802 0803 #include "moc_PackageDetails.cpp"