Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RPC][QT] UI support for abandoned transactions #7707

Merged
merged 1 commit into from Apr 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Makefile.qt.include
Expand Up @@ -268,7 +268,8 @@ RES_ICONS = \
qt/res/icons/tx_output.png \
qt/res/icons/tx_mined.png \
qt/res/icons/warning.png \
qt/res/icons/verify.png
qt/res/icons/verify.png \
qt/res/icons/transaction_abandoned.png
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Document the author?

Edit: Also you may want to consider adding the source svg and running optimize png, if not already done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already optimized and the source SVG is not required because it's part of Hutchings typicons (we only added sourced for manipulated or self-created icons).
Also src/qt/res/icons/transaction*.png covers the copyright info in contrib/debian/copyright.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clarifying, I haven't seen this is unmodified.


BITCOIN_QT_CPP = \
qt/bantablemodel.cpp \
Expand Down
1 change: 1 addition & 0 deletions src/qt/bitcoin.qrc
Expand Up @@ -49,6 +49,7 @@
<file alias="fontbigger">res/icons/fontbigger.png</file>
<file alias="fontsmaller">res/icons/fontsmaller.png</file>
<file alias="prompticon">res/icons/chevron.png</file>
<file alias="transaction_abandoned">res/icons/transaction_abandoned.png</file>
</qresource>
<qresource prefix="/movies">
<file alias="spinner-000">res/movies/spinner-000.png</file>
Expand Down
2 changes: 2 additions & 0 deletions src/qt/guiconstants.h
Expand Up @@ -29,6 +29,8 @@ static const bool DEFAULT_SPLASHSCREEN = true;
#define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255)
/* Transaction list -- TX status decoration - offline */
#define COLOR_TX_STATUS_OFFLINE QColor(192, 192, 192)
/* Transaction list -- TX status decoration - danger, tx needs attention */
#define COLOR_TX_STATUS_DANGER QColor(200, 100, 100)
/* Transaction list -- TX status decoration - default color */
#define COLOR_BLACK QColor(0, 0, 0)

Expand Down
Binary file added src/qt/res/icons/transaction_abandoned.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/qt/transactiondesc.cpp
Expand Up @@ -39,7 +39,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
return tr("%1/offline").arg(nDepth);
else if (nDepth == 0)
return tr("0/unconfirmed, %1").arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool")));
return tr("0/unconfirmed, %1").arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool"))) + (wtx.isAbandoned() ? ", "+tr("abandoned") : "");
else if (nDepth < 6)
return tr("%1/unconfirmed").arg(nDepth);
else
Expand Down
2 changes: 2 additions & 0 deletions src/qt/transactionrecord.cpp
Expand Up @@ -239,6 +239,8 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
else if (status.depth == 0)
{
status.status = TransactionStatus::Unconfirmed;
if (wtx.isAbandoned())
status.status = TransactionStatus::Abandoned;
}
else if (status.depth < RecommendedNumConfirmations)
{
Expand Down
1 change: 1 addition & 0 deletions src/qt/transactionrecord.h
Expand Up @@ -33,6 +33,7 @@ class TransactionStatus
Unconfirmed, /**< Not yet mined into a block **/
Confirming, /**< Confirmed, but waiting for the recommended number of confirmations **/
Conflicted, /**< Conflicts with other transaction or mempool **/
Abandoned, /**< Abandoned from the wallet **/
/// Generated (mined) transactions
Immature, /**< Mined but waiting for maturity */
MaturesWarning, /**< Transaction will likely not mature because no nodes have confirmed */
Expand Down
10 changes: 10 additions & 0 deletions src/qt/transactiontablemodel.cpp
Expand Up @@ -312,6 +312,9 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
case TransactionStatus::Unconfirmed:
status = tr("Unconfirmed");
break;
case TransactionStatus::Abandoned:
status = tr("Abandoned");
break;
case TransactionStatus::Confirming:
status = tr("Confirming (%1 of %2 recommended confirmations)").arg(wtx->status.depth).arg(TransactionRecord::RecommendedNumConfirmations);
break;
Expand Down Expand Up @@ -468,6 +471,8 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx)
return COLOR_TX_STATUS_OFFLINE;
case TransactionStatus::Unconfirmed:
return QIcon(":/icons/transaction_0");
case TransactionStatus::Abandoned:
return QIcon(":/icons/transaction_abandoned");
case TransactionStatus::Confirming:
switch(wtx->status.depth)
{
Expand Down Expand Up @@ -573,6 +578,11 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case Qt::TextAlignmentRole:
return column_alignments[index.column()];
case Qt::ForegroundRole:
// Use the "danger" color for abandoned transactions
if(rec->status.status == TransactionStatus::Abandoned)
{
return COLOR_TX_STATUS_DANGER;
}
// Non-confirmed (but not immature) as transactions are grey
if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature)
{
Expand Down
33 changes: 31 additions & 2 deletions src/qt/transactionview.cpp
Expand Up @@ -37,7 +37,7 @@

TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) :
QWidget(parent), model(0), transactionProxyModel(0),
transactionView(0)
transactionView(0), abandonAction(0)
{
// Build filter row
setContentsMargins(0,0,0,0);
Expand Down Expand Up @@ -137,6 +137,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
transactionView = view;

// Actions
abandonAction = new QAction(tr("Abandon transaction"), this);
QAction *copyAddressAction = new QAction(tr("Copy address"), this);
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
Expand All @@ -153,8 +154,10 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
contextMenu->addAction(copyTxIDAction);
contextMenu->addAction(copyTxHexAction);
contextMenu->addAction(copyTxPlainText);
contextMenu->addAction(editLabelAction);
contextMenu->addAction(showDetailsAction);
contextMenu->addSeparator();
contextMenu->addAction(abandonAction);
contextMenu->addAction(editLabelAction);

mapperThirdPartyTxUrls = new QSignalMapper(this);

Expand All @@ -170,6 +173,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex)));
connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));

connect(abandonAction, SIGNAL(triggered()), this, SLOT(abandonTx()));
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
Expand Down Expand Up @@ -360,12 +364,37 @@ void TransactionView::exportClicked()
void TransactionView::contextualMenu(const QPoint &point)
{
QModelIndex index = transactionView->indexAt(point);
QModelIndexList selection = transactionView->selectionModel()->selectedRows(0);

// check if transaction can be abandoned, disable context menu action in case it doesn't
uint256 hash;
hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString());
abandonAction->setEnabled(model->transactionCanBeAbandoned(hash));

if(index.isValid())
{
contextMenu->exec(QCursor::pos());
}
}

void TransactionView::abandonTx()
{
if(!transactionView || !transactionView->selectionModel())
return;
QModelIndexList selection = transactionView->selectionModel()->selectedRows(0);

// get the hash from the TxHashRole (QVariant / QString)
uint256 hash;
QString hashQStr = selection.at(0).data(TransactionTableModel::TxHashRole).toString();
hash.SetHex(hashQStr.toStdString());

// Abandon the wallet transaction over the walletModel
model->abandonTransaction(hash);

// Update the table
model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false);
}

void TransactionView::copyAddress()
{
GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole);
Expand Down
2 changes: 2 additions & 0 deletions src/qt/transactionview.h
Expand Up @@ -75,6 +75,7 @@ class TransactionView : public QWidget
QFrame *dateRangeWidget;
QDateTimeEdit *dateFrom;
QDateTimeEdit *dateTo;
QAction *abandonAction;

QWidget *createDateRangeWidget();

Expand All @@ -97,6 +98,7 @@ private Q_SLOTS:
void copyTxPlainText();
void openThirdPartyTxUrl(QString url);
void updateWatchOnlyColumn(bool fHaveWatchOnly);
void abandonTx();

Q_SIGNALS:
void doubleClicked(const QModelIndex&);
Expand Down
15 changes: 15 additions & 0 deletions src/qt/walletmodel.cpp
Expand Up @@ -668,3 +668,18 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t
else
return wallet->AddDestData(dest, key, sRequest);
}

bool WalletModel::transactionCanBeAbandoned(uint256 hash) const
{
LOCK2(cs_main, wallet->cs_wallet);
const CWalletTx *wtx = wallet->GetWalletTx(hash);
if (!wtx || wtx->isAbandoned() || wtx->GetDepthInMainChain() > 0 || wtx->InMempool())
return false;
return true;
}

bool WalletModel::abandonTransaction(uint256 hash) const
{
LOCK2(cs_main, wallet->cs_wallet);
return wallet->AbandonTransaction(hash);
}
3 changes: 3 additions & 0 deletions src/qt/walletmodel.h
Expand Up @@ -200,6 +200,9 @@ class WalletModel : public QObject
void loadReceiveRequests(std::vector<std::string>& vReceiveRequests);
bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest);

bool transactionCanBeAbandoned(uint256 hash) const;
bool abandonTransaction(uint256 hash) const;

private:
CWallet *wallet;
bool fHaveWatchOnly;
Expand Down