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