Feathercoin  0.5.0
P2P Digital Currency
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
coincontroldialog.cpp
Go to the documentation of this file.
1 #include "coincontroldialog.h"
2 #include "ui_coincontroldialog.h"
3 
4 #include "init.h"
5 #include "bitcoinunits.h"
6 #include "walletmodel.h"
7 #include "addresstablemodel.h"
8 #include "optionsmodel.h"
9 #include "guiutil.h"
10 #include "coincontrol.h"
11 
12 #include <QApplication>
13 #include <QCheckBox>
14 #include <QClipboard>
15 #include <QColor>
16 #include <QCursor>
17 #include <QDateTime>
18 #include <QDialogButtonBox>
19 #include <QFlags>
20 #include <QIcon>
21 #include <QString>
22 #include <QTreeWidget>
23 #include <QTreeWidgetItem>
24 
25 using namespace std;
26 QList<qint64> CoinControlDialog::payAmounts;
28 
30  QDialog(parent),
31  ui(new Ui::CoinControlDialog),
32  model(0)
33 {
34  ui->setupUi(this);
35 
36  // context menu actions
37  QAction *copyAddressAction = new QAction(tr("Copy address"), this);
38  QAction *copyLabelAction = new QAction(tr("Copy label"), this);
39  QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
40  copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this); // we need to enable/disable this
41  lockAction = new QAction(tr("Lock unspent"), this); // we need to enable/disable this
42  unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this
43 
44  // context menu
45  contextMenu = new QMenu();
46  contextMenu->addAction(copyAddressAction);
47  contextMenu->addAction(copyLabelAction);
48  contextMenu->addAction(copyAmountAction);
50  contextMenu->addSeparator();
51  contextMenu->addAction(lockAction);
52  contextMenu->addAction(unlockAction);
53 
54  // context menu signals
55  connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
56  connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
57  connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
58  connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
59  connect(copyTransactionHashAction, SIGNAL(triggered()), this, SLOT(copyTransactionHash()));
60  connect(lockAction, SIGNAL(triggered()), this, SLOT(lockCoin()));
61  connect(unlockAction, SIGNAL(triggered()), this, SLOT(unlockCoin()));
62 
63  // clipboard actions
64  QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
65  QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
66  QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
67  QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
68  QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
69  QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this);
70  QAction *clipboardLowOutputAction = new QAction(tr("Copy low output"), this);
71  QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
72 
73  connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(clipboardQuantity()));
74  connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(clipboardAmount()));
75  connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(clipboardFee()));
76  connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(clipboardAfterFee()));
77  connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(clipboardBytes()));
78  connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(clipboardPriority()));
79  connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(clipboardLowOutput()));
80  connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(clipboardChange()));
81 
82  ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
83  ui->labelCoinControlAmount->addAction(clipboardAmountAction);
84  ui->labelCoinControlFee->addAction(clipboardFeeAction);
85  ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
86  ui->labelCoinControlBytes->addAction(clipboardBytesAction);
87  ui->labelCoinControlPriority->addAction(clipboardPriorityAction);
88  ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
89  ui->labelCoinControlChange->addAction(clipboardChangeAction);
90 
91  // toggle tree/list mode
92  connect(ui->radioTreeMode, SIGNAL(toggled(bool)), this, SLOT(radioTreeMode(bool)));
93  connect(ui->radioListMode, SIGNAL(toggled(bool)), this, SLOT(radioListMode(bool)));
94 
95  // click on checkbox
96  connect(ui->treeWidget, SIGNAL(itemChanged( QTreeWidgetItem*, int)), this, SLOT(viewItemChanged( QTreeWidgetItem*, int)));
97 
98  // click on header
99  #if QT_VERSION < 0x050000
100  ui->treeWidget->header()->setClickable(true);
101  #else
102  ui->treeWidget->header()->setSectionsClickable(true);
103  #endif
104  connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this, SLOT(headerSectionClicked(int)));
105 
106  // ok button
107  connect(ui->buttonBox, SIGNAL(clicked( QAbstractButton*)), this, SLOT(buttonBoxClicked(QAbstractButton*)));
108 
109  // (un)select all
110  connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked()));
111 
112  ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84);
113  ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100);
114  ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170);
115  ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 290);
116  ui->treeWidget->setColumnWidth(COLUMN_DATE, 110);
117  ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 100);
118  ui->treeWidget->setColumnWidth(COLUMN_PRIORITY, 100);
119  ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transacton hash in this column, but dont show it
120  ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but dont show it
121  ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64 in this column, but dont show it
122  ui->treeWidget->setColumnHidden(COLUMN_PRIORITY_INT64, true); // store priority int64 in this column, but dont show it
123 
124  // default view is sorted by amount desc
125  sortView(COLUMN_AMOUNT_INT64, Qt::DescendingOrder);
126 }
127 
129 {
130  delete ui;
131 }
132 
134 {
135  this->model = model;
136 
137  if(model && model->getOptionsModel() && model->getAddressTableModel())
138  {
139  updateView();
141  CoinControlDialog::updateLabels(model, this);
142  }
143 }
144 
145 // helper function str_pad
146 QString CoinControlDialog::strPad(QString s, int nPadLength, QString sPadding)
147 {
148  while (s.length() < nPadLength)
149  s = sPadding + s;
150 
151  return s;
152 }
153 
154 // ok button
155 void CoinControlDialog::buttonBoxClicked(QAbstractButton* button)
156 {
157  if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
158  done(QDialog::Accepted); // closes the dialog
159 }
160 
161 // (un)select all
163 {
164  Qt::CheckState state = Qt::Checked;
165  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
166  {
167  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != Qt::Unchecked)
168  {
169  state = Qt::Unchecked;
170  break;
171  }
172  }
173  ui->treeWidget->setEnabled(false);
174  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
175  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != state)
176  ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state);
177  ui->treeWidget->setEnabled(true);
178  if (state == Qt::Unchecked)
179  coinControl->UnSelectAll(); // just to be sure
181 }
182 
183 // context menu
184 void CoinControlDialog::showMenu(const QPoint &point)
185 {
186  QTreeWidgetItem *item = ui->treeWidget->itemAt(point);
187  if(item)
188  {
189  contextMenuItem = item;
190 
191  // disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu
192  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)
193  {
194  copyTransactionHashAction->setEnabled(true);
195  if (model->isLockedCoin(uint256(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()))
196  {
197  lockAction->setEnabled(false);
198  unlockAction->setEnabled(true);
199  }
200  else
201  {
202  lockAction->setEnabled(true);
203  unlockAction->setEnabled(false);
204  }
205  }
206  else // this means click on parent node in tree mode -> disable all
207  {
208  copyTransactionHashAction->setEnabled(false);
209  lockAction->setEnabled(false);
210  unlockAction->setEnabled(false);
211  }
212 
213  // show context menu
214  contextMenu->exec(QCursor::pos());
215  }
216 }
217 
218 // context menu action: copy amount
220 {
222 }
223 
224 // context menu action: copy label
226 {
227  if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_LABEL).length() == 0 && contextMenuItem->parent())
229  else
231 }
232 
233 // context menu action: copy address
235 {
236  if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_ADDRESS).length() == 0 && contextMenuItem->parent())
238  else
240 }
241 
242 // context menu action: copy transaction id
244 {
246 }
247 
248 // context menu action: lock coin
250 {
251  if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked)
252  contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
253 
254  COutPoint outpt(uint256(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
255  model->lockCoin(outpt);
256  contextMenuItem->setDisabled(true);
257  contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed"));
259 }
260 
261 // context menu action: unlock coin
263 {
264  COutPoint outpt(uint256(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
265  model->unlockCoin(outpt);
266  contextMenuItem->setDisabled(false);
267  contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
269 }
270 
271 // copy label "Quantity" to clipboard
273 {
274  GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
275 }
276 
277 // copy label "Amount" to clipboard
279 {
280  GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
281 }
282 
283 // copy label "Fee" to clipboard
285 {
286  GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")));
287 }
288 
289 // copy label "After fee" to clipboard
291 {
292  GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")));
293 }
294 
295 // copy label "Bytes" to clipboard
297 {
298  GUIUtil::setClipboard(ui->labelCoinControlBytes->text());
299 }
300 
301 // copy label "Priority" to clipboard
303 {
304  GUIUtil::setClipboard(ui->labelCoinControlPriority->text());
305 }
306 
307 // copy label "Low output" to clipboard
309 {
310  GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
311 }
312 
313 // copy label "Change" to clipboard
315 {
316  GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")));
317 }
318 
319 // treeview: sort
320 void CoinControlDialog::sortView(int column, Qt::SortOrder order)
321 {
322  sortColumn = column;
323  sortOrder = order;
324  ui->treeWidget->sortItems(column, order);
325  ui->treeWidget->header()->setSortIndicator((sortColumn == COLUMN_AMOUNT_INT64 ? COLUMN_AMOUNT : (sortColumn == COLUMN_PRIORITY_INT64 ? COLUMN_PRIORITY : sortColumn)), sortOrder);
326 }
327 
328 // treeview: clicked on header
330 {
331  if (logicalIndex == COLUMN_CHECKBOX) // click on most left column -> do nothing
332  {
333  ui->treeWidget->header()->setSortIndicator((sortColumn == COLUMN_AMOUNT_INT64 ? COLUMN_AMOUNT : (sortColumn == COLUMN_PRIORITY_INT64 ? COLUMN_PRIORITY : sortColumn)), sortOrder);
334  }
335  else
336  {
337  if (logicalIndex == COLUMN_AMOUNT) // sort by amount
338  logicalIndex = COLUMN_AMOUNT_INT64;
339 
340  if (logicalIndex == COLUMN_PRIORITY) // sort by priority
341  logicalIndex = COLUMN_PRIORITY_INT64;
342 
343  if (sortColumn == logicalIndex)
344  sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder);
345  else
346  {
347  sortColumn = logicalIndex;
348  sortOrder = ((sortColumn == COLUMN_AMOUNT_INT64 || sortColumn == COLUMN_PRIORITY_INT64 || sortColumn == COLUMN_DATE || sortColumn == COLUMN_CONFIRMATIONS) ? Qt::DescendingOrder : Qt::AscendingOrder); // if amount,date,conf,priority then default => desc, else default => asc
349  }
350 
352  }
353 }
354 
355 // toggle tree mode
357 {
358  if (checked && model)
359  updateView();
360 }
361 
362 // toggle list mode
364 {
365  if (checked && model)
366  updateView();
367 }
368 
369 // checkbox clicked by user
370 void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
371 {
372  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)
373  {
374  COutPoint outpt(uint256(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt());
375 
376  if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked)
377  coinControl->UnSelect(outpt);
378  else if (item->isDisabled()) // locked (this happens if "check all" through parent node)
379  item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
380  else
381  coinControl->Select(outpt);
382 
383  // selection changed -> update labels
384  if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all
386  }
387 }
388 
389 // helper function, return human readable label for priority number
390 QString CoinControlDialog::getPriorityLabel(double dPriority)
391 {
392  if (CTransaction::AllowFree(dPriority)) // at least medium
393  {
394  if (CTransaction::AllowFree(dPriority / 10000)) return tr("highest");
395  else if (CTransaction::AllowFree(dPriority / 1000)) return tr("high");
396  else if (CTransaction::AllowFree(dPriority / 100)) return tr("medium-high");
397  else return tr("medium");
398  }
399  else
400  {
401  if (CTransaction::AllowFree(dPriority * 100)) return tr("low-medium");
402  else if (CTransaction::AllowFree(dPriority * 10000)) return tr("low");
403  else return tr("lowest");
404  }
405 }
406 
407 // shows count of locked unspent outputs
409 {
410  vector<COutPoint> vOutpts;
411  model->listLockedCoins(vOutpts);
412  if (vOutpts.size() > 0)
413  {
414  ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size()));
415  ui->labelLocked->setVisible(true);
416  }
417  else ui->labelLocked->setVisible(false);
418 }
419 
420 void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
421 {
422  if (!model) return;
423 
424  // nPayAmount
425  qint64 nPayAmount = 0;
426  bool fLowOutput = false;
427  bool fDust = false;
428  unsigned int nQuantityDust = 0;
429  CTransaction txDummy;
430  foreach(const qint64 &amount, CoinControlDialog::payAmounts)
431  {
432  nPayAmount += amount;
433 
434  if (amount > 0)
435  {
436  if (amount < CENT) {
437  fLowOutput = true;
438  nQuantityDust++;
439  }
440 
441  CTxOut txout(amount, (CScript)vector<unsigned char>(24, 0));
442  txDummy.vout.push_back(txout);
443  if (txout.IsDust())
444  fDust = true;
445  }
446  }
447 
448  QString sPriorityLabel = "";
449  int64 nAmount = 0;
450  int64 nPayFee = 0;
451  int64 nAfterFee = 0;
452  int64 nChange = 0;
453  unsigned int nBytes = 0;
454  unsigned int nBytesInputs = 0;
455  double dPriority = 0;
456  double dPriorityInputs = 0;
457  unsigned int nQuantity = 0;
458 
459  vector<COutPoint> vCoinControl;
460  vector<COutput> vOutputs;
461  coinControl->ListSelected(vCoinControl);
462  model->getOutputs(vCoinControl, vOutputs);
463 
464  BOOST_FOREACH(const COutput& out, vOutputs)
465  {
466  // unselect already spent, very unlikely scenario, this could happen when selected are spent elsewhere, like rpc or another computer
467  if (out.tx->IsSpent(out.i))
468  {
469  uint256 txhash = out.tx->GetHash();
470  COutPoint outpt(txhash, out.i);
471  coinControl->UnSelect(outpt);
472  continue;
473  }
474 
475  // Quantity
476  nQuantity++;
477 
478  // Amount
479  nAmount += out.tx->vout[out.i].nValue;
480 
481  // Priority
482  dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
483 
484  // Bytes
485  CTxDestination address;
486  if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
487  {
488  CPubKey pubkey;
489  CKeyID *keyid = boost::get<CKeyID>(&address);
490  if (keyid && model->getPubKey(*keyid, pubkey))
491  nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
492  else
493  nBytesInputs += 148; // in all error cases, simply assume 148 here
494  }
495  else nBytesInputs += 148;
496  }
497 
498  // calculation
499  if (nQuantity > 0)
500  {
501  // Bytes
502  nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here
503 
504  // Priority
505  dPriority = dPriorityInputs / nBytes;
506  sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority);
507 
508  // Fee
509  int64 nFee = nTransactionFee * (1 + (int64)nBytes / 1000);
510 
511  // Min Fee
512  int64 nMinFee = CTransaction::nMinTxFee * (1 + (int64)nBytes / 1000) + CTransaction::nMinTxFee * nQuantityDust;
513  if (CTransaction::AllowFree(dPriority) && nBytes < 5000)
514  nMinFee = 0;
515 
516  nPayFee = max(nFee, nMinFee);
517 
518  if (nPayAmount > 0)
519  {
520  nChange = nAmount - nPayFee - nPayAmount;
521 
522  // require CTransaction::nMinTxFee if any output is less than 0.01
523  if (nPayFee < CTransaction::nMinTxFee && fLowOutput)
524  {
525  nChange = nChange + nPayFee - CTransaction::nMinTxFee;
526  nPayFee = CTransaction::nMinTxFee * nQuantityDust;
527  }
528 
529  // if sub-cent change is required, the fee must be raised to at least CTransaction::nMinTxFee
530  if (nPayFee < CTransaction::nMinTxFee && nChange > 0 && nChange < CENT)
531  {
532  if (nChange < CTransaction::nMinTxFee) // change < 0.0001 => simply move all change to fees
533  {
534  nPayFee += nChange;
535  nChange = 0;
536  }
537  else
538  {
539  nChange = nChange + nPayFee - CTransaction::nMinTxFee;
540  nPayFee = CTransaction::nMinTxFee;
541  }
542  }
543 
544  // Never create dust outputs; if we would, just add the dust to the fee.
545  if (nChange > 0 && nChange < CENT)
546  {
547  CTxOut txout(nChange, (CScript)vector<unsigned char>(24, 0));
548  if (txout.IsDust())
549  {
550  nPayFee += nChange;
551  nChange = 0;
552  }
553  }
554 
555  if (nChange == 0)
556  nBytes -= 34;
557  }
558 
559  // after fee
560  nAfterFee = nAmount - nPayFee;
561  if (nAfterFee < 0)
562  nAfterFee = 0;
563  }
564 
565  // actually update labels
566  int nDisplayUnit = BitcoinUnits::BTC;
567  if (model && model->getOptionsModel())
568  nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
569 
570  QLabel *l1 = dialog->findChild<QLabel *>("labelCoinControlQuantity");
571  QLabel *l2 = dialog->findChild<QLabel *>("labelCoinControlAmount");
572  QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee");
573  QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee");
574  QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes");
575  QLabel *l6 = dialog->findChild<QLabel *>("labelCoinControlPriority");
576  QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput");
577  QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange");
578 
579  // enable/disable "low output" and "change"
580  dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0);
581  dialog->findChild<QLabel *>("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0);
582  dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0);
583  dialog->findChild<QLabel *>("labelCoinControlChange") ->setEnabled(nPayAmount > 0);
584 
585  // stats
586  l1->setText(QString::number(nQuantity)); // Quantity
587  l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount)); // Amount
588  l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee
589  l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee
590  l5->setText(((nBytes > 0) ? "~" : "") + QString::number(nBytes)); // Bytes
591  l6->setText(sPriorityLabel); // Priority
592  l7->setText((fLowOutput ? (fDust ? tr("DUST") : tr("yes")) : tr("no"))); // Low Output / Dust
593  l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change
594 
595  // turn labels "red"
596  l5->setStyleSheet((nBytes >= 5000) ? "color:red;" : ""); // Bytes >= 5000
597  l6->setStyleSheet((!CTransaction::AllowFree(dPriority)) ? "color:red;" : ""); // Priority < "medium"
598  l7->setStyleSheet((fLowOutput) ? "color:red;" : ""); // Low Output = "yes"
599  l8->setStyleSheet((nChange > 0 && nChange < CENT) ? "color:red;" : ""); // Change < 0.01BTC
600 
601  // tool tips
602  l5->setToolTip(tr("This label turns red, if the transaction size is bigger than 5000 bytes.\n\n This means a fee of at least %1 per kb is required.\n\n Can vary +/- 1 Byte per input.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)));
603  l6->setToolTip(tr("Transactions with higher priority get more likely into a block.\n\nThis label turns red, if the priority is smaller than \"medium\".\n\n This means a fee of at least %1 per kb is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)));
604  l7->setToolTip(tr("This label turns red, if any recipient receives an amount smaller than %1.\n\n This means a fee of at least %2 is required. \n\n Amounts below 0.546 times the minimum relay fee are shown as DUST.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)));
605  l8->setToolTip(tr("This label turns red, if the change is smaller than %1.\n\n This means a fee of at least %2 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)));
606  dialog->findChild<QLabel *>("labelCoinControlBytesText") ->setToolTip(l5->toolTip());
607  dialog->findChild<QLabel *>("labelCoinControlPriorityText") ->setToolTip(l6->toolTip());
608  dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip());
609  dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setToolTip(l8->toolTip());
610 
611  // Insufficient funds
612  QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds");
613  if (label)
614  label->setVisible(nChange < 0);
615 }
616 
618 {
619  bool treeMode = ui->radioTreeMode->isChecked();
620 
621  ui->treeWidget->clear();
622  ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox
623  ui->treeWidget->setAlternatingRowColors(!treeMode);
624  QFlags<Qt::ItemFlag> flgCheckbox=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
625  QFlags<Qt::ItemFlag> flgTristate=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
626 
627  int nDisplayUnit = BitcoinUnits::BTC;
628  if (model && model->getOptionsModel())
629  nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
630 
631  map<QString, vector<COutput> > mapCoins;
632  model->listCoins(mapCoins);
633 
634  BOOST_FOREACH(PAIRTYPE(QString, vector<COutput>) coins, mapCoins)
635  {
636  QTreeWidgetItem *itemWalletAddress = new QTreeWidgetItem();
637  QString sWalletAddress = coins.first;
638  QString sWalletLabel = "";
640  sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress);
641  if (sWalletLabel.length() == 0)
642  sWalletLabel = tr("(no label)");
643 
644  if (treeMode)
645  {
646  // wallet address
647  ui->treeWidget->addTopLevelItem(itemWalletAddress);
648 
649  itemWalletAddress->setFlags(flgTristate);
650  itemWalletAddress->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked);
651 
652  for (int i = 0; i < ui->treeWidget->columnCount(); i++)
653  itemWalletAddress->setBackground(i, QColor(248, 247, 246));
654 
655  // label
656  itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel);
657 
658  // address
659  itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress);
660  }
661 
662  int64 nSum = 0;
663  double dPrioritySum = 0;
664  int nChildren = 0;
665  int nInputSum = 0;
666  BOOST_FOREACH(const COutput& out, coins.second)
667  {
668  int nInputSize = 148; // 180 if uncompressed public key
669  nSum += out.tx->vout[out.i].nValue;
670  nChildren++;
671 
672  QTreeWidgetItem *itemOutput;
673  if (treeMode) itemOutput = new QTreeWidgetItem(itemWalletAddress);
674  else itemOutput = new QTreeWidgetItem(ui->treeWidget);
675  itemOutput->setFlags(flgCheckbox);
676  itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked);
677 
678  // address
679  CTxDestination outputAddress;
680  QString sAddress = "";
681  if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress))
682  {
683  sAddress = CBitcoinAddress(outputAddress).ToString().c_str();
684 
685  // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs
686  if (!treeMode || (!(sAddress == sWalletAddress)))
687  itemOutput->setText(COLUMN_ADDRESS, sAddress);
688 
689  CPubKey pubkey;
690  CKeyID *keyid = boost::get<CKeyID>(&outputAddress);
691  if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed())
692  nInputSize = 180;
693  }
694 
695  // label
696  if (!(sAddress == sWalletAddress)) // change
697  {
698  // tooltip from where the change comes from
699  itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress));
700  itemOutput->setText(COLUMN_LABEL, tr("(change)"));
701  }
702  else if (!treeMode)
703  {
704  QString sLabel = "";
706  sLabel = model->getAddressTableModel()->labelForAddress(sAddress);
707  if (sLabel.length() == 0)
708  sLabel = tr("(no label)");
709  itemOutput->setText(COLUMN_LABEL, sLabel);
710  }
711 
712  // amount
713  itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue));
714  itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly
715 
716  // date
717  itemOutput->setText(COLUMN_DATE, QDateTime::fromTime_t(out.tx->GetTxTime()).toString("yy-MM-dd hh:mm"));
718 
719  // confirmations
720  itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " "));
721 
722  // priority
723  double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10
724  itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority));
725  itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64)dPriority), 20, " "));
726  dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
727  nInputSum += nInputSize;
728 
729  // transaction hash
730  uint256 txhash = out.tx->GetHash();
731  itemOutput->setText(COLUMN_TXHASH, txhash.GetHex().c_str());
732 
733  // vout index
734  itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i));
735 
736  // disable locked coins
737  if (model->isLockedCoin(txhash, out.i))
738  {
739  COutPoint outpt(txhash, out.i);
740  coinControl->UnSelect(outpt); // just to be sure
741  itemOutput->setDisabled(true);
742  itemOutput->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed"));
743  }
744 
745  // set checkbox
746  if (coinControl->IsSelected(txhash, out.i))
747  itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Checked);
748  }
749 
750  // amount
751  if (treeMode)
752  {
753  dPrioritySum = dPrioritySum / (nInputSum + 78);
754  itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
755  itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
756  itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " "));
757  itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum));
758  itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64)dPrioritySum), 20, " "));
759  }
760  }
761 
762  // expand all partially selected
763  if (treeMode)
764  {
765  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
766  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
767  ui->treeWidget->topLevelItem(i)->setExpanded(true);
768  }
769 
770  // sort view
772  ui->treeWidget->setEnabled(true);
773 }
void viewItemChanged(QTreeWidgetItem *, int)
void getOutputs(const std::vector< COutPoint > &vOutpoints, std::vector< COutput > &vOutputs)
int i
Definition: wallet.h:700
void lockCoin(COutPoint &output)
static QString formatWithUnit(int unit, qint64 amount, bool plussign=false)
Format as string (with unit)
static CCoinControl * coinControl
#define PAIRTYPE(t1, t2)
Definition: util.h:78
Definition: aboutdialog.h:6
bool isLockedCoin(uint256 hash, unsigned int n) const
bool IsSpent(unsigned int nOut) const
Definition: wallet.h:545
static bool AllowFree(double dPriority)
Definition: main.h:623
uint256 GetHash() const
Definition: main.h:515
bool IsSelected(const uint256 &hash, unsigned int n) const
Definition: coincontrol.h:26
bool IsDust() const
Definition: main.cpp:367
AddressTableModel * getAddressTableModel()
Coin Control Features.
Definition: coincontrol.h:5
int64 nTransactionFee
Definition: main.cpp:84
static int64 nMinTxFee
Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) ...
Definition: main.h:480
QAction * copyTransactionHashAction
int nDepth
Definition: wallet.h:701
Ui::CoinControlDialog * ui
void setClipboard(const QString &str)
Definition: guiutil.cpp:184
std::vector< CTxOut > vout
Definition: main.h:485
An encapsulated public key.
Definition: key.h:40
bool getPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
MTState * state
Definition: db_test.cc:1708
static QString getPriorityLabel(double)
CoinControlDialog(QWidget *parent=0)
std::string ToString() const
Definition: base58.h:229
static void updateLabels(WalletModel *, QDialog *)
An output of a transaction.
Definition: main.h:400
int getDisplayUnit()
Definition: optionsmodel.h:50
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: main.h:278
std::string GetHex() const
Definition: uint256.h:298
void UnSelectAll()
Definition: coincontrol.h:42
int64 GetTxTime() const
Definition: wallet.cpp:592
bool IsCompressed() const
Definition: key.h:147
256-bit unsigned integer
Definition: uint256.h:537
void setModel(WalletModel *model)
QTreeWidgetItem * contextMenuItem
QString labelForAddress(const QString &address) const
void listLockedCoins(std::vector< COutPoint > &vOutpts)
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:244
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:36
void Select(COutPoint &output)
Definition: coincontrol.h:32
void unlockCoin(COutPoint &output)
A reference to a CKey: the Hash160 of its serialized public key.
Definition: key.h:24
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Definition: script.cpp:1422
const CWalletTx * tx
Definition: wallet.h:699
void sortView(int, Qt::SortOrder)
static QList< qint64 > payAmounts
Qt::SortOrder sortOrder
QString strPad(QString, int, QString)
void ListSelected(std::vector< COutPoint > &vOutpoints)
Definition: coincontrol.h:47
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
Definition: script.h:62
void buttonBoxClicked(QAbstractButton *)
void UnSelect(COutPoint &output)
Definition: coincontrol.h:37
The basic transaction that is broadcasted on the network and contained in blocks. ...
Definition: main.h:477
WalletModel * model
void showMenu(const QPoint &)
void listCoins(std::map< QString, std::vector< COutput > > &mapCoins) const
static QString format(int unit, qint64 amount, bool plussign=false)
Format as string.
void * arg
Definition: env_posix.cc:716
OptionsModel * getOptionsModel()
long long int64
Definition: serialize.h:25