mirror of
https://github.com/dashpay/dash.git
synced 2024-12-29 13:59:06 +01:00
9707ca5cea
* Merge #7506: Use CCoinControl selection in CWallet::FundTransactiond6cc6a1
Use CCoinControl selection in CWallet::FundTransaction (João Barbosa) * Merge #7732: [Qt] Debug window: replace "Build date" with "Datadir"fc737d1
[Qt] remove unused formatBuildDate method (Jonas Schnelli)4856f1d
[Qt] Debug window: replace "Build date" with "Datadir" (Jonas Schnelli) * Merge #7707: [RPC][QT] UI support for abandoned transactions8efed3b
[Qt] Support for abandoned/abandoning transactions (Jonas Schnelli) * Merge #7688: List solvability in listunspent output and improve helpc3932b3
List solvability in listunspent output and improve help (Pieter Wuille) * Merge #8006: Qt: Add option to disable the system tray icon8b0e497
Qt: Add option to hide the system tray icon (Tyler Hardin) * Merge #8073: qt: askpassphrasedialog: Clear pass fields on accept02ce2a3
qt: askpassphrasedialog: Clear pass fields on accept (Pavel Vasin) * Merge #8231: [Qt] fix a bug where the SplashScreen will not be hidden during startupb3e1348
[Qt] fix a bug where the SplashScreen will not be hidden during startup (Jonas Schnelli) * Merge #8257: Do not ask a UI question from bitcoind1acf1db
Do not ask a UI question from bitcoind (Pieter Wuille) * Merge #8463: [qt] Remove Priority from coincontrol dialogfa8dd78
[qt] Remove Priority from coincontrol dialog (MarcoFalke) * Merge #8678: [Qt][CoinControl] fix UI bug that could result in paying unexpected fee0480293
[Qt][CoinControl] fix UI bug that could result in paying unexpected fee (Jonas Schnelli) * Merge #8672: Qt: Show transaction size in transaction details windowc015634
qt: Adding transaction size to transaction details window (Hampus Sjöberg) \-- merge fix for s/size/total size/fdf82fb
Adding method GetTotalSize() to CTransaction (Hampus Sjöberg) * Merge #8371: [Qt] Add out-of-sync modal info layer08827df
[Qt] modalinfolayer: removed unused comments, renamed signal, code style overhaul (Jonas Schnelli)d8b062e
[Qt] only update "amount of blocks left" when the header chain is in-sync (Jonas Schnelli)e3245b4
[Qt] add out-of-sync modal info layer (Jonas Schnelli)e47052f
[Qt] ClientModel add method to get the height of the header chain (Jonas Schnelli)a001f18
[Qt] Always pass the numBlocksChanged signal for headers tip changed (Jonas Schnelli)bd44a04
[Qt] make Out-Of-Sync warning icon clickable (Jonas Schnelli)0904c3c
[Refactor] refactor function that forms human readable text out of a timeoffset (Jonas Schnelli) * Merge #8805: Trivial: Grammar and capitalizationc9ce17b
Trivial: Grammar and capitalization (Derek Miller) * Merge #8885: gui: fix ban from qt consolecb78c60
gui: fix ban from qt console (Cory Fields) * Merge #8821: [qt] sync-overlay: Don't block during reindexfa85e86
[qt] sync-overlay: Don't show estimated number of headers left (MarcoFalke)faa4de2
[qt] sync-overlay: Don't block during reindex (MarcoFalke) * Support themes for new transaction_abandoned icon * Fix constructor call to COutput * Merge #7842: RPC: do not print minping time in getpeerinfo when no ping received yet62a6486
RPC: do not print ping info in getpeerinfo when no ping received yet, fix help (Pavel Janík) * Merge #8918: Qt: Add "Copy URI" to payment request context menu21f5a63
Qt: Add "Copy URI" to payment request context menu (Luke Dashjr) * Merge #8925: qt: Display minimum ping in debug window.1724a40
Display minimum ping in debug window. (R E Broadley) * Merge #8972: [Qt] make warnings label selectable (jonasschnelli)ef0c9ee
[Qt] make warnings label selectable (Jonas Schnelli) * Make background of warning icon transparent in modaloverlay * Merge #9088: Reduce ambiguity of warning message77cbbd9
Make warning message about wallet balance possibly being incorrect less ambiguous. (R E Broadley) * Replace Bitcoin with Dash in modal overlay * Remove clicked signals from labelWalletStatus and labelTransactionsStatus As both are really just labels, clicking on those is not possible. This is different in Bitcoin, where these labels are actually buttons. * Pull out modaloverlay show/hide into it's own if/else block and switch to time based check Also don't use masternodeSync.IsBlockchainSynced() for now as it won't report the blockchain being synced before the first block (or other MN data?) arrives. This would otherwise give the impression that sync is being stuck.
948 lines
36 KiB
C++
948 lines
36 KiB
C++
// Copyright (c) 2011-2015 The Bitcoin Core developers
|
|
// Copyright (c) 2014-2017 The Dash 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 "base58.h"
|
|
#include "coincontrol.h"
|
|
#include "validation.h" // mempool and minRelayTxFee
|
|
#include "ui_interface.h"
|
|
#include "txmempool.h"
|
|
#include "wallet/wallet.h"
|
|
|
|
#include "privatesend.h"
|
|
|
|
#include <QMessageBox>
|
|
#include <QScrollBar>
|
|
#include <QSettings>
|
|
#include <QTextDocument>
|
|
|
|
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);
|
|
QString theme = GUIUtil::getThemeName();
|
|
|
|
if (!platformStyle->getImagesOnButtons()) {
|
|
ui->addButton->setIcon(QIcon());
|
|
ui->clearButton->setIcon(QIcon());
|
|
ui->sendButton->setIcon(QIcon());
|
|
} else {
|
|
ui->addButton->setIcon(QIcon(":/icons/" + theme + "/add"));
|
|
ui->clearButton->setIcon(QIcon(":/icons/" + theme + "/remove"));
|
|
ui->sendButton->setIcon(QIcon(":/icons/" + theme + "/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 &)));
|
|
|
|
// Dash specific
|
|
QSettings settings;
|
|
if (!settings.contains("bUseDarkSend"))
|
|
settings.setValue("bUseDarkSend", false);
|
|
if (!settings.contains("bUseInstantX"))
|
|
settings.setValue("bUseInstantX", false);
|
|
|
|
bool fUsePrivateSend = settings.value("bUseDarkSend").toBool();
|
|
bool fUseInstantSend = settings.value("bUseInstantX").toBool();
|
|
if(fLiteMode) {
|
|
ui->checkUsePrivateSend->setChecked(false);
|
|
ui->checkUsePrivateSend->setVisible(false);
|
|
ui->checkUseInstantSend->setVisible(false);
|
|
CoinControlDialog::coinControl->fUsePrivateSend = false;
|
|
CoinControlDialog::coinControl->fUseInstantSend = false;
|
|
}
|
|
else{
|
|
ui->checkUsePrivateSend->setChecked(fUsePrivateSend);
|
|
ui->checkUseInstantSend->setChecked(fUseInstantSend);
|
|
CoinControlDialog::coinControl->fUsePrivateSend = fUsePrivateSend;
|
|
CoinControlDialog::coinControl->fUseInstantSend = fUseInstantSend;
|
|
}
|
|
|
|
connect(ui->checkUsePrivateSend, SIGNAL(stateChanged ( int )), this, SLOT(updateDisplayUnit()));
|
|
connect(ui->checkUseInstantSend, SIGNAL(stateChanged ( int )), this, SLOT(updateInstantSend()));
|
|
|
|
// 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
|
|
if (!settings.contains("fFeeSectionMinimized"))
|
|
settings.setValue("fFeeSectionMinimized", true);
|
|
if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
|
|
settings.setValue("nFeeRadio", 1); // custom
|
|
if (!settings.contains("nFeeRadio"))
|
|
settings.setValue("nFeeRadio", 0); // recommended
|
|
if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
|
|
settings.setValue("nCustomFeeRadio", 1); // total at least
|
|
if (!settings.contains("nCustomFeeRadio"))
|
|
settings.setValue("nCustomFeeRadio", 0); // per kilobyte
|
|
if (!settings.contains("nSmartFeeSliderPosition"))
|
|
settings.setValue("nSmartFeeSliderPosition", 0);
|
|
if (!settings.contains("nTransactionFee"))
|
|
settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
|
|
if (!settings.contains("fPayOnlyMinFee"))
|
|
settings.setValue("fPayOnlyMinFee", false);
|
|
if (!settings.contains("fSendFreeTransactions"))
|
|
settings.setValue("fSendFreeTransactions", false);
|
|
|
|
ui->groupFee->setId(ui->radioSmartFee, 0);
|
|
ui->groupFee->setId(ui->radioCustomFee, 1);
|
|
ui->groupFee->button((int)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((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
|
|
ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
|
|
ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
|
|
ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
|
|
ui->checkBoxFreeTx->setChecked(settings.value("fSendFreeTransactions").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<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
|
|
if(entry)
|
|
{
|
|
entry->setModel(model);
|
|
}
|
|
}
|
|
|
|
setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), model->getAnonymizedBalance(),
|
|
model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance());
|
|
connect(model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)));
|
|
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()));
|
|
connect(ui->checkBoxFreeTx, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
|
|
connect(ui->checkBoxFreeTx, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
|
|
ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000));
|
|
updateFeeSectionControls();
|
|
updateMinFeeLabel();
|
|
updateSmartFeeLabel();
|
|
updateGlobalFeeVariables();
|
|
}
|
|
}
|
|
|
|
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());
|
|
settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
|
|
settings.setValue("fSendFreeTransactions", ui->checkBoxFreeTx->isChecked());
|
|
|
|
delete ui;
|
|
}
|
|
|
|
void SendCoinsDialog::on_sendButton_clicked()
|
|
{
|
|
if(!model || !model->getOptionsModel())
|
|
return;
|
|
|
|
QList<SendCoinsRecipient> recipients;
|
|
bool valid = true;
|
|
|
|
for(int i = 0; i < ui->entries->count(); ++i)
|
|
{
|
|
SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
|
|
if(entry)
|
|
{
|
|
if(entry->validate())
|
|
{
|
|
recipients.append(entry->getValue());
|
|
}
|
|
else
|
|
{
|
|
valid = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!valid || recipients.isEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
QString strFunds = tr("using") + " <b>" + tr("anonymous funds") + "</b>";
|
|
QString strFee = "";
|
|
recipients[0].inputType = ONLY_DENOMINATED;
|
|
|
|
if(ui->checkUsePrivateSend->isChecked()) {
|
|
recipients[0].inputType = ONLY_DENOMINATED;
|
|
strFunds = tr("using") + " <b>" + tr("anonymous funds") + "</b>";
|
|
QString strNearestAmount(
|
|
BitcoinUnits::formatWithUnit(
|
|
model->getOptionsModel()->getDisplayUnit(), CPrivateSend::GetSmallestDenomination()));
|
|
strFee = QString(tr(
|
|
"(privatesend requires this amount to be rounded up to the nearest %1)."
|
|
).arg(strNearestAmount));
|
|
} else {
|
|
recipients[0].inputType = ALL_COINS;
|
|
strFunds = tr("using") + " <b>" + tr("any available funds (not anonymous)") + "</b>";
|
|
}
|
|
|
|
if(ui->checkUseInstantSend->isChecked()) {
|
|
recipients[0].fUseInstantSend = true;
|
|
strFunds += " ";
|
|
strFunds += tr("and InstantSend");
|
|
} else {
|
|
recipients[0].fUseInstantSend = false;
|
|
}
|
|
|
|
|
|
fNewRecipientAllowed = false;
|
|
// request unlock only if was locked or unlocked for mixing:
|
|
// this way we let users unlock by walletpassphrase or by menu
|
|
// and make many transactions while unlocking through this dialog
|
|
// will call relock
|
|
WalletModel::EncryptionStatus encStatus = model->getEncryptionStatus();
|
|
if(encStatus == model->Locked || encStatus == model->UnlockedForMixingOnly)
|
|
{
|
|
WalletModel::UnlockContext ctx(model->requestUnlock());
|
|
if(!ctx.isValid())
|
|
{
|
|
// Unlock wallet was cancelled
|
|
fNewRecipientAllowed = true;
|
|
return;
|
|
}
|
|
send(recipients, strFee, strFunds);
|
|
return;
|
|
}
|
|
// already unlocked or not encrypted at all
|
|
send(recipients, strFee, strFunds);
|
|
}
|
|
|
|
void SendCoinsDialog::send(QList<SendCoinsRecipient> recipients, QString strFee, QString strFunds)
|
|
{
|
|
// prepare transaction for getting txFee earlier
|
|
WalletModelTransaction currentTransaction(recipients);
|
|
WalletModel::SendCoinsReturn prepareStatus;
|
|
if (model->getOptionsModel()->getCoinControlFeatures()) // coin control enabled
|
|
prepareStatus = model->prepareTransaction(currentTransaction, CoinControlDialog::coinControl);
|
|
else
|
|
prepareStatus = model->prepareTransaction(currentTransaction);
|
|
|
|
// 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;
|
|
}
|
|
|
|
CAmount txFee = currentTransaction.getTransactionFee();
|
|
|
|
// Format confirmation message
|
|
QStringList formatted;
|
|
Q_FOREACH(const SendCoinsRecipient &rcp, currentTransaction.getRecipients())
|
|
{
|
|
// generate bold amount string
|
|
QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
|
|
amount.append("</b> ").append(strFunds);
|
|
|
|
// generate monospace address string
|
|
QString address = "<span style='font-family: monospace;'>" + rcp.address;
|
|
address.append("</span>");
|
|
|
|
QString recipientElement;
|
|
|
|
if (!rcp.paymentRequest.IsInitialized()) // normal payment
|
|
{
|
|
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("<br /><br />%1");
|
|
|
|
if(txFee > 0)
|
|
{
|
|
// append fee string if a fee is required
|
|
questionString.append("<hr /><span style='color:#aa0000;'>");
|
|
questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
|
|
questionString.append("</span> ");
|
|
questionString.append(tr("are added as transaction fee"));
|
|
questionString.append(" ");
|
|
questionString.append(strFee);
|
|
|
|
// append transaction size
|
|
questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)");
|
|
}
|
|
|
|
// add total amount in all subdivision units
|
|
questionString.append("<hr />");
|
|
CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
|
|
QStringList alternativeUnits;
|
|
Q_FOREACH(BitcoinUnits::Unit u, BitcoinUnits::availableUnits())
|
|
{
|
|
if(u != model->getOptionsModel()->getDisplayUnit())
|
|
alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
|
|
}
|
|
|
|
// Show total amount + all alternative units
|
|
questionString.append(tr("Total Amount = <b>%1</b><br />= %2")
|
|
.arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount))
|
|
.arg(alternativeUnits.join("<br />= ")));
|
|
|
|
// Limit number of displayed entries
|
|
int messageEntries = formatted.size();
|
|
int displayedEntries = 0;
|
|
for(int i = 0; i < formatted.size(); i++){
|
|
if(i >= MAX_SEND_POPUP_ENTRIES){
|
|
formatted.removeLast();
|
|
i--;
|
|
}
|
|
else{
|
|
displayedEntries = i+1;
|
|
}
|
|
}
|
|
questionString.append("<hr />");
|
|
questionString.append(tr("<b>(%1 of %2 entries displayed)</b>").arg(displayedEntries).arg(messageEntries));
|
|
|
|
// Display message box
|
|
QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
|
|
questionString.arg(formatted.join("<br />")),
|
|
QMessageBox::Yes | QMessageBox::Cancel,
|
|
QMessageBox::Cancel);
|
|
|
|
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(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<SendCoinsEntry*>(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<SendCoinsEntry*>(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<SendCoinsEntry*>(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 CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& anonymizedBalance,
|
|
const CAmount& watchBalance, const CAmount& watchUnconfirmedBalance, const CAmount& watchImmatureBalance)
|
|
{
|
|
Q_UNUSED(unconfirmedBalance);
|
|
Q_UNUSED(immatureBalance);
|
|
Q_UNUSED(anonymizedBalance);
|
|
Q_UNUSED(watchBalance);
|
|
Q_UNUSED(watchUnconfirmedBalance);
|
|
Q_UNUSED(watchImmatureBalance);
|
|
|
|
if(model && model->getOptionsModel())
|
|
{
|
|
uint64_t bal = 0;
|
|
QSettings settings;
|
|
settings.setValue("bUseDarkSend", ui->checkUsePrivateSend->isChecked());
|
|
if(ui->checkUsePrivateSend->isChecked()) {
|
|
bal = anonymizedBalance;
|
|
} else {
|
|
bal = balance;
|
|
}
|
|
|
|
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), bal));
|
|
}
|
|
}
|
|
|
|
void SendCoinsDialog::updateDisplayUnit()
|
|
{
|
|
setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), model->getAnonymizedBalance(),
|
|
model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance());
|
|
CoinControlDialog::coinControl->fUsePrivateSend = ui->checkUsePrivateSend->isChecked();
|
|
coinControlUpdateLabels();
|
|
ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
|
|
updateMinFeeLabel();
|
|
updateSmartFeeLabel();
|
|
}
|
|
|
|
void SendCoinsDialog::updateInstantSend()
|
|
{
|
|
QSettings settings;
|
|
settings.setValue("bUseInstantX", ui->checkUseInstantSend->isChecked());
|
|
CoinControlDialog::coinControl->fUseInstantSend = ui->checkUseInstantSend->isChecked();
|
|
coinControlUpdateLabels();
|
|
}
|
|
|
|
void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
|
|
{
|
|
QPair<QString, CClientUIInterface::MessageBoxFlags> 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! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
|
|
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::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->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())
|
|
{
|
|
nTxConfirmTarget = defaultConfirmTarget - ui->sliderSmartFee->value();
|
|
payTxFee = CFeeRate(0);
|
|
|
|
// set nMinimumTotalFee to 0 to not accidentally pay a custom fee
|
|
CoinControlDialog::coinControl->nMinimumTotalFee = 0;
|
|
}
|
|
else
|
|
{
|
|
nTxConfirmTarget = defaultConfirmTarget;
|
|
payTxFee = CFeeRate(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() : 0;
|
|
}
|
|
|
|
fSendFreeTransactions = ui->checkBoxFreeTx->isChecked();
|
|
}
|
|
|
|
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 = defaultConfirmTarget - ui->sliderSmartFee->value();
|
|
int estimateFoundAtBlocks = nBlocksToConfirm;
|
|
CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
|
|
if (feeRate <= CFeeRate(0)) // not enough data => minfee
|
|
{
|
|
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
|
|
std::max(CWallet::fallbackFee.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
|
|
ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
|
|
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);
|
|
|
|
if (!checked && model) // coin control features disabled
|
|
CoinControlDialog::coinControl->SetNull();
|
|
|
|
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;}");
|
|
|
|
CBitcoinAddress addr = CBitcoinAddress(text.toStdString());
|
|
|
|
if (text.isEmpty()) // Nothing entered
|
|
{
|
|
ui->labelCoinControlChangeLabel->setText("");
|
|
}
|
|
else if (!addr.IsValid()) // Invalid address
|
|
{
|
|
ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Dash address"));
|
|
}
|
|
else // Valid address
|
|
{
|
|
CKeyID keyid;
|
|
addr.GetKeyID(keyid);
|
|
if (!model->havePrivKey(keyid)) // Unknown change address
|
|
{
|
|
ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
|
|
}
|
|
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 = addr.Get();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
|
|
if(entry && !entry->isHidden())
|
|
{
|
|
SendCoinsRecipient rcp = entry->getValue();
|
|
CoinControlDialog::payAmounts.append(rcp.amount);
|
|
if (rcp.fSubtractFeeFromAmount)
|
|
CoinControlDialog::fSubtractFeeFromAmount = true;
|
|
}
|
|
}
|
|
|
|
ui->checkUsePrivateSend->setChecked(CoinControlDialog::coinControl->fUsePrivateSend);
|
|
|
|
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();
|
|
}
|
|
}
|