diff --git a/Apper/Apper.cpp b/Apper/Apper.cpp index 96eec42..1ce8711 100644 --- a/Apper/Apper.cpp +++ b/Apper/Apper.cpp @@ -1,239 +1,239 @@ /*************************************************************************** * Copyright (C) 2008-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 "Apper.h" #include "BackendDetails.h" #include "MainUi.h" //#include //#include //#include #include #include //#include //#include //#include #include #include #include #include #include #include #include #include #include Apper::Apper(int& argc, char** argv) : QApplication(argc, argv), m_pkUi(0), m_running(0) { setQuitOnLastWindowClosed(false); auto service = new KDBusService(KDBusService::Unique); connect(this, &Apper::aboutToQuit, service, &KDBusService::deleteLater); connect(service, &KDBusService::activateRequested, this, &Apper::activate); } Apper::~Apper() { } void Apper::appClose() { //check whether we can close if (!m_running && !m_pkUi) { quit(); } } void Apper::kcmFinished() { // kcm is finished we set to 0 to be able to quit m_pkUi->deleteLater(); m_pkUi = 0; appClose(); } void Apper::decreaseAndKillRunning() { m_running--; sender()->deleteLater(); appClose(); } void Apper::activate(const QStringList& arguments, const QString& workingDirectory) { Q_UNUSED(workingDirectory); QCommandLineParser parser; parser.addVersionOption(); parser.addHelpOption(); QCommandLineOption updatesOpt(QStringList() << QLatin1String("updates"), i18n("Show updates")); parser.addOption(updatesOpt); QCommandLineOption settingsOpt(QStringList() << QLatin1String("settings"), i18n("Show settings")); parser.addOption(settingsOpt); QCommandLineOption backendOpt(QStringList() << QLatin1String("backend-details"), i18n("Show backend details")); parser.addOption(backendOpt); QCommandLineOption mimeTypeOpt(QStringList() << QLatin1String("install-mime-type"), i18n("Mime type installer"), QLatin1String("mime-type")); parser.addOption(mimeTypeOpt); QCommandLineOption nameOpt(QStringList() << QLatin1String("install-package-name"), i18n("Package name installer"), QLatin1String("name")); parser.addOption(nameOpt); QCommandLineOption fileOpt(QStringList() << QLatin1String("file"), i18n("Single file installer"), QLatin1String("file")); parser.addOption(fileOpt); QCommandLineOption resourceOpt(QStringList() << QLatin1String("resource"), i18n("Font resource installer"), QLatin1String("lang")); parser.addOption(resourceOpt); QCommandLineOption catalogOpt(QStringList() << QLatin1String("install-catalog"), i18n("Catalog installer"), QLatin1String("file")); parser.addOption(catalogOpt); QCommandLineOption removeOpt(QStringList() << QLatin1String("remove-package-by-file"), i18n("Single package remover"), QLatin1String("filename")); parser.addOption(removeOpt); parser.addPositionalArgument(QLatin1String("[package]"), i18n("Package file to install")); KAboutData::applicationData().setupCommandLine(&parser); parser.process(arguments); KAboutData::applicationData().processCommandLine(&parser); auto args = parser.positionalArguments(); if (args.count()) { // grab the list of files - QStringList urls; - for (int i = 0; i < args.count(); i++) { - urls << args[i]; - } +// QStringList urls; +// for (int i = 0; i < args.count(); i++) { +// urls << args[i]; +// } // TODO remote files are copied to /tmp // what will happen if we call the other process to // install and this very one closes? will the files // in /tmp be deleted? - invoke("InstallPackageFiles", urls); + invoke("InstallPackageFiles", args); return; } if (parser.isSet(updatesOpt)) { QTimer::singleShot(0, this, &Apper::showUpdates); return; } if (parser.isSet(settingsOpt)) { QTimer::singleShot(0, this, &Apper::showSettings); return; } if (parser.isSet(mimeTypeOpt)) { invoke("InstallMimeTypes", parser.values(mimeTypeOpt)); return; } if (parser.isSet(nameOpt)) { invoke("InstallPackageNames", parser.values(nameOpt)); return; } if (parser.isSet("install-provide-file")) { invoke("InstallProvideFiles", parser.values("install-provide-file")); return; } if (parser.isSet(catalogOpt)) { invoke("InstallCatalogs", parser.values(catalogOpt)); return; } if (parser.isSet(removeOpt)) { invoke("RemovePackageByFiles", parser.values(removeOpt)); return; } if (parser.isSet(backendOpt)) { auto helper = new BackendDetails; connect(helper, &BackendDetails::rejected, this, &Apper::decreaseAndKillRunning); QTimer::singleShot(0, helper, &BackendDetails::show); m_running++; return; } // If we are here, we neet to show/activate the main UI QTimer::singleShot(0, this, &Apper::showUi); } void Apper::showUi() { if (!m_pkUi) { m_pkUi = new MainUi(); -// connect(m_pkUi, &MainUi::abou, this, &Apper::kcmFinished); + connect(m_pkUi, &MainUi::finished, this, &Apper::kcmFinished); } // Show all m_pkUi->showAll(); m_pkUi->show(); // KWindowSystem::forceActiveWindow(m_pkUi->winId()); } void Apper::showUpdates() { if (!m_pkUi) { m_pkUi = new MainUi(); -// connect(m_pkUi, &MainUi::finished, this, &Apper::kcmFinished); + connect(m_pkUi, &MainUi::finished, this, &Apper::kcmFinished); } m_pkUi->showUpdates(); m_pkUi->show(); // KWindowSystem::forceActiveWindow(m_pkUi->winId()); } void Apper::showSettings() { if (!m_pkUi) { m_pkUi = new MainUi(); -// connect(m_pkUi, &MainUi::finished, this, &Apper::kcmFinished); + connect(m_pkUi, &MainUi::finished, this, &Apper::kcmFinished); } m_pkUi->showSettings(); m_pkUi->show(); // KWindowSystem::forceActiveWindow(m_pkUi->winId()); } void Apper::invoke(const QString &method_name, const QStringList &args) { QDBusMessage message; message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.PackageKit"), QStringLiteral("/org/freedesktop/PackageKit"), QStringLiteral("org.freedesktop.PackageKit.Modify"), method_name); message << uint(0); message << args; message << QString(); // This call must block otherwise this application closes before // smarticon is activated QDBusConnection::sessionBus().call(message, QDBus::BlockWithGui); QTimer::singleShot(0, this, &Apper::appClose); } #include "Apper.moc" diff --git a/Apper/ApperKCM.cpp b/Apper/ApperKCM.cpp index fadd576..2d6e841 100644 --- a/Apper/ApperKCM.cpp +++ b/Apper/ApperKCM.cpp @@ -1,907 +1,922 @@ /*************************************************************************** * 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 -//KCONFIGGROUP_DECLARE_ENUM_QOBJECT(Transaction, Filter) - -//K_PLUGIN_FACTORY(ApperFactory, registerPlugin();) -//K_EXPORT_PLUGIN(ApperFactory("kcm_apper", "apper")) - ApperKCM::ApperKCM(QWidget *parent) : QWidget(parent), ui(new Ui::ApperKCM), m_findIcon(QIcon::fromTheme("edit-find")), m_cancelIcon(QIcon::fromTheme("dialog-cancel")) { ui->setupUi(this); -// auto aboutData = new KAboutData("kcm_apper", -// "apper", -// APPER_VERSION, -// i18n("KDE interface for managing software"), -// KAboutLicense::LicenseKey::GPL); -// aboutData->addAuthor(i18n("(C) 2008-2018 Daniel Nicoletti"), QString(), "dantti12@gmail.com", "http://dantti.wordpress.com"); -// aboutData->addAuthor(i18n("Matthias Klumpp"), QString(), QStringLiteral("matthias@tenstral.net")); -// setAboutData(aboutData); -// setButtons(Apply); - ui->buttonBox->setStandardButtons(QDialogButtonBox::Apply); - // 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("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("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->changesPB->setIcon(QIcon::fromTheme("edit-redo")); + ui->reviewMessage->setIcon(QIcon::fromTheme("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("preferences-other")); auto signalMapper = new QSignalMapper(this); connect(signalMapper, QOverload::of(&QSignalMapper::mapped), this, &ApperKCM::setPage); QAction *action; action = menu->addAction(QIcon::fromTheme("view-history"), i18n("History")); signalMapper->setMapping(action, "history"); connect(action, &QAction::triggered, signalMapper, QOverload<>::of(&QSignalMapper::map)); action = menu->addAction(QIcon::fromTheme("preferences-other"), i18n("Settings")); signalMapper->setMapping(action, "settings"); connect(action, &QAction::triggered, signalMapper, QOverload<>::of(&QSignalMapper::map)); - // Only show help menu if not on System Settings -// if (!args.isEmpty()) { - // adds the help menu - //! KHelpMenu *helpMenu = new KHelpMenu(this, KGlobal::mainComponent().aboutData()); - //! menu->addMenu(helpMenu->menu()); -// } + 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("document-open")); ui->actionFindDescription->setIcon(QIcon::fromTheme("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(); } - ui->changesPB->setEnabled(hasChanges); + 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('@') || (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("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 == 0) { + 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("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; } -// setButtons(Apply); - ui->buttonBox->setStandardButtons(QDialogButtonBox::Apply); 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::on_changesPB_clicked() +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)); 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("settings"); } else { setPage("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("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/ApperKCM.h b/Apper/ApperKCM.h index bd2736c..b5fa238 100644 --- a/Apper/ApperKCM.h +++ b/Apper/ApperKCM.h @@ -1,130 +1,130 @@ /*************************************************************************** * Copyright (C) 2008-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. * ***************************************************************************/ #ifndef APPER_KCM_U #define APPER_KCM_U #include #include #include #include using namespace PackageKit; namespace Ui { class ApperKCM; } class PackageModel; class FiltersMenu; class TransactionHistory; class CategoryModel; class Settings; class Updater; class ApperKCM : public QWidget { Q_OBJECT Q_PROPERTY(QString page READ page WRITE setPage USER true) public: ApperKCM(QWidget *parent); ~ApperKCM(); QString page() const; Q_SIGNALS: void changed(bool state); void caption(const QString &title = QString()); public Q_SLOTS: void daemonChanged(); void load(); void save(); void defaults(); void setPage(const QString &page); private Q_SLOTS: void search(); void setupHomeModel(); void genericActionKTriggered(); void on_backTB_clicked(); - void on_changesPB_clicked(); + void showReviewPages(); void on_actionFindName_triggered(); void on_actionFindDescription_triggered(); void on_actionFindFile_triggered(); void on_homeView_activated(const QModelIndex &index); void finished(); void errorCode(PackageKit::Transaction::Error error, const QString &detail); void checkChanged(); void changed(); void refreshCache(); protected: virtual void closeEvent(QCloseEvent *event); private: void disconnectTransaction(); bool canChangePage(); void setCurrentActionEnabled(bool state); void setCurrentAction(QAction *action); void setCurrentActionCancel(bool cancel); void setActionCancel(bool enabled); void keyPressEvent(QKeyEvent *event); Ui::ApperKCM *ui; KToolBarPopupAction *m_genericActionK; QAction *m_currentAction = nullptr; CategoryModel *m_groupsModel; KCategorizedSortFilterProxyModel *m_groupsProxyModel = nullptr; PackageModel *m_browseModel; PackageModel *m_changesModel; Settings *m_settingsPage = nullptr; Updater *m_updaterPage = nullptr; Transaction *m_searchTransaction = nullptr; QIcon m_findIcon; QIcon m_cancelIcon; FiltersMenu *m_filtersMenu; Transaction::Roles m_roles; bool m_forceRefreshCache = false; uint m_cacheAge = 600; TransactionHistory *m_history = nullptr; // Old search cache Transaction::Role m_searchRole = Transaction::RoleUnknown; QString m_searchString; QString m_searchGroupCategory; PackageKit::Transaction::Group m_searchGroup; QModelIndex m_searchParentCategory; QStringList m_searchCategory; }; #endif diff --git a/Apper/ApperKCM.ui b/Apper/ApperKCM.ui index 9c862b9..bb1b9f9 100644 --- a/Apper/ApperKCM.ui +++ b/Apper/ApperKCM.ui @@ -1,454 +1,474 @@ ApperKCM 0 0 - 411 - 322 + 481 + 353 0 0 Add and Remove Software 0 0 0 0 0 + + + + false + + + Back + + + true + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::DragDrop + + + Qt::ElideNone + + + QListView::IconMode + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + QAbstractItemView::NoEditTriggers + + + false + + + true + + + QAbstractItemView::ScrollPerPixel + + + + + + + 0 0 0 0 0 0 0 Qt::Vertical Search packages true 0 0 11 0 0 0 0 0 0 0 Filters QToolButton::InstantPopup Qt::ToolButtonTextBesideIcon true Qt::NoArrow Qt::Vertical - - - - false - - - Pending Changes - - - true - - - QToolButton::InstantPopup true 0 0 0 0 Qt::Vertical + + + + false + + + Update + + + false + + + true + + + Check for new Updates true Qt::Horizontal 40 20 Average size for download General Settings true true true true Software Origins true false true true Qt::Horizontal 40 20 0 0 0 0 - Some Title + - - - - false - - - Back - - - true - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::DragDrop - - - Qt::ElideNone - - - QListView::IconMode - - - true - - - - - - - QDialogButtonBox::Apply|QDialogButtonBox::RestoreDefaults - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - QAbstractItemView::NoEditTriggers - - - false - - - true - - - QAbstractItemView::ScrollPerPixel - - - - - - + + Find by &name Find by &description Find by f&ile name + + KMessageWidget + QWidget +
KMessageWidget
+ 1 +
CategorizedView QListView
CategorizedView.h
BrowseView QWidget
BrowseView.h
1
+ + searchKLE + backTB + filtersTB + settingsTB + checkUpdatesPB + generalSettingsPB + repoSettingsPB + homeView + changesView + updatePB +
diff --git a/Apper/BackendDetails.cpp b/Apper/BackendDetails.cpp index 08f647b..7846904 100644 --- a/Apper/BackendDetails.cpp +++ b/Apper/BackendDetails.cpp @@ -1,111 +1,110 @@ /*************************************************************************** - * Copyright (C) 2009-2013 by Daniel Nicoletti * + * Copyright (C) 2009-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "BackendDetails.h" #include "ui_BackendDetails.h" #include #include #include using namespace PackageKit; BackendDetails::BackendDetails(QWidget *parent) : QDialog(parent), ui(new Ui::BackendDetails) { - setWindowTitle(i18n("Backend Details")); - ui->setupUi(this); -// setButtons(KDialog::Close); - setWindowIcon(QIcon::fromTheme("help-about")); + + setWindowTitle(i18n("Backend Details")); + setWindowIcon(QIcon::fromTheme(QLatin1String("help-about"))); // update information about PackageKit backend connect(Daemon::global(), &Daemon::changed, this, &BackendDetails::daemonChanged); if (Daemon::global()->isRunning()) { daemonChanged(); } } BackendDetails::~BackendDetails() { delete ui; } void BackendDetails::daemonChanged() { // PackageKit QString versionMajor = QString::number(Daemon::global()->versionMajor()); QString versionMinor = QString::number(Daemon::global()->versionMinor()); QString versionMicro = QString::number(Daemon::global()->versionMicro()); ui->pkVersionL->setText(versionMajor % QLatin1Char('.') % versionMinor % QLatin1Char('.') % versionMicro); // GENERAL - Setup backend name and author ui->nameL->setText(Daemon::global()->backendName()); ui->descriptionL->setText(Daemon::global()->backendDescription()); ui->authorL->setText(Daemon::global()->backendAuthor()); ui->distroL->setText(Daemon::global()->distroID()); // METHODS - Setup backend supported methods Transaction::Roles actions = Daemon::global()->roles();// TODO this is async now ui->getUpdatesCB->setChecked(actions & Transaction::RoleGetUpdates); ui->getDistroUpgradesCB->setChecked(actions & Transaction::RoleGetDistroUpgrades); ui->refreshCacheCB->setChecked(actions & Transaction::RoleRefreshCache); ui->searchNameCB->setChecked(actions & Transaction::RoleSearchName); ui->searchDetailsCB->setChecked(actions & Transaction::RoleSearchDetails); ui->searchGroupCB->setChecked(actions & Transaction::RoleSearchGroup); ui->searchFileCB->setChecked(actions & Transaction::RoleSearchFile); ui->cancelCB->setChecked(actions & Transaction::RoleCancel); ui->resolveCB->setChecked(actions & Transaction::RoleResolve); ui->updatePackageCB->setChecked(actions & Transaction::RoleUpdatePackages); ui->installPackageCB->setChecked(actions & Transaction::RoleInstallPackages); ui->removePackageCB->setChecked(actions & Transaction::RoleRemovePackages); ui->getDependsCB->setChecked(actions & Transaction::RoleDependsOn); ui->getRequiresCB->setChecked(actions & Transaction::RoleRequiredBy); ui->getUpdateDetailCB->setChecked(actions & Transaction::RoleGetUpdateDetail); ui->getDescriptionCB->setChecked(actions & Transaction::RoleGetDetails); ui->getFilesCB->setChecked(actions & Transaction::RoleRefreshCache); ui->installFileCB->setChecked(actions & Transaction::RoleInstallFiles); ui->getRepositoryListCB->setChecked(actions & Transaction::RoleGetRepoList); ui->repositoryEnableCB->setChecked(actions & Transaction::RoleRepoEnable); ui->repositorySetEnableCB->setChecked(actions & Transaction::RoleRepoSetData); ui->whatProvidesCB->setChecked(actions & Transaction::RoleWhatProvides); ui->getPackagesCB->setChecked(actions & Transaction::RoleGetPackages); ui->repairSystemCB->setChecked(actions & Transaction::RoleRepairSystem); // FILTERS - Setup filters Transaction::Filters filters = Daemon::global()->filters(); ui->installedCB->setChecked(filters & Transaction::FilterInstalled); ui->guiCB->setChecked(filters & Transaction::FilterGui); ui->developmentCB->setChecked(filters & Transaction::FilterDevel); ui->freeCB->setChecked(filters & Transaction::FilterFree); ui->visibleCB->setChecked(filters & Transaction::FilterVisible); ui->supportedCB->setChecked(filters & Transaction::FilterSupported); ui->newestCB->setChecked(filters & Transaction::FilterNewest); ui->archCB->setChecked(filters & Transaction::FilterNotArch); } #include "BackendDetails.moc" diff --git a/Apper/BackendDetails.h b/Apper/BackendDetails.h index fd7749e..cd75db4 100644 --- a/Apper/BackendDetails.h +++ b/Apper/BackendDetails.h @@ -1,44 +1,43 @@ /*************************************************************************** - * Copyright (C) 2009-2013 by Daniel Nicoletti * + * Copyright (C) 2009-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef BACKEND_DETAILS_H #define BACKEND_DETAILS_H #include namespace Ui { class BackendDetails; } class BackendDetails : public QDialog { Q_OBJECT public: explicit BackendDetails(QWidget *parent = 0); ~BackendDetails(); -private Q_SLOTS: void daemonChanged(); private: Ui::BackendDetails *ui; }; #endif diff --git a/Apper/MainUi.cpp b/Apper/MainUi.cpp index f1c995d..82ec55c 100644 --- a/Apper/MainUi.cpp +++ b/Apper/MainUi.cpp @@ -1,94 +1,89 @@ /*************************************************************************** - * Copyright (C) 2009-2011 by Daniel Nicoletti * + * Copyright (C) 2009-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "MainUi.h" #include #include #include -//#include #include #include #include +#include #include "ApperKCM.h" -//Q_LOGGING_CATEGORY(APPER, "apper") Q_DECLARE_LOGGING_CATEGORY(APPER) MainUi::MainUi(QWidget *parent) : QMainWindow(parent), m_apperModule(0) { setWindowIcon(QIcon::fromTheme("system-software-install")); + setWindowTitle(i18n("Apper")); -// KConfig config("apper"); -// KConfigGroup configGroup(&config, "MainUi"); - //! restoreDialogSize(configGroup); - - // Set Apply and Cancel buttons -// setStandardButtons(QDialogButtonBox::Apply /*| KDialog::Help*/ | QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Reset); + KConfig config("apper"); + KConfigGroup configGroup(&config, "MainUi"); + restoreGeometry(configGroup.readEntry("geometry", QByteArray())); + restoreState(configGroup.readEntry("state", QByteArray())); -// KPageWidgetItem *page = addModule(QLatin1String("kcm_apper.desktop"), -// QStringList() << QLatin1String("apper")); m_apperModule = new ApperKCM(this); setCentralWidget(m_apperModule); -// if (page) { -// auto proxy = static_cast(page->widget()); -// if (proxy) { -// m_apperModule = proxy->realModule(); -// connect(m_apperModule, &KCModule::windowTitleChanged, this, &MainUi::setWindowTitle); -// } -// } else { -// qCWarning(APPER) << "Could not load kcm_apper.desktop!"; -// } + connect(m_apperModule, &ApperKCM::caption, this, &MainUi::setWindowTitle); } MainUi::~MainUi() { - // save size - KConfig config("apper"); - KConfigGroup configGroup(&config, "MainUi"); - //! saveDialogSize(configGroup); } void MainUi::showAll() { if (m_apperModule) { m_apperModule->setProperty("page", "home"); } } void MainUi::showUpdates() { if (m_apperModule) { m_apperModule->setProperty("page", "updates"); } } void MainUi::showSettings() { if (m_apperModule) { m_apperModule->setProperty("page", "settings"); } } +void MainUi::closeEvent(QCloseEvent *event) +{ + KConfig config("apper"); + KConfigGroup configGroup(&config, "MainUi"); + configGroup.writeEntry("geometry", saveGeometry()); + configGroup.writeEntry("state", saveState()); + QMainWindow::closeEvent(event); + + emit finished(); +} + #include "MainUi.moc" diff --git a/Apper/MainUi.h b/Apper/MainUi.h index d476f68..329b4d9 100644 --- a/Apper/MainUi.h +++ b/Apper/MainUi.h @@ -1,42 +1,48 @@ /*************************************************************************** * Copyright (C) 2009-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef MAIN_UI_H #define MAIN_UI_H #include class ApperKCM; class MainUi : public QMainWindow { Q_OBJECT public: explicit MainUi(QWidget *parent = 0); ~MainUi(); void showAll(); void showUpdates(); void showSettings(); +Q_SIGNALS: + void finished(); + +protected: + void closeEvent(QCloseEvent *event); + private: ApperKCM *m_apperModule; }; #endif diff --git a/Apper/Settings/OriginModel.cpp b/Apper/Settings/OriginModel.cpp index 3e0d67c..45a15e7 100644 --- a/Apper/Settings/OriginModel.cpp +++ b/Apper/Settings/OriginModel.cpp @@ -1,103 +1,103 @@ /*************************************************************************** * Copyright (C) 2008-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 "OriginModel.h" #include #include #include #include #include using namespace PackageKit; OriginModel::OriginModel(QObject *parent) : QStandardItemModel(parent), m_finished(true) { - setHorizontalHeaderLabels(QStringList() << i18n("Origin of Packages")); + setHorizontalHeaderLabels({ i18n("Origin of Packages") }); } OriginModel::~OriginModel() { } bool OriginModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == Qt::CheckStateRole && index.isValid()) { Transaction *transaction = Daemon::repoEnable(index.data(RepoId).toString(), value.toBool()); connect(transaction, &Transaction::errorCode, this, &OriginModel::errorCode); connect(transaction, &Transaction::finished, this, &OriginModel::setRepoFinished); } return false; } QVariantHash OriginModel::changes() const { QVariantHash ret; for (int i = 0; i < rowCount(); ++i) { QStandardItem *repo = item(i); bool currentState = repo->checkState(); if (currentState != repo->data(RepoInitialState).toBool()) { ret[repo->data(RepoId).toString()] = currentState; } } return ret; } void OriginModel::addOriginItem(const QString &repo_id, const QString &details, bool enabled) { if (m_finished) { // if we received a finished signal this is a new query removeRows(0, rowCount()); m_finished = false; } auto item = new QStandardItem(details); item->setCheckable(true); item->setCheckState(enabled ? Qt::Checked : Qt::Unchecked); item->setData(repo_id, RepoId); item->setData(enabled, RepoInitialState); appendRow(item); } void OriginModel::finished() { m_finished = true; } void OriginModel::errorCode(PackageKit::Transaction::Error error, const QString &details) { if (error != Transaction::ErrorTransactionCancelled) { KMessageBox::detailedSorry(0, PkStrings::errorMessage(error), details, PkStrings::error(error), KMessageBox::Notify); } } void OriginModel::setRepoFinished(Transaction::Exit exit) { if (exit == Transaction::ExitSuccess) { emit refreshRepoList(); } sender()->deleteLater(); } diff --git a/Apper/Updater/Updater.cpp b/Apper/Updater/Updater.cpp index 8288d7a..dbb728d 100644 --- a/Apper/Updater/Updater.cpp +++ b/Apper/Updater/Updater.cpp @@ -1,355 +1,349 @@ /*************************************************************************** * Copyright (C) 2008-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 "Updater.h" #include "ui_Updater.h" #include "UpdateDetails.h" #include "DistroUpgrade.h" #include "CheckableHeader.h" #include //#include #include #include #include #include #include #include #include #include #include #include #include #include -//#include #include #include #include +Q_DECLARE_LOGGING_CATEGORY(APPER) + Updater::Updater(Transaction::Roles roles, QWidget *parent) : QWidget(parent), ui(new Ui::Updater), - m_roles(roles), - m_selected(true), - m_updatesT(0) + m_roles(roles) { ui->setupUi(this); updatePallete(); // connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), // this, SLOT(updatePallete())); m_updatesModel = new PackageModel(this); m_updatesModel->setCheckable(true); auto proxyModel = new ApplicationSortFilterModel(this); proxyModel->setSourceModel(m_updatesModel); ui->packageView->setModel(proxyModel); m_delegate = new ApplicationsDelegate(ui->packageView); m_delegate->setCheckable(true); ui->packageView->setItemDelegate(m_delegate); ui->packageView->sortByColumn(PackageModel::NameCol, Qt::AscendingOrder); connect(m_updatesModel, &PackageModel::changed, this, &Updater::checkEnableUpdateButton); //initialize the model, delegate, client and connect it's signals m_header = new CheckableHeader(Qt::Horizontal, this); connect(m_header, &CheckableHeader::toggled, m_updatesModel, &PackageModel::setAllChecked); m_header->setCheckBoxVisible(false); m_header->setDefaultAlignment(Qt::AlignCenter); ui->packageView->setHeaderHidden(false); ui->packageView->setHeader(m_header); // This must be set AFTER the model is set, otherwise it doesn't work m_header->setSectionResizeMode(PackageModel::NameCol, QHeaderView::Stretch); m_header->setSectionResizeMode(PackageModel::VersionCol, QHeaderView::ResizeToContents); m_header->setSectionResizeMode(PackageModel::CurrentVersionCol, QHeaderView::ResizeToContents); m_header->setSectionResizeMode(PackageModel::ArchCol, QHeaderView::ResizeToContents); m_header->setSectionResizeMode(PackageModel::OriginCol, QHeaderView::ResizeToContents); m_header->setSectionResizeMode(PackageModel::SizeCol, QHeaderView::ResizeToContents); m_header->setStretchLastSection(false); // Setup the busy cursor m_busySeq = new KPixmapSequenceOverlayPainter(this); m_busySeq->setSequence(KPixmapSequence("process-working", KIconLoader::SizeSmallMedium)); m_busySeq->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_busySeq->setWidget(ui->packageView->viewport()); // hide distro Upgrade container and line ui->distroUpgrade->hide(); KConfig config("apper"); KConfigGroup viewGroup(&config, "UpdateView"); // versions ui->packageView->header()->setSectionHidden(PackageModel::VersionCol, true); m_showPackageVersion = new QAction(i18n("Show Versions"), this); m_showPackageVersion->setCheckable(true); connect(m_showPackageVersion, &QAction::toggled, this, &Updater::showVersions); m_showPackageVersion->setChecked(viewGroup.readEntry("ShowVersions", true)); // versions ui->packageView->header()->setSectionHidden(PackageModel::CurrentVersionCol, true); m_showPackageCurrentVersion = new QAction(i18n("Show Current Versions"), this); m_showPackageCurrentVersion->setCheckable(true); connect(m_showPackageCurrentVersion, &QAction::toggled, this, &Updater::showCurrentVersions); m_showPackageCurrentVersion->setChecked(viewGroup.readEntry("ShowCurrentVersions", false)); // Arch ui->packageView->header()->setSectionHidden(PackageModel::ArchCol, true); m_showPackageArch = new QAction(i18n("Show Architectures"), this); m_showPackageArch->setCheckable(true); connect(m_showPackageArch, &QAction::toggled, this, &Updater::showArchs); m_showPackageArch->setChecked(viewGroup.readEntry("ShowArchs", false)); // Origin ui->packageView->header()->setSectionHidden(PackageModel::OriginCol, true); m_showPackageOrigin = new QAction(i18n("Show Origins"), this); m_showPackageOrigin->setCheckable(true); connect(m_showPackageOrigin, &QAction::toggled, this, &Updater::showOrigins); m_showPackageOrigin->setChecked(viewGroup.readEntry("ShowOrigins", false)); // Sizes ui->packageView->header()->setSectionHidden(PackageModel::SizeCol, true); m_showPackageSize = new QAction(i18n("Show Sizes"), this); m_showPackageSize->setCheckable(true); connect(m_showPackageSize, &QAction::toggled, this, &Updater::showSizes); m_showPackageSize->setChecked(viewGroup.readEntry("ShowSizes", true)); } Updater::~Updater() { delete ui; } void Updater::showVersions(bool enabled) { KConfig config("apper"); KConfigGroup viewGroup(&config, "UpdateView"); viewGroup.writeEntry("ShowVersions", enabled); ui->packageView->header()->setSectionHidden(PackageModel::VersionCol, !enabled); } void Updater::showCurrentVersions(bool enabled) { KConfig config("apper"); KConfigGroup viewGroup(&config, "UpdateView"); viewGroup.writeEntry("ShowCurrentVersions", enabled); ui->packageView->header()->setSectionHidden(PackageModel::CurrentVersionCol, !enabled); if (enabled) { m_updatesModel->fetchCurrentVersions(); } } void Updater::showArchs(bool enabled) { KConfig config("apper"); KConfigGroup viewGroup(&config, "UpdateView"); viewGroup.writeEntry("ShowArchs", enabled); ui->packageView->header()->setSectionHidden(PackageModel::ArchCol, !enabled); } void Updater::showOrigins(bool enabled) { KConfig config("apper"); KConfigGroup viewGroup(&config, "UpdateView"); viewGroup.writeEntry("showOrigins", enabled); ui->packageView->header()->setSectionHidden(PackageModel::OriginCol, !enabled); } void Updater::showSizes(bool enabled) { KConfig config("apper"); KConfigGroup viewGroup(&config, "UpdateView"); viewGroup.writeEntry("ShowSizes", enabled); ui->packageView->header()->setSectionHidden(PackageModel::SizeCol, !enabled); if (enabled) { m_updatesModel->fetchSizes(); } } void Updater::updatePallete() { QPalette pal; pal.setColor(QPalette::Window, pal.base().color()); pal.setColor(QPalette::WindowText, pal.text().color()); ui->backgroundFrame->setPalette(pal); } void Updater::on_packageView_clicked(const QModelIndex &index) { QString pkgId = index.data(PackageModel::IdRole).toString(); auto pkgInfo = index.data(PackageModel::InfoRole).value(); ui->updateDetails->setPackage(pkgId, pkgInfo); } //TODO: We should add some kind of configuration to let users show unstable distributions //That way, by default, users only see stable ones. void Updater::distroUpgrade(PackageKit::Transaction::DistroUpgrade type, const QString &name, const QString &description) { // TODO name should be used to do a upgrade to a different type Q_UNUSED(name) if (type != Transaction::DistroUpgradeStable) { // Ignore unstable distros upgrades for now return; } ui->distroUpgrade->setName(description); ui->distroUpgrade->animatedShow(); } bool Updater::hasChanges() const { return m_updatesModel->hasChanges(); } void Updater::checkEnableUpdateButton() { + qCDebug(APPER) << "updates has changes" << hasChanges(); emit changed(hasChanges()); int selectedSize = m_updatesModel->selectedPackagesToInstall().size(); int updatesSize = m_updatesModel->rowCount(); if (selectedSize == 0) { m_header->setCheckState(Qt::Unchecked); } else if (selectedSize == updatesSize) { m_header->setCheckState(Qt::Checked); } else { m_header->setCheckState(Qt::PartiallyChecked); } unsigned long dwSize = m_updatesModel->downloadSize(); if (dwSize) { emit downloadSize(i18n("Estimated download size: %1", KFormat().formatByteSize(dwSize))); } else { emit downloadSize(QString()); } // if we don't have any upates let's disable the button m_header->setCheckBoxVisible(m_updatesModel->rowCount() != 0); ui->packageView->setHeaderHidden(m_updatesModel->rowCount() == 0); } void Updater::load() { // set focus on the updates view ui->packageView->setFocus(Qt::OtherFocusReason); emit downloadSize(QString()); // If the model already has some packages // let's just clear the selection if (m_updatesModel->rowCount()) { m_updatesModel->setAllChecked(false); } else { getUpdates(); } } void Updater::getUpdatesFinished() { m_updatesT = 0; m_updatesModel->clearSelectedNotPresent(); checkEnableUpdateButton(); if (m_updatesModel->rowCount() == 0) { // Set the info page ui->stackedWidget->setCurrentIndex(1); uint lastTime = Daemon::global()->getTimeSinceAction(Transaction::RoleRefreshCache); ui->titleL->setText(PkStrings::lastCacheRefreshTitle(lastTime)); ui->descriptionL->setText(PkStrings::lastCacheRefreshSubTitle(lastTime)); ui->iconL->setPixmap(QIcon::fromTheme(PkIcons::lastCacheRefreshIconName(lastTime)).pixmap(128, 128)); } } QStringList Updater::packagesToUpdate() const { return m_updatesModel->selectedPackagesToInstall(); } void Updater::getUpdates() { if (m_updatesT) { // There is a getUpdates running ignore this call return; } if (ui->stackedWidget->currentIndex() != 0) { ui->stackedWidget->setCurrentIndex(0); } // clears the model ui->packageView->setHeaderHidden(true); m_updatesModel->clear(); ui->updateDetails->hide(); m_updatesT = Daemon::getUpdates(); connect(m_updatesT, &Transaction::package, m_updatesModel, &PackageModel::addSelectedPackage); connect(m_updatesT, &Transaction::errorCode, this, &Updater::errorCode); connect(m_updatesT, &Transaction::finished, m_busySeq, &KPixmapSequenceOverlayPainter::stop); connect(m_updatesT, &Transaction::finished, m_updatesModel, &PackageModel::finished); // This is required to estimate download size connect(m_updatesT, &Transaction::finished, m_updatesModel, &PackageModel::fetchSizes); if (m_showPackageCurrentVersion->isChecked()) { connect(m_updatesT, &Transaction::finished, m_updatesModel, &PackageModel::fetchCurrentVersions); } connect(m_updatesT, &Transaction::finished, this, &Updater::getUpdatesFinished); m_busySeq->start(); // Hide the distribution upgrade information ui->distroUpgrade->animatedHide(); if (m_roles & Transaction::RoleGetDistroUpgrades) { // Check for distribution Upgrades Transaction *t = Daemon::getDistroUpgrades(); connect(m_updatesT, &Transaction::distroUpgrade, this, &Updater::distroUpgrade); connect(m_updatesT, &Transaction::finished, t, &Transaction::deleteLater); } } void Updater::on_packageView_customContextMenuRequested(const QPoint &pos) { auto menu = new QMenu(this); menu->addAction(m_showPackageVersion); menu->addAction(m_showPackageCurrentVersion); menu->addAction(m_showPackageArch); menu->addAction(m_showPackageOrigin); menu->addAction(m_showPackageSize); QAction *action; action = menu->addAction(i18n("Check for new updates")); action->setIcon(QIcon::fromTheme("view-refresh")); connect(action, &QAction::triggered, this, &Updater::refreshCache); - - action = menu->addAction(i18n("Update")); - action->setIcon(QIcon::fromTheme("update")); - connect(action, &QAction::triggered, this, &Updater::installUpdates); - connect(this, &Updater::changed, action, &QAction::setEnabled); - menu->exec(ui->packageView->viewport()->mapToGlobal(pos)); delete menu; } void Updater::errorCode(PackageKit::Transaction::Error error, const QString &details) { KMessageBox::detailedSorry(this, PkStrings::errorMessage(error), details, PkStrings::error(error), KMessageBox::Notify); } diff --git a/Apper/Updater/Updater.h b/Apper/Updater/Updater.h index 4570b39..6fd4846 100644 --- a/Apper/Updater/Updater.h +++ b/Apper/Updater/Updater.h @@ -1,96 +1,96 @@ /*************************************************************************** * Copyright (C) 2008-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. * ***************************************************************************/ #ifndef UPDATER_H #define UPDATER_H #include #include #include #include using namespace PackageKit; namespace Ui { class Updater; } class PackageModel; class ApplicationsDelegate; class CheckableHeader; class Updater : public QWidget { Q_OBJECT public: Updater(Transaction::Roles roles, QWidget *parent); ~Updater(); bool hasChanges() const; QStringList packagesToUpdate() const; Q_SIGNALS: void installUpdates(); void changed(bool); void refreshCache(); void downloadSize(const QString &message); public Q_SLOTS: void load(); void getUpdates(); private Q_SLOTS: void on_packageView_customContextMenuRequested(const QPoint &pos); void distroUpgrade(PackageKit::Transaction::DistroUpgrade type, const QString &name, const QString &description); void getUpdatesFinished(); void on_packageView_clicked(const QModelIndex &index); void checkEnableUpdateButton(); void errorCode(PackageKit::Transaction::Error error, const QString &details); void showVersions(bool enabled); void showCurrentVersions(bool enabled); void showArchs(bool enabled); void showOrigins(bool enabled); void showSizes(bool enabled); void updatePallete(); private: Ui::Updater *ui; Transaction::Roles m_roles; - bool m_selected; + bool m_selected = true; PackageModel *m_updatesModel; ApplicationsDelegate *m_delegate; CheckableHeader *m_header; QAction *m_showPackageVersion; QAction *m_showPackageCurrentVersion; QAction *m_showPackageArch; QAction *m_showPackageOrigin; QAction *m_showPackageSize; - Transaction *m_updatesT; + Transaction *m_updatesT = nullptr; KPixmapSequenceOverlayPainter *m_busySeq; }; #endif diff --git a/Apper/main.cpp b/Apper/main.cpp index 83d867f..9af8ca2 100644 --- a/Apper/main.cpp +++ b/Apper/main.cpp @@ -1,56 +1,57 @@ /*************************************************************************** * 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 "Apper.h" #include #include #include #include #include #include #include #include #include int main(int argc, char **argv) { QApplication::setWindowIcon(QIcon::fromTheme("system-software-install")); Apper app(argc, argv); KLocalizedString::setApplicationDomain("apper"); - KAboutData aboutData("apper", - "apper", // DO NOT change this catalog unless you know it will not break translations! - APPER_VERSION, - i18n("Apper is an application to get and manage software"), - KAboutLicense::LicenseKey::GPL); - aboutData.addAuthor(i18n("Daniel Nicoletti"), QString(), "dantti12@gmail.com", "http://dantti.wordpress.com"); + KAboutData aboutData("apper", // DO NOT change this catalog unless you know it will not break translations! + i18n("Apper"), + APPER_VERSION, + i18n("Apper is an application to get and manage software"), + KAboutLicense::LicenseKey::GPL); + aboutData.addAuthor(i18n("(C) 2008-2018 Daniel Nicoletti"), QString(), "dantti12@gmail.com", "http://dantti.wordpress.com"); aboutData.addCredit(i18n("Adrien Bustany"), i18n("libpackagekit-qt and other stuff"), "@"); + aboutData.addAuthor(i18n("Matthias Klumpp"), QString(), QStringLiteral("matthias@tenstral.net")); KAboutData::setApplicationData(aboutData); app.activate(app.arguments(), QDir::currentPath()); return app.exec(); } diff --git a/libapper/PackageModel.cpp b/libapper/PackageModel.cpp index f668c7e..2047f67 100644 --- a/libapper/PackageModel.cpp +++ b/libapper/PackageModel.cpp @@ -1,893 +1,907 @@ /*************************************************************************** * Copyright (C) 2008-2018 by Daniel Nicoletti * * dantti12@gmail.com * * Copyright (C) 2008 by Trever Fischer * * wm161@wm161.net * * * * 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 #include "PackageModel.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_APPSTREAM #include #endif #define ICON_SIZE 22 #define OVERLAY_SIZE 16 using namespace PackageKit; PackageModel::PackageModel(QObject *parent) : QAbstractItemModel(parent), m_finished(false), m_checkable(false), m_fetchSizesTransaction(0), m_fetchInstalledVersionsTransaction(0) { m_installedEmblem = PkIcons::getIcon("dialog-ok-apply", QString()).pixmap(16, 16); m_roles[SortRole] = "rSort"; m_roles[NameRole] = "rName"; m_roles[SummaryRole] = "rSummary"; m_roles[VersionRole] = "rVersion"; m_roles[ArchRole] = "rArch"; m_roles[IconRole] = "rIcon"; m_roles[IdRole] = "rId"; m_roles[CheckStateRole] = "rChecked"; m_roles[InfoRole] = "rInfo"; m_roles[ApplicationId] = "rApplicationId"; m_roles[IsPackageRole] = "rIsPackageRole"; m_roles[PackageName] = "rPackageName"; m_roles[InfoIconRole] = "rInfoIcon"; } void PackageModel::addSelectedPackagesFromModel(PackageModel *model) { const QList packages = model->internalSelectedPackages(); for (const InternalPackage &package : packages) { addPackage(package.info, package.packageID, package.summary, true); } finished(); } void PackageModel::addNotSelectedPackage(Transaction::Info info, const QString &packageID, const QString &summary) { addPackage(info, packageID, summary); } void PackageModel::addPackage(Transaction::Info info, const QString &packageID, const QString &summary, bool selected) { if (m_finished) { qDebug() << Q_FUNC_INFO << "we are finished calling clear"; clear(); } switch(info) { case Transaction::InfoBlocked: case Transaction::InfoFinished: case Transaction::InfoCleanup: return; default: break; } #ifdef HAVE_APPSTREAM QList applications; if (!m_checkable) { const QString packageName = Transaction::packageName(packageID); // applications = AppStream::instance()->applications(packageName); for (const AppStream::Application &app : applications) { InternalPackage iPackage; iPackage.info = info; iPackage.packageID = packageID; iPackage.pkgName = packageName; iPackage.version = Transaction::packageVersion(packageID); iPackage.arch = Transaction::packageArch(packageID); iPackage.repo = Transaction::packageData(packageID); iPackage.isPackage = false; if (app.name.isEmpty()) { iPackage.displayName = packageName; } else { iPackage.displayName = app.name; } if (app.summary.isEmpty()) { iPackage.summary = summary; } else { iPackage.summary = app.summary; } iPackage.icon = app.icon_url; iPackage.appId = app.id; iPackage.size = 0; if (selected) { checkPackage(iPackage, false); } m_packages.append(iPackage); } } if (applications.isEmpty()) { #endif //HAVE_APPSTREAM InternalPackage iPackage; iPackage.info = info; iPackage.packageID = packageID; iPackage.pkgName = Transaction::packageName(packageID); iPackage.displayName = iPackage.pkgName; iPackage.version = Transaction::packageVersion(packageID); iPackage.arch = Transaction::packageArch(packageID); iPackage.repo = Transaction::packageData(packageID); iPackage.summary = summary; #ifdef HAVE_APPSTREAM // iPackage.icon = AppStream::instance()->genericIcon(iPackage.pkgName); if (m_checkable) { // in case of updates model only check if it's an app // applications = AppStream::instance()->applications(iPackage.pkgName); if (!applications.isEmpty()) { iPackage.isPackage = false; } } #endif // HAVE_APPSTREAM if (selected) { checkPackage(iPackage, false); } m_packages.append(iPackage); #ifdef HAVE_APPSTREAM } #endif // HAVE_APPSTREAM } void PackageModel::addSelectedPackage(Transaction::Info info, const QString &packageID, const QString &summary) { addPackage(info, packageID, summary, true); } QVariant PackageModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant ret; if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case NameCol: if (m_checkable) { ret = PkStrings::packageQuantity(true, m_packages.size(), m_checkedPackages.size()); } else { ret = i18n("Name"); } break; case VersionCol: ret = i18n("Version"); break; case CurrentVersionCol: ret = i18n("Installed Version"); break; case ArchCol: ret = i18n("Arch"); break; case OriginCol: ret = i18n("Origin"); break; case SizeCol: ret = i18n("Size"); break; case ActionCol: ret = i18n("Action"); break; } } return ret; } int PackageModel::rowCount(const QModelIndex &parent) const { if (parent.isValid() || !m_finished) { return 0; } return m_packages.size(); } QModelIndex PackageModel::index(int row, int column, const QModelIndex &parent) const { // kDebug() << parent.isValid() << m_packageCount << row << column; // Check to see if the index isn't out of list if (!parent.isValid() && m_packages.size() > row) { return createIndex(row, column); } return QModelIndex(); } QModelIndex PackageModel::parent(const QModelIndex &index) const { Q_UNUSED(index) return QModelIndex(); } QHash PackageModel::roleNames() const { return m_roles; } QVariant PackageModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const InternalPackage &package = m_packages[index.row()]; if (index.column() == NameCol) { switch (role) { case Qt::CheckStateRole: if (!m_checkable) { return QVariant(); } if (containsChecked(package.packageID)) { return Qt::Checked; } return Qt::Unchecked; case CheckStateRole: if (containsChecked(package.packageID)) { return Qt::Checked; } return Qt::Unchecked; case IsPackageRole: return package.isPackage; case Qt::DisplayRole: return package.displayName; case Qt::DecorationRole: { QPixmap icon = QPixmap(44, ICON_SIZE); icon.fill(Qt::transparent); if (!package.icon.isNull()) { QPixmap pixmap; if (package.icon.startsWith("/")) { pixmap = QPixmap(); pixmap.load(package.icon); pixmap = pixmap.scaledToHeight(ICON_SIZE); } else { pixmap = KIconLoader::global()->loadIcon(package.icon, KIconLoader::NoGroup, ICON_SIZE, KIconLoader::DefaultState, QStringList(), 0L, true); } if (!pixmap.isNull()) { QPainter painter(&icon); painter.drawPixmap(QPoint(2, 0), pixmap); } } if (package.info == Transaction::InfoInstalled || package.info == Transaction::InfoCollectionInstalled) { QPainter painter(&icon); QPoint startPoint; // bottom right corner startPoint = QPoint(44 - OVERLAY_SIZE, 4); painter.drawPixmap(startPoint, m_installedEmblem); } else if (m_checkable) { QIcon emblemIcon = PkIcons::packageIcon(package.info); QPainter painter(&icon); QPoint startPoint; // bottom right corner startPoint = QPoint(44 - OVERLAY_SIZE, 4); painter.drawPixmap(startPoint, emblemIcon.pixmap(OVERLAY_SIZE, OVERLAY_SIZE)); } return icon; } case PackageName: return package.pkgName; case Qt::ToolTipRole: if (m_checkable) { return PkStrings::info(package.info); } else { return i18n("Version: %1\nArchitecture: %2", package.version, package.arch); } } } else if (role == Qt::DisplayRole) { if (index.column() == VersionCol) { return package.version; } else if (index.column() == CurrentVersionCol) { return package.currentVersion; } else if (index.column() == ArchCol) { return package.arch; } else if (index.column() == OriginCol) { return package.repo; } else if (index.column() == SizeCol) { KFormat f; return package.size ? f.formatByteSize(package.size) : QString(); } } else if (index.column() == SizeCol && role == Qt::TextAlignmentRole) { return static_cast(Qt::AlignRight | Qt::AlignVCenter); } switch (role) { case IconRole: return package.icon; case SortRole: return QString(package.displayName % QLatin1Char(' ') % package.version % QLatin1Char(' ') % package.arch); case CheckStateRole: if (containsChecked(package.packageID)) { return Qt::Checked; } return Qt::Unchecked; case IdRole: return package.packageID; case NameRole: return package.displayName; case SummaryRole: return package.summary; case VersionRole: return package.version; case ArchRole: return package.arch; case OriginCol: return package.repo; case InfoRole: return qVariantFromValue(package.info); case KCategorizedSortFilterProxyModel::CategoryDisplayRole: if (package.info == Transaction::InfoInstalled || package.info == Transaction::InfoCollectionInstalled) { return i18n("To be Removed"); } else { return i18n("To be Installed"); } case KCategorizedSortFilterProxyModel::CategorySortRole: // USING 0 here seems to let things unsorted return package.isPackage ? 1 : 0; // Packages comes after applications case ApplicationId: return package.appId; case InfoIconRole: return PkIcons::packageIcon(package.info); default: return QVariant(); } return QVariant(); } bool PackageModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == Qt::CheckStateRole && m_packages.size() > index.row()) { if (value.toBool()) { checkPackage(m_packages[index.row()]); } else { uncheckPackage(m_packages[index.row()].packageID); } emit changed(!m_checkedPackages.isEmpty()); return true; } return false; } Qt::ItemFlags PackageModel::flags(const QModelIndex &index) const { if (index.column() == NameCol) { return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | QAbstractItemModel::flags(index); } return QAbstractItemModel::flags(index); } int PackageModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); if (m_checkable) { // when the model is checkable the action column is not shown return ActionCol; } else { return ActionCol + 1; } } void PackageModel::removePackage(const QString &packageID) { int i = 0; while (i < m_packages.size()) { InternalPackage iPackage = m_packages[i]; if (iPackage.packageID == packageID && iPackage.info != Transaction::InfoUntrusted) { beginRemoveRows(QModelIndex(), i, i); m_packages.remove(i); endRemoveRows(); // since we removed one entry we don't // need to increase the counter continue; } ++i; } } +void PackageModel::checkAll() +{ + m_checkedPackages.clear(); + for (const InternalPackage &package : qAsConst(m_packages)) { + checkPackage(package, false); + } + emit dataChanged(createIndex(0, 0), + createIndex(m_packages.size(), 0)); + emit changed(!m_checkedPackages.isEmpty()); +} + void PackageModel::clear() { qDebug() << Q_FUNC_INFO; beginRemoveRows(QModelIndex(), 0, m_packages.size()); m_finished = false; m_packages.clear(); m_fetchSizesTransaction = 0; m_fetchInstalledVersionsTransaction = 0; if (m_getUpdatesTransaction) { m_getUpdatesTransaction->disconnect(this); m_getUpdatesTransaction->cancel(); } endRemoveRows(); } void PackageModel::clearSelectedNotPresent() { auto it = m_checkedPackages.begin(); while (it != m_checkedPackages.end()) { const InternalPackage &package = it.value(); bool notFound = true; for (const InternalPackage &iPackage : qAsConst(m_packages)) { if (iPackage.packageID == package.packageID) { notFound = false; break; } } if (notFound) { // Uncheck the package If it's not in the model m_checkedPackages.erase(it); uncheckPackageLogic(package.packageID); } else { ++it; } } } bool PackageModel::checkable() const { return m_checkable; } void PackageModel::uncheckInstalledPackages() { auto it = m_checkedPackages.begin(); while (it != m_checkedPackages.end()) { const InternalPackage &package = it.value(); if (package.info == Transaction::InfoInstalled || package.info == Transaction::InfoCollectionInstalled) { + const QString pkgId = it.key(); it = m_checkedPackages.erase(it); - uncheckPackageLogic(it.key(), true); + uncheckPackageLogic(pkgId, true); } else { ++it; } } } void PackageModel::uncheckAvailablePackages() { auto it = m_checkedPackages.begin(); while (it != m_checkedPackages.end()) { const InternalPackage &package = it.value(); if (package.info == Transaction::InfoAvailable || package.info == Transaction::InfoCollectionAvailable) { + const QString pkgId = it.key(); it = m_checkedPackages.erase(it); - uncheckPackageLogic(it.key(), true); + uncheckPackageLogic(pkgId, true); } else { ++it; } } } void PackageModel::finished() { auto trans = qobject_cast(sender()); qDebug() << Q_FUNC_INFO << trans << sender(); if (trans /*== m_getUpdatesTransaction*/) { // m_getUpdatesTransaction = 0; // When pkd dies this method is called twice // pk-qt2 bug.. disconnect(trans, &Transaction::finished, this, &PackageModel::finished); } // The whole structure is about to change if (!m_packages.isEmpty()) { beginInsertRows(QModelIndex(), 0, m_packages.size() - 1); m_finished = true; endInsertRows(); } emit changed(!m_checkedPackages.isEmpty()); } void PackageModel::fetchSizes() { if (m_fetchSizesTransaction) { return; } // get package size QStringList pkgs; for (const InternalPackage &p : qAsConst(m_packages)) { pkgs << p.packageID; } if (!pkgs.isEmpty()) { m_fetchSizesTransaction = Daemon::getDetails(pkgs); connect(m_fetchSizesTransaction, &Transaction::details, this, &PackageModel::updateSize); connect(m_fetchSizesTransaction, &Transaction::finished, this, &PackageModel::fetchSizesFinished); } } void PackageModel::fetchSizesFinished() { auto trans = qobject_cast(sender()); if (trans) { // When pkd dies this method is called twice // pk-qt2 bug.. disconnect(trans, &Transaction::finished, this, &PackageModel::fetchSizesFinished); } // emit this after all is changed otherwise on large models it will // be hell slow... emit dataChanged(createIndex(0, SizeCol), createIndex(m_packages.size(), SizeCol)); emit changed(!m_checkedPackages.isEmpty()); } void PackageModel::updateSize(const PackageKit::Details &details) { // if size is 0 don't waste time looking for the package qulonglong size = details.size(); if (size == 0) { return; } for (int i = 0; i < m_packages.size(); ++i) { const QString packageId = details.packageId(); if (packageId == m_packages[i].packageID) { m_packages[i].size = size; if (m_checkable) { // updates the checked packages as well if (m_checkedPackages.contains(packageId)) { // Avoid checking packages that aren't checked m_checkedPackages[packageId].size = size; } break; } #ifdef HAVE_APPSTREAM if (m_checkable) { // checkable models don't have duplicated package ids // so don't waste time scanning all list break; } #else // Without AppStream we don't have duplicated package ids break; #endif // HAVE_APPSTREAM } } } void PackageModel::fetchCurrentVersions() { if (m_fetchInstalledVersionsTransaction) { return; } // get package current version QStringList pkgs; for (const InternalPackage &p : qAsConst(m_packages)) { pkgs << p.pkgName; } if (!pkgs.isEmpty()) { m_fetchInstalledVersionsTransaction = Daemon::resolve(pkgs, Transaction::FilterInstalled);; connect(m_fetchInstalledVersionsTransaction, &Transaction::package, this, &PackageModel::updateCurrentVersion); connect(m_fetchInstalledVersionsTransaction, &Transaction::finished, this, &PackageModel::fetchCurrentVersionsFinished); } } void PackageModel::fetchCurrentVersionsFinished() { auto trans = qobject_cast(sender()); if (trans) { // When pkd dies this method is called twice // pk-qt2 bug.. disconnect(trans, &Transaction::finished, this, &PackageModel::fetchCurrentVersionsFinished); } // emit this after all is changed otherwise on large models it will // be hell slow... emit dataChanged(createIndex(0, CurrentVersionCol), createIndex(m_packages.size(), CurrentVersionCol)); emit changed(!m_checkedPackages.isEmpty()); } void PackageModel::updateCurrentVersion(Transaction::Info info, const QString &packageID, const QString &summary) { Q_UNUSED(info) Q_UNUSED(summary) // if current version is empty don't waste time looking if (!Transaction::packageVersion(packageID).isEmpty()) { for (int i = 0; i < m_packages.size(); ++i) { if (Transaction::packageName(packageID) == m_packages[i].pkgName && Transaction::packageArch(packageID) == m_packages[i].arch) { m_packages[i].currentVersion = Transaction::packageVersion(packageID); if (m_checkable) { // updates the checked packages as well if (m_checkedPackages.contains(m_packages[i].packageID)) { // Avoid checking packages that aren't checked m_checkedPackages[m_packages[i].packageID].currentVersion = Transaction::packageVersion(packageID); } break; } } } } } void PackageModel::getUpdates(bool fetchCurrentVersions, bool selected) { clear(); m_getUpdatesTransaction = Daemon::getUpdates(); if (selected) { connect(m_getUpdatesTransaction, &Transaction::package, this, &PackageModel::addSelectedPackage); } else { connect(m_getUpdatesTransaction, &Transaction::package, this, &PackageModel::addNotSelectedPackage); } // connect(m_getUpdatesTransaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), // m_busySeq, SLOT(stop())); // connect(m_getUpdatesTransaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), // this, SLOT(finished())); // This is required to estimate download size connect(m_getUpdatesTransaction, &Transaction::finished, this, &PackageModel::fetchSizes); if (fetchCurrentVersions) { connect(m_getUpdatesTransaction, &Transaction::finished, this, &PackageModel::fetchCurrentVersions); } connect(m_getUpdatesTransaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), this, SLOT(getUpdatesFinished())); // get all updates } void PackageModel::toggleSelection(const QString &packageID) { if (containsChecked(packageID)) { uncheckPackage(packageID, true); } else { for (const InternalPackage &package : qAsConst(m_packages)) { if (package.packageID == packageID) { checkPackage(package); break; } } } } QString PackageModel::selectionStateText() const { return headerData(NameCol, Qt::Horizontal).toString(); } bool PackageModel::hasChanges() const { return !m_checkedPackages.isEmpty(); } int PackageModel::countInfo(PackageKit::Transaction::Info info) const { int ret = 0; for (const InternalPackage &package : qAsConst(m_packages)) { if (package.info == info) { ++ret; } } return ret; } void PackageModel::checkPackage(const InternalPackage &package, bool emitDataChanged) { QString pkgId = package.packageID; if (!containsChecked(pkgId)) { m_checkedPackages[pkgId] = package; // A checkable model does not have duplicated entries if (emitDataChanged || !m_checkable || !m_packages.isEmpty()) { // This is a slow operation so in case the user // is unchecking all of the packages there is // no need to emit data changed for every item for (int i = 0; i < m_packages.size(); ++i) { if (m_packages[i].packageID == pkgId) { QModelIndex index = createIndex(i, 0); emit dataChanged(index, index); } } // The model might not be displayed yet if (m_finished) { emit changed(!m_checkedPackages.isEmpty()); } } } } +void PackageModel::uncheckAll() +{ + auto it = m_checkedPackages.begin(); + while (it != m_checkedPackages.end()) { + const QString pkgId = it.key(); + it = m_checkedPackages.erase(it); + uncheckPackageLogic(pkgId, true, false); + } + emit dataChanged(createIndex(0, 0), + createIndex(m_packages.size(), 0)); + emit changed(!m_checkedPackages.isEmpty()); +} + void PackageModel::uncheckPackageDefault(const QString &packageID) { uncheckPackage(packageID); } void PackageModel::uncheckPackage(const QString &packageID, bool forceEmitUnchecked, bool emitDataChanged) { auto it = m_checkedPackages.find(packageID); if (it != m_checkedPackages.end()) { m_checkedPackages.erase(it); uncheckPackageLogic(packageID, forceEmitUnchecked, emitDataChanged); } } void PackageModel::uncheckPackageLogic(const QString &packageID, bool forceEmitUnchecked, bool emitDataChanged) { if (forceEmitUnchecked || sender() == 0) { // The package might be removed by rmSelectedPackage // If we don't copy it the browse model won't uncheck there // right package emit packageUnchecked(packageID); } if (emitDataChanged || !m_checkable) { // This is a slow operation so in case the user // is unchecking all of the packages there is // no need to emit data changed for every item for (int i = 0; i < m_packages.size(); ++i) { if (m_packages[i].packageID == packageID) { QModelIndex index = createIndex(i, 0); emit dataChanged(index, index); } } // The model might not be displayed yet if (m_finished) { emit changed(!m_checkedPackages.isEmpty()); } } } QList PackageModel::internalSelectedPackages() const { QList ret; QHash::const_iterator i = m_checkedPackages.constBegin(); while (i != m_checkedPackages.constEnd()) { ret << i.value(); ++i; } return ret; } bool PackageModel::containsChecked(const QString &pid) const { return m_checkedPackages.contains(pid); } void PackageModel::setAllChecked(bool checked) { if (checked) { - m_checkedPackages.clear(); - for (const InternalPackage &package : qAsConst(m_packages)) { - checkPackage(package, false); - } - emit dataChanged(createIndex(0, 0), - createIndex(m_packages.size(), 0)); + checkAll(); } else { - auto it = m_checkedPackages.begin(); - while (it != m_checkedPackages.end()) { - uncheckPackageLogic(it.key(), true, false); - it = m_checkedPackages.erase(it); - } - emit dataChanged(createIndex(0, 0), - createIndex(m_packages.size(), 0)); + uncheckAll(); } - emit changed(!m_checkedPackages.isEmpty()); } QStringList PackageModel::selectedPackagesToInstall() const { QStringList list; for (const InternalPackage &package : qAsConst(m_checkedPackages)) { if (package.info != Transaction::InfoInstalled && package.info != Transaction::InfoCollectionInstalled) { // append the packages are not installed list << package.packageID; } } return list; } QStringList PackageModel::selectedPackagesToRemove() const { QStringList list; for (const InternalPackage &package : qAsConst(m_checkedPackages)) { if (package.info == Transaction::InfoInstalled || package.info == Transaction::InfoCollectionInstalled) { // check what packages are installed and marked to be removed list << package.packageID; } } return list; } QStringList PackageModel::packagesWithInfo(Transaction::Info info) const { QStringList list; for (const InternalPackage &package : qAsConst(m_packages)) { if (package.info == info) { // Append to the list if the package matches the info value list << package.packageID; } } return list; } QStringList PackageModel::packageIDs() const { QStringList list; for (const InternalPackage &package : qAsConst(m_packages)) { list << package.packageID; } return list; } unsigned long PackageModel::downloadSize() const { unsigned long size = 0; for (const InternalPackage &package : qAsConst(m_checkedPackages)) { size += package.size; } return size; } bool PackageModel::allSelected() const { for (const InternalPackage &package : qAsConst(m_packages)) { if (!containsChecked(package.packageID)) { return false; } } return true; } void PackageModel::setCheckable(bool checkable) { m_checkable = checkable; } diff --git a/libapper/PackageModel.h b/libapper/PackageModel.h index 07da89b..b4a609e 100644 --- a/libapper/PackageModel.h +++ b/libapper/PackageModel.h @@ -1,161 +1,163 @@ /*************************************************************************** * Copyright (C) 2008-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. * ***************************************************************************/ #ifndef PACKAGE_MODEL_H #define PACKAGE_MODEL_H #include #include #include #include
class Q_DECL_EXPORT PackageModel : public QAbstractItemModel { Q_OBJECT Q_PROPERTY(bool checkable READ checkable WRITE setCheckable NOTIFY changed) Q_PROPERTY(QString selectionStateText READ selectionStateText NOTIFY changed) public: enum { NameCol = 0, VersionCol, CurrentVersionCol, ArchCol, OriginCol, SizeCol, ActionCol }; enum { SortRole = Qt::UserRole, NameRole, SummaryRole, VersionRole, ArchRole, IconRole, IdRole, CheckStateRole, InfoRole, ApplicationId, IsPackageRole, PackageName, InfoIconRole }; typedef struct { QString displayName; QString pkgName; QString version; QString arch; QString repo; QString packageID; QString summary; PackageKit::Transaction::Info info; QString icon; QString appId; QString currentVersion; bool isPackage = true; double size = 0; } InternalPackage; explicit PackageModel(QObject *parent = 0); Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); Qt::ItemFlags flags(const QModelIndex &index) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; Q_INVOKABLE bool allSelected() const; Q_INVOKABLE QStringList selectedPackagesToInstall() const; Q_INVOKABLE QStringList selectedPackagesToRemove() const; Q_INVOKABLE QStringList packagesWithInfo(PackageKit::Transaction::Info info) const; Q_INVOKABLE QStringList packageIDs() const; unsigned long downloadSize() const; Q_INVOKABLE void clear(); /** * This removes all selected packages that are not in the model */ Q_INVOKABLE void clearSelectedNotPresent(); bool checkable() const; void setCheckable(bool checkable); QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &index) const; virtual QHash roleNames() const override; public Q_SLOTS: void addSelectedPackagesFromModel(PackageModel *model); void addNotSelectedPackage(PackageKit::Transaction::Info info, const QString &packageID, const QString &summary); void addPackage(PackageKit::Transaction::Info info, const QString &packageID, const QString &summary, bool selected = false); void addSelectedPackage(PackageKit::Transaction::Info info, const QString &packageID, const QString &summary); void removePackage(const QString &packageID); + void checkAll(); void setAllChecked(bool checked); void checkPackage(const PackageModel::InternalPackage &package, bool emitDataChanged = true); + void uncheckAll(); void uncheckPackageDefault(const QString &packageID); void uncheckPackage(const QString &packageID, bool forceEmitUnchecked = false, bool emitDataChanged = true); void uncheckPackageLogic(const QString &packageID, bool forceEmitUnchecked = false, bool emitDataChanged = true); bool hasChanges() const; int countInfo(PackageKit::Transaction::Info info) const; void uncheckInstalledPackages(); void uncheckAvailablePackages(); void finished(); void fetchSizes(); void fetchSizesFinished(); void updateSize(const PackageKit::Details &details); void fetchCurrentVersions(); void fetchCurrentVersionsFinished(); void updateCurrentVersion(PackageKit::Transaction::Info info, const QString &packageID, const QString &summary); void getUpdates(bool fetchCurrentVersions, bool selected); void toggleSelection(const QString &packageID); QString selectionStateText() const; Q_SIGNALS: void changed(bool value); void packageUnchecked(const QString &packageID); private: QList internalSelectedPackages() const; bool containsChecked(const QString &pid) const; bool m_finished = true; bool m_checkable; QPixmap m_installedEmblem; QVector m_packages; QHash m_checkedPackages; PackageKit::Transaction *m_getUpdatesTransaction = 0; PackageKit::Transaction *m_fetchSizesTransaction; PackageKit::Transaction *m_fetchInstalledVersionsTransaction; QHash m_roles; }; #endif diff --git a/libapper/Requirements.cpp b/libapper/Requirements.cpp index 22db524..37979e4 100644 --- a/libapper/Requirements.cpp +++ b/libapper/Requirements.cpp @@ -1,273 +1,269 @@ /*************************************************************************** * Copyright (C) 2008-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 "Requirements.h" #include "ui_Requirements.h" #include "PkIcons.h" #include "PackageModel.h" #include "ApplicationSortFilterModel.h" #include #include #include #include #include #include Requirements::Requirements(PackageModel *model, QWidget *parent) : QDialog(parent), m_embed(false), m_shouldShow(true), m_untrustedButton(0), ui(new Ui::Requirements) { setAttribute(Qt::WA_DeleteOnClose); ui->setupUi(this); connect(ui->confirmCB, SIGNAL(toggled(bool)), this, SLOT(on_confirmCB_Toggled(bool))); ApplicationSortFilterModel *proxy = new ApplicationSortFilterModel(this); proxy->setSourceModel(model); ui->packageView->setModel(proxy); ui->packageView->header()->setSectionResizeMode(PackageModel::NameCol, QHeaderView::ResizeToContents); ui->packageView->header()->hideSection(PackageModel::ActionCol); ui->packageView->header()->hideSection(PackageModel::ArchCol); ui->packageView->header()->hideSection(PackageModel::CurrentVersionCol); ui->packageView->header()->hideSection(PackageModel::OriginCol); ui->packageView->header()->hideSection(PackageModel::SizeCol); m_hideAutoConfirm = false; setWindowTitle(i18n("Additional changes")); setWindowIcon(QIcon::fromTheme("dialog-warning")); -// setButtons(KDialog::Ok | KDialog::Cancel | KDialog::Help); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(i18n("Continue")); -// setButtonText(KDialog::Ok, i18n("Continue")); + // restore size setMinimumSize(QSize(600,480)); // setInitialSize(QSize(600,600)); KConfig config("apper"); KConfigGroup requirementsDialog(&config, "requirementsDialog"); // restoreGeometry(requirementsDialog.readEntry("geometry").toByteArray()); // restoreDialogSize(requirementsDialog); - QPushButton *help = ui->buttonBox->button(QDialogButtonBox::Help); - help->setFlat(true); - help->setEnabled(false); - help->setIcon(QIcon::fromTheme("download")); + ui->downloadT->hide(); + ui->downloadI->hide(); + ui->downloadI->setPixmap(QIcon::fromTheme(QLatin1String("download")).pixmap(32, 32)); m_buttonGroup = new QButtonGroup(this); connect(m_buttonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &Requirements::actionClicked); int count = 0; if (int c = model->countInfo(Transaction::InfoRemoving)) { auto button = new QToolButton(this); button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); button->setCheckable(true); button->setAutoRaise(true); button->setIconSize(QSize(32, 32)); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->setText(i18np("1 package to remove", "%1 packages to remove", c)); button->setIcon(PkIcons::actionIcon(Transaction::RoleRemovePackages)); m_buttonGroup->addButton(button, Transaction::InfoRemoving); ui->verticalLayout->insertWidget(count++, button); m_hideAutoConfirm = true; } if (int c = model->countInfo(Transaction::InfoDowngrading)) { auto button = new QToolButton(this); button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); button->setCheckable(true); button->setAutoRaise(true); button->setIconSize(QSize(32, 32)); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->setText(i18np("1 package to downgrade", "%1 packages to downgrade", c)); button->setIcon(PkIcons::actionIcon(Transaction::RoleRepairSystem)); m_buttonGroup->addButton(button, Transaction::InfoDowngrading); ui->verticalLayout->insertWidget(count++, button); m_hideAutoConfirm = true; } if (int c = model->countInfo(Transaction::InfoReinstalling)) { auto button = new QToolButton(this); button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); button->setCheckable(true); button->setAutoRaise(true); button->setIconSize(QSize(32, 32)); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->setText(i18np("1 package to reinstall", "%1 packages to reinstall", c)); button->setIcon(PkIcons::actionIcon(Transaction::RoleRemovePackages)); m_buttonGroup->addButton(button, Transaction::InfoReinstalling); ui->verticalLayout->insertWidget(count++, button); } if (int c = model->countInfo(Transaction::InfoInstalling)) { auto button = new QToolButton(this); button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); button->setCheckable(true); button->setAutoRaise(true); button->setIconSize(QSize(32, 32)); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->setText(i18np("1 package to install", "%1 packages to install", c)); button->setIcon(PkIcons::actionIcon(Transaction::RoleInstallPackages)); m_buttonGroup->addButton(button, Transaction::InfoInstalling); ui->verticalLayout->insertWidget(count++, button); } if (int c = model->countInfo(Transaction::InfoUpdating)) { auto button = new QToolButton(this); button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); button->setCheckable(true); button->setAutoRaise(true); button->setIconSize(QSize(32, 32)); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->setText(i18np("1 package to update", "%1 packages to update", c)); button->setIcon(PkIcons::actionIcon(Transaction::RoleUpdatePackages)); m_buttonGroup->addButton(button, Transaction::InfoUpdating); ui->verticalLayout->insertWidget(count++, button); } if (int c = model->countInfo(Transaction::InfoUntrusted)) { m_untrustedButton = new QToolButton(this); m_untrustedButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); m_untrustedButton->setCheckable(true); m_untrustedButton->setAutoRaise(true); m_untrustedButton->setIconSize(QSize(32, 32)); m_untrustedButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); m_untrustedButton->setText(i18np("1 untrusted package", "%1 untrusted packages", c)); m_untrustedButton->setIcon(QIcon::fromTheme("security-low")); m_untrustedButton->setVisible(false); ui->verticalLayout->insertWidget(count++, m_untrustedButton); } if (!m_buttonGroup->buttons().isEmpty()) { m_buttonGroup->buttons().first()->click(); if (m_hideAutoConfirm) { ui->confirmCB->setVisible(false); } else { // if the confirmCB is visible means that we can skip this // dialog, but only if the user previusly set so ui->confirmCB->setChecked(requirementsDialog.readEntry("autoConfirm", false)); } } else if (m_untrustedButton) { showUntrustedButton(); } else { // set this as false so the dialog is not shown m_shouldShow = false; } } Requirements::~Requirements() { KConfig config("apper"); KConfigGroup requirementsDialog(&config, "requirementsDialog"); requirementsDialog.writeEntry("geometry", saveGeometry()); delete ui; } bool Requirements::embedded() const { return m_embed; } void Requirements::setEmbedded(bool embedded) { m_embed = embedded; ui->label->setVisible(!embedded); } void Requirements::setDownloadSizeRemaining(qulonglong size) { - QPushButton *help = ui->buttonBox->button(QDialogButtonBox::Help); if (size) { - KFormat f; - QString text; - text = i18nc("how many bytes are required for download", - "Need to get %1 of archives", - f.formatByteSize(size)); - help->setText(text); - help->setToolTip(text); - help->show(); + QString text = i18nc("how many bytes are required for download", + "Need to get %1 of archives", + KFormat().formatByteSize(size)); + ui->downloadT->setText(text); + ui->downloadT->show(); + ui->downloadI->show(); } else { - help->hide(); + ui->downloadT->hide(); + ui->downloadI->hide(); } } bool Requirements::trusted() const { // There are untrusted packages if the button was created... return !m_untrustedButton; } bool Requirements::shouldShow() const { return (m_shouldShow && !ui->confirmCB->isChecked()); } void Requirements::slotButtonClicked(int) { // FIXME // if (button == KDialog::Ok && // m_untrustedButton && // !m_untrustedButton->isVisible()) { // showUntrustedButton(); // } else { // KDialog::slotButtonClicked(button); // } } void Requirements::on_confirmCB_Toggled(bool checked) { KConfig config("apper"); KConfigGroup requirementsDialog(&config, "requirementsDialog"); if (!m_hideAutoConfirm) { requirementsDialog.writeEntry("autoConfirm", checked); } config.sync(); } void Requirements::actionClicked(int type) { auto proxy = qobject_cast(ui->packageView->model()); proxy->setInfoFilter(static_cast(type)); } void Requirements::showUntrustedButton() { // Clear the other buttons qDeleteAll(m_buttonGroup->buttons()); // Hide the auto confirm button since we will be showing this dialog anyway ui->confirmCB->setVisible(false); ui->label->setText(i18n("You are about to install unsigned packages that can compromise your system, " "as it is impossible to verify if the software came from a trusted source.")); m_untrustedButton->setVisible(true); m_buttonGroup->addButton(m_untrustedButton, Transaction::InfoUntrusted); m_untrustedButton->click(); } #include "moc_Requirements.cpp" diff --git a/libapper/Requirements.ui b/libapper/Requirements.ui index 1072a55..3c14886 100644 --- a/libapper/Requirements.ui +++ b/libapper/Requirements.ui @@ -1,149 +1,170 @@ Requirements 0 0 497 359 - + + + + + + 0 + 0 + + + + + 11 + + + + Additional changes are required to complete the task + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + 0 0 200 0 Qt::NoFocus Qt::ScrollBarAlwaysOff QAbstractItemView::NoEditTriggers false true QAbstractItemView::NoSelection false false false 50 - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - Do not confirm when installing or updating additional packages - - - - - 0 - 0 - - - - - 11 - - - - Additional changes are required to complete the task - - - true - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok - - + + + + + icon + + + + + + + download size + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + confirmCB + buttonBox accepted() Requirements accept() 210 336 440 292 buttonBox rejected() Requirements reject() 427 332 497 323