mirror of
https://github.com/dashpay/dash.git
synced 2024-12-28 21:42:47 +01:00
8a1ec935a0
* scripted-diff: Replace #include "" with #include <> (ryanofsky) -BEGIN VERIFY SCRIPT- for f in \ src/*.cpp \ src/*.h \ src/bench/*.cpp \ src/bench/*.h \ src/compat/*.cpp \ src/compat/*.h \ src/consensus/*.cpp \ src/consensus/*.h \ src/crypto/*.cpp \ src/crypto/*.h \ src/crypto/ctaes/*.h \ src/policy/*.cpp \ src/policy/*.h \ src/primitives/*.cpp \ src/primitives/*.h \ src/qt/*.cpp \ src/qt/*.h \ src/qt/test/*.cpp \ src/qt/test/*.h \ src/rpc/*.cpp \ src/rpc/*.h \ src/script/*.cpp \ src/script/*.h \ src/support/*.cpp \ src/support/*.h \ src/support/allocators/*.h \ src/test/*.cpp \ src/test/*.h \ src/wallet/*.cpp \ src/wallet/*.h \ src/wallet/test/*.cpp \ src/wallet/test/*.h \ src/zmq/*.cpp \ src/zmq/*.h do base=${f%/*}/ relbase=${base#src/} sed -i "s:#include \"\(.*\)\"\(.*\):if test -e \$base'\\1'; then echo \"#include <\"\$relbase\"\\1>\\2\"; else echo \"#include <\\1>\\2\"; fi:e" $f done -END VERIFY SCRIPT- Signed-off-by: Pasta <pasta@dashboost.org> * scripted-diff: Replace #include "" with #include <> (Dash Specific) -BEGIN VERIFY SCRIPT- for f in \ src/bls/*.cpp \ src/bls/*.h \ src/evo/*.cpp \ src/evo/*.h \ src/governance/*.cpp \ src/governance/*.h \ src/llmq/*.cpp \ src/llmq/*.h \ src/masternode/*.cpp \ src/masternode/*.h \ src/privatesend/*.cpp \ src/privatesend/*.h do base=${f%/*}/ relbase=${base#src/} sed -i "s:#include \"\(.*\)\"\(.*\):if test -e \$base'\\1'; then echo \"#include <\"\$relbase\"\\1>\\2\"; else echo \"#include <\\1>\\2\"; fi:e" $f done -END VERIFY SCRIPT- Signed-off-by: Pasta <pasta@dashboost.org> * build: Remove -I for everything but project root Remove -I from build system for everything but the project root, and built-in dependencies. Signed-off-by: Pasta <pasta@dashboost.org> # Conflicts: # src/Makefile.test.include * qt: refactor: Use absolute include paths in .ui files * qt: refactor: Changes to make include paths absolute This makes all include paths in the GUI absolute. Many changes are involved as every single source file in src/qt/ assumes to be able to use relative includes. Signed-off-by: Pasta <pasta@dashboost.org> # Conflicts: # src/qt/dash.cpp # src/qt/optionsmodel.cpp # src/qt/test/rpcnestedtests.cpp * test: refactor: Use absolute include paths for test data files * Recommend #include<> syntax in developer notes * refactor: Include obj/build.h instead of build.h * END BACKPORT #11651 Remove trailing whitespace causing travis failure * fix backport 11651 Signed-off-by: Pasta <pasta@dashboost.org> * More of 11651 * fix blockchain.cpp Signed-off-by: pasta <pasta@dashboost.org> * Add missing "qt/" in includes * Add missing "test/" in includes * Fix trailing whitespaces Co-authored-by: Wladimir J. van der Laan <laanwj@gmail.com> Co-authored-by: Russell Yanofsky <russ@yanofsky.org> Co-authored-by: MeshCollider <dobsonsa68@gmail.com> Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
1029 lines
38 KiB
C++
1029 lines
38 KiB
C++
// Copyright (c) 2011-2015 The Bitcoin Core developers
|
|
// Copyright (c) 2014-2020 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 <qt/sendcoinsdialog.h>
|
|
#include <qt/forms/ui_sendcoinsdialog.h>
|
|
|
|
#include <qt/addresstablemodel.h>
|
|
#include <qt/bitcoinunits.h>
|
|
#include <qt/clientmodel.h>
|
|
#include <qt/coincontroldialog.h>
|
|
#include <qt/guiutil.h>
|
|
#include <qt/optionsmodel.h>
|
|
#include <qt/platformstyle.h>
|
|
#include <qt/sendcoinsentry.h>
|
|
#include <qt/walletmodel.h>
|
|
|
|
#include <base58.h>
|
|
#include <wallet/coincontrol.h>
|
|
#include <validation.h> // mempool and minRelayTxFee
|
|
#include <ui_interface.h>
|
|
#include <txmempool.h>
|
|
#include <policy/fees.h>
|
|
#include <wallet/fees.h>
|
|
|
|
#include <privatesend/privatesend.h>
|
|
#include <privatesend/privatesend-client.h>
|
|
|
|
#include <QFontMetrics>
|
|
#include <QMessageBox>
|
|
#include <QScrollBar>
|
|
#include <QSettings>
|
|
#include <QTextDocument>
|
|
#include <QTimer>
|
|
|
|
#define SEND_CONFIRM_DELAY 3
|
|
|
|
static const std::array<int, 9> confTargets = { {2, 4, 6, 12, 24, 48, 144, 504, 1008} };
|
|
int getConfTargetForIndex(int index) {
|
|
if (index+1 > static_cast<int>(confTargets.size())) {
|
|
return confTargets.back();
|
|
}
|
|
if (index < 0) {
|
|
return confTargets[0];
|
|
}
|
|
return confTargets[index];
|
|
}
|
|
int getIndexForConfTarget(int target) {
|
|
for (unsigned int i = 0; i < confTargets.size(); i++) {
|
|
if (confTargets[i] >= target) {
|
|
return i;
|
|
}
|
|
}
|
|
return confTargets.size() - 1;
|
|
}
|
|
|
|
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(QIcon(":/icons/add"));
|
|
ui->clearButton->setIcon(QIcon(":/icons/remove"));
|
|
ui->sendButton->setIcon(QIcon(":/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 &)));
|
|
|
|
// Dash specific
|
|
QSettings settings;
|
|
//TODO remove Darksend sometime after 0.14.1
|
|
if (settings.contains("bUseDarkSend")) {
|
|
settings.setValue("bUsePrivateSend", settings.value("bUseDarkSend").toBool());
|
|
settings.remove("bUseDarkSend");
|
|
}
|
|
if (!settings.contains("bUsePrivateSend"))
|
|
settings.setValue("bUsePrivateSend", false);
|
|
|
|
//TODO remove InstantX sometime after 0.14.1
|
|
if (settings.contains("bUseInstantX")) {
|
|
settings.remove("bUseInstantX");
|
|
}
|
|
if (settings.contains("bUseInstantSend")) {
|
|
settings.remove("bUseInstantSend");
|
|
}
|
|
|
|
if (!privateSendClient.fEnablePrivateSend) {
|
|
ui->checkUsePrivateSend->setChecked(false);
|
|
ui->checkUsePrivateSend->setVisible(false);
|
|
CoinControlDialog::coinControl->UsePrivateSend(false);
|
|
} else {
|
|
bool fUsePrivateSend = settings.value("bUsePrivateSend").toBool();
|
|
ui->checkUsePrivateSend->setChecked(fUsePrivateSend);
|
|
CoinControlDialog::coinControl->UsePrivateSend(fUsePrivateSend);
|
|
connect(ui->checkUsePrivateSend, SIGNAL(stateChanged ( int )), this, SLOT(updateDisplayUnit()));
|
|
}
|
|
|
|
// 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("nSmartFeeSliderPosition"))
|
|
settings.setValue("nSmartFeeSliderPosition", 0);
|
|
if (!settings.contains("nTransactionFee"))
|
|
settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
|
|
if (!settings.contains("fPayOnlyMinFee"))
|
|
settings.setValue("fPayOnlyMinFee", 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->customFee->setValue(settings.value("nTransactionFee").toLongLong());
|
|
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<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
|
|
for (const int n : confTargets) {
|
|
ui->confTargetSelector->addItem(tr("%1 (%2 blocks)").arg(GUIUtil::formatNiceTimeOffset(n*Params().GetConsensus().nPowTargetSpacing)).arg(n));
|
|
}
|
|
connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSmartFeeLabel()));
|
|
connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(coinControlUpdateLabels()));
|
|
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
|
|
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
|
|
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(coinControlUpdateLabels()));
|
|
ui->customFee->setSingleStep(GetRequiredFee(1000));
|
|
updateFeeSectionControls();
|
|
updateMinFeeLabel();
|
|
updateSmartFeeLabel();
|
|
|
|
// set the smartfee-sliders default value (wallets default conf.target or last stored value)
|
|
QSettings settings;
|
|
if (settings.value("nSmartFeeSliderPosition").toInt() != 0) {
|
|
// migrate nSmartFeeSliderPosition to nConfTarget
|
|
// nConfTarget is available since 0.15 (replaced nSmartFeeSliderPosition)
|
|
int nConfirmTarget = 25 - settings.value("nSmartFeeSliderPosition").toInt(); // 25 == old slider range
|
|
settings.setValue("nConfTarget", nConfirmTarget);
|
|
settings.remove("nSmartFeeSliderPosition");
|
|
}
|
|
if (settings.value("nConfTarget").toInt() == 0)
|
|
ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->getDefaultConfirmTarget()));
|
|
else
|
|
ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt()));
|
|
}
|
|
}
|
|
|
|
SendCoinsDialog::~SendCoinsDialog()
|
|
{
|
|
QSettings settings;
|
|
settings.setValue("fFeeSectionMinimized", fFeeMinimized);
|
|
settings.setValue("nFeeRadio", ui->groupFee->checkedId());
|
|
settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex()));
|
|
settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
|
|
settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->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;
|
|
}
|
|
|
|
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);
|
|
return;
|
|
}
|
|
// already unlocked or not encrypted at all
|
|
send(recipients);
|
|
}
|
|
|
|
void SendCoinsDialog::send(QList<SendCoinsRecipient> recipients)
|
|
{
|
|
// 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;
|
|
|
|
updateCoinControlState(ctrl);
|
|
|
|
ctrl.UsePrivateSend(ui->checkUsePrivateSend->isChecked());
|
|
|
|
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;
|
|
}
|
|
|
|
// Format confirmation message
|
|
QStringList formatted;
|
|
for (const SendCoinsRecipient &rcp : currentTransaction.getRecipients())
|
|
{
|
|
// generate bold amount string
|
|
QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
|
|
amount.append("</b> ");
|
|
|
|
// 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);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
QString questionString = tr("Are you sure you want to send?");
|
|
questionString.append("<br /><br />");
|
|
questionString.append(formatted.join("<br />"));
|
|
questionString.append("<br />");
|
|
|
|
|
|
if(ctrl.IsUsingPrivateSend()) {
|
|
questionString.append(tr("using") + " <b>" + tr("PrivateSend funds only") + "</b>");
|
|
} else {
|
|
questionString.append(tr("using") + " <b>" + tr("any available funds") + "</b>");
|
|
}
|
|
|
|
if (displayedEntries < messageEntries) {
|
|
questionString.append("<br />");
|
|
questionString.append("<span style='" + GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR) + "'>");
|
|
questionString.append(tr("<b>(%1 of %2 entries displayed)</b>").arg(displayedEntries).arg(messageEntries));
|
|
questionString.append("</span>");
|
|
}
|
|
|
|
CAmount txFee = currentTransaction.getTransactionFee();
|
|
|
|
if(txFee > 0)
|
|
{
|
|
// append fee string if a fee is required
|
|
questionString.append("<hr /><span style='" + GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR) + "'>");
|
|
questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
|
|
questionString.append("</span> ");
|
|
questionString.append(tr("are added as transaction fee"));
|
|
|
|
if (ctrl.IsUsingPrivateSend()) {
|
|
questionString.append(" " + tr("(PrivateSend transactions have higher fees usually due to no change output being allowed)"));
|
|
}
|
|
}
|
|
|
|
// Show some additioinal information
|
|
questionString.append("<hr />");
|
|
// append transaction size
|
|
questionString.append(tr("Transaction size: %1").arg(QString::number((double)currentTransaction.getTransactionSize() / 1000)) + " kB");
|
|
questionString.append("<br />");
|
|
CFeeRate feeRate(txFee, currentTransaction.getTransactionSize());
|
|
questionString.append(tr("Fee rate: %1").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK())) + "/kB");
|
|
|
|
if (ctrl.IsUsingPrivateSend()) {
|
|
// append number of inputs
|
|
questionString.append("<hr />");
|
|
int nInputs = currentTransaction.getTransaction()->tx->vin.size();
|
|
questionString.append(tr("This transaction will consume %n input(s)", "", nInputs));
|
|
|
|
// warn about potential privacy issues when spending too many inputs at once
|
|
if (nInputs >= 10 && ctrl.IsUsingPrivateSend()) {
|
|
questionString.append("<br />");
|
|
questionString.append("<span style='" + GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR) + "'>");
|
|
questionString.append(tr("Warning: Using PrivateSend with %1 or more inputs can harm your privacy and is not recommended").arg(10));
|
|
questionString.append("</span> ");
|
|
}
|
|
}
|
|
|
|
// add total amount in all subdivision units
|
|
questionString.append("<hr />");
|
|
CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
|
|
QStringList alternativeUnits;
|
|
for (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 />= ")));
|
|
|
|
// Display message box
|
|
SendConfirmationDialog confirmationDialog(tr("Confirm send coins"),
|
|
questionString, 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<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("bUsePrivateSend", 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->UsePrivateSend(ui->checkUsePrivateSend->isChecked());
|
|
coinControlUpdateLabels();
|
|
ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
|
|
updateMinFeeLabel();
|
|
updateSmartFeeLabel();
|
|
}
|
|
|
|
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 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.
|
|
CAmount amount = model->getBalance(&coin_control);
|
|
for (int i = 0; i < ui->entries->count(); ++i) {
|
|
SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
|
|
if (e && !e->isHidden() && e != entry) {
|
|
amount -= e->getValue().amount;
|
|
}
|
|
}
|
|
|
|
if (amount > 0) {
|
|
entry->checkSubtractFeeFromAmount();
|
|
entry->setAmount(amount);
|
|
} else {
|
|
entry->setAmount(0);
|
|
}
|
|
}
|
|
|
|
void SendCoinsDialog::setMinimumFee()
|
|
{
|
|
ui->customFee->setValue(GetRequiredFee(1000));
|
|
}
|
|
|
|
void SendCoinsDialog::updateFeeSectionControls()
|
|
{
|
|
ui->confTargetSelector ->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->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
|
|
ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
|
|
ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
|
|
ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->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()) + "/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(), GetRequiredFee(1000)) + "/kB")
|
|
);
|
|
}
|
|
|
|
void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
|
|
{
|
|
if (ui->radioCustomFee->isChecked()) {
|
|
ctrl.m_feerate = CFeeRate(ui->customFee->value());
|
|
} else {
|
|
ctrl.m_feerate.reset();
|
|
}
|
|
// Avoid using global defaults when sending money from the GUI
|
|
// Either custom fee will be used or if not selected, the confirmation target from dropdown box
|
|
ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
|
|
}
|
|
|
|
void SendCoinsDialog::updateSmartFeeLabel()
|
|
{
|
|
if(!model || !model->getOptionsModel())
|
|
return;
|
|
CCoinControl coin_control;
|
|
updateCoinControlState(coin_control);
|
|
coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
|
|
FeeCalculation feeCalc;
|
|
CFeeRate feeRate = CFeeRate(GetMinimumFee(1000, coin_control, ::mempool, ::feeEstimator, &feeCalc));
|
|
|
|
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
|
|
|
|
if (feeCalc.reason == FeeReason::FALLBACK) {
|
|
ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
|
|
ui->labelFeeEstimation->setText("");
|
|
ui->fallbackFeeWarningLabel->setVisible(true);
|
|
}
|
|
else
|
|
{
|
|
ui->labelSmartFee2->hide();
|
|
ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", feeCalc.returnedTarget));
|
|
ui->fallbackFeeWarningLabel->setVisible(false);
|
|
}
|
|
|
|
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(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR));
|
|
|
|
const CTxDestination dest = DecodeDestination(text.toStdString());
|
|
|
|
if (text.isEmpty()) // Nothing entered
|
|
{
|
|
ui->labelCoinControlChangeLabel->setText("");
|
|
}
|
|
else if (!IsValidDestination(dest)) // Invalid address
|
|
{
|
|
ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Dash 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(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_PRIMARY));
|
|
ui->labelCoinControlChangeLabel->setText("");
|
|
}
|
|
}
|
|
else // Known change address
|
|
{
|
|
ui->labelCoinControlChangeLabel->setStyleSheet(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_PRIMARY));
|
|
|
|
// 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;
|
|
|
|
updateCoinControlState(*CoinControlDialog::coinControl);
|
|
|
|
// 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->IsUsingPrivateSend());
|
|
|
|
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"));
|
|
}
|
|
}
|