Raven Core  3.0.0
P2P Digital Currency
sendcoinsdialog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2016 The Bitcoin Core developers
2 // Copyright (c) 2017-2019 The Raven Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "sendcoinsdialog.h"
7 #include "ui_sendcoinsdialog.h"
8 
9 #include "addresstablemodel.h"
10 #include "ravenunits.h"
11 #include "clientmodel.h"
12 #include "coincontroldialog.h"
13 #include "guiutil.h"
14 #include "optionsmodel.h"
15 #include "platformstyle.h"
16 #include "sendcoinsentry.h"
17 #include "walletmodel.h"
18 #include "guiconstants.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/fees.h"
28 
29 #include <QGraphicsDropShadowEffect>
30 #include <QFontMetrics>
31 #include <QMessageBox>
32 #include <QScrollBar>
33 #include <QSettings>
34 #include <QTextDocument>
35 #include <QTimer>
36 
37 SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
38  QDialog(parent),
39  ui(new Ui::SendCoinsDialog),
40  clientModel(0),
41  model(0),
42  fNewRecipientAllowed(true),
43  fFeeMinimized(true),
44  platformStyle(_platformStyle)
45 {
46  ui->setupUi(this);
47 
48  if (!_platformStyle->getImagesOnButtons()) {
49  ui->addButton->setIcon(QIcon());
50  ui->clearButton->setIcon(QIcon());
51  ui->sendButton->setIcon(QIcon());
52  } else {
53  ui->addButton->setIcon(_platformStyle->SingleColorIcon(":/icons/add"));
54  ui->clearButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
55  ui->sendButton->setIcon(_platformStyle->SingleColorIcon(":/icons/send"));
56  }
57 
58  GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
59 
60  addEntry();
61 
62  connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
63  connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
64 
65  // Coin Control
66  connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
67  connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
68  connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &)));
69 
70  // Coin Control: clipboard actions
71  QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
72  QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
73  QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
74  QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
75  QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
76  QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
77  QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
78  connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity()));
79  connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount()));
80  connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee()));
81  connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee()));
82  connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes()));
83  connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput()));
84  connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange()));
85  ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
86  ui->labelCoinControlAmount->addAction(clipboardAmountAction);
87  ui->labelCoinControlFee->addAction(clipboardFeeAction);
88  ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
89  ui->labelCoinControlBytes->addAction(clipboardBytesAction);
90  ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
91  ui->labelCoinControlChange->addAction(clipboardChangeAction);
92 
93  // init transaction fee section
94  QSettings settings;
95  if (!settings.contains("fFeeSectionMinimized"))
96  settings.setValue("fFeeSectionMinimized", true);
97  if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
98  settings.setValue("nFeeRadio", 1); // custom
99  if (!settings.contains("nFeeRadio"))
100  settings.setValue("nFeeRadio", 0); // recommended
101  if (!settings.contains("nSmartFeeSliderPosition"))
102  settings.setValue("nSmartFeeSliderPosition", 0);
103  if (!settings.contains("nTransactionFee"))
104  settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
105  if (!settings.contains("fPayOnlyMinFee"))
106  settings.setValue("fPayOnlyMinFee", false);
107  ui->groupFee->setId(ui->radioSmartFee, 0);
108  ui->groupFee->setId(ui->radioCustomFee, 1);
109  ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
110  ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
111  ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
112  minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
113 
114  // Setup the coin control visuals and labels
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  for (const int &n : confTargets) {
158  ui->confTargetSelector->addItem(tr("%1 (%2 blocks)").arg(GUIUtil::formatNiceTimeOffset(n*Params().GetConsensus().nPowTargetSpacing)).arg(n));
159  }
160  connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSmartFeeLabel()));
161  connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(coinControlUpdateLabels()));
162  connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
163  connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
164  connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels()));
165  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee()));
166  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
167  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
168 // connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(updateSmartFeeLabel()));
169 // connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
170  ui->customFee->setSingleStep(GetRequiredFee(1000));
174 
175  // set default rbf checkbox state
176 // ui->optInRBF->setCheckState(model->getDefaultWalletRbf() ? Qt::Checked : Qt::Unchecked);
177  ui->optInRBF->hide();
178 
179  // set the smartfee-sliders default value (wallets default conf.target or last stored value)
180  QSettings settings;
181  if (settings.value("nSmartFeeSliderPosition").toInt() != 0) {
182  // migrate nSmartFeeSliderPosition to nConfTarget
183  // nConfTarget is available since 0.15 (replaced nSmartFeeSliderPosition)
184  int nConfirmTarget = 25 - settings.value("nSmartFeeSliderPosition").toInt(); // 25 == old slider range
185  settings.setValue("nConfTarget", nConfirmTarget);
186  settings.remove("nSmartFeeSliderPosition");
187  }
188  if (settings.value("nConfTarget").toInt() == 0)
189  ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->getDefaultConfirmTarget()));
190  else
191  ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt()));
192  }
193 }
194 
196 {
197  QSettings settings;
198  settings.setValue("fFeeSectionMinimized", fFeeMinimized);
199  settings.setValue("nFeeRadio", ui->groupFee->checkedId());
200  settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex()));
201  settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
202  settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
203 
204  delete ui;
205 }
206 
208 {
210  ui->frameCoinControl->setStyleSheet(QString(".QFrame {background-color: %1; padding-top: 10px; padding-right: 5px; border: none;}").arg(platformStyle->WidgetBackGroundColor().name()));
211  ui->widgetCoinControl->setStyleSheet(".QWidget {background-color: transparent;}");
214  ui->frameCoinControl->setGraphicsEffect(GUIUtil::getShadowEffect());
215 
216  ui->labelCoinControlFeatures->setStyleSheet(STRING_LABEL_COLOR);
217  ui->labelCoinControlFeatures->setFont(GUIUtil::getTopLabelFont());
218 
219  ui->labelCoinControlQuantityText->setStyleSheet(STRING_LABEL_COLOR);
220  ui->labelCoinControlQuantityText->setFont(GUIUtil::getSubLabelFont());
221 
222  ui->labelCoinControlAmountText->setStyleSheet(STRING_LABEL_COLOR);
223  ui->labelCoinControlAmountText->setFont(GUIUtil::getSubLabelFont());
224 
225  ui->labelCoinControlFeeText->setStyleSheet(STRING_LABEL_COLOR);
226  ui->labelCoinControlFeeText->setFont(GUIUtil::getSubLabelFont());
227 
228  ui->labelCoinControlAfterFeeText->setStyleSheet(STRING_LABEL_COLOR);
229  ui->labelCoinControlAfterFeeText->setFont(GUIUtil::getSubLabelFont());
230 
231  ui->labelCoinControlBytesText->setStyleSheet(STRING_LABEL_COLOR);
232  ui->labelCoinControlBytesText->setFont(GUIUtil::getSubLabelFont());
233 
234  ui->labelCoinControlLowOutputText->setStyleSheet(STRING_LABEL_COLOR);
235  ui->labelCoinControlLowOutputText->setFont(GUIUtil::getSubLabelFont());
236 
237  ui->labelCoinControlChangeText->setStyleSheet(STRING_LABEL_COLOR);
238  ui->labelCoinControlChangeText->setFont(GUIUtil::getSubLabelFont());
239 
240  // Align the other labels next to the input buttons to the text in the same height
241  ui->labelCoinControlAutomaticallySelected->setStyleSheet(STRING_LABEL_COLOR);
242 
243  // Align the Custom change address checkbox
244  ui->checkBoxCoinControlChange->setStyleSheet(STRING_LABEL_COLOR);
245 
246  ui->labelCoinControlQuantity->setFont(GUIUtil::getSubLabelFont());
247  ui->labelCoinControlAmount->setFont(GUIUtil::getSubLabelFont());
248  ui->labelCoinControlFee->setFont(GUIUtil::getSubLabelFont());
249  ui->labelCoinControlAfterFee->setFont(GUIUtil::getSubLabelFont());
250  ui->labelCoinControlBytes->setFont(GUIUtil::getSubLabelFont());
251  ui->labelCoinControlLowOutput->setFont(GUIUtil::getSubLabelFont());
252  ui->labelCoinControlChange->setFont(GUIUtil::getSubLabelFont());
253  ui->checkBoxCoinControlChange->setFont(GUIUtil::getSubLabelFont());
254  ui->lineEditCoinControlChange->setFont(GUIUtil::getSubLabelFont());
255  ui->labelCoinControlInsuffFunds->setFont(GUIUtil::getSubLabelFont());
256  ui->labelCoinControlAutomaticallySelected->setFont(GUIUtil::getSubLabelFont());
257  ui->labelCoinControlChangeLabel->setFont(GUIUtil::getSubLabelFontBolded());
258 }
259 
261 {
263  ui->scrollArea->setStyleSheet(QString(".QScrollArea{background-color: %1; border: none}").arg(platformStyle->WidgetBackGroundColor().name()));
264  ui->scrollArea->setGraphicsEffect(GUIUtil::getShadowEffect());
265 
266  // Add some spacing so we can see the whole card
267  ui->entries->setContentsMargins(10,10,20,0);
268  ui->scrollAreaWidgetContents->setStyleSheet(QString(".QWidget{ background-color: %1;}").arg(platformStyle->WidgetBackGroundColor().name()));
269 }
270 
272 {
274  ui->frameFee->setStyleSheet(QString(".QFrame {background-color: %1; padding-top: 10px; padding-right: 5px; border: none;}").arg(platformStyle->WidgetBackGroundColor().name()));
277  ui->frameFee->setGraphicsEffect(GUIUtil::getShadowEffect());
278 
279  ui->labelFeeHeadline->setStyleSheet(STRING_LABEL_COLOR);
280  ui->labelFeeHeadline->setFont(GUIUtil::getSubLabelFont());
281 
282  ui->labelSmartFee3->setStyleSheet(STRING_LABEL_COLOR);
283  ui->labelCustomPerKilobyte->setStyleSheet(STRING_LABEL_COLOR);
284  ui->radioSmartFee->setStyleSheet(STRING_LABEL_COLOR);
285  ui->radioCustomFee->setStyleSheet(STRING_LABEL_COLOR);
286  ui->checkBoxMinimumFee->setStyleSheet(STRING_LABEL_COLOR);
287 
288 
289  ui->buttonChooseFee->setFont(GUIUtil::getSubLabelFont());
290  ui->fallbackFeeWarningLabel->setFont(GUIUtil::getSubLabelFont());
291  ui->buttonMinimizeFee->setFont(GUIUtil::getSubLabelFont());
292  ui->radioSmartFee->setFont(GUIUtil::getSubLabelFont());
293  ui->labelSmartFee2->setFont(GUIUtil::getSubLabelFont());
294  ui->labelSmartFee3->setFont(GUIUtil::getSubLabelFont());
295  ui->confTargetSelector->setFont(GUIUtil::getSubLabelFont());
296  ui->radioCustomFee->setFont(GUIUtil::getSubLabelFont());
297  ui->labelCustomPerKilobyte->setFont(GUIUtil::getSubLabelFont());
298  ui->customFee->setFont(GUIUtil::getSubLabelFont());
299  ui->labelMinFeeWarning->setFont(GUIUtil::getSubLabelFont());
300  ui->optInRBF->setFont(GUIUtil::getSubLabelFont());
301  ui->sendButton->setFont(GUIUtil::getSubLabelFont());
302  ui->clearButton->setFont(GUIUtil::getSubLabelFont());
303  ui->addButton->setFont(GUIUtil::getSubLabelFont());
304  ui->labelSmartFee->setFont(GUIUtil::getSubLabelFont());
305  ui->labelFeeEstimation->setFont(GUIUtil::getSubLabelFont());
306  ui->labelFeeMinimized->setFont(GUIUtil::getSubLabelFont());
307 }
308 
310 {
311  if(!model || !model->getOptionsModel())
312  return;
313 
314  QList<SendCoinsRecipient> recipients;
315  bool valid = true;
316 
317  for(int i = 0; i < ui->entries->count(); ++i)
318  {
319  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
320  if(entry)
321  {
322  if(entry->validate())
323  {
324  recipients.append(entry->getValue());
325  }
326  else
327  {
328  valid = false;
329  }
330  }
331  }
332 
333  if(!valid || recipients.isEmpty())
334  {
335  return;
336  }
337 
338  fNewRecipientAllowed = false;
340  if(!ctx.isValid())
341  {
342  // Unlock wallet was cancelled
343  fNewRecipientAllowed = true;
344  return;
345  }
346 
347  // prepare transaction for getting txFee earlier
348  WalletModelTransaction currentTransaction(recipients);
349  WalletModel::SendCoinsReturn prepareStatus;
350 
351  // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled
352  CCoinControl ctrl;
355 
357 
358  prepareStatus = model->prepareTransaction(currentTransaction, ctrl);
359 
360  // process prepareStatus and on error generate message shown to user
361  processSendCoinsReturn(prepareStatus,
363 
364  if(prepareStatus.status != WalletModel::OK) {
365  fNewRecipientAllowed = true;
366  return;
367  }
368 
369  CAmount txFee = currentTransaction.getTransactionFee();
370 
371  // Format confirmation message
372  QStringList formatted;
373  for (const SendCoinsRecipient &rcp : currentTransaction.getRecipients())
374  {
375  // generate bold amount string
376  QString amount = "<b>" + RavenUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
377  amount.append("</b>");
378  // generate monospace address string
379  QString address = "<span style='font-family: monospace;'>" + rcp.address;
380  address.append("</span>");
381 
382  QString recipientElement;
383 
384  if (!rcp.paymentRequest.IsInitialized()) // normal payment
385  {
386  if(rcp.label.length() > 0) // label with address
387  {
388  recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label));
389  recipientElement.append(QString(" (%1)").arg(address));
390  }
391  else // just address
392  {
393  recipientElement = tr("%1 to %2").arg(amount, address);
394  }
395  }
396  else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request
397  {
398  recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant));
399  }
400  else // unauthenticated payment request
401  {
402  recipientElement = tr("%1 to %2").arg(amount, address);
403  }
404 
405  formatted.append(recipientElement);
406  }
407 
408  QString questionString = tr("Are you sure you want to send?");
409  questionString.append("<br /><br />%1");
410 
411  if(txFee > 0)
412  {
413  // append fee string if a fee is required
414  questionString.append("<hr /><span style='color:#aa0000;'>");
415  questionString.append(RavenUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
416  questionString.append("</span> ");
417  questionString.append(tr("added as transaction fee"));
418 
419  // append transaction size
420  questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)");
421  }
422 
423  // add total amount in all subdivision units
424  questionString.append("<hr />");
425  CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
426  QStringList alternativeUnits;
428  {
429  if(u != model->getOptionsModel()->getDisplayUnit())
430  alternativeUnits.append(RavenUnits::formatHtmlWithUnit(u, totalAmount));
431  }
432  questionString.append(tr("Total Amount %1")
434  questionString.append(QString("<span style='font-size:10pt;font-weight:normal;'><br />(=%2)</span>")
435  .arg(alternativeUnits.join(" " + tr("or") + "<br />")));
436 
437 // if (ui->optInRBF->isChecked())
438 // {
439 // questionString.append("<hr /><span>");
440 // questionString.append(tr("This transaction signals replaceability (optin-RBF)."));
441 // questionString.append("</span>");
442 // }
443 
444  SendConfirmationDialog confirmationDialog(tr("Confirm send coins"),
445  questionString.arg(formatted.join("<br />")), SEND_CONFIRM_DELAY, this);
446  confirmationDialog.exec();
447  QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result();
448 
449  if(retval != QMessageBox::Yes)
450  {
451  fNewRecipientAllowed = true;
452  return;
453  }
454 
455  // now send the prepared transaction
456  WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
457  // process sendStatus and on error generate message shown to user
458  processSendCoinsReturn(sendStatus);
459 
460  if (sendStatus.status == WalletModel::OK)
461  {
462  accept();
465  }
466  fNewRecipientAllowed = true;
467 }
468 
470 {
471  // Remove entries until only one left
472  while(ui->entries->count())
473  {
474  ui->entries->takeAt(0)->widget()->deleteLater();
475  }
476  addEntry();
477 
479 }
480 
482 {
483  clear();
484 }
485 
487 {
488  clear();
489 }
490 
492 {
493  SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
494  entry->setModel(model);
495  ui->entries->addWidget(entry);
496  connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
497  connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels()));
498  connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this, SLOT(coinControlUpdateLabels()));
499 
500  // Focus the field, so that entry can start immediately
501  entry->clear();
502  entry->setFocus();
503  ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
504  qApp->processEvents();
505  QScrollBar* bar = ui->scrollArea->verticalScrollBar();
506  if(bar)
507  bar->setSliderPosition(bar->maximum());
508 
510  return entry;
511 }
512 
514 {
515  setupTabChain(0);
517 }
518 
520 {
521  entry->hide();
522 
523  // If the last entry is about to be removed add an empty one
524  if (ui->entries->count() == 1)
525  addEntry();
526 
527  entry->deleteLater();
528 
530 }
531 
532 QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
533 {
534  for(int i = 0; i < ui->entries->count(); ++i)
535  {
536  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
537  if(entry)
538  {
539  prev = entry->setupTabChain(prev);
540  }
541  }
542  QWidget::setTabOrder(prev, ui->sendButton);
543  QWidget::setTabOrder(ui->sendButton, ui->clearButton);
544  QWidget::setTabOrder(ui->clearButton, ui->addButton);
545  return ui->addButton;
546 }
547 
548 void SendCoinsDialog::setAddress(const QString &address)
549 {
550  SendCoinsEntry *entry = 0;
551  // Replace the first entry if it is still unused
552  if(ui->entries->count() == 1)
553  {
554  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
555  if(first->isClear())
556  {
557  entry = first;
558  }
559  }
560  if(!entry)
561  {
562  entry = addEntry();
563  }
564 
565  entry->setAddress(address);
566 }
567 
569 {
571  return;
572 
573  SendCoinsEntry *entry = 0;
574  // Replace the first entry if it is still unused
575  if(ui->entries->count() == 1)
576  {
577  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
578  if(first->isClear())
579  {
580  entry = first;
581  }
582  }
583  if(!entry)
584  {
585  entry = addEntry();
586  }
587 
588  entry->setValue(rv);
590 }
591 
593 {
594  // Just paste the entry, all pre-checks
595  // are done in paymentserver.cpp.
596  pasteEntry(rv);
597  return true;
598 }
599 
600 void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
601  const CAmount& watchBalance, const CAmount& watchUnconfirmedBalance, const CAmount& watchImmatureBalance)
602 {
603  Q_UNUSED(unconfirmedBalance);
604  Q_UNUSED(immatureBalance);
605  Q_UNUSED(watchBalance);
606  Q_UNUSED(watchUnconfirmedBalance);
607  Q_UNUSED(watchImmatureBalance);
608 
609 
610  ui->labelBalance->setFont(GUIUtil::getSubLabelFont());
611  ui->label->setFont(GUIUtil::getSubLabelFont());
612 
613  if(model && model->getOptionsModel())
614  {
615  ui->labelBalance->setText(RavenUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
616  }
617 }
618 
620 {
621  setBalance(model->getBalance(), 0, 0, 0, 0, 0);
622  ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
625 }
626 
627 void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
628 {
629  QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
630  // Default to a warning message, override if error message is needed
631  msgParams.second = CClientUIInterface::MSG_WARNING;
632 
633  // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
634  // WalletModel::TransactionCommitFailed is used only in WalletModel::sendCoins()
635  // all others are used only in WalletModel::prepareTransaction()
636  switch(sendCoinsReturn.status)
637  {
639  msgParams.first = tr("The recipient address is not valid. Please recheck.");
640  break;
642  msgParams.first = tr("The amount to pay must be larger than 0.");
643  break;
645  msgParams.first = tr("The amount exceeds your balance.");
646  break;
648  msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
649  break;
651  msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
652  break;
654  msgParams.first = tr("Transaction creation failed!");
655  msgParams.second = CClientUIInterface::MSG_ERROR;
656  break;
658  msgParams.first = tr("The transaction was rejected with the following reason: %1").arg(sendCoinsReturn.reasonCommitFailed);
659  msgParams.second = CClientUIInterface::MSG_ERROR;
660  break;
662  msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(RavenUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), maxTxFee));
663  break;
665  msgParams.first = tr("Payment request expired.");
666  msgParams.second = CClientUIInterface::MSG_ERROR;
667  break;
668  // included to prevent a compiler warning.
669  case WalletModel::OK:
670  default:
671  return;
672  }
673 
674  Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
675 }
676 
678 {
679  ui->labelFeeMinimized->setVisible(fMinimize);
680  ui->buttonChooseFee ->setVisible(fMinimize);
681  ui->buttonMinimizeFee->setVisible(!fMinimize);
682  ui->frameFeeSelection->setVisible(!fMinimize);
683  ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
684  fFeeMinimized = fMinimize;
685 }
686 
688 {
689  minimizeFeeSection(false);
690 }
691 
693 {
695  minimizeFeeSection(true);
696 }
697 
699 {
700  ui->customFee->setValue(GetRequiredFee(1000));
701 }
702 
704 {
705  ui->confTargetSelector ->setEnabled(ui->radioSmartFee->isChecked());
706  ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
707  ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
708  ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
709  ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
710  ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
711  ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
712  ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
713  ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
714 }
715 
717 {
718  if(!model || !model->getOptionsModel())
719  return;
720 
721  if (ui->radioSmartFee->isChecked())
722  ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
723  else {
724  ui->labelFeeMinimized->setText(RavenUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + "/kB");
725  }
726 }
727 
729 {
730  if (model && model->getOptionsModel())
731  ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg(
733  );
734 }
735 
737 {
738  if (ui->radioCustomFee->isChecked()) {
739  ctrl.m_feerate = CFeeRate(ui->customFee->value());
740  } else {
741  ctrl.m_feerate.reset();
742  }
743  // Avoid using global defaults when sending money from the GUI
744  // Either custom fee will be used or if not selected, the confirmation target from dropdown box
745  ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
746 // ctrl.signalRbf = ui->optInRBF->isChecked();
747 }
748 
750 {
751  if(!model || !model->getOptionsModel())
752  return;
753  CCoinControl coin_control;
754  updateCoinControlState(coin_control);
755  coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
756  FeeCalculation feeCalc;
757  CFeeRate feeRate = CFeeRate(GetMinimumFee(1000, coin_control, ::mempool, ::feeEstimator, &feeCalc));
758 
759  ui->labelSmartFee->setText(RavenUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
760 
761  if (feeCalc.reason == FeeReason::FALLBACK) {
762  ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
763  ui->labelFeeEstimation->setText("");
764  ui->fallbackFeeWarningLabel->setVisible(true);
765  int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
766  QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
767  ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }");
768  ui->fallbackFeeWarningLabel->setIndent(QFontMetrics(ui->fallbackFeeWarningLabel->font()).width("x"));
769  }
770  else
771  {
772  ui->labelSmartFee2->hide();
773  ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", feeCalc.returnedTarget));
774  ui->fallbackFeeWarningLabel->setVisible(false);
775  }
776 
778 }
779 
780 // Coin Control: copy label "Quantity" to clipboard
782 {
783  GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
784 }
785 
786 // Coin Control: copy label "Amount" to clipboard
788 {
789  GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
790 }
791 
792 // Coin Control: copy label "Fee" to clipboard
794 {
795  GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
796 }
797 
798 // Coin Control: copy label "After fee" to clipboard
800 {
801  GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
802 }
803 
804 // Coin Control: copy label "Bytes" to clipboard
806 {
807  GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
808 }
809 
810 // Coin Control: copy label "Dust" to clipboard
812 {
813  GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
814 }
815 
816 // Coin Control: copy label "Change" to clipboard
818 {
819  GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
820 }
821 
822 // Coin Control: settings menu - coin control enabled/disabled by user
824 {
825  ui->frameCoinControl->setVisible(checked);
826 
827  if (!checked && model) // coin control features disabled
829 
831 }
832 
833 // Coin Control: button inputs -> show actual coin control dialog
835 {
837  dlg.setModel(model);
838  dlg.exec();
840 }
841 
842 // Coin Control: checkbox custom change address
844 {
845  if (state == Qt::Unchecked)
846  {
848  ui->labelCoinControlChangeLabel->clear();
849  }
850  else
851  // use this to re-validate an already entered address
852  coinControlChangeEdited(ui->lineEditCoinControlChange->text());
853 
854  ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
855 }
856 
857 // Coin Control: custom change address changed
859 {
860  if (model && model->getAddressTableModel())
861  {
862  // Default to no change address until verified
864  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
865 
866  const CTxDestination dest = DecodeDestination(text.toStdString());
867 
868  if (text.isEmpty()) // Nothing entered
869  {
870  ui->labelCoinControlChangeLabel->setText("");
871  }
872  else if (!IsValidDestination(dest)) // Invalid address
873  {
874  ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Raven address"));
875  }
876  else // Valid address
877  {
878  if (!model->IsSpendable(dest)) {
879  ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
880 
881  // confirmation dialog
882  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?"),
883  QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
884 
885  if(btnRetVal == QMessageBox::Yes)
887  else
888  {
889  ui->lineEditCoinControlChange->setText("");
890  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
891  ui->labelCoinControlChangeLabel->setText("");
892  }
893  }
894  else // Known change address
895  {
896  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
897 
898  // Query label
899  QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
900  if (!associatedLabel.isEmpty())
901  ui->labelCoinControlChangeLabel->setText(associatedLabel);
902  else
903  ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
904 
906  }
907  }
908  }
909 }
910 
911 // Coin Control: update labels
913 {
914  if (!model || !model->getOptionsModel())
915  return;
916 
918 
919  // set pay amounts
922 
923  for(int i = 0; i < ui->entries->count(); ++i)
924  {
925  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
926  if(entry && !entry->isHidden())
927  {
928  SendCoinsRecipient rcp = entry->getValue();
930  if (rcp.fSubtractFeeFromAmount)
932  }
933  }
934 
935  if (CoinControlDialog::coinControl->HasSelected())
936  {
937  // actual coin control calculation
939 
940  // show coin control stats
941  ui->labelCoinControlAutomaticallySelected->hide();
942  ui->widgetCoinControl->show();
943  }
944  else
945  {
946  // hide coin control stats
947  ui->labelCoinControlAutomaticallySelected->show();
948  ui->widgetCoinControl->hide();
949  ui->labelCoinControlInsuffFunds->hide();
950  }
951 }
952 
953 SendConfirmationDialog::SendConfirmationDialog(const QString &title, const QString &text, int _secDelay,
954  QWidget *parent) :
955  QMessageBox(QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::Cancel, parent), secDelay(_secDelay)
956 {
957  setDefaultButton(QMessageBox::Cancel);
958  yesButton = button(QMessageBox::Yes);
959  updateYesButton();
960  connect(&countDownTimer, SIGNAL(timeout()), this, SLOT(countDown()));
961 }
962 
964 {
965  updateYesButton();
966  countDownTimer.start(1000);
967  return QMessageBox::exec();
968 }
969 
971 {
972  secDelay--;
973  updateYesButton();
974 
975  if(secDelay <= 0)
976  {
977  countDownTimer.stop();
978  }
979 }
980 
982 {
983  if(secDelay > 0)
984  {
985  yesButton->setEnabled(false);
986  yesButton->setText(tr("Yes") + " (" + QString::number(secDelay) + ")");
987  }
988  else
989  {
990  yesButton->setEnabled(true);
991  yesButton->setText(tr("Yes"));
992  }
993 }
CTxMemPool mempool
void removeEntry(SendCoinsEntry *entry)
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:89
void setValue(const SendCoinsRecipient &value)
int returnedTarget
Definition: fees.h:131
QGraphicsDropShadowEffect * getShadowEffect()
Definition: guiutil.cpp:146
static CCoinControl * coinControl
void updateFeeMinimizedLabel()
boost::optional< unsigned int > m_confirm_target
Override the default confirmation target if set.
Definition: coincontrol.h:30
void setFocus()
void on_buttonChooseFee_clicked()
SendCoinsRecipient getValue()
CAmount maxTxFee
Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendra...
Definition: validation.cpp:105
UnlockContext requestUnlock()
void coinControlClipboardQuantity()
void coinControlClipboardAfterFee()
void setAddress(const QString &address)
FeeReason reason
Definition: fees.h:129
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
Definition: standard.cpp:402
CTxDestination DecodeDestination(const std::string &str)
Definition: base58.cpp:333
#define SEND_CONFIRM_DELAY
void setupScrollView(const PlatformStyle *platformStyle)
SendCoinsReturn sendCoins(WalletModelTransaction &transaction)
QList< SendCoinsRecipient > getRecipients() const
void updateCoinControlState(CCoinControl &ctrl)
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:336
bool handlePaymentRequest(const SendCoinsRecipient &recipient)
CAmount GetRequiredFee(unsigned int nTxBytes)
Return the minimum required fee taking into account the floating relay fee and user set minimum trans...
Definition: fees.cpp:17
AddressTableModel * getAddressTableModel()
void setBalance(const CAmount &balance, const CAmount &unconfirmedBalance, const CAmount &immatureBalance, const CAmount &watchOnlyBalance, const CAmount &watchUnconfBalance, const CAmount &watchImmatureBalance)
bool validate()
A single entry in the dialog for sending ravens.
Coin Control Features.
Definition: coincontrol.h:17
boost::optional< CFeeRate > m_feerate
Override the default payTxFee if set.
Definition: coincontrol.h:28
int getDisplayUnit() const
Definition: optionsmodel.h:68
CAmount getBalance(const CCoinControl *coinControl=nullptr) const
Definition: walletmodel.cpp:72
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://...
int64_t CAmount
Amount in corbies (Can be negative)
Definition: amount.h:13
int getConfTargetForIndex(int index)
Definition: fees.cpp:1056
CAmount getImmatureBalance() const
Definition: walletmodel.cpp:87
CBlockPolicyEstimator feeEstimator
Definition: validation.cpp:109
static QList< CAmount > payAmounts
QFont getSubLabelFont()
Definition: guiutil.cpp:86
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
Definition: ravenunits.cpp:146
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
Ui::SendCoinsDialog * ui
SendCoinsEntry * addEntry()
void clear()
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
Definition: guiutil.cpp:201
void setAddress(const QString &address)
void coinControlClipboardChange()
void setClientModel(ClientModel *clientModel)
void setClipboard(const QString &str)
Definition: guiutil.cpp:945
ClientModel * clientModel
#define STRING_LABEL_COLOR
Definition: guiconstants.h:90
void SetNull()
Definition: coincontrol.h:46
CTxDestination destChange
Definition: coincontrol.h:20
int getDefaultConfirmTarget() const
CAmount getWatchUnconfirmedBalance() const
QString labelForAddress(const QString &address) const
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl &coinControl)
WalletModel * model
Dialog for sending ravens.
void coinControlChangeEdited(const QString &)
static void updateLabels(WalletModel *, QDialog *)
int getIndexForConfTarget(int target)
Definition: fees.cpp:1066
Model for Raven network client.
Definition: clientmodel.h:39
void coinControlUpdateLabels()
void UnSelectAll()
Definition: coincontrol.h:106
void setModel(WalletModel *model)
bool getCoinControlFeatures() const
Definition: optionsmodel.h:71
bool isClear()
Return whether the entry is still empty and unedited.
void minimizeFeeSection(bool fMinimize)
void coinControlClipboardLowOutput()
void updateFeeSectionControls()
void setupCoinControl(const PlatformStyle *platformStyle)
void setModel(WalletModel *model)
static bool fSubtractFeeFromAmount
QColor WidgetBackGroundColor() const
#define ASYMP_UTF8
void setupFeeControl(const PlatformStyle *platformStyle)
const CChainParams & Params()
Return the currently selected parameters.
Interface to Raven wallet from Qt view code.
Definition: walletmodel.h:165
CAmount getWatchBalance() const
Definition: walletmodel.cpp:97
CAmount getUnconfirmedBalance() const
Definition: walletmodel.cpp:82
static QString formatHtmlWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as HTML string (with unit)
Definition: ravenunits.cpp:156
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 satoshis per kilobyte: CAmount / kB.
Definition: feerate.h:20
Data model for a walletmodel transaction.
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
Definition: ravenunits.cpp:18
void coinControlClipboardBytes()
QFont getTopLabelFont(int weight, int pxsize)
Definition: guiutil.cpp:122
Unit
Raven units.
Definition: ravenunits.h:63
bool fSubtractFeeFromAmount
Definition: walletmodel.h:64
CAmount getWatchImmatureBalance() const
const PlatformStyle * platformStyle
CAmount getTotalTransactionAmount() const
QString formatNiceTimeOffset(qint64 secs)
Definition: guiutil.cpp:1029
void coinControlClipboardAmount()
void on_buttonMinimizeFee_clicked()
QFont getSubLabelFontBolded()
Definition: guiutil.cpp:98
void pasteEntry(const SendCoinsRecipient &rv)
QAbstractButton * yesButton
void message(const QString &title, const QString &message, unsigned int style)
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: fees.cpp:23
CAmount GetFeePerK() const
Return the fee in satoshis for a size of 1000 bytes.
Definition: feerate.h:42
bool getImagesOnButtons() const
Definition: platformstyle.h:24
void coinControlButtonClicked()
void coinControlClipboardFee()
OptionsModel * getOptionsModel()
bool IsSpendable(const CTxDestination &dest) const
void coinControlChangeChecked(int)