Bitcoin ABC 0.30.5
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
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>
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
38
39bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const {
40 int column = treeWidget()->sortColumn();
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
193void 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
227void 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
299 COutPoint outpoint = buildOutPoint(contextMenuItem);
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
309 COutPoint outpoint = buildOutPoint(contextMenuItem);
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
363void 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
408void 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 +
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
649COutPoint 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 encapsulated public key.
Definition: pubkey.h:31
bool IsCompressed() const
Check whether this is a compressed public key.
Definition: pubkey.h:154
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
interfaces::Node & node() const
Definition: walletmodel.h:149
const CChainParams & getChainParams() const
AddressTableModel * getAddressTableModel()
OptionsModel * getOptionsModel()
interfaces::Wallet & wallet() const
Definition: walletmodel.h:150
void SetHex(const char *psz)
Definition: uint256.cpp:24
const uint8_t * data() const
Definition: uint256.h:82
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 std::vector< WalletTxOut > getCoins(const std::vector< COutPoint > &outputs)=0
Return wallet transaction output information.
virtual void unlockCoin(const COutPoint &output)=0
Unlock coin.
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:407
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:78
void setClipboard(const QString &str)
Definition: guiutil.cpp:776
bool operator<(const CNetAddr &a, const CNetAddr &b)
Definition: netaddress.cpp:679
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