LCOV - code coverage report
Current view: top level - src/qt - sendcoinsdialog.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 0 448 0.0 %
Date: 2015-10-12 22:39:14 Functions: 0 41 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2014 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include "sendcoinsdialog.h"
       6             : #include "ui_sendcoinsdialog.h"
       7             : 
       8             : #include "addresstablemodel.h"
       9             : #include "bitcoinunits.h"
      10             : #include "clientmodel.h"
      11             : #include "coincontroldialog.h"
      12             : #include "guiutil.h"
      13             : #include "optionsmodel.h"
      14             : #include "platformstyle.h"
      15             : #include "sendcoinsentry.h"
      16             : #include "walletmodel.h"
      17             : 
      18             : #include "base58.h"
      19             : #include "coincontrol.h"
      20             : #include "main.h" // mempool and minRelayTxFee
      21             : #include "ui_interface.h"
      22             : #include "txmempool.h"
      23             : #include "wallet/wallet.h"
      24             : 
      25             : #include <QMessageBox>
      26             : #include <QScrollBar>
      27             : #include <QSettings>
      28             : #include <QTextDocument>
      29             : 
      30           0 : SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent) :
      31             :     QDialog(parent),
      32           0 :     ui(new Ui::SendCoinsDialog),
      33             :     clientModel(0),
      34             :     model(0),
      35             :     fNewRecipientAllowed(true),
      36             :     fFeeMinimized(true),
      37           0 :     platformStyle(platformStyle)
      38             : {
      39           0 :     ui->setupUi(this);
      40             : 
      41           0 :     if (!platformStyle->getImagesOnButtons()) {
      42           0 :         ui->addButton->setIcon(QIcon());
      43           0 :         ui->clearButton->setIcon(QIcon());
      44           0 :         ui->sendButton->setIcon(QIcon());
      45             :     } else {
      46           0 :         ui->addButton->setIcon(platformStyle->SingleColorIcon(":/icons/add"));
      47           0 :         ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
      48           0 :         ui->sendButton->setIcon(platformStyle->SingleColorIcon(":/icons/send"));
      49             :     }
      50             : 
      51           0 :     GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
      52             : 
      53           0 :     addEntry();
      54             : 
      55           0 :     connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
      56           0 :     connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
      57             : 
      58             :     // Coin Control
      59           0 :     connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
      60           0 :     connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
      61           0 :     connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &)));
      62             : 
      63             :     // Coin Control: clipboard actions
      64           0 :     QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
      65           0 :     QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
      66           0 :     QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
      67           0 :     QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
      68           0 :     QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
      69           0 :     QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this);
      70           0 :     QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
      71           0 :     QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
      72           0 :     connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity()));
      73           0 :     connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount()));
      74           0 :     connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee()));
      75           0 :     connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee()));
      76           0 :     connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes()));
      77           0 :     connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardPriority()));
      78           0 :     connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput()));
      79           0 :     connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange()));
      80           0 :     ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
      81           0 :     ui->labelCoinControlAmount->addAction(clipboardAmountAction);
      82           0 :     ui->labelCoinControlFee->addAction(clipboardFeeAction);
      83           0 :     ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
      84           0 :     ui->labelCoinControlBytes->addAction(clipboardBytesAction);
      85           0 :     ui->labelCoinControlPriority->addAction(clipboardPriorityAction);
      86           0 :     ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
      87           0 :     ui->labelCoinControlChange->addAction(clipboardChangeAction);
      88             : 
      89             :     // init transaction fee section
      90           0 :     QSettings settings;
      91           0 :     if (!settings.contains("fFeeSectionMinimized"))
      92           0 :         settings.setValue("fFeeSectionMinimized", true);
      93           0 :     if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
      94           0 :         settings.setValue("nFeeRadio", 1); // custom
      95           0 :     if (!settings.contains("nFeeRadio"))
      96           0 :         settings.setValue("nFeeRadio", 0); // recommended
      97           0 :     if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
      98           0 :         settings.setValue("nCustomFeeRadio", 1); // total at least
      99           0 :     if (!settings.contains("nCustomFeeRadio"))
     100           0 :         settings.setValue("nCustomFeeRadio", 0); // per kilobyte
     101           0 :     if (!settings.contains("nSmartFeeSliderPosition"))
     102           0 :         settings.setValue("nSmartFeeSliderPosition", 0);
     103           0 :     if (!settings.contains("nTransactionFee"))
     104           0 :         settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
     105           0 :     if (!settings.contains("fPayOnlyMinFee"))
     106           0 :         settings.setValue("fPayOnlyMinFee", false);
     107           0 :     if (!settings.contains("fSendFreeTransactions"))
     108           0 :         settings.setValue("fSendFreeTransactions", false);
     109           0 :     ui->groupFee->setId(ui->radioSmartFee, 0);
     110           0 :     ui->groupFee->setId(ui->radioCustomFee, 1);
     111           0 :     ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
     112           0 :     ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0);
     113           0 :     ui->groupCustomFee->setId(ui->radioCustomAtLeast, 1);
     114           0 :     ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
     115           0 :     ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
     116           0 :     ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
     117           0 :     ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
     118           0 :     ui->checkBoxFreeTx->setChecked(settings.value("fSendFreeTransactions").toBool());
     119           0 :     minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
     120           0 : }
     121             : 
     122           0 : void SendCoinsDialog::setClientModel(ClientModel *clientModel)
     123             : {
     124           0 :     this->clientModel = clientModel;
     125             : 
     126           0 :     if (clientModel) {
     127           0 :         connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime)), this, SLOT(updateSmartFeeLabel()));
     128             :     }
     129           0 : }
     130             : 
     131           0 : void SendCoinsDialog::setModel(WalletModel *model)
     132             : {
     133           0 :     this->model = model;
     134             : 
     135           0 :     if(model && model->getOptionsModel())
     136             :     {
     137           0 :         for(int i = 0; i < ui->entries->count(); ++i)
     138             :         {
     139           0 :             SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
     140           0 :             if(entry)
     141             :             {
     142           0 :                 entry->setModel(model);
     143             :             }
     144             :         }
     145             : 
     146           0 :         setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(),
     147           0 :                    model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance());
     148           0 :         connect(model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)));
     149           0 :         connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
     150           0 :         updateDisplayUnit();
     151             : 
     152             :         // Coin Control
     153           0 :         connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
     154           0 :         connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
     155           0 :         ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures());
     156           0 :         coinControlUpdateLabels();
     157             : 
     158             :         // fee section
     159           0 :         connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateSmartFeeLabel()));
     160           0 :         connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateGlobalFeeVariables()));
     161           0 :         connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(coinControlUpdateLabels()));
     162           0 :         connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
     163           0 :         connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
     164           0 :         connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
     165           0 :         connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
     166           0 :         connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
     167           0 :         connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(updateGlobalFeeVariables()));
     168           0 :         connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels()));
     169           0 :         connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee()));
     170           0 :         connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
     171           0 :         connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
     172           0 :         connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
     173           0 :         connect(ui->checkBoxFreeTx, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
     174           0 :         connect(ui->checkBoxFreeTx, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
     175           0 :         ui->customFee->setSingleStep(CWallet::minTxFee.GetFeePerK());
     176           0 :         updateFeeSectionControls();
     177           0 :         updateMinFeeLabel();
     178           0 :         updateSmartFeeLabel();
     179           0 :         updateGlobalFeeVariables();
     180             :     }
     181           0 : }
     182             : 
     183           0 : SendCoinsDialog::~SendCoinsDialog()
     184             : {
     185           0 :     QSettings settings;
     186           0 :     settings.setValue("fFeeSectionMinimized", fFeeMinimized);
     187           0 :     settings.setValue("nFeeRadio", ui->groupFee->checkedId());
     188           0 :     settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId());
     189           0 :     settings.setValue("nSmartFeeSliderPosition", ui->sliderSmartFee->value());
     190           0 :     settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
     191           0 :     settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
     192           0 :     settings.setValue("fSendFreeTransactions", ui->checkBoxFreeTx->isChecked());
     193             : 
     194           0 :     delete ui;
     195           0 : }
     196             : 
     197           0 : void SendCoinsDialog::on_sendButton_clicked()
     198             : {
     199           0 :     if(!model || !model->getOptionsModel())
     200           0 :         return;
     201             : 
     202             :     QList<SendCoinsRecipient> recipients;
     203           0 :     bool valid = true;
     204             : 
     205           0 :     for(int i = 0; i < ui->entries->count(); ++i)
     206             :     {
     207           0 :         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
     208           0 :         if(entry)
     209             :         {
     210           0 :             if(entry->validate())
     211             :             {
     212           0 :                 recipients.append(entry->getValue());
     213             :             }
     214             :             else
     215             :             {
     216             :                 valid = false;
     217             :             }
     218             :         }
     219             :     }
     220             : 
     221           0 :     if(!valid || recipients.isEmpty())
     222             :     {
     223           0 :         return;
     224             :     }
     225             : 
     226           0 :     fNewRecipientAllowed = false;
     227           0 :     WalletModel::UnlockContext ctx(model->requestUnlock());
     228           0 :     if(!ctx.isValid())
     229             :     {
     230             :         // Unlock wallet was cancelled
     231           0 :         fNewRecipientAllowed = true;
     232           0 :         return;
     233             :     }
     234             : 
     235             :     // prepare transaction for getting txFee earlier
     236           0 :     WalletModelTransaction currentTransaction(recipients);
     237             :     WalletModel::SendCoinsReturn prepareStatus;
     238           0 :     if (model->getOptionsModel()->getCoinControlFeatures()) // coin control enabled
     239           0 :         prepareStatus = model->prepareTransaction(currentTransaction, CoinControlDialog::coinControl);
     240             :     else
     241           0 :         prepareStatus = model->prepareTransaction(currentTransaction);
     242             : 
     243             :     // process prepareStatus and on error generate message shown to user
     244             :     processSendCoinsReturn(prepareStatus,
     245           0 :         BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee()));
     246             : 
     247           0 :     if(prepareStatus.status != WalletModel::OK) {
     248           0 :         fNewRecipientAllowed = true;
     249           0 :         return;
     250             :     }
     251             : 
     252           0 :     CAmount txFee = currentTransaction.getTransactionFee();
     253             : 
     254             :     // Format confirmation message
     255             :     QStringList formatted;
     256           0 :     Q_FOREACH(const SendCoinsRecipient &rcp, currentTransaction.getRecipients())
     257             :     {
     258             :         // generate bold amount string
     259           0 :         QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
     260           0 :         amount.append("</b>");
     261             :         // generate monospace address string
     262           0 :         QString address = "<span style='font-family: monospace;'>" + rcp.address;
     263           0 :         address.append("</span>");
     264             : 
     265           0 :         QString recipientElement;
     266             : 
     267           0 :         if (!rcp.paymentRequest.IsInitialized()) // normal payment
     268             :         {
     269           0 :             if(rcp.label.length() > 0) // label with address
     270             :             {
     271           0 :                 recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label));
     272           0 :                 recipientElement.append(QString(" (%1)").arg(address));
     273             :             }
     274             :             else // just address
     275             :             {
     276           0 :                 recipientElement = tr("%1 to %2").arg(amount, address);
     277             :             }
     278             :         }
     279           0 :         else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request
     280             :         {
     281           0 :             recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant));
     282             :         }
     283             :         else // unauthenticated payment request
     284             :         {
     285           0 :             recipientElement = tr("%1 to %2").arg(amount, address);
     286             :         }
     287             : 
     288           0 :         formatted.append(recipientElement);
     289           0 :     }
     290             : 
     291           0 :     QString questionString = tr("Are you sure you want to send?");
     292           0 :     questionString.append("<br /><br />%1");
     293             : 
     294           0 :     if(txFee > 0)
     295             :     {
     296             :         // append fee string if a fee is required
     297           0 :         questionString.append("<hr /><span style='color:#aa0000;'>");
     298           0 :         questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
     299           0 :         questionString.append("</span> ");
     300           0 :         questionString.append(tr("added as transaction fee"));
     301             : 
     302             :         // append transaction size
     303           0 :         questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)");
     304             :     }
     305             : 
     306             :     // add total amount in all subdivision units
     307           0 :     questionString.append("<hr />");
     308           0 :     CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
     309             :     QStringList alternativeUnits;
     310           0 :     Q_FOREACH(BitcoinUnits::Unit u, BitcoinUnits::availableUnits())
     311             :     {
     312           0 :         if(u != model->getOptionsModel()->getDisplayUnit())
     313           0 :             alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
     314             :     }
     315             :     questionString.append(tr("Total Amount %1<span style='font-size:10pt;font-weight:normal;'><br />(=%2)</span>")
     316             :         .arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount))
     317           0 :         .arg(alternativeUnits.join(" " + tr("or") + "<br />")));
     318             : 
     319             :     QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
     320             :         questionString.arg(formatted.join("<br />")),
     321           0 :         QMessageBox::Yes | QMessageBox::Cancel,
     322           0 :         QMessageBox::Cancel);
     323             : 
     324           0 :     if(retval != QMessageBox::Yes)
     325             :     {
     326           0 :         fNewRecipientAllowed = true;
     327             :         return;
     328             :     }
     329             : 
     330             :     // now send the prepared transaction
     331           0 :     WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
     332             :     // process sendStatus and on error generate message shown to user
     333           0 :     processSendCoinsReturn(sendStatus);
     334             : 
     335           0 :     if (sendStatus.status == WalletModel::OK)
     336             :     {
     337           0 :         accept();
     338           0 :         CoinControlDialog::coinControl->UnSelectAll();
     339           0 :         coinControlUpdateLabels();
     340             :     }
     341           0 :     fNewRecipientAllowed = true;
     342             : }
     343             : 
     344           0 : void SendCoinsDialog::clear()
     345             : {
     346             :     // Remove entries until only one left
     347           0 :     while(ui->entries->count())
     348             :     {
     349           0 :         ui->entries->takeAt(0)->widget()->deleteLater();
     350             :     }
     351           0 :     addEntry();
     352             : 
     353             :     updateTabsAndLabels();
     354           0 : }
     355             : 
     356           0 : void SendCoinsDialog::reject()
     357             : {
     358           0 :     clear();
     359           0 : }
     360             : 
     361           0 : void SendCoinsDialog::accept()
     362             : {
     363           0 :     clear();
     364           0 : }
     365             : 
     366           0 : SendCoinsEntry *SendCoinsDialog::addEntry()
     367             : {
     368           0 :     SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
     369           0 :     entry->setModel(model);
     370           0 :     ui->entries->addWidget(entry);
     371           0 :     connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
     372           0 :     connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels()));
     373           0 :     connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this, SLOT(coinControlUpdateLabels()));
     374             : 
     375             :     updateTabsAndLabels();
     376             : 
     377             :     // Focus the field, so that entry can start immediately
     378           0 :     entry->clear();
     379           0 :     entry->setFocus();
     380           0 :     ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
     381           0 :     qApp->processEvents();
     382           0 :     QScrollBar* bar = ui->scrollArea->verticalScrollBar();
     383           0 :     if(bar)
     384           0 :         bar->setSliderPosition(bar->maximum());
     385           0 :     return entry;
     386             : }
     387             : 
     388           0 : void SendCoinsDialog::updateTabsAndLabels()
     389             : {
     390           0 :     setupTabChain(0);
     391           0 :     coinControlUpdateLabels();
     392           0 : }
     393             : 
     394           0 : void SendCoinsDialog::removeEntry(SendCoinsEntry* entry)
     395             : {
     396           0 :     entry->hide();
     397             : 
     398             :     // If the last entry is about to be removed add an empty one
     399           0 :     if (ui->entries->count() == 1)
     400           0 :         addEntry();
     401             : 
     402           0 :     entry->deleteLater();
     403             : 
     404             :     updateTabsAndLabels();
     405           0 : }
     406             : 
     407           0 : QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
     408             : {
     409           0 :     for(int i = 0; i < ui->entries->count(); ++i)
     410             :     {
     411           0 :         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
     412           0 :         if(entry)
     413             :         {
     414           0 :             prev = entry->setupTabChain(prev);
     415             :         }
     416             :     }
     417           0 :     QWidget::setTabOrder(prev, ui->sendButton);
     418           0 :     QWidget::setTabOrder(ui->sendButton, ui->clearButton);
     419           0 :     QWidget::setTabOrder(ui->clearButton, ui->addButton);
     420           0 :     return ui->addButton;
     421             : }
     422             : 
     423           0 : void SendCoinsDialog::setAddress(const QString &address)
     424             : {
     425           0 :     SendCoinsEntry *entry = 0;
     426             :     // Replace the first entry if it is still unused
     427           0 :     if(ui->entries->count() == 1)
     428             :     {
     429           0 :         SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
     430           0 :         if(first->isClear())
     431             :         {
     432           0 :             entry = first;
     433             :         }
     434             :     }
     435           0 :     if(!entry)
     436             :     {
     437           0 :         entry = addEntry();
     438             :     }
     439             : 
     440           0 :     entry->setAddress(address);
     441           0 : }
     442             : 
     443           0 : void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv)
     444             : {
     445           0 :     if(!fNewRecipientAllowed)
     446           0 :         return;
     447             : 
     448           0 :     SendCoinsEntry *entry = 0;
     449             :     // Replace the first entry if it is still unused
     450           0 :     if(ui->entries->count() == 1)
     451             :     {
     452           0 :         SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
     453           0 :         if(first->isClear())
     454             :         {
     455           0 :             entry = first;
     456             :         }
     457             :     }
     458           0 :     if(!entry)
     459             :     {
     460           0 :         entry = addEntry();
     461             :     }
     462             : 
     463           0 :     entry->setValue(rv);
     464             :     updateTabsAndLabels();
     465             : }
     466             : 
     467           0 : bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv)
     468             : {
     469             :     // Just paste the entry, all pre-checks
     470             :     // are done in paymentserver.cpp.
     471           0 :     pasteEntry(rv);
     472           0 :     return true;
     473             : }
     474             : 
     475           0 : void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
     476             :                                  const CAmount& watchBalance, const CAmount& watchUnconfirmedBalance, const CAmount& watchImmatureBalance)
     477             : {
     478             :     Q_UNUSED(unconfirmedBalance);
     479             :     Q_UNUSED(immatureBalance);
     480             :     Q_UNUSED(watchBalance);
     481             :     Q_UNUSED(watchUnconfirmedBalance);
     482             :     Q_UNUSED(watchImmatureBalance);
     483             : 
     484           0 :     if(model && model->getOptionsModel())
     485             :     {
     486           0 :         ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
     487             :     }
     488           0 : }
     489             : 
     490           0 : void SendCoinsDialog::updateDisplayUnit()
     491             : {
     492           0 :     setBalance(model->getBalance(), 0, 0, 0, 0, 0);
     493           0 :     ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
     494           0 :     updateMinFeeLabel();
     495           0 :     updateSmartFeeLabel();
     496           0 : }
     497             : 
     498           0 : void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
     499             : {
     500             :     QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
     501             :     // Default to a warning message, override if error message is needed
     502           0 :     msgParams.second = CClientUIInterface::MSG_WARNING;
     503             : 
     504             :     // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
     505             :     // WalletModel::TransactionCommitFailed is used only in WalletModel::sendCoins()
     506             :     // all others are used only in WalletModel::prepareTransaction()
     507           0 :     switch(sendCoinsReturn.status)
     508             :     {
     509             :     case WalletModel::InvalidAddress:
     510           0 :         msgParams.first = tr("The recipient address is not valid. Please recheck.");
     511           0 :         break;
     512             :     case WalletModel::InvalidAmount:
     513           0 :         msgParams.first = tr("The amount to pay must be larger than 0.");
     514           0 :         break;
     515             :     case WalletModel::AmountExceedsBalance:
     516           0 :         msgParams.first = tr("The amount exceeds your balance.");
     517           0 :         break;
     518             :     case WalletModel::AmountWithFeeExceedsBalance:
     519           0 :         msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
     520           0 :         break;
     521             :     case WalletModel::DuplicateAddress:
     522           0 :         msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
     523           0 :         break;
     524             :     case WalletModel::TransactionCreationFailed:
     525           0 :         msgParams.first = tr("Transaction creation failed!");
     526           0 :         msgParams.second = CClientUIInterface::MSG_ERROR;
     527           0 :         break;
     528             :     case WalletModel::TransactionCommitFailed:
     529           0 :         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.");
     530           0 :         msgParams.second = CClientUIInterface::MSG_ERROR;
     531           0 :         break;
     532             :     case WalletModel::AbsurdFee:
     533           0 :         msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), 10000000));
     534           0 :         break;
     535             :     case WalletModel::PaymentRequestExpired:
     536           0 :         msgParams.first = tr("Payment request expired.");
     537           0 :         msgParams.second = CClientUIInterface::MSG_ERROR;
     538           0 :         break;
     539             :     // included to prevent a compiler warning.
     540             :     case WalletModel::OK:
     541             :     default:
     542           0 :         return;
     543             :     }
     544             : 
     545           0 :     Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
     546             : }
     547             : 
     548           0 : void SendCoinsDialog::minimizeFeeSection(bool fMinimize)
     549             : {
     550           0 :     ui->labelFeeMinimized->setVisible(fMinimize);
     551           0 :     ui->buttonChooseFee  ->setVisible(fMinimize);
     552           0 :     ui->buttonMinimizeFee->setVisible(!fMinimize);
     553           0 :     ui->frameFeeSelection->setVisible(!fMinimize);
     554           0 :     ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
     555           0 :     fFeeMinimized = fMinimize;
     556           0 : }
     557             : 
     558           0 : void SendCoinsDialog::on_buttonChooseFee_clicked()
     559             : {
     560           0 :     minimizeFeeSection(false);
     561           0 : }
     562             : 
     563           0 : void SendCoinsDialog::on_buttonMinimizeFee_clicked()
     564             : {
     565           0 :     updateFeeMinimizedLabel();
     566           0 :     minimizeFeeSection(true);
     567           0 : }
     568             : 
     569           0 : void SendCoinsDialog::setMinimumFee()
     570             : {
     571           0 :     ui->radioCustomPerKilobyte->setChecked(true);
     572           0 :     ui->customFee->setValue(CWallet::minTxFee.GetFeePerK());
     573           0 : }
     574             : 
     575           0 : void SendCoinsDialog::updateFeeSectionControls()
     576             : {
     577           0 :     ui->sliderSmartFee          ->setEnabled(ui->radioSmartFee->isChecked());
     578           0 :     ui->labelSmartFee           ->setEnabled(ui->radioSmartFee->isChecked());
     579           0 :     ui->labelSmartFee2          ->setEnabled(ui->radioSmartFee->isChecked());
     580           0 :     ui->labelSmartFee3          ->setEnabled(ui->radioSmartFee->isChecked());
     581           0 :     ui->labelFeeEstimation      ->setEnabled(ui->radioSmartFee->isChecked());
     582           0 :     ui->labelSmartFeeNormal     ->setEnabled(ui->radioSmartFee->isChecked());
     583           0 :     ui->labelSmartFeeFast       ->setEnabled(ui->radioSmartFee->isChecked());
     584           0 :     ui->checkBoxMinimumFee      ->setEnabled(ui->radioCustomFee->isChecked());
     585           0 :     ui->labelMinFeeWarning      ->setEnabled(ui->radioCustomFee->isChecked());
     586           0 :     ui->radioCustomPerKilobyte  ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
     587           0 :     ui->radioCustomAtLeast      ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
     588           0 :     ui->customFee               ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
     589           0 : }
     590             : 
     591           0 : void SendCoinsDialog::updateGlobalFeeVariables()
     592             : {
     593           0 :     if (ui->radioSmartFee->isChecked())
     594             :     {
     595           0 :         nTxConfirmTarget = defaultConfirmTarget - ui->sliderSmartFee->value();
     596           0 :         payTxFee = CFeeRate(0);
     597             :     }
     598             :     else
     599             :     {
     600           0 :         nTxConfirmTarget = defaultConfirmTarget;
     601           0 :         payTxFee = CFeeRate(ui->customFee->value());
     602           0 :         fPayAtLeastCustomFee = ui->radioCustomAtLeast->isChecked();
     603             :     }
     604             : 
     605           0 :     fSendFreeTransactions = ui->checkBoxFreeTx->isChecked();
     606           0 : }
     607             : 
     608           0 : void SendCoinsDialog::updateFeeMinimizedLabel()
     609             : {
     610           0 :     if(!model || !model->getOptionsModel())
     611           0 :         return;
     612             : 
     613           0 :     if (ui->radioSmartFee->isChecked())
     614           0 :         ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
     615             :     else {
     616           0 :         ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) +
     617           0 :             ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : ""));
     618             :     }
     619             : }
     620             : 
     621           0 : void SendCoinsDialog::updateMinFeeLabel()
     622             : {
     623           0 :     if (model && model->getOptionsModel())
     624             :         ui->checkBoxMinimumFee->setText(tr("Pay only the minimum fee of %1").arg(
     625           0 :             BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::minTxFee.GetFeePerK()) + "/kB")
     626           0 :         );
     627           0 : }
     628             : 
     629           0 : void SendCoinsDialog::updateSmartFeeLabel()
     630             : {
     631           0 :     if(!model || !model->getOptionsModel())
     632           0 :         return;
     633             : 
     634           0 :     int nBlocksToConfirm = defaultConfirmTarget - ui->sliderSmartFee->value();
     635           0 :     CFeeRate feeRate = mempool.estimateFee(nBlocksToConfirm);
     636           0 :     if (feeRate <= CFeeRate(0)) // not enough data => minfee
     637             :     {
     638           0 :         ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::minTxFee.GetFeePerK()) + "/kB");
     639           0 :         ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
     640           0 :         ui->labelFeeEstimation->setText("");
     641             :     }
     642             :     else
     643             :     {
     644           0 :         ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
     645           0 :         ui->labelSmartFee2->hide();
     646           0 :         ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", nBlocksToConfirm));
     647             :     }
     648             : 
     649           0 :     updateFeeMinimizedLabel();
     650             : }
     651             : 
     652             : // Coin Control: copy label "Quantity" to clipboard
     653           0 : void SendCoinsDialog::coinControlClipboardQuantity()
     654             : {
     655           0 :     GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
     656           0 : }
     657             : 
     658             : // Coin Control: copy label "Amount" to clipboard
     659           0 : void SendCoinsDialog::coinControlClipboardAmount()
     660             : {
     661           0 :     GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
     662           0 : }
     663             : 
     664             : // Coin Control: copy label "Fee" to clipboard
     665           0 : void SendCoinsDialog::coinControlClipboardFee()
     666             : {
     667           0 :     GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
     668           0 : }
     669             : 
     670             : // Coin Control: copy label "After fee" to clipboard
     671           0 : void SendCoinsDialog::coinControlClipboardAfterFee()
     672             : {
     673           0 :     GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
     674           0 : }
     675             : 
     676             : // Coin Control: copy label "Bytes" to clipboard
     677           0 : void SendCoinsDialog::coinControlClipboardBytes()
     678             : {
     679           0 :     GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
     680           0 : }
     681             : 
     682             : // Coin Control: copy label "Priority" to clipboard
     683           0 : void SendCoinsDialog::coinControlClipboardPriority()
     684             : {
     685           0 :     GUIUtil::setClipboard(ui->labelCoinControlPriority->text());
     686           0 : }
     687             : 
     688             : // Coin Control: copy label "Dust" to clipboard
     689           0 : void SendCoinsDialog::coinControlClipboardLowOutput()
     690             : {
     691           0 :     GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
     692           0 : }
     693             : 
     694             : // Coin Control: copy label "Change" to clipboard
     695           0 : void SendCoinsDialog::coinControlClipboardChange()
     696             : {
     697           0 :     GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
     698           0 : }
     699             : 
     700             : // Coin Control: settings menu - coin control enabled/disabled by user
     701           0 : void SendCoinsDialog::coinControlFeatureChanged(bool checked)
     702             : {
     703           0 :     ui->frameCoinControl->setVisible(checked);
     704             : 
     705           0 :     if (!checked && model) // coin control features disabled
     706           0 :         CoinControlDialog::coinControl->SetNull();
     707             : 
     708           0 :     if (checked)
     709           0 :         coinControlUpdateLabels();
     710           0 : }
     711             : 
     712             : // Coin Control: button inputs -> show actual coin control dialog
     713           0 : void SendCoinsDialog::coinControlButtonClicked()
     714             : {
     715           0 :     CoinControlDialog dlg(platformStyle);
     716           0 :     dlg.setModel(model);
     717           0 :     dlg.exec();
     718           0 :     coinControlUpdateLabels();
     719           0 : }
     720             : 
     721             : // Coin Control: checkbox custom change address
     722           0 : void SendCoinsDialog::coinControlChangeChecked(int state)
     723             : {
     724           0 :     if (state == Qt::Unchecked)
     725             :     {
     726           0 :         CoinControlDialog::coinControl->destChange = CNoDestination();
     727           0 :         ui->labelCoinControlChangeLabel->clear();
     728             :     }
     729             :     else
     730             :         // use this to re-validate an already entered address
     731           0 :         coinControlChangeEdited(ui->lineEditCoinControlChange->text());
     732             : 
     733           0 :     ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
     734           0 : }
     735             : 
     736             : // Coin Control: custom change address changed
     737           0 : void SendCoinsDialog::coinControlChangeEdited(const QString& text)
     738             : {
     739           0 :     if (model && model->getAddressTableModel())
     740             :     {
     741             :         // Default to no change address until verified
     742           0 :         CoinControlDialog::coinControl->destChange = CNoDestination();
     743           0 :         ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
     744             : 
     745           0 :         CBitcoinAddress addr = CBitcoinAddress(text.toStdString());
     746             : 
     747           0 :         if (text.isEmpty()) // Nothing entered
     748             :         {
     749           0 :             ui->labelCoinControlChangeLabel->setText("");
     750             :         }
     751           0 :         else if (!addr.IsValid()) // Invalid address
     752             :         {
     753           0 :             ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
     754             :         }
     755             :         else // Valid address
     756             :         {
     757             :             CKeyID keyid;
     758           0 :             addr.GetKeyID(keyid);
     759           0 :             if (!model->havePrivKey(keyid)) // Unknown change address
     760             :             {
     761           0 :                 ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
     762             :             }
     763             :             else // Known change address
     764             :             {
     765           0 :                 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
     766             : 
     767             :                 // Query label
     768           0 :                 QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
     769           0 :                 if (!associatedLabel.isEmpty())
     770           0 :                     ui->labelCoinControlChangeLabel->setText(associatedLabel);
     771             :                 else
     772           0 :                     ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
     773             : 
     774           0 :                 CoinControlDialog::coinControl->destChange = addr.Get();
     775             :             }
     776             :         }
     777             :     }
     778           0 : }
     779             : 
     780             : // Coin Control: update labels
     781           0 : void SendCoinsDialog::coinControlUpdateLabels()
     782             : {
     783           0 :     if (!model || !model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures())
     784           0 :         return;
     785             : 
     786             :     // set pay amounts
     787           0 :     CoinControlDialog::payAmounts.clear();
     788           0 :     CoinControlDialog::fSubtractFeeFromAmount = false;
     789           0 :     for(int i = 0; i < ui->entries->count(); ++i)
     790             :     {
     791           0 :         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
     792           0 :         if(entry)
     793             :         {
     794           0 :             SendCoinsRecipient rcp = entry->getValue();
     795           0 :             CoinControlDialog::payAmounts.append(rcp.amount);
     796           0 :             if (rcp.fSubtractFeeFromAmount)
     797           0 :                 CoinControlDialog::fSubtractFeeFromAmount = true;
     798             :         }
     799             :     }
     800             : 
     801           0 :     if (CoinControlDialog::coinControl->HasSelected())
     802             :     {
     803             :         // actual coin control calculation
     804           0 :         CoinControlDialog::updateLabels(model, this);
     805             : 
     806             :         // show coin control stats
     807           0 :         ui->labelCoinControlAutomaticallySelected->hide();
     808           0 :         ui->widgetCoinControl->show();
     809             :     }
     810             :     else
     811             :     {
     812             :         // hide coin control stats
     813           0 :         ui->labelCoinControlAutomaticallySelected->show();
     814           0 :         ui->widgetCoinControl->hide();
     815           0 :         ui->labelCoinControlInsuffFunds->hide();
     816             :     }
     817           0 : }

Generated by: LCOV version 1.11