1
/***************************************************************************
2
* Copyright (C) 2009-2010 by Daniel Nicoletti *
3
* dantti85-pk@yahoo.com.br *
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU General Public License as published by *
7
* the Free Software Foundation; either version 2 of the License, or *
8
* (at your option) any later version. *
10
* This program is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
* GNU General Public License for more details. *
15
* You should have received a copy of the GNU General Public License *
16
* along with this program; see the file COPYING. If not, write to *
17
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
18
* Boston, MA 02110-1301, USA. *
19
***************************************************************************/
21
#include "KpkPackageDetails.h"
23
#include "ScreenShotViewer.h"
25
#include <KpkPackageModel.h>
26
#include <KpkSimplePackageModel.h>
27
#include <KpkStrings.h>
29
#include <AppInstall.h>
31
#include <KMessageBox>
34
#include <KServiceGroup>
35
#include <KDesktopFile>
36
#include <KTemporaryFile>
37
#include <KPixmapSequence>
38
#include <QTextDocument>
39
#include <QPlainTextEdit>
47
#include "GraphicsOpacityDropShadowEffect.h"
49
#define BLUR_RADIUS 15
50
#define FINAL_HEIGHT 210
52
Q_DECLARE_METATYPE(KPixmapSequenceOverlayPainter**)
54
KpkPackageDetails::KpkPackageDetails(QWidget *parent)
67
Enum::Roles roles = Client::instance()->actions();
68
// Create a stacked layout to put the views in
69
m_viewLayout = new QStackedLayout(stackedWidget);
71
KMenu *menu = new KMenu(i18n("Display"), this);
72
m_actionGroup = new QActionGroup(this);
75
// we check to see which roles are supported by the backend
76
// if so we ask for information and create the containers
77
if (roles & Enum::RoleGetDetails) {
78
action = menu->addAction(i18n("Description"));
79
action->setCheckable(true);
80
action->setData(Enum::RoleGetDetails);
81
m_actionGroup->addAction(action);
82
m_viewLayout->addWidget(descriptionW);
83
descriptionW->setWidgetResizable(true);
86
if (roles & Enum::RoleGetDepends) {
87
action = menu->addAction(i18n("Depends On"));
88
action->setCheckable(true);
89
action->setData(Enum::RoleGetDepends);
90
m_actionGroup->addAction(action);
91
dependsOnLV = new QListView(stackedWidget);
92
dependsOnLV->setFrameShape(QFrame::NoFrame);
93
// Sets a transparent background
94
QWidget *actionsViewport = dependsOnLV->viewport();
95
QPalette palette = actionsViewport->palette();
96
palette.setColor(actionsViewport->backgroundRole(), Qt::transparent);
97
palette.setColor(actionsViewport->foregroundRole(), palette.color(QPalette::WindowText));
98
actionsViewport->setPalette(palette);
99
m_viewLayout->addWidget(dependsOnLV);
102
if (roles & Enum::RoleGetRequires) {
103
action = menu->addAction(i18n("Required By"));
104
action->setCheckable(true);
105
action->setData(Enum::RoleGetRequires);
106
m_actionGroup->addAction(action);
107
requiredByLV = new QListView(stackedWidget);
108
requiredByLV->setFrameShape(QFrame::NoFrame);
109
// Sets a transparent background
110
QWidget *actionsViewport = requiredByLV->viewport();
111
QPalette palette = actionsViewport->palette();
112
palette.setColor(actionsViewport->backgroundRole(), Qt::transparent);
113
palette.setColor(actionsViewport->foregroundRole(), palette.color(QPalette::WindowText));
114
actionsViewport->setPalette(palette);
115
m_viewLayout->addWidget(requiredByLV);
118
if (roles & Enum::RoleGetFiles) {
119
action = menu->addAction(i18n("File List"));
120
action->setCheckable(true);
121
action->setData(Enum::RoleGetFiles);
122
m_actionGroup->addAction(action);
123
filesPTE = new QPlainTextEdit(stackedWidget);
124
filesPTE->setFrameShape(QFrame::NoFrame);
125
filesPTE->setReadOnly(true);
126
// Sets a transparent background
127
QWidget *actionsViewport = filesPTE->viewport();
128
QPalette palette = actionsViewport->palette();
129
palette.setColor(actionsViewport->backgroundRole(), Qt::transparent);
130
palette.setColor(actionsViewport->foregroundRole(), palette.color(QPalette::WindowText));
131
actionsViewport->setPalette(palette);
132
m_viewLayout->addWidget(filesPTE);
135
// Check to se if we have any action
136
if (m_actionGroup->actions().isEmpty()) {
139
action = m_actionGroup->actions().first();
140
action->setChecked(true);
141
connect(m_actionGroup, SIGNAL(triggered(QAction *)),
142
this, SLOT(actionActivated(QAction *)));
144
menuTB->setMenu(menu);
145
menuTB->setIcon(KIcon("help-about"));
148
m_busySeq = new KPixmapSequenceOverlayPainter(this);
149
m_busySeq->setSequence(KPixmapSequence("process-working", KIconLoader::SizeSmallMedium));
150
m_busySeq->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
151
m_busySeq->setWidget(stackedWidget);
153
// Setup the opacit effect that makes the descriptio transparent
154
// after finished it checks in display() to see if it shouldn't show
155
// up again. The property animation is always the same, the only different thing
156
// is the the Forward or Backward property
157
QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect(stackedWidget);
158
effect->setOpacity(0);
159
// stackedWidget->setVisible(false);
160
stackedWidget->setGraphicsEffect(effect);
161
m_fadeStacked = new QPropertyAnimation(effect, "opacity");
162
m_fadeStacked->setDuration(500);
163
m_fadeStacked->setStartValue(qreal(0));
164
m_fadeStacked->setEndValue(qreal(1));
165
connect(m_fadeStacked, SIGNAL(finished()), this, SLOT(display()));
168
// It's is impossible due to some limitation in Qt to set two effects on the same
170
m_fadeScreenshot = new QPropertyAnimation(effect, "opacity");
171
GraphicsOpacityDropShadowEffect *shadow = new GraphicsOpacityDropShadowEffect(screenshotL);
172
shadow->setOpacity(0);
173
shadow->setBlurRadius(BLUR_RADIUS);
174
shadow->setOffset(2);
175
shadow->setColor(QApplication::palette().dark().color());
176
screenshotL->setGraphicsEffect(shadow);
178
m_fadeScreenshot = new QPropertyAnimation(shadow, "opacity");
179
m_fadeScreenshot->setDuration(500);
180
m_fadeScreenshot->setStartValue(qreal(0));
181
m_fadeScreenshot->setEndValue(qreal(1));
182
connect(m_fadeScreenshot, SIGNAL(finished()), this, SLOT(display()));
184
// This pannel expanding
185
QPropertyAnimation *anim1 = new QPropertyAnimation(this, "maximumSize");
186
anim1->setDuration(500);
187
anim1->setEasingCurve(QEasingCurve::OutQuart);
188
anim1->setStartValue(QSize(QWIDGETSIZE_MAX, 0));
189
anim1->setEndValue(QSize(QWIDGETSIZE_MAX, FINAL_HEIGHT));
190
QPropertyAnimation *anim2 = new QPropertyAnimation(this, "minimumSize");
191
anim2->setDuration(500);
192
anim2->setEasingCurve(QEasingCurve::OutQuart);
193
anim2->setStartValue(QSize(QWIDGETSIZE_MAX, 0));
194
anim2->setEndValue(QSize(QWIDGETSIZE_MAX, FINAL_HEIGHT));
196
m_expandPanel = new QParallelAnimationGroup;
197
m_expandPanel->addAnimation(anim1);
198
m_expandPanel->addAnimation(anim2);
199
connect(m_expandPanel, SIGNAL(finished()), this, SLOT(display()));
202
KpkPackageDetails::~KpkPackageDetails()
206
void KpkPackageDetails::setPackage(const QModelIndex &index)
208
QString pkgId = index.data(KpkPackageModel::IdRole).toString();
209
QString appId = index.data(KpkPackageModel::ApplicationId).toString();
211
// if it's the same package and the same application, return
212
if (pkgId == m_packageId && appId == m_appId) {
214
} else if (maximumSize().height() == 0) {
217
m_expandPanel->setDirection(QAbstractAnimation::Forward);
218
m_expandPanel->start();
220
// Hide the old description
221
fadeOut(KpkPackageDetails::FadeScreenshot | KpkPackageDetails::FadeStacked);
226
Enum::Info info = static_cast<Enum::Info>(index.data(KpkPackageModel::InfoRole).toUInt());
228
m_package = QSharedPointer<Package>(new Package(m_packageId, info, QString()));;
229
m_hasDetails = false;
230
m_hasFileList = false;
231
m_hasRequires = false;
232
m_hasDepends = false;
234
QString pkgIconPath = index.data(KpkPackageModel::IconRole).toString();
235
m_currentIcon = KpkIcons::getIcon(pkgIconPath, QString()).pixmap(64, 64);
236
m_appName = index.data(KpkPackageModel::NameRole).toString();
238
m_currentScreenshot = AppInstall::instance()->thumbnail(m_package->name());
239
if (!m_currentScreenshot.isEmpty()) {
240
if (m_screenshotPath.contains(m_currentScreenshot)) {
243
KTemporaryFile *tempFile = new KTemporaryFile;
244
tempFile->setPrefix("appget");
245
tempFile->setSuffix(".png");
247
KIO::FileCopyJob *job = KIO::file_copy(m_currentScreenshot,
248
tempFile->fileName(),
250
KIO::Overwrite | KIO::HideProgressInfo);
251
connect(job, SIGNAL(result(KJob *)),
252
this, SLOT(resultJob(KJob *)));
256
if (m_actionGroup->checkedAction()) {
257
actionActivated(m_actionGroup->checkedAction());
261
void KpkPackageDetails::on_screenshotL_clicked()
264
screenshot = AppInstall::instance()->screenshot(m_package->name());
265
if (screenshot.isEmpty()) {
268
ScreenShotViewer *view = new ScreenShotViewer(screenshot);
269
view->setWindowTitle(m_appName);
273
void KpkPackageDetails::hidePackageVersion(bool hide)
275
m_hideVersion = hide;
278
void KpkPackageDetails::actionActivated(QAction *action)
280
// don't fade the screenshot
281
// if the package changed setPackage() fades both
282
fadeOut(FadeStacked);
284
// disconnect the transaction
285
// so that we don't get old data
287
disconnect(m_transaction, SIGNAL(details(const QSharedPointer<PackageKit::Package> &)),
288
this, SLOT(description(const QSharedPointer<PackageKit::Package> &)));
289
disconnect(m_transaction, SIGNAL(package(const QSharedPointer<PackageKit::Package> &)),
290
m_dependsModel, SLOT(addPackage(const QSharedPointer<PackageKit::Package> &)));
291
disconnect(m_transaction, SIGNAL(package(const QSharedPointer<PackageKit::Package> &)),
292
m_requiresModel, SLOT(addPackage(const QSharedPointer<PackageKit::Package> &)));
293
disconnect(m_transaction, SIGNAL(files(const QSharedPointer<PackageKit::Package> &, const QStringList &)),
294
this, SLOT(files(const QSharedPointer<PackageKit::Package> &, const QStringList &)));
295
disconnect(m_transaction, SIGNAL(finished(PackageKit::Enum::Exit, uint)),
296
this, SLOT(finished()));
300
// Check to see if we don't already have the required data
301
uint role = action->data().toUInt();
302
if (role == Enum::RoleGetDetails &&
303
m_package->hasDetails()) {
304
description(m_package);
308
case Enum::RoleGetDetails:
309
if (m_package->hasDetails()) {
310
description(m_package);
314
case Enum::RoleGetDepends:
320
case Enum::RoleGetRequires:
326
case Enum::RoleGetFiles:
334
// we don't have the data
335
m_transaction = new Transaction(QString());
336
connect(m_transaction, SIGNAL(finished(PackageKit::Enum::Exit, uint)),
337
this, SLOT(finished()));
339
case Enum::RoleGetDetails:
340
connect(m_transaction, SIGNAL(details(const QSharedPointer<PackageKit::Package> &)),
341
this, SLOT(description(const QSharedPointer<PackageKit::Package> &)));
342
m_transaction->getDetails(m_package);
344
case Enum::RoleGetDepends:
345
if (m_dependsModel) {
346
delete m_dependsModel;
348
m_dependsModel = new KpkSimplePackageModel(this);
349
connect(m_transaction, SIGNAL(package(const QSharedPointer<PackageKit::Package> &)),
350
m_dependsModel, SLOT(addPackage(const QSharedPointer<PackageKit::Package> &)));
351
m_transaction->getDepends(m_package, PackageKit::Enum::NoFilter, false);
353
case Enum::RoleGetRequires:
354
if (m_requiresModel) {
355
delete m_requiresModel;
357
m_requiresModel = new KpkSimplePackageModel(this);
358
connect(m_transaction, SIGNAL(package(const QSharedPointer<PackageKit::Package> &)),
359
m_requiresModel, SLOT(addPackage(const QSharedPointer<PackageKit::Package> &)));
360
m_transaction->getRequires(m_package, PackageKit::Enum::NoFilter, false);
362
case Enum::RoleGetFiles:
363
connect(m_transaction, SIGNAL(files(const QSharedPointer<PackageKit::Package> &, const QStringList &)),
364
this, SLOT(files(const QSharedPointer<PackageKit::Package> &, const QStringList &)));
365
m_transaction->getFiles(m_package);
369
if (m_transaction->error()) {
370
KMessageBox::sorry(this, KpkStrings::daemonError(m_transaction->error()));
376
void KpkPackageDetails::resultJob(KJob *job)
379
KIO::FileCopyJob *fJob = qobject_cast<KIO::FileCopyJob*>(job);
380
if (!fJob->error()) {
381
m_screenshotPath[fJob->srcUrl().url()] = fJob->destUrl().toLocalFile();
386
void KpkPackageDetails::hide()
389
// Clean the old description otherwise if the user selects the same
390
// package the pannel won't expand
394
if (maximumSize().height() == FINAL_HEIGHT) {
395
if (m_fadeStacked->currentValue().toReal() == 0 &&
396
m_fadeScreenshot->currentValue().toReal() == 0) {
397
// Screen shot and description faded let's shrink the pannel
398
m_expandPanel->setDirection(QAbstractAnimation::Backward);
399
m_expandPanel->start();
401
// Hide current description
402
fadeOut(KpkPackageDetails::FadeScreenshot | KpkPackageDetails::FadeStacked);
407
void KpkPackageDetails::fadeOut(FadeWidgets widgets)
409
// Fade out only if needed
410
if ((widgets & FadeStacked) && m_fadeStacked->currentValue().toReal() != 0) {
411
m_fadeStacked->setDirection(QAbstractAnimation::Backward);
412
m_fadeStacked->start();
415
// Fade out the screenshot only if needed
416
if ((widgets & FadeScreenshot) && m_fadeScreenshot->currentValue().toReal() != 0) {
417
screenshotL->unsetCursor();
418
m_fadeScreenshot->setDirection(QAbstractAnimation::Backward);
419
m_fadeScreenshot->start();
423
void KpkPackageDetails::display()
425
// If we shouldn't be showing hide the pannel
428
} else if (maximumSize().height() == FINAL_HEIGHT) {
429
// Check to see if the stacked widget is transparent
430
if (m_fadeStacked->currentValue().toReal() == 0 &&
431
m_actionGroup->checkedAction())
434
switch (m_actionGroup->checkedAction()->data().toUInt()) {
435
case Enum::RoleGetDetails:
441
case Enum::RoleGetDepends:
443
QAbstractItemModel *currentModel = dependsOnLV->model();
444
if (currentModel != m_dependsModel) {
445
dependsOnLV->setModel(m_dependsModel);
448
if (m_viewLayout->currentWidget() != dependsOnLV) {
449
m_viewLayout->setCurrentWidget(dependsOnLV);
454
case Enum::RoleGetRequires:
456
QAbstractItemModel *currentModel = requiredByLV->model();
457
if (currentModel != m_requiresModel) {
458
requiredByLV->setModel(m_requiresModel);
461
if (m_viewLayout->currentWidget() != requiredByLV) {
462
m_viewLayout->setCurrentWidget(requiredByLV);
467
case Enum::RoleGetFiles:
470
if (m_currentFileList.isEmpty()) {
471
filesPTE->appendPlainText(i18n("No files were found."));
473
filesPTE->insertPlainText(m_currentFileList.join("\n"));
476
if (m_viewLayout->currentWidget() != filesPTE) {
477
m_viewLayout->setCurrentWidget(filesPTE);
486
m_fadeStacked->setDirection(QAbstractAnimation::Forward);
487
m_fadeStacked->start();
491
// Check to see if we have a screen shot and if we are
492
// transparent, and make sure the details are going
494
if (m_fadeScreenshot->currentValue().toReal() == 0 &&
495
m_screenshotPath.contains(m_currentScreenshot) &&
496
m_fadeStacked->direction() == QAbstractAnimation::Forward) {
498
pixmap = QPixmap(m_screenshotPath[m_currentScreenshot])
499
.scaled(160,120, Qt::KeepAspectRatio, Qt::SmoothTransformation);
500
screenshotL->setPixmap(pixmap);
501
screenshotL->setCursor(Qt::PointingHandCursor);
503
m_fadeScreenshot->setDirection(QAbstractAnimation::Forward);
504
m_fadeScreenshot->start();
509
void KpkPackageDetails::setupDescription()
511
if (m_viewLayout->currentWidget() != descriptionW) {
512
m_viewLayout->setCurrentWidget(descriptionW);
515
//format and show description
516
Package::Details *details = m_package->details();
518
if (!details->description().isEmpty()) {
519
descriptionL->setText(details->description().replace('\n', "<br>"));
520
descriptionL->show();
522
descriptionL->clear();
525
if (!details->url().isEmpty()) {
526
homepageL->setText("<a href=\"" + details->url() + "\">" +
527
details->url() + "</a>");
533
if (!details->license().isEmpty() && details->license() != "unknown") {
534
// We have a license, check if we have and should show show package version
535
if (!m_hideVersion && !m_package->version().isEmpty()) {
536
licenseL->setText(m_package->version() + " - " + details->license());
538
licenseL->setText(details->license());
541
} else if (!m_hideVersion) {
542
licenseL->setText(m_package->version());
548
// Let's try to find the application's path in human user
549
// readable easiest form :D
550
KService::Ptr service = KService::serviceByDesktopName(m_appId);
551
QVector<QPair<QString, QString> > ret;
553
ret = locateApplication(QString(), service->menuId());
559
path.append(QString("<img width=\"16\" heigh=\"16\"src=\"%1\"/>")
560
.arg(KIconLoader::global()->iconPath("kde", KIconLoader::Small)));
561
path.append(QString(" %1 <img width=\"16\" heigh=\"16\" src=\"%2\"/> %3")
562
.arg(QString::fromUtf8("➜"))
563
.arg(KIconLoader::global()->iconPath("applications-other", KIconLoader::Small))
564
.arg(i18n("Applications")));
565
for (int i = 0; i < ret.size(); i++) {
566
path.append(QString(" %1 <img width=\"16\" heigh=\"16\" src=\"%2\"/> %3")
567
.arg(QString::fromUtf8("➜"))
568
.arg(KIconLoader::global()->iconPath(ret.at(i).second, KIconLoader::Small))
569
.arg(ret.at(i).first));
571
pathL->setText(path);
575
// if (details->group() != Enum::UnknownGroup) {
576
// // description += "<tr><td align=\"right\"><b>" + i18nc("Group of the package", "Group") + ":</b></td><td>"
577
// // + KpkStrings::groups(details->group())
578
// // + "</td></tr>";
581
if (details->size() > 0) {
582
sizeL->setText(KGlobal::locale()->formatByteSize(details->size()));
588
if (m_currentIcon.isNull()) {
591
iconL->setPixmap(m_currentIcon);
595
QVector<QPair<QString, QString> > KpkPackageDetails::locateApplication(const QString &_relPath, const QString &menuId) const
597
QVector<QPair<QString, QString> > ret;
598
KServiceGroup::Ptr root = KServiceGroup::group(_relPath);
600
if (!root || !root->isValid()) {
604
const KServiceGroup::List list = root->entries(false /* sorted */,
605
true /* exclude no display entries */,
606
false /* allow separators */);
608
for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
609
const KSycocaEntry::Ptr p = (*it);
611
if (p->isType(KST_KService)) {
612
const KService::Ptr service = KService::Ptr::staticCast(p);
614
if (service->noDisplay()) {
618
// kDebug() << menuId << service->menuId();
619
if (service->menuId() == menuId) {
620
QPair<QString, QString> pair;
621
pair.first = service->name();
622
pair.second = service->icon();
624
// kDebug() << "FOUND!";
627
} else if (p->isType(KST_KServiceGroup)) {
628
const KServiceGroup::Ptr serviceGroup = KServiceGroup::Ptr::staticCast(p);
630
if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0) {
634
QVector<QPair<QString, QString> > found;
635
found = locateApplication(serviceGroup->relPath(), menuId);
636
if (!found.isEmpty()) {
637
QPair<QString, QString> pair;
638
pair.first = serviceGroup->caption();
639
pair.second = serviceGroup->icon();
645
kWarning(250) << "KServiceGroup: Unexpected object in list!";
653
void KpkPackageDetails::description(const QSharedPointer<PackageKit::Package> &package)
658
void KpkPackageDetails::finished()
665
Transaction *transaction = qobject_cast<Transaction*>(sender());
667
if (transaction->role() == Enum::RoleGetDetails) {
669
} else if (transaction->role() == Enum::RoleGetFiles) {
670
m_hasFileList = true;
671
} else if (transaction->role() == Enum::RoleGetRequires) {
672
m_hasRequires = true;
673
} else if (transaction->role() == Enum::RoleGetDepends) {
683
void KpkPackageDetails::files(QSharedPointer<PackageKit::Package> package, const QStringList &files)
687
m_currentFileList = files;
691
#include "KpkPackageDetails.moc"