diff --git a/src/DetailsTabs/ChangelogTab.cpp b/src/DetailsTabs/ChangelogTab.cpp index 3418a6b2..639d8298 100644 --- a/src/DetailsTabs/ChangelogTab.cpp +++ b/src/DetailsTabs/ChangelogTab.cpp @@ -1,129 +1,133 @@ /*************************************************************************** * Copyright © 2010,2011 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 "ChangelogTab.h" // Qt includes #include #include #include #include #include // KDE includes #include #include #include #include #include #include // QApt includes #include #include ChangelogTab::ChangelogTab(QWidget *parent) : DetailsTab(parent) { m_name = i18nc("@title:tab", "Changes List"); m_changelogBrowser = new QTextBrowser(this); m_busyWidget = new KPixmapSequenceOverlayPainter(this); m_busyWidget->setSequence(KIconLoader::global()->loadPixmapSequence("process-working", KIconLoader::SizeSmallMedium)); m_busyWidget->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_busyWidget->setWidget(m_changelogBrowser->viewport()); m_layout->addWidget(m_changelogBrowser); } void ChangelogTab::setPackage(QApt::Package *package) { m_package = package; +} + +void ChangelogTab::refresh() +{ fetchChangelog(); } void ChangelogTab::clear() { DetailsTab::clear(); // Delete any KJobs lying around. We could get stale package pointers if the jobs // finish during a cache reload auto iter = m_jobFilenames.constBegin(); while (iter != m_jobFilenames.constEnd()) { iter.key()->deleteLater(); ++iter; } m_jobFilenames.clear(); } void ChangelogTab::changelogFetched(KJob *job) { // Work around http://bugreports.qt.nokia.com/browse/QTBUG-2533 by forcibly resetting the CharFormat QTextCharFormat format; m_changelogBrowser->setCurrentCharFormat(format); QFile changelogFile(m_jobFilenames[job]); m_jobFilenames.remove(job); m_busyWidget->stop(); if (job->error() || !changelogFile.open(QFile::ReadOnly)) { if (m_package->origin() == QStringLiteral("Ubuntu")) { m_changelogBrowser->setText(xi18nc("@info/rich", "The list of changes is not yet available. " "Please use Launchpad instead.", QStringLiteral("http://launchpad.net/ubuntu/+source/") + m_package->sourcePackage())); } else { m_changelogBrowser->setText(i18nc("@info", "The list of changes is not yet available.")); } } else { QTextStream stream(&changelogFile); const QApt::Changelog log(stream.readAll(), m_package->sourcePackage()); m_changelogBrowser->setText(log.text()); } changelogFile.remove(); job->deleteLater(); } void ChangelogTab::fetchChangelog() { if (!m_package) { return; } m_changelogBrowser->clear(); m_busyWidget->start(); QTemporaryFile *changelogFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/muon_XXXXXX.txt")); changelogFile->setAutoRemove(false); changelogFile->open(); QString filename = changelogFile->fileName(); delete changelogFile; KIO::FileCopyJob *getJob = KIO::file_copy(m_package->changelogUrl(), QUrl::fromLocalFile(filename), -1, KIO::Overwrite | KIO::HideProgressInfo); getJob->setAutoDelete(false); m_jobFilenames.insert(getJob, filename); connect(getJob, SIGNAL(result(KJob*)), this, SLOT(changelogFetched(KJob*))); } diff --git a/src/DetailsTabs/ChangelogTab.h b/src/DetailsTabs/ChangelogTab.h index d6837437..000e57e5 100644 --- a/src/DetailsTabs/ChangelogTab.h +++ b/src/DetailsTabs/ChangelogTab.h @@ -1,55 +1,56 @@ /*************************************************************************** * Copyright © 2010 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 . * ***************************************************************************/ #ifndef CHANGELOGTAB_H #define CHANGELOGTAB_H #include "DetailsTab.h" // Qt includes #include class QTextBrowser; class KJob; class KPixmapSequenceOverlayPainter; class KTemporaryFile; class ChangelogTab : public DetailsTab { Q_OBJECT public: explicit ChangelogTab(QWidget *parent = 0); private: QTextBrowser *m_changelogBrowser; KPixmapSequenceOverlayPainter *m_busyWidget; QHash m_jobFilenames; public Q_SLOTS: void setPackage(QApt::Package *package); + void refresh() override; void clear(); private Q_SLOTS: void fetchChangelog(); void changelogFetched(KJob *job); }; #endif diff --git a/src/DetailsTabs/DetailsTab.cpp b/src/DetailsTabs/DetailsTab.cpp index 3f92ed0e..e44b01f1 100644 --- a/src/DetailsTabs/DetailsTab.cpp +++ b/src/DetailsTabs/DetailsTab.cpp @@ -1,64 +1,63 @@ /*************************************************************************** * Copyright © 2011 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 "DetailsTab.h" #include DetailsTab::DetailsTab(QWidget *parent) : QWidget(parent) , m_backend(0) , m_package(0) { m_layout = new QVBoxLayout(this); m_layout->setMargin(0); m_layout->setSpacing(0); setLayout(m_layout); } QString DetailsTab::name() const { return m_name; } bool DetailsTab::shouldShow() const { return true; } void DetailsTab::setBackend(QApt::Backend *backend) { m_backend = backend; } void DetailsTab::setPackage(QApt::Package *package) { m_package = package; - refresh(); } void DetailsTab::refresh() { } void DetailsTab::clear() { m_package = 0; } diff --git a/src/DetailsTabs/InstalledFilesTab.cpp b/src/DetailsTabs/InstalledFilesTab.cpp index b0bc4869..53409f15 100644 --- a/src/DetailsTabs/InstalledFilesTab.cpp +++ b/src/DetailsTabs/InstalledFilesTab.cpp @@ -1,66 +1,70 @@ /*************************************************************************** * Copyright © 2010 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 "InstalledFilesTab.h" // Qt includes #include // KDE includes #include // QApt includes #include InstalledFilesTab::InstalledFilesTab(QWidget *parent) : DetailsTab(parent) { m_name = i18nc("@title:tab", "Installed Files"); m_filesBrowser = new QTextBrowser(this); m_layout->addWidget(m_filesBrowser); } bool InstalledFilesTab::shouldShow() const { return m_package->isInstalled(); } void InstalledFilesTab::setPackage(QApt::Package *package) { m_package = package; +} + +void InstalledFilesTab::refresh() +{ populateFilesList(); } void InstalledFilesTab::populateFilesList() { m_filesBrowser->clear(); QStringList filesList = m_package->installedFilesList(); qSort(filesList); QString filesString; foreach(const QString &file, filesList) { filesString.append(file + '\n'); } m_filesBrowser->setPlainText(filesString); } #include "InstalledFilesTab.moc" diff --git a/src/DetailsTabs/InstalledFilesTab.h b/src/DetailsTabs/InstalledFilesTab.h index 424cc8fa..5120eeb3 100644 --- a/src/DetailsTabs/InstalledFilesTab.h +++ b/src/DetailsTabs/InstalledFilesTab.h @@ -1,46 +1,47 @@ /*************************************************************************** * Copyright © 2010 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 . * ***************************************************************************/ #ifndef INSTALLEDFILESTAB_H #define INSTALLEDFILESTAB_H #include "DetailsTab.h" class QTextBrowser; class InstalledFilesTab : public DetailsTab { Q_OBJECT public: explicit InstalledFilesTab(QWidget *parent = 0); bool shouldShow() const; private: QTextBrowser *m_filesBrowser; public Q_SLOTS: void setPackage(QApt::Package *package); + void refresh() override; private Q_SLOTS: void populateFilesList(); }; #endif diff --git a/src/DetailsTabs/VersionTab.cpp b/src/DetailsTabs/VersionTab.cpp index 629b4812..c70f743a 100644 --- a/src/DetailsTabs/VersionTab.cpp +++ b/src/DetailsTabs/VersionTab.cpp @@ -1,120 +1,124 @@ /*************************************************************************** * Copyright © 2010 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 "VersionTab.h" // Qt includes #include #include #include #include #include // KDE includes #include // QApt includes #include VersionTab::VersionTab(QWidget *parent) : DetailsTab(parent) { m_name = i18nc("@title:tab", "Versions"); QLabel *label = new QLabel(this); label->setText(i18nc("@label", "Available versions:")); m_versionModel = new QStandardItemModel(this); m_versionsView = new QListView(this); m_versionsView->setModel(m_versionModel); connect(m_versionsView, SIGNAL(activated(QModelIndex)), this, SLOT(enableButton())); QWidget *footerWidget = new QWidget(this); QHBoxLayout *footerLayout = new QHBoxLayout(footerWidget); footerWidget->setLayout(footerLayout); QLabel *infoIconLabel = new QLabel(footerWidget); infoIconLabel->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(32, 32)); footerLayout->addWidget(infoIconLabel); QLabel *infoLabel = new QLabel(footerWidget); infoLabel->setWordWrap(true); infoLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); infoLabel->setText(i18nc("@label", "Muon always selects the most " "applicable version available. If " "you force a different version from the " "default one, errors in the dependency " "handling can occur.")); footerLayout->addWidget(infoLabel); m_forceButton = new QPushButton(footerWidget); m_forceButton->setText(i18nc("@action:button", "Force Version")); m_forceButton->setEnabled(false); connect(m_forceButton, SIGNAL(clicked()), this, SLOT(forceVersion())); footerLayout->addWidget(m_forceButton); m_layout->addWidget(label); m_layout->addWidget(m_versionsView); m_layout->addWidget(footerWidget); } bool VersionTab::shouldShow() const { if (!m_package) { return false; } return m_package->availableVersions().size() > 1; } void VersionTab::enableButton() { m_forceButton->setEnabled(true); } void VersionTab::setPackage(QApt::Package *package) { m_package = package; +} + +void VersionTab::refresh() +{ m_versionModel->clear(); populateVersions(); } void VersionTab::populateVersions() { QStringList availableVersions = m_package->availableVersions(); foreach(const QString &version, availableVersions) { QStringList split = version.split(' '); m_versions.append(split.at(0)); QStandardItem *versionItem = new QStandardItem; versionItem->setEditable(false); versionItem->setText(version); m_versionModel->appendRow(versionItem); } } void VersionTab::forceVersion() { m_package->setVersion(m_versions.at(m_versionsView->currentIndex().row())); m_package->setInstall(); } #include "VersionTab.moc" diff --git a/src/DetailsTabs/VersionTab.h b/src/DetailsTabs/VersionTab.h index c59d740c..e322c2b8 100644 --- a/src/DetailsTabs/VersionTab.h +++ b/src/DetailsTabs/VersionTab.h @@ -1,53 +1,54 @@ /*************************************************************************** * Copyright © 2010 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 . * ***************************************************************************/ #ifndef VERSIONTAB_H #define VERSIONTAB_H #include "DetailsTab.h" class QListView; class QPushButton; class QStandardItemModel; class VersionTab : public DetailsTab { Q_OBJECT public: explicit VersionTab(QWidget *parent = 0); bool shouldShow() const; private: QStandardItemModel *m_versionModel; QListView *m_versionsView; QPushButton *m_forceButton; QStringList m_versions; public Q_SLOTS: void setPackage(QApt::Package *package); + void refresh() override; private Q_SLOTS: void enableButton(); void populateVersions(); void forceVersion(); }; #endif diff --git a/src/DetailsWidget.cpp b/src/DetailsWidget.cpp index 257bc679..dea0d76c 100644 --- a/src/DetailsWidget.cpp +++ b/src/DetailsWidget.cpp @@ -1,124 +1,132 @@ /*************************************************************************** * Copyright © 2010 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 "DetailsWidget.h" // Qt #include // KDE #include // QApt includes #include #include // Own includes #include "DetailsTabs/MainTab.h" #include "DetailsTabs/TechnicalDetailsTab.h" #include "DetailsTabs/DependsTab.h" #include "DetailsTabs/ChangelogTab.h" #include "DetailsTabs/InstalledFilesTab.h" #include "DetailsTabs/VersionTab.h" DetailsWidget::DetailsWidget(QWidget *parent) : QTabWidget(parent) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); setDocumentMode(true); DetailsTab *mainTab = new MainTab(this); m_detailsTabs.append(mainTab); m_detailsTabs.append(new TechnicalDetailsTab(this)); m_detailsTabs.append(new DependsTab(this)); m_detailsTabs.append(new InstalledFilesTab(0)); m_detailsTabs.append(new VersionTab(0)); m_detailsTabs.append(new ChangelogTab(this)); // Hide until a package is clicked hide(); + Q_FOREACH (DetailsTab *tab, m_detailsTabs) { + addTab(tab, tab->name()); + } + connect(mainTab, SIGNAL(setInstall(QApt::Package*)), this, SIGNAL(setInstall(QApt::Package*))); connect(mainTab, SIGNAL(setRemove(QApt::Package*)), this, SIGNAL(setRemove(QApt::Package*))); connect(mainTab, SIGNAL(setUpgrade(QApt::Package*)), this, SIGNAL(setUpgrade(QApt::Package*))); connect(mainTab, SIGNAL(setReInstall(QApt::Package*)), this, SIGNAL(setReInstall(QApt::Package*))); connect(mainTab, SIGNAL(setKeep(QApt::Package*)), this, SIGNAL(setKeep(QApt::Package*))); connect(mainTab, SIGNAL(setPurge(QApt::Package*)), this, SIGNAL(setPurge(QApt::Package*))); connect(this, SIGNAL(emitHideButtonsSignal()), mainTab, SLOT(hideButtons())); + connect(this, &DetailsWidget::currentChanged, + this, &DetailsWidget::refreshCurrentTab); } DetailsWidget::~DetailsWidget() { } void DetailsWidget::setBackend(QApt::Backend *backend) { Q_FOREACH (DetailsTab *tab, m_detailsTabs) { tab->setBackend(backend); } } void DetailsWidget::setPackage(QApt::Package *package) { + bool tabChanged = false; Q_FOREACH (DetailsTab *tab, m_detailsTabs) { tab->setPackage(package); - if (tab->shouldShow()) { - addTab(tab, tab->name()); - } else { - if (currentIndex() == indexOf(tab)) { - setCurrentIndex(0); - } - removeTab(indexOf(tab)); + if (currentIndex() == indexOf(tab) && !tab->shouldShow()) { + setCurrentIndex(0); + tabChanged = true; } + setTabEnabled(indexOf(tab), tab->shouldShow()); + } + if (!tabChanged) { + refreshCurrentTab(); } show(); } void DetailsWidget::emitHideButtons() { emit emitHideButtonsSignal(); } -void DetailsWidget::refreshTabs() +void DetailsWidget::refreshCurrentTab() { - Q_FOREACH (DetailsTab *tab, m_detailsTabs) { + DetailsTab *tab = qobject_cast(currentWidget()); + if (tab) { tab->refresh(); } } void DetailsWidget::clear() { Q_FOREACH (DetailsTab *tab, m_detailsTabs) { tab->clear(); } hide(); } #include "DetailsWidget.moc" diff --git a/src/DetailsWidget.h b/src/DetailsWidget.h index 9187e2db..1581848d 100644 --- a/src/DetailsWidget.h +++ b/src/DetailsWidget.h @@ -1,65 +1,65 @@ /*************************************************************************** * Copyright © 2010 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 . * ***************************************************************************/ #ifndef DETAILSWIDGET_H #define DETAILSWIDGET_H // Qt includes #include #include class QScrollArea; namespace QApt { class Backend; class Package; } class DetailsTab; class DetailsWidget : public QTabWidget { Q_OBJECT public: explicit DetailsWidget(QWidget *parent = 0); ~DetailsWidget(); private: QVector m_detailsTabs; public Q_SLOTS: void setBackend(QApt::Backend *backend); void setPackage(QApt::Package *package); - void refreshTabs(); + void refreshCurrentTab(); void clear(); void emitHideButtons(); signals: void setInstall(QApt::Package *package); void setRemove(QApt::Package *package); void setUpgrade(QApt::Package *package); void setReInstall(QApt::Package *package); void setKeep(QApt::Package *package); void setPurge(QApt::Package *package); void emitHideButtonsSignal(); }; #endif diff --git a/src/PackageModel/PackageWidget.cpp b/src/PackageModel/PackageWidget.cpp index d5f29126..beaa608f 100644 --- a/src/PackageModel/PackageWidget.cpp +++ b/src/PackageModel/PackageWidget.cpp @@ -1,712 +1,712 @@ /*************************************************************************** * Copyright © 2010,2011 Jonathan Thomas * * Copyright © 2010 Guillaume Martres * * * * 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 "PackageWidget.h" // Qt includes #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include #include #include #include // QApt includes #include #include #include // Own includes #include "muonapt/ChangesDialog.h" #include "DetailsWidget.h" #include "MuonSettings.h" #include "PackageModel.h" #include "PackageProxyModel.h" #include "PackageView.h" #include "PackageDelegate.h" bool packageNameLessThan(QApt::Package *p1, QApt::Package *p2) { return p1->name() < p2->name(); } QApt::PackageList sortPackages(QApt::PackageList list) { qSort(list.begin(), list.end(), packageNameLessThan); return list; } PackageWidget::PackageWidget(QWidget *parent) : QWidget(parent) , m_backend(0) , m_headerLabel(0) , m_searchEdit(0) , m_packagesType(0) , m_stop(false) { m_watcher = new QFutureWatcher >(this); connect(m_watcher, SIGNAL(finished()), this, SLOT(setSortedPackages())); m_model = new PackageModel(this); PackageDelegate *delegate = new PackageDelegate(this); m_proxyModel = new PackageProxyModel(this); m_proxyModel->setSourceModel(m_model); QVBoxLayout *topVBox = new QVBoxLayout; topVBox->setContentsMargins(0, 0, 0, 0); topVBox->setSpacing(0); m_headerLabel = new QLabel; m_headerLabel->setTextFormat(Qt::RichText); topVBox->addWidget(m_headerLabel); m_searchTimer = new QTimer(this); m_searchTimer->setInterval(300); m_searchTimer->setSingleShot(true); connect(m_searchTimer, SIGNAL(timeout()), this, SLOT(startSearch())); setupActions(); m_searchEdit = new QLineEdit; m_searchEdit->setEnabled(false); m_searchEdit->setPlaceholderText(i18nc("@label Line edit click message", "Search")); m_searchEdit->setClearButtonEnabled(true); m_searchEdit->hide(); // Off by default, use showSearchEdit() to show topVBox->addWidget(m_searchEdit); m_packageView = new PackageView; m_packageView->setModel(m_proxyModel); m_packageView->setItemDelegate(delegate); m_packageView->header()->setSectionResizeMode(0, QHeaderView::Stretch); const int numColumns = m_packageView->header()->count(); Q_ASSERT(numColumns >= 3); for (int i = 3; i < numColumns; ++i) { m_packageView->header()->setSectionHidden(i, true); } topVBox->addWidget(m_packageView); m_detailsWidget = new DetailsWidget; connect(m_detailsWidget, SIGNAL(setInstall(QApt::Package*)), this, SLOT(setInstall(QApt::Package*))); connect(m_detailsWidget, SIGNAL(setRemove(QApt::Package*)), this, SLOT(setRemove(QApt::Package*))); connect(m_detailsWidget, SIGNAL(setUpgrade(QApt::Package*)), this, SLOT(setUpgrade(QApt::Package*))); connect(m_detailsWidget, SIGNAL(setReInstall(QApt::Package*)), this, SLOT(setReInstall(QApt::Package*))); connect(m_detailsWidget, SIGNAL(setKeep(QApt::Package*)), this, SLOT(setKeep(QApt::Package*))); connect(m_detailsWidget, SIGNAL(setPurge(QApt::Package*)), this, SLOT(setPurge(QApt::Package*))); m_busyWidget = new KPixmapSequenceOverlayPainter(this); m_busyWidget->setSequence(KPixmapSequence("process-working", KIconLoader::SizeSmallMedium)); m_busyWidget->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_busyWidget->setWidget(m_packageView->viewport()); QApplication::setOverrideCursor(Qt::WaitCursor); m_busyWidget->start(); connect(m_packageView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); connect(m_packageView, SIGNAL(currentPackageChanged(QModelIndex)), this, SLOT(packageActivated(QModelIndex))); connect(m_packageView, SIGNAL(selectionEmpty()), m_detailsWidget, SLOT(hide())); connect(m_packageView, SIGNAL(selectionMulti()), m_detailsWidget, SLOT(emitHideButtons())); connect(m_searchEdit, SIGNAL(textChanged(QString)), m_searchTimer, SLOT(start())); QWidget* topWidget = new QWidget; topWidget->setLayout(topVBox); QSplitter *splitter = new QSplitter; splitter->setOrientation(Qt::Vertical); splitter->addWidget(topWidget); splitter->addWidget(m_detailsWidget); setLayout(new QVBoxLayout); layout()->setContentsMargins(0, 0, 0, 0); layout()->addWidget(splitter); } void PackageWidget::setupActions() { m_installAction = new QAction(this); m_installAction->setIcon(QIcon::fromTheme("download")); m_installAction->setText(i18nc("@action:inmenu", "Mark for Installation")); connect(m_installAction, SIGNAL(triggered()), this, SLOT(setPackagesInstall())); m_removeAction = new QAction(this); m_removeAction->setIcon(QIcon::fromTheme("edit-delete")); m_removeAction->setText(i18nc("@action:button", "Mark for Removal")); connect(m_removeAction, SIGNAL(triggered()), this, SLOT(setPackagesRemove())); m_upgradeAction = new QAction(this); m_upgradeAction->setIcon(QIcon::fromTheme("system-software-update")); m_upgradeAction->setText(i18nc("@action:button", "Mark for Upgrade")); connect(m_upgradeAction, SIGNAL(triggered()), this, SLOT(setPackagesUpgrade())); m_reinstallAction = new QAction(this); m_reinstallAction->setIcon(QIcon::fromTheme("view-refresh")); m_reinstallAction->setText(i18nc("@action:button", "Mark for Reinstallation")); connect(m_reinstallAction, SIGNAL(triggered()), this, SLOT(setPackagesReInstall())); m_purgeAction = new QAction(this); m_purgeAction->setIcon(QIcon::fromTheme("edit-delete-shred")); m_purgeAction->setText(i18nc("@action:button", "Mark for Purge")); connect(m_purgeAction, SIGNAL(triggered()), this, SLOT(setPackagesPurge())); m_keepAction = new QAction(this); m_keepAction->setIcon(QIcon::fromTheme("dialog-cancel")); m_keepAction->setText(i18nc("@action:button", "Unmark")); connect(m_keepAction, SIGNAL(triggered()), this, SLOT(setPackagesKeep())); m_lockAction = new QAction(this); m_lockAction->setCheckable(true); m_lockAction->setIcon(QIcon::fromTheme("object-locked")); m_lockAction->setText(i18nc("@action:button", "Lock Package at Current Version")); connect(m_lockAction, SIGNAL(triggered(bool)), this, SLOT(setPackagesLocked(bool))); } void PackageWidget::setHeaderText(const QString &text) { m_headerLabel->setText(text); } void PackageWidget::setPackagesType(int type) { m_packagesType = type; QApt::Package::State state = (QApt::Package::State)0; if (type == MarkedPackages) { state = (QApt::Package::State)(QApt::Package::ToInstall | QApt::Package::ToReInstall | QApt::Package::ToUpgrade | QApt::Package::ToDowngrade | QApt::Package::ToRemove | QApt::Package::ToPurge); } m_proxyModel->setStateFilter(state); } void PackageWidget::hideHeaderLabel() { m_headerLabel->hide(); } void PackageWidget::showSearchEdit() { m_searchEdit->show(); } void PackageWidget::setFocusSearchEdit() { m_searchEdit->setFocus(); m_searchEdit->selectAll(); } QByteArray PackageWidget::saveColumnsState() const { return m_packageView->header()->saveState(); } bool PackageWidget::restoreColumnsState(const QByteArray& state) { return m_packageView->header()->restoreState(state); } void PackageWidget::setBackend(QApt::Backend *backend) { m_backend = backend; - connect(m_backend, SIGNAL(packageChanged()), m_detailsWidget, SLOT(refreshTabs())); + connect(m_backend, SIGNAL(packageChanged()), m_detailsWidget, SLOT(refreshCurrentTab())); connect(m_backend, SIGNAL(packageChanged()), m_model, SLOT(externalDataChanged())); connect(m_backend, SIGNAL(cacheReloadStarted()), this, SLOT(cacheReloadStarted())); connect(m_backend, SIGNAL(cacheReloadFinished()), this, SLOT(cacheReloadFinished())); connect(m_backend, SIGNAL(xapianUpdateFinished()), this, SLOT(startSearch())); m_detailsWidget->setBackend(backend); m_proxyModel->setBackend(m_backend); m_packageView->setSortingEnabled(true); QApt::PackageList packageList = m_backend->availablePackages(); QFuture > future = QtConcurrent::run(sortPackages, packageList); m_watcher->setFuture(future); m_packageView->updateView(); } void PackageWidget::reload() { m_backend->reloadCache(); } void PackageWidget::cacheReloadStarted() { m_detailsWidget->clear(); m_model->clear(); m_proxyModel->clear(); m_proxyModel->reset(); m_busyWidget->start(); } void PackageWidget::cacheReloadFinished() { QApt::PackageList packageList = m_backend->availablePackages(); QFuture > future = QtConcurrent::run(sortPackages, packageList); m_watcher->setFuture(future); m_packageView->header()->setSectionResizeMode(0, QHeaderView::Stretch); startSearch(); } void PackageWidget::packageActivated(const QModelIndex &index) { QApt::Package *package = m_proxyModel->packageAt(index); if (package == 0) { m_detailsWidget->hide(); return; } m_detailsWidget->setPackage(package); } void PackageWidget::contextMenuRequested(const QPoint &pos) { QMenu menu; menu.addAction(m_installAction); menu.addAction(m_removeAction); menu.addAction(m_upgradeAction); menu.addAction(m_reinstallAction); menu.addAction(m_purgeAction); menu.addAction(m_keepAction); menu.addSeparator(); menu.addAction(m_lockAction); const int selected = m_packageView->selectionCount(); if (selected <= 0) { return; } if (selected == 1) { int state = m_proxyModel->packageAt(m_packageView->currentIndex())->state(); bool upgradeable = (state & QApt::Package::Upgradeable); if (state & QApt::Package::Installed) { m_installAction->setEnabled(false); if (upgradeable) { m_upgradeAction->setEnabled(true); } else { m_upgradeAction->setEnabled(false); } if (state & (QApt::Package::NotDownloadable) || upgradeable) { m_reinstallAction->setEnabled(false); } else { m_reinstallAction->setEnabled(true); } if (state & (QApt::Package::ToReInstall | QApt::Package::ToUpgrade | QApt::Package::ToDowngrade | QApt::Package::ToRemove | QApt::Package::ToPurge)) { m_removeAction->setEnabled(false); m_purgeAction->setEnabled(false); m_keepAction->setEnabled(true); } else { m_removeAction->setEnabled(true); m_keepAction->setEnabled(false); m_purgeAction->setEnabled(true); } } else if (state & QApt::Package::ResidualConfig) { m_purgeAction->setEnabled(true); m_installAction->setEnabled(true); m_removeAction->setEnabled(false); m_upgradeAction->setEnabled(false); m_reinstallAction->setEnabled(false); m_keepAction->setEnabled(false); } else if (state & (QApt::Package::ToInstall | QApt::Package::ToReInstall | QApt::Package::ToUpgrade | QApt::Package::ToDowngrade | QApt::Package::ToRemove | QApt::Package::ToPurge)) { m_purgeAction->setEnabled(false); m_installAction->setEnabled(false); m_removeAction->setEnabled(false); m_upgradeAction->setEnabled(false); m_reinstallAction->setEnabled(false); m_keepAction->setEnabled(true); } else { m_purgeAction->setEnabled(false); m_installAction->setEnabled(true); m_removeAction->setEnabled(false); m_upgradeAction->setEnabled(false); m_reinstallAction->setEnabled(false); m_keepAction->setEnabled(false); } if (state & QApt::Package::IsPinned) { m_lockAction->setChecked(true); m_lockAction->setText(i18nc("@action:button", "Unlock package")); m_lockAction->setIcon(QIcon::fromTheme("object-unlocked")); } else { m_lockAction->setChecked(false); m_lockAction->setText(i18nc("@action:button", "Lock at Current Version")); m_lockAction->setIcon(QIcon::fromTheme("object-locked")); } } else { m_installAction->setEnabled(true); m_removeAction->setEnabled(true); m_upgradeAction->setEnabled(true); m_reinstallAction->setEnabled(true); m_purgeAction->setEnabled(true); m_keepAction->setEnabled(true); } menu.exec(m_packageView->mapToGlobal(pos)); } void PackageWidget::setSortedPackages() { QApt::PackageList packageList = m_watcher->future().result(); m_model->setPackages(packageList); m_searchEdit->setEnabled(true); m_searchEdit->setFocus(); m_busyWidget->stop(); QApplication::restoreOverrideCursor(); } void PackageWidget::startSearch() { if (m_proxyModel->sourceModel()) { m_proxyModel->search(m_searchEdit->text()); } } void PackageWidget::invalidateFilter() { if (m_proxyModel) { m_proxyModel->invalidate(); } } bool PackageWidget::confirmEssentialRemoval() { QString text = i18nc("@label", "Removing this package may break your system. Are you sure you want to remove it?"); QString title = i18nc("@label", "Warning - Removing Important Package"); int result = KMessageBox::Cancel; result = KMessageBox::warningContinueCancel(this, text, title, KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous); return (result == KMessageBox::Continue) ? true : false; } void PackageWidget::saveState() { if (!m_backend->areEventsCompressed()) { m_oldCacheState = m_backend->currentCacheState(); m_backend->saveCacheState(); } } void PackageWidget::handleBreakage(QApt::Package *package) { if (package->wouldBreak() || m_backend->isBroken()) { showBrokenReason(package); m_backend->restoreCacheState(m_oldCacheState); m_stop = true; } } void PackageWidget::actOnPackages(QApt::Package::State action) { const QApt::PackageList packages = selectedPackages(); if (packages.isEmpty()) return; QApplication::setOverrideCursor(Qt::WaitCursor); saveState(); m_backend->setCompressEvents(true); m_stop = false; for (QApt::Package *package : packages) { if (m_stop) { m_backend->setCompressEvents(false); QApplication::restoreOverrideCursor(); break; } switch (action) { case QApt::Package::ToInstall: setInstall(package); break; case QApt::Package::ToRemove: setRemove(package); break; case QApt::Package::ToUpgrade: setUpgrade(package); break; case QApt::Package::ToReInstall: if (package->isInstalled()) setReInstall(package); break; case QApt::Package::ToKeep: setKeep(package); break; case QApt::Package::ToPurge: setPurge(package); break; default: break; } } emit packageChanged(); m_backend->setCompressEvents(false); QApplication::restoreOverrideCursor(); checkChanges(); } void PackageWidget::setInstall(QApt::Package *package) { saveState(); if (!package->availableVersion().isEmpty()) { package->setInstall(); } // Check for/handle breakage handleBreakage(package); checkChanges(); } void PackageWidget::setPackagesInstall() { actOnPackages(QApt::Package::ToInstall); } void PackageWidget::setRemove(QApt::Package *package) { bool remove = true; if (package->state() & QApt::Package::IsImportant) { remove = confirmEssentialRemoval(); } if (remove) { saveState(); package->setRemove(); handleBreakage(package); checkChanges(); } } void PackageWidget::setPackagesRemove() { actOnPackages(QApt::Package::ToRemove); } void PackageWidget::setUpgrade(QApt::Package *package) { setInstall(package); } void PackageWidget::setPackagesUpgrade() { actOnPackages(QApt::Package::ToUpgrade); } void PackageWidget::setReInstall(QApt::Package *package) { saveState(); package->setReInstall(); handleBreakage(package); checkChanges(); } void PackageWidget::setPackagesReInstall() { actOnPackages(QApt::Package::ToReInstall); } void PackageWidget::setPurge(QApt::Package *package) { bool remove = true; if (package->state() & QApt::Package::IsImportant) { remove = confirmEssentialRemoval(); } if (remove) { saveState(); package->setPurge(); handleBreakage(package); checkChanges(); } } void PackageWidget::setPackagesPurge() { actOnPackages(QApt::Package::ToPurge); } void PackageWidget::setKeep(QApt::Package *package) { saveState(); package->setKeep(); handleBreakage(package); checkChanges(); } void PackageWidget::setPackagesKeep() { actOnPackages(QApt::Package::ToKeep); } void PackageWidget::setPackagesLocked(bool lock) { const QApt::PackageList packages = selectedPackages(); if (packages.isEmpty()) return; for (QApt::Package *package : packages) { if (!m_backend->setPackagePinned(package, lock)) { QString title = i18nc("@title:window", "Failed to Lock Package"); QString text = i18nc("@info Error text", "The package %1 could not " "be locked. Failed to write lock file.", package->name()); KMessageBox::error(this, text, title); } } reload(); } void PackageWidget::checkChanges() { MuonSettings *settings = MuonSettings::self(); if (m_backend->areEventsCompressed() || !settings->askChanges()) return; auto changes = m_backend->stateChanges(m_oldCacheState, selectedPackages()); if (changes.isEmpty()) return; ChangesDialog *dialog = new ChangesDialog(this, changes); int res = dialog->exec(); if (res != QDialog::Accepted) m_backend->restoreCacheState(m_oldCacheState); } QApt::PackageList PackageWidget::selectedPackages() { const QModelIndexList selected = m_packageView->selectionModel()->selectedIndexes(); if (selected.isEmpty()) { return QApt::PackageList(); } // There are three indexes per row, so we want a duplicate-less set of packages QSet packages; for (const QModelIndex &index : selected) { packages << m_proxyModel->packageAt(index); } return packages.toList(); } void PackageWidget::showBrokenReason(QApt::Package *package) { QList failedReasons = package->brokenReason(); QString dialogText = i18nc("@label", "The \"%1\" package could not be marked for installation or upgrade:", package->name()); dialogText += '\n'; QString title = i18nc("@title:window", "Unable to Mark Package"); for (const QApt::MarkingErrorInfo &reason : failedReasons) dialogText += digestReason(package, reason); KMessageBox::information(this, dialogText, title); } QString PackageWidget::digestReason(QApt::Package *pkg, const QApt::MarkingErrorInfo &info) { QString reason; QString relation = QApt::DependencyInfo::typeName(info.errorInfo().dependencyType()); reason += '\t'; switch (info.errorType()) { case QApt::ParentNotInstallable: reason += i18nc("@label", "The \"%1\" package has no available version, but exists in the database.\n" "\tThis typically means that the package was mentioned in a dependency and " "never uploaded, has been obsoleted, or is not available from the currently-enabled " "repositories.", pkg->name()); break; case QApt::WrongCandidateVersion: reason += i18nc("@label Example: Depends: libqapt 0.1, but 0.2 is to be installed", "%1: %2 %3, but %4 is to be installed", relation, pkg->name(), info.errorInfo().packageVersion(), pkg->availableVersion()); break; case QApt::DepNotInstallable: reason += i18nc("@label Example: Depends: libqapt, but is not installable", "%1: %2, but it is not installable", relation, pkg->name()); break; case QApt::VirtualPackage: reason += i18nc("@label Example: Depends: libqapt, but it is a virtual package", "%1: %2, but it is a virtual package", relation, pkg->name()); break; default: break; } reason += '\n'; return reason; } bool PackageWidget::isSortingPackages() const { return m_watcher->isRunning(); }