File indexing completed on 2024-05-12 05:29:06

0001 /*
0002  *   SPDX-FileCopyrightText: 2012 Jonathan Thomas <echidnaman@kubuntu.org>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "TransactionModel.h"
0008 
0009 // Qt includes
0010 #include <KLocalizedString>
0011 #include <QDebug>
0012 #include <QMetaProperty>
0013 
0014 // Own includes
0015 #include "libdiscover_debug.h"
0016 #include "resources/AbstractResource.h"
0017 
0018 Q_GLOBAL_STATIC(TransactionModel, globalTransactionModel)
0019 
0020 TransactionModel *TransactionModel::global()
0021 {
0022     return globalTransactionModel;
0023 }
0024 
0025 TransactionModel::TransactionModel(QObject *parent)
0026     : QAbstractListModel(parent)
0027 {
0028     connect(this, &QAbstractItemModel::rowsInserted, this, &TransactionModel::countChanged);
0029     connect(this, &QAbstractItemModel::rowsRemoved, this, &TransactionModel::countChanged);
0030     connect(this, &TransactionModel::countChanged, this, &TransactionModel::progressChanged);
0031 }
0032 
0033 QHash<int, QByteArray> TransactionModel::roleNames() const
0034 {
0035     QHash<int, QByteArray> roles;
0036     roles[TransactionRoleRole] = "transactionRole";
0037     roles[TransactionStatusRole] = "status";
0038     roles[CancellableRole] = "cancellable";
0039     roles[ProgressRole] = "progress";
0040     roles[StatusTextRole] = "statusText";
0041     roles[ResourceRole] = "resource";
0042     roles[TransactionRole] = "transaction";
0043     return roles;
0044 }
0045 
0046 int TransactionModel::rowCount(const QModelIndex &parent) const
0047 {
0048     // Root element parents all children
0049     if (!parent.isValid()) {
0050         return m_transactions.size();
0051     }
0052 
0053     // Child elements have no children themselves
0054     return 0;
0055 }
0056 
0057 QVariant TransactionModel::data(const QModelIndex &index, int role) const
0058 {
0059     if (!index.isValid()) {
0060         return QVariant();
0061     }
0062 
0063     Transaction *transaction = m_transactions[index.row()];
0064     switch (role) {
0065     case TransactionRoleRole:
0066         return transaction->role();
0067     case TransactionStatusRole:
0068         return transaction->status();
0069     case CancellableRole:
0070         return transaction->isCancellable();
0071     case ProgressRole:
0072         return transaction->progress();
0073     case StatusTextRole:
0074         switch (transaction->status()) {
0075         case Transaction::SetupStatus:
0076             return i18nc("@info:status", "Starting");
0077         case Transaction::QueuedStatus:
0078             return i18nc("@info:status", "Waiting");
0079         case Transaction::DownloadingStatus:
0080             return i18nc("@info:status", "Downloading");
0081         case Transaction::CommittingStatus:
0082             switch (transaction->role()) {
0083             case Transaction::InstallRole:
0084                 return i18nc("@info:status", "Installing");
0085             case Transaction::RemoveRole:
0086                 return i18nc("@info:status", "Removing");
0087             case Transaction::ChangeAddonsRole:
0088                 return i18nc("@info:status", "Changing Addons");
0089             }
0090             break;
0091         case Transaction::DoneStatus:
0092             return i18nc("@info:status", "Done");
0093         case Transaction::DoneWithErrorStatus:
0094             return i18nc("@info:status", "Failed");
0095         case Transaction::CancelledStatus:
0096             return i18nc("@info:status", "Cancelled");
0097         }
0098         break;
0099     case TransactionRole:
0100         return QVariant::fromValue<QObject *>(transaction);
0101     case ResourceRole:
0102         return QVariant::fromValue<QObject *>(transaction->resource());
0103     }
0104 
0105     return QVariant();
0106 }
0107 
0108 Transaction *TransactionModel::transactionFromResource(AbstractResource *resource) const
0109 {
0110     for (const auto transaction : std::as_const(m_transactions)) {
0111         if (transaction->resource() == resource) {
0112             return transaction;
0113         }
0114     }
0115 
0116     return nullptr;
0117 }
0118 
0119 QModelIndex TransactionModel::indexOf(Transaction *transaction) const
0120 {
0121     int row = m_transactions.indexOf(transaction);
0122     QModelIndex ret = index(row);
0123     Q_ASSERT(!transaction || ret.isValid());
0124     return ret;
0125 }
0126 
0127 QModelIndex TransactionModel::indexOf(AbstractResource *resource) const
0128 {
0129     Transaction *transaction = transactionFromResource(resource);
0130 
0131     return indexOf(transaction);
0132 }
0133 
0134 void TransactionModel::addTransaction(Transaction *transaction)
0135 {
0136     if (!transaction) {
0137         return;
0138     }
0139 
0140     if (m_transactions.contains(transaction)) {
0141         return;
0142     }
0143 
0144     if (m_transactions.isEmpty()) {
0145         Q_EMIT startingFirstTransaction();
0146     }
0147 
0148     int before = m_transactions.size();
0149     beginInsertRows(QModelIndex(), before, before + 1);
0150     m_transactions.append(transaction);
0151 
0152     if (before == 0) { // Should emit before count changes
0153         Q_EMIT mainTransactionTextChanged();
0154     }
0155     endInsertRows();
0156 
0157     connect(transaction, &Transaction::statusChanged, this, [this, transaction]() {
0158         transactionChanged(transaction, StatusTextRole);
0159     });
0160     connect(transaction, &Transaction::cancellableChanged, this, [this, transaction]() {
0161         transactionChanged(transaction, CancellableRole);
0162     });
0163     connect(transaction, &Transaction::progressChanged, this, [this, transaction]() {
0164         transactionChanged(transaction, ProgressRole);
0165         Q_EMIT progressChanged();
0166     });
0167 
0168     Q_EMIT transactionAdded(transaction);
0169 }
0170 
0171 void TransactionModel::removeTransaction(Transaction *transaction)
0172 {
0173     Q_ASSERT(transaction);
0174     transaction->deleteLater();
0175     int index = m_transactions.indexOf(transaction);
0176     if (index < 0) {
0177         qCWarning(LIBDISCOVER_LOG) << "transaction not part of the model" << transaction;
0178         return;
0179     }
0180 
0181     disconnect(transaction, nullptr, this, nullptr);
0182 
0183     beginRemoveRows(QModelIndex(), index, index);
0184     m_transactions.removeAt(index);
0185     endRemoveRows();
0186 
0187     Q_EMIT transactionRemoved(transaction);
0188     if (m_transactions.isEmpty()) {
0189         Q_EMIT lastTransactionFinished();
0190     }
0191 
0192     if (index == 0) {
0193         Q_EMIT mainTransactionTextChanged();
0194     }
0195 }
0196 
0197 void TransactionModel::transactionChanged(Transaction *transaction, int role)
0198 {
0199     QModelIndex index = indexOf(transaction);
0200     Q_EMIT dataChanged(index, index, {role});
0201 }
0202 
0203 int TransactionModel::progress() const
0204 {
0205     int sum = 0;
0206     int count = 0;
0207     for (const auto transaction : std::as_const(m_transactions)) {
0208         if (transaction->isActive() && transaction->isVisible()) {
0209             sum += transaction->progress();
0210             ++count;
0211         }
0212     }
0213     return count == 0 ? 0 : sum / count;
0214 }
0215 
0216 QString TransactionModel::mainTransactionText() const
0217 {
0218     return m_transactions.isEmpty() ? QString() : m_transactions.constFirst()->name();
0219 }