Dogecoin Core  1.14.2
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 #include "coincontroldialog.h"
6 #include "ui_coincontroldialog.h"
7 
8 #include "addresstablemodel.h"
9 #include "bitcoinunits.h"
10 #include "guiutil.h"
11 #include "optionsmodel.h"
12 #include "platformstyle.h"
13 #include "txmempool.h"
14 #include "walletmodel.h"
15 
16 #include "wallet/coincontrol.h"
17 #include "init.h"
18 #include "policy/policy.h"
19 #include "validation.h" // For mempool
20 #include "wallet/wallet.h"
21 
22 #include <boost/assign/list_of.hpp> // for 'map_list_of()'
23 
24 #include <QApplication>
25 #include <QCheckBox>
26 #include <QCursor>
27 #include <QDialogButtonBox>
28 #include <QFlags>
29 #include <QIcon>
30 #include <QSettings>
31 #include <QString>
32 #include <QTreeWidget>
33 #include <QTreeWidgetItem>
34 
35 QList<CAmount> CoinControlDialog::payAmounts;
38 
39 bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const {
40  int column = treeWidget()->sortColumn();
42  return data(column, Qt::UserRole).toLongLong() < other.data(column, Qt::UserRole).toLongLong();
43  return QTreeWidgetItem::operator<(other);
44 }
45 
46 CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
47  QDialog(parent),
48  ui(new Ui::CoinControlDialog),
49  model(0),
50  platformStyle(_platformStyle)
51 {
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  copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this); // we need to enable/disable this
59  lockAction = new QAction(tr("Lock unspent"), this); // we need to enable/disable this
60  unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this
61 
62  // context menu
63  contextMenu = new QMenu(this);
64  contextMenu->addAction(copyAddressAction);
65  contextMenu->addAction(copyLabelAction);
66  contextMenu->addAction(copyAmountAction);
68  contextMenu->addSeparator();
69  contextMenu->addAction(lockAction);
70  contextMenu->addAction(unlockAction);
71 
72  // context menu signals
73  connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
74  connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
75  connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
76  connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
77  connect(copyTransactionHashAction, SIGNAL(triggered()), this, SLOT(copyTransactionHash()));
78  connect(lockAction, SIGNAL(triggered()), this, SLOT(lockCoin()));
79  connect(unlockAction, SIGNAL(triggered()), this, SLOT(unlockCoin()));
80 
81  // clipboard actions
82  QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
83  QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
84  QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
85  QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
86  QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
87  QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
88  QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
89 
90  connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(clipboardQuantity()));
91  connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(clipboardAmount()));
92  connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(clipboardFee()));
93  connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(clipboardAfterFee()));
94  connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(clipboardBytes()));
95  connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(clipboardLowOutput()));
96  connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(clipboardChange()));
97 
98  ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
99  ui->labelCoinControlAmount->addAction(clipboardAmountAction);
100  ui->labelCoinControlFee->addAction(clipboardFeeAction);
101  ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
102  ui->labelCoinControlBytes->addAction(clipboardBytesAction);
103  ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
104  ui->labelCoinControlChange->addAction(clipboardChangeAction);
105 
106  // toggle tree/list mode
107  connect(ui->radioTreeMode, SIGNAL(toggled(bool)), this, SLOT(radioTreeMode(bool)));
108  connect(ui->radioListMode, SIGNAL(toggled(bool)), this, SLOT(radioListMode(bool)));
109 
110  // click on checkbox
111  connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(viewItemChanged(QTreeWidgetItem*, int)));
112 
113  // click on header
114 #if QT_VERSION < 0x050000
115  ui->treeWidget->header()->setClickable(true);
116 #else
117  ui->treeWidget->header()->setSectionsClickable(true);
118 #endif
119  connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this, SLOT(headerSectionClicked(int)));
120 
121  // ok button
122  connect(ui->buttonBox, SIGNAL(clicked( QAbstractButton*)), this, SLOT(buttonBoxClicked(QAbstractButton*)));
123 
124  // (un)select all
125  connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked()));
126 
127  // change coin control first column label due Qt4 bug.
128  // see https://github.com/bitcoin/bitcoin/issues/5716
129  ui->treeWidget->headerItem()->setText(COLUMN_CHECKBOX, QString());
130 
131  ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84);
132  ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 110);
133  ui->treeWidget->setColumnWidth(COLUMN_LABEL, 190);
134  ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 320);
135  ui->treeWidget->setColumnWidth(COLUMN_DATE, 130);
136  ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 110);
137  ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transaction hash in this column, but don't show it
138  ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but don't show it
139 
140  // default view is sorted by amount desc
141  sortView(COLUMN_AMOUNT, Qt::DescendingOrder);
142 
143  // restore list mode and sortorder as a convenience feature
144  QSettings settings;
145  if (settings.contains("nCoinControlMode") && !settings.value("nCoinControlMode").toBool())
146  ui->radioTreeMode->click();
147  if (settings.contains("nCoinControlSortColumn") && settings.contains("nCoinControlSortOrder"))
148  sortView(settings.value("nCoinControlSortColumn").toInt(), ((Qt::SortOrder)settings.value("nCoinControlSortOrder").toInt()));
149 }
150 
152 {
153  QSettings settings;
154  settings.setValue("nCoinControlMode", ui->radioListMode->isChecked());
155  settings.setValue("nCoinControlSortColumn", sortColumn);
156  settings.setValue("nCoinControlSortOrder", (int)sortOrder);
157 
158  delete ui;
159 }
160 
162 {
163  this->model = _model;
164 
165  if(_model && _model->getOptionsModel() && _model->getAddressTableModel())
166  {
167  updateView();
169  CoinControlDialog::updateLabels(_model, this);
170  }
171 }
172 
173 // ok button
174 void CoinControlDialog::buttonBoxClicked(QAbstractButton* button)
175 {
176  if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
177  done(QDialog::Accepted); // closes the dialog
178 }
179 
180 // (un)select all
182 {
183  Qt::CheckState state = Qt::Checked;
184  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
185  {
186  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != Qt::Unchecked)
187  {
188  state = Qt::Unchecked;
189  break;
190  }
191  }
192  ui->treeWidget->setEnabled(false);
193  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
194  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != state)
195  ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state);
196  ui->treeWidget->setEnabled(true);
197  if (state == Qt::Unchecked)
198  coinControl->UnSelectAll(); // just to be sure
200 }
201 
202 // context menu
203 void CoinControlDialog::showMenu(const QPoint &point)
204 {
205  QTreeWidgetItem *item = ui->treeWidget->itemAt(point);
206  if(item)
207  {
208  contextMenuItem = item;
209 
210  // disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu
211  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)
212  {
213  copyTransactionHashAction->setEnabled(true);
214  if (model->isLockedCoin(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()))
215  {
216  lockAction->setEnabled(false);
217  unlockAction->setEnabled(true);
218  }
219  else
220  {
221  lockAction->setEnabled(true);
222  unlockAction->setEnabled(false);
223  }
224  }
225  else // this means click on parent node in tree mode -> disable all
226  {
227  copyTransactionHashAction->setEnabled(false);
228  lockAction->setEnabled(false);
229  unlockAction->setEnabled(false);
230  }
231 
232  // show context menu
233  contextMenu->exec(QCursor::pos());
234  }
235 }
236 
237 // context menu action: copy amount
239 {
241 }
242 
243 // context menu action: copy label
245 {
246  if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_LABEL).length() == 0 && contextMenuItem->parent())
248  else
250 }
251 
252 // context menu action: copy address
254 {
255  if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_ADDRESS).length() == 0 && contextMenuItem->parent())
257  else
259 }
260 
261 // context menu action: copy transaction id
263 {
265 }
266 
267 // context menu action: lock coin
269 {
270  if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked)
271  contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
272 
273  COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
274  model->lockCoin(outpt);
275  contextMenuItem->setDisabled(true);
276  contextMenuItem->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
278 }
279 
280 // context menu action: unlock coin
282 {
283  COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
284  model->unlockCoin(outpt);
285  contextMenuItem->setDisabled(false);
286  contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
288 }
289 
290 // copy label "Quantity" to clipboard
292 {
293  GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
294 }
295 
296 // copy label "Amount" to clipboard
298 {
299  GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
300 }
301 
302 // copy label "Fee" to clipboard
304 {
305  GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
306 }
307 
308 // copy label "After fee" to clipboard
310 {
311  GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
312 }
313 
314 // copy label "Bytes" to clipboard
316 {
317  GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
318 }
319 
320 // copy label "Dust" to clipboard
322 {
323  GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
324 }
325 
326 // copy label "Change" to clipboard
328 {
329  GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
330 }
331 
332 // treeview: sort
333 void CoinControlDialog::sortView(int column, Qt::SortOrder order)
334 {
335  sortColumn = column;
336  sortOrder = order;
337  ui->treeWidget->sortItems(column, order);
338  ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
339 }
340 
341 // treeview: clicked on header
343 {
344  if (logicalIndex == COLUMN_CHECKBOX) // click on most left column -> do nothing
345  {
346  ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
347  }
348  else
349  {
350  if (sortColumn == logicalIndex)
351  sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder);
352  else
353  {
354  sortColumn = logicalIndex;
355  sortOrder = ((sortColumn == COLUMN_LABEL || sortColumn == COLUMN_ADDRESS) ? Qt::AscendingOrder : Qt::DescendingOrder); // if label or address then default => asc, else default => desc
356  }
357 
359  }
360 }
361 
362 // toggle tree mode
364 {
365  if (checked && model)
366  updateView();
367 }
368 
369 // toggle list mode
371 {
372  if (checked && model)
373  updateView();
374 }
375 
376 // checkbox clicked by user
377 void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
378 {
379  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)
380  {
381  COutPoint outpt(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt());
382 
383  if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked)
384  coinControl->UnSelect(outpt);
385  else if (item->isDisabled()) // locked (this happens if "check all" through parent node)
386  item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
387  else
388  coinControl->Select(outpt);
389 
390  // selection changed -> update labels
391  if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all
393  }
394 
395  // TODO: Remove this temporary qt5 fix after Qt5.3 and Qt5.4 are no longer used.
396  // Fixed in Qt5.5 and above: https://bugreports.qt.io/browse/QTBUG-43473
397 #if QT_VERSION >= 0x050000
398  else if (column == COLUMN_CHECKBOX && item->childCount() > 0)
399  {
400  if (item->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked && item->child(0)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
401  item->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
402  }
403 #endif
404 }
405 
406 // shows count of locked unspent outputs
408 {
409  std::vector<COutPoint> vOutpts;
410  model->listLockedCoins(vOutpts);
411  if (vOutpts.size() > 0)
412  {
413  ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size()));
414  ui->labelLocked->setVisible(true);
415  }
416  else ui->labelLocked->setVisible(false);
417 }
418 
419 void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
420 {
421  if (!model)
422  return;
423 
424  // nPayAmount
425  CAmount nPayAmount = 0;
426  bool fDust = false;
427  CMutableTransaction txDummy;
428  Q_FOREACH(const CAmount &amount, CoinControlDialog::payAmounts)
429  {
430  nPayAmount += amount;
431 
432  if (amount > 0)
433  {
434  CTxOut txout(amount, (CScript)std::vector<unsigned char>(24, 0));
435  txDummy.vout.push_back(txout);
436  if (txout.IsDust(dustRelayFee))
437  fDust = true;
438  }
439  }
440 
441  CAmount nAmount = 0;
442  CAmount nPayFee = 0;
443  CAmount nAfterFee = 0;
444  CAmount nChange = 0;
445  unsigned int nBytes = 0;
446  unsigned int nBytesInputs = 0;
447  double dPriority = 0;
448  double dPriorityInputs = 0;
449  unsigned int nQuantity = 0;
450  int nQuantityUncompressed = 0;
451  bool fAllowFree = false;
452  bool fWitness = false;
453 
454  std::vector<COutPoint> vCoinControl;
455  std::vector<COutput> vOutputs;
456  coinControl->ListSelected(vCoinControl);
457  model->getOutputs(vCoinControl, vOutputs);
458 
459  BOOST_FOREACH(const COutput& out, vOutputs) {
460  // unselect already spent, very unlikely scenario, this could happen
461  // when selected are spent elsewhere, like rpc or another computer
462  uint256 txhash = out.tx->GetHash();
463  COutPoint outpt(txhash, out.i);
464  if (model->isSpent(outpt))
465  {
466  coinControl->UnSelect(outpt);
467  continue;
468  }
469 
470  // Quantity
471  nQuantity++;
472 
473  // Amount
474  nAmount += out.tx->tx->vout[out.i].nValue;
475 
476  // Priority
477  dPriorityInputs += (double)out.tx->tx->vout[out.i].nValue * (out.nDepth+1);
478 
479  // Bytes
480  CTxDestination address;
481  int witnessversion = 0;
482  std::vector<unsigned char> witnessprogram;
483  if (out.tx->tx->vout[out.i].scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram))
484  {
485  nBytesInputs += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
486  fWitness = true;
487  }
488  else if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address))
489  {
490  CPubKey pubkey;
491  CKeyID *keyid = boost::get<CKeyID>(&address);
492  if (keyid && model->getPubKey(*keyid, pubkey))
493  {
494  nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
495  if (!pubkey.IsCompressed())
496  nQuantityUncompressed++;
497  }
498  else
499  nBytesInputs += 148; // in all error cases, simply assume 148 here
500  }
501  else nBytesInputs += 148;
502  }
503 
504  // calculation
505  if (nQuantity > 0)
506  {
507  // Bytes
508  nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here
509  if (fWitness)
510  {
511  // there is some fudging in these numbers related to the actual virtual transaction size calculation that will keep this estimate from being exact.
512  // usually, the result will be an overestimate within a couple of satoshis so that the confirmation dialog ends up displaying a slightly smaller fee.
513  // also, the witness stack size value value is a variable sized integer. usually, the number of stack items will be well under the single byte var int limit.
514  nBytes += 2; // account for the serialized marker and flag bytes
515  nBytes += nQuantity; // account for the witness byte that holds the number of stack items for each input.
516  }
517 
518  // in the subtract fee from amount case, we can tell if zero change already and subtract the bytes, so that fee calculation afterwards is accurate
520  if (nAmount - nPayAmount == 0)
521  nBytes -= 34;
522 
523  // Fee
524  nPayFee = CWallet::GetMinimumFee(txDummy, nBytes, nTxConfirmTarget, mempool);
525  if (nPayFee > 0 && coinControl->nMinimumTotalFee > nPayFee)
526  nPayFee = coinControl->nMinimumTotalFee;
527 
528 
529  // Allow free? (require at least hard-coded threshold and default to that if no estimate)
530  double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget);
531  dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority)
532  double dPriorityNeeded = std::max(mempoolEstimatePriority, AllowFreeThreshold());
533  fAllowFree = (dPriority >= dPriorityNeeded);
534 
536  if (fAllowFree && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
537  nPayFee = 0;
538 
539  if (nPayAmount > 0)
540  {
541  nChange = nAmount - nPayAmount;
543  nChange -= nPayFee;
544 
545  // Never create dust outputs; if we would, just add the dust to the fee.
546  if (nChange > 0 && nChange < MIN_CHANGE)
547  {
548  CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0));
549  if (txout.IsDust(dustRelayFee))
550  {
551  if (CoinControlDialog::fSubtractFeeFromAmount) // dust-change will be raised until no dust
552  nChange = txout.GetDustThreshold(dustRelayFee);
553  else
554  {
555  nPayFee += nChange;
556  nChange = 0;
557  }
558  }
559  }
560 
561  if (nChange == 0 && !CoinControlDialog::fSubtractFeeFromAmount)
562  nBytes -= 34;
563  }
564 
565  // after fee
566  nAfterFee = std::max<CAmount>(nAmount - nPayFee, 0);
567  }
568 
569  // actually update labels
570  int nDisplayUnit = BitcoinUnits::BTC;
571  if (model && model->getOptionsModel())
572  nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
573 
574  QLabel *l1 = dialog->findChild<QLabel *>("labelCoinControlQuantity");
575  QLabel *l2 = dialog->findChild<QLabel *>("labelCoinControlAmount");
576  QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee");
577  QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee");
578  QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes");
579  QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput");
580  QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange");
581 
582  // enable/disable "dust" and "change"
583  dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0);
584  dialog->findChild<QLabel *>("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0);
585  dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0);
586  dialog->findChild<QLabel *>("labelCoinControlChange") ->setEnabled(nPayAmount > 0);
587 
588  // stats
589  l1->setText(QString::number(nQuantity)); // Quantity
590  l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount)); // Amount
591  l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee
592  l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee
593  l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes)); // Bytes
594  l7->setText(fDust ? tr("yes") : tr("no")); // Dust
595  l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change
596  if (nPayFee > 0 && (coinControl->nMinimumTotalFee < nPayFee))
597  {
598  l3->setText(ASYMP_UTF8 + l3->text());
599  l4->setText(ASYMP_UTF8 + l4->text());
600  if (nChange > 0 && !CoinControlDialog::fSubtractFeeFromAmount)
601  l8->setText(ASYMP_UTF8 + l8->text());
602  }
603 
604  // turn label red when dust
605  l7->setStyleSheet((fDust) ? "color:red;" : "");
606 
607  // tool tips
608  QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold.");
609 
610  // how many satoshis the estimated fee can vary per byte we guess wrong
611  double dFeeVary;
612  if (payTxFee.GetFeePerK() > 0)
613  dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000;
614  else {
615  dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), mempool.estimateSmartFee(nTxConfirmTarget).GetFeePerK()) / 1000;
616  }
617  QString toolTip4 = tr("Can vary +/- %1 koinu(s) per input.").arg(dFeeVary);
618 
619  l3->setToolTip(toolTip4);
620  l4->setToolTip(toolTip4);
621  l7->setToolTip(toolTipDust);
622  l8->setToolTip(toolTip4);
623  dialog->findChild<QLabel *>("labelCoinControlFeeText") ->setToolTip(l3->toolTip());
624  dialog->findChild<QLabel *>("labelCoinControlAfterFeeText") ->setToolTip(l4->toolTip());
625  dialog->findChild<QLabel *>("labelCoinControlBytesText") ->setToolTip(l5->toolTip());
626  dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip());
627  dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setToolTip(l8->toolTip());
628 
629  // Insufficient funds
630  QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds");
631  if (label)
632  label->setVisible(nChange < 0);
633 }
634 
636 {
638  return;
639 
640  bool treeMode = ui->radioTreeMode->isChecked();
641 
642  ui->treeWidget->clear();
643  ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox
644  ui->treeWidget->setAlternatingRowColors(!treeMode);
645  QFlags<Qt::ItemFlag> flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
646  QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
647 
648  int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
649 
650  std::map<QString, std::vector<COutput> > mapCoins;
651  model->listCoins(mapCoins);
652 
653  BOOST_FOREACH(const PAIRTYPE(QString, std::vector<COutput>)& coins, mapCoins) {
654  CCoinControlWidgetItem *itemWalletAddress = new CCoinControlWidgetItem();
655  itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
656  QString sWalletAddress = coins.first;
657  QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress);
658  if (sWalletLabel.isEmpty())
659  sWalletLabel = tr("(no label)");
660 
661  if (treeMode)
662  {
663  // wallet address
664  ui->treeWidget->addTopLevelItem(itemWalletAddress);
665 
666  itemWalletAddress->setFlags(flgTristate);
667  itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
668 
669  // label
670  itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel);
671 
672  // address
673  itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress);
674  }
675 
676  CAmount nSum = 0;
677  int nChildren = 0;
678  BOOST_FOREACH(const COutput& out, coins.second) {
679  nSum += out.tx->tx->vout[out.i].nValue;
680  nChildren++;
681 
682  CCoinControlWidgetItem *itemOutput;
683  if (treeMode) itemOutput = new CCoinControlWidgetItem(itemWalletAddress);
684  else itemOutput = new CCoinControlWidgetItem(ui->treeWidget);
685  itemOutput->setFlags(flgCheckbox);
686  itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked);
687 
688  // address
689  CTxDestination outputAddress;
690  QString sAddress = "";
691  if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress))
692  {
693  sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString());
694 
695  // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs
696  if (!treeMode || (!(sAddress == sWalletAddress)))
697  itemOutput->setText(COLUMN_ADDRESS, sAddress);
698  }
699 
700  // label
701  if (!(sAddress == sWalletAddress)) // change
702  {
703  // tooltip from where the change comes from
704  itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress));
705  itemOutput->setText(COLUMN_LABEL, tr("(change)"));
706  }
707  else if (!treeMode)
708  {
709  QString sLabel = model->getAddressTableModel()->labelForAddress(sAddress);
710  if (sLabel.isEmpty())
711  sLabel = tr("(no label)");
712  itemOutput->setText(COLUMN_LABEL, sLabel);
713  }
714 
715  // amount
716  itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue));
717  itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->tx->vout[out.i].nValue)); // padding so that sorting works correctly
718 
719  // date
720  itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime()));
721  itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong)out.tx->GetTxTime()));
722 
723  // confirmations
724  itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(out.nDepth));
725  itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong)out.nDepth));
726 
727  // transaction hash
728  uint256 txhash = out.tx->GetHash();
729  itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex()));
730 
731  // vout index
732  itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i));
733 
734  // disable locked coins
735  if (model->isLockedCoin(txhash, out.i))
736  {
737  COutPoint outpt(txhash, out.i);
738  coinControl->UnSelect(outpt); // just to be sure
739  itemOutput->setDisabled(true);
740  itemOutput->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
741  }
742 
743  // set checkbox
744  if (coinControl->IsSelected(COutPoint(txhash, out.i)))
745  itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
746  }
747 
748  // amount
749  if (treeMode)
750  {
751  itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
752  itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
753  itemWalletAddress->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)nSum));
754  }
755  }
756 
757  // expand all partially selected
758  if (treeMode)
759  {
760  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
761  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
762  ui->treeWidget->topLevelItem(i)->setExpanded(true);
763  }
764 
765  // sort view
767  ui->treeWidget->setEnabled(true);
768 }
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:15
QString labelForAddress(const QString &address) const
static QString removeSpaces(QString text)
Definition: bitcoinunits.h:112
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string.
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
base58-encoded Bitcoin addresses.
Definition: base58.h:104
Coin Control Features.
Definition: coincontrol.h:12
bool IsSelected(const COutPoint &output) const
Definition: coincontrol.h:50
void UnSelectAll()
Definition: coincontrol.h:65
void Select(const COutPoint &output)
Definition: coincontrol.h:55
void UnSelect(const COutPoint &output)
Definition: coincontrol.h:60
void ListSelected(std::vector< COutPoint > &vOutpoints) const
Definition: coincontrol.h:70
CAmount nMinimumTotalFee
Minimum absolute fee (not per kilobyte)
Definition: coincontrol.h:20
bool operator<(const QTreeWidgetItem &other) const
CAmount GetFeePerK() const
Return the fee in satoshis for a size of 1000 bytes.
Definition: amount.h:55
A reference to a CKey: the Hash160 of its serialized public key.
Definition: pubkey.h:30
const uint256 & GetHash() const
Definition: auxpow.h:113
CTransactionRef tx
Definition: auxpow.h:37
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:20
int nDepth
Definition: wallet.h:363
const CWalletTx * tx
Definition: wallet.h:361
int i
Definition: wallet.h:362
An encapsulated public key.
Definition: pubkey.h:40
bool IsCompressed() const
Check whether this is a compressed public key.
Definition: pubkey.h:167
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:377
CFeeRate estimateSmartFee(int nBlocks, int *answerFoundAtBlocks=NULL) const
Estimate fee rate needed to get into the next nBlocks If no answer can be given at nBlocks,...
Definition: txmempool.cpp:876
double estimateSmartPriority(int nBlocks, int *answerFoundAtBlocks=NULL) const
Estimate priority needed to get into the next nBlocks If no answer can be given at nBlocks,...
Definition: txmempool.cpp:886
An output of a transaction.
Definition: transaction.h:133
CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const
Definition: transaction.h:164
bool IsDust(const CFeeRate &minRelayTxFee) const
Definition: transaction.h:201
QTreeWidgetItem * contextMenuItem
WalletModel * model
friend class CCoinControlWidgetItem
CoinControlDialog(const PlatformStyle *platformStyle, QWidget *parent=0)
void setModel(WalletModel *model)
static void updateLabels(WalletModel *, QDialog *)
const PlatformStyle * platformStyle
QAction * copyTransactionHashAction
Ui::CoinControlDialog * ui
void sortView(int, Qt::SortOrder)
static CCoinControl * coinControl
void showMenu(const QPoint &)
void viewItemChanged(QTreeWidgetItem *, int)
Qt::SortOrder sortOrder
static QList< CAmount > payAmounts
static bool fSubtractFeeFromAmount
void buttonBoxClicked(QAbstractButton *)
int getDisplayUnit()
Definition: optionsmodel.h:65
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:99
void unlockCoin(COutPoint &output)
void listLockedCoins(std::vector< COutPoint > &vOutpts)
void getOutputs(const std::vector< COutPoint > &vOutpoints, std::vector< COutput > &vOutputs)
AddressTableModel * getAddressTableModel()
OptionsModel * getOptionsModel()
void lockCoin(COutPoint &output)
bool getPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
bool isLockedCoin(uint256 hash, unsigned int n) const
bool isSpent(const COutPoint &outpoint) const
void listCoins(std::map< QString, std::vector< COutput > > &mapCoins) const
std::string GetHex() const
Definition: uint256.cpp:21
256-bit opaque blob.
Definition: uint256.h:123
#define ASYMP_UTF8
static CAmount GetRequiredFee(const CMutableTransaction &tx, unsigned int nTxBytes)
Return the minimum required fee taking into account the floating relay fee and user set minimum trans...
Definition: wallet.cpp:2843
static CAmount GetMinimumFee(const CMutableTransaction &tx, unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool &pool)
Estimate the minimum fee considering user set parameters and the required fee.
Definition: wallet.cpp:2854
int64_t GetTxTime() const
Definition: wallet.cpp:1407
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:86
void setClipboard(const QString &str)
Definition: guiutil.cpp:852
bool operator<(const CNetAddr &a, const CNetAddr &b)
Definition: netaddress.cpp:280
CFeeRate dustRelayFee
Definition: policy.cpp:210
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Definition: standard.cpp:182
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:71
A mutable version of CTransaction.
Definition: transaction.h:413
std::vector< CTxOut > vout
Definition: transaction.h:416
double AllowFreeThreshold()
Definition: txmempool.h:33
uint256 uint256S(const char *str)
Definition: uint256.h:144
#define PAIRTYPE(t1, t2)
This is needed because the foreach macro can't get over the comma in pair<t1, t2>
CTxMemPool mempool(::minRelayTxFee)
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE)
Transaction fee set by the user.
unsigned int nTxConfirmTarget
Definition: wallet.cpp:41
bool fSendFreeTransactions
Definition: wallet.cpp:43