mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-01-11 15:48:05 +00:00
eec7757445
Introduce a PlatformStyle to handle platform-specific customization of the UI. This replaces 'scicon', as well as #ifdefs to determine whether to place icons on buttons. The selected PlatformStyle defaults to the platform that the application was compiled on, but can be overridden from the command line with `-uiplatform=<x>`. Also fixes the warning from #6328.
819 lines
31 KiB
C++
819 lines
31 KiB
C++
// Copyright (c) 2011-2014 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 "base58.h"
|
|
#include "coincontrol.h"
|
|
#include "main.h" // mempool and minRelayTxFee
|
|
#include "ui_interface.h"
|
|
#include "txmempool.h"
|
|
#include "wallet/wallet.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);
|
|
|
|
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 *clipboardPriorityAction = new QAction(tr("Copy priority"), 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(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardPriority()));
|
|
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->labelCoinControlPriority->addAction(clipboardPriorityAction);
|
|
ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
|
|
ui->labelCoinControlChange->addAction(clipboardChangeAction);
|
|
|
|
// init transaction fee section
|
|
QSettings settings;
|
|
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)), 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->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance());
|
|
connect(model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(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::minTxFee.GetFeePerK());
|
|
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;
|
|
}
|
|
|
|
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;
|
|
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>");
|
|
// 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("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("<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));
|
|
}
|
|
questionString.append(tr("Total Amount %1 (= %2)")
|
|
.arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount))
|
|
.arg(alternativeUnits.join(" " + tr("or") + " ")));
|
|
|
|
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()));
|
|
|
|
updateTabsAndLabels();
|
|
|
|
// 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());
|
|
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& watchBalance, const CAmount& watchUnconfirmedBalance, const CAmount& 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(), 0, 0, 0, 0, 0);
|
|
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! 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(), 10000000));
|
|
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::minTxFee.GetFeePerK());
|
|
}
|
|
|
|
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());
|
|
ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
|
|
}
|
|
|
|
void SendCoinsDialog::updateGlobalFeeVariables()
|
|
{
|
|
if (ui->radioSmartFee->isChecked())
|
|
{
|
|
nTxConfirmTarget = defaultConfirmTarget - ui->sliderSmartFee->value();
|
|
payTxFee = CFeeRate(0);
|
|
}
|
|
else
|
|
{
|
|
nTxConfirmTarget = defaultConfirmTarget;
|
|
payTxFee = CFeeRate(ui->customFee->value());
|
|
fPayAtLeastCustomFee = ui->radioCustomAtLeast->isChecked();
|
|
}
|
|
|
|
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 minimum fee of %1").arg(
|
|
BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::minTxFee.GetFeePerK()) + "/kB")
|
|
);
|
|
}
|
|
|
|
void SendCoinsDialog::updateSmartFeeLabel()
|
|
{
|
|
if(!model || !model->getOptionsModel())
|
|
return;
|
|
|
|
int nBlocksToConfirm = defaultConfirmTarget - ui->sliderSmartFee->value();
|
|
CFeeRate feeRate = mempool.estimateFee(nBlocksToConfirm);
|
|
if (feeRate <= CFeeRate(0)) // not enough data => minfee
|
|
{
|
|
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::minTxFee.GetFeePerK()) + "/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(), feeRate.GetFeePerK()) + "/kB");
|
|
ui->labelSmartFee2->hide();
|
|
ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", nBlocksToConfirm));
|
|
}
|
|
|
|
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 "Priority" to clipboard
|
|
void SendCoinsDialog::coinControlClipboardPriority()
|
|
{
|
|
GUIUtil::setClipboard(ui->labelCoinControlPriority->text());
|
|
}
|
|
|
|
// 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();
|
|
|
|
if (checked)
|
|
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 Bitcoin address"));
|
|
}
|
|
else // Valid address
|
|
{
|
|
CPubKey pubkey;
|
|
CKeyID keyid;
|
|
addr.GetKeyID(keyid);
|
|
if (!model->getPubKey(keyid, pubkey)) // 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() || !model->getOptionsModel()->getCoinControlFeatures())
|
|
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)
|
|
{
|
|
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();
|
|
}
|
|
}
|