/* * SPDX-FileCopyrightText: 2012 Jonathan Thomas * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ #include "TransactionModel.h" // Qt includes #include #include #include // Own includes #include "libdiscover_debug.h" #include "resources/AbstractResource.h" Q_GLOBAL_STATIC(TransactionModel, globalTransactionModel) TransactionModel *TransactionModel::global() { return globalTransactionModel; } TransactionModel::TransactionModel(QObject *parent) : QAbstractListModel(parent) { connect(this, &QAbstractItemModel::rowsInserted, this, &TransactionModel::countChanged); connect(this, &QAbstractItemModel::rowsRemoved, this, &TransactionModel::countChanged); connect(this, &TransactionModel::countChanged, this, &TransactionModel::progressChanged); } QHash TransactionModel::roleNames() const { QHash roles; roles[TransactionRoleRole] = "transactionRole"; roles[TransactionStatusRole] = "status"; roles[CancellableRole] = "cancellable"; roles[ProgressRole] = "progress"; roles[StatusTextRole] = "statusText"; roles[ResourceRole] = "resource"; roles[TransactionRole] = "transaction"; return roles; } int TransactionModel::rowCount(const QModelIndex &parent) const { // Root element parents all children if (!parent.isValid()) { return m_transactions.size(); } // Child elements have no children themselves return 0; } QVariant TransactionModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } Transaction *transaction = m_transactions[index.row()]; switch (role) { case TransactionRoleRole: return transaction->role(); case TransactionStatusRole: return transaction->status(); case CancellableRole: return transaction->isCancellable(); case ProgressRole: return transaction->progress(); case StatusTextRole: switch (transaction->status()) { case Transaction::SetupStatus: return i18nc("@info:status", "Starting"); case Transaction::QueuedStatus: return i18nc("@info:status", "Waiting"); case Transaction::DownloadingStatus: return i18nc("@info:status", "Downloading"); case Transaction::CommittingStatus: switch (transaction->role()) { case Transaction::InstallRole: return i18nc("@info:status", "Installing"); case Transaction::RemoveRole: return i18nc("@info:status", "Removing"); case Transaction::ChangeAddonsRole: return i18nc("@info:status", "Changing Addons"); } break; case Transaction::DoneStatus: return i18nc("@info:status", "Done"); case Transaction::DoneWithErrorStatus: return i18nc("@info:status", "Failed"); case Transaction::CancelledStatus: return i18nc("@info:status", "Cancelled"); } break; case TransactionRole: return QVariant::fromValue(transaction); case ResourceRole: return QVariant::fromValue(transaction->resource()); } return QVariant(); } Transaction *TransactionModel::transactionFromResource(AbstractResource *resource) const { for (const auto transaction : std::as_const(m_transactions)) { if (transaction->resource() == resource) { return transaction; } } return nullptr; } QModelIndex TransactionModel::indexOf(Transaction *transaction) const { int row = m_transactions.indexOf(transaction); QModelIndex ret = index(row); Q_ASSERT(!transaction || ret.isValid()); return ret; } QModelIndex TransactionModel::indexOf(AbstractResource *resource) const { Transaction *transaction = transactionFromResource(resource); return indexOf(transaction); } void TransactionModel::addTransaction(Transaction *transaction) { if (!transaction) { return; } if (m_transactions.contains(transaction)) { return; } if (m_transactions.isEmpty()) { Q_EMIT startingFirstTransaction(); } int before = m_transactions.size(); beginInsertRows(QModelIndex(), before, before + 1); m_transactions.append(transaction); if (before == 0) { // Should emit before count changes Q_EMIT mainTransactionTextChanged(); } endInsertRows(); connect(transaction, &Transaction::statusChanged, this, [this, transaction]() { transactionChanged(transaction, StatusTextRole); }); connect(transaction, &Transaction::cancellableChanged, this, [this, transaction]() { transactionChanged(transaction, CancellableRole); }); connect(transaction, &Transaction::progressChanged, this, [this, transaction]() { transactionChanged(transaction, ProgressRole); Q_EMIT progressChanged(); }); Q_EMIT transactionAdded(transaction); } void TransactionModel::removeTransaction(Transaction *transaction) { Q_ASSERT(transaction); transaction->deleteLater(); int index = m_transactions.indexOf(transaction); if (index < 0) { qCWarning(LIBDISCOVER_LOG) << "transaction not part of the model" << transaction; return; } disconnect(transaction, nullptr, this, nullptr); beginRemoveRows(QModelIndex(), index, index); m_transactions.removeAt(index); endRemoveRows(); Q_EMIT transactionRemoved(transaction); if (m_transactions.isEmpty()) { Q_EMIT lastTransactionFinished(); } if (index == 0) { Q_EMIT mainTransactionTextChanged(); } } void TransactionModel::transactionChanged(Transaction *transaction, int role) { QModelIndex index = indexOf(transaction); Q_EMIT dataChanged(index, index, {role}); } int TransactionModel::progress() const { int sum = 0; int count = 0; for (const auto transaction : std::as_const(m_transactions)) { if (transaction->isActive() && transaction->isVisible()) { sum += transaction->progress(); ++count; } } return count == 0 ? 0 : sum / count; } QString TransactionModel::mainTransactionText() const { return m_transactions.isEmpty() ? QString() : m_transactions.constFirst()->name(); } #include "moc_TransactionModel.cpp"