Bitcoin ABC  0.28.12
P2P Digital Currency
coincontroldialog.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 #if defined(HAVE_CONFIG_H)
6 #include <config/bitcoin-config.h>
7 #endif
8 
9 #include <qt/coincontroldialog.h>
10 #include <qt/forms/ui_coincontroldialog.h>
11 
12 #include <cashaddrenc.h>
13 #include <interfaces/node.h>
14 #include <key_io.h>
15 #include <policy/policy.h>
16 #include <qt/addresstablemodel.h>
17 #include <qt/bitcoinunits.h>
18 #include <qt/guiutil.h>
19 #include <qt/optionsmodel.h>
20 #include <qt/platformstyle.h>
21 #include <qt/walletmodel.h>
22 #include <wallet/coincontrol.h>
23 #include <wallet/wallet.h>
24 
25 #include <variant>
26 
27 #include <QApplication>
28 #include <QCheckBox>
29 #include <QCursor>
30 #include <QDialogButtonBox>
31 #include <QFlags>
32 #include <QIcon>
33 #include <QSettings>
34 #include <QTreeWidget>
35 
36 QList<Amount> CoinControlDialog::payAmounts;
38 
39 bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const {
40  int column = treeWidget()->sortColumn();
41  if (column == CoinControlDialog::COLUMN_AMOUNT ||
44  return data(column, Qt::UserRole).toLongLong() <
45  other.data(column, Qt::UserRole).toLongLong();
46  }
47  return QTreeWidgetItem::operator<(other);
48 }
49 
51  WalletModel *_model,
52  const PlatformStyle *_platformStyle,
53  QWidget *parent)
54  : QDialog(parent), ui(new Ui::CoinControlDialog),
55  m_coin_control(coin_control), model(_model),
56  platformStyle(_platformStyle) {
57  ui->setupUi(this);
58 
59  // context menu actions
60  QAction *copyAddressAction = new QAction(tr("Copy address"), this);
61  QAction *copyLabelAction = new QAction(tr("Copy label"), this);
62  QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
63  // we need to enable/disable this
64  copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this);
65  // we need to enable/disable this
66  lockAction = new QAction(tr("Lock unspent"), this);
67  // we need to enable/disable this
68  unlockAction = new QAction(tr("Unlock unspent"), this);
69 
70  // context menu
71  contextMenu = new QMenu(this);
72  contextMenu->addAction(copyAddressAction);
73  contextMenu->addAction(copyLabelAction);
74  contextMenu->addAction(copyAmountAction);
76  contextMenu->addSeparator();
77  contextMenu->addAction(lockAction);
78  contextMenu->addAction(unlockAction);
79 
80  // context menu signals
81  connect(ui->treeWidget, &QWidget::customContextMenuRequested, this,
83  connect(copyAddressAction, &QAction::triggered, this,
85  connect(copyLabelAction, &QAction::triggered, this,
87  connect(copyAmountAction, &QAction::triggered, this,
89  connect(copyTransactionHashAction, &QAction::triggered, this,
91  connect(lockAction, &QAction::triggered, this,
93  connect(unlockAction, &QAction::triggered, this,
95 
96  // clipboard actions
97  QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
98  QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
99  QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
100  QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
101  QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
102  QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
103  QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
104 
105  connect(clipboardQuantityAction, &QAction::triggered, this,
107  connect(clipboardAmountAction, &QAction::triggered, this,
109  connect(clipboardFeeAction, &QAction::triggered, this,
111  connect(clipboardAfterFeeAction, &QAction::triggered, this,
113  connect(clipboardBytesAction, &QAction::triggered, this,
115  connect(clipboardLowOutputAction, &QAction::triggered, this,
117  connect(clipboardChangeAction, &QAction::triggered, this,
119 
120  ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
121  ui->labelCoinControlAmount->addAction(clipboardAmountAction);
122  ui->labelCoinControlFee->addAction(clipboardFeeAction);
123  ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
124  ui->labelCoinControlBytes->addAction(clipboardBytesAction);
125  ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
126  ui->labelCoinControlChange->addAction(clipboardChangeAction);
127 
128  // toggle tree/list mode
129  connect(ui->radioTreeMode, &QRadioButton::toggled, this,
131  connect(ui->radioListMode, &QRadioButton::toggled, this,
133 
134  // click on checkbox
135  connect(ui->treeWidget, &QTreeWidget::itemChanged, this,
137 
138  // click on header
139  ui->treeWidget->header()->setSectionsClickable(true);
140  connect(ui->treeWidget->header(), &QHeaderView::sectionClicked, this,
142 
143  // ok button
144  connect(ui->buttonBox, &QDialogButtonBox::clicked, this,
146 
147  // (un)select all
148  connect(ui->pushButtonSelectAll, &QPushButton::clicked, this,
150 
151  ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84);
152  ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 110);
153  ui->treeWidget->setColumnWidth(COLUMN_LABEL, 190);
154  ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 320);
155  ui->treeWidget->setColumnWidth(COLUMN_DATE, 130);
156  ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 110);
157 
158  // default view is sorted by amount desc
159  sortView(COLUMN_AMOUNT, Qt::DescendingOrder);
160 
161  // restore list mode and sortorder as a convenience feature
162  QSettings settings;
163  if (settings.contains("nCoinControlMode") &&
164  !settings.value("nCoinControlMode").toBool()) {
165  ui->radioTreeMode->click();
166  }
167  if (settings.contains("nCoinControlSortColumn") &&
168  settings.contains("nCoinControlSortOrder")) {
169  sortView(settings.value("nCoinControlSortColumn").toInt(),
170  (static_cast<Qt::SortOrder>(
171  settings.value("nCoinControlSortOrder").toInt())));
172  }
173 
175 
176  if (_model->getOptionsModel() && _model->getAddressTableModel()) {
177  updateView();
180  }
181 }
182 
184  QSettings settings;
185  settings.setValue("nCoinControlMode", ui->radioListMode->isChecked());
186  settings.setValue("nCoinControlSortColumn", sortColumn);
187  settings.setValue("nCoinControlSortOrder", (int)sortOrder);
188 
189  delete ui;
190 }
191 
192 // ok button
193 void CoinControlDialog::buttonBoxClicked(QAbstractButton *button) {
194  if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) {
195  // closes the dialog
196  done(QDialog::Accepted);
197  }
198 }
199 
200 // (un)select all
202  Qt::CheckState state = Qt::Checked;
203  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) {
204  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) !=
205  Qt::Unchecked) {
206  state = Qt::Unchecked;
207  break;
208  }
209  }
210  ui->treeWidget->setEnabled(false);
211  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) {
212  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) !=
213  state) {
214  ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX,
215  state);
216  }
217  }
218  ui->treeWidget->setEnabled(true);
219  if (state == Qt::Unchecked) {
220  // just to be sure
222  }
224 }
225 
226 // context menu
227 void CoinControlDialog::showMenu(const QPoint &point) {
228  QTreeWidgetItem *item = ui->treeWidget->itemAt(point);
229  if (item) {
230  contextMenuItem = item;
231 
232  // disable some items (like Copy Transaction ID, lock, unlock) for tree
233  // roots in context menu
234  if (item->data(COLUMN_ADDRESS, TxIdRole).toString().length() == 64) {
235  COutPoint outpoint = buildOutPoint(item);
236 
237  // transaction hash is 64 characters (this means it is a child node,
238  // so it is not a parent node in tree mode)
239  copyTransactionHashAction->setEnabled(true);
240  if (model->wallet().isLockedCoin(outpoint)) {
241  lockAction->setEnabled(false);
242  unlockAction->setEnabled(true);
243  } else {
244  lockAction->setEnabled(true);
245  unlockAction->setEnabled(false);
246  }
247  } else {
248  // this means click on parent node in tree mode -> disable all
249  copyTransactionHashAction->setEnabled(false);
250  lockAction->setEnabled(false);
251  unlockAction->setEnabled(false);
252  }
253 
254  // show context menu
255  contextMenu->exec(QCursor::pos());
256  }
257 }
258 
259 // context menu action: copy amount
263 }
264 
265 // context menu action: copy label
267  if (ui->radioTreeMode->isChecked() &&
268  contextMenuItem->text(COLUMN_LABEL).length() == 0 &&
269  contextMenuItem->parent()) {
271  } else {
273  }
274 }
275 
276 // context menu action: copy address
278  if (ui->radioTreeMode->isChecked() &&
279  contextMenuItem->text(COLUMN_ADDRESS).length() == 0 &&
280  contextMenuItem->parent()) {
282  } else {
284  }
285 }
286 
287 // context menu action: copy transaction id
290  contextMenuItem->data(COLUMN_ADDRESS, TxIdRole).toString());
291 }
292 
293 // context menu action: lock coin
295  if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked) {
296  contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
297  }
298 
300  model->wallet().lockCoin(outpoint);
301  contextMenuItem->setDisabled(true);
302  contextMenuItem->setIcon(
303  COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
305 }
306 
307 // context menu action: unlock coin
310  model->wallet().unlockCoin(outpoint);
311  contextMenuItem->setDisabled(false);
312  contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
314 }
315 
316 // copy label "Quantity" to clipboard
318  GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
319 }
320 
321 // copy label "Amount" to clipboard
323  GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(
324  ui->labelCoinControlAmount->text().indexOf(" ")));
325 }
326 
327 // copy label "Fee" to clipboard
330  ui->labelCoinControlFee->text()
331  .left(ui->labelCoinControlFee->text().indexOf(" "))
332  .replace(ASYMP_UTF8, ""));
333 }
334 
335 // copy label "After fee" to clipboard
338  ui->labelCoinControlAfterFee->text()
339  .left(ui->labelCoinControlAfterFee->text().indexOf(" "))
340  .replace(ASYMP_UTF8, ""));
341 }
342 
343 // copy label "Bytes" to clipboard
346  ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
347 }
348 
349 // copy label "Dust" to clipboard
351  GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
352 }
353 
354 // copy label "Change" to clipboard
357  ui->labelCoinControlChange->text()
358  .left(ui->labelCoinControlChange->text().indexOf(" "))
359  .replace(ASYMP_UTF8, ""));
360 }
361 
362 // treeview: sort
363 void CoinControlDialog::sortView(int column, Qt::SortOrder order) {
364  sortColumn = column;
365  sortOrder = order;
366  ui->treeWidget->sortItems(column, order);
367  ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
368 }
369 
370 // treeview: clicked on header
372  // click on most left column -> do nothing
373  if (logicalIndex == COLUMN_CHECKBOX) {
374  ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
375  } else {
376  if (sortColumn == logicalIndex) {
377  sortOrder =
378  ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder
379  : Qt::AscendingOrder);
380  } else {
381  sortColumn = logicalIndex;
382  // if label or address then default => asc, else default => desc
383  sortOrder =
385  ? Qt::AscendingOrder
386  : Qt::DescendingOrder);
387  }
388 
390  }
391 }
392 
393 // toggle tree mode
395  if (checked && model) {
396  updateView();
397  }
398 }
399 
400 // toggle list mode
402  if (checked && model) {
403  updateView();
404  }
405 }
406 
407 // checkbox clicked by user
408 void CoinControlDialog::viewItemChanged(QTreeWidgetItem *item, int column) {
409  // transaction hash is 64 characters (this means it is a child node, so it
410  // is not a parent node in tree mode)
411  if (column == COLUMN_CHECKBOX &&
412  item->data(COLUMN_ADDRESS, TxIdRole).toString().length() == 64) {
413  COutPoint outpoint = buildOutPoint(item);
414 
415  if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked) {
416  m_coin_control.UnSelect(outpoint);
417  } else if (item->isDisabled()) {
418  // locked (this happens if "check all" through parent node)
419  item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
420  } else {
421  m_coin_control.Select(outpoint);
422  }
423 
424  // selection changed -> update labels
425  if (ui->treeWidget->isEnabled()) {
426  // do not update on every click for (un)select all
428  }
429  }
430 }
431 
432 // shows count of locked unspent outputs
434  std::vector<COutPoint> vOutpts;
435  model->wallet().listLockedCoins(vOutpts);
436  if (vOutpts.size() > 0) {
437  ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size()));
438  ui->labelLocked->setVisible(true);
439  } else {
440  ui->labelLocked->setVisible(false);
441  }
442 }
443 
445  WalletModel *model, QDialog *dialog) {
446  if (!model) {
447  return;
448  }
449 
450  // nPayAmount
451  Amount nPayAmount = Amount::zero();
452  bool fDust = false;
453  for (const Amount &amount : CoinControlDialog::payAmounts) {
454  nPayAmount += amount;
455 
456  if (amount > Amount::zero()) {
457  // Assumes a p2pkh script size
458  CTxOut txout(amount, CScript() << std::vector<uint8_t>(24, 0));
459  fDust |= IsDust(txout, model->node().getDustRelayFee());
460  }
461  }
462 
463  Amount nAmount = Amount::zero();
464  Amount nPayFee = Amount::zero();
465  Amount nAfterFee = Amount::zero();
466  Amount nChange = Amount::zero();
467  unsigned int nBytes = 0;
468  unsigned int nBytesInputs = 0;
469  unsigned int nQuantity = 0;
470 
471  std::vector<COutPoint> vCoinControl;
472  m_coin_control.ListSelected(vCoinControl);
473 
474  size_t i = 0;
475  for (const auto &out : model->wallet().getCoins(vCoinControl)) {
476  if (out.depth_in_main_chain < 0) {
477  continue;
478  }
479 
480  // unselect already spent, very unlikely scenario, this could happen
481  // when selected are spent elsewhere, like rpc or another computer
482  const COutPoint &output = vCoinControl[i++];
483  if (out.is_spent) {
484  m_coin_control.UnSelect(output);
485  continue;
486  }
487 
488  // Quantity
489  nQuantity++;
490 
491  // Amount
492  nAmount += out.txout.nValue;
493 
494  // Bytes
495  CTxDestination address;
496  if (ExtractDestination(out.txout.scriptPubKey, address)) {
497  CPubKey pubkey;
498  PKHash *pkhash = std::get_if<PKHash>(&address);
499  if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey,
500  ToKeyID(*pkhash), pubkey)) {
501  nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
502  } else {
503  // in all error cases, simply assume 148 here
504  nBytesInputs += 148;
505  }
506  } else {
507  nBytesInputs += 148;
508  }
509  }
510 
511  // calculation
512  if (nQuantity > 0) {
513  // Bytes
514  // always assume +1 output for change here
515  nBytes = nBytesInputs +
516  ((CoinControlDialog::payAmounts.size() > 0
517  ? CoinControlDialog::payAmounts.size() + 1
518  : 2) *
519  34) +
520  10;
521 
522  // in the subtract fee from amount case, we can tell if zero change
523  // already and subtract the bytes, so that fee calculation afterwards is
524  // accurate
526  if (nAmount - nPayAmount == Amount::zero()) {
527  nBytes -= 34;
528  }
529  }
530 
531  // Fee
532  nPayFee = model->wallet().getMinimumFee(nBytes, m_coin_control);
533 
534  if (nPayAmount > Amount::zero()) {
535  nChange = nAmount - nPayAmount;
537  nChange -= nPayFee;
538  }
539 
540  // Never create dust outputs; if we would, just add the dust to the
541  // fee.
542  if (nChange > Amount::zero() && nChange < MIN_CHANGE) {
543  // Assumes a p2pkh script size
544  CTxOut txout(nChange, CScript() << std::vector<uint8_t>(24, 0));
545  if (IsDust(txout, model->node().getDustRelayFee())) {
546  nPayFee += nChange;
547  nChange = Amount::zero();
549  // we didn't detect lack of change above
550  nBytes -= 34;
551  }
552  }
553  }
554 
555  if (nChange == Amount::zero() &&
557  nBytes -= 34;
558  }
559  }
560 
561  // after fee
562  nAfterFee = std::max(nAmount - nPayFee, Amount::zero());
563  }
564 
565  // actually update labels
566  int nDisplayUnit = BitcoinUnits::base;
567  if (model && model->getOptionsModel()) {
568  nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
569  }
570 
571  QLabel *l1 = dialog->findChild<QLabel *>("labelCoinControlQuantity");
572  QLabel *l2 = dialog->findChild<QLabel *>("labelCoinControlAmount");
573  QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee");
574  QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee");
575  QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes");
576  QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput");
577  QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange");
578 
579  // enable/disable "dust" and "change"
580  dialog->findChild<QLabel *>("labelCoinControlLowOutputText")
581  ->setEnabled(nPayAmount > Amount::zero());
582  dialog->findChild<QLabel *>("labelCoinControlLowOutput")
583  ->setEnabled(nPayAmount > Amount::zero());
584  dialog->findChild<QLabel *>("labelCoinControlChangeText")
585  ->setEnabled(nPayAmount > Amount::zero());
586  dialog->findChild<QLabel *>("labelCoinControlChange")
587  ->setEnabled(nPayAmount > Amount::zero());
588 
589  // stats
590  // Quantity
591  l1->setText(QString::number(nQuantity));
592  // Amount
593  l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount));
594  // Fee
595  l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee));
596  // After Fee
597  l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee));
598  // Bytes
599  l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes));
600  // Dust
601  l7->setText(fDust ? tr("yes") : tr("no"));
602  // Change
603  l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange));
604  if (nPayFee > Amount::zero()) {
605  l3->setText(ASYMP_UTF8 + l3->text());
606  l4->setText(ASYMP_UTF8 + l4->text());
607  if (nChange > Amount::zero() &&
609  l8->setText(ASYMP_UTF8 + l8->text());
610  }
611  }
612 
613  // turn label red when dust
614  l7->setStyleSheet((fDust) ? "color:red;" : "");
615 
616  // tool tips
617  QString toolTipDust =
618  tr("This label turns red if any recipient receives an amount smaller "
619  "than the current dust threshold.");
620 
621  // how many satoshis the estimated fee can vary per byte we guess wrong
622  double dFeeVary = (nBytes != 0) ? double(nPayFee / SATOSHI) / nBytes : 0;
623 
624  QString toolTip4 =
625  tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
626 
627  l3->setToolTip(toolTip4);
628  l4->setToolTip(toolTip4);
629  l7->setToolTip(toolTipDust);
630  l8->setToolTip(toolTip4);
631  dialog->findChild<QLabel *>("labelCoinControlFeeText")
632  ->setToolTip(l3->toolTip());
633  dialog->findChild<QLabel *>("labelCoinControlAfterFeeText")
634  ->setToolTip(l4->toolTip());
635  dialog->findChild<QLabel *>("labelCoinControlBytesText")
636  ->setToolTip(l5->toolTip());
637  dialog->findChild<QLabel *>("labelCoinControlLowOutputText")
638  ->setToolTip(l7->toolTip());
639  dialog->findChild<QLabel *>("labelCoinControlChangeText")
640  ->setToolTip(l8->toolTip());
641 
642  // Insufficient funds
643  QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds");
644  if (label) {
645  label->setVisible(nChange < Amount::zero());
646  }
647 }
648 
649 COutPoint CoinControlDialog::buildOutPoint(const QTreeWidgetItem *item) {
650  TxId txid;
651  txid.SetHex(item->data(COLUMN_ADDRESS, TxIdRole).toString().toStdString());
652  return COutPoint(txid, item->data(COLUMN_ADDRESS, VOutRole).toUInt());
653 }
654 
657  return;
658  }
659 
660  bool treeMode = ui->radioTreeMode->isChecked();
661 
662  ui->treeWidget->clear();
663  // performance, otherwise updateLabels would be called for every checked
664  // checkbox
665  ui->treeWidget->setEnabled(false);
666  ui->treeWidget->setAlternatingRowColors(!treeMode);
667  QFlags<Qt::ItemFlag> flgCheckbox =
668  Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
669  QFlags<Qt::ItemFlag> flgTristate =
670  Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable |
671  Qt::ItemIsTristate;
672 
673  int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
674 
675  for (const auto &coins : model->wallet().listCoins()) {
676  CCoinControlWidgetItem *itemWalletAddress{nullptr};
677  QString sWalletAddress = QString::fromStdString(
678  EncodeCashAddr(coins.first, model->getChainParams()));
679  QString sWalletLabel =
680  model->getAddressTableModel()->labelForAddress(sWalletAddress);
681  if (sWalletLabel.isEmpty()) {
682  sWalletLabel = tr("(no label)");
683  }
684 
685  if (treeMode) {
686  // wallet address
687  itemWalletAddress = new CCoinControlWidgetItem(ui->treeWidget);
688 
689  itemWalletAddress->setFlags(flgTristate);
690  itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
691 
692  // label
693  itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel);
694 
695  // address
696  itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress);
697  }
698 
699  Amount nSum = Amount::zero();
700  int nChildren = 0;
701  for (const auto &outpair : coins.second) {
702  const COutPoint &output = std::get<0>(outpair);
703  const interfaces::WalletTxOut &out = std::get<1>(outpair);
704  nSum += out.txout.nValue;
705  nChildren++;
706 
707  CCoinControlWidgetItem *itemOutput;
708  if (treeMode) {
709  itemOutput = new CCoinControlWidgetItem(itemWalletAddress);
710  } else {
711  itemOutput = new CCoinControlWidgetItem(ui->treeWidget);
712  }
713  itemOutput->setFlags(flgCheckbox);
714  itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
715 
716  // address
717  CTxDestination outputAddress;
718  QString sAddress = "";
719  if (ExtractDestination(out.txout.scriptPubKey, outputAddress)) {
720  sAddress = QString::fromStdString(
721  EncodeCashAddr(outputAddress, model->getChainParams()));
722 
723  // if listMode or change => show bitcoin address. In tree mode,
724  // address is not shown again for direct wallet address outputs
725  if (!treeMode || (!(sAddress == sWalletAddress))) {
726  itemOutput->setText(COLUMN_ADDRESS, sAddress);
727  }
728  }
729 
730  // label
731  if (!(sAddress == sWalletAddress)) {
732  // change tooltip from where the change comes from
733  itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)")
734  .arg(sWalletLabel)
735  .arg(sWalletAddress));
736  itemOutput->setText(COLUMN_LABEL, tr("(change)"));
737  } else if (!treeMode) {
738  QString sLabel =
740  if (sLabel.isEmpty()) {
741  sLabel = tr("(no label)");
742  }
743  itemOutput->setText(COLUMN_LABEL, sLabel);
744  }
745 
746  // amount
747  itemOutput->setText(
749  BitcoinUnits::format(nDisplayUnit, out.txout.nValue));
750  // padding so that sorting works correctly
751  itemOutput->setData(
752  COLUMN_AMOUNT, Qt::UserRole,
753  QVariant(qlonglong(out.txout.nValue / SATOSHI)));
754 
755  // date
756  itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.time));
757  itemOutput->setData(COLUMN_DATE, Qt::UserRole,
758  QVariant((qlonglong)out.time));
759 
760  // confirmations
761  itemOutput->setText(COLUMN_CONFIRMATIONS,
762  QString::number(out.depth_in_main_chain));
763  itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole,
764  QVariant((qlonglong)out.depth_in_main_chain));
765 
766  // transaction id
767  itemOutput->setData(
769  QString::fromStdString(output.GetTxId().GetHex()));
770 
771  // vout index
772  itemOutput->setData(COLUMN_ADDRESS, VOutRole, output.GetN());
773 
774  // disable locked coins
775  if (model->wallet().isLockedCoin(output)) {
776  // just to be sure
777  m_coin_control.UnSelect(output);
778  itemOutput->setDisabled(true);
779  itemOutput->setIcon(
781  platformStyle->SingleColorIcon(":/icons/lock_closed"));
782  }
783 
784  // set checkbox
785  if (m_coin_control.IsSelected(output)) {
786  itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
787  }
788  }
789 
790  // amount
791  if (treeMode) {
792  itemWalletAddress->setText(COLUMN_CHECKBOX,
793  "(" + QString::number(nChildren) + ")");
794  itemWalletAddress->setText(
795  COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
796  itemWalletAddress->setData(COLUMN_AMOUNT, Qt::UserRole,
797  QVariant(qlonglong(nSum / SATOSHI)));
798  }
799  }
800 
801  // expand all partially selected
802  if (treeMode) {
803  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) {
804  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) ==
805  Qt::PartiallyChecked) {
806  ui->treeWidget->topLevelItem(i)->setExpanded(true);
807  }
808  }
809  }
810 
811  // sort view
813  ui->treeWidget->setEnabled(true);
814 }
static constexpr Amount SATOSHI
Definition: amount.h:143
std::string EncodeCashAddr(const CTxDestination &dst, const CChainParams &params)
Definition: cashaddrenc.cpp:90
QString labelForAddress(const QString &address) const
Look up label for address in address book, if not found return empty string.
static QString formatWithUnit(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
static QString removeSpaces(QString text)
Definition: bitcoinunits.h:98
static QString format(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD, bool justify=false)
Format as string.
Coin Control Features.
Definition: coincontrol.h:21
bool IsSelected(const COutPoint &output) const
Definition: coincontrol.h:56
void UnSelectAll()
Definition: coincontrol.h:64
void Select(const COutPoint &output)
Definition: coincontrol.h:60
void UnSelect(const COutPoint &output)
Definition: coincontrol.h:62
void ListSelected(std::vector< COutPoint > &vOutpoints) const
Definition: coincontrol.h:66
bool operator<(const QTreeWidgetItem &other) const override
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:20
uint32_t GetN() const
Definition: transaction.h:36
const TxId & GetTxId() const
Definition: transaction.h:35
An encapsulated public key.
Definition: pubkey.h:31
bool IsCompressed() const
Check whether this is a compressed public key.
Definition: pubkey.h:157
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:431
An output of a transaction.
Definition: transaction.h:128
CScript scriptPubKey
Definition: transaction.h:131
Amount nValue
Definition: transaction.h:130
static COutPoint buildOutPoint(const QTreeWidgetItem *item)
QTreeWidgetItem * contextMenuItem
WalletModel * model
static QList< Amount > payAmounts
friend class CCoinControlWidgetItem
static void updateLabels(CCoinControl &m_coin_control, WalletModel *, QDialog *)
const PlatformStyle * platformStyle
CCoinControl & m_coin_control
QAction * copyTransactionHashAction
Ui::CoinControlDialog * ui
void sortView(int, Qt::SortOrder)
CoinControlDialog(CCoinControl &coin_control, WalletModel *model, const PlatformStyle *platformStyle, QWidget *parent=nullptr)
void showMenu(const QPoint &)
void viewItemChanged(QTreeWidgetItem *, int)
Qt::SortOrder sortOrder
static bool fSubtractFeeFromAmount
void buttonBoxClicked(QAbstractButton *)
int getDisplayUnit() const
Definition: optionsmodel.h:97
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:47
const CChainParams & getChainParams() const
AddressTableModel * getAddressTableModel()
OptionsModel * getOptionsModel()
interfaces::Wallet & wallet() const
Definition: walletmodel.h:150
interfaces::Node & node() const
Definition: walletmodel.h:149
const uint8_t * data() const
Definition: uint256.h:80
void SetHex(const char *psz)
Definition: uint256.cpp:24
std::string GetHex() const
Definition: uint256.cpp:16
virtual CFeeRate getDustRelayFee()=0
Get dust relay fee.
virtual CoinsList listCoins()=0
virtual bool getPubKey(const CScript &script, const CKeyID &address, CPubKey &pub_key)=0
Get public key.
virtual void unlockCoin(const COutPoint &output)=0
Unlock coin.
virtual std::vector< WalletTxOut > getCoins(const std::vector< COutPoint > &outputs)=0
Return wallet transaction output information.
virtual bool isLockedCoin(const COutPoint &output)=0
Return whether coin is locked.
virtual void listLockedCoins(std::vector< COutPoint > &outputs)=0
List locked coins.
virtual void lockCoin(const COutPoint &output)=0
Lock coin.
virtual Amount getMinimumFee(unsigned int tx_bytes, const CCoinControl &coin_control)=0
Get minimum fee.
#define ASYMP_UTF8
static constexpr Amount MIN_CHANGE
target minimum change amount
Definition: coinselection.h:13
void handleCloseWindowShortcut(QWidget *w)
Definition: guiutil.cpp:404
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:75
void setClipboard(const QString &str)
Definition: guiutil.cpp:774
bool operator<(const CNetAddr &a, const CNetAddr &b)
Definition: netaddress.cpp:681
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
Definition: policy.cpp:34
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:158
CKeyID ToKeyID(const PKHash &key_hash)
Definition: standard.cpp:25
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:85
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32
A TxId is the identifier of a transaction.
Definition: txid.h:14
Wallet transaction output.
Definition: wallet.h:396