123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816 |
- // 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 "scicon.h"
- #include "sendcoinsentry.h"
- #include "walletmodel.h"
-
- #include "base58.h"
- #include "coincontrol.h"
- #include "main.h"
- #include "ui_interface.h"
- #include "wallet/wallet.h"
-
- #include <QMessageBox>
- #include <QScrollBar>
- #include <QSettings>
- #include <QTextDocument>
-
- SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
- QDialog(parent),
- ui(new Ui::SendCoinsDialog),
- clientModel(0),
- model(0),
- fNewRecipientAllowed(true),
- fFeeMinimized(true)
- {
- ui->setupUi(this);
-
- #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
- ui->addButton->setIcon(QIcon());
- ui->clearButton->setIcon(QIcon());
- ui->sendButton->setIcon(QIcon());
- #else
- ui->addButton->setIcon(SingleColorIcon(":/icons/add"));
- ui->clearButton->setIcon(SingleColorIcon(":/icons/remove"));
- ui->sendButton->setIcon(SingleColorIcon(":/icons/send"));
- #endif
-
- 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;
- 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;
- 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(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;
- }
-
- 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 = (int)25 - (int)std::max(0, std::min(24, ui->sliderSmartFee->value()));
- payTxFee = CFeeRate(0);
- }
- else
- {
- nTxConfirmTarget = 25;
- 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 = (int)25 - (int)std::max(0, std::min(24, 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;
- 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();
- }
- }
|