File indexing completed on 2024-05-05 17:33:23

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     // Child elements have no children themselves
0053     return 0;
0054 }
0055 
0056 QVariant TransactionModel::data(const QModelIndex &index, int role) const
0057 {
0058     if (!index.isValid())
0059         return QVariant();
0060 
0061     Transaction *trans = m_transactions[index.row()];
0062     switch (role) {
0063     case TransactionRoleRole:
0064         return trans->role();
0065     case TransactionStatusRole:
0066         return trans->status();
0067     case CancellableRole:
0068         return trans->isCancellable();
0069     case ProgressRole:
0070         return trans->progress();
0071     case StatusTextRole:
0072         switch (trans->status()) {
0073         case Transaction::SetupStatus:
0074             return i18nc("@info:status", "Starting");
0075         case Transaction::QueuedStatus:
0076             return i18nc("@info:status", "Waiting");
0077         case Transaction::DownloadingStatus:
0078             return i18nc("@info:status", "Downloading");
0079         case Transaction::CommittingStatus:
0080             switch (trans->role()) {
0081             case Transaction::InstallRole:
0082                 return i18nc("@info:status", "Installing");
0083             case Transaction::RemoveRole:
0084                 return i18nc("@info:status", "Removing");
0085             case Transaction::ChangeAddonsRole:
0086                 return i18nc("@info:status", "Changing Addons");
0087             }
0088             break;
0089         case Transaction::DoneStatus:
0090             return i18nc("@info:status", "Done");
0091         case Transaction::DoneWithErrorStatus:
0092             return i18nc("@info:status", "Failed");
0093         case Transaction::CancelledStatus:
0094             return i18nc("@info:status", "Cancelled");
0095         }
0096         break;
0097     case TransactionRole:
0098         return QVariant::fromValue<QObject *>(trans);
0099     case ResourceRole:
0100         return QVariant::fromValue<QObject *>(trans->resource());
0101     }
0102 
0103     return QVariant();
0104 }
0105 
0106 Transaction *TransactionModel::transactionFromResource(AbstractResource *resource) const
0107 {
0108     Transaction *ret = nullptr;
0109 
0110     for (Transaction *trans : qAsConst(m_transactions)) {
0111         if (trans->resource() == resource) {
0112             ret = trans;
0113             break;
0114         }
0115     }
0116 
0117     return ret;
0118 }
0119 
0120 QModelIndex TransactionModel::indexOf(Transaction *trans) const
0121 {
0122     int row = m_transactions.indexOf(trans);
0123     QModelIndex ret = index(row);
0124     Q_ASSERT(!trans || ret.isValid());
0125     return ret;
0126 }
0127 
0128 QModelIndex TransactionModel::indexOf(AbstractResource *res) const
0129 {
0130     Transaction *trans = transactionFromResource(res);
0131 
0132     return indexOf(trans);
0133 }
0134 
0135 void TransactionModel::addTransaction(Transaction *trans)
0136 {
0137     if (!trans)
0138         return;
0139 
0140     if (m_transactions.contains(trans))
0141         return;
0142 
0143     if (m_transactions.isEmpty())
0144         Q_EMIT startingFirstTransaction();
0145 
0146     int before = m_transactions.size();
0147     beginInsertRows(QModelIndex(), before, before + 1);
0148     m_transactions.append(trans);
0149 
0150     if (before == 0) { // Should emit before count changes
0151         Q_EMIT mainTransactionTextChanged();
0152     }
0153     endInsertRows();
0154 
0155     connect(trans, &Transaction::statusChanged, this, [this]() {
0156         transactionChanged(StatusTextRole);
0157     });
0158     connect(trans, &Transaction::cancellableChanged, this, [this]() {
0159         transactionChanged(CancellableRole);
0160     });
0161     connect(trans, &Transaction::progressChanged, this, [this]() {
0162         transactionChanged(ProgressRole);
0163         Q_EMIT progressChanged();
0164     });
0165 
0166     Q_EMIT transactionAdded(trans);
0167 }
0168 
0169 void TransactionModel::removeTransaction(Transaction *trans)
0170 {
0171     Q_ASSERT(trans);
0172     trans->deleteLater();
0173     int r = m_transactions.indexOf(trans);
0174     if (r < 0) {
0175         qCWarning(LIBDISCOVER_LOG) << "transaction not part of the model" << trans;
0176         return;
0177     }
0178 
0179     disconnect(trans, nullptr, this, nullptr);
0180 
0181     beginRemoveRows(QModelIndex(), r, r);
0182     m_transactions.removeAt(r);
0183     endRemoveRows();
0184 
0185     Q_EMIT transactionRemoved(trans);
0186     if (m_transactions.isEmpty())
0187         Q_EMIT lastTransactionFinished();
0188 
0189     if (r == 0) {
0190         Q_EMIT mainTransactionTextChanged();
0191     }
0192 }
0193 
0194 void TransactionModel::transactionChanged(int role)
0195 {
0196     Transaction *trans = qobject_cast<Transaction *>(sender());
0197     QModelIndex transIdx = indexOf(trans);
0198     Q_EMIT dataChanged(transIdx, transIdx, {role});
0199 }
0200 
0201 int TransactionModel::progress() const
0202 {
0203     int sum = 0;
0204     int count = 0;
0205     for (Transaction *t : qAsConst(m_transactions)) {
0206         if (t->isActive() && t->isVisible()) {
0207             sum += t->progress();
0208             ++count;
0209         }
0210     }
0211     return count == 0 ? 0 : sum / count;
0212 }
0213 
0214 QString TransactionModel::mainTransactionText() const
0215 {
0216     return m_transactions.isEmpty() ? QString() : m_transactions.constFirst()->name();
0217 }