mirror of
https://github.com/feather-wallet/feather.git
synced 2025-04-16 18:01:54 +00:00
Coins: cleanup
This commit is contained in:
parent
4aa571454b
commit
c5f010bd3c
12 changed files with 278 additions and 444 deletions
|
@ -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;
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue