Coins: cleanup

This commit is contained in:
tobtoht 2025-03-11 21:45:38 +01:00
parent 4aa571454b
commit c5f010bd3c
No known key found for this signature in database
GPG key ID: E45B10DD027D2472
12 changed files with 278 additions and 444 deletions

View file

@ -121,12 +121,15 @@ void CoinsWidget::showContextMenu(const QPoint &point) {
menu->addAction(m_sweepOutputsAction);
}
else {
CoinsInfo* c = this->currentEntry();
if (!c) return;
auto index = this->getCurrentIndex();
if (!index.isValid()) {
return;
}
const CoinsInfo& c = m_model->entryFromIndex(index);
bool isSpent = c->spent();
bool isFrozen = c->frozen();
bool isUnlocked = c->unlocked();
bool isSpent = c.spent;
bool isFrozen = c.frozen;
bool isUnlocked = c.unlocked;
menu->addAction(m_spendAction);
menu->addMenu(m_copyMenu);
@ -170,7 +173,7 @@ QStringList CoinsWidget::selectedPubkeys() {
QStringList pubkeys;
for (QModelIndex index: list) {
pubkeys << m_model->entryFromIndex(m_proxyModel->mapToSource(index))->pubKey();
pubkeys << m_model->entryFromIndex(m_proxyModel->mapToSource(index)).pubKey;
}
return pubkeys;
}
@ -186,16 +189,19 @@ void CoinsWidget::thawAllSelected() {
}
void CoinsWidget::spendSelected() {
QVector<CoinsInfo*> selectedCoins = this->currentEntries();
QStringList keyimages;
QModelIndexList selectedRows = ui->coins->selectionModel()->selectedRows();
for (const auto coin : selectedCoins) {
if (!coin) return;
QStringList keyimages;
for (const auto index : selectedRows) {
if (!index.isValid()) {
return;
}
const CoinsInfo& coin = m_model->entryFromIndex(m_proxyModel->mapToSource(index));
bool spendable = this->isCoinSpendable(coin);
if (!spendable) return;
QString keyImage = coin->keyImage();
QString keyImage = coin.keyImage;
keyimages << keyImage;
}
@ -204,8 +210,11 @@ void CoinsWidget::spendSelected() {
}
void CoinsWidget::viewOutput() {
CoinsInfo* c = this->currentEntry();
if (!c) return;
auto index = this->getCurrentIndex();
if (!index.isValid()) {
return;
}
const CoinsInfo& c = m_model->entryFromIndex(index);
auto * dialog = new OutputInfoDialog(c, this);
dialog->show();
@ -224,19 +233,23 @@ void CoinsWidget::onSweepOutputs() {
return;
}
QVector<CoinsInfo*> selectedCoins = this->currentEntries();
QModelIndexList selectedRows = ui->coins->selectionModel()->selectedRows();
QVector<QString> keyImages;
quint64 totalAmount = 0;
for (const auto coin : selectedCoins) {
if (!coin) return;
for (const auto index : selectedRows) {
if (!index.isValid()) {
return;
}
const CoinsInfo& coin = m_model->entryFromIndex(m_proxyModel->mapToSource(index));
bool spendable = this->isCoinSpendable(coin);
if (!spendable) return;
QString keyImage = coin->keyImage();
QString keyImage = coin.keyImage;
keyImages.push_back(keyImage);
totalAmount += coin->amount();
totalAmount += coin.amount;
}
OutputSweepDialog dialog{this, totalAmount};
@ -270,59 +283,44 @@ void CoinsWidget::onSweepOutputs() {
}
void CoinsWidget::copy(copyField field) {
CoinsInfo* c = this->currentEntry();
if (!c) return;
auto index = this->getCurrentIndex();
if (!index.isValid()) {
return;
}
const CoinsInfo& c = m_model->entryFromIndex(index);
QString data;
switch (field) {
case PubKey:
data = c->pubKey();
data = c.pubKey;
break;
case KeyImage:
data = c->keyImage();
data = c.keyImage;
break;
case TxID:
data = c->hash();
data = c.hash;
break;
case Address:
data = c->address();
data = c.address;
break;
case Label: {
if (!c->description().isEmpty())
data = c->description();
if (!c.description.isEmpty())
data = c.description;
else
data = c->addressLabel();
data = c.getAddressLabel();
break;
}
case Height:
data = QString::number(c->blockHeight());
data = QString::number(c.blockHeight);
break;
case Amount:
data = c->displayAmount();
data = c.displayAmount();
break;
}
Utils::copyToClipboard(data);
}
CoinsInfo* CoinsWidget::currentEntry() {
QModelIndexList list = ui->coins->selectionModel()->selectedRows();
if (list.size() == 1) {
return m_model->entryFromIndex(m_proxyModel->mapToSource(list.first()));
} else {
return nullptr;
}
}
QVector<CoinsInfo*> CoinsWidget::currentEntries() {
QModelIndexList list = ui->coins->selectionModel()->selectedRows();
QVector<CoinsInfo*> selectedCoins;
for (const auto index : list) {
selectedCoins.push_back(m_model->entryFromIndex(m_proxyModel->mapToSource(index)));
}
return selectedCoins;
}
void CoinsWidget::freezeCoins(QStringList &pubkeys) {
m_wallet->coins()->freeze(pubkeys);
m_wallet->updateBalance();
@ -344,23 +342,23 @@ void CoinsWidget::editLabel() {
ui->coins->edit(index);
}
bool CoinsWidget::isCoinSpendable(CoinsInfo *coin) {
if (!coin->keyImageKnown()) {
bool CoinsWidget::isCoinSpendable(const CoinsInfo &coin) {
if (!coin.keyImageKnown) {
Utils::showError(this, "Unable to spend outputs", "Selected output has unknown key image");
return false;
}
if (coin->spent()) {
if (coin.spent) {
Utils::showError(this, "Unable to spend outputs", "Selected output was already spent");
return false;
}
if (coin->frozen()) {
if (coin.frozen) {
Utils::showError(this, "Unable to spend outputs", "Selected output is frozen", {"Thaw the selected output(s) before spending"}, "freeze_thaw_outputs");
return false;
}
if (!coin->unlocked()) {
if (!coin.unlocked) {
Utils::showError(this, "Unable to spend outputs", "Selected output is locked", {"Wait until the output has reached the required number of confirmation before spending."});
return false;
}
@ -368,4 +366,14 @@ bool CoinsWidget::isCoinSpendable(CoinsInfo *coin) {
return true;
}
QModelIndex CoinsWidget::getCurrentIndex()
{
QModelIndexList list = ui->coins->selectionModel()->selectedRows();
if (list.length() < 1) {
return {};
}
return m_proxyModel->mapToSource(list.first());
}
CoinsWidget::~CoinsWidget() = default;

View file

@ -83,10 +83,9 @@ private:
void showContextMenu(const QPoint & point);
void copy(copyField field);
CoinsInfo* currentEntry();
QVector<CoinsInfo*> currentEntries();
QStringList selectedPubkeys();
bool isCoinSpendable(CoinsInfo* coin);
bool isCoinSpendable(const CoinsInfo& coin);
QModelIndex getCurrentIndex();
};

View file

@ -1673,11 +1673,7 @@ void MainWindow::onSelectedInputsChanged(const QStringList &selectedInputs) {
ui->frame_coinControl->setVisible(numInputs > 0);
if (numInputs > 0) {
quint64 totalAmount = 0;
auto coins = m_wallet->coins()->coinsFromKeyImage(selectedInputs);
for (const auto coin : coins) {
totalAmount += coin->amount();
}
quint64 totalAmount = m_wallet->coins()->sumAmounts(selectedInputs);
QString text = QString("Coin control active: %1 selected outputs, %2 XMR").arg(QString::number(numInputs), WalletManager::displayAmount(totalAmount));
ui->label_coinControl->setText(text);

View file

@ -6,7 +6,7 @@
#include "utils/Utils.h"
OutputInfoDialog::OutputInfoDialog(CoinsInfo *cInfo, QWidget *parent)
OutputInfoDialog::OutputInfoDialog(const CoinsInfo &cInfo, QWidget *parent)
: WindowModalDialog(parent)
, ui(new Ui::OutputInfoDialog)
{
@ -18,19 +18,19 @@ OutputInfoDialog::OutputInfoDialog(CoinsInfo *cInfo, QWidget *parent)
ui->label_txid->setFont(font);
ui->label_address->setFont(font);
ui->label_pubKey->setText(cInfo->pubKey());
ui->label_keyImage->setText(cInfo->keyImage());
ui->label_txid->setText(cInfo->hash());
ui->label_address->setText(cInfo->address());
ui->label_pubKey->setText(cInfo.pubKey);
ui->label_keyImage->setText(cInfo.keyImage);
ui->label_txid->setText(cInfo.hash);
ui->label_address->setText(cInfo.address);
QString status = cInfo->spent() ? "spent" : (cInfo->frozen() ? "frozen" : "unspent");
QString status = cInfo.spent ? "spent" : (cInfo.frozen ? "frozen" : "unspent");
ui->label_status->setText(status);
ui->label_amount->setText(QString("%1 XMR").arg(cInfo->displayAmount()));
ui->label_creationHeight->setText(QString::number(cInfo->blockHeight()));
ui->label_globalIndex->setText(QString::number(cInfo->globalOutputIndex()));
ui->label_internalIndex->setText(QString::number(cInfo->internalOutputIndex()));
ui->label_amount->setText(QString("%1 XMR").arg(cInfo.displayAmount()));
ui->label_creationHeight->setText(QString::number(cInfo.blockHeight));
ui->label_globalIndex->setText(QString::number(cInfo.globalOutputIndex));
ui->label_internalIndex->setText(QString::number(cInfo.internalOutputIndex));
QString spentHeight = QVariant(cInfo->spentHeight()).toString();
QString spentHeight = QVariant(cInfo.spentHeight).toString();
if (spentHeight == "0") spentHeight = "n/a";
ui->label_spentHeight->setText(spentHeight);

View file

@ -19,12 +19,11 @@ class OutputInfoDialog : public WindowModalDialog
Q_OBJECT
public:
explicit OutputInfoDialog(CoinsInfo *cInfo, QWidget *parent = nullptr);
explicit OutputInfoDialog(const CoinsInfo &cInfo, QWidget *parent = nullptr);
~OutputInfoDialog() override;
private:
QScopedPointer<Ui::OutputInfoDialog> ui;
};
#endif //FEATHER_OUTPUTINFODIALOG_H

View file

@ -13,23 +13,17 @@ Coins::Coins(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent)
}
bool Coins::coin(int index, std::function<void (CoinsInfo &)> callback)
const QList<CoinsInfo>& Coins::getRows()
{
QReadLocker locker(&m_lock);
if (index < 0 || index >= m_rows.size()) {
qCritical("%s: no transaction info for index %d", __FUNCTION__, index);
qCritical("%s: there's %lld transactions in backend", __FUNCTION__, m_rows.count());
return false;
}
callback(*m_rows.value(index));
return true;
return m_rows;
}
CoinsInfo* Coins::coin(int index)
const CoinsInfo& Coins::getRow(const qsizetype i)
{
return m_rows.value(index);
if (i < 0 || i >= m_rows.size()) {
throw std::out_of_range("Index out of range");
}
return m_rows[i];
}
void Coins::refresh()
@ -54,30 +48,30 @@ void Coins::refresh()
continue;
}
auto ci = new CoinsInfo(this);
ci->m_blockHeight = td.m_block_height;
ci->m_hash = QString::fromStdString(epee::string_tools::pod_to_hex(td.m_txid));
ci->m_internalOutputIndex = td.m_internal_output_index;
ci->m_globalOutputIndex = td.m_global_output_index;
ci->m_spent = td.m_spent;
ci->m_frozen = td.m_frozen;
ci->m_spentHeight = td.m_spent_height;
ci->m_amount = td.m_amount;
ci->m_rct = td.m_rct;
ci->m_keyImageKnown = td.m_key_image_known;
ci->m_pkIndex = td.m_pk_index;
ci->m_subaddrIndex = td.m_subaddr_index.minor;
ci->m_subaddrAccount = td.m_subaddr_index.major;
ci->m_address = QString::fromStdString(m_wallet2->get_subaddress_as_str(td.m_subaddr_index)); // todo: this is expensive, cache maybe?
ci->m_addressLabel = QString::fromStdString(m_wallet2->get_subaddress_label(td.m_subaddr_index));
ci->m_txNote = QString::fromStdString(m_wallet2->get_tx_note(td.m_txid));
ci->m_keyImage = QString::fromStdString(epee::string_tools::pod_to_hex(td.m_key_image));
ci->m_unlockTime = td.m_tx.unlock_time;
ci->m_unlocked = m_wallet2->is_transfer_unlocked(td);
ci->m_pubKey = QString::fromStdString(epee::string_tools::pod_to_hex(td.get_public_key()));
ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen);
ci->m_description = m_wallet->getCacheAttribute(QString("coin.description:%1").arg(ci->m_pubKey));
ci->m_change = m_wallet2->is_change(td);
CoinsInfo ci;
ci.blockHeight = td.m_block_height;
ci.hash = QString::fromStdString(epee::string_tools::pod_to_hex(td.m_txid));
ci.internalOutputIndex = td.m_internal_output_index;
ci.globalOutputIndex = td.m_global_output_index;
ci.spent = td.m_spent;
ci.frozen = td.m_frozen;
ci.spentHeight = td.m_spent_height;
ci.amount = td.m_amount;
ci.rct = td.m_rct;
ci.keyImageKnown = td.m_key_image_known;
ci.pkIndex = td.m_pk_index;
ci.subaddrIndex = td.m_subaddr_index.minor;
ci.subaddrAccount = td.m_subaddr_index.major;
ci.address = QString::fromStdString(m_wallet2->get_subaddress_as_str(td.m_subaddr_index)); // todo: this is expensive, cache maybe?
ci.addressLabel = QString::fromStdString(m_wallet2->get_subaddress_label(td.m_subaddr_index));
ci.txNote = QString::fromStdString(m_wallet2->get_tx_note(td.m_txid));
ci.keyImage = QString::fromStdString(epee::string_tools::pod_to_hex(td.m_key_image));
ci.unlockTime = td.m_tx.unlock_time;
ci.unlocked = m_wallet2->is_transfer_unlocked(td);
ci.pubKey = QString::fromStdString(epee::string_tools::pod_to_hex(td.get_public_key()));
ci.coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen);
ci.description = m_wallet->getCacheAttribute(QString("coin.description:%1").arg(ci.pubKey));
ci.change = m_wallet2->is_change(td);
m_rows.push_back(ci);
}
@ -90,10 +84,10 @@ void Coins::refreshUnlocked()
{
QWriteLocker locker(&m_lock);
for (CoinsInfo* c : m_rows) {
if (!c->unlocked()) {
bool unlocked = m_wallet2->is_transfer_unlocked(c->unlockTime(), c->blockHeight());
c->setUnlocked(unlocked);
for (CoinsInfo& c : m_rows) {
if (!c.unlocked) {
bool unlocked = m_wallet2->is_transfer_unlocked(c.unlockTime, c.blockHeight);
c.setUnlocked(unlocked);
}
}
}
@ -153,30 +147,21 @@ void Coins::thaw(QStringList &publicKeys)
refresh();
}
QVector<CoinsInfo*> Coins::coins_from_txid(const QString &txid)
{
QVector<CoinsInfo*> coins;
for (int i = 0; i < this->count(); i++) {
CoinsInfo* coin = this->coin(i);
if (coin->hash() == txid) {
coins.append(coin);
quint64 Coins::sumAmounts(const QStringList &keyImages) {
quint64 amount = 0;
for (const CoinsInfo& coin : m_rows) {
if (!coin.keyImageKnown) {
continue;
}
}
return coins;
}
QVector<CoinsInfo*> Coins::coinsFromKeyImage(const QStringList &keyimages) {
QVector<CoinsInfo*> coins;
for (int i = 0; i < this->count(); i++) {
CoinsInfo* coin = this->coin(i);
if (coin->keyImageKnown() && keyimages.contains(coin->keyImage())) {
coins.append(coin);
if (!keyImages.contains(coin.keyImage)) {
continue;
}
amount += coin.amount;
}
return coins;
return amount;
}
void Coins::setDescription(const QString &publicKey, quint32 accountIndex, const QString &description)
@ -187,6 +172,5 @@ void Coins::setDescription(const QString &publicKey, quint32 accountIndex, const
}
void Coins::clearRows() {
qDeleteAll(m_rows);
m_rows.clear();
}
}

View file

@ -4,8 +4,6 @@
#ifndef FEATHER_COINS_H
#define FEATHER_COINS_H
#include <functional>
#include <QObject>
#include <QList>
#include <QReadWriteLock>
@ -27,16 +25,17 @@ class Coins : public QObject
Q_OBJECT
public:
bool coin(int index, std::function<void (CoinsInfo &)> callback);
CoinsInfo * coin(int index);
const QList<CoinsInfo>& getRows();
const CoinsInfo& getRow(qsizetype i);
void refresh();
void refreshUnlocked();
void freeze(QStringList &publicKeys);
void thaw(QStringList &publicKeys);
QVector<CoinsInfo*> coins_from_txid(const QString &txid);
QVector<CoinsInfo*> coinsFromKeyImage(const QStringList &keyimages);
quint64 sumAmounts(const QStringList &keyImages);
void setDescription(const QString &publicKey, quint32 accountIndex, const QString &description);
quint64 count() const;
@ -55,7 +54,7 @@ private:
Wallet *m_wallet;
tools::wallet2 *m_wallet2;
QList<CoinsInfo*> m_rows;
QList<CoinsInfo> m_rows;
mutable QReadWriteLock m_lock;
};

View file

@ -4,144 +4,49 @@
#include "CoinsInfo.h"
#include "libwalletqt/WalletManager.h"
quint64 CoinsInfo::blockHeight() const
{
return m_blockHeight;
}
QString CoinsInfo::hash() const
{
return m_hash;
}
quint64 CoinsInfo::internalOutputIndex() const
{
return m_internalOutputIndex;
}
quint64 CoinsInfo::globalOutputIndex() const
{
return m_globalOutputIndex;
}
bool CoinsInfo::spent() const
{
return m_spent;
}
bool CoinsInfo::frozen() const
{
return m_frozen;
}
quint64 CoinsInfo::spentHeight() const
{
return m_spentHeight;
}
quint64 CoinsInfo::amount() const {
return m_amount;
}
QString CoinsInfo::displayAmount() const
{
return WalletManager::displayAmount(m_amount);
return WalletManager::displayAmount(amount);
}
bool CoinsInfo::rct() const {
return m_rct;
}
bool CoinsInfo::keyImageKnown() const {
return m_keyImageKnown;
}
quint64 CoinsInfo::pkIndex() const {
return m_pkIndex;
}
quint32 CoinsInfo::subaddrIndex() const {
return m_subaddrIndex;
}
quint32 CoinsInfo::subaddrAccount() const {
return m_subaddrAccount;
}
QString CoinsInfo::address() const {
return m_address;
}
QString CoinsInfo::addressLabel() const {
if (m_subaddrIndex == 0) {
if (m_coinbase) {
QString CoinsInfo::getAddressLabel() const {
if (subaddrIndex == 0) {
if (coinbase) {
return "Coinbase";
}
if (m_change) {
if (change) {
return "Change";
}
if (m_addressLabel == "Primary account") {
if (addressLabel == "Primary account") {
return "Primary address";
}
}
return m_addressLabel;
return addressLabel;
}
QString CoinsInfo::keyImage() const {
return m_keyImage;
void CoinsInfo::setUnlocked(bool unlocked_) {
unlocked = unlocked_;
}
quint64 CoinsInfo::unlockTime() const {
return m_unlockTime;
}
bool CoinsInfo::unlocked() const {
return m_unlocked;
}
void CoinsInfo::setUnlocked(bool unlocked) {
m_unlocked = unlocked;
}
QString CoinsInfo::pubKey() const {
return m_pubKey;
}
bool CoinsInfo::coinbase() const {
return m_coinbase;
}
QString CoinsInfo::description() const {
return m_description;
}
bool CoinsInfo::change() const {
return m_change;
}
QString CoinsInfo::txNote() const {
return m_txNote;
}
CoinsInfo::CoinsInfo(QObject *parent)
: QObject(parent)
, m_blockHeight(0)
, m_internalOutputIndex(0)
, m_globalOutputIndex(0)
, m_spent(false)
, m_frozen(false)
, m_spentHeight(0)
, m_amount(0)
, m_rct(false)
, m_keyImageKnown(false)
, m_pkIndex(0)
, m_subaddrIndex(0)
, m_subaddrAccount(0)
, m_unlockTime(0)
, m_unlocked(false)
, m_coinbase(false)
, m_change(false)
CoinsInfo::CoinsInfo()
: blockHeight(0)
, internalOutputIndex(0)
, globalOutputIndex(0)
, spent(false)
, frozen(false)
, spentHeight(0)
, amount(0)
, rct(false)
, keyImageKnown(false)
, pkIndex(0)
, subaddrIndex(0)
, subaddrAccount(0)
, unlockTime(0)
, unlocked(false)
, coinbase(false)
, change(false)
{
}

View file

@ -4,73 +4,39 @@
#ifndef FEATHER_COINSINFO_H
#define FEATHER_COINSINFO_H
#include <QObject>
#include <QDateTime>
#include <QSet>
#include <QString>
class Coins;
class CoinsInfo : public QObject
struct CoinsInfo
{
Q_OBJECT
quint64 blockHeight;
QString hash;
quint64 internalOutputIndex;
quint64 globalOutputIndex;
bool spent;
bool frozen;
quint64 spentHeight;
quint64 amount;
bool rct;
bool keyImageKnown;
quint64 pkIndex;
quint32 subaddrIndex;
quint32 subaddrAccount;
QString address;
QString addressLabel;
QString keyImage;
quint64 unlockTime;
bool unlocked;
QString pubKey;
bool coinbase;
QString description;
bool change;
QString txNote;
public:
quint64 blockHeight() const;
QString hash() const;
quint64 internalOutputIndex() const;
quint64 globalOutputIndex() const;
bool spent() const;
bool frozen() const;
quint64 spentHeight() const;
quint64 amount() const;
QString getAddressLabel() const;
QString displayAmount() const;
bool rct() const;
bool keyImageKnown() const;
quint64 pkIndex() const;
quint32 subaddrIndex() const;
quint32 subaddrAccount() const;
QString address() const;
QString addressLabel() const;
QString keyImage() const;
quint64 unlockTime() const;
bool unlocked() const;
QString pubKey() const;
bool coinbase() const;
QString description() const;
bool change() const;
QString txNote() const;
void setUnlocked(bool unlocked);
private:
explicit CoinsInfo(QObject *parent);
private:
friend class Coins;
quint64 m_blockHeight;
QString m_hash;
quint64 m_internalOutputIndex;
quint64 m_globalOutputIndex;
bool m_spent;
bool m_frozen;
quint64 m_spentHeight;
quint64 m_amount;
bool m_rct;
bool m_keyImageKnown;
quint64 m_pkIndex;
quint32 m_subaddrIndex;
quint32 m_subaddrAccount;
QString m_address;
QString m_addressLabel;
QString m_keyImage;
quint64 m_unlockTime;
bool m_unlocked;
QString m_pubKey;
bool m_coinbase;
QString m_description;
bool m_change;
QString m_txNote;
explicit CoinsInfo();
};
#endif //FEATHER_COINSINFO_H

View file

@ -28,10 +28,6 @@ void CoinsModel::endReset(){
endResetModel();
}
Coins * CoinsModel::coins() const {
return m_coins;
}
int CoinsModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
@ -51,91 +47,81 @@ int CoinsModel::columnCount(const QModelIndex &parent) const
QVariant CoinsModel::data(const QModelIndex &index, int role) const
{
if (!m_coins) {
return QVariant();
const QList<CoinsInfo>& rows = m_coins->getRows();
if (index.row() < 0 || index.row() >= rows.size()) {
return {};
}
const CoinsInfo& row = rows[index.row()];
bool selected = row.keyImageKnown && m_selected.contains(row.keyImage);
if(role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
return parseTransactionInfo(row, index.column(), role);
}
else if (role == Qt::BackgroundRole) {
if (row.spent) {
return QBrush(ColorScheme::RED.asColor(true));
}
if (row.frozen) {
return QBrush(ColorScheme::BLUE.asColor(true));
}
if (!row.unlocked) {
return QBrush(ColorScheme::YELLOW.asColor(true));
}
if (selected) {
return QBrush(ColorScheme::GREEN.asColor(true));
}
}
else if (role == Qt::TextAlignmentRole) {
switch (index.column()) {
case Amount:
return Qt::AlignRight;
}
}
else if (role == Qt::DecorationRole) {
switch (index.column()) {
case KeyImageKnown:
{
if (row.keyImageKnown) {
return QVariant(icons()->icon("eye1.png"));
}
return QVariant(icons()->icon("eye_blind.png"));
}
}
}
else if (role == Qt::FontRole) {
switch(index.column()) {
case PubKey:
case TxID:
case Address:
return Utils::getMonospaceFont();
}
}
else if (role == Qt::ToolTipRole) {
switch(index.column()) {
case KeyImageKnown:
{
if (row.keyImageKnown) {
return "Key image known";
}
return "Key image unknown. Outgoing transactions that include this output will not be detected.";
}
}
if (row.frozen) {
return "Output is frozen.";
}
if (!row.unlocked) {
return "Output is locked (needs more confirmations)";
}
if (row.spent) {
return "Output is spent";
}
if (selected) {
return "Coin selected to be spent";
}
}
if (!index.isValid() || index.row() < 0 || static_cast<quint64>(index.row()) >= m_coins->count())
return QVariant();
QVariant result;
bool found = m_coins->coin(index.row(), [this, &index, &result, &role](const CoinsInfo &cInfo) {
bool selected = cInfo.keyImageKnown() && m_selected.contains(cInfo.keyImage());
if(role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
result = parseTransactionInfo(cInfo, index.column(), role);
}
else if (role == Qt::BackgroundRole) {
if (cInfo.spent()) {
result = QBrush(ColorScheme::RED.asColor(true));
}
else if (cInfo.frozen()) {
result = QBrush(ColorScheme::BLUE.asColor(true));
}
else if (!cInfo.unlocked()) {
result = QBrush(ColorScheme::YELLOW.asColor(true));
}
else if (selected) {
result = QBrush(ColorScheme::GREEN.asColor(true));
}
}
else if (role == Qt::TextAlignmentRole) {
switch (index.column()) {
case Amount:
result = Qt::AlignRight;
}
}
else if (role == Qt::DecorationRole) {
switch (index.column()) {
case KeyImageKnown:
{
if (cInfo.keyImageKnown()) {
result = QVariant(icons()->icon("eye1.png"));
}
else {
result = QVariant(icons()->icon("eye_blind.png"));
}
}
}
}
else if (role == Qt::FontRole) {
switch(index.column()) {
case PubKey:
case TxID:
case Address:
result = Utils::getMonospaceFont();
}
}
else if (role == Qt::ToolTipRole) {
switch(index.column()) {
case KeyImageKnown:
{
if (cInfo.keyImageKnown()) {
result = "Key image known";
} else {
result = "Key image unknown. Outgoing transactions that include this output will not be detected.";
}
}
}
if (cInfo.frozen()) {
result = "Output is frozen.";
}
else if (!cInfo.unlocked()) {
result = "Output is locked (needs more confirmations)";
}
else if (cInfo.spent()) {
result = "Output is spent";
}
else if (selected) {
result = "Coin selected to be spent";
}
}
});
if (!found) {
qCritical("%s: internal error: no transaction info for index %d", __FUNCTION__, index.row());
}
return result;
return {};
}
QVariant CoinsModel::headerData(int section, Qt::Orientation orientation, int role) const
@ -185,15 +171,9 @@ Qt::ItemFlags CoinsModel::flags(const QModelIndex &index) const
bool CoinsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
const int row = index.row();
const CoinsInfo& row = m_coins->getRow(index.row());
QString pubkey;
bool found = m_coins->coin(index.row(), [this, &pubkey](const CoinsInfo &cInfo) {
pubkey = cInfo.pubKey();
});
if (!found) {
return false;
}
QString pubkey = row.pubKey;
switch (index.column()) {
case Label:
@ -218,33 +198,33 @@ QVariant CoinsModel::parseTransactionInfo(const CoinsInfo &cInfo, int column, in
case KeyImageKnown:
return "";
case PubKey:
return cInfo.pubKey().mid(0,8);
return cInfo.pubKey.mid(0,8);
case TxID:
return cInfo.hash().mid(0, 8) + " ";
return cInfo.hash.mid(0, 8) + " ";
case BlockHeight:
return cInfo.blockHeight();
return cInfo.blockHeight;
case Address:
return Utils::displayAddress(cInfo.address(), 1, "");
return Utils::displayAddress(cInfo.address, 1, "");
case Label: {
if (!cInfo.description().isEmpty())
return cInfo.description();
if (!cInfo.txNote().isEmpty())
return cInfo.txNote();
return cInfo.addressLabel();
if (!cInfo.description.isEmpty())
return cInfo.description;
if (!cInfo.txNote.isEmpty())
return cInfo.txNote;
return cInfo.getAddressLabel();
}
case Spent:
return cInfo.spent();
return cInfo.spent;
case SpentHeight:
return cInfo.spentHeight();
return cInfo.spentHeight;
case Amount:
{
if (role == Qt::UserRole) {
return cInfo.amount();
return cInfo.amount;
}
return cInfo.displayAmount();
}
case Frozen:
return cInfo.frozen();
return cInfo.frozen;
default:
{
qCritical() << "Unimplemented role";
@ -265,7 +245,7 @@ void CoinsModel::setSelected(const QStringList &keyimages) {
emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
}
CoinsInfo* CoinsModel::entryFromIndex(const QModelIndex &index) const {
const CoinsInfo& CoinsModel::entryFromIndex(const QModelIndex &index) const {
Q_ASSERT(index.isValid() && index.row() < m_coins->count());
return m_coins->coin(index.row());
}
return m_coins->getRow(index.row());
}

View file

@ -31,8 +31,6 @@ public:
explicit CoinsModel(QObject *parent, Coins *coins);
Coins * coins() const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@ -40,7 +38,7 @@ public:
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
CoinsInfo* entryFromIndex(const QModelIndex &index) const;
const CoinsInfo& entryFromIndex(const QModelIndex &index) const;
void setCurrentSubaddressAccount(quint32 accountIndex);
void setSelected(const QStringList &selected);

View file

@ -26,16 +26,16 @@ void CoinsProxyModel::setSearchFilter(const QString &searchString) {
bool CoinsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
CoinsInfo* coin = m_coins->coin(sourceRow);
const CoinsInfo& coin = m_coins->getRow(sourceRow);
if (!m_showSpent && coin->spent()) {
if (!m_showSpent && coin.spent) {
return false;
}
if (!m_searchRegExp.pattern().isEmpty()) {
return coin->pubKey().contains(m_searchRegExp) || coin->address().contains(m_searchRegExp)
|| coin->hash().contains(m_searchRegExp) || coin->addressLabel().contains(m_searchRegExp)
|| coin->description().contains(m_searchRegExp);
return coin.pubKey.contains(m_searchRegExp) || coin.address.contains(m_searchRegExp)
|| coin.hash.contains(m_searchRegExp) || coin.addressLabel.contains(m_searchRegExp)
|| coin.description.contains(m_searchRegExp);
}
return true;