LCOV - code coverage report
Current view: top level - src/qt - sendcoinsdialog.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 0 618 0.0 %
Date: 2020-09-26 01:30:44 Functions: 0 49 0.0 %

          Line data    Source code
       1             : // Copyright (c) 2011-2020 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             : #if defined(HAVE_CONFIG_H)
       6             : #include <config/bitcoin-config.h>
       7             : #endif
       8             : 
       9             : #include <qt/sendcoinsdialog.h>
      10             : #include <qt/forms/ui_sendcoinsdialog.h>
      11             : 
      12             : #include <qt/addresstablemodel.h>
      13             : #include <qt/bitcoinunits.h>
      14             : #include <qt/clientmodel.h>
      15             : #include <qt/coincontroldialog.h>
      16             : #include <qt/guiutil.h>
      17             : #include <qt/optionsmodel.h>
      18             : #include <qt/platformstyle.h>
      19             : #include <qt/sendcoinsentry.h>
      20             : 
      21             : #include <chainparams.h>
      22             : #include <interfaces/node.h>
      23             : #include <key_io.h>
      24             : #include <node/ui_interface.h>
      25             : #include <policy/fees.h>
      26             : #include <txmempool.h>
      27             : #include <wallet/coincontrol.h>
      28             : #include <wallet/fees.h>
      29             : #include <wallet/wallet.h>
      30             : 
      31             : #include <QFontMetrics>
      32             : #include <QScrollBar>
      33             : #include <QSettings>
      34             : #include <QTextDocument>
      35             : 
      36             : static const std::array<int, 9> confTargets = { {2, 4, 6, 12, 24, 48, 144, 504, 1008} };
      37           0 : int getConfTargetForIndex(int index) {
      38           0 :     if (index+1 > static_cast<int>(confTargets.size())) {
      39           0 :         return confTargets.back();
      40             :     }
      41           0 :     if (index < 0) {
      42           0 :         return confTargets[0];
      43             :     }
      44           0 :     return confTargets[index];
      45           0 : }
      46           0 : int getIndexForConfTarget(int target) {
      47           0 :     for (unsigned int i = 0; i < confTargets.size(); i++) {
      48           0 :         if (confTargets[i] >= target) {
      49           0 :             return i;
      50             :         }
      51             :     }
      52           0 :     return confTargets.size() - 1;
      53           0 : }
      54             : 
      55           0 : SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
      56           0 :     QDialog(parent),
      57           0 :     ui(new Ui::SendCoinsDialog),
      58           0 :     clientModel(nullptr),
      59           0 :     model(nullptr),
      60           0 :     m_coin_control(new CCoinControl),
      61           0 :     fNewRecipientAllowed(true),
      62           0 :     fFeeMinimized(true),
      63           0 :     platformStyle(_platformStyle)
      64           0 : {
      65           0 :     ui->setupUi(this);
      66             : 
      67           0 :     if (!_platformStyle->getImagesOnButtons()) {
      68           0 :         ui->addButton->setIcon(QIcon());
      69           0 :         ui->clearButton->setIcon(QIcon());
      70           0 :         ui->sendButton->setIcon(QIcon());
      71           0 :     } else {
      72           0 :         ui->addButton->setIcon(_platformStyle->SingleColorIcon(":/icons/add"));
      73           0 :         ui->clearButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
      74           0 :         ui->sendButton->setIcon(_platformStyle->SingleColorIcon(":/icons/send"));
      75             :     }
      76             : 
      77           0 :     GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
      78             : 
      79           0 :     addEntry();
      80             : 
      81           0 :     connect(ui->addButton, &QPushButton::clicked, this, &SendCoinsDialog::addEntry);
      82           0 :     connect(ui->clearButton, &QPushButton::clicked, this, &SendCoinsDialog::clear);
      83             : 
      84             :     // Coin Control
      85           0 :     connect(ui->pushButtonCoinControl, &QPushButton::clicked, this, &SendCoinsDialog::coinControlButtonClicked);
      86           0 :     connect(ui->checkBoxCoinControlChange, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlChangeChecked);
      87           0 :     connect(ui->lineEditCoinControlChange, &QValidatedLineEdit::textEdited, this, &SendCoinsDialog::coinControlChangeEdited);
      88             : 
      89             :     // Coin Control: clipboard actions
      90           0 :     QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
      91           0 :     QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
      92           0 :     QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
      93           0 :     QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
      94           0 :     QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
      95           0 :     QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
      96           0 :     QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
      97           0 :     connect(clipboardQuantityAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardQuantity);
      98           0 :     connect(clipboardAmountAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAmount);
      99           0 :     connect(clipboardFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardFee);
     100           0 :     connect(clipboardAfterFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAfterFee);
     101           0 :     connect(clipboardBytesAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardBytes);
     102           0 :     connect(clipboardLowOutputAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardLowOutput);
     103           0 :     connect(clipboardChangeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardChange);
     104           0 :     ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
     105           0 :     ui->labelCoinControlAmount->addAction(clipboardAmountAction);
     106           0 :     ui->labelCoinControlFee->addAction(clipboardFeeAction);
     107           0 :     ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
     108           0 :     ui->labelCoinControlBytes->addAction(clipboardBytesAction);
     109           0 :     ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
     110           0 :     ui->labelCoinControlChange->addAction(clipboardChangeAction);
     111             : 
     112             :     // init transaction fee section
     113           0 :     QSettings settings;
     114           0 :     if (!settings.contains("fFeeSectionMinimized"))
     115           0 :         settings.setValue("fFeeSectionMinimized", true);
     116           0 :     if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
     117           0 :         settings.setValue("nFeeRadio", 1); // custom
     118           0 :     if (!settings.contains("nFeeRadio"))
     119           0 :         settings.setValue("nFeeRadio", 0); // recommended
     120           0 :     if (!settings.contains("nSmartFeeSliderPosition"))
     121           0 :         settings.setValue("nSmartFeeSliderPosition", 0);
     122           0 :     if (!settings.contains("nTransactionFee"))
     123           0 :         settings.setValue("nTransactionFee", (qint64)DEFAULT_PAY_TX_FEE);
     124           0 :     ui->groupFee->setId(ui->radioSmartFee, 0);
     125           0 :     ui->groupFee->setId(ui->radioCustomFee, 1);
     126           0 :     ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
     127           0 :     ui->customFee->SetAllowEmpty(false);
     128           0 :     ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
     129           0 :     minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
     130           0 : }
     131             : 
     132           0 : void SendCoinsDialog::setClientModel(ClientModel *_clientModel)
     133             : {
     134           0 :     this->clientModel = _clientModel;
     135             : 
     136           0 :     if (_clientModel) {
     137           0 :         connect(_clientModel, &ClientModel::numBlocksChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
     138           0 :     }
     139           0 : }
     140             : 
     141           0 : void SendCoinsDialog::setModel(WalletModel *_model)
     142             : {
     143           0 :     this->model = _model;
     144             : 
     145           0 :     if(_model && _model->getOptionsModel())
     146             :     {
     147           0 :         for(int i = 0; i < ui->entries->count(); ++i)
     148             :         {
     149           0 :             SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
     150           0 :             if(entry)
     151             :             {
     152           0 :                 entry->setModel(_model);
     153           0 :             }
     154             :         }
     155             : 
     156           0 :         interfaces::WalletBalances balances = _model->wallet().getBalances();
     157           0 :         setBalance(balances);
     158           0 :         connect(_model, &WalletModel::balanceChanged, this, &SendCoinsDialog::setBalance);
     159           0 :         connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &SendCoinsDialog::updateDisplayUnit);
     160           0 :         updateDisplayUnit();
     161             : 
     162             :         // Coin Control
     163           0 :         connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
     164           0 :         connect(_model->getOptionsModel(), &OptionsModel::coinControlFeaturesChanged, this, &SendCoinsDialog::coinControlFeatureChanged);
     165           0 :         ui->frameCoinControl->setVisible(_model->getOptionsModel()->getCoinControlFeatures());
     166           0 :         coinControlUpdateLabels();
     167             : 
     168             :         // fee section
     169           0 :         for (const int n : confTargets) {
     170           0 :             ui->confTargetSelector->addItem(tr("%1 (%2 blocks)").arg(GUIUtil::formatNiceTimeOffset(n*Params().GetConsensus().nPowTargetSpacing)).arg(n));
     171             :         }
     172           0 :         connect(ui->confTargetSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::updateSmartFeeLabel);
     173           0 :         connect(ui->confTargetSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::coinControlUpdateLabels);
     174           0 :         connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::updateFeeSectionControls);
     175           0 :         connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::coinControlUpdateLabels);
     176           0 :         connect(ui->customFee, &BitcoinAmountField::valueChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
     177           0 :         connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
     178           0 :         connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
     179           0 :         CAmount requiredFee = model->wallet().getRequiredFee(1000);
     180           0 :         ui->customFee->SetMinValue(requiredFee);
     181           0 :         if (ui->customFee->value() < requiredFee) {
     182           0 :             ui->customFee->setValue(requiredFee);
     183           0 :         }
     184           0 :         ui->customFee->setSingleStep(requiredFee);
     185           0 :         updateFeeSectionControls();
     186           0 :         updateSmartFeeLabel();
     187             : 
     188             :         // set default rbf checkbox state
     189           0 :         ui->optInRBF->setCheckState(Qt::Checked);
     190             : 
     191           0 :         if (model->wallet().privateKeysDisabled()) {
     192           0 :             ui->sendButton->setText(tr("Cr&eate Unsigned"));
     193           0 :             ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
     194           0 :         }
     195             : 
     196             :         // set the smartfee-sliders default value (wallets default conf.target or last stored value)
     197           0 :         QSettings settings;
     198           0 :         if (settings.value("nSmartFeeSliderPosition").toInt() != 0) {
     199             :             // migrate nSmartFeeSliderPosition to nConfTarget
     200             :             // nConfTarget is available since 0.15 (replaced nSmartFeeSliderPosition)
     201           0 :             int nConfirmTarget = 25 - settings.value("nSmartFeeSliderPosition").toInt(); // 25 == old slider range
     202           0 :             settings.setValue("nConfTarget", nConfirmTarget);
     203           0 :             settings.remove("nSmartFeeSliderPosition");
     204           0 :         }
     205           0 :         if (settings.value("nConfTarget").toInt() == 0)
     206           0 :             ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->wallet().getConfirmTarget()));
     207             :         else
     208           0 :             ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt()));
     209           0 :     }
     210           0 : }
     211             : 
     212           0 : SendCoinsDialog::~SendCoinsDialog()
     213           0 : {
     214           0 :     QSettings settings;
     215           0 :     settings.setValue("fFeeSectionMinimized", fFeeMinimized);
     216           0 :     settings.setValue("nFeeRadio", ui->groupFee->checkedId());
     217           0 :     settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex()));
     218           0 :     settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
     219             : 
     220           0 :     delete ui;
     221           0 : }
     222             : 
     223           0 : bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informative_text, QString& detailed_text)
     224             : {
     225           0 :     QList<SendCoinsRecipient> recipients;
     226           0 :     bool valid = true;
     227             : 
     228           0 :     for(int i = 0; i < ui->entries->count(); ++i)
     229             :     {
     230           0 :         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
     231           0 :         if(entry)
     232             :         {
     233           0 :             if(entry->validate(model->node()))
     234             :             {
     235           0 :                 recipients.append(entry->getValue());
     236           0 :             }
     237           0 :             else if (valid)
     238             :             {
     239           0 :                 ui->scrollArea->ensureWidgetVisible(entry);
     240             :                 valid = false;
     241           0 :             }
     242             :         }
     243           0 :     }
     244             : 
     245           0 :     if(!valid || recipients.isEmpty())
     246             :     {
     247           0 :         return false;
     248             :     }
     249             : 
     250           0 :     fNewRecipientAllowed = false;
     251           0 :     WalletModel::UnlockContext ctx(model->requestUnlock());
     252           0 :     if(!ctx.isValid())
     253             :     {
     254             :         // Unlock wallet was cancelled
     255           0 :         fNewRecipientAllowed = true;
     256           0 :         return false;
     257             :     }
     258             : 
     259             :     // prepare transaction for getting txFee earlier
     260           0 :     m_current_transaction = MakeUnique<WalletModelTransaction>(recipients);
     261           0 :     WalletModel::SendCoinsReturn prepareStatus;
     262             : 
     263           0 :     updateCoinControlState(*m_coin_control);
     264             : 
     265           0 :     prepareStatus = model->prepareTransaction(*m_current_transaction, *m_coin_control);
     266             : 
     267             :     // process prepareStatus and on error generate message shown to user
     268           0 :     processSendCoinsReturn(prepareStatus,
     269           0 :         BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), m_current_transaction->getTransactionFee()));
     270             : 
     271           0 :     if(prepareStatus.status != WalletModel::OK) {
     272           0 :         fNewRecipientAllowed = true;
     273           0 :         return false;
     274             :     }
     275             : 
     276           0 :     CAmount txFee = m_current_transaction->getTransactionFee();
     277           0 :     QStringList formatted;
     278           0 :     for (const SendCoinsRecipient &rcp : m_current_transaction->getRecipients())
     279             :     {
     280             :         // generate amount string with wallet name in case of multiwallet
     281           0 :         QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
     282           0 :         if (model->isMultiwallet()) {
     283           0 :             amount.append(tr(" from wallet '%1'").arg(GUIUtil::HtmlEscape(model->getWalletName())));
     284           0 :         }
     285             : 
     286             :         // generate address string
     287           0 :         QString address = rcp.address;
     288             : 
     289           0 :         QString recipientElement;
     290             : 
     291             :         {
     292           0 :             if(rcp.label.length() > 0) // label with address
     293             :             {
     294           0 :                 recipientElement.append(tr("%1 to '%2'").arg(amount, GUIUtil::HtmlEscape(rcp.label)));
     295           0 :                 recipientElement.append(QString(" (%1)").arg(address));
     296           0 :             }
     297             :             else // just address
     298             :             {
     299           0 :                 recipientElement.append(tr("%1 to %2").arg(amount, address));
     300             :             }
     301             :         }
     302           0 :         formatted.append(recipientElement);
     303           0 :     }
     304             : 
     305           0 :     if (model->wallet().privateKeysDisabled()) {
     306           0 :         question_string.append(tr("Do you want to draft this transaction?"));
     307           0 :     } else {
     308           0 :         question_string.append(tr("Are you sure you want to send?"));
     309             :     }
     310             : 
     311           0 :     question_string.append("<br /><span style='font-size:10pt;'>");
     312           0 :     if (model->wallet().privateKeysDisabled()) {
     313           0 :         question_string.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
     314           0 :     } else {
     315           0 :         question_string.append(tr("Please, review your transaction."));
     316             :     }
     317           0 :     question_string.append("</span>%1");
     318             : 
     319           0 :     if(txFee > 0)
     320             :     {
     321             :         // append fee string if a fee is required
     322           0 :         question_string.append("<hr /><b>");
     323           0 :         question_string.append(tr("Transaction fee"));
     324           0 :         question_string.append("</b>");
     325             : 
     326             :         // append transaction size
     327           0 :         question_string.append(" (" + QString::number((double)m_current_transaction->getTransactionSize() / 1000) + " kB): ");
     328             : 
     329             :         // append transaction fee value
     330           0 :         question_string.append("<span style='color:#aa0000; font-weight:bold;'>");
     331           0 :         question_string.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
     332           0 :         question_string.append("</span><br />");
     333             : 
     334             :         // append RBF message according to transaction's signalling
     335           0 :         question_string.append("<span style='font-size:10pt; font-weight:normal;'>");
     336           0 :         if (ui->optInRBF->isChecked()) {
     337           0 :             question_string.append(tr("You can increase the fee later (signals Replace-By-Fee, BIP-125)."));
     338           0 :         } else {
     339           0 :             question_string.append(tr("Not signalling Replace-By-Fee, BIP-125."));
     340             :         }
     341           0 :         question_string.append("</span>");
     342             :     }
     343             : 
     344             :     // add total amount in all subdivision units
     345           0 :     question_string.append("<hr />");
     346           0 :     CAmount totalAmount = m_current_transaction->getTotalTransactionAmount() + txFee;
     347           0 :     QStringList alternativeUnits;
     348           0 :     for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits())
     349             :     {
     350           0 :         if(u != model->getOptionsModel()->getDisplayUnit())
     351           0 :             alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
     352           0 :     }
     353           0 :     question_string.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
     354           0 :         .arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount)));
     355           0 :     question_string.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
     356           0 :         .arg(alternativeUnits.join(" " + tr("or") + " ")));
     357             : 
     358           0 :     if (formatted.size() > 1) {
     359           0 :         question_string = question_string.arg("");
     360           0 :         informative_text = tr("To review recipient list click \"Show Details...\"");
     361           0 :         detailed_text = formatted.join("\n\n");
     362           0 :     } else {
     363           0 :         question_string = question_string.arg("<br /><br />" + formatted.at(0));
     364             :     }
     365             : 
     366             :     return true;
     367           0 : }
     368             : 
     369           0 : void SendCoinsDialog::on_sendButton_clicked()
     370             : {
     371           0 :     if(!model || !model->getOptionsModel())
     372             :         return;
     373             : 
     374           0 :     QString question_string, informative_text, detailed_text;
     375           0 :     if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
     376           0 :     assert(m_current_transaction);
     377             : 
     378           0 :     const QString confirmation = model->wallet().privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
     379           0 :     const QString confirmButtonText = model->wallet().privateKeysDisabled() ? tr("Create Unsigned") : tr("Send");
     380           0 :     SendConfirmationDialog confirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
     381           0 :     confirmationDialog.exec();
     382           0 :     QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
     383             : 
     384           0 :     if(retval != QMessageBox::Yes)
     385             :     {
     386           0 :         fNewRecipientAllowed = true;
     387           0 :         return;
     388             :     }
     389             : 
     390           0 :     bool send_failure = false;
     391           0 :     if (model->wallet().privateKeysDisabled()) {
     392           0 :         CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
     393           0 :         PartiallySignedTransaction psbtx(mtx);
     394           0 :         bool complete = false;
     395           0 :         const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
     396           0 :         assert(!complete);
     397           0 :         assert(err == TransactionError::OK);
     398             :         // Serialize the PSBT
     399           0 :         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
     400           0 :         ssTx << psbtx;
     401           0 :         GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
     402           0 :         QMessageBox msgBox;
     403           0 :         msgBox.setText("Unsigned Transaction");
     404           0 :         msgBox.setInformativeText("The PSBT has been copied to the clipboard. You can also save it.");
     405           0 :         msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard);
     406           0 :         msgBox.setDefaultButton(QMessageBox::Discard);
     407           0 :         switch (msgBox.exec()) {
     408             :         case QMessageBox::Save: {
     409           0 :             QString selectedFilter;
     410           0 :             QString fileNameSuggestion = "";
     411             :             bool first = true;
     412           0 :             for (const SendCoinsRecipient &rcp : m_current_transaction->getRecipients()) {
     413           0 :                 if (!first) {
     414           0 :                     fileNameSuggestion.append(" - ");
     415             :                 }
     416           0 :                 QString labelOrAddress = rcp.label.isEmpty() ? rcp.address : rcp.label;
     417           0 :                 QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
     418           0 :                 fileNameSuggestion.append(labelOrAddress + "-" + amount);
     419             :                 first = false;
     420           0 :             }
     421           0 :             fileNameSuggestion.append(".psbt");
     422           0 :             QString filename = GUIUtil::getSaveFileName(this,
     423           0 :                 tr("Save Transaction Data"), fileNameSuggestion,
     424           0 :                 tr("Partially Signed Transaction (Binary) (*.psbt)"), &selectedFilter);
     425           0 :             if (filename.isEmpty()) {
     426           0 :                 return;
     427             :             }
     428           0 :             std::ofstream out(filename.toLocal8Bit().data());
     429           0 :             out << ssTx.str();
     430           0 :             out.close();
     431           0 :             Q_EMIT message(tr("PSBT saved"), "PSBT saved to disk", CClientUIInterface::MSG_INFORMATION);
     432             :             break;
     433           0 :         }
     434             :         case QMessageBox::Discard:
     435             :             break;
     436             :         default:
     437           0 :             assert(false);
     438             :         }
     439           0 :     } else {
     440             :         // now send the prepared transaction
     441           0 :         WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
     442             :         // process sendStatus and on error generate message shown to user
     443           0 :         processSendCoinsReturn(sendStatus);
     444             : 
     445           0 :         if (sendStatus.status == WalletModel::OK) {
     446           0 :             Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
     447             :         } else {
     448             :             send_failure = true;
     449             :         }
     450           0 :     }
     451           0 :     if (!send_failure) {
     452           0 :         accept();
     453           0 :         m_coin_control->UnSelectAll();
     454           0 :         coinControlUpdateLabels();
     455             :     }
     456           0 :     fNewRecipientAllowed = true;
     457           0 :     m_current_transaction.reset();
     458           0 : }
     459             : 
     460           0 : void SendCoinsDialog::clear()
     461             : {
     462           0 :     m_current_transaction.reset();
     463             : 
     464             :     // Clear coin control settings
     465           0 :     m_coin_control->UnSelectAll();
     466           0 :     ui->checkBoxCoinControlChange->setChecked(false);
     467           0 :     ui->lineEditCoinControlChange->clear();
     468           0 :     coinControlUpdateLabels();
     469             : 
     470             :     // Remove entries until only one left
     471           0 :     while(ui->entries->count())
     472             :     {
     473           0 :         ui->entries->takeAt(0)->widget()->deleteLater();
     474             :     }
     475           0 :     addEntry();
     476             : 
     477           0 :     updateTabsAndLabels();
     478           0 : }
     479             : 
     480           0 : void SendCoinsDialog::reject()
     481             : {
     482           0 :     clear();
     483           0 : }
     484             : 
     485           0 : void SendCoinsDialog::accept()
     486             : {
     487           0 :     clear();
     488           0 : }
     489             : 
     490           0 : SendCoinsEntry *SendCoinsDialog::addEntry()
     491             : {
     492           0 :     SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
     493           0 :     entry->setModel(model);
     494           0 :     ui->entries->addWidget(entry);
     495           0 :     connect(entry, &SendCoinsEntry::removeEntry, this, &SendCoinsDialog::removeEntry);
     496           0 :     connect(entry, &SendCoinsEntry::useAvailableBalance, this, &SendCoinsDialog::useAvailableBalance);
     497           0 :     connect(entry, &SendCoinsEntry::payAmountChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
     498           0 :     connect(entry, &SendCoinsEntry::subtractFeeFromAmountChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
     499             : 
     500             :     // Focus the field, so that entry can start immediately
     501           0 :     entry->clear();
     502           0 :     entry->setFocus();
     503           0 :     ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
     504           0 :     qApp->processEvents();
     505           0 :     QScrollBar* bar = ui->scrollArea->verticalScrollBar();
     506           0 :     if(bar)
     507           0 :         bar->setSliderPosition(bar->maximum());
     508             : 
     509           0 :     updateTabsAndLabels();
     510           0 :     return entry;
     511           0 : }
     512             : 
     513           0 : void SendCoinsDialog::updateTabsAndLabels()
     514             : {
     515           0 :     setupTabChain(nullptr);
     516           0 :     coinControlUpdateLabels();
     517           0 : }
     518             : 
     519           0 : void SendCoinsDialog::removeEntry(SendCoinsEntry* entry)
     520             : {
     521           0 :     entry->hide();
     522             : 
     523             :     // If the last entry is about to be removed add an empty one
     524           0 :     if (ui->entries->count() == 1)
     525           0 :         addEntry();
     526             : 
     527           0 :     entry->deleteLater();
     528             : 
     529           0 :     updateTabsAndLabels();
     530           0 : }
     531             : 
     532           0 : QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
     533             : {
     534           0 :     for(int i = 0; i < ui->entries->count(); ++i)
     535             :     {
     536           0 :         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
     537           0 :         if(entry)
     538             :         {
     539           0 :             prev = entry->setupTabChain(prev);
     540           0 :         }
     541             :     }
     542           0 :     QWidget::setTabOrder(prev, ui->sendButton);
     543           0 :     QWidget::setTabOrder(ui->sendButton, ui->clearButton);
     544           0 :     QWidget::setTabOrder(ui->clearButton, ui->addButton);
     545           0 :     return ui->addButton;
     546             : }
     547             : 
     548           0 : void SendCoinsDialog::setAddress(const QString &address)
     549             : {
     550             :     SendCoinsEntry *entry = nullptr;
     551             :     // Replace the first entry if it is still unused
     552           0 :     if(ui->entries->count() == 1)
     553             :     {
     554           0 :         SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
     555           0 :         if(first->isClear())
     556             :         {
     557             :             entry = first;
     558           0 :         }
     559           0 :     }
     560           0 :     if(!entry)
     561             :     {
     562           0 :         entry = addEntry();
     563           0 :     }
     564             : 
     565           0 :     entry->setAddress(address);
     566           0 : }
     567             : 
     568           0 : void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv)
     569             : {
     570           0 :     if(!fNewRecipientAllowed)
     571             :         return;
     572             : 
     573             :     SendCoinsEntry *entry = nullptr;
     574             :     // Replace the first entry if it is still unused
     575           0 :     if(ui->entries->count() == 1)
     576             :     {
     577           0 :         SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
     578           0 :         if(first->isClear())
     579             :         {
     580             :             entry = first;
     581           0 :         }
     582           0 :     }
     583           0 :     if(!entry)
     584             :     {
     585           0 :         entry = addEntry();
     586           0 :     }
     587             : 
     588           0 :     entry->setValue(rv);
     589           0 :     updateTabsAndLabels();
     590           0 : }
     591             : 
     592           0 : bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv)
     593             : {
     594             :     // Just paste the entry, all pre-checks
     595             :     // are done in paymentserver.cpp.
     596           0 :     pasteEntry(rv);
     597           0 :     return true;
     598             : }
     599             : 
     600           0 : void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances)
     601             : {
     602           0 :     if(model && model->getOptionsModel())
     603             :     {
     604           0 :         CAmount balance = balances.balance;
     605           0 :         if (model->wallet().privateKeysDisabled()) {
     606           0 :             balance = balances.watch_only_balance;
     607           0 :             ui->labelBalanceName->setText(tr("Watch-only balance:"));
     608           0 :         }
     609           0 :         ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
     610           0 :     }
     611           0 : }
     612             : 
     613           0 : void SendCoinsDialog::updateDisplayUnit()
     614             : {
     615           0 :     setBalance(model->wallet().getBalances());
     616           0 :     ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
     617           0 :     updateSmartFeeLabel();
     618           0 : }
     619             : 
     620           0 : void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
     621             : {
     622           0 :     QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
     623             :     // Default to a warning message, override if error message is needed
     624           0 :     msgParams.second = CClientUIInterface::MSG_WARNING;
     625             : 
     626             :     // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
     627             :     // All status values are used only in WalletModel::prepareTransaction()
     628           0 :     switch(sendCoinsReturn.status)
     629             :     {
     630             :     case WalletModel::InvalidAddress:
     631           0 :         msgParams.first = tr("The recipient address is not valid. Please recheck.");
     632           0 :         break;
     633             :     case WalletModel::InvalidAmount:
     634           0 :         msgParams.first = tr("The amount to pay must be larger than 0.");
     635           0 :         break;
     636             :     case WalletModel::AmountExceedsBalance:
     637           0 :         msgParams.first = tr("The amount exceeds your balance.");
     638           0 :         break;
     639             :     case WalletModel::AmountWithFeeExceedsBalance:
     640           0 :         msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
     641           0 :         break;
     642             :     case WalletModel::DuplicateAddress:
     643           0 :         msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
     644           0 :         break;
     645             :     case WalletModel::TransactionCreationFailed:
     646           0 :         msgParams.first = tr("Transaction creation failed!");
     647           0 :         msgParams.second = CClientUIInterface::MSG_ERROR;
     648           0 :         break;
     649             :     case WalletModel::AbsurdFee:
     650           0 :         msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getDefaultMaxTxFee()));
     651           0 :         break;
     652             :     case WalletModel::PaymentRequestExpired:
     653           0 :         msgParams.first = tr("Payment request expired.");
     654           0 :         msgParams.second = CClientUIInterface::MSG_ERROR;
     655           0 :         break;
     656             :     // included to prevent a compiler warning.
     657             :     case WalletModel::OK:
     658             :     default:
     659           0 :         return;
     660             :     }
     661             : 
     662           0 :     Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
     663           0 : }
     664             : 
     665           0 : void SendCoinsDialog::minimizeFeeSection(bool fMinimize)
     666             : {
     667           0 :     ui->labelFeeMinimized->setVisible(fMinimize);
     668           0 :     ui->buttonChooseFee  ->setVisible(fMinimize);
     669           0 :     ui->buttonMinimizeFee->setVisible(!fMinimize);
     670           0 :     ui->frameFeeSelection->setVisible(!fMinimize);
     671           0 :     ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
     672           0 :     fFeeMinimized = fMinimize;
     673           0 : }
     674             : 
     675           0 : void SendCoinsDialog::on_buttonChooseFee_clicked()
     676             : {
     677           0 :     minimizeFeeSection(false);
     678           0 : }
     679             : 
     680           0 : void SendCoinsDialog::on_buttonMinimizeFee_clicked()
     681             : {
     682           0 :     updateFeeMinimizedLabel();
     683           0 :     minimizeFeeSection(true);
     684           0 : }
     685             : 
     686           0 : void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
     687             : {
     688             :     // Include watch-only for wallets without private key
     689           0 :     m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled();
     690             : 
     691             :     // Calculate available amount to send.
     692           0 :     CAmount amount = model->wallet().getAvailableBalance(*m_coin_control);
     693           0 :     for (int i = 0; i < ui->entries->count(); ++i) {
     694           0 :         SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
     695           0 :         if (e && !e->isHidden() && e != entry) {
     696           0 :             amount -= e->getValue().amount;
     697           0 :         }
     698             :     }
     699             : 
     700           0 :     if (amount > 0) {
     701           0 :       entry->checkSubtractFeeFromAmount();
     702           0 :       entry->setAmount(amount);
     703           0 :     } else {
     704           0 :       entry->setAmount(0);
     705             :     }
     706           0 : }
     707             : 
     708           0 : void SendCoinsDialog::updateFeeSectionControls()
     709             : {
     710           0 :     ui->confTargetSelector      ->setEnabled(ui->radioSmartFee->isChecked());
     711           0 :     ui->labelSmartFee           ->setEnabled(ui->radioSmartFee->isChecked());
     712           0 :     ui->labelSmartFee2          ->setEnabled(ui->radioSmartFee->isChecked());
     713           0 :     ui->labelSmartFee3          ->setEnabled(ui->radioSmartFee->isChecked());
     714           0 :     ui->labelFeeEstimation      ->setEnabled(ui->radioSmartFee->isChecked());
     715           0 :     ui->labelCustomFeeWarning   ->setEnabled(ui->radioCustomFee->isChecked());
     716           0 :     ui->labelCustomPerKilobyte  ->setEnabled(ui->radioCustomFee->isChecked());
     717           0 :     ui->customFee               ->setEnabled(ui->radioCustomFee->isChecked());
     718           0 : }
     719             : 
     720           0 : void SendCoinsDialog::updateFeeMinimizedLabel()
     721             : {
     722           0 :     if(!model || !model->getOptionsModel())
     723             :         return;
     724             : 
     725           0 :     if (ui->radioSmartFee->isChecked())
     726           0 :         ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
     727             :     else {
     728           0 :         ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + "/kB");
     729             :     }
     730           0 : }
     731             : 
     732           0 : void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
     733             : {
     734           0 :     if (ui->radioCustomFee->isChecked()) {
     735           0 :         ctrl.m_feerate = CFeeRate(ui->customFee->value());
     736           0 :     } else {
     737           0 :         ctrl.m_feerate.reset();
     738             :     }
     739             :     // Avoid using global defaults when sending money from the GUI
     740             :     // Either custom fee will be used or if not selected, the confirmation target from dropdown box
     741           0 :     ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
     742           0 :     ctrl.m_signal_bip125_rbf = ui->optInRBF->isChecked();
     743             :     // Include watch-only for wallets without private key
     744           0 :     ctrl.fAllowWatchOnly = model->wallet().privateKeysDisabled();
     745           0 : }
     746             : 
     747           0 : void SendCoinsDialog::updateSmartFeeLabel()
     748             : {
     749           0 :     if(!model || !model->getOptionsModel())
     750             :         return;
     751           0 :     updateCoinControlState(*m_coin_control);
     752           0 :     m_coin_control->m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
     753           0 :     int returned_target;
     754           0 :     FeeReason reason;
     755           0 :     CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, *m_coin_control, &returned_target, &reason));
     756             : 
     757           0 :     ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
     758             : 
     759           0 :     if (reason == FeeReason::FALLBACK) {
     760           0 :         ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
     761           0 :         ui->labelFeeEstimation->setText("");
     762           0 :         ui->fallbackFeeWarningLabel->setVisible(true);
     763           0 :         int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
     764           0 :         QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
     765           0 :         ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }");
     766           0 :         ui->fallbackFeeWarningLabel->setIndent(GUIUtil::TextWidth(QFontMetrics(ui->fallbackFeeWarningLabel->font()), "x"));
     767           0 :     }
     768             :     else
     769             :     {
     770           0 :         ui->labelSmartFee2->hide();
     771           0 :         ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", returned_target));
     772           0 :         ui->fallbackFeeWarningLabel->setVisible(false);
     773             :     }
     774             : 
     775           0 :     updateFeeMinimizedLabel();
     776           0 : }
     777             : 
     778             : // Coin Control: copy label "Quantity" to clipboard
     779           0 : void SendCoinsDialog::coinControlClipboardQuantity()
     780             : {
     781           0 :     GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
     782           0 : }
     783             : 
     784             : // Coin Control: copy label "Amount" to clipboard
     785           0 : void SendCoinsDialog::coinControlClipboardAmount()
     786             : {
     787           0 :     GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
     788           0 : }
     789             : 
     790             : // Coin Control: copy label "Fee" to clipboard
     791           0 : void SendCoinsDialog::coinControlClipboardFee()
     792             : {
     793           0 :     GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
     794           0 : }
     795             : 
     796             : // Coin Control: copy label "After fee" to clipboard
     797           0 : void SendCoinsDialog::coinControlClipboardAfterFee()
     798             : {
     799           0 :     GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
     800           0 : }
     801             : 
     802             : // Coin Control: copy label "Bytes" to clipboard
     803           0 : void SendCoinsDialog::coinControlClipboardBytes()
     804             : {
     805           0 :     GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
     806           0 : }
     807             : 
     808             : // Coin Control: copy label "Dust" to clipboard
     809           0 : void SendCoinsDialog::coinControlClipboardLowOutput()
     810             : {
     811           0 :     GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
     812           0 : }
     813             : 
     814             : // Coin Control: copy label "Change" to clipboard
     815           0 : void SendCoinsDialog::coinControlClipboardChange()
     816             : {
     817           0 :     GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
     818           0 : }
     819             : 
     820             : // Coin Control: settings menu - coin control enabled/disabled by user
     821           0 : void SendCoinsDialog::coinControlFeatureChanged(bool checked)
     822             : {
     823           0 :     ui->frameCoinControl->setVisible(checked);
     824             : 
     825           0 :     if (!checked && model) // coin control features disabled
     826           0 :         m_coin_control->SetNull();
     827             : 
     828           0 :     coinControlUpdateLabels();
     829           0 : }
     830             : 
     831             : // Coin Control: button inputs -> show actual coin control dialog
     832           0 : void SendCoinsDialog::coinControlButtonClicked()
     833             : {
     834           0 :     CoinControlDialog dlg(*m_coin_control, model, platformStyle);
     835           0 :     dlg.exec();
     836           0 :     coinControlUpdateLabels();
     837           0 : }
     838             : 
     839             : // Coin Control: checkbox custom change address
     840           0 : void SendCoinsDialog::coinControlChangeChecked(int state)
     841             : {
     842           0 :     if (state == Qt::Unchecked)
     843             :     {
     844           0 :         m_coin_control->destChange = CNoDestination();
     845           0 :         ui->labelCoinControlChangeLabel->clear();
     846           0 :     }
     847             :     else
     848             :         // use this to re-validate an already entered address
     849           0 :         coinControlChangeEdited(ui->lineEditCoinControlChange->text());
     850             : 
     851           0 :     ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
     852           0 : }
     853             : 
     854             : // Coin Control: custom change address changed
     855           0 : void SendCoinsDialog::coinControlChangeEdited(const QString& text)
     856             : {
     857           0 :     if (model && model->getAddressTableModel())
     858             :     {
     859             :         // Default to no change address until verified
     860           0 :         m_coin_control->destChange = CNoDestination();
     861           0 :         ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
     862             : 
     863           0 :         const CTxDestination dest = DecodeDestination(text.toStdString());
     864             : 
     865           0 :         if (text.isEmpty()) // Nothing entered
     866             :         {
     867           0 :             ui->labelCoinControlChangeLabel->setText("");
     868           0 :         }
     869           0 :         else if (!IsValidDestination(dest)) // Invalid address
     870             :         {
     871           0 :             ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
     872           0 :         }
     873             :         else // Valid address
     874             :         {
     875           0 :             if (!model->wallet().isSpendable(dest)) {
     876           0 :                 ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
     877             : 
     878             :                 // confirmation dialog
     879           0 :                 QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm custom change address"), tr("The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?"),
     880           0 :                     QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
     881             : 
     882           0 :                 if(btnRetVal == QMessageBox::Yes)
     883           0 :                     m_coin_control->destChange = dest;
     884             :                 else
     885             :                 {
     886           0 :                     ui->lineEditCoinControlChange->setText("");
     887           0 :                     ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
     888           0 :                     ui->labelCoinControlChangeLabel->setText("");
     889             :                 }
     890           0 :             }
     891             :             else // Known change address
     892             :             {
     893           0 :                 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
     894             : 
     895             :                 // Query label
     896           0 :                 QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
     897           0 :                 if (!associatedLabel.isEmpty())
     898           0 :                     ui->labelCoinControlChangeLabel->setText(associatedLabel);
     899             :                 else
     900           0 :                     ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
     901             : 
     902           0 :                 m_coin_control->destChange = dest;
     903           0 :             }
     904             :         }
     905           0 :     }
     906           0 : }
     907             : 
     908             : // Coin Control: update labels
     909           0 : void SendCoinsDialog::coinControlUpdateLabels()
     910             : {
     911           0 :     if (!model || !model->getOptionsModel())
     912             :         return;
     913             : 
     914           0 :     updateCoinControlState(*m_coin_control);
     915             : 
     916             :     // set pay amounts
     917           0 :     CoinControlDialog::payAmounts.clear();
     918           0 :     CoinControlDialog::fSubtractFeeFromAmount = false;
     919             : 
     920           0 :     for(int i = 0; i < ui->entries->count(); ++i)
     921             :     {
     922           0 :         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
     923           0 :         if(entry && !entry->isHidden())
     924             :         {
     925           0 :             SendCoinsRecipient rcp = entry->getValue();
     926           0 :             CoinControlDialog::payAmounts.append(rcp.amount);
     927           0 :             if (rcp.fSubtractFeeFromAmount)
     928           0 :                 CoinControlDialog::fSubtractFeeFromAmount = true;
     929           0 :         }
     930             :     }
     931             : 
     932           0 :     if (m_coin_control->HasSelected())
     933             :     {
     934             :         // actual coin control calculation
     935           0 :         CoinControlDialog::updateLabels(*m_coin_control, model, this);
     936             : 
     937             :         // show coin control stats
     938           0 :         ui->labelCoinControlAutomaticallySelected->hide();
     939           0 :         ui->widgetCoinControl->show();
     940           0 :     }
     941             :     else
     942             :     {
     943             :         // hide coin control stats
     944           0 :         ui->labelCoinControlAutomaticallySelected->show();
     945           0 :         ui->widgetCoinControl->hide();
     946           0 :         ui->labelCoinControlInsuffFunds->hide();
     947             :     }
     948           0 : }
     949             : 
     950           0 : SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, const QString& _confirmButtonText, QWidget* parent)
     951           0 :     : QMessageBox(parent), secDelay(_secDelay), confirmButtonText(_confirmButtonText)
     952           0 : {
     953           0 :     setIcon(QMessageBox::Question);
     954           0 :     setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
     955           0 :     setText(text);
     956           0 :     setInformativeText(informative_text);
     957           0 :     setDetailedText(detailed_text);
     958           0 :     setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
     959           0 :     setDefaultButton(QMessageBox::Cancel);
     960           0 :     yesButton = button(QMessageBox::Yes);
     961           0 :     updateYesButton();
     962           0 :     connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown);
     963           0 : }
     964             : 
     965           0 : int SendConfirmationDialog::exec()
     966             : {
     967           0 :     updateYesButton();
     968           0 :     countDownTimer.start(1000);
     969           0 :     return QMessageBox::exec();
     970             : }
     971             : 
     972           0 : void SendConfirmationDialog::countDown()
     973             : {
     974           0 :     secDelay--;
     975           0 :     updateYesButton();
     976             : 
     977           0 :     if(secDelay <= 0)
     978             :     {
     979           0 :         countDownTimer.stop();
     980           0 :     }
     981           0 : }
     982             : 
     983           0 : void SendConfirmationDialog::updateYesButton()
     984             : {
     985           0 :     if(secDelay > 0)
     986             :     {
     987           0 :         yesButton->setEnabled(false);
     988           0 :         yesButton->setText(confirmButtonText + " (" + QString::number(secDelay) + ")");
     989           0 :     }
     990             :     else
     991             :     {
     992           0 :         yesButton->setEnabled(true);
     993           0 :         yesButton->setText(confirmButtonText);
     994             :     }
     995           0 : }

Generated by: LCOV version 1.15