diff --git a/CMakeLists.txt b/CMakeLists.txt index 6719bdc8..66a95252 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,36 +1,36 @@ project(muon) set(PROJECT_VERSION "5.8.0") set(PROJECT_VERSION_MAJOR 5) cmake_minimum_required(VERSION 2.8.12) find_package(ECM REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) -find_package(Qt5 5.6.0 REQUIRED CONFIG COMPONENTS Widgets Concurrent) +find_package(Qt5 5.7.0 REQUIRED CONFIG COMPONENTS Widgets Concurrent) include(KDEInstallDirs) include(KDECMakeSettings NO_POLICY_SCOPE) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ECMMarkAsTest) include(GenerateExportHeader) find_package(KF5 REQUIRED KIO DBusAddons I18n IconThemes XmlGui) find_package(QApt 3.0.2 REQUIRED) find_package(DebconfKDE 1.0.0 REQUIRED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(MUON_VERSION ${PROJECT_VERSION}) configure_file(MuonVersion.h.in MuonVersion.h) add_subdirectory(src) set_package_properties(QApt PROPERTIES DESCRIPTION "Qt wrapper around the libapt-pkg library" PURPOSE "Used to support apt-based distribution systems" TYPE REQUIRED) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/muonapt/QAptActions.cpp b/src/muonapt/QAptActions.cpp index 7158b66b..a7d899c1 100644 --- a/src/muonapt/QAptActions.cpp +++ b/src/muonapt/QAptActions.cpp @@ -1,552 +1,558 @@ /*************************************************************************** * Copyright © 2012 Jonathan Thomas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "QAptActions.h" #include "MuonStrings.h" #include "HistoryView/HistoryView.h" // Qt includes #include #include #include #include #include #include +#include #include #include #include #include // KDE includes #include #include #include #include -#include #include #include #include #include // QApt includes #include #include #include QAptActions::QAptActions() : QObject(nullptr) , m_backend(nullptr) , m_actionsDisabled(false) , m_mainWindow(nullptr) , m_reloadWhenEditorFinished(false) , m_historyDialog(nullptr) , m_distUpgradeAvailable(false) , m_config(new QNetworkConfigurationManager(this)) { connect(m_config, &QNetworkConfigurationManager::onlineStateChanged, this, &QAptActions::shouldConnect); } QAptActions* QAptActions::self() { static QPointer self; if(!self) { self = new QAptActions; } return self; } void QAptActions::setMainWindow(KXmlGuiWindow* w) { setParent(w); m_mainWindow = w; setupActions(); } KXmlGuiWindow* QAptActions::mainWindow() const { return m_mainWindow; } void QAptActions::setBackend(QApt::Backend* backend) { if(backend == m_backend) return; m_backend = backend; if (!m_backend->init()) initError(); connect(m_backend, SIGNAL(packageChanged()), this, SLOT(setActionsEnabled())); setOriginalState(m_backend->currentCacheState()); setReloadWhenEditorFinished(true); // Some actions need an initialized backend to be able to set their enabled state setActionsEnabled(true); checkDistUpgrade(); } void QAptActions::setupActions() { QAction* undoAction = KStandardAction::undo(this, SLOT(undo()), actionCollection()); actionCollection()->addAction("undo", undoAction); m_actions.append(undoAction); QAction* redoAction = KStandardAction::redo(this, SLOT(redo()), actionCollection()); actionCollection()->addAction("redo", redoAction); m_actions.append(redoAction); QAction* revertAction = actionCollection()->addAction("revert"); revertAction->setIcon(QIcon::fromTheme("document-revert")); revertAction->setText(i18nc("@action Reverts all potential changes to the cache", "Unmark All")); connect(revertAction, SIGNAL(triggered()), this, SLOT(revertChanges())); m_actions.append(revertAction); QAction* softwarePropertiesAction = actionCollection()->addAction("software_properties"); softwarePropertiesAction->setPriority(QAction::LowPriority); softwarePropertiesAction->setIcon(QIcon::fromTheme("configure")); softwarePropertiesAction->setText(i18nc("@action Opens the software sources configuration dialog", "Configure Software Sources")); connect(softwarePropertiesAction, SIGNAL(triggered()), this, SLOT(runSourcesEditor())); m_actions.append(softwarePropertiesAction); QAction* loadSelectionsAction = actionCollection()->addAction("open_markings"); loadSelectionsAction->setIcon(QIcon::fromTheme("document-open")); loadSelectionsAction->setText(i18nc("@action", "Read Markings...")); connect(loadSelectionsAction, SIGNAL(triggered()), this, SLOT(loadSelections())); m_actions.append(loadSelectionsAction); QAction* saveSelectionsAction = actionCollection()->addAction("save_markings"); saveSelectionsAction->setIcon(QIcon::fromTheme("document-save-as")); saveSelectionsAction->setText(i18nc("@action", "Save Markings As...")); connect(saveSelectionsAction, SIGNAL(triggered()), this, SLOT(saveSelections())); m_actions.append(saveSelectionsAction); QAction* createDownloadListAction = actionCollection()->addAction("save_download_list"); createDownloadListAction->setPriority(QAction::LowPriority); createDownloadListAction->setIcon(QIcon::fromTheme("document-save-as")); createDownloadListAction->setText(i18nc("@action", "Save Package Download List...")); connect(createDownloadListAction, SIGNAL(triggered()), this, SLOT(createDownloadList())); m_actions.append(createDownloadListAction); QAction* downloadListAction = actionCollection()->addAction("download_from_list"); downloadListAction->setPriority(QAction::LowPriority); downloadListAction->setIcon(QIcon::fromTheme("download")); downloadListAction->setText(i18nc("@action", "Download Packages From List...")); connect(downloadListAction, SIGNAL(triggered()), this, SLOT(downloadPackagesFromList())); downloadListAction->setEnabled(isConnected()); connect(this, SIGNAL(shouldConnect(bool)), downloadListAction, SLOT(setEnabled(bool))); m_actions.append(downloadListAction); QAction* loadArchivesAction = actionCollection()->addAction("load_archives"); loadArchivesAction->setPriority(QAction::LowPriority); loadArchivesAction->setIcon(QIcon::fromTheme("document-open")); loadArchivesAction->setText(i18nc("@action", "Add Downloaded Packages")); connect(loadArchivesAction, SIGNAL(triggered()), this, SLOT(loadArchives())); m_actions.append(loadArchivesAction); QAction* saveInstalledAction = actionCollection()->addAction("save_package_list"); saveInstalledAction->setPriority(QAction::LowPriority); saveInstalledAction->setIcon(QIcon::fromTheme("document-save-as")); saveInstalledAction->setText(i18nc("@action", "Save Installed Packages List...")); connect(saveInstalledAction, SIGNAL(triggered()), this, SLOT(saveInstalledPackagesList())); QAction* historyAction = actionCollection()->addAction("history"); historyAction->setPriority(QAction::LowPriority); historyAction->setIcon(QIcon::fromTheme("view-history")); historyAction->setText(i18nc("@action::inmenu", "History...")); actionCollection()->setDefaultShortcut(historyAction, QKeySequence(Qt::CTRL + Qt::Key_H)); connect(historyAction, SIGNAL(triggered()), this, SLOT(showHistoryDialog())); QAction *distUpgradeAction = actionCollection()->addAction("dist-upgrade"); distUpgradeAction->setIcon(QIcon::fromTheme("system-software-update")); distUpgradeAction->setText(i18nc("@action", "Upgrade")); distUpgradeAction->setPriority(QAction::HighPriority); distUpgradeAction->setWhatsThis(i18nc("Notification when a new version of Kubuntu is available", "A new version of Kubuntu is available.")); distUpgradeAction->setEnabled(m_distUpgradeAvailable); connect(distUpgradeAction, SIGNAL(triggered(bool)), SLOT(launchDistUpgrade())); m_actions.append(saveInstalledAction); } void QAptActions::setActionsEnabled(bool enabled) { m_actionsDisabled = !enabled; Q_FOREACH (QAction *action, m_actions) { action->setEnabled(enabled); } if (!enabled || !m_mainWindow || !actionCollection()) return; actionCollection()->action("update")->setEnabled(isConnected() && enabled); actionCollection()->action("undo")->setEnabled(m_backend && !m_backend->isUndoStackEmpty()); actionCollection()->action("redo")->setEnabled(m_backend && !m_backend->isRedoStackEmpty()); actionCollection()->action("revert")->setEnabled(m_backend && m_backend->areChangesMarked()); actionCollection()->action("save_download_list")->setEnabled(isConnected()); bool changesPending = m_backend && m_backend->areChangesMarked(); actionCollection()->action("save_markings")->setEnabled(changesPending); actionCollection()->action("save_download_list")->setEnabled(changesPending); actionCollection()->action("dist-upgrade")->setEnabled(m_distUpgradeAvailable); } bool QAptActions::reloadWhenSourcesEditorFinished() const { return m_reloadWhenEditorFinished; } bool QAptActions::isConnected() const { return m_config->isOnline(); } bool QAptActions::saveSelections() { QString filename = QFileDialog::getSaveFileName(m_mainWindow, i18nc("@title:window", "Save Markings As")); if (filename.isEmpty()) { return false; } if (!m_backend->saveSelections(filename)) { QString text = xi18nc("@label", "The document could not be saved, as it " "was not possible to write to " "%1\n\nCheck " "that you have write access to this file " "or that enough disk space is available.", filename); KMessageBox::error(m_mainWindow, text, QString()); return false; } return true; } bool QAptActions::saveInstalledPackagesList() { QString filename; filename = QFileDialog::getSaveFileName(m_mainWindow, i18nc("@title:window", "Save Installed Packages List As")); if (filename.isEmpty()) { return false; } if (!m_backend->saveInstalledPackagesList(filename)) { QString text = xi18nc("@label", "The document could not be saved, as it " "was not possible to write to " "%1\n\nCheck " "that you have write access to this file " "or that enough disk space is available.", filename); KMessageBox::error(m_mainWindow, text, QString()); return false; } return true; } bool QAptActions::createDownloadList() { QString filename; filename = QFileDialog::getSaveFileName(m_mainWindow, i18nc("@title:window", "Save Download List As")); if (filename.isEmpty()) { return false; } if (!m_backend->saveDownloadList(filename)) { QString text = xi18nc("@label", "The document could not be saved, as it " "was not possible to write to " "%1\n\nCheck " "that you have write access to this file " "or that enough disk space is available.", filename); KMessageBox::error(m_mainWindow, text, QString()); return false; } return true; } void QAptActions::downloadPackagesFromList() { QString filename = QFileDialog::getOpenFileName(m_mainWindow, i18nc("@title:window", "Open File")); if (filename.isEmpty()) { return; } QString dirName = filename.left(filename.lastIndexOf('/')); setActionsEnabled(false); QApt::Transaction *trans = m_backend->downloadArchives(filename, dirName % QLatin1String("/packages")); if (trans) emit downloadArchives(trans); } void QAptActions::loadSelections() { QString filename = QFileDialog::getOpenFileName(m_mainWindow, i18nc("@title:window", "Open File")); if (filename.isEmpty()) { return; } m_backend->saveCacheState(); if (!m_backend->loadSelections(filename)) { QString text = i18nc("@label", "Could not mark changes. Please make sure " "that the file is a markings file created by " "either the Muon Package Manager or the " "Synaptic Package Manager."); KMessageBox::error(m_mainWindow, text, QString()); } } void QAptActions::loadArchives() { QString dirName = QFileDialog::getExistingDirectory(m_mainWindow, i18nc("@title:window", "Choose a Directory")); if (dirName.isEmpty()) { // User canceled return; } QDir dir(dirName); QStringList archiveFiles = dir.entryList(QDir::Files, QDir::Name); int successCount = 0; foreach (const QString &archiveFile, archiveFiles) { const QApt::DebFile debFile(dirName % '/' % archiveFile); if (debFile.isValid()) { if (m_backend->addArchiveToCache(debFile)) { successCount++; } } } if (successCount) { QString message = i18ncp("@label", "%1 package was successfully added to the cache", "%1 packages were successfully added to the cache", successCount); KMessageBox::information(m_mainWindow, message, QString()); } else { QString message = i18nc("@label", "No valid packages could be found in this directory. " "Please make sure the packages are compatible with your " "computer and are at the latest version."); KMessageBox::error(m_mainWindow, message, i18nc("@title:window", "Packages Could Not be Found")); } } void QAptActions::undo() { m_backend->undo(); } void QAptActions::redo() { m_backend->redo(); } void QAptActions::revertChanges() { m_backend->saveCacheState(); m_backend->restoreCacheState(m_originalState); emit changesReverted(); } void QAptActions::runSourcesEditor() { - KProcess *proc = new KProcess(this); + QProcess *proc = new QProcess(this); QStringList arguments; int winID = m_mainWindow->effectiveWinId(); const QString kdesu = QFile::decodeName(CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 "/kdesu"); QString editor = QStandardPaths::findExecutable("software-properties-qt"); if (editor.isEmpty()) { editor = QStandardPaths::findExecutable("software-properties-kde"); } if (editor.isEmpty()) { - QString text = i18nc("@label", - "Could not find software-properties-qt " + QString text = xi18nc("@info", + "Could not find software-properties-qt " "nor software-properties-kde " - "on your system, please install it. Alternatively, you can use " - "Plasma Discover to configure " - "software sources."); - QString title = i18nc("@title:window", + "on your system, please install it." + "Alternatively, you can use Plasma Discover " + "to configure software sources."); + QString title = xi18nc("@title:window", "Cannot find software-properties-qt"); KMessageBox::sorry(m_mainWindow, text, title); return; } - arguments << kdesu << "--" << editor << QStringLiteral("--attach") << QString::number(winID); + arguments << QStringLiteral("--") << editor << QStringLiteral("--attach") << QString::number(winID); if (m_reloadWhenEditorFinished) { arguments << QStringLiteral("--dont-update"); } - proc->setProgram(arguments); + connect(proc, QOverload::of(&QProcess::finished), + this, &QAptActions::sourcesEditorFinished); + connect(proc, QOverload::of(&QProcess::finished), + proc, &QProcess::deleteLater); m_mainWindow->find(winID)->setEnabled(false); - proc->start(); - connect(proc, SIGNAL(finished(int,QProcess::ExitStatus)), - this, SLOT(sourcesEditorFinished(int))); + proc->setProcessChannelMode(QProcess::ForwardedChannels); + proc->start(kdesu, arguments); } void QAptActions::sourcesEditorFinished(int exitStatus) { bool reload = (exitStatus != 0); m_mainWindow->find(m_mainWindow->effectiveWinId())->setEnabled(true); if (m_reloadWhenEditorFinished && reload) { actionCollection()->action("update")->trigger(); } emit sourcesEditorClosed(reload); } KActionCollection* QAptActions::actionCollection() { return m_mainWindow->actionCollection(); } void QAptActions::setOriginalState(QApt::CacheState state) { m_originalState = state; } void QAptActions::setReloadWhenEditorFinished(bool reload) { m_reloadWhenEditorFinished = reload; } void QAptActions::initError() { QString details = m_backend->initErrorMessage(); MuonStrings *muonStrings = MuonStrings::global(); QString title = muonStrings->errorTitle(QApt::InitError); QString text = muonStrings->errorText(QApt::InitError, nullptr); KMessageBox::detailedError(m_mainWindow, text, details, title); exit(-1); } void QAptActions::displayTransactionError(QApt::ErrorCode error, QApt::Transaction* trans) { if (error == QApt::Success) return; MuonStrings *muonStrings = MuonStrings::global(); QString title = muonStrings->errorTitle(error); QString text = muonStrings->errorText(error, trans); switch (error) { case QApt::InitError: case QApt::FetchError: case QApt::CommitError: KMessageBox::detailedError(QAptActions::self()->mainWindow(), text, trans->errorDetails(), title); break; default: KMessageBox::error(QAptActions::self()->mainWindow(), text, title); break; } } void QAptActions::showHistoryDialog() { if (!m_historyDialog) { m_historyDialog = new QDialog(mainWindow()); m_historyDialog->setLayout(new QVBoxLayout(m_historyDialog)); KConfigGroup dialogConfig(KSharedConfig::openConfig("muonrc"), "HistoryDialog"); KWindowConfig::restoreWindowSize(m_historyDialog->windowHandle(), dialogConfig); connect(m_historyDialog, SIGNAL(finished(int)), SLOT(closeHistoryDialog())); HistoryView *historyView = new HistoryView(m_historyDialog); m_historyDialog->layout()->addWidget(historyView); m_historyDialog->setWindowTitle(i18nc("@title:window", "Package History")); m_historyDialog->setWindowIcon(QIcon::fromTheme("view-history")); QDialogButtonBox* box = new QDialogButtonBox(m_historyDialog); box->setStandardButtons(QDialogButtonBox::Close); connect(box, SIGNAL(accepted()), m_historyDialog, SLOT(accept())); connect(box, SIGNAL(rejected()), m_historyDialog, SLOT(reject())); m_historyDialog->layout()->addWidget(box); m_historyDialog->show(); } else { m_historyDialog->raise(); } } void QAptActions::closeHistoryDialog() { KConfigGroup dialogConfig(KSharedConfig::openConfig("muonrc"), "HistoryDialog"); KWindowConfig::restoreWindowSize(m_historyDialog->windowHandle(), dialogConfig); m_historyDialog->deleteLater(); m_historyDialog = nullptr; } void QAptActions::launchDistUpgrade() { const QString kdesu = QFile::decodeName(CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 "/kdesu"); QProcess::startDetached(kdesu, {"--", "do-release-upgrade", "-m", "desktop", "-f", "DistUpgradeViewKDE"}); } void QAptActions::checkDistUpgrade() { if(!QFile::exists("/usr/lib/python3/dist-packages/DistUpgrade/DistUpgradeFetcherKDE.py")) { qWarning() << "Couldn't find the /usr/lib/python3/dist-packages/DistUpgrade/DistUpgradeFetcherKDE.py file"; return; } QString checkerFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "libmuon/applicationsbackend/releasechecker"); if(checkerFile.isEmpty()) { qWarning() << "Couldn't find the releasechecker script" << QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); return; } - KProcess* checkerProcess = new KProcess(this); - checkerProcess->setProgram(QStringList() << "/usr/bin/python3" << checkerFile); - connect(checkerProcess, SIGNAL(finished(int)), this, SLOT(checkerFinished(int))); - connect(checkerProcess, SIGNAL(finished(int)), checkerProcess, SLOT(deleteLater())); + QProcess *checkerProcess = new QProcess(this); + checkerProcess->setProgram(QStringLiteral("/usr/bin/python3")); + checkerProcess->setArguments(QStringList() << checkerFile); + checkerProcess->setProcessChannelMode(QProcess::ForwardedChannels); + connect(checkerProcess, QOverload::of(&QProcess::finished), + this, &QAptActions::checkerFinished); + connect(checkerProcess, QOverload::of(&QProcess::finished), + checkerProcess, &QProcess::deleteLater); checkerProcess->start(); } void QAptActions::checkerFinished(int res) { m_distUpgradeAvailable = (res == 0); if (!m_mainWindow) return; actionCollection()->action("dist-upgrade")->setEnabled(m_distUpgradeAvailable); }