PIVX Core  5.6.99
P2P Digital Currency
coincontroldialog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2014 The Bitcoin developers
2 // Copyright (c) 2014-2015 The Dash developers
3 // Copyright (c) 2015-2021 The PIVX Core developers
4 // Distributed under the MIT/X11 software license, see the accompanying
5 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 
7 #include "coincontroldialog.h"
8 #include "ui_coincontroldialog.h"
9 
10 #include "addresstablemodel.h"
11 #include "bitcoinunits.h"
12 #include "coincontrol.h"
13 #include "guiutil.h"
14 #include "optionsmodel.h"
15 #include "policy/policy.h"
16 #include "txmempool.h"
17 #include "wallet/fees.h"
18 #include "wallet/wallet.h"
19 #include "walletmodel.h"
20 
21 #include "qtutils.h"
22 
23 #include <QApplication>
24 #include <QCheckBox>
25 #include <QCursor>
26 #include <QDialogButtonBox>
27 #include <QFlags>
28 #include <QIcon>
29 #include <QSettings>
30 #include <QTreeWidget>
31 
32 
33 bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const {
34  int column = treeWidget()->sortColumn();
36  return data(column, Qt::UserRole).toLongLong() < other.data(column, Qt::UserRole).toLongLong();
37  return QTreeWidgetItem::operator<(other);
38 }
39 
40 
41 CoinControlDialog::CoinControlDialog(QWidget* parent, bool _forDelegation) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint),
42  ui(new Ui::CoinControlDialog),
43  model(0),
44  forDelegation(_forDelegation)
45 {
46  coinControl = new CCoinControl();
47  ui->setupUi(this);
48 
49  /* Open CSS when configured */
50  this->setStyleSheet(GUIUtil::loadStyleSheet());
51  setCssProperty({ui->frameContainer,
52  ui->layoutAmount,
53  ui->layoutAfter,
54  ui->layoutBytes,
55  ui->layoutChange,
56  ui->layoutDust,
57  ui->layoutFee,
58  ui->layoutQuantity
59  }, "container-border-purple");
60 
61  // Title
62  ui->labelTitle->setProperty("cssClass", "text-title-dialog");
63 
64  // Label Style
65  setCssProperty({ui->labelCoinControlAfterFeeText,
66  ui->labelCoinControlAmountText,
67  ui->labelCoinControlBytesText,
68  ui->labelCoinControlChangeText,
69  ui->labelCoinControlLowOutputText,
70  ui->labelCoinControlFeeText,
71  ui->labelCoinControlQuantityText
72  }, "text-main-purple");
73 
74  // Value Style
75  setCssProperty({ui->labelCoinControlAfterFee,
76  ui->labelCoinControlAmount,
77  ui->labelCoinControlBytes,
78  ui->labelCoinControlChange,
79  ui->labelCoinControlLowOutput,
80  ui->labelCoinControlFee,
81  ui->labelCoinControlQuantity
82  }, "text-main-purple");
83 
84  ui->groupBox_2->setProperty("cssClass", "group-box");
85  ui->treeWidget->setProperty("cssClass", "table-tree");
86  ui->labelLocked->setProperty("cssClass", "text-main-purple");
87 
88  // Buttons
89  setCssProperty({ui->pushButtonSelectAll, ui->pushButtonToggleLock}, "btn-check");
90  ui->pushButtonOk->setProperty("cssClass", "btn-primary");
91 
92  // context menu actions
93  QAction* copyAddressAction = new QAction(tr("Copy address"), this);
94  QAction* copyLabelAction = new QAction(tr("Copy label"), this);
95  QAction* copyAmountAction = new QAction(tr("Copy amount"), this);
96  copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this); // we need to enable/disable this
97  lockAction = new QAction(tr("Lock unspent"), this); // we need to enable/disable this
98  unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this
99 
100  // context menu
101  contextMenu = new QMenu();
102  contextMenu->addAction(copyAddressAction);
103  contextMenu->addAction(copyLabelAction);
104  contextMenu->addAction(copyAmountAction);
106  contextMenu->addSeparator();
107  contextMenu->addAction(lockAction);
108  contextMenu->addAction(unlockAction);
109 
110  // context menu signals
111  connect(ui->treeWidget, &QWidget::customContextMenuRequested, this, &CoinControlDialog::showMenu);
112  connect(copyAddressAction, &QAction::triggered, this, &CoinControlDialog::copyAddress);
113  connect(copyLabelAction, &QAction::triggered, this, &CoinControlDialog::copyLabel);
114  connect(copyAmountAction, &QAction::triggered, this, &CoinControlDialog::copyAmount);
115  connect(copyTransactionHashAction, &QAction::triggered, this, &CoinControlDialog::copyTransactionHash);
116  connect(lockAction, &QAction::triggered, this, &CoinControlDialog::lockCoin);
117  connect(unlockAction, &QAction::triggered, this, &CoinControlDialog::unlockCoin);
118 
119  // clipboard actions
121  ui->pushButtonAmount,
122  ui->pushButtonQuantity,
123  ui->pushButtonFee,
124  ui->pushButtonAlterFee,
125  ui->pushButtonBytes,
126  ui->pushButtonChange,
127  ui->pushButtonDust
128  }, "ic-copy-big"
129  );
130 
131  connect(ui->pushButtonQuantity, &QPushButton::clicked, this, &CoinControlDialog::clipboardQuantity);
132  connect(ui->pushButtonAmount, &QPushButton::clicked, this, &CoinControlDialog::clipboardAmount);
133  connect(ui->pushButtonFee, &QPushButton::clicked, this, &CoinControlDialog::clipboardFee);
134  connect(ui->pushButtonAlterFee, &QPushButton::clicked, this, &CoinControlDialog::clipboardAfterFee);
135  connect(ui->pushButtonBytes, &QPushButton::clicked, this, &CoinControlDialog::clipboardBytes);
136  connect(ui->pushButtonDust, &QPushButton::clicked, this, &CoinControlDialog::clipboardLowOutput);
137  connect(ui->pushButtonChange, &QPushButton::clicked, this, &CoinControlDialog::clipboardChange);
138 
139  if (ui->pushButtonSelectAll->isChecked()) {
140  ui->pushButtonSelectAll->setText(tr("Unselect all"));
141  } else {
142  ui->pushButtonSelectAll->setText(tr("Select all"));
143  }
144 
145  // toggle tree/list mode
146  connect(ui->radioTreeMode, &QRadioButton::toggled, this, &CoinControlDialog::radioTreeMode);
147  connect(ui->radioListMode, &QRadioButton::toggled, this, &CoinControlDialog::radioListMode);
148 
149  // click on checkbox
150  connect(ui->treeWidget, &QTreeWidget::itemChanged, this, &CoinControlDialog::viewItemChanged);
151 
152  // click on header
153  ui->treeWidget->header()->setSectionsClickable(true);
154  connect(ui->treeWidget->header(), &QHeaderView::sectionClicked, this, &CoinControlDialog::headerSectionClicked);
155 
156  // ok button
157  connect(ui->pushButtonOk, &QPushButton::clicked, this, &CoinControlDialog::accept);
158 
159  // (un)select all
160  connect(ui->pushButtonSelectAll, &QPushButton::clicked, this, &CoinControlDialog::buttonSelectAllClicked);
161 
162  // Toggle lock state
163  connect(ui->pushButtonToggleLock, &QPushButton::clicked, this, &CoinControlDialog::buttonToggleLockClicked);
164 
165  ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, colCheckBoxWidth_treeMode);
166  ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 110);
167  ui->treeWidget->setColumnWidth(COLUMN_LABEL, 160);
168  ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 310);
169  ui->treeWidget->setColumnWidth(COLUMN_DATE, 145);
170  ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 65);
171  ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transacton hash in this column, but dont show it
172  ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but dont show it
173 
174  ui->treeWidget->header()->setStretchLastSection(true);
175  // default view is sorted by amount desc
176  sortView(COLUMN_AMOUNT, Qt::DescendingOrder);
177 
178  // restore list mode and sortorder as a convenience feature
179  QSettings settings;
180  if (settings.contains("nCoinControlMode") && !settings.value("nCoinControlMode").toBool()) {
181  ui->radioTreeMode->setChecked(true);
182  ui->treeWidget->setRootIsDecorated(true);
183  ui->radioTreeMode->click();
184  }else{
185  ui->radioListMode->setChecked(true);
186  ui->treeWidget->setRootIsDecorated(false);
187  }
188  if (settings.contains("nCoinControlSortColumn") && settings.contains("nCoinControlSortOrder"))
189  sortView(settings.value("nCoinControlSortColumn").toInt(), (static_cast<Qt::SortOrder>(settings.value("nCoinControlSortOrder").toInt())));
190 }
191 
193 {
194  QSettings settings;
195  settings.setValue("nCoinControlMode", ui->radioListMode->isChecked());
196  settings.setValue("nCoinControlSortColumn", sortColumn);
197  settings.setValue("nCoinControlSortOrder", (int)sortOrder);
198 
199  delete ui;
200  delete coinControl;
201 }
202 
204 {
205  this->model = _model;
206 
208  updateView();
210  updateLabels();
211  }
212 }
213 
214 // (un)select all
216 {
217  // "Select all": if some entry is unchecked, then check it
218  // "Unselect all": if some entry is checked, then uncheck it
219  const bool fSelectAll = ui->pushButtonSelectAll->isChecked();
220  Qt::CheckState wantedState = fSelectAll ? Qt::Checked : Qt::Unchecked;
221  ui->treeWidget->setEnabled(false);
222  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
223  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != wantedState)
224  ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, wantedState);
225  ui->treeWidget->setEnabled(true);
226  if (!fSelectAll)
227  coinControl->UnSelectAll(); // just to be sure
228  updateLabels();
229 }
230 
231 void CoinControlDialog::toggleItemLock(QTreeWidgetItem* item)
232 {
233  uint256 hash = uint256S(item->text(COLUMN_TXHASH).toStdString());
234  int n = item->text(COLUMN_VOUT_INDEX).toUInt();
235  if (model->isLockedCoin(hash, n, fSelectTransparent)) {
237  item->setDisabled(false);
238  // restore cold-stake snowflake icon for P2CS which were previously locked
239  if (item->data(COLUMN_CHECKBOX, Qt::UserRole) == QString("Delegated"))
240  item->setIcon(COLUMN_CHECKBOX, QIcon("://ic-check-cold-staking-off"));
241  else
242  item->setIcon(COLUMN_CHECKBOX, QIcon());
243  } else {
244  model->lockCoin(hash, n, fSelectTransparent);
245  item->setDisabled(true);
246  item->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed"));
247  }
249 }
250 
252 {
253  QTreeWidgetItem* item;
254  bool treemode = ui->treeWidget->rootIsDecorated();
255  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) {
256  item = ui->treeWidget->topLevelItem(i);
257  if (treemode) {
258  auto subItems = item->takeChildren();
259  for (auto j : subItems) {
260  toggleItemLock(j);
261  }
262  } else {
263  toggleItemLock(item);
264  }
265  }
266 }
267 
268 // Toggle lock state
270 {
271  ui->treeWidget->setEnabled(false);
272  toggleCoinLock();
273  ui->treeWidget->setEnabled(true);
274  updateView();
275 }
276 
277 // context menu
278 void CoinControlDialog::showMenu(const QPoint& point)
279 {
280  QTreeWidgetItem* item = ui->treeWidget->itemAt(point);
281  if (item) {
282  contextMenuItem = item;
283 
284  // disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu
285  if (item->text(COLUMN_TXHASH).length() == 64) { // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode)
286  copyTransactionHashAction->setEnabled(true);
287  if (model->isLockedCoin(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt(), fSelectTransparent)) {
288  lockAction->setEnabled(false);
289  unlockAction->setEnabled(true);
290  } else {
291  lockAction->setEnabled(true);
292  unlockAction->setEnabled(false);
293  }
294  } else { // this means click on parent node in tree mode -> disable all
295  copyTransactionHashAction->setEnabled(false);
296  lockAction->setEnabled(false);
297  unlockAction->setEnabled(false);
298  }
299 
300  // show context menu
301  contextMenu->exec(QCursor::pos());
302  }
303 }
304 
305 // context menu action: copy amount
307 {
309 }
310 
311 // context menu action: copy label
313 {
314  if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_LABEL).length() == 0 && contextMenuItem->parent())
316  else
318 }
319 
320 // context menu action: copy address
322 {
323  if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_ADDRESS).length() == 0 && contextMenuItem->parent())
325  else
327 }
328 
329 // context menu action: copy transaction id
331 {
333 }
334 
335 // context menu action: lock coin
337 {
338  if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked)
339  contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
340  uint256 txHash = uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString());
341  int n = contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt();
342  model->lockCoin(txHash, n, fSelectTransparent);
343  contextMenuItem->setDisabled(true);
344  contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed"));
346 }
347 
348 // context menu action: unlock coin
350 {
351  uint256 txHash = uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString());
352  int n = contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt();
353  model->unlockCoin(txHash, n, fSelectTransparent);
354  contextMenuItem->setDisabled(false);
355  // restore cold-stake snowflake icon for P2CS which were previously locked
356  if (contextMenuItem->data(COLUMN_CHECKBOX, Qt::UserRole) == QString("Delegated"))
357  contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon("://ic-check-cold-staking-off"));
358  else
359  contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
361 }
362 
363 // copy label "Quantity" to clipboard
365 {
366  GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
367  inform(tr("Quantity Copied"));
368 }
369 
370 // copy label "Amount" to clipboard
372 {
373  GUIUtil::setClipboard(BitcoinUnits::removeSpaces(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" "))));
374  inform(tr("Amount Copied"));
375 }
376 
377 // copy label "Fee" to clipboard
379 {
380  GUIUtil::setClipboard(BitcoinUnits::removeSpaces(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace("~", "")));
381  inform(tr("Fee Copied"));
382 }
383 
384 // copy label "After fee" to clipboard
386 {
387  GUIUtil::setClipboard(BitcoinUnits::removeSpaces(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace("~", "")));
388  inform(tr("After Fee Copied"));
389 }
390 
391 // copy label "Bytes" to clipboard
393 {
394  GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace("~", ""));
395  inform(tr("Bytes Copied"));
396 }
397 
398 // copy label "Dust" to clipboard
400 {
401  GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
402  inform(tr("Dust Copied"));
403 }
404 
405 // copy label "Change" to clipboard
407 {
408  GUIUtil::setClipboard(BitcoinUnits::removeSpaces(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace("~", "")));
409  inform(tr("Change Copied"));
410 }
411 
412 // treeview: sort
413 void CoinControlDialog::sortView(int column, Qt::SortOrder order)
414 {
415  sortColumn = column;
416  sortOrder = order;
417  ui->treeWidget->sortItems(column, order);
418  ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
419 }
420 
421 // treeview: clicked on header
423 {
424  if (logicalIndex == COLUMN_CHECKBOX) { // click on most left column -> do nothing
425  ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
426  } else {
427  if (sortColumn == logicalIndex)
428  sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder);
429  else {
430  sortColumn = logicalIndex;
431  sortOrder = ((sortColumn == COLUMN_LABEL || sortColumn == COLUMN_ADDRESS) ? Qt::AscendingOrder : Qt::DescendingOrder); // if label or address then default => asc, else default => desc
432  }
433 
435  }
436 }
437 
438 // toggle tree mode
440 {
441  if (checked && model)
442  updateView();
443 }
444 
445 // toggle list mode
447 {
448  if (checked && model)
449  updateView();
450 }
451 
452 // checkbox clicked by user
453 void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
454 {
455  if (column == COLUMN_CHECKBOX && item->text(COLUMN_TXHASH).length() == 64) { // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode)
456  BaseOutPoint outpt(uint256S(item->text(COLUMN_TXHASH).toStdString()),
457  item->text(COLUMN_VOUT_INDEX).toUInt(),
459  if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked)
460  coinControl->UnSelect(outpt);
461  else if (item->isDisabled()) // locked (this happens if "check all" through parent node)
462  item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
463  else {
464  const CAmount value = static_cast<CAmount>(item->data(CoinControlDialog::COLUMN_AMOUNT, Qt::UserRole).toLongLong());
465  bool isP2CS = item->data(COLUMN_CHECKBOX, Qt::UserRole) == QString("Delegated");
466  coinControl->Select(outpt, value, isP2CS);
467  }
468 
469  // selection changed -> update labels
470  if (ui->treeWidget->isEnabled()) { // do not update on every click for (un)select all
471  updateLabels();
472  }
473  }
474 }
475 
476 // shows count of locked unspent outputs
478 {
479  int nLocked = fSelectTransparent ? model->listLockedCoins().size() : model->listLockedNotes().size();
480  if (nLocked > 0) {
481  ui->labelLocked->setText(tr("(%1 locked)").arg(nLocked));
482  ui->labelLocked->setVisible(true);
483  } else
484  ui->labelLocked->setVisible(false);
485 }
486 
487 // serialized int size
488 static int GetCompactSize(uint64_t nSize)
489 {
490  if (nSize < 253) {
491  return 1;
492  } else if (nSize <= std::numeric_limits<unsigned short>::max()) {
493  return 3;
494  } else if (nSize <= std::numeric_limits<unsigned int>::max()) {
495  return 5;
496  }
497  return 9;
498 }
499 
501 {
502  TotalAmounts t;
503 
504  std::vector<OutPointWrapper> vCoinControl;
505  coinControl->ListSelected(vCoinControl);
506 
507  for (const OutPointWrapper& out : vCoinControl) {
508  // Quantity
509  t.nQuantity++;
510  // Amount
511  t.nAmount += out.value;
512  // Bytes
513  t.nBytes += (fSelectTransparent ? (CTXIN_SPEND_DUST_SIZE + (out.isP2CS ? 1 : 0))
515  }
516 
517  // selected inputs
518  int nTransIns, nShieldIns;
519  if (fSelectTransparent) {
520  nTransIns = t.nQuantity;
521  nShieldIns = 0;
522  } else {
523  nTransIns = 0;
524  nShieldIns = t.nQuantity;
525  }
526 
527  // calculation
528  const int P2CS_OUT_SIZE = 61;
529  int nTransOuts = 0, nShieldOuts = 0;
530  if (t.nQuantity > 0) {
531  // Bytes: nBytesInputs + (sum of nBytesOutputs)
532  // always assume +1 (p2pkh) output for change here
534  for (const auto& a : payAmounts) {
535  t.nPayAmount += a.first;
536  bool shieldedOut = a.second;
537  if (shieldedOut) nShieldOuts++;
538  else nTransOuts++;
539  if (a.first > 0 && !t.fDust) {
540  if (a.first < (shieldedOut ? GetShieldedDustThreshold(dustRelayFee) : GetDustThreshold(dustRelayFee)))
541  t.fDust = true;
542  }
543  t.nBytes += (shieldedOut ? OUTPUTDESCRIPTION_SIZE
544  : (forDelegation ? P2CS_OUT_SIZE : CTXOUT_REGULAR_SIZE));
545  }
546 
547  // Shielded txes must include binding sig and valueBalance
548  bool isShieldedTx = (nShieldIns + nShieldOuts > 0);
549  if (isShieldedTx) {
550  t.nBytes += (BINDINGSIG_SIZE + 8);
551  // shielded in/outs len sizes
552  t.nBytes += (GetCompactSize(nShieldIns) + GetCompactSize(nShieldOuts));
553  }
554 
555  // nVersion, nType, nLockTime
556  t.nBytes += 8;
557 
558  // vin/vout len sizes
559  t.nBytes += (GetCompactSize(nTransIns) + GetCompactSize(nTransOuts));
560 
561  // Fee (default K fixed for shielded fee for now)
562  t.nPayFee = GetMinRelayFee(t.nBytes) * (isShieldedTx ? DEFAULT_SHIELDEDTXFEE_K : 1);
563 
564  if (t.nPayAmount > 0) {
565  t.nChange = t.nAmount - t.nPayFee - t.nPayAmount;
566 
567  // Never create dust outputs; if we would, just add the dust to the fee.
570  if (t.nChange > 0 && t.nChange < dustThreshold) {
571  t.nPayFee += t.nChange;
572  t.nChange = 0;
573  }
574 
575  if (t.nChange == 0)
577  }
578 
579  // after fee
580  t.nAfterFee = std::max<CAmount>(t.nAmount - t.nPayFee, 0);
581  }
582  return t;
583 }
584 
586 {
587  if (!model)
588  return;
589 
590  ui->labelTitle->setText(fSelectTransparent ?
591  "Select PIV Outputs to Spend" :
592  "Select Shielded PIV to Spend");
593 
594  const TotalAmounts& t = getTotals();
595 
596  // update SelectAll button state
597  // if inputs selected > inputs unselected, set checked (label "Unselect All")
598  // if inputs selected <= inputs unselected, set unchecked (label "Select All")
600 
601  // actually update labels
602  int nDisplayUnit = BitcoinUnits::PIV;
603  if (model && model->getOptionsModel())
604  nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
605 
606  // enable/disable "dust" and "change"
607  const bool hasPayAmount = t.nPayAmount > 0;
608  ui->labelCoinControlLowOutputText->setEnabled(hasPayAmount);
609  ui->labelCoinControlLowOutput->setEnabled(hasPayAmount);
610  ui->labelCoinControlChangeText->setEnabled(hasPayAmount);
611  ui->labelCoinControlChange->setEnabled(hasPayAmount);
612 
613  // stats
614  ui->labelCoinControlQuantity->setText(QString::number(t.nQuantity));
615  ui->labelCoinControlAmount->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, t.nAmount));
616  ui->labelCoinControlFee->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, t.nPayFee));
617  ui->labelCoinControlAfterFee->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, t.nAfterFee));
618  ui->labelCoinControlBytes->setText(((t.nBytes > 0) ? "~" : "") + QString::number(t.nBytes));
619  ui->labelCoinControlLowOutput->setText(t.fDust ? tr("yes") : tr("no"));
620  ui->labelCoinControlChange->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, t.nChange));
621  if (t.nPayFee > 0 && !(payTxFee.GetFeePerK() > 0 && fPayAtLeastCustomFee && t.nBytes < 1000)) {
622  ui->labelCoinControlFee->setText("~" + ui->labelCoinControlFee->text());
623  ui->labelCoinControlAfterFee->setText("~" + ui->labelCoinControlAfterFee->text());
624  if (t.nChange > 0)
625  ui->labelCoinControlChange->setText("~" + ui->labelCoinControlChange->text());
626  }
627 
628  // turn labels "red"
629  ui->labelCoinControlLowOutput->setStyleSheet((t.fDust) ? "color:red;" : ""); // Dust = "yes"
630 
631  // tool tips
632  QString toolTip3 = tr("This label turns red, if recipient receives an amount smaller than %1 (transparent) / %2 (shield)."
634 
635  // how many satoshis the estimated fee can vary per byte we guess wrong
636  double dFeeVary;
637  if (payTxFee.GetFeePerK() > 0)
638  dFeeVary = (double)std::max(GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000;
639  else
640  dFeeVary = (double)std::max(GetRequiredFee(1000), mempool.estimateSmartFee(nTxConfirmTarget).GetFeePerK()) / 1000;
641  QString toolTip4 = tr("Can vary +/- %1 u%2 per input.").arg(dFeeVary).arg(CURRENCY_UNIT.c_str());
642 
643  ui->labelCoinControlFee->setToolTip(toolTip4);
644  ui->labelCoinControlAfterFee->setToolTip(toolTip4);
645  ui->labelCoinControlLowOutput->setToolTip(toolTip3);
646  ui->labelCoinControlChange->setToolTip(toolTip4);
647  ui->labelCoinControlFeeText->setToolTip(ui->labelCoinControlFee->toolTip());
648  ui->labelCoinControlAfterFeeText->setToolTip(ui->labelCoinControlAfterFee->toolTip());
649  ui->labelCoinControlBytesText->setToolTip(ui->labelCoinControlBytes->toolTip());
650  ui->labelCoinControlLowOutputText->setToolTip(ui->labelCoinControlLowOutput->toolTip());
651  ui->labelCoinControlChangeText->setToolTip(ui->labelCoinControlChange->toolTip());
652 
653  // Insufficient funds
654  QLabel* label = findChild<QLabel*>("labelCoinControlInsuffFunds");
655  if (label)
656  label->setVisible(t.nChange < 0);
657 }
658 
660  CCoinControlWidgetItem* itemWalletAddress,
661  QFlags<Qt::ItemFlag> flgCheckbox,
662  QFlags<Qt::ItemFlag> flgTristate,
663  int nDisplayUnit,
664  const QString& sWalletAddress,
665  const Optional<QString>& stakerAddress,
666  const QString& sWalletLabel,
667  const uint256& txhash,
668  const uint32_t outIndex,
669  const CAmount nValue,
670  const int64_t nTime,
671  const int nDepth,
672  const bool isChange)
673 {
674  CCoinControlWidgetItem* itemOutput;
675  if (treeMode)
676  itemOutput = new CCoinControlWidgetItem(itemWalletAddress);
677  else
678  itemOutput = new CCoinControlWidgetItem(ui->treeWidget);
679  itemOutput->setFlags(flgCheckbox);
680  itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
681 
682  // if listMode or change => show PIVX address. In tree mode, address is not shown again for direct wallet address outputs
683  if (!treeMode) {
684  itemOutput->setText(COLUMN_ADDRESS, sWalletAddress);
685  }else {
686  itemOutput->setToolTip(COLUMN_ADDRESS, sWalletAddress);
687  }
688 
689  // label
690  if (isChange) {
691  // tooltip stating where the change is being stored.
692  itemOutput->setToolTip(COLUMN_LABEL, tr("change in %1").arg(sWalletAddress));
693  itemOutput->setText(COLUMN_LABEL, tr("(change)"));
694  } else if (!treeMode) {
695  itemOutput->setText(COLUMN_LABEL, sWalletLabel);
696  }
697 
698  // amount
699  itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nValue));
700  itemOutput->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nValue));
701  itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong) nValue));
702 
703  // date
704  itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(nTime));
705  itemOutput->setToolTip(COLUMN_DATE, GUIUtil::dateTimeStr(nTime));
706  itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong) nTime));
707 
708  // confirmations
709  itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(nDepth));
710  itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong) nDepth));
711 
712  // transaction hash
713  itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex()));
714 
715  // vout index
716  itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(outIndex));
717 
718  bool isLockedCoin{false};
719  isLockedCoin = model->isLockedCoin(txhash, outIndex, fSelectTransparent);
720  if (isLockedCoin) {
722  coinControl->UnSelect({txhash, outIndex}); // just to be sure
723  itemOutput->setDisabled(true);
724  itemOutput->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed"));
725  }
726 
727  // set checkbox
728  if (coinControl->IsSelected(COutPoint(txhash, outIndex)))
729  itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
730 
731  // outputs delegated (for cold staking)
732  if (stakerAddress) {
733  itemOutput->setData(COLUMN_CHECKBOX, Qt::UserRole, QString("Delegated"));
734  if (!isLockedCoin)
735  itemOutput->setIcon(COLUMN_CHECKBOX, QIcon("://ic-check-cold-staking-off"));
736  itemOutput->setToolTip(COLUMN_CHECKBOX, tr("delegated to %1 for cold staking").arg(*stakerAddress));
737  }
738 }
739 
741 {
743  return;
744 
745  bool treeMode = ui->radioTreeMode->isChecked();
746  ui->treeWidget->setRootIsDecorated(treeMode);
747 
748  ui->treeWidget->clear();
749  ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox
750  QFlags<Qt::ItemFlag> flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
751  QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
752 
753  int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
754  nSelectableInputs = 0;
755  std::map<WalletModel::ListCoinsKey, std::vector<WalletModel::ListCoinsValue>> mapCoins;
756  model->listCoins(mapCoins, fSelectTransparent);
757 
758  for (const auto& coins : mapCoins) {
759  CCoinControlWidgetItem* itemWalletAddress = new CCoinControlWidgetItem();
760  itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
761  const WalletModel::ListCoinsKey& keys = coins.first;
762  const QString& sWalletAddress = keys.address;
763  const Optional<QString>& stakerAddress = keys.stakerAddress;
764  QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress);
765  if (sWalletLabel.isEmpty())
766  sWalletLabel = tr("(no label)");
767 
768  if (treeMode) {
769  // wallet address
770  ui->treeWidget->addTopLevelItem(itemWalletAddress);
771 
772  itemWalletAddress->setFlags(flgTristate);
773  itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
774 
775  // label
776  itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel);
777  itemWalletAddress->setToolTip(COLUMN_LABEL, sWalletLabel);
778 
779  // address
780  itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress);
781  if (stakerAddress != nullopt) {
782  itemWalletAddress->setIcon(COLUMN_CONFIRMATIONS, QIcon("://ic-check-cold-staking-off"));
783  QString label = tr("Delegated to %1").arg(*stakerAddress);
784  itemWalletAddress->setToolTip(COLUMN_ADDRESS, label);
785  itemWalletAddress->setToolTip(COLUMN_CONFIRMATIONS, label);
786  } else {
787  itemWalletAddress->setToolTip(COLUMN_ADDRESS, sWalletAddress);
788  }
789  }
790 
791  CAmount nSum = 0;
792  int nChildren = 0;
793  for (const WalletModel::ListCoinsValue& out : coins.second) {
795  nSum += out.nValue;
796  nChildren++;
797 
798  loadAvailableCoin(treeMode, itemWalletAddress, flgCheckbox, flgTristate,
799  nDisplayUnit, sWalletAddress, stakerAddress, sWalletLabel,
800  out.txhash, out.outIndex, out.nValue, out.nTime, out.nDepth,
801  keys.isChange);
802  }
803 
804  // amount
805  if (treeMode) {
806  itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
807  itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
808  itemWalletAddress->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
809  itemWalletAddress->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong) nSum));
810  }
811  }
812 
813  // expand all partially selected
814  if (treeMode) {
815  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
816  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
817  ui->treeWidget->topLevelItem(i)->setExpanded(true);
818  // restore saved width for COLUMN_CHECKBOX
819  ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, colCheckBoxWidth_treeMode);
820  } else {
821  // save COLUMN_CHECKBOX width for tree-mode
822  colCheckBoxWidth_treeMode = std::max(110, ui->treeWidget->columnWidth(COLUMN_CHECKBOX));
823  // minimize COLUMN_CHECKBOX width in list-mode (need to display only the check box)
824  ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 70);
825  }
826 
827  // sort view
829  ui->treeWidget->setEnabled(true);
830 }
831 
833 {
834  updateView();
836  updateLabels();
837 }
838 
839 void CoinControlDialog::inform(const QString& text)
840 {
841  if (!snackBar) snackBar = new SnackBar(nullptr, this);
842  snackBar->setText(text);
843  snackBar->resize(this->width(), snackBar->height());
844  openDialog(snackBar, this);
845 }
846 
848 {
849  payAmounts.clear();
850 }
851 
852 void CoinControlDialog::addPayAmount(const CAmount& amount, bool isShieldedRecipient)
853 {
854  payAmounts.emplace_back(amount, isShieldedRecipient);
855 }
856 
858 {
859  ui->pushButtonSelectAll->setChecked(checked);
860  ui->pushButtonSelectAll->setText(checked ? tr("Unselect all") : tr("Select All"));
861 }
int64_t CAmount
Amount in PIV (Can be negative)
Definition: amount.h:13
bool operator<(const CBigNum &a, const CBigNum &b)
Definition: bignum.h:224
QString labelForAddress(const QString &address) const
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:33
static QString removeSpaces(QString text)
Definition: bitcoinunits.h:119
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard, bool cleanRemainderZeros=true)
Format as string.
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
Coin Control Features.
Definition: coincontrol.h:34
bool IsSelected(const BaseOutPoint &output) const
Definition: coincontrol.h:71
unsigned int QuantitySelected() const
Definition: coincontrol.h:96
void Select(const BaseOutPoint &output, CAmount value=0, bool isP2CS=false)
Definition: coincontrol.h:76
void ListSelected(std::vector< OutPointWrapper > &vOutpoints) const
Definition: coincontrol.h:91
void UnSelectAll()
Definition: coincontrol.h:86
void UnSelect(const BaseOutPoint &output)
Definition: coincontrol.h:81
bool operator<(const QTreeWidgetItem &other) const override
CAmount GetFeePerK() const
Definition: feerate.h:29
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:72
CFeeRate estimateSmartFee(int nBlocks, int *answerFoundAtBlocks=nullptr) const
Estimate fee rate needed to get into the next nBlocks If no answer can be given at nBlocks,...
Definition: txmempool.cpp:1206
void updatePushButtonSelectAll(bool checked)
QTreeWidgetItem * contextMenuItem
CoinControlDialog(QWidget *parent=nullptr, bool _forDelegation=false)
WalletModel * model
void toggleItemLock(QTreeWidgetItem *item)
TotalAmounts getTotals() const
void loadAvailableCoin(bool treeMode, CCoinControlWidgetItem *itemWalletAddress, QFlags< Qt::ItemFlag > flgCheckbox, QFlags< Qt::ItemFlag > flgTristate, int nDisplayUnit, const QString &sWalletAddress, const Optional< QString > &stakerAddress, const QString &sWalletLabel, const uint256 &txhash, const uint32_t outIndex, const CAmount nValue, const int64_t nTime, const int nDepth, const bool isChange)
unsigned int nSelectableInputs
friend class CCoinControlWidgetItem
void setModel(WalletModel *model)
CCoinControl * coinControl
QAction * copyTransactionHashAction
void inform(const QString &text)
Ui::CoinControlDialog * ui
void sortView(int, Qt::SortOrder)
void showMenu(const QPoint &)
void addPayAmount(const CAmount &amount, bool isShieldedRecipient)
void viewItemChanged(QTreeWidgetItem *, int)
Qt::SortOrder sortOrder
std::vector< std::pair< CAmount, bool > > payAmounts
int getDisplayUnit()
Definition: optionsmodel.h:74
void setText(const QString &text)
Definition: snackbar.cpp:51
Optional< QString > stakerAddress
Definition: walletmodel.h:312
Interface to PIVX wallet from Qt view code.
Definition: walletmodel.h:109
void lockCoin(uint256 hash, unsigned int n, bool isTransparent=true)
void unlockCoin(uint256 hash, unsigned int n, bool isTransparent=true)
void listCoins(std::map< ListCoinsKey, std::vector< ListCoinsValue >> &mapCoins, bool fSelectTransparent) const
AddressTableModel * getAddressTableModel()
OptionsModel * getOptionsModel()
std::set< COutPoint > listLockedCoins()
bool isLockedCoin(uint256 hash, unsigned int n, bool isTransparent=true) const
std::set< SaplingOutPoint > listLockedNotes()
std::string GetHex() const
Definition: uint256.cpp:21
256-bit opaque blob.
Definition: uint256.h:138
const std::string CURRENCY_UNIT
Definition: feerate.cpp:11
QString loadStyleSheet()
Load global CSS theme.
Definition: guiutil.cpp:639
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:69
void setClipboard(const QString &str)
Definition: guiutil.cpp:670
boost::optional< T > Optional
Substitute for C++17 std::optional.
Definition: optional.h:12
CAmount GetDustThreshold(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
Definition: policy.cpp:21
CAmount GetShieldedDustThreshold(const CFeeRate &dustRelayFeeIn)
Definition: policy.cpp:50
CFeeRate dustRelayFee
Definition: policy.cpp:19
QSettings * settings
Definition: qtutils.cpp:197
void setCssProperty(std::initializer_list< QWidget * > args, const QString &value)
Definition: qtutils.cpp:334
bool openDialog(QDialog *widget, QWidget *gui)
Definition: qtutils.cpp:23
#define BINDINGSIG_SIZE
#define SPENDDESCRIPTION_SIZE
#define CTXIN_SPEND_DUST_SIZE
#define OUTPUTDESCRIPTION_SIZE
#define CTXOUT_REGULAR_SIZE
unsigned int nQuantity
unsigned int nBytes
uint256 uint256S(const char *str)
Definition: uint256.h:157
CTxMemPool mempool(::minRelayTxFee)
CAmount GetMinRelayFee(const CTransaction &tx, const CTxMemPool &pool, unsigned int nBytes)
Definition: validation.cpp:265
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:15
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE)
Settings.
bool fPayAtLeastCustomFee
Definition: wallet.cpp:41
unsigned int nTxConfirmTarget
Definition: wallet.cpp:39