File indexing completed on 2024-05-19 09:21:27

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 *transaction = m_transactions[index.row()];
0062     switch (role) {
0063     case TransactionRoleRole:
0064         return transaction->role();
0065     case TransactionStatusRole:
0066         return transaction->status();
0067     case CancellableRole:
0068         return transaction->isCancellable();
0069     case ProgressRole:
0070         return transaction->progress();
0071     case StatusTextRole:
0072         switch (transaction->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 (transaction->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 *>(transaction);
0099     case ResourceRole:
0100         return QVariant::fromValue<QObject *>(transaction->resource());
0101     }
0102 
0103     return QVariant();
0104 }
0105 
0106 Transaction *TransactionModel::transactionFromResource(AbstractResource *resource) const
0107 {
0108     for (const auto transaction : std::as_const(m_transactions)) {
0109         if (transaction->resource() == resource) {
0110             return transaction;
0111         }
0112     }
0113 
0114     return nullptr;
0115 }
0116 
0117 QModelIndex TransactionModel::indexOf(Transaction *transaction) const
0118 {
0119     int row = m_transactions.indexOf(transaction);
0120     QModelIndex ret = index(row);
0121     Q_ASSERT(!transaction || ret.isValid());
0122     return ret;
0123 }
0124 
0125 QModelIndex TransactionModel::indexOf(AbstractResource *resource) const
0126 {
0127     Transaction *transaction = transactionFromResource(resource);
0128 
0129     return indexOf(transaction);
0130 }
0131 
0132 void TransactionModel::addTransaction(Transaction *transaction)
0133 {
0134     if (!transaction) {
0135         return;
0136     }
0137 
0138     if (m_transactions.contains(transaction)) {
0139         return;
0140     }
0141 
0142     if (m_transactions.isEmpty()) {
0143         Q_EMIT startingFirstTransaction();
0144     }
0145 
0146     int before = m_transactions.size();
0147     beginInsertRows(QModelIndex(), before, before + 1);
0148     m_transactions.append(transaction);
0149 
0150     if (before == 0) { // Should emit before count changes
0151         Q_EMIT mainTransactionTextChanged();
0152     }
0153     endInsertRows();
0154 
0155     connect(transaction, &Transaction::statusChanged, this, [this, transaction]() {
0156         transactionChanged(transaction, StatusTextRole);
0157     });
0158     connect(transaction, &Transaction::cancellableChanged, this, [this, transaction]() {
0159         transactionChanged(transaction, CancellableRole);
0160     });
0161     connect(transaction, &Transaction::progressChanged, this, [this, transaction]() {
0162         transactionChanged(transaction, ProgressRole);
0163         Q_EMIT progressChanged();
0164     });
0165 
0166     Q_EMIT transactionAdded(transaction);
0167 }
0168 
0169 void TransactionModel::removeTransaction(Transaction *transaction)
0170 {
0171     Q_ASSERT(transaction);
0172     transaction->deleteLater();
0173     int index = m_transactions.indexOf(transaction);
0174     if (index < 0) {
0175         qCWarning(LIBDISCOVER_LOG) << "transaction not part of the model" << transaction;
0176         return;
0177     }
0178 
0179     disconnect(transaction, nullptr, this, nullptr);
0180 
0181     beginRemoveRows(QModelIndex(), index, index);
0182     m_transactions.removeAt(index);
0183     endRemoveRows();
0184 
0185     Q_EMIT transactionRemoved(transaction);
0186     if (m_transactions.isEmpty()) {
0187         Q_EMIT lastTransactionFinished();
0188     }
0189 
0190     if (index == 0) {
0191         Q_EMIT mainTransactionTextChanged();
0192     }
0193 }
0194 
0195 void TransactionModel::transactionChanged(Transaction *transaction, int role)
0196 {
0197     QModelIndex index = indexOf(transaction);
0198     Q_EMIT dataChanged(index, index, {role});
0199 }
0200 
0201 int TransactionModel::progress() const
0202 {
0203     int sum = 0;
0204     int count = 0;
0205     for (const auto transaction : std::as_const(m_transactions)) {
0206         if (transaction->isActive() && transaction->isVisible()) {
0207             sum += transaction->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 }