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