Fabcoin Core  0.16.2
P2P Digital Currency
sendcoinsdialog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2017 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 <fabcoinunits.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 #include <guiconstants.h>
18 #include <styleSheet.h>
19 
20 #include <base58.h>
21 #include <chainparams.h>
22 #include <wallet/coincontrol.h>
23 #include <validation.h> // mempool and minRelayTxFee
24 #include <ui_interface.h>
25 #include <txmempool.h>
26 #include <policy/fees.h>
27 #include <wallet/wallet.h>
28 
29 #include <QFontMetrics>
30 #include <QMessageBox>
31 #include <QScrollBar>
32 #include <QSettings>
33 #include <QTextDocument>
34 #include <QTimer>
35 
36 static const std::array<int, 9> confTargets = { {2, 4, 6, 12, 24, 48, 144, 504, 1008} };
37 int getConfTargetForIndex(int index) {
38  if (index+1 > static_cast<int>(confTargets.size())) {
39  return confTargets.back();
40  }
41  if (index < 0) {
42  return confTargets[0];
43  }
44  return confTargets[index];
45 }
46 int getIndexForConfTarget(int target) {
47  for (unsigned int i = 0; i < confTargets.size(); i++) {
48  if (confTargets[i] >= target) {
49  return i;
50  }
51  }
52  return confTargets.size() - 1;
53 }
54 
55 SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
56  QDialog(parent),
57  ui(new Ui::SendCoinsDialog),
58  clientModel(0),
59  model(0),
60  fNewRecipientAllowed(true),
61  platformStyle(_platformStyle)
62 {
63  ui->setupUi(this);
64 
65  // Set stylesheet
66  SetObjectStyleSheet(ui->clearButton, StyleSheetNames::ButtonBlack);
67  SetObjectStyleSheet(ui->addButton, StyleSheetNames::ButtonBlue);
68  SetObjectStyleSheet(ui->sendButton, StyleSheetNames::ButtonBlue);
69  SetObjectStyleSheet(ui->pushButtonCoinControl, StyleSheetNames::ButtonBlack);
70 
71  if (!_platformStyle->getImagesOnButtons()) {
72  ui->addButton->setIcon(QIcon());
73  } else {
74  ui->addButton->setIcon(_platformStyle->MultiStatesIcon(":/icons/add_recipient", PlatformStyle::PushButton));
75  }
76 
78 
79  addEntry();
80 
81  connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
82  connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
83 
84  // Coin Control
85  connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
86  connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
87  connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &)));
88 
89  // Coin Control: clipboard actions
90  QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
91  QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
92  QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
93  QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
94  QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
95  QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
96  QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
97  connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity()));
98  connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount()));
99  connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee()));
100  connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee()));
101  connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes()));
102  connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput()));
103  connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange()));
104  ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
105  ui->labelCoinControlAmount->addAction(clipboardAmountAction);
106  ui->labelCoinControlFee->addAction(clipboardFeeAction);
107  ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
108  ui->labelCoinControlBytes->addAction(clipboardBytesAction);
109  ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
110  ui->labelCoinControlChange->addAction(clipboardChangeAction);
111 
112  // init transaction fee section
113  QSettings settings;
114  if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
115  settings.setValue("nFeeRadio", 1); // custom
116  if (!settings.contains("nFeeRadio"))
117  settings.setValue("nFeeRadio", 0); // recommended
118  if (!settings.contains("nSmartFeeSliderPosition"))
119  settings.setValue("nSmartFeeSliderPosition", 0);
120  if (!settings.contains("nTransactionFee"))
121  settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
122  if (!settings.contains("fPayOnlyMinFee"))
123  settings.setValue("fPayOnlyMinFee", false);
124  ui->groupFee->setId(ui->radioSmartFee, 0);
125  ui->groupFee->setId(ui->radioCustomFee, 1);
126  ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
127  ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
128  ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
129 }
130 
132 {
133  this->clientModel = _clientModel;
134 
135  if (_clientModel) {
136  connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(updateSmartFeeLabel()));
137  }
138 }
139 
141 {
142  this->model = _model;
143 
144  if(_model && _model->getOptionsModel())
145  {
146  for(int i = 0; i < ui->entries->count(); ++i)
147  {
148  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
149  if(entry)
150  {
151  entry->setModel(_model);
152  }
153  }
154 
158  connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
160 
161  // Coin Control
162  connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
163  connect(_model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
166 
167  // fee section
168  for (const int &n : confTargets) {
169  ui->confTargetSelector->addItem(tr("%1 (%2 blocks)").arg(GUIUtil::formatNiceTimeOffset(n*Params().GetConsensus().nPowTargetSpacing)).arg(n));
170  }
171  connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSmartFeeLabel()));
172  connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(coinControlUpdateLabels()));
173  connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
174  connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
175  connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels()));
176  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee()));
177  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
178  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
179  connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(updateSmartFeeLabel()));
180  connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
185 
186  // set default rbf checkbox state
187  ui->optInRBF->setCheckState(model->getDefaultWalletRbf() ? Qt::Checked : Qt::Unchecked);
188 
189  // set the smartfee-sliders default value (wallets default conf.target or last stored value)
190  QSettings settings;
191  if (settings.value("nSmartFeeSliderPosition").toInt() != 0) {
192  // migrate nSmartFeeSliderPosition to nConfTarget
193  // nConfTarget is available since 0.15 (replaced nSmartFeeSliderPosition)
194  int nConfirmTarget = 25 - settings.value("nSmartFeeSliderPosition").toInt(); // 25 == old slider range
195  settings.setValue("nConfTarget", nConfirmTarget);
196  settings.remove("nSmartFeeSliderPosition");
197  }
198  if (settings.value("nConfTarget").toInt() == 0)
200  else
201  ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt()));
202  }
203 }
204 
206 {
207  QSettings settings;
208  settings.setValue("nFeeRadio", ui->groupFee->checkedId());
209  settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex()));
210  settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
211  settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
212 
213  delete ui;
214 }
215 
217 {
218  if(!model || !model->getOptionsModel())
219  return;
220 
221  QList<SendCoinsRecipient> recipients;
222  bool valid = true;
223 
224  for(int i = 0; i < ui->entries->count(); ++i)
225  {
226  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
227  if(entry)
228  {
229  if(entry->validate())
230  {
231  recipients.append(entry->getValue());
232  }
233  else
234  {
235  valid = false;
236  }
237  }
238  }
239 
240  if(!valid || recipients.isEmpty())
241  {
242  return;
243  }
244 
245  fNewRecipientAllowed = false;
247  if(!ctx.isValid())
248  {
249  // Unlock wallet was cancelled
250  fNewRecipientAllowed = true;
251  return;
252  }
253 
254  // prepare transaction for getting txFee earlier
255  WalletModelTransaction currentTransaction(recipients);
256  WalletModel::SendCoinsReturn prepareStatus;
257 
258  // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled
259  CCoinControl ctrl;
262 
264 
265  prepareStatus = model->prepareTransaction(currentTransaction, ctrl);
266 
267  // process prepareStatus and on error generate message shown to user
268  processSendCoinsReturn(prepareStatus,
270 
271  if(prepareStatus.status != WalletModel::OK) {
272  fNewRecipientAllowed = true;
273  return;
274  }
275 
276  CAmount txFee = currentTransaction.getTransactionFee();
277 
278  // Format confirmation message
279  QStringList formatted;
280  for (const SendCoinsRecipient &rcp : currentTransaction.getRecipients())
281  {
282  // generate bold amount string
283  QString amount = "<b>" + FabcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
284  amount.append("</b>");
285  // generate monospace address string
286  QString address = "<span style='font-family: monospace;'>" + rcp.address;
287  address.append("</span>");
288 
289  QString recipientElement;
290 
291  if (!rcp.paymentRequest.IsInitialized()) // normal payment
292  {
293  if(rcp.label.length() > 0) // label with address
294  {
295  recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label));
296  recipientElement.append(QString(" (%1)").arg(address));
297  }
298  else // just address
299  {
300  recipientElement = tr("%1 to %2").arg(amount, address);
301  }
302  }
303  else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request
304  {
305  recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant));
306  }
307  else // unauthenticated payment request
308  {
309  recipientElement = tr("%1 to %2").arg(amount, address);
310  }
311 
312  formatted.append(recipientElement);
313  }
314 
315  QString questionString = tr("Are you sure you want to send?");
316  questionString.append("<br /><br />%1");
317 
318  if(txFee > 0)
319  {
320  // append fee string if a fee is required
321  questionString.append("<hr /><span style='color:#aa0000;'>");
322  questionString.append(FabcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
323  questionString.append("</span> ");
324  questionString.append(tr("added as transaction fee"));
325 
326  // append transaction size
327  questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)");
328  }
329 
330  // add total amount in all subdivision units
331  questionString.append("<hr />");
332  CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
333  QStringList alternativeUnits;
335  {
336  if(u != model->getOptionsModel()->getDisplayUnit())
337  alternativeUnits.append(FabcoinUnits::formatHtmlWithUnit(u, totalAmount));
338  }
339  questionString.append(tr("Total Amount %1")
341  questionString.append(QString("<span style='font-size:10pt;font-weight:normal;'><br />(=%2)</span>")
342  .arg(alternativeUnits.join(" " + tr("or") + "<br />")));
343 
344  if (ui->optInRBF->isChecked())
345  {
346  questionString.append("<hr /><span>");
347  questionString.append(tr("This transaction signals replaceability (optin-RBF)."));
348  questionString.append("</span>");
349  }
350 
351  SendConfirmationDialog confirmationDialog(tr("Confirm send coins"),
352  questionString.arg(formatted.join("<br />")), SEND_CONFIRM_DELAY, this);
353  confirmationDialog.exec();
354  QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result();
355 
356  if(retval != QMessageBox::Yes)
357  {
358  fNewRecipientAllowed = true;
359  return;
360  }
361 
362  // now send the prepared transaction
363  WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
364  // process sendStatus and on error generate message shown to user
365  processSendCoinsReturn(sendStatus);
366 
367  if (sendStatus.status == WalletModel::OK)
368  {
369  accept();
372  }
373  fNewRecipientAllowed = true;
374 }
375 
377 {
378  // Remove entries until only one left
379  while(ui->entries->count())
380  {
381  ui->entries->takeAt(0)->widget()->deleteLater();
382  }
383  addEntry();
384 
386 }
387 
389 {
390  clear();
391 }
392 
394 {
395  clear();
396 }
397 
399 {
400  SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
401  entry->setModel(model);
402  ui->entries->addWidget(entry);
403  connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
404  connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels()));
405  connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this, SLOT(coinControlUpdateLabels()));
406 
407  // Focus the field, so that entry can start immediately
408  entry->clear();
409  entry->setFocus();
411  qApp->processEvents();
412  QScrollBar* bar = ui->scrollArea->verticalScrollBar();
413  if(bar)
414  bar->setSliderPosition(bar->maximum());
415 
417  return entry;
418 }
419 
421 {
422  setupTabChain(0);
424 }
425 
427 {
428  entry->hide();
429 
430  // If the last entry is about to be removed add an empty one
431  if (ui->entries->count() == 1)
432  addEntry();
433 
434  entry->deleteLater();
435 
437 }
438 
439 QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
440 {
441  for(int i = 0; i < ui->entries->count(); ++i)
442  {
443  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
444  if(entry)
445  {
446  prev = entry->setupTabChain(prev);
447  }
448  }
449  QWidget::setTabOrder(prev, ui->sendButton);
450  QWidget::setTabOrder(ui->sendButton, ui->clearButton);
451  QWidget::setTabOrder(ui->clearButton, ui->addButton);
452  return ui->addButton;
453 }
454 
456 {
457  SendCoinsEntry *entry = 0;
458  // Replace the first entry if it is still unused
459  if(ui->entries->count() == 1)
460  {
461  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
462  if(first->isClear())
463  {
464  entry = first;
465  }
466  }
467  if(!entry)
468  {
469  entry = addEntry();
470  }
471 
472  entry->setAddress(address);
473 }
474 
476 {
478  return;
479 
480  SendCoinsEntry *entry = 0;
481  // Replace the first entry if it is still unused
482  if(ui->entries->count() == 1)
483  {
484  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
485  if(first->isClear())
486  {
487  entry = first;
488  }
489  }
490  if(!entry)
491  {
492  entry = addEntry();
493  }
494 
495  entry->setValue(rv);
497 }
498 
500 {
501  // Just paste the entry, all pre-checks
502  // are done in paymentserver.cpp.
503  pasteEntry(rv);
504  return true;
505 }
506 
507 void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& stake,
508  const CAmount& watchBalance, const CAmount& watchUnconfirmedBalance, const CAmount& watchImmatureBalance, const CAmount& watchStake)
509 {
510  Q_UNUSED(unconfirmedBalance);
511  Q_UNUSED(immatureBalance);
512  Q_UNUSED(watchBalance);
513  Q_UNUSED(stake);
514  Q_UNUSED(watchUnconfirmedBalance);
515  Q_UNUSED(watchImmatureBalance);
516  Q_UNUSED(watchStake);
517 
518  if(model && model->getOptionsModel())
519  {
521  }
522 }
523 
525 {
526  setBalance(model->getBalance(), 0, 0, 0, 0, 0, 0, 0);
530 }
531 
532 void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
533 {
534  QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
535  // Default to a warning message, override if error message is needed
536  msgParams.second = CClientUIInterface::MSG_WARNING;
537 
538  // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
539  // WalletModel::TransactionCommitFailed is used only in WalletModel::sendCoins()
540  // all others are used only in WalletModel::prepareTransaction()
541  switch(sendCoinsReturn.status)
542  {
544  msgParams.first = tr("The recipient address is not valid. Please recheck.");
545  break;
547  msgParams.first = tr("The amount to pay must be larger than 0.");
548  break;
550  msgParams.first = tr("The amount exceeds your balance.");
551  break;
553  msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
554  break;
556  msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
557  break;
559  msgParams.first = tr("Transaction creation failed!");
560  msgParams.second = CClientUIInterface::MSG_ERROR;
561  break;
563  msgParams.first = tr("The transaction was rejected with the following reason: %1").arg(sendCoinsReturn.reasonCommitFailed);
564  msgParams.second = CClientUIInterface::MSG_ERROR;
565  break;
567  msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(FabcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), maxTxFee));
568  break;
570  msgParams.first = tr("Payment request expired.");
571  msgParams.second = CClientUIInterface::MSG_ERROR;
572  break;
573  // included to prevent a compiler warning.
574  case WalletModel::OK:
575  default:
576  return;
577  }
578 
579  Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
580 }
581 
583 {
585 }
586 
588 {
589  ui->confTargetSelector ->setEnabled(ui->radioSmartFee->isChecked());
590  ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
591  ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
592  ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
593  ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
594  ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
595  ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
596  ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
597  ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
598 
599  ui->stackedFeeTypes->setCurrentIndex(ui->radioSmartFee->isChecked() ? 0 : 1);
600 }
601 
603 {
604  if (model && model->getOptionsModel())
605  ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg(
607  );
608 }
609 
611 {
612  if (ui->radioCustomFee->isChecked()) {
613  ctrl.m_feerate = CFeeRate(ui->customFee->value());
614  } else {
615  ctrl.m_feerate.reset();
616  }
617  // Avoid using global defaults when sending money from the GUI
618  // Either custom fee will be used or if not selected, the confirmation target from dropdown box
620  ctrl.signalRbf = ui->optInRBF->isChecked();
621 }
622 
624 {
625  if(!model || !model->getOptionsModel())
626  return;
627  CCoinControl coin_control;
628  updateCoinControlState(coin_control);
629  coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
630  FeeCalculation feeCalc;
631  CFeeRate feeRate = CFeeRate(CWallet::GetMinimumFee(1000, coin_control, ::mempool, ::feeEstimator, &feeCalc));
632 
634 
635  if (feeCalc.reason == FeeReason::FALLBACK) {
636  ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
637  ui->labelFeeEstimation->setText("");
638  ui->fallbackFeeWarningLabel->setVisible(true);
639  int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
640  QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
641  ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }");
642  ui->fallbackFeeWarningLabel->setIndent(QFontMetrics(ui->fallbackFeeWarningLabel->font()).width("x"));
643  }
644  else
645  {
646  ui->labelSmartFee2->hide();
647  ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", feeCalc.returnedTarget));
648  ui->fallbackFeeWarningLabel->setVisible(false);
649  }
650 }
651 
652 // Coin Control: copy label "Quantity" to clipboard
654 {
656 }
657 
658 // Coin Control: copy label "Amount" to clipboard
660 {
661  GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
662 }
663 
664 // Coin Control: copy label "Fee" to clipboard
666 {
667  GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
668 }
669 
670 // Coin Control: copy label "After fee" to clipboard
672 {
673  GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
674 }
675 
676 // Coin Control: copy label "Bytes" to clipboard
678 {
680 }
681 
682 // Coin Control: copy label "Dust" to clipboard
684 {
686 }
687 
688 // Coin Control: copy label "Change" to clipboard
690 {
691  GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
692 }
693 
694 // Coin Control: settings menu - coin control enabled/disabled by user
696 {
697  ui->groupBoxCoinControl->setVisible(checked);
698 
699  if (!checked && model) // coin control features disabled
701 
703 }
704 
705 // Coin Control: button inputs -> show actual coin control dialog
707 {
709  dlg.setModel(model);
710  dlg.exec();
712 }
713 
714 // Coin Control: checkbox custom change address
716 {
717  if (state == Qt::Unchecked)
718  {
721  }
722  else
723  // use this to re-validate an already entered address
725 
726  ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
727 }
728 
729 // Coin Control: custom change address changed
731 {
732  if (model && model->getAddressTableModel())
733  {
734  // Default to no change address until verified
736  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
737 
738  CFabcoinAddress addr = CFabcoinAddress(text.toStdString());
739 
740  if (text.isEmpty()) // Nothing entered
741  {
742  ui->labelCoinControlChangeLabel->setText("");
743  }
744  else if (!addr.IsValid()) // Invalid address
745  {
746  ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Fabcoin address"));
747  }
748  else // Valid address
749  {
750  const CTxDestination dest = addr.Get();
751  if (!model->IsSpendable(dest)) {
752  ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
753 
754  // confirmation dialog
755  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?"),
756  QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
757 
758  if(btnRetVal == QMessageBox::Yes)
760  else
761  {
762  ui->lineEditCoinControlChange->setText("");
763  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
764  ui->labelCoinControlChangeLabel->setText("");
765  }
766  }
767  else // Known change address
768  {
769  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
770 
771  // Query label
772  QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
773  if (!associatedLabel.isEmpty())
774  ui->labelCoinControlChangeLabel->setText(associatedLabel);
775  else
776  ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
777 
779  }
780  }
781  }
782 }
783 
784 // Coin Control: update labels
786 {
787  if (!model || !model->getOptionsModel())
788  return;
789 
791 
792  // set pay amounts
795 
796  for(int i = 0; i < ui->entries->count(); ++i)
797  {
798  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
799  if(entry && !entry->isHidden())
800  {
801  SendCoinsRecipient rcp = entry->getValue();
803  if (rcp.fSubtractFeeFromAmount)
805  }
806  }
807 
808  if (CoinControlDialog::coinControl->HasSelected())
809  {
810  // actual coin control calculation
812 
813  // show coin control stats
815  ui->widgetCoinControl->show();
816  }
817  else
818  {
819  // hide coin control stats
821  ui->widgetCoinControl->hide();
823  }
824 }
825 
826 SendConfirmationDialog::SendConfirmationDialog(const QString &title, const QString &text, int _secDelay,
827  QWidget *parent) :
828  QMessageBox(QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::Cancel, parent), secDelay(_secDelay)
829 {
830  setDefaultButton(QMessageBox::Cancel);
831  yesButton = button(QMessageBox::Yes);
832  updateYesButton();
833  connect(&countDownTimer, SIGNAL(timeout()), this, SLOT(countDown()));
834 }
835 
837 {
838  updateYesButton();
839  countDownTimer.start(1000);
840  return QMessageBox::exec();
841 }
842 
844 {
845  secDelay--;
846  updateYesButton();
847 
848  if(secDelay <= 0)
849  {
850  countDownTimer.stop();
851  }
852 }
853 
855 {
856  if(secDelay > 0)
857  {
858  yesButton->setEnabled(false);
859  yesButton->setText(tr("Yes") + " (" + QString::number(secDelay) + ")");
860  }
861  else
862  {
863  yesButton->setEnabled(true);
864  yesButton->setText(tr("Yes"));
865  }
866 }
CAmount GetFeePerK() const
Return the fee in liu for a size of 1000 bytes.
Definition: feerate.h:38
CTxMemPool mempool
void removeEntry(SendCoinsEntry *entry)
#define SetObjectStyleSheet(object, name)
Definition: styleSheet.h:10
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:79
void setValue(const SendCoinsRecipient &value)
QButtonGroup * groupFee
int returnedTarget
Definition: fees.h:129
struct evm_uint256be balance(struct evm_env *env, struct evm_uint160be address)
Definition: capi.c:7
static CCoinControl * coinControl
bool IsSpendable(const CTxDestination &dest) const
boost::optional< unsigned int > m_confirm_target
Override the default confirmation target if set.
Definition: coincontrol.h:29
void setFocus()
SendCoinsRecipient getValue()
CAmount maxTxFee
Absolute maximum transaction fee (in liu) used by wallet and mempool (rejects high fee in sendrawtran...
Definition: validation.cpp:104
UnlockContext requestUnlock()
QPushButton * clearButton
QPushButton * sendButton
void coinControlClipboardQuantity()
QPushButton * addButton
void coinControlClipboardAfterFee()
void setAddress(const QString &address)
FeeReason reason
Definition: fees.h:127
base58-encoded Fabcoin addresses.
Definition: base58.h:104
void setSingleStep(const CAmount &step)
Set single step in satoshis.
QValidatedLineEdit * lineEditCoinControlChange
#define SEND_CONFIRM_DELAY
SendCoinsReturn sendCoins(WalletModelTransaction &transaction)
void updateCoinControlState(CCoinControl &ctrl)
bool IsValid() const
Definition: base58.cpp:247
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:258
bool handlePaymentRequest(const SendCoinsRecipient &recipient)
QScrollArea * scrollArea
CAmount getUnconfirmedBalance() const
QLabel * labelCoinControlAfterFee
ExecStats::duration min
Definition: ExecStats.cpp:35
AddressTableModel * getAddressTableModel()
#define ASYMP_UTF8
bool validate()
QLabel * labelCoinControlQuantity
A single entry in the dialog for sending fabcoins.
QLabel * labelCoinControlLowOutput
Coin Control Features.
Definition: coincontrol.h:16
QPushButton * pushButtonCoinControl
QLabel * labelCoinControlAutomaticallySelected
boost::optional< CFeeRate > m_feerate
Override the default payTxFee if set.
Definition: coincontrol.h:27
void coinControlFeatureChanged(bool)
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
static CAmount GetRequiredFee(unsigned int nTxBytes)
Return the minimum required fee taking into account the floating relay fee and user set minimum trans...
Definition: wallet.cpp:3109
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
bool getCoinControlFeatures()
Definition: optionsmodel.h:73
int64_t CAmount
Amount in lius (Can be negative)
Definition: amount.h:15
QList< SendCoinsRecipient > getRecipients()
CBlockPolicyEstimator feeEstimator
Definition: validation.cpp:106
static QList< CAmount > payAmounts
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
QCheckBox * checkBoxMinimumFee
Ui::SendCoinsDialog * ui
SendCoinsEntry * addEntry()
void clear()
CAmount getWatchBalance() const
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
Definition: guiutil.cpp:122
bool signalRbf
Signal BIP-125 replace by fee.
Definition: coincontrol.h:31
void setAddress(const QString &address)
void coinControlClipboardChange()
void setClientModel(ClientModel *clientModel)
void setClipboard(const QString &str)
Definition: guiutil.cpp:858
ClientModel * clientModel
void SetNull()
Definition: coincontrol.h:40
CTxDestination destChange
Definition: coincontrol.h:19
Unit
Fabcoin units.
Definition: fabcoinunits.h:59
FabcoinAmountField * customFee
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl &coinControl)
WalletModel * model
Dialog for sending fabcoins.
QCheckBox * checkBoxCoinControlChange
void coinControlChangeEdited(const QString &)
QStackedWidget * stackedFeeTypes
static void updateLabels(WalletModel *, QDialog *)
int getDisplayUnit()
Definition: optionsmodel.h:70
CTxDestination Get() const
Definition: base58.cpp:260
Model for Fabcoin network client.
Definition: clientmodel.h:38
void coinControlUpdateLabels()
void UnSelectAll()
Definition: coincontrol.h:73
void setModel(WalletModel *model)
int getConfTargetForIndex(int index)
CAmount getWatchImmatureBalance() const
void setupUi(QDialog *SendCoinsDialog)
bool isClear()
Return whether the entry is still empty and unedited.
void coinControlClipboardLowOutput()
QLabel * labelCoinControlChangeLabel
void updateFeeSectionControls()
void setEnabled(bool fEnabled)
Enable/Disable.
QWidget * scrollAreaWidgetContents
CAmount getStake() const
Definition: walletmodel.cpp:95
void setModel(WalletModel *model)
static CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl &coin_control, const CTxMemPool &pool, const CBlockPolicyEstimator &estimator, FeeCalculation *feeCalc)
Estimate the minimum fee considering user set parameters and the required fee.
Definition: wallet.cpp:3114
static bool fSubtractFeeFromAmount
void setValue(const CAmount &value)
QString labelForAddress(const QString &address) const
int getIndexForConfTarget(int target)
QIcon MultiStatesIcon(const QString &resourcename, StateType type=NavBar, QColor color=Qt::white, QColor colorAlt=0x2d2d2d) const
Get multi-states icon.
const CChainParams & Params()
Return the currently selected parameters.
Interface to Fabcoin wallet from Qt view code.
Definition: walletmodel.h:103
CAmount getWatchStake() const
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent=0)
SendConfirmationDialog(const QString &title, const QString &text, int secDelay=SEND_CONFIRM_DELAY, QWidget *parent=0)
void setModel(WalletModel *model)
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg=QString())
Fee rate in liu per kilobyte: CAmount / kB.
Definition: feerate.h:20
Data model for a walletmodel transaction.
QComboBox * confTargetSelector
void setEnabled(bool enabled)
void coinControlClipboardBytes()
CAmount getImmatureBalance() const
bool getImagesOnButtons() const
Definition: platformstyle.h:21
static QString formatHtmlWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as HTML string (with unit)
bool fSubtractFeeFromAmount
Definition: walletmodel.h:65
const PlatformStyle * platformStyle
QString formatNiceTimeOffset(qint64 secs)
Definition: guiutil.cpp:942
dev::WithExisting max(dev::WithExisting _a, dev::WithExisting _b)
Definition: Common.h:326
struct evm_uint160be address(struct evm_env *env)
Definition: capi.c:13
void coinControlClipboardAmount()
QLabel * fallbackFeeWarningLabel
bool getDefaultWalletRbf() const
void pasteEntry(const SendCoinsRecipient &rv)
QGroupBox * groupBoxCoinControl
QAbstractButton * yesButton
void message(const QString &title, const QString &message, unsigned int style)
void setDisplayUnit(int unit)
Change unit used to display amount.
CAmount getWatchUnconfirmedBalance() const
QCheckBox * radioCustomFee
void setBalance(const CAmount &balance, const CAmount &unconfirmedBalance, const CAmount &immatureBalance, const CAmount &stake, const CAmount &watchOnlyBalance, const CAmount &watchUnconfBalance, const CAmount &watchImmatureBalance, const CAmount &watchOnlyStake)
void coinControlButtonClicked()
int getDefaultConfirmTarget() const
void coinControlClipboardFee()
CAmount getBalance(const CCoinControl *coinControl=nullptr) const
Definition: walletmodel.cpp:86
OptionsModel * getOptionsModel()
QLabel * labelCoinControlInsuffFunds
void coinControlChangeChecked(int)