diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui
index 74e25f87a..8e136521c 100644
--- a/src/qt/forms/sendcoinsentry.ui
+++ b/src/qt/forms/sendcoinsentry.ui
@@ -1,1276 +1,1283 @@
SendCoinsEntry
0
0
729
150
Qt::TabFocus
false
This is a normal payment.
QFrame::NoFrame
8
4
12
8
-
Pay &To:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
payTo
-
0
-
The Bitcoin address to send the payment to
-
Choose previously used address
:/icons/address-book:/icons/address-book
22
22
Alt+A
-
Paste address from clipboard
:/icons/editpaste:/icons/editpaste
22
22
Alt+P
-
Remove this entry
:/icons/remove:/icons/remove
22
22
-
&Label:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
addAsLabel
-
Enter a label for this address to add it to the list of used addresses
-
A&mount:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
payAmount
-
-
+
-
-
The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.
S&ubtract fee from amount
+ -
+
+
+ Use available balance
+
+
+
-
Message:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
Qt::PlainText
-
Qt::Horizontal
0
0
0
255
255
127
255
255
255
255
255
191
127
127
63
170
170
84
0
0
0
255
255
255
0
0
0
255
255
255
255
255
127
0
0
0
255
255
191
255
255
220
0
0
0
0
0
0
255
255
127
255
255
255
255
255
191
127
127
63
170
170
84
0
0
0
255
255
255
0
0
0
255
255
255
255
255
127
0
0
0
255
255
191
255
255
220
0
0
0
127
127
63
255
255
127
255
255
255
255
255
191
127
127
63
170
170
84
127
127
63
255
255
255
127
127
63
255
255
127
255
255
127
0
0
0
255
255
127
255
255
220
0
0
0
This is an unauthenticated payment request.
true
QFrame::NoFrame
12
-
Pay To:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
0
-
-
Remove this entry
:/icons/remove:/icons/remove
-
Memo:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
Qt::PlainText
-
A&mount:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
payAmount_is
-
false
0
0
0
140
232
119
230
255
224
185
243
171
70
116
59
93
155
79
0
0
0
155
255
147
0
0
0
119
255
233
140
232
119
0
0
0
197
243
187
125
194
122
255
255
220
0
0
0
0
0
0
140
232
119
230
255
224
185
243
171
70
116
59
93
155
79
0
0
0
155
255
147
0
0
0
119
255
233
140
232
119
0
0
0
197
243
187
125
194
122
255
255
220
0
0
0
70
116
59
140
232
119
230
255
224
185
243
171
70
116
59
93
155
79
70
116
59
155
255
147
70
116
59
140
232
119
140
232
119
0
0
0
140
232
119
125
194
122
255
255
220
0
0
0
This is an authenticated payment request.
true
QFrame::NoFrame
12
-
Pay To:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
0
-
Qt::PlainText
-
Remove this entry
:/icons/remove:/icons/remove
-
Memo:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
Qt::PlainText
-
A&mount:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
payAmount_s
-
false
QValidatedLineEdit
QLineEdit
BitcoinAmountField
QLineEdit
1
payTo
addressBookButton
pasteButton
deleteButton
addAsLabel
payAmount
payAmount_is
deleteButton_is
payAmount_s
deleteButton_s
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 659848ac7..23ba782f4 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -1,972 +1,999 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "sendcoinsdialog.h"
#include "ui_sendcoinsdialog.h"
#include "addresstablemodel.h"
#include "bitcoinunits.h"
#include "clientmodel.h"
#include "coincontroldialog.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "platformstyle.h"
#include "sendcoinsentry.h"
#include "walletmodel.h"
#include "chainparams.h"
#include "dstencode.h"
#include "txmempool.h"
#include "ui_interface.h"
#include "validation.h" // mempool and minRelayTxFee
#include "wallet/coincontrol.h"
#include "wallet/wallet.h"
#include
#include
#include
#include
#include
#define SEND_CONFIRM_DELAY 3
SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle,
QWidget *parent)
: QDialog(parent), ui(new Ui::SendCoinsDialog), clientModel(0), model(0),
fNewRecipientAllowed(true), fFeeMinimized(true),
platformStyle(_platformStyle) {
ui->setupUi(this);
if (!_platformStyle->getImagesOnButtons()) {
ui->addButton->setIcon(QIcon());
ui->clearButton->setIcon(QIcon());
ui->sendButton->setIcon(QIcon());
} else {
ui->addButton->setIcon(_platformStyle->SingleColorIcon(":/icons/add"));
ui->clearButton->setIcon(
_platformStyle->SingleColorIcon(":/icons/remove"));
ui->sendButton->setIcon(
_platformStyle->SingleColorIcon(":/icons/send"));
}
GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
addEntry();
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
// Coin Control
connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this,
SLOT(coinControlButtonClicked()));
connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this,
SLOT(coinControlChangeChecked(int)));
connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)),
this, SLOT(coinControlChangeEdited(const QString &)));
// Coin Control: clipboard actions
QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
connect(clipboardQuantityAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardQuantity()));
connect(clipboardAmountAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardAmount()));
connect(clipboardFeeAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardFee()));
connect(clipboardAfterFeeAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardAfterFee()));
connect(clipboardBytesAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardBytes()));
connect(clipboardLowOutputAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardLowOutput()));
connect(clipboardChangeAction, SIGNAL(triggered()), this,
SLOT(coinControlClipboardChange()));
ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
ui->labelCoinControlAmount->addAction(clipboardAmountAction);
ui->labelCoinControlFee->addAction(clipboardFeeAction);
ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
ui->labelCoinControlBytes->addAction(clipboardBytesAction);
ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
ui->labelCoinControlChange->addAction(clipboardChangeAction);
// init transaction fee section
QSettings settings;
if (!settings.contains("fFeeSectionMinimized"))
settings.setValue("fFeeSectionMinimized", true);
// compatibility
if (!settings.contains("nFeeRadio") &&
settings.contains("nTransactionFee") &&
settings.value("nTransactionFee").toLongLong() > 0) {
// custom
settings.setValue("nFeeRadio", 1);
}
if (!settings.contains("nFeeRadio")) {
// recommended
settings.setValue("nFeeRadio", 0);
}
// compatibility
if (!settings.contains("nCustomFeeRadio") &&
settings.contains("nTransactionFee") &&
settings.value("nTransactionFee").toLongLong() > 0) {
// total at least
settings.setValue("nCustomFeeRadio", 1);
}
if (!settings.contains("nCustomFeeRadio")) {
// per kilobyte
settings.setValue("nCustomFeeRadio", 0);
}
if (!settings.contains("nSmartFeeSliderPosition"))
settings.setValue("nSmartFeeSliderPosition", 0);
if (!settings.contains("nTransactionFee"))
settings.setValue("nTransactionFee",
qint64(DEFAULT_TRANSACTION_FEE / SATOSHI));
if (!settings.contains("fPayOnlyMinFee"))
settings.setValue("fPayOnlyMinFee", false);
ui->groupFee->setId(ui->radioSmartFee, 0);
ui->groupFee->setId(ui->radioCustomFee, 1);
ui->groupFee
->button(
std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))
->setChecked(true);
ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0);
ui->groupCustomFee->setId(ui->radioCustomAtLeast, 1);
ui->groupCustomFee
->button(std::max(
0, std::min(1, settings.value("nCustomFeeRadio").toInt())))
->setChecked(true);
ui->customFee->setValue(
int64_t(settings.value("nTransactionFee").toLongLong()) * SATOSHI);
ui->checkBoxMinimumFee->setChecked(
settings.value("fPayOnlyMinFee").toBool());
minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
}
void SendCoinsDialog::setClientModel(ClientModel *_clientModel) {
this->clientModel = _clientModel;
if (_clientModel) {
connect(_clientModel,
SIGNAL(numBlocksChanged(int, QDateTime, double, bool)), this,
SLOT(updateSmartFeeLabel()));
}
}
void SendCoinsDialog::setModel(WalletModel *_model) {
this->model = _model;
if (_model && _model->getOptionsModel()) {
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry *entry = qobject_cast(
ui->entries->itemAt(i)->widget());
if (entry) {
entry->setModel(_model);
}
}
setBalance(_model->getBalance(), _model->getUnconfirmedBalance(),
_model->getImmatureBalance(), _model->getWatchBalance(),
_model->getWatchUnconfirmedBalance(),
_model->getWatchImmatureBalance());
connect(_model, SIGNAL(balanceChanged(Amount, Amount, Amount, Amount,
Amount, Amount)),
this, SLOT(setBalance(Amount, Amount, Amount, Amount, Amount,
Amount)));
connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)),
this, SLOT(updateDisplayUnit()));
updateDisplayUnit();
// Coin Control
connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)),
this, SLOT(coinControlUpdateLabels()));
connect(_model->getOptionsModel(),
SIGNAL(coinControlFeaturesChanged(bool)), this,
SLOT(coinControlFeatureChanged(bool)));
ui->frameCoinControl->setVisible(
_model->getOptionsModel()->getCoinControlFeatures());
coinControlUpdateLabels();
// fee section
connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this,
SLOT(updateSmartFeeLabel()));
connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this,
SLOT(updateGlobalFeeVariables()));
connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this,
SLOT(coinControlUpdateLabels()));
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this,
SLOT(updateFeeSectionControls()));
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this,
SLOT(updateGlobalFeeVariables()));
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this,
SLOT(coinControlUpdateLabels()));
connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this,
SLOT(updateGlobalFeeVariables()));
connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this,
SLOT(coinControlUpdateLabels()));
connect(ui->customFee, SIGNAL(valueChanged()), this,
SLOT(updateGlobalFeeVariables()));
connect(ui->customFee, SIGNAL(valueChanged()), this,
SLOT(coinControlUpdateLabels()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this,
SLOT(setMinimumFee()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this,
SLOT(updateFeeSectionControls()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this,
SLOT(updateGlobalFeeVariables()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this,
SLOT(coinControlUpdateLabels()));
ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000));
updateFeeSectionControls();
updateMinFeeLabel();
updateSmartFeeLabel();
updateGlobalFeeVariables();
// set the smartfee-sliders default value (wallets default conf.target
// or last stored value)
QSettings settings;
if (settings.value("nSmartFeeSliderPosition").toInt() == 0)
ui->sliderSmartFee->setValue(ui->sliderSmartFee->maximum() -
model->getDefaultConfirmTarget() + 2);
else
ui->sliderSmartFee->setValue(
settings.value("nSmartFeeSliderPosition").toInt());
}
}
SendCoinsDialog::~SendCoinsDialog() {
QSettings settings;
settings.setValue("fFeeSectionMinimized", fFeeMinimized);
settings.setValue("nFeeRadio", ui->groupFee->checkedId());
settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId());
settings.setValue("nSmartFeeSliderPosition", ui->sliderSmartFee->value());
settings.setValue("nTransactionFee",
qint64(ui->customFee->value() / SATOSHI));
settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
delete ui;
}
void SendCoinsDialog::on_sendButton_clicked() {
if (!model || !model->getOptionsModel()) return;
QList recipients;
bool valid = true;
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry *entry =
qobject_cast(ui->entries->itemAt(i)->widget());
if (entry) {
if (entry->validate()) {
recipients.append(entry->getValue());
} else {
valid = false;
}
}
}
if (!valid || recipients.isEmpty()) {
return;
}
fNewRecipientAllowed = false;
WalletModel::UnlockContext ctx(model->requestUnlock());
if (!ctx.isValid()) {
// Unlock wallet was cancelled
fNewRecipientAllowed = true;
return;
}
// prepare transaction for getting txFee earlier
WalletModelTransaction currentTransaction(recipients);
WalletModel::SendCoinsReturn prepareStatus;
// Always use a CCoinControl instance, use the CoinControlDialog instance if
// CoinControl has been enabled
CCoinControl ctrl;
if (model->getOptionsModel()->getCoinControlFeatures())
ctrl = *CoinControlDialog::coinControl;
if (ui->radioSmartFee->isChecked())
ctrl.nConfirmTarget =
ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
else
ctrl.nConfirmTarget = 0;
prepareStatus = model->prepareTransaction(currentTransaction, &ctrl);
// process prepareStatus and on error generate message shown to user
processSendCoinsReturn(
prepareStatus,
BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
currentTransaction.getTransactionFee()));
if (prepareStatus.status != WalletModel::OK) {
fNewRecipientAllowed = true;
return;
}
Amount txFee = currentTransaction.getTransactionFee();
// Format confirmation message
QStringList formatted;
for (const SendCoinsRecipient &rcp : currentTransaction.getRecipients()) {
// generate bold amount string
QString amount =
"" + BitcoinUnits::formatHtmlWithUnit(
model->getOptionsModel()->getDisplayUnit(), rcp.amount);
amount.append("");
// generate monospace address string
QString address =
"" + rcp.address;
address.append("");
QString recipientElement;
// normal payment
if (!rcp.paymentRequest.IsInitialized()) {
if (rcp.label.length() > 0) {
// label with address
recipientElement =
tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label));
recipientElement.append(QString(" (%1)").arg(address));
} else {
// just address
recipientElement = tr("%1 to %2").arg(amount, address);
}
} else if (!rcp.authenticatedMerchant.isEmpty()) {
// authenticated payment request
recipientElement =
tr("%1 to %2")
.arg(amount,
GUIUtil::HtmlEscape(rcp.authenticatedMerchant));
} else {
// unauthenticated payment request
recipientElement = tr("%1 to %2").arg(amount, address);
}
formatted.append(recipientElement);
}
QString questionString = tr("Are you sure you want to send?");
questionString.append("
%1");
if (txFee > Amount::zero()) {
// append fee string if a fee is required
questionString.append("
");
questionString.append(BitcoinUnits::formatHtmlWithUnit(
model->getOptionsModel()->getDisplayUnit(), txFee));
questionString.append(" ");
questionString.append(tr("added as transaction fee"));
// append transaction size
questionString.append(
" (" + QString::number(
(double)currentTransaction.getTransactionSize() / 1000) +
" kB)");
}
// add total amount in all subdivision units
questionString.append("
");
Amount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
QStringList alternativeUnits;
for (BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) {
if (u != model->getOptionsModel()->getDisplayUnit())
alternativeUnits.append(
BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
}
questionString.append(
tr("Total Amount %1")
.arg(BitcoinUnits::formatHtmlWithUnit(
model->getOptionsModel()->getDisplayUnit(), totalAmount)));
questionString.append(
QString("
(=%2)")
.arg(alternativeUnits.join(" " + tr("or") + "
")));
SendConfirmationDialog confirmationDialog(
tr("Confirm send coins"), questionString.arg(formatted.join("
")),
SEND_CONFIRM_DELAY, this);
confirmationDialog.exec();
QMessageBox::StandardButton retval =
(QMessageBox::StandardButton)confirmationDialog.result();
if (retval != QMessageBox::Yes) {
fNewRecipientAllowed = true;
return;
}
// now send the prepared transaction
WalletModel::SendCoinsReturn sendStatus =
model->sendCoins(currentTransaction);
// process sendStatus and on error generate message shown to user
processSendCoinsReturn(sendStatus);
if (sendStatus.status == WalletModel::OK) {
accept();
CoinControlDialog::coinControl->UnSelectAll();
coinControlUpdateLabels();
}
fNewRecipientAllowed = true;
}
void SendCoinsDialog::clear() {
// Remove entries until only one left
while (ui->entries->count()) {
ui->entries->takeAt(0)->widget()->deleteLater();
}
addEntry();
updateTabsAndLabels();
}
void SendCoinsDialog::reject() {
clear();
}
void SendCoinsDialog::accept() {
clear();
}
SendCoinsEntry *SendCoinsDialog::addEntry() {
SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
entry->setModel(model);
ui->entries->addWidget(entry);
connect(entry, SIGNAL(removeEntry(SendCoinsEntry *)), this,
SLOT(removeEntry(SendCoinsEntry *)));
+ connect(entry, SIGNAL(useAvailableBalance(SendCoinsEntry *)), this,
+ SLOT(useAvailableBalance(SendCoinsEntry *)));
connect(entry, SIGNAL(payAmountChanged()), this,
SLOT(coinControlUpdateLabels()));
connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this,
SLOT(coinControlUpdateLabels()));
// Focus the field, so that entry can start immediately
entry->clear();
entry->setFocus();
ui->scrollAreaWidgetContents->resize(
ui->scrollAreaWidgetContents->sizeHint());
qApp->processEvents();
QScrollBar *bar = ui->scrollArea->verticalScrollBar();
if (bar) bar->setSliderPosition(bar->maximum());
updateTabsAndLabels();
return entry;
}
void SendCoinsDialog::updateTabsAndLabels() {
setupTabChain(0);
coinControlUpdateLabels();
}
void SendCoinsDialog::removeEntry(SendCoinsEntry *entry) {
entry->hide();
// If the last entry is about to be removed add an empty one
if (ui->entries->count() == 1) addEntry();
entry->deleteLater();
updateTabsAndLabels();
}
QWidget *SendCoinsDialog::setupTabChain(QWidget *prev) {
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry *entry =
qobject_cast(ui->entries->itemAt(i)->widget());
if (entry) {
prev = entry->setupTabChain(prev);
}
}
QWidget::setTabOrder(prev, ui->sendButton);
QWidget::setTabOrder(ui->sendButton, ui->clearButton);
QWidget::setTabOrder(ui->clearButton, ui->addButton);
return ui->addButton;
}
void SendCoinsDialog::setAddress(const QString &address) {
SendCoinsEntry *entry = 0;
// Replace the first entry if it is still unused
if (ui->entries->count() == 1) {
SendCoinsEntry *first =
qobject_cast(ui->entries->itemAt(0)->widget());
if (first->isClear()) {
entry = first;
}
}
if (!entry) {
entry = addEntry();
}
entry->setAddress(address);
}
void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) {
if (!fNewRecipientAllowed) return;
SendCoinsEntry *entry = 0;
// Replace the first entry if it is still unused
if (ui->entries->count() == 1) {
SendCoinsEntry *first =
qobject_cast(ui->entries->itemAt(0)->widget());
if (first->isClear()) {
entry = first;
}
}
if (!entry) {
entry = addEntry();
}
entry->setValue(rv);
updateTabsAndLabels();
}
bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) {
// Just paste the entry, all pre-checks are done in paymentserver.cpp.
pasteEntry(rv);
return true;
}
void SendCoinsDialog::setBalance(const Amount balance,
const Amount unconfirmedBalance,
const Amount immatureBalance,
const Amount watchBalance,
const Amount watchUnconfirmedBalance,
const Amount watchImmatureBalance) {
Q_UNUSED(unconfirmedBalance);
Q_UNUSED(immatureBalance);
Q_UNUSED(watchBalance);
Q_UNUSED(watchUnconfirmedBalance);
Q_UNUSED(watchImmatureBalance);
if (model && model->getOptionsModel()) {
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(
model->getOptionsModel()->getDisplayUnit(), balance));
}
}
void SendCoinsDialog::updateDisplayUnit() {
setBalance(model->getBalance(), Amount::zero(), Amount::zero(),
Amount::zero(), Amount::zero(), Amount::zero());
ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
updateMinFeeLabel();
updateSmartFeeLabel();
}
void SendCoinsDialog::processSendCoinsReturn(
const WalletModel::SendCoinsReturn &sendCoinsReturn,
const QString &msgArg) {
QPair msgParams;
// Default to a warning message, override if error message is needed
msgParams.second = CClientUIInterface::MSG_WARNING;
// This comment is specific to SendCoinsDialog usage of
// WalletModel::SendCoinsReturn.
// WalletModel::TransactionCommitFailed is used only in
// WalletModel::sendCoins() all others are used only in
// WalletModel::prepareTransaction()
switch (sendCoinsReturn.status) {
case WalletModel::InvalidAddress:
msgParams.first =
tr("The recipient address is not valid. Please recheck.");
break;
case WalletModel::InvalidAmount:
msgParams.first = tr("The amount to pay must be larger than 0.");
break;
case WalletModel::AmountExceedsBalance:
msgParams.first = tr("The amount exceeds your balance.");
break;
case WalletModel::AmountWithFeeExceedsBalance:
msgParams.first = tr("The total exceeds your balance when the %1 "
"transaction fee is included.")
.arg(msgArg);
break;
case WalletModel::DuplicateAddress:
msgParams.first = tr("Duplicate address found: addresses should "
"only be used once each.");
break;
case WalletModel::TransactionCreationFailed:
msgParams.first = tr("Transaction creation failed!");
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
case WalletModel::TransactionCommitFailed:
msgParams.first =
tr("The transaction was rejected with the following reason: %1")
.arg(sendCoinsReturn.reasonCommitFailed);
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
case WalletModel::AbsurdFee:
msgParams.first =
tr("A fee higher than %1 is considered an absurdly high fee.")
.arg(BitcoinUnits::formatWithUnit(
model->getOptionsModel()->getDisplayUnit(), maxTxFee));
break;
case WalletModel::PaymentRequestExpired:
msgParams.first = tr("Payment request expired.");
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
// included to prevent a compiler warning.
case WalletModel::OK:
default:
return;
}
Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
}
void SendCoinsDialog::minimizeFeeSection(bool fMinimize) {
ui->labelFeeMinimized->setVisible(fMinimize);
ui->buttonChooseFee->setVisible(fMinimize);
ui->buttonMinimizeFee->setVisible(!fMinimize);
ui->frameFeeSelection->setVisible(!fMinimize);
ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0,
0);
fFeeMinimized = fMinimize;
}
void SendCoinsDialog::on_buttonChooseFee_clicked() {
minimizeFeeSection(false);
}
void SendCoinsDialog::on_buttonMinimizeFee_clicked() {
updateFeeMinimizedLabel();
minimizeFeeSection(true);
}
+void SendCoinsDialog::useAvailableBalance(SendCoinsEntry *entry) {
+ // Get CCoinControl instance if CoinControl is enabled or create a new one.
+ CCoinControl coin_control;
+ if (model->getOptionsModel()->getCoinControlFeatures()) {
+ coin_control = *CoinControlDialog::coinControl;
+ }
+
+ // Calculate available amount to send.
+ Amount amount = model->getBalance(&coin_control);
+ for (int i = 0; i < ui->entries->count(); ++i) {
+ SendCoinsEntry *e =
+ qobject_cast(ui->entries->itemAt(i)->widget());
+ if (e && !e->isHidden() && e != entry) {
+ amount -= e->getValue().amount;
+ }
+ }
+
+ if (amount > Amount::zero()) {
+ entry->checkSubtractFeeFromAmount();
+ entry->setAmount(amount);
+ } else {
+ entry->setAmount(Amount::zero());
+ }
+}
+
void SendCoinsDialog::setMinimumFee() {
ui->radioCustomPerKilobyte->setChecked(true);
ui->customFee->setValue(CWallet::GetRequiredFee(1000));
}
void SendCoinsDialog::updateFeeSectionControls() {
ui->sliderSmartFee->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFee->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFee2->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFee3->setEnabled(ui->radioSmartFee->isChecked());
ui->labelFeeEstimation->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFeeNormal->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFeeFast->setEnabled(ui->radioSmartFee->isChecked());
ui->confirmationTargetLabel->setEnabled(ui->radioSmartFee->isChecked());
ui->checkBoxMinimumFee->setEnabled(ui->radioCustomFee->isChecked());
ui->labelMinFeeWarning->setEnabled(ui->radioCustomFee->isChecked());
ui->radioCustomPerKilobyte->setEnabled(
ui->radioCustomFee->isChecked() &&
!ui->checkBoxMinimumFee->isChecked());
ui->radioCustomAtLeast->setEnabled(
ui->radioCustomFee->isChecked() &&
!ui->checkBoxMinimumFee->isChecked() &&
CoinControlDialog::coinControl->HasSelected());
ui->customFee->setEnabled(ui->radioCustomFee->isChecked() &&
!ui->checkBoxMinimumFee->isChecked());
}
void SendCoinsDialog::updateGlobalFeeVariables() {
if (ui->radioSmartFee->isChecked()) {
int nConfirmTarget =
ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
payTxFee = CFeeRate(Amount::zero());
// set nMinimumTotalFee to 0 to not accidentally pay a custom fee
CoinControlDialog::coinControl->nMinimumTotalFee = Amount::zero();
// show the estimated required time for confirmation
ui->confirmationTargetLabel->setText(
GUIUtil::formatDurationStr(
nConfirmTarget *
model->getChainParams().GetConsensus().nPowTargetSpacing) +
" / " + tr("%n block(s)", "", nConfirmTarget));
} else {
payTxFee = CFeeRate(Amount(ui->customFee->value()));
// if user has selected to set a minimum absolute fee, pass the value to
// coincontrol
// set nMinimumTotalFee to 0 in case of user has selected that the fee
// is per KB
CoinControlDialog::coinControl->nMinimumTotalFee =
ui->radioCustomAtLeast->isChecked() ? ui->customFee->value()
: Amount::zero();
}
}
void SendCoinsDialog::updateFeeMinimizedLabel() {
if (!model || !model->getOptionsModel()) return;
if (ui->radioSmartFee->isChecked())
ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
else {
ui->labelFeeMinimized->setText(
BitcoinUnits::formatWithUnit(
model->getOptionsModel()->getDisplayUnit(),
ui->customFee->value()) +
((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : ""));
}
}
void SendCoinsDialog::updateMinFeeLabel() {
if (model && model->getOptionsModel())
ui->checkBoxMinimumFee->setText(
tr("Pay only the required fee of %1")
.arg(BitcoinUnits::formatWithUnit(
model->getOptionsModel()->getDisplayUnit(),
CWallet::GetRequiredFee(1000)) +
"/kB"));
}
void SendCoinsDialog::updateSmartFeeLabel() {
if (!model || !model->getOptionsModel()) return;
int nBlocksToConfirm =
ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
int estimateFoundAtBlocks = nBlocksToConfirm;
CFeeRate feeRate =
mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
// not enough data => minfee
if (feeRate <= CFeeRate(Amount::zero())) {
ui->labelSmartFee->setText(
BitcoinUnits::formatWithUnit(
model->getOptionsModel()->getDisplayUnit(),
std::max(CWallet::fallbackFee.GetFeePerK(),
CWallet::GetRequiredFee(1000))) +
"/kB");
// (Smart fee not initialized yet. This usually takes a few blocks...)
ui->labelSmartFee2->show();
ui->labelFeeEstimation->setText("");
} else {
ui->labelSmartFee->setText(
BitcoinUnits::formatWithUnit(
model->getOptionsModel()->getDisplayUnit(),
std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) +
"/kB");
ui->labelSmartFee2->hide();
ui->labelFeeEstimation->setText(
tr("Estimated to begin confirmation within %n block(s).", "",
estimateFoundAtBlocks));
}
updateFeeMinimizedLabel();
}
// Coin Control: copy label "Quantity" to clipboard
void SendCoinsDialog::coinControlClipboardQuantity() {
GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
}
// Coin Control: copy label "Amount" to clipboard
void SendCoinsDialog::coinControlClipboardAmount() {
GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(
ui->labelCoinControlAmount->text().indexOf(" ")));
}
// Coin Control: copy label "Fee" to clipboard
void SendCoinsDialog::coinControlClipboardFee() {
GUIUtil::setClipboard(
ui->labelCoinControlFee->text()
.left(ui->labelCoinControlFee->text().indexOf(" "))
.replace(ASYMP_UTF8, ""));
}
// Coin Control: copy label "After fee" to clipboard
void SendCoinsDialog::coinControlClipboardAfterFee() {
GUIUtil::setClipboard(
ui->labelCoinControlAfterFee->text()
.left(ui->labelCoinControlAfterFee->text().indexOf(" "))
.replace(ASYMP_UTF8, ""));
}
// Coin Control: copy label "Bytes" to clipboard
void SendCoinsDialog::coinControlClipboardBytes() {
GUIUtil::setClipboard(
ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
}
// Coin Control: copy label "Dust" to clipboard
void SendCoinsDialog::coinControlClipboardLowOutput() {
GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
}
// Coin Control: copy label "Change" to clipboard
void SendCoinsDialog::coinControlClipboardChange() {
GUIUtil::setClipboard(
ui->labelCoinControlChange->text()
.left(ui->labelCoinControlChange->text().indexOf(" "))
.replace(ASYMP_UTF8, ""));
}
// Coin Control: settings menu - coin control enabled/disabled by user
void SendCoinsDialog::coinControlFeatureChanged(bool checked) {
ui->frameCoinControl->setVisible(checked);
// coin control features disabled
if (!checked && model) CoinControlDialog::coinControl->SetNull();
// make sure we set back the confirmation target
updateGlobalFeeVariables();
coinControlUpdateLabels();
}
// Coin Control: button inputs -> show actual coin control dialog
void SendCoinsDialog::coinControlButtonClicked() {
CoinControlDialog dlg(platformStyle);
dlg.setModel(model);
dlg.exec();
coinControlUpdateLabels();
}
// Coin Control: checkbox custom change address
void SendCoinsDialog::coinControlChangeChecked(int state) {
if (state == Qt::Unchecked) {
CoinControlDialog::coinControl->destChange = CNoDestination();
ui->labelCoinControlChangeLabel->clear();
} else {
// use this to re-validate an already entered address
coinControlChangeEdited(ui->lineEditCoinControlChange->text());
}
ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
}
// Coin Control: custom change address changed
void SendCoinsDialog::coinControlChangeEdited(const QString &text) {
if (model && model->getAddressTableModel()) {
// Default to no change address until verified
CoinControlDialog::coinControl->destChange = CNoDestination();
ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
const CTxDestination dest =
DecodeDestination(text.toStdString(), model->getChainParams());
if (text.isEmpty()) {
// Nothing entered
ui->labelCoinControlChangeLabel->setText("");
} else if (!IsValidDestination(dest)) {
// Invalid address
ui->labelCoinControlChangeLabel->setText(
tr("Warning: Invalid Bitcoin address"));
} else {
// Valid address
if (!model->IsSpendable(dest)) {
ui->labelCoinControlChangeLabel->setText(
tr("Warning: Unknown change address"));
// confirmation dialog
QMessageBox::StandardButton btnRetVal = QMessageBox::question(
this, tr("Confirm custom change address"),
tr("The address you selected for change is not part of "
"this wallet. Any or all funds in your wallet may be "
"sent to this address. Are you sure?"),
QMessageBox::Yes | QMessageBox::Cancel,
QMessageBox::Cancel);
if (btnRetVal == QMessageBox::Yes)
CoinControlDialog::coinControl->destChange = dest;
else {
ui->lineEditCoinControlChange->setText("");
ui->labelCoinControlChangeLabel->setStyleSheet(
"QLabel{color:black;}");
ui->labelCoinControlChangeLabel->setText("");
}
} else {
// Known change address
ui->labelCoinControlChangeLabel->setStyleSheet(
"QLabel{color:black;}");
// Query label
QString associatedLabel =
model->getAddressTableModel()->labelForAddress(text);
if (!associatedLabel.isEmpty())
ui->labelCoinControlChangeLabel->setText(associatedLabel);
else
ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
CoinControlDialog::coinControl->destChange = dest;
}
}
}
}
// Coin Control: update labels
void SendCoinsDialog::coinControlUpdateLabels() {
if (!model || !model->getOptionsModel()) return;
if (model->getOptionsModel()->getCoinControlFeatures()) {
// enable minimum absolute fee UI controls
ui->radioCustomAtLeast->setVisible(true);
// only enable the feature if inputs are selected
ui->radioCustomAtLeast->setEnabled(
ui->radioCustomFee->isChecked() &&
!ui->checkBoxMinimumFee->isChecked() &&
CoinControlDialog::coinControl->HasSelected());
} else {
// in case coin control is disabled (=default), hide minimum absolute
// fee UI controls
ui->radioCustomAtLeast->setVisible(false);
return;
}
// set pay amounts
CoinControlDialog::payAmounts.clear();
CoinControlDialog::fSubtractFeeFromAmount = false;
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry *entry =
qobject_cast(ui->entries->itemAt(i)->widget());
if (entry && !entry->isHidden()) {
SendCoinsRecipient rcp = entry->getValue();
CoinControlDialog::payAmounts.append(rcp.amount);
if (rcp.fSubtractFeeFromAmount)
CoinControlDialog::fSubtractFeeFromAmount = true;
}
}
if (CoinControlDialog::coinControl->HasSelected()) {
// actual coin control calculation
CoinControlDialog::updateLabels(model, this);
// show coin control stats
ui->labelCoinControlAutomaticallySelected->hide();
ui->widgetCoinControl->show();
} else {
// hide coin control stats
ui->labelCoinControlAutomaticallySelected->show();
ui->widgetCoinControl->hide();
ui->labelCoinControlInsuffFunds->hide();
}
}
SendConfirmationDialog::SendConfirmationDialog(const QString &title,
const QString &text,
int _secDelay, QWidget *parent)
: QMessageBox(QMessageBox::Question, title, text,
QMessageBox::Yes | QMessageBox::Cancel, parent),
secDelay(_secDelay) {
setDefaultButton(QMessageBox::Cancel);
yesButton = button(QMessageBox::Yes);
updateYesButton();
connect(&countDownTimer, SIGNAL(timeout()), this, SLOT(countDown()));
}
int SendConfirmationDialog::exec() {
updateYesButton();
countDownTimer.start(1000);
return QMessageBox::exec();
}
void SendConfirmationDialog::countDown() {
secDelay--;
updateYesButton();
if (secDelay <= 0) {
countDownTimer.stop();
}
}
void SendConfirmationDialog::updateYesButton() {
if (secDelay > 0) {
yesButton->setEnabled(false);
yesButton->setText(tr("Yes") + " (" + QString::number(secDelay) + ")");
} else {
yesButton->setEnabled(true);
yesButton->setText(tr("Yes"));
}
}
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index edbfbeda2..866d8eb0b 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -1,128 +1,129 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_QT_SENDCOINSDIALOG_H
#define BITCOIN_QT_SENDCOINSDIALOG_H
#include "walletmodel.h"
#include
#include
#include
#include
class ClientModel;
class OptionsModel;
class PlatformStyle;
class SendCoinsEntry;
class SendCoinsRecipient;
namespace Ui {
class SendCoinsDialog;
}
QT_BEGIN_NAMESPACE
class QUrl;
QT_END_NAMESPACE
/** Dialog for sending bitcoins */
class SendCoinsDialog : public QDialog {
Q_OBJECT
public:
explicit SendCoinsDialog(const PlatformStyle *platformStyle,
QWidget *parent = 0);
~SendCoinsDialog();
void setClientModel(ClientModel *clientModel);
void setModel(WalletModel *model);
/**
* Set up the tab chain manually, as Qt messes up the tab chain by default
* in some cases (issue
* https://bugreports.qt-project.org/browse/QTBUG-10907).
*/
QWidget *setupTabChain(QWidget *prev);
void setAddress(const QString &address);
void pasteEntry(const SendCoinsRecipient &rv);
bool handlePaymentRequest(const SendCoinsRecipient &recipient);
public Q_SLOTS:
void clear();
void reject() override;
void accept() override;
SendCoinsEntry *addEntry();
void updateTabsAndLabels();
void setBalance(const Amount balance, const Amount unconfirmedBalance,
const Amount immatureBalance, const Amount watchOnlyBalance,
const Amount watchUnconfBalance,
const Amount watchImmatureBalance);
private:
Ui::SendCoinsDialog *ui;
ClientModel *clientModel;
WalletModel *model;
bool fNewRecipientAllowed;
bool fFeeMinimized;
const PlatformStyle *platformStyle;
// Process WalletModel::SendCoinsReturn and generate a pair consisting of a
// message and message flags for use in Q_EMIT message().
// Additional parameter msgArg can be used via .arg(msgArg).
void
processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn,
const QString &msgArg = QString());
void minimizeFeeSection(bool fMinimize);
void updateFeeMinimizedLabel();
private Q_SLOTS:
void on_sendButton_clicked();
void on_buttonChooseFee_clicked();
void on_buttonMinimizeFee_clicked();
void removeEntry(SendCoinsEntry *entry);
+ void useAvailableBalance(SendCoinsEntry *entry);
void updateDisplayUnit();
void coinControlFeatureChanged(bool);
void coinControlButtonClicked();
void coinControlChangeChecked(int);
void coinControlChangeEdited(const QString &);
void coinControlUpdateLabels();
void coinControlClipboardQuantity();
void coinControlClipboardAmount();
void coinControlClipboardFee();
void coinControlClipboardAfterFee();
void coinControlClipboardBytes();
void coinControlClipboardLowOutput();
void coinControlClipboardChange();
void setMinimumFee();
void updateFeeSectionControls();
void updateMinFeeLabel();
void updateSmartFeeLabel();
void updateGlobalFeeVariables();
Q_SIGNALS:
// Fired when a message should be reported to the user
void message(const QString &title, const QString &message,
unsigned int style);
};
class SendConfirmationDialog : public QMessageBox {
Q_OBJECT
public:
SendConfirmationDialog(const QString &title, const QString &text,
int secDelay = 0, QWidget *parent = 0);
int exec();
private Q_SLOTS:
void countDown();
void updateYesButton();
private:
QAbstractButton *yesButton;
QTimer countDownTimer;
int secDelay;
};
#endif // BITCOIN_QT_SENDCOINSDIALOG_H
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index 9bf0b124e..b67f691ec 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -1,262 +1,280 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "sendcoinsentry.h"
#include "ui_sendcoinsentry.h"
#include "addressbookpage.h"
#include "addresstablemodel.h"
#include "config.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "platformstyle.h"
#include "walletmodel.h"
#include
#include
SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle,
QWidget *parent)
: QStackedWidget(parent), ui(new Ui::SendCoinsEntry), model(0),
platformStyle(_platformStyle) {
ui->setupUi(this);
ui->addressBookButton->setIcon(
platformStyle->SingleColorIcon(":/icons/address-book"));
ui->pasteButton->setIcon(
platformStyle->SingleColorIcon(":/icons/editpaste"));
ui->deleteButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
ui->deleteButton_is->setIcon(
platformStyle->SingleColorIcon(":/icons/remove"));
ui->deleteButton_s->setIcon(
platformStyle->SingleColorIcon(":/icons/remove"));
ui->messageTextLabel->setToolTip(
tr("A message that was attached to the %1 URI which will be"
" stored with the transaction for your reference. Note: "
"This message will not be sent over the Bitcoin network.")
.arg(GUIUtil::bitcoinURIScheme(GetConfig())));
setCurrentWidget(ui->SendCoins);
if (platformStyle->getUseExtraSpacing()) ui->payToLayout->setSpacing(4);
#if QT_VERSION >= 0x040700
ui->addAsLabel->setPlaceholderText(
tr("Enter a label for this address to add it to your address book"));
#endif
// normal bitcoin address field
GUIUtil::setupAddressWidget(ui->payTo, this);
// just a label for displaying bitcoin address(es)
ui->payTo_is->setFont(GUIUtil::fixedPitchFont());
// Connect signals
connect(ui->payAmount, SIGNAL(valueChanged()), this,
SIGNAL(payAmountChanged()));
connect(ui->checkboxSubtractFeeFromAmount, SIGNAL(toggled(bool)), this,
SIGNAL(subtractFeeFromAmountChanged()));
connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
connect(ui->deleteButton_is, SIGNAL(clicked()), this,
SLOT(deleteClicked()));
connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked()));
+ connect(ui->useAvailableBalanceButton, SIGNAL(clicked()), this,
+ SLOT(useAvailableBalanceClicked()));
}
SendCoinsEntry::~SendCoinsEntry() {
delete ui;
}
void SendCoinsEntry::on_pasteButton_clicked() {
// Paste text from clipboard into recipient field
ui->payTo->setText(QApplication::clipboard()->text());
}
void SendCoinsEntry::on_addressBookButton_clicked() {
if (!model) return;
AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection,
AddressBookPage::SendingTab, this);
dlg.setModel(model->getAddressTableModel());
if (dlg.exec()) {
ui->payTo->setText(dlg.getReturnValue());
ui->payAmount->setFocus();
}
}
void SendCoinsEntry::on_payTo_textChanged(const QString &address) {
updateLabel(address);
}
void SendCoinsEntry::setModel(WalletModel *_model) {
this->model = _model;
if (_model && _model->getOptionsModel())
connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)),
this, SLOT(updateDisplayUnit()));
clear();
}
void SendCoinsEntry::clear() {
// clear UI elements for normal payment
ui->payTo->clear();
ui->addAsLabel->clear();
ui->payAmount->clear();
ui->checkboxSubtractFeeFromAmount->setCheckState(Qt::Unchecked);
ui->messageTextLabel->clear();
ui->messageTextLabel->hide();
ui->messageLabel->hide();
// clear UI elements for unauthenticated payment request
ui->payTo_is->clear();
ui->memoTextLabel_is->clear();
ui->payAmount_is->clear();
// clear UI elements for authenticated payment request
ui->payTo_s->clear();
ui->memoTextLabel_s->clear();
ui->payAmount_s->clear();
// update the display unit, to not use the default ("BCH")
updateDisplayUnit();
}
+void SendCoinsEntry::checkSubtractFeeFromAmount() {
+ ui->checkboxSubtractFeeFromAmount->setChecked(true);
+}
+
void SendCoinsEntry::deleteClicked() {
Q_EMIT removeEntry(this);
}
+void SendCoinsEntry::useAvailableBalanceClicked() {
+ Q_EMIT useAvailableBalance(this);
+}
+
bool SendCoinsEntry::validate() {
- if (!model) return false;
+ if (!model) {
+ return false;
+ }
// Check input validity
bool retval = true;
// Skip checks for payment request
- if (recipient.paymentRequest.IsInitialized()) return retval;
+ if (recipient.paymentRequest.IsInitialized()) {
+ return retval;
+ }
if (!model->validateAddress(ui->payTo->text())) {
ui->payTo->setValid(false);
retval = false;
}
if (!ui->payAmount->validate()) {
retval = false;
}
// Sending a zero amount is invalid
if (ui->payAmount->value(0) <= Amount::zero()) {
ui->payAmount->setValid(false);
retval = false;
}
// Reject dust outputs:
if (retval && GUIUtil::isDust(ui->payTo->text(), ui->payAmount->value(),
model->getChainParams())) {
ui->payAmount->setValid(false);
retval = false;
}
return retval;
}
SendCoinsRecipient SendCoinsEntry::getValue() {
// Payment request
if (recipient.paymentRequest.IsInitialized()) return recipient;
// Normal payment
recipient.address = ui->payTo->text();
recipient.label = ui->addAsLabel->text();
recipient.amount = ui->payAmount->value();
recipient.message = ui->messageTextLabel->text();
recipient.fSubtractFeeFromAmount =
(ui->checkboxSubtractFeeFromAmount->checkState() == Qt::Checked);
return recipient;
}
QWidget *SendCoinsEntry::setupTabChain(QWidget *prev) {
QWidget::setTabOrder(prev, ui->payTo);
QWidget::setTabOrder(ui->payTo, ui->addAsLabel);
QWidget *w = ui->payAmount->setupTabChain(ui->addAsLabel);
QWidget::setTabOrder(w, ui->checkboxSubtractFeeFromAmount);
QWidget::setTabOrder(ui->checkboxSubtractFeeFromAmount,
ui->addressBookButton);
QWidget::setTabOrder(ui->addressBookButton, ui->pasteButton);
QWidget::setTabOrder(ui->pasteButton, ui->deleteButton);
return ui->deleteButton;
}
void SendCoinsEntry::setValue(const SendCoinsRecipient &value) {
recipient = value;
if (recipient.paymentRequest.IsInitialized()) // payment request
{
if (recipient.authenticatedMerchant.isEmpty()) // unauthenticated
{
ui->payTo_is->setText(recipient.address);
ui->memoTextLabel_is->setText(recipient.message);
ui->payAmount_is->setValue(recipient.amount);
ui->payAmount_is->setReadOnly(true);
setCurrentWidget(ui->SendCoins_UnauthenticatedPaymentRequest);
} else // authenticated
{
ui->payTo_s->setText(recipient.authenticatedMerchant);
ui->memoTextLabel_s->setText(recipient.message);
ui->payAmount_s->setValue(recipient.amount);
ui->payAmount_s->setReadOnly(true);
setCurrentWidget(ui->SendCoins_AuthenticatedPaymentRequest);
}
} else // normal payment
{
// message
ui->messageTextLabel->setText(recipient.message);
ui->messageTextLabel->setVisible(!recipient.message.isEmpty());
ui->messageLabel->setVisible(!recipient.message.isEmpty());
ui->addAsLabel->clear();
ui->payTo->setText(
recipient.address); // this may set a label from addressbook
if (!recipient.label.isEmpty()) // if a label had been set from the
// addressbook, don't overwrite with an
// empty label
ui->addAsLabel->setText(recipient.label);
ui->payAmount->setValue(recipient.amount);
}
}
void SendCoinsEntry::setAddress(const QString &address) {
ui->payTo->setText(address);
ui->payAmount->setFocus();
}
+void SendCoinsEntry::setAmount(const Amount amount) {
+ ui->payAmount->setValue(amount);
+}
+
bool SendCoinsEntry::isClear() {
return ui->payTo->text().isEmpty() && ui->payTo_is->text().isEmpty() &&
ui->payTo_s->text().isEmpty();
}
void SendCoinsEntry::setFocus() {
ui->payTo->setFocus();
}
void SendCoinsEntry::updateDisplayUnit() {
if (model && model->getOptionsModel()) {
// Update payAmount with the current unit
ui->payAmount->setDisplayUnit(
model->getOptionsModel()->getDisplayUnit());
ui->payAmount_is->setDisplayUnit(
model->getOptionsModel()->getDisplayUnit());
ui->payAmount_s->setDisplayUnit(
model->getOptionsModel()->getDisplayUnit());
}
}
bool SendCoinsEntry::updateLabel(const QString &address) {
if (!model) return false;
// Fill in label from address book, if address has an associated label
QString associatedLabel =
model->getAddressTableModel()->labelForAddress(address);
if (!associatedLabel.isEmpty()) {
ui->addAsLabel->setText(associatedLabel);
return true;
}
return false;
}
diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h
index f7a2cf602..d9c8017fc 100644
--- a/src/qt/sendcoinsentry.h
+++ b/src/qt/sendcoinsentry.h
@@ -1,74 +1,78 @@
// Copyright (c) 2011-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_QT_SENDCOINSENTRY_H
#define BITCOIN_QT_SENDCOINSENTRY_H
#include "walletmodel.h"
#include
class WalletModel;
class PlatformStyle;
namespace Ui {
class SendCoinsEntry;
}
/**
* A single entry in the dialog for sending bitcoins.
* Stacked widget, with different UIs for payment requests
* with a strong payee identity.
*/
class SendCoinsEntry : public QStackedWidget {
Q_OBJECT
public:
explicit SendCoinsEntry(const PlatformStyle *platformStyle,
QWidget *parent = 0);
~SendCoinsEntry();
void setModel(WalletModel *model);
bool validate();
SendCoinsRecipient getValue();
/** Return whether the entry is still empty and unedited */
bool isClear();
void setValue(const SendCoinsRecipient &value);
void setAddress(const QString &address);
+ void setAmount(const Amount amount);
/** Set up the tab chain manually, as Qt messes up the tab chain by default
* in some cases
* (issue https://bugreports.qt-project.org/browse/QTBUG-10907).
*/
QWidget *setupTabChain(QWidget *prev);
void setFocus();
public Q_SLOTS:
void clear();
+ void checkSubtractFeeFromAmount();
Q_SIGNALS:
void removeEntry(SendCoinsEntry *entry);
+ void useAvailableBalance(SendCoinsEntry *entry);
void payAmountChanged();
void subtractFeeFromAmountChanged();
private Q_SLOTS:
void deleteClicked();
+ void useAvailableBalanceClicked();
void on_payTo_textChanged(const QString &address);
void on_addressBookButton_clicked();
void on_pasteButton_clicked();
void updateDisplayUnit();
private:
SendCoinsRecipient recipient;
Ui::SendCoinsEntry *ui;
WalletModel *model;
const PlatformStyle *platformStyle;
bool updateLabel(const QString &address);
};
#endif // BITCOIN_QT_SENDCOINSENTRY_H