File indexing completed on 2024-04-21 09:36:44

0001 /*******************************************************************************
0002  * Copyright (C) 2008-2013 Konstantinos Smanis <konstantinos.smanis@gmail.com> *
0003  *                                                                             *
0004  * This program is free software: you can redistribute it and/or modify it     *
0005  * under the terms of the GNU General Public License as published by the Free  *
0006  * Software Foundation, either version 3 of the License, or (at your option)   *
0007  * any later version.                                                          *
0008  *                                                                             *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT *
0010  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
0011  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    *
0012  * more details.                                                               *
0013  *                                                                             *
0014  * You should have received a copy of the GNU General Public License along     *
0015  * with this program. If not, see <http://www.gnu.org/licenses/>.              *
0016  *******************************************************************************/
0017 
0018 //Krazy
0019 //krazy:excludeall=cpp
0020 
0021 //Own
0022 #include "removeDlg.h"
0023 
0024 //Qt
0025 #include <QFile>
0026 #include <QTextStream>
0027 #include <QTimer>
0028 #include <QIcon>
0029 #include <QProgressDialog>
0030 #include <QPushButton>
0031 
0032 //KDE
0033 #include <KLocalizedString>
0034 #include <KMessageBox>
0035 
0036 //Project
0037 #include "entry.h"
0038 
0039 //Ui
0040 #include "ui_removeDlg.h"
0041 
0042 RemoveDialog::RemoveDialog(const QList<Entry> &entries, QWidget *parent) : QDialog(parent)
0043 {
0044     QWidget *widget = new QWidget(this);
0045     ui = new Ui::RemoveDialog;
0046     ui->setupUi(widget);
0047     ui->gridLayout->setContentsMargins(0, 0, 0, 0);
0048 
0049     auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0050     connect(buttonBox, &QDialogButtonBox::accepted, this, &RemoveDialog::slotAccepted);
0051     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0052     m_okButton = buttonBox->button(QDialogButtonBox::Ok);
0053     m_okButton->setEnabled(false);
0054 
0055     QVBoxLayout *mainLayout = new QVBoxLayout;
0056     setLayout(mainLayout);
0057     mainLayout->addWidget(widget);
0058     mainLayout->addWidget(buttonBox);
0059 
0060     setWindowTitle(i18nc("@title:window", "Remove Old Entries"));
0061     setWindowIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
0062 
0063     m_progressDlg = nullptr;
0064 
0065 #if HAVE_QAPT
0066     m_backend = new QAptBackend;
0067 #elif HAVE_QPACKAGEKIT
0068     m_backend = new QPkBackend;
0069 #endif
0070 
0071     detectCurrentKernelImage();
0072     QProgressDialog progressDlg(this);
0073     progressDlg.setWindowTitle(i18nc("@title:window", "Finding Old Entries"));
0074     progressDlg.setLabelText(i18nc("@info:progress", "Finding Old Entries..."));
0075     progressDlg.setCancelButton(nullptr);
0076     progressDlg.setModal(true);
0077     progressDlg.show();
0078     bool found = false;
0079     for (int i = 0; i < entries.size(); i++) {
0080         progressDlg.setValue(100. / entries.size() * (i + 1));
0081         QString file = entries.at(i).kernel();
0082         if (file.isEmpty() || file == m_currentKernelImage) {
0083             continue;
0084         }
0085         QStringList package = m_backend->ownerPackage(file);
0086         if (package.size() < 2) {
0087             continue;
0088         }
0089         found = true;
0090         const QString packageName = package.takeFirst();
0091         const QString packageVersion = package.takeFirst();
0092         QTreeWidgetItem *item = nullptr;
0093         for (int j = 0; j < ui->treeWidget->topLevelItemCount(); j++) {
0094             if (ui->treeWidget->topLevelItem(j)->data(0, Qt::UserRole).toString() == packageName && ui->treeWidget->topLevelItem(j)->data(0, Qt::UserRole + 1).toString() == packageVersion) {
0095                 item = ui->treeWidget->topLevelItem(j);
0096                 break;
0097             }
0098         }
0099         if (!item) {
0100             item = new QTreeWidgetItem(ui->treeWidget, QStringList(i18nc("@item:inlistbox", "Kernel %1", packageVersion)));
0101             item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
0102             item->setData(0, Qt::UserRole, packageName);
0103             item->setData(0, Qt::UserRole + 1, packageVersion);
0104             item->setCheckState(0, Qt::Checked);
0105             ui->treeWidget->addTopLevelItem(item);
0106         }
0107         item->addChild(new QTreeWidgetItem(QStringList(entries.at(i).prettyTitle())));
0108     }
0109     if (found) {
0110         ui->treeWidget->expandAll();
0111         ui->treeWidget->resizeColumnToContents(0);
0112         ui->treeWidget->setMinimumWidth(ui->treeWidget->columnWidth(0) + ui->treeWidget->sizeHintForRow(0));
0113         connect(ui->treeWidget, &QTreeWidget::itemChanged, this, &RemoveDialog::slotItemChanged);
0114         m_okButton->setEnabled(true);
0115     } else {
0116         KMessageBox::information(this, i18nc("@info", "No removable entries were found."));
0117         QTimer::singleShot(0, this, &QDialog::reject);
0118     }
0119 }
0120 RemoveDialog::~RemoveDialog()
0121 {
0122     delete m_backend;
0123     delete ui;
0124 }
0125 void RemoveDialog::slotAccepted()
0126 {
0127     for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) {
0128         if (ui->treeWidget->topLevelItem(i)->checkState(0) == Qt::Checked) {
0129             QString packageName = ui->treeWidget->topLevelItem(i)->data(0, Qt::UserRole).toString();
0130             m_backend->markForRemoval(packageName);
0131             if (ui->checkBox_headers->isChecked()) {
0132                 packageName.replace(QLatin1String("image"), QLatin1String("headers"));
0133                 m_backend->markForRemoval(packageName);
0134             }
0135         }
0136     }
0137     if (KMessageBox::questionYesNoList(this, i18nc("@info", "Are you sure you want to remove the following packages?"), m_backend->markedForRemoval()) == KMessageBox::Yes) {
0138 #if HAVE_QAPT
0139         connect(m_backend, &QAptBackend::progress, this, &RemoveDialog::slotProgress);
0140         connect(m_backend, &QAptBackend::finished, this, &RemoveDialog::slotFinished);
0141 #elif HAVE_QPACKAGEKIT
0142         connect(m_backend, &QPkBackend::progress, this, &RemoveDialog::slotProgress);
0143         connect(m_backend, &QPkBackend::finished, this, &RemoveDialog::slotFinished);
0144 #endif
0145         m_backend->removePackages();
0146     } else {
0147         m_backend->undoChanges();
0148     }
0149 
0150     accept();
0151 }
0152 void RemoveDialog::slotItemChanged()
0153 {
0154     for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) {
0155         if (ui->treeWidget->topLevelItem(i)->checkState(0) == Qt::Checked) {
0156             m_okButton->setEnabled(true);
0157             return;
0158         }
0159     }
0160     m_okButton->setEnabled(false);
0161 }
0162 void RemoveDialog::slotProgress(const QString &status, int percentage)
0163 {
0164     if (!m_progressDlg) {
0165         m_progressDlg = new QProgressDialog(this);
0166         m_progressDlg->setWindowTitle(i18nc("@title:window", "Removing Old Entries"));
0167         m_progressDlg->setCancelButton(nullptr);
0168         m_progressDlg->setModal(true);
0169         m_progressDlg->show();
0170     }
0171     m_progressDlg->setLabelText(status);
0172     m_progressDlg->setValue(percentage);
0173 }
0174 void RemoveDialog::slotFinished(bool success)
0175 {
0176     if (success) {
0177         accept();
0178     } else {
0179         KMessageBox::error(this, i18nc("@info", "Package removal failed."));
0180         reject();
0181     }
0182 }
0183 void RemoveDialog::detectCurrentKernelImage()
0184 {
0185     QFile file(QStringLiteral("/proc/cmdline"));
0186     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
0187         return;
0188     }
0189 
0190     const QStringList args = QTextStream(&file).readAll().split(QRegExp(QLatin1String("\\s+")));
0191     for (const QString &argument : args) {
0192         if (argument.startsWith(QLatin1String("BOOT_IMAGE"))) {
0193             m_currentKernelImage = argument.section(QLatin1Char('='), 1);
0194             return;
0195         }
0196     }
0197 }