Dogecoin Core  1.14.2
P2P Digital Currency
sendcoinsdialog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2016 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 "chainparams.h"
20 #include "wallet/coincontrol.h"
21 #include "validation.h" // mempool and minRelayTxFee
22 #include "ui_interface.h"
23 #include "txmempool.h"
24 #include "wallet/wallet.h"
25 
26 #include <QFontMetrics>
27 #include <QMessageBox>
28 #include <QScrollBar>
29 #include <QSettings>
30 #include <QTextDocument>
31 #include <QTimer>
32 
33 #define SEND_CONFIRM_DELAY 3
34 
35 SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
36  QDialog(parent),
37  ui(new Ui::SendCoinsDialog),
38  clientModel(0),
39  model(0),
40  fNewRecipientAllowed(true),
41  fFeeMinimized(true),
42  platformStyle(_platformStyle)
43 {
44  ui->setupUi(this);
45 
46  if (!_platformStyle->getImagesOnButtons()) {
47  ui->addButton->setIcon(QIcon());
48  ui->clearButton->setIcon(QIcon());
49  ui->sendButton->setIcon(QIcon());
50  } else {
51  ui->addButton->setIcon(_platformStyle->SingleColorIcon(":/icons/add"));
52  ui->clearButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
53  ui->sendButton->setIcon(_platformStyle->SingleColorIcon(":/icons/send"));
54  }
55 
56  GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
57 
58  addEntry();
59 
60  connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
61  connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
62 
63  // Coin Control
64  connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
65  connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
66  connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &)));
67 
68  // Coin Control: clipboard actions
69  QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
70  QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
71  QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
72  QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
73  QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
74  QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
75  QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
76  connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity()));
77  connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount()));
78  connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee()));
79  connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee()));
80  connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes()));
81  connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput()));
82  connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange()));
83  ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
84  ui->labelCoinControlAmount->addAction(clipboardAmountAction);
85  ui->labelCoinControlFee->addAction(clipboardFeeAction);
86  ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
87  ui->labelCoinControlBytes->addAction(clipboardBytesAction);
88  ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
89  ui->labelCoinControlChange->addAction(clipboardChangeAction);
90 
91  // init transaction fee section
92  QSettings settings;
93  if (!settings.contains("fFeeSectionMinimized"))
94  settings.setValue("fFeeSectionMinimized", true);
95  if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
96  settings.setValue("nFeeRadio", 1); // custom
97  if (!settings.contains("nFeeRadio"))
98  settings.setValue("nFeeRadio", 0); // recommended
99  if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
100  settings.setValue("nCustomFeeRadio", 1); // total at least
101  if (!settings.contains("nCustomFeeRadio"))
102  settings.setValue("nCustomFeeRadio", 0); // per kilobyte
103  if (!settings.contains("nSmartFeeSliderPosition"))
104  settings.setValue("nSmartFeeSliderPosition", 0);
105  if (!settings.contains("nTransactionFee"))
106  settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
107  if (!settings.contains("fPayOnlyMinFee"))
108  settings.setValue("fPayOnlyMinFee", false);
109  ui->groupFee->setId(ui->radioSmartFee, 0);
110  ui->groupFee->setId(ui->radioCustomFee, 1);
111  ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
112  ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0);
113  ui->groupCustomFee->setId(ui->radioCustomAtLeast, 1);
114  ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
115  ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
116  ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
117  minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
118 }
119 
121 {
122  this->clientModel = _clientModel;
123 
124  if (_clientModel) {
125  connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(updateSmartFeeLabel()));
126  }
127 }
128 
130 {
131  this->model = _model;
132 
133  if(_model && _model->getOptionsModel())
134  {
135  for(int i = 0; i < ui->entries->count(); ++i)
136  {
137  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
138  if(entry)
139  {
140  entry->setModel(_model);
141  }
142  }
143 
144  setBalance(_model->getBalance(), _model->getUnconfirmedBalance(), _model->getImmatureBalance(),
145  _model->getWatchBalance(), _model->getWatchUnconfirmedBalance(), _model->getWatchImmatureBalance());
146  connect(_model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)));
147  connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
149 
150  // Coin Control
151  connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
152  connect(_model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
153  ui->frameCoinControl->setVisible(_model->getOptionsModel()->getCoinControlFeatures());
155 
156  // fee section
157  connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateSmartFeeLabel()));
158  connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateGlobalFeeVariables()));
159  connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(coinControlUpdateLabels()));
160  connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
161  connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
162  connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
163  connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
164  connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
165  connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(updateGlobalFeeVariables()));
166  connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels()));
167  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee()));
168  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
169  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
170  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
171  ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000));
176 
177  // set the smartfee-sliders default value (wallets default conf.target or last stored value)
178  QSettings settings;
179  if (settings.value("nSmartFeeSliderPosition").toInt() == 0)
180  ui->sliderSmartFee->setValue(ui->sliderSmartFee->maximum() - model->getDefaultConfirmTarget() + 2);
181  else
182  ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
183  }
184 }
185 
187 {
188  QSettings settings;
189  settings.setValue("fFeeSectionMinimized", fFeeMinimized);
190  settings.setValue("nFeeRadio", ui->groupFee->checkedId());
191  settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId());
192  settings.setValue("nSmartFeeSliderPosition", ui->sliderSmartFee->value());
193  settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
194  settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
195 
196  delete ui;
197 }
198 
200 {
201  if(!model || !model->getOptionsModel())
202  return;
203 
204  QList<SendCoinsRecipient> recipients;
205  bool valid = true;
206 
207  for(int i = 0; i < ui->entries->count(); ++i)
208  {
209  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
210  if(entry)
211  {
212  if(entry->validate())
213  {
214  recipients.append(entry->getValue());
215  }
216  else
217  {
218  valid = false;
219  }
220  }
221  }
222 
223  if(!valid || recipients.isEmpty())
224  {
225  return;
226  }
227 
228  fNewRecipientAllowed = false;
230  if(!ctx.isValid())
231  {
232  // Unlock wallet was cancelled
233  fNewRecipientAllowed = true;
234  return;
235  }
236 
237  // prepare transaction for getting txFee earlier
238  WalletModelTransaction currentTransaction(recipients);
239  WalletModel::SendCoinsReturn prepareStatus;
240 
241  // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled
242  CCoinControl ctrl;
245  if (ui->radioSmartFee->isChecked())
246  ctrl.nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
247  else
248  ctrl.nConfirmTarget = 0;
249 
250  prepareStatus = model->prepareTransaction(currentTransaction, &ctrl);
251 
252  // process prepareStatus and on error generate message shown to user
253  processSendCoinsReturn(prepareStatus,
255 
256  if(prepareStatus.status != WalletModel::OK) {
257  fNewRecipientAllowed = true;
258  return;
259  }
260 
261  CAmount txFee = currentTransaction.getTransactionFee();
262 
263  // Format confirmation message
264  QStringList formatted;
265  Q_FOREACH(const SendCoinsRecipient &rcp, currentTransaction.getRecipients())
266  {
267  // generate bold amount string
268  QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
269  amount.append("</b>");
270  // generate monospace address string
271  QString address = "<span style='font-family: monospace;'>" + rcp.address;
272  address.append("</span>");
273 
274  QString recipientElement;
275 
276  if (!rcp.paymentRequest.IsInitialized()) // normal payment
277  {
278  if(rcp.label.length() > 0) // label with address
279  {
280  recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label));
281  recipientElement.append(QString(" (%1)").arg(address));
282  }
283  else // just address
284  {
285  recipientElement = tr("%1 to %2").arg(amount, address);
286  }
287  }
288  else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request
289  {
290  recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant));
291  }
292  else // unauthenticated payment request
293  {
294  recipientElement = tr("%1 to %2").arg(amount, address);
295  }
296 
297  formatted.append(recipientElement);
298  }
299 
300  QString questionString = tr("Are you sure you want to send?");
301  questionString.append("<br /><br />%1");
302 
303  if(txFee > 0)
304  {
305  // append fee string if a fee is required
306  questionString.append("<hr /><span style='color:#aa0000;'>");
307  questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
308  questionString.append("</span> ");
309  questionString.append(tr("added as transaction fee"));
310 
311  // append transaction size
312  questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)");
313  }
314 
315  // add total amount in all subdivision units
316  questionString.append("<hr />");
317  CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
318  QStringList alternativeUnits;
320  {
321  if(u != model->getOptionsModel()->getDisplayUnit())
322  alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
323  }
324  questionString.append(tr("Total Amount %1")
326  questionString.append(QString("<span style='font-size:10pt;font-weight:normal;'><br />(=%2)</span>")
327  .arg(alternativeUnits.join(" " + tr("or") + "<br />")));
328 
329  SendConfirmationDialog confirmationDialog(tr("Confirm send coins"),
330  questionString.arg(formatted.join("<br />")), SEND_CONFIRM_DELAY, this);
331  confirmationDialog.exec();
332  QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result();
333 
334  if(retval != QMessageBox::Yes)
335  {
336  fNewRecipientAllowed = true;
337  return;
338  }
339 
340  // now send the prepared transaction
341  WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
342  // process sendStatus and on error generate message shown to user
343  processSendCoinsReturn(sendStatus);
344 
345  if (sendStatus.status == WalletModel::OK)
346  {
347  accept();
350  }
351  fNewRecipientAllowed = true;
352 }
353 
355 {
356  // Remove entries until only one left
357  while(ui->entries->count())
358  {
359  ui->entries->takeAt(0)->widget()->deleteLater();
360  }
361  addEntry();
362 
364 }
365 
367 {
368  clear();
369 }
370 
372 {
373  clear();
374 }
375 
377 {
378  SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
379  entry->setModel(model);
380  ui->entries->addWidget(entry);
381  connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
382  connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels()));
383  connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this, SLOT(coinControlUpdateLabels()));
384 
385  // Focus the field, so that entry can start immediately
386  entry->clear();
387  entry->setFocus();
388  ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
389  qApp->processEvents();
390  QScrollBar* bar = ui->scrollArea->verticalScrollBar();
391  if(bar)
392  bar->setSliderPosition(bar->maximum());
393 
395  return entry;
396 }
397 
399 {
400  setupTabChain(0);
402 }
403 
405 {
406  entry->hide();
407 
408  // If the last entry is about to be removed add an empty one
409  if (ui->entries->count() == 1)
410  addEntry();
411 
412  entry->deleteLater();
413 
415 }
416 
417 QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
418 {
419  for(int i = 0; i < ui->entries->count(); ++i)
420  {
421  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
422  if(entry)
423  {
424  prev = entry->setupTabChain(prev);
425  }
426  }
427  QWidget::setTabOrder(prev, ui->sendButton);
428  QWidget::setTabOrder(ui->sendButton, ui->clearButton);
429  QWidget::setTabOrder(ui->clearButton, ui->addButton);
430  return ui->addButton;
431 }
432 
433 void SendCoinsDialog::setAddress(const QString &address)
434 {
435  SendCoinsEntry *entry = 0;
436  // Replace the first entry if it is still unused
437  if(ui->entries->count() == 1)
438  {
439  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
440  if(first->isClear())
441  {
442  entry = first;
443  }
444  }
445  if(!entry)
446  {
447  entry = addEntry();
448  }
449 
450  entry->setAddress(address);
451 }
452 
454 {
456  return;
457 
458  SendCoinsEntry *entry = 0;
459  // Replace the first entry if it is still unused
460  if(ui->entries->count() == 1)
461  {
462  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
463  if(first->isClear())
464  {
465  entry = first;
466  }
467  }
468  if(!entry)
469  {
470  entry = addEntry();
471  }
472 
473  entry->setValue(rv);
475 }
476 
478 {
479  // Just paste the entry, all pre-checks
480  // are done in paymentserver.cpp.
481  pasteEntry(rv);
482  return true;
483 }
484 
485 void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
486  const CAmount& watchBalance, const CAmount& watchUnconfirmedBalance, const CAmount& watchImmatureBalance)
487 {
488  Q_UNUSED(unconfirmedBalance);
489  Q_UNUSED(immatureBalance);
490  Q_UNUSED(watchBalance);
491  Q_UNUSED(watchUnconfirmedBalance);
492  Q_UNUSED(watchImmatureBalance);
493 
494  if(model && model->getOptionsModel())
495  {
496  ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
497  }
498 }
499 
501 {
502  setBalance(model->getBalance(), 0, 0, 0, 0, 0);
503  ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
506 }
507 
508 void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
509 {
510  QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
511  // Default to a warning message, override if error message is needed
512  msgParams.second = CClientUIInterface::MSG_WARNING;
513 
514  // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
515  // WalletModel::TransactionCommitFailed is used only in WalletModel::sendCoins()
516  // all others are used only in WalletModel::prepareTransaction()
517  switch(sendCoinsReturn.status)
518  {
520  msgParams.first = tr("The recipient address is not valid. Please recheck.");
521  break;
523  msgParams.first = tr("The amount to pay must be larger than 0.");
524  break;
526  msgParams.first = tr("The amount exceeds your balance.");
527  break;
529  msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
530  break;
532  msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
533  break;
535  msgParams.first = tr("Transaction creation failed!");
536  msgParams.second = CClientUIInterface::MSG_ERROR;
537  break;
539  msgParams.first = tr("The transaction was rejected with the following reason: %1").arg(sendCoinsReturn.reasonCommitFailed);
540  msgParams.second = CClientUIInterface::MSG_ERROR;
541  break;
543  msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), maxTxFee));
544  break;
546  msgParams.first = tr("Payment request expired.");
547  msgParams.second = CClientUIInterface::MSG_ERROR;
548  break;
549  // included to prevent a compiler warning.
550  case WalletModel::OK:
551  default:
552  return;
553  }
554 
555  Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
556 }
557 
559 {
560  ui->labelFeeMinimized->setVisible(fMinimize);
561  ui->buttonChooseFee ->setVisible(fMinimize);
562  ui->buttonMinimizeFee->setVisible(!fMinimize);
563  ui->frameFeeSelection->setVisible(!fMinimize);
564  ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
565  fFeeMinimized = fMinimize;
566 }
567 
569 {
570  minimizeFeeSection(false);
571 }
572 
574 {
576  minimizeFeeSection(true);
577 }
578 
580 {
581  ui->radioCustomPerKilobyte->setChecked(true);
582  ui->customFee->setValue(CWallet::GetRequiredFee(1000));
583 }
584 
586 {
587  ui->sliderSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
588  ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
589  ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
590  ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
591  ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
592  ui->labelSmartFeeNormal ->setEnabled(ui->radioSmartFee->isChecked());
593  ui->labelSmartFeeFast ->setEnabled(ui->radioSmartFee->isChecked());
594  ui->confirmationTargetLabel ->setEnabled(ui->radioSmartFee->isChecked());
595  ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
596  ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
597  ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
598  ui->radioCustomAtLeast ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked() && CoinControlDialog::coinControl->HasSelected());
599  ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
600 }
601 
603 {
604  if (ui->radioSmartFee->isChecked())
605  {
606  int nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
607  payTxFee = CFeeRate(0);
608 
609  // set nMinimumTotalFee to 0 to not accidentally pay a custom fee
611 
612  // show the estimated required time for confirmation
613  // Dogecoin: We manually set height well past the last hard fork here
614  ui->confirmationTargetLabel->setText(GUIUtil::formatDurationStr(nConfirmTarget * Params().GetConsensus(400000).nPowTargetSpacing) + " / " + tr("%n block(s)", "", nConfirmTarget));
615  }
616  else
617  {
618  payTxFee = CFeeRate(ui->customFee->value());
619 
620  // if user has selected to set a minimum absolute fee, pass the value to coincontrol
621  // set nMinimumTotalFee to 0 in case of user has selected that the fee is per KB
622  CoinControlDialog::coinControl->nMinimumTotalFee = ui->radioCustomAtLeast->isChecked() ? ui->customFee->value() : 0;
623  }
624 }
625 
627 {
628  if(!model || !model->getOptionsModel())
629  return;
630 
631  if (ui->radioSmartFee->isChecked())
632  ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
633  else {
634  ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) +
635  ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : ""));
636  }
637 }
638 
640 {
641  if (model && model->getOptionsModel())
642  ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg(
644  );
645 }
646 
648 {
649  if(!model || !model->getOptionsModel())
650  return;
651 
652  int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
653  int estimateFoundAtBlocks = nBlocksToConfirm;
654  CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
655  if (feeRate <= CFeeRate(0)) // not enough data => minfee
656  {
658  std::max(CWallet::fallbackFee.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
659  ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
660  ui->labelFeeEstimation->setText("");
661  ui->fallbackFeeWarningLabel->setVisible(true);
662  int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
663  QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
664  ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }");
665  ui->fallbackFeeWarningLabel->setIndent(QFontMetrics(ui->fallbackFeeWarningLabel->font()).width("x"));
666  }
667  else
668  {
670  std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
671  ui->labelSmartFee2->hide();
672  ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks));
673  ui->fallbackFeeWarningLabel->setVisible(false);
674  }
675 
677 }
678 
679 // Coin Control: copy label "Quantity" to clipboard
681 {
682  GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
683 }
684 
685 // Coin Control: copy label "Amount" to clipboard
687 {
688  GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
689 }
690 
691 // Coin Control: copy label "Fee" to clipboard
693 {
694  GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
695 }
696 
697 // Coin Control: copy label "After fee" to clipboard
699 {
700  GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
701 }
702 
703 // Coin Control: copy label "Bytes" to clipboard
705 {
706  GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
707 }
708 
709 // Coin Control: copy label "Dust" to clipboard
711 {
712  GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
713 }
714 
715 // Coin Control: copy label "Change" to clipboard
717 {
718  GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
719 }
720 
721 // Coin Control: settings menu - coin control enabled/disabled by user
723 {
724  ui->frameCoinControl->setVisible(checked);
725 
726  if (!checked && model) // coin control features disabled
728 
729  // make sure we set back the confirmation target
732 }
733 
734 // Coin Control: button inputs -> show actual coin control dialog
736 {
738  dlg.setModel(model);
739  dlg.exec();
741 }
742 
743 // Coin Control: checkbox custom change address
745 {
746  if (state == Qt::Unchecked)
747  {
749  ui->labelCoinControlChangeLabel->clear();
750  }
751  else
752  // use this to re-validate an already entered address
753  coinControlChangeEdited(ui->lineEditCoinControlChange->text());
754 
755  ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
756 }
757 
758 // Coin Control: custom change address changed
760 {
761  if (model && model->getAddressTableModel())
762  {
763  // Default to no change address until verified
765  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
766 
767  CBitcoinAddress addr = CBitcoinAddress(text.toStdString());
768 
769  if (text.isEmpty()) // Nothing entered
770  {
771  ui->labelCoinControlChangeLabel->setText("");
772  }
773  else if (!addr.IsValid()) // Invalid address
774  {
775  ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Dogecoin address"));
776  }
777  else // Valid address
778  {
779  CKeyID keyid;
780  addr.GetKeyID(keyid);
781  if (!model->havePrivKey(keyid)) // Unknown change address
782  {
783  ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
784 
785  // confirmation dialog
786  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?"),
787  QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
788 
789  if(btnRetVal == QMessageBox::Yes)
791  else
792  {
793  ui->lineEditCoinControlChange->setText("");
794  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
795  ui->labelCoinControlChangeLabel->setText("");
796  }
797  }
798  else // Known change address
799  {
800  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
801 
802  // Query label
803  QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
804  if (!associatedLabel.isEmpty())
805  ui->labelCoinControlChangeLabel->setText(associatedLabel);
806  else
807  ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
808 
810  }
811  }
812  }
813 }
814 
815 // Coin Control: update labels
817 {
818  if (!model || !model->getOptionsModel())
819  return;
820 
822  {
823  // enable minimum absolute fee UI controls
824  ui->radioCustomAtLeast->setVisible(true);
825 
826  // only enable the feature if inputs are selected
827  ui->radioCustomAtLeast->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked() &&CoinControlDialog::coinControl->HasSelected());
828  }
829  else
830  {
831  // in case coin control is disabled (=default), hide minimum absolute fee UI controls
832  ui->radioCustomAtLeast->setVisible(false);
833  return;
834  }
835 
836  // set pay amounts
839  for(int i = 0; i < ui->entries->count(); ++i)
840  {
841  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
842  if(entry && !entry->isHidden())
843  {
844  SendCoinsRecipient rcp = entry->getValue();
846  if (rcp.fSubtractFeeFromAmount)
848  }
849  }
850 
851  if (CoinControlDialog::coinControl->HasSelected())
852  {
853  // actual coin control calculation
855 
856  // show coin control stats
857  ui->labelCoinControlAutomaticallySelected->hide();
858  ui->widgetCoinControl->show();
859  }
860  else
861  {
862  // hide coin control stats
863  ui->labelCoinControlAutomaticallySelected->show();
864  ui->widgetCoinControl->hide();
865  ui->labelCoinControlInsuffFunds->hide();
866  }
867 }
868 
869 SendConfirmationDialog::SendConfirmationDialog(const QString &title, const QString &text, int _secDelay,
870  QWidget *parent) :
871  QMessageBox(QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::Cancel, parent), secDelay(_secDelay)
872 {
873  setDefaultButton(QMessageBox::Cancel);
874  yesButton = button(QMessageBox::Yes);
875  updateYesButton();
876  connect(&countDownTimer, SIGNAL(timeout()), this, SLOT(countDown()));
877 }
878 
880 {
881  updateYesButton();
882  countDownTimer.start(1000);
883  return QMessageBox::exec();
884 }
885 
887 {
888  secDelay--;
889  updateYesButton();
890 
891  if(secDelay <= 0)
892  {
893  countDownTimer.stop();
894  }
895 }
896 
898 {
899  if(secDelay > 0)
900  {
901  yesButton->setEnabled(false);
902  yesButton->setText(tr("Yes") + " (" + QString::number(secDelay) + ")");
903  }
904  else
905  {
906  yesButton->setEnabled(true);
907  yesButton->setText(tr("Yes"));
908  }
909 }
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:15
const CChainParams & Params()
Return the currently selected parameters.
QString labelForAddress(const QString &address) const
static QString formatHtmlWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as HTML string (with unit)
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
Unit
Bitcoin units.
Definition: bitcoinunits.h:58
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
base58-encoded Bitcoin addresses.
Definition: base58.h:104
CTxDestination Get() const
Definition: base58.cpp:260
bool IsValid() const
Definition: base58.cpp:247
bool GetKeyID(CKeyID &keyID) const
Definition: base58.cpp:274
Coin Control Features.
Definition: coincontrol.h:12
bool HasSelected() const
Definition: coincontrol.h:45
void UnSelectAll()
Definition: coincontrol.h:65
int nConfirmTarget
Override the default confirmation target, 0 = use default.
Definition: coincontrol.h:26
CTxDestination destChange
Definition: coincontrol.h:14
void SetNull()
Definition: coincontrol.h:33
CAmount nMinimumTotalFee
Minimum absolute fee (not per kilobyte)
Definition: coincontrol.h:20
Fee rate in satoshis per kilobyte: CAmount / kB.
Definition: amount.h:38
CAmount GetFeePerK() const
Return the fee in satoshis for a size of 1000 bytes.
Definition: amount.h:55
A reference to a CKey: the Hash160 of its serialized public key.
Definition: pubkey.h:30
CFeeRate estimateSmartFee(int nBlocks, int *answerFoundAtBlocks=NULL) const
Estimate fee rate needed to get into the next nBlocks If no answer can be given at nBlocks,...
Definition: txmempool.cpp:876
static CFeeRate fallbackFee
If fee estimation does not have enough data to provide estimates, use this fee instead.
Definition: wallet.h:747
Model for Bitcoin network client.
Definition: clientmodel.h:42
void setModel(WalletModel *model)
static void updateLabels(WalletModel *, QDialog *)
static CCoinControl * coinControl
static QList< CAmount > payAmounts
static bool fSubtractFeeFromAmount
int getDisplayUnit()
Definition: optionsmodel.h:65
bool getCoinControlFeatures()
Definition: optionsmodel.h:68
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
bool getImagesOnButtons() const
Definition: platformstyle.h:21
Dialog for sending bitcoins.
WalletModel * model
void updateGlobalFeeVariables()
void setBalance(const CAmount &balance, const CAmount &unconfirmedBalance, const CAmount &immatureBalance, const CAmount &watchOnlyBalance, const CAmount &watchUnconfBalance, const CAmount &watchImmatureBalance)
ClientModel * clientModel
void coinControlChangeEdited(const QString &)
void coinControlChangeChecked(int)
void coinControlClipboardFee()
Ui::SendCoinsDialog * ui
void on_buttonChooseFee_clicked()
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg=QString())
void setClientModel(ClientModel *clientModel)
void updateFeeSectionControls()
SendCoinsEntry * addEntry()
void pasteEntry(const SendCoinsRecipient &rv)
void updateFeeMinimizedLabel()
const PlatformStyle * platformStyle
void coinControlClipboardQuantity()
void coinControlButtonClicked()
void coinControlClipboardAfterFee()
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
void setModel(WalletModel *model)
void coinControlClipboardLowOutput()
bool handlePaymentRequest(const SendCoinsRecipient &recipient)
void coinControlClipboardAmount()
void setAddress(const QString &address)
void coinControlClipboardChange()
void removeEntry(SendCoinsEntry *entry)
void coinControlClipboardBytes()
void message(const QString &title, const QString &message, unsigned int style)
void on_buttonMinimizeFee_clicked()
void coinControlUpdateLabels()
void coinControlFeatureChanged(bool)
void minimizeFeeSection(bool fMinimize)
SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent=0)
A single entry in the dialog for sending bitcoins.
void setFocus()
void setAddress(const QString &address)
bool isClear()
Return whether the entry is still empty and unedited.
void setValue(const SendCoinsRecipient &value)
void setModel(WalletModel *model)
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
bool validate()
void clear()
SendCoinsRecipient getValue()
PaymentRequestPlus paymentRequest
Definition: walletmodel.h:56
bool fSubtractFeeFromAmount
Definition: walletmodel.h:60
QString authenticatedMerchant
Definition: walletmodel.h:58
QAbstractButton * yesButton
SendConfirmationDialog(const QString &title, const QString &text, int secDelay=0, QWidget *parent=0)
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:99
CAmount getBalance(const CCoinControl *coinControl=NULL) const
Definition: walletmodel.cpp:62
CAmount getUnconfirmedBalance() const
Definition: walletmodel.cpp:79
SendCoinsReturn sendCoins(WalletModelTransaction &transaction)
CAmount getWatchUnconfirmedBalance() const
Definition: walletmodel.cpp:99
bool havePrivKey(const CKeyID &address) const
CAmount getWatchBalance() const
Definition: walletmodel.cpp:94
AddressTableModel * getAddressTableModel()
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl=NULL)
OptionsModel * getOptionsModel()
CAmount getWatchImmatureBalance() const
CAmount getImmatureBalance() const
Definition: walletmodel.cpp:84
UnlockContext requestUnlock()
@ AmountWithFeeExceedsBalance
Definition: walletmodel.h:112
@ TransactionCreationFailed
Definition: walletmodel.h:114
@ AmountExceedsBalance
Definition: walletmodel.h:111
@ TransactionCommitFailed
Definition: walletmodel.h:115
@ PaymentRequestExpired
Definition: walletmodel.h:117
int getDefaultConfirmTarget() const
Data model for a walletmodel transaction.
QList< SendCoinsRecipient > getRecipients()
#define ASYMP_UTF8
static CAmount GetRequiredFee(const CMutableTransaction &tx, unsigned int nTxBytes)
Return the minimum required fee taking into account the floating relay fee and user set minimum trans...
Definition: wallet.cpp:2843
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:255
QString formatDurationStr(int secs)
Definition: guiutil.cpp:881
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
Definition: guiutil.cpp:120
void setClipboard(const QString &str)
Definition: guiutil.cpp:852
#define SEND_CONFIRM_DELAY
CTxMemPool mempool(::minRelayTxFee)
CAmount maxTxFee
Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendra...
Definition: validation.cpp:87
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE)
Transaction fee set by the user.