diff --git a/Apper/ApperKCM.cpp b/Apper/ApperKCM.cpp index 6b7af5a..2058a74 100644 --- a/Apper/ApperKCM.cpp +++ b/Apper/ApperKCM.cpp @@ -1,922 +1,920 @@ /*************************************************************************** * Copyright (C) 2008-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 "ApperKCM.h" #include "ui_ApperKCM.h" #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 #include #include "FiltersMenu.h" #include "BrowseView.h" #include "CategoryModel.h" #include "TransactionHistory.h" #include "Settings/Settings.h" #include "Updater/Updater.h" Q_LOGGING_CATEGORY(APPER, "apper") #define BAR_SEARCH 0 #define BAR_UPDATE 1 #define BAR_SETTINGS 2 #define BAR_TITLE 3 ApperKCM::ApperKCM(QWidget *parent) : QWidget(parent), ui(new Ui::ApperKCM), m_findIcon(QIcon::fromTheme(QLatin1String("edit-find"))), m_cancelIcon(QIcon::fromTheme(QLatin1String("dialog-cancel"))) { ui->setupUi(this); // store the actions supported by the backend connect(Daemon::global(), &Daemon::changed, this, &ApperKCM::daemonChanged); // Set the current locale Daemon::global()->setHints(QLatin1String("locale=") + QLocale::system().name() + QLatin1String(".UTF-8")); // Browse TAB ui->backTB->setIcon(QIcon::fromTheme(QLatin1String("go-previous"))); // create our toolbar auto toolBar = new QToolBar(this); ui->gridLayout_2->addWidget(toolBar); toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); connect(ui->browseView, &BrowseView::categoryActivated, this, &ApperKCM::on_homeView_activated); auto findMenu = new QMenu(this); // find is just a generic name in case we don't have any search method m_genericActionK = new KToolBarPopupAction(m_findIcon, i18n("Find"), this); toolBar->addAction(m_genericActionK); // Add actions that the backend supports findMenu->addAction(ui->actionFindName); setCurrentAction(ui->actionFindName); findMenu->addAction(ui->actionFindDescription); if (!m_currentAction) { setCurrentAction(ui->actionFindDescription); } findMenu->addAction(ui->actionFindFile); if (!m_currentAction) { setCurrentAction(ui->actionFindFile); } // If no action was set we can't use this search if (m_currentAction == 0) { m_genericActionK->setEnabled(false); ui->searchKLE->setEnabled(false); } else { // Check to see if we need the KToolBarPopupAction setCurrentActionCancel(false); if (findMenu->actions().size() > 1) { m_currentAction->setVisible(false); m_genericActionK->setMenu(findMenu); } else { m_currentAction->setVisible(true); toolBar->removeAction(m_genericActionK); toolBar->addAction(m_currentAction); } connect(m_genericActionK, &KToolBarPopupAction::triggered, this, &ApperKCM::genericActionKTriggered); } // Create the groups model m_groupsModel = new CategoryModel(this); ui->browseView->setCategoryModel(m_groupsModel); connect(m_groupsModel, &CategoryModel::finished, this, &ApperKCM::setupHomeModel); ui->homeView->setSpacing(10); ui->homeView->viewport()->setAttribute(Qt::WA_Hover); KFileItemDelegate *delegate = new KFileItemDelegate(this); delegate->setWrapMode(QTextOption::WordWrap); ui->homeView->setItemDelegate(delegate); // install the backend filters ui->filtersTB->setMenu(m_filtersMenu = new FiltersMenu(this)); connect(m_filtersMenu, &FiltersMenu::filtersChanged, this, &ApperKCM::search); ui->filtersTB->setIcon(QIcon::fromTheme(QLatin1String("view-filter"))); ApplicationSortFilterModel *proxy = ui->browseView->proxy(); proxy->setApplicationFilter(m_filtersMenu->filterApplications()); connect(m_filtersMenu, QOverload::of(&FiltersMenu::filterApplications), proxy, &ApplicationSortFilterModel::setApplicationFilter); //initialize the model, delegate, client and connect it's signals m_browseModel = ui->browseView->model(); // CHANGES TAB ui->changesView->viewport()->setAttribute(Qt::WA_Hover); m_changesModel = new PackageModel(this); auto changedProxy = new KCategorizedSortFilterProxyModel(this); changedProxy->setSourceModel(m_changesModel); changedProxy->setDynamicSortFilter(true); changedProxy->setCategorizedModel(true); changedProxy->setSortCaseSensitivity(Qt::CaseInsensitive); changedProxy->setSortRole(PackageModel::SortRole); changedProxy->sort(0); ui->changesView->setModel(changedProxy); auto changesDelegate = new ChangesDelegate(ui->changesView); changesDelegate->setExtendPixmapWidth(0); ui->changesView->setItemDelegate(changesDelegate); // Connect this signal to keep track of changes connect(m_browseModel, &PackageModel::changed, this, &ApperKCM::checkChanged); // packageUnchecked from changes model connect(m_changesModel, &PackageModel::packageUnchecked, m_changesModel, &PackageModel::removePackage); connect(m_changesModel, &PackageModel::packageUnchecked, m_browseModel, &PackageModel::uncheckPackageDefault); ui->reviewMessage->setIcon(QIcon::fromTheme(QLatin1String("edit-redo"))); ui->reviewMessage->setText(i18n("Some software changes were made")); auto reviewAction = new QAction(i18n("Review"), this); connect(reviewAction, &QAction::triggered, this, &ApperKCM::showReviewPages); ui->reviewMessage->addAction(reviewAction); auto discardAction = new QAction(i18n("Discard"), this); connect(discardAction, &QAction::triggered, m_browseModel, &PackageModel::uncheckAll); ui->reviewMessage->addAction(discardAction); auto applyAction = new QAction(i18n("Apply"), this); connect(applyAction, &QAction::triggered, this, &ApperKCM::save); ui->reviewMessage->addAction(applyAction); ui->reviewMessage->setCloseButtonVisible(false); ui->reviewMessage->hide(); connect(ui->reviewMessage, &KMessageWidget::showAnimationFinished, this, [this] () { if (!ui->reviewMessage->property("HasChanges").toBool()) { ui->reviewMessage->animatedHide(); } }); connect(ui->reviewMessage, &KMessageWidget::hideAnimationFinished, this, [this] () { if (ui->reviewMessage->property("HasChanges").toBool()) { ui->reviewMessage->animatedShow(); } }); auto menu = new QMenu(this); ui->settingsTB->setMenu(menu); ui->settingsTB->setIcon(QIcon::fromTheme(QLatin1String("preferences-other"))); auto signalMapper = new QSignalMapper(this); connect(signalMapper, QOverload::of(&QSignalMapper::mapped), this, &ApperKCM::setPage); QAction *action; action = menu->addAction(QIcon::fromTheme(QLatin1String("view-history")), i18n("History")); signalMapper->setMapping(action, QLatin1String("history")); connect(action, &QAction::triggered, signalMapper, QOverload<>::of(&QSignalMapper::map)); action = menu->addAction(QIcon::fromTheme(QLatin1String("preferences-other")), i18n("Settings")); signalMapper->setMapping(action, QLatin1String("settings")); connect(action, &QAction::triggered, signalMapper, QOverload<>::of(&QSignalMapper::map)); auto helpMenu = new KHelpMenu(this, KAboutData::applicationData()); menu->addMenu(helpMenu->menu()); // Make sure the search bar is visible ui->stackedWidgetBar->setCurrentIndex(BAR_SEARCH); } void ApperKCM::setupHomeModel() { KCategorizedSortFilterProxyModel *oldProxy = m_groupsProxyModel; m_groupsProxyModel = new KCategorizedSortFilterProxyModel(this); m_groupsProxyModel->setSourceModel(m_groupsModel); m_groupsProxyModel->setCategorizedModel(true); m_groupsProxyModel->sort(0); ui->homeView->setModel(m_groupsProxyModel); if (oldProxy) { oldProxy->deleteLater(); } } void ApperKCM::genericActionKTriggered() { m_currentAction->trigger(); } void ApperKCM::setCurrentAction(QAction *action) { // just load the new action if it changes this // also ensures that our menu has more than one action if (m_currentAction != action) { // hides the item from the list action->setVisible(false); // ensures the current action was created if (m_currentAction) { // show the item back in the list m_currentAction->setVisible(true); } m_currentAction = action; // copy data from the curront action m_genericActionK->setText(m_currentAction->text()); m_genericActionK->setIcon(m_currentAction->icon()); } } void ApperKCM::setCurrentActionEnabled(bool state) { if (m_currentAction) { m_currentAction->setEnabled(state); } m_genericActionK->setEnabled(state); } void ApperKCM::setCurrentActionCancel(bool cancel) { if (cancel) { // every action should like cancel ui->actionFindName->setText(i18n("&Cancel")); ui->actionFindFile->setText(i18n("&Cancel")); ui->actionFindDescription->setText(i18n("&Cancel")); m_genericActionK->setText(i18n("&Cancel")); // set cancel icons ui->actionFindFile->setIcon(m_cancelIcon); ui->actionFindDescription->setIcon(m_cancelIcon); ui->actionFindName->setIcon(m_cancelIcon); m_genericActionK->setIcon(m_cancelIcon); } else { ui->actionFindName->setText(i18n("Find by &name")); ui->actionFindFile->setText(i18n("Find by f&ile name")); ui->actionFindDescription->setText(i18n("Find by &description")); // Define actions icon ui->actionFindFile->setIcon(QIcon::fromTheme(QLatin1String("document-open"))); ui->actionFindDescription->setIcon(QIcon::fromTheme(QLatin1String("document-edit"))); ui->actionFindName->setIcon(m_findIcon); m_genericActionK->setIcon(m_findIcon); if (m_currentAction) { m_genericActionK->setText(m_currentAction->text()); } else { // This might happen when the backend can // only search groups m_genericActionK->setText(i18n("Find")); } } } void ApperKCM::checkChanged() { bool hasChanges = false; if (ui->stackedWidget->currentWidget() == ui->pageHome || ui->stackedWidget->currentWidget() == ui->pageChanges || ui->stackedWidget->currentWidget() == ui->pageBrowse) { hasChanges = m_browseModel->hasChanges(); if (!hasChanges && ui->stackedWidget->currentWidget() == ui->pageChanges) { search(); } if (hasChanges) { if (!ui->reviewMessage->isHideAnimationRunning() && !ui->reviewMessage->isShowAnimationRunning()) { ui->reviewMessage->animatedShow(); } } else { if (!ui->reviewMessage->isHideAnimationRunning() && !ui->reviewMessage->isShowAnimationRunning()) { ui->reviewMessage->animatedHide(); } } ui->reviewMessage->setProperty("HasChanges", hasChanges); } else if (ui->stackedWidget->currentWidget() == m_updaterPage) { hasChanges = m_updaterPage->hasChanges(); } else if (ui->stackedWidget->currentWidget() == m_settingsPage) { hasChanges = m_settingsPage->hasChanges(); } emit changed(hasChanges); } void ApperKCM::errorCode(PackageKit::Transaction::Error error, const QString &details) { if (error != Transaction::ErrorTransactionCancelled) { KMessageBox::detailedSorry(this, PkStrings::errorMessage(error), details, PkStrings::error(error), KMessageBox::Notify); } } ApperKCM::~ApperKCM() { delete ui; } void ApperKCM::daemonChanged() { Transaction::Roles roles = Daemon::roles(); if (m_roles == roles) { return; } m_roles = roles; // Add actions that the backend supports ui->actionFindName->setEnabled(roles & Transaction::RoleSearchName); ui->actionFindDescription->setEnabled(roles & Transaction::RoleSearchDetails); ui->actionFindFile->setEnabled(roles & Transaction::RoleSearchFile); ui->browseView->init(roles); m_groupsModel->setRoles(roles); m_filtersMenu->setFilters(Daemon::filters()); } void ApperKCM::on_actionFindName_triggered() { setCurrentAction(ui->actionFindName); if (!ui->searchKLE->text().isEmpty()) { // cache the search m_searchRole = Transaction::RoleSearchName; m_searchString = ui->searchKLE->text(); // create the main transaction search(); } } void ApperKCM::on_actionFindDescription_triggered() { setCurrentAction(ui->actionFindDescription); if (!ui->searchKLE->text().isEmpty()) { // cache the search m_searchRole = Transaction::RoleSearchDetails; m_searchString = ui->searchKLE->text(); // create the main transaction search(); } } void ApperKCM::on_actionFindFile_triggered() { setCurrentAction(ui->actionFindFile); if (!ui->searchKLE->text().isEmpty()) { // cache the search m_searchRole = Transaction::RoleSearchFile; m_searchString = ui->searchKLE->text(); // create the main transaction search(); } } void ApperKCM::on_homeView_activated(const QModelIndex &index) { if (index.isValid()) { const auto proxy = qobject_cast(index.model()); // If the cast failed it's the index came from browseView if (proxy) { m_searchParentCategory = proxy->mapToSource(index); } else { m_searchParentCategory = index; } // cache the search m_searchRole = static_cast(index.data(CategoryModel::SearchRole).toUInt()); qCDebug(APPER) << m_searchRole << index.data(CategoryModel::CategoryRole).toString(); if (m_searchRole == Transaction::RoleResolve) { #ifdef HAVE_APPSTREAM CategoryMatcher parser = index.data(CategoryModel::CategoryRole).value(); // m_searchCategory = AppStream::instance()->findPkgNames(parser); #endif // HAVE_APPSTREAM } else if (m_searchRole == Transaction::RoleSearchGroup) { if (index.data(CategoryModel::GroupRole).type() == QVariant::String) { QString category = index.data(CategoryModel::GroupRole).toString(); if (category.startsWith(QLatin1Char('@')) || (category.startsWith(QLatin1String("repo:")) && category.size() > 5)) { m_searchGroupCategory = category; } else { m_groupsModel->setRootIndex(m_searchParentCategory); ui->backTB->setEnabled(true); return; } } else { m_searchGroupCategory.clear(); int groupRole = index.data(CategoryModel::GroupRole).toInt(); m_searchGroup = static_cast(groupRole); m_searchString = index.data().toString(); // Store the nice name to change the title } } else if (m_searchRole == Transaction::RoleGetUpdates) { setPage(QLatin1String("updates")); return; } // create the main transaction search(); } } bool ApperKCM::canChangePage() { bool changed; // Check if we can change the current page if (ui->stackedWidget->currentWidget() == m_updaterPage) { changed = m_updaterPage->hasChanges(); } else if (ui->stackedWidget->currentWidget() == m_settingsPage) { changed = m_settingsPage->hasChanges(); } else { changed = m_browseModel->hasChanges(); } // if there are no changes don't ask the user if (!changed) { return true; } const int queryUser = KMessageBox::warningYesNoCancel( this, i18n("The settings of the current module have changed.\n" "Do you want to apply the changes or discard them?"), i18n("Apply Settings"), KStandardGuiItem::apply(), KStandardGuiItem::discard(), KStandardGuiItem::cancel()); switch (queryUser) { case KMessageBox::Yes: save(); return true; case KMessageBox::No: load(); return true; case KMessageBox::Cancel: return false; default: return false; } } QString ApperKCM::page() const { return QString(); } void ApperKCM::setPage(const QString &page) { auto transaction = qobject_cast(ui->stackedWidget->currentWidget()); if (transaction) { return; } if (page == QLatin1String("settings")) { if (ui->stackedWidget->currentWidget() != m_settingsPage) { if (!canChangePage()) { return; } if (m_settingsPage == 0) { m_settingsPage = new Settings(m_roles, this); connect(m_settingsPage, &Settings::changed, this, &ApperKCM::checkChanged); connect(m_settingsPage, &Settings::refreshCache, this, &ApperKCM::refreshCache); ui->stackedWidget->addWidget(m_settingsPage); connect(ui->generalSettingsPB, &QPushButton::toggled, m_settingsPage, &Settings::showGeneralSettings); connect(ui->repoSettingsPB, &QPushButton::toggled, m_settingsPage, &Settings::showRepoSettings); } checkChanged(); // ui->buttonBox->clear(); // ui->buttonBox->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Reset); // setButtons(KCModule::Default | KCModule::Apply); emit changed(true); // THIS IS DUMB setButtons only take effect after changed goes true emit changed(false); ui->generalSettingsPB->setChecked(true); ui->stackedWidgetBar->setCurrentIndex(BAR_SETTINGS); ui->stackedWidget->setCurrentWidget(m_settingsPage); m_settingsPage->load(); ui->titleL->clear(); ui->backTB->setEnabled(true); emit caption(i18n("Settings")); } } else if (page == QLatin1String("updates")) { if (ui->stackedWidget->currentWidget() != m_updaterPage) { if (!canChangePage()) { return; } if (m_updaterPage == nullptr) { m_updaterPage = new Updater(m_roles, this); connect(m_updaterPage, &Updater::refreshCache, this, &ApperKCM::refreshCache); connect(m_updaterPage, &Updater::downloadSize, ui->downloadL, &QLabel::setText); // connect(m_updaterPage, &Updater::changed, this, &ApperKCM::checkChanged); ui->stackedWidget->addWidget(m_updaterPage); ui->checkUpdatesPB->setIcon(QIcon::fromTheme(QLatin1String("view-refresh"))); connect(ui->checkUpdatesPB, &QPushButton::clicked, this, &ApperKCM::refreshCache); ui->updatePB->setIcon(QIcon::fromTheme(QLatin1String("system-software-update"))); connect(ui->updatePB, &QPushButton::clicked, this, &ApperKCM::save); connect(m_updaterPage, &Updater::changed, ui->updatePB, &QPushButton::setEnabled); } checkChanged(); ui->stackedWidget->setCurrentWidget(m_updaterPage); m_updaterPage->load(); ui->stackedWidgetBar->setCurrentIndex(BAR_UPDATE); ui->backTB->setEnabled(true); emit caption(i18n("Updates")); } } else if (page == QLatin1String("home")) { if (ui->stackedWidget->currentWidget() == m_updaterPage || ui->stackedWidget->currentWidget() == m_settingsPage) { on_backTB_clicked(); } } else if (page == QLatin1String("history")) { m_history = new TransactionHistory(this); ui->searchKLE->clear(); connect(ui->searchKLE, &QLineEdit::textChanged, m_history, &TransactionHistory::setFilterRegExp); ui->stackedWidget->addWidget(m_history); ui->stackedWidget->setCurrentWidget(m_history); ui->backTB->setEnabled(true); ui->filtersTB->setEnabled(false); ui->widget->setEnabled(false); emit caption(i18n("History")); } } void ApperKCM::on_backTB_clicked() { bool canGoBack = false; if (ui->stackedWidget->currentWidget() == ui->pageBrowse) { if (!ui->browseView->goBack()) { return; } else if (m_groupsModel->hasParent()) { canGoBack = true; } } else if (ui->stackedWidget->currentWidget() == m_history) { ui->filtersTB->setEnabled(true); ui->widget->setEnabled(true); m_history->deleteLater(); m_history = 0; } else if (ui->stackedWidget->currentWidget() == ui->pageHome) { if (m_groupsModel->setParentIndex()) { // if we are able to set a new parent item // do not disable back button return; } } else if (ui->stackedWidget->currentWidget() == m_updaterPage) { if (!canChangePage()) { return; } ui->stackedWidgetBar->setCurrentIndex(BAR_SEARCH); checkChanged(); } else if (ui->stackedWidget->currentWidget() == m_settingsPage) { if (!canChangePage()) { return; } emit changed(true); // THIS IS DUMB setButtons only take effect after changed goes true ui->stackedWidgetBar->setCurrentIndex(BAR_SEARCH); checkChanged(); } ui->homeView->selectionModel()->clear(); ui->stackedWidget->setCurrentWidget(ui->pageHome); ui->backTB->setEnabled(canGoBack); // reset the search role m_searchRole = Transaction::RoleUnknown; emit caption(); } void ApperKCM::showReviewPages() { m_changesModel->clear(); m_changesModel->addSelectedPackagesFromModel(m_browseModel); ui->stackedWidget->setCurrentWidget(ui->pageChanges); ui->backTB->setEnabled(true); emit caption(i18n("Pending Changes")); } void ApperKCM::disconnectTransaction() { if (m_searchTransaction) { // Disconnect everything so that the model don't store // wrong data m_searchTransaction->cancel(); disconnect(m_searchTransaction, &Transaction::finished, ui->browseView->busyCursor(), &KPixmapSequenceOverlayPainter::stop); disconnect(m_searchTransaction, &Transaction::finished, this, &ApperKCM::finished); disconnect(m_searchTransaction, &Transaction::finished, m_browseModel, &PackageModel::finished); disconnect(m_searchTransaction, &Transaction::finished, m_browseModel, &PackageModel::fetchSizes); disconnect(m_searchTransaction, &Transaction::package, m_browseModel, &PackageModel::addNotSelectedPackage); disconnect(m_searchTransaction, &Transaction::errorCode, this, &ApperKCM::errorCode); } } void ApperKCM::search() { ui->browseView->cleanUi(); if (ui->stackedWidgetBar->currentIndex() != BAR_SEARCH) { ui->stackedWidgetBar->setCurrentIndex(BAR_SEARCH); } disconnectTransaction(); // search switch (m_searchRole) { case Transaction::RoleSearchName: m_searchTransaction = Daemon::searchNames(m_searchString, m_filtersMenu->filters()); emit caption(m_searchString); break; case Transaction::RoleSearchDetails: m_searchTransaction = Daemon::searchDetails(m_searchString, m_filtersMenu->filters()); emit caption(m_searchString); break; case Transaction::RoleSearchFile: m_searchTransaction = Daemon::searchFiles(m_searchString, m_filtersMenu->filters()); emit caption(m_searchString); break; case Transaction::RoleSearchGroup: if (m_searchGroupCategory.isEmpty()) { m_searchTransaction = Daemon::searchGroup(m_searchGroup, m_filtersMenu->filters()); // m_searchString has the group nice name emit caption(m_searchString); } else { ui->browseView->setParentCategory(m_searchParentCategory); emit caption(m_searchParentCategory.data().toString()); #ifndef HAVE_APPSTREAM if (m_searchGroupCategory.startsWith(QLatin1Char('@')) || m_searchGroupCategory.startsWith(QLatin1String("repo:"))) { m_searchTransaction = Daemon::searchGroup(m_searchGroupCategory, m_filtersMenu->filters()); } #endif // else the transaction is useless } break; case Transaction::RoleGetPackages: // we want all the installed ones ui->browseView->disableExportInstalledPB(); m_searchTransaction = Daemon::getPackages(Transaction::FilterInstalled | m_filtersMenu->filters()); connect(m_searchTransaction, &Transaction::finished, ui->browseView, &BrowseView::enableExportInstalledPB); emit caption(i18n("Installed Software")); break; case Transaction::RoleResolve: #ifdef HAVE_APPSTREAM if (!m_searchCategory.isEmpty()) { ui->browseView->setParentCategory(m_searchParentCategory); // WARNING the resolve might fail if the backend // has a low limit MaximumItemsToResolve m_searchTransaction = Daemon::resolve(m_searchCategory, m_filtersMenu->filters()); emit caption(m_searchParentCategory.data().toString()); } else { ui->browseView->setParentCategory(m_searchParentCategory); KMessageBox::sorry(this, i18n("Could not find an application that matched this category")); emit caption(); disconnectTransaction(); m_searchTransaction = 0; return; } break; #endif default: qCWarning(APPER) << "Search type not defined yet"; emit caption(); disconnectTransaction(); m_searchTransaction = 0; return; } connect(m_searchTransaction, &Transaction::finished, ui->browseView->busyCursor(), &KPixmapSequenceOverlayPainter::stop); connect(m_searchTransaction, &Transaction::finished, this, &ApperKCM::finished); connect(m_searchTransaction, &Transaction::finished, m_browseModel, &PackageModel::finished); if (ui->browseView->isShowingSizes()) { connect(m_searchTransaction, &Transaction::finished, m_browseModel, &PackageModel::fetchSizes); } connect(m_searchTransaction, &Transaction::package, m_browseModel, &PackageModel::addNotSelectedPackage); connect(m_searchTransaction, &Transaction::errorCode, this, &ApperKCM::errorCode); // cleans the models m_browseModel->clear(); ui->browseView->showInstalledPanel(m_searchRole == Transaction::RoleGetPackages); ui->browseView->busyCursor()->start(); ui->backTB->setEnabled(true); setCurrentActionCancel(true); setCurrentActionEnabled(m_searchTransaction->allowCancel()); ui->stackedWidget->setCurrentWidget(ui->pageBrowse); } void ApperKCM::changed() { Transaction *trans = qobject_cast(sender()); setCurrentActionEnabled(trans->allowCancel()); } void ApperKCM::refreshCache() { emit changed(false); QWidget *currentWidget = ui->stackedWidget->currentWidget(); auto transactionW = new PkTransactionWidget(this); connect(transactionW, &PkTransactionWidget::titleChangedProgress, this, &ApperKCM::caption); QPointer transaction = new PkTransaction(transactionW); - Daemon::setHints (QLatin1String("cache-age=")+QString::number(m_cacheAge)); + Daemon::setHints(QLatin1String("cache-age=")+QString::number(m_cacheAge)); transaction->refreshCache(m_forceRefreshCache); transactionW->setTransaction(transaction, Transaction::RoleRefreshCache); ui->stackedWidget->addWidget(transactionW); ui->stackedWidget->setCurrentWidget(transactionW); ui->stackedWidgetBar->setCurrentIndex(BAR_TITLE); ui->backTB->setEnabled(false); connect(transactionW, &PkTransactionWidget::titleChanged, ui->titleL, &QLabel::setText); QEventLoop loop; connect(transaction, &PkTransaction::finished, &loop, &QEventLoop::quit); // wait for the end of transaction if (!transaction->isFinished()) { loop.exec(); if (!transaction) { // Avoid crashing return; } // If the refresh failed force next refresh Cache call m_forceRefreshCache = transaction->exitStatus() == PkTransaction::Failed; } if (m_updaterPage) { m_updaterPage->getUpdates(); } if (currentWidget == m_settingsPage) { setPage(QLatin1String("settings")); } else { setPage(QLatin1String("updates")); } QTimer::singleShot(0, this, &ApperKCM::checkChanged); } void ApperKCM::save() { QWidget *currentWidget = ui->stackedWidget->currentWidget(); if (currentWidget == m_settingsPage) { m_settingsPage->save(); } else { ui->reviewMessage->hide(); auto transactionW = new PkTransactionWidget(this); connect(transactionW, &PkTransactionWidget::titleChangedProgress, this, &ApperKCM::caption); QPointer transaction = new PkTransaction(transactionW); ui->stackedWidget->addWidget(transactionW); ui->stackedWidget->setCurrentWidget(transactionW); ui->stackedWidgetBar->setCurrentIndex(BAR_TITLE); ui->backTB->setEnabled(false); connect(transactionW, &PkTransactionWidget::titleChanged, ui->titleL, &QLabel::setText); emit changed(false); QEventLoop loop; connect(transaction, &PkTransaction::finished, &loop, &QEventLoop::quit); if (currentWidget == m_updaterPage) { transaction->updatePackages(m_updaterPage->packagesToUpdate()); transactionW->setTransaction(transaction, Transaction::RoleUpdatePackages); // wait for the end of transaction if (!transaction->isFinished()) { loop.exec(); if (!transaction) { // Avoid crashing return; } } } else { // install then remove packages QStringList installPackages = m_browseModel->selectedPackagesToInstall(); if (!installPackages.isEmpty()) { transaction->installPackages(installPackages); transactionW->setTransaction(transaction, Transaction::RoleInstallPackages); // wait for the end of transaction if (!transaction->isFinished()) { loop.exec(); if (!transaction) { // Avoid crashing return; } } if (transaction->exitStatus() == PkTransaction::Success) { m_browseModel->uncheckAvailablePackages(); } } QStringList removePackages = m_browseModel->selectedPackagesToRemove(); if (!removePackages.isEmpty()) { transaction->removePackages(removePackages); transactionW->setTransaction(transaction, Transaction::RoleRemovePackages); // wait for the end of transaction if (!transaction->isFinished()) { loop.exec(); if (!transaction) { // Avoid crashing return; } } if (transaction->exitStatus() == PkTransaction::Success) { m_browseModel->uncheckInstalledPackages(); } } } transaction->deleteLater(); if (currentWidget == m_updaterPage) { m_updaterPage->getUpdates(); setPage(QLatin1String("updates")); } else { // install then remove packages search(); } QTimer::singleShot(0, this, &ApperKCM::checkChanged); } } void ApperKCM::load() { if (ui->stackedWidget->currentWidget() == m_updaterPage) { m_updaterPage->load(); } else if (ui->stackedWidget->currentWidget() == m_settingsPage) { m_settingsPage->load(); } else { // set focus on the search lineEdit ui->searchKLE->setFocus(Qt::OtherFocusReason); m_browseModel->setAllChecked(false); } } void ApperKCM::defaults() { if (ui->stackedWidget->currentWidget() == m_settingsPage) { m_settingsPage->defaults(); } } void ApperKCM::finished() { // if m_currentAction is false means that our // find button should be disable as there aren't any // search methods setCurrentActionEnabled(m_currentAction); setCurrentActionCancel(false); m_searchTransaction = 0; } void ApperKCM::keyPressEvent(QKeyEvent *event) { if (ui->searchKLE->hasFocus() && ui->stackedWidget->currentWidget() != m_history && (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)) { // special tab handling here m_currentAction->trigger(); return; } // KCModule::keyPressEvent(event); } void ApperKCM::closeEvent(QCloseEvent *event) { // PkTransaction *transaction = qobject_cast(stackedWidget->currentWidget()); // if (transaction) { event->ignore(); // } else { // event->accept(); // } } #include "ApperKCM.moc" diff --git a/Apper/PackageDetails.cpp b/Apper/PackageDetails.cpp index 21b1158..3a36763 100644 --- a/Apper/PackageDetails.cpp +++ b/Apper/PackageDetails.cpp @@ -1,801 +1,802 @@ /*************************************************************************** * Copyright (C) 2009-2011 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(KPixmapSequence(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); 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); 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); 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); 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_screenshotPath.contains(m_currentScreenshot)) { display(); } else { auto tempFile = new QTemporaryFile; tempFile->open(); KIO::FileCopyJob *job = KIO::file_copy(QUrl(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() { QString url; url = screenshot(Transaction::packageName(m_packageID)); if (!url.isNull()) { ScreenShotViewer *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(); 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 { #ifndef HAVE_APPSTREAM Q_UNUSED(pkgName) return QString(); #else return QString();//AppStream::instance()->thumbnail(pkgName); #endif } QString PackageDetails::screenshot(const QString &pkgName) const { #ifndef HAVE_APPSTREAM Q_UNUSED(pkgName) return QString(); #else return QString();// AppStream::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. QList apps; // apps = AppStream::instance()->applications(Transaction::packageName(m_packageID)); for (const AppStream::Application &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; }