New transaction fees scheme
[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         bool fAllowFree = CTransaction::AllowFree(dPriority);
492
493         // Disable free transactions until 1 July 2014
494         if (!fTestNet && txDummy.nTime < FEE_SWITCH_TIME)
495         {
496             fAllowFree = false;
497         }
498
499         int64 nMinFee = txDummy.GetMinFee(1, fAllowFree, GMF_SEND, nBytes);
500
501         nPayFee = max(nFee, nMinFee);
502
503         if (nPayAmount > 0)
504         {
505             nChange = nAmount - nPayFee - nPayAmount;
506
507             // if sub-cent change is required, the fee must be raised to at least CTransaction::nMinTxFee   
508             if (txDummy.nTime < FEE_SWITCH_TIME && !fTestNet)
509             {
510                 if (nPayFee < CENT && nChange > 0 && nChange < CENT)
511                 {
512                     if (nChange < CENT) // change < 0.01 => simply move all change to fees
513                     {
514                         nPayFee = nChange;
515                         nChange = 0;
516                     }
517                     else
518                     {
519                         nChange = nChange + nPayFee - CENT;
520                         nPayFee = CENT;
521                     }
522                 }
523             }
524
525             if (nChange == 0)
526                 nBytes -= 34;
527         }
528
529         // after fee
530         nAfterFee = nAmount - nPayFee;
531         if (nAfterFee < 0)
532             nAfterFee = 0;
533     }
534
535     // actually update labels
536     int nDisplayUnit = BitcoinUnits::BTC;
537     if (model && model->getOptionsModel())
538         nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
539
540     QLabel *l1 = dialog->findChild<QLabel *>("labelCoinControlQuantity");
541     QLabel *l2 = dialog->findChild<QLabel *>("labelCoinControlAmount");
542     QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee");
543     QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee");
544     QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes");
545     QLabel *l6 = dialog->findChild<QLabel *>("labelCoinControlPriority");
546     QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput");
547     QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange");
548
549     // enable/disable "low output" and "change"
550     dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0);
551     dialog->findChild<QLabel *>("labelCoinControlLowOutput")    ->setEnabled(nPayAmount > 0);
552     dialog->findChild<QLabel *>("labelCoinControlChangeText")   ->setEnabled(nPayAmount > 0);
553     dialog->findChild<QLabel *>("labelCoinControlChange")       ->setEnabled(nPayAmount > 0);
554
555     // stats
556     l1->setText(QString::number(nQuantity));                                 // Quantity        
557     l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount));        // Amount
558     l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee));        // Fee
559     l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee));      // After Fee
560     l5->setText(((nBytes > 0) ? "~" : "") + QString::number(nBytes));                                    // Bytes
561     l6->setText(sPriorityLabel);                                             // Priority
562     l7->setText((fLowOutput ? (fDust ? tr("DUST") : tr("yes")) : tr("no"))); // Low Output / Dust
563     l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange));        // Change
564
565     // turn labels "red"
566     l5->setStyleSheet((nBytes >= 5000) ? "color:red;" : "");               // Bytes >= 5000
567     l6->setStyleSheet((dPriority <= 576000) ? "color:red;" : "");         // Priority < "medium"
568     l7->setStyleSheet((fLowOutput) ? "color:red;" : "");                    // Low Output = "yes"
569     l8->setStyleSheet((nChange > 0 && nChange < CENT) ? "color:red;" : ""); // Change < 0.01BTC
570
571     // tool tips
572     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, CENT)));
573     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)));
574     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)));
575     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)));
576     dialog->findChild<QLabel *>("labelCoinControlBytesText")    ->setToolTip(l5->toolTip());
577     dialog->findChild<QLabel *>("labelCoinControlPriorityText") ->setToolTip(l6->toolTip());
578     dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip());
579     dialog->findChild<QLabel *>("labelCoinControlChangeText")   ->setToolTip(l8->toolTip());
580
581     // Insufficient funds
582     QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds");
583     if (label)
584         label->setVisible(nChange < 0);
585 }
586
587 void CoinControlDialog::updateView()
588 {
589     bool treeMode = ui->radioTreeMode->isChecked();
590
591     ui->treeWidget->clear();
592     ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox
593     ui->treeWidget->setAlternatingRowColors(!treeMode);
594     QFlags<Qt::ItemFlag> flgCheckbox=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
595     QFlags<Qt::ItemFlag> flgTristate=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;    
596     
597     int nDisplayUnit = BitcoinUnits::BTC;
598     if (model && model->getOptionsModel())
599         nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
600         
601     map<QString, vector<COutput> > mapCoins;
602     model->listCoins(mapCoins);
603
604     BOOST_FOREACH(PAIRTYPE(QString, vector<COutput>) coins, mapCoins)
605     {
606         QTreeWidgetItem *itemWalletAddress = new QTreeWidgetItem();
607         QString sWalletAddress = coins.first;
608         QString sWalletLabel = "";
609         if (model->getAddressTableModel())
610             sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress);
611         if (sWalletLabel.length() == 0)
612             sWalletLabel = tr("(no label)");
613         
614         if (treeMode)
615         {
616             // wallet address
617             ui->treeWidget->addTopLevelItem(itemWalletAddress);
618
619             itemWalletAddress->setFlags(flgTristate);
620             itemWalletAddress->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked);
621             
622             for (int i = 0; i < ui->treeWidget->columnCount(); i++)
623                 itemWalletAddress->setBackground(i, QColor(248, 247, 246));
624             
625             // label
626             itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel);
627
628             // address
629             itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress);
630         }
631
632         int64 nSum = 0;
633         double dPrioritySum = 0;
634         int nChildren = 0;
635         int nInputSum = 0;
636         BOOST_FOREACH(const COutput& out, coins.second)
637         {
638             int nInputSize = 148; // 180 if uncompressed public key
639             nSum += out.tx->vout[out.i].nValue;
640             nChildren++;
641             
642             QTreeWidgetItem *itemOutput;
643             if (treeMode)    itemOutput = new QTreeWidgetItem(itemWalletAddress);
644             else             itemOutput = new QTreeWidgetItem(ui->treeWidget);
645             itemOutput->setFlags(flgCheckbox);
646             itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked);
647                 
648             // address
649             CTxDestination outputAddress;
650             QString sAddress = "";
651             if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress))
652             {
653                 sAddress = CBitcoinAddress(outputAddress).ToString().c_str();
654                 
655                 // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs
656                 if (!treeMode || (!(sAddress == sWalletAddress)))
657                     itemOutput->setText(COLUMN_ADDRESS, sAddress);
658                     
659                 CPubKey pubkey;
660                 CKeyID *keyid = boost::get< CKeyID >(&outputAddress);
661                 if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed())
662                     nInputSize = 180;
663             }
664
665             // label
666             if (!(sAddress == sWalletAddress)) // change
667             {
668                 // tooltip from where the change comes from
669                 itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress));
670                 itemOutput->setText(COLUMN_LABEL, tr("(change)"));
671             }
672             else if (!treeMode)
673             {
674                 QString sLabel = "";
675                 if (model->getAddressTableModel())
676                     sLabel = model->getAddressTableModel()->labelForAddress(sAddress);
677                 if (sLabel.length() == 0)
678                     sLabel = tr("(no label)");
679                 itemOutput->setText(COLUMN_LABEL, sLabel); 
680             }
681
682             // amount
683             itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue));
684             itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly
685
686             // date
687             itemOutput->setText(COLUMN_DATE, QDateTime::fromTime_t(out.tx->GetTxTime()).toUTC().toString("yy-MM-dd hh:mm"));
688             
689             // immature PoS reward
690             if (out.tx->IsCoinStake() && out.tx->GetBlocksToMaturity() > 0 && out.tx->GetDepthInMainChain() > 0) {
691               itemOutput->setBackground(COLUMN_CONFIRMATIONS, Qt::red);
692               itemOutput->setDisabled(true);
693             }
694
695             // confirmations
696             itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " "));
697             
698             // priority
699             double dPriority = ((double)out.tx->vout[out.i].nValue  / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10
700             itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority));
701             itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64)dPriority), 20, " "));
702             dPrioritySum += (double)out.tx->vout[out.i].nValue  * (out.nDepth+1);
703             nInputSum    += nInputSize;
704             
705             // transaction hash
706             uint256 txhash = out.tx->GetHash();
707             itemOutput->setText(COLUMN_TXHASH, txhash.GetHex().c_str());
708     
709             // vout index
710             itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i));
711             
712             // disable locked coins     
713             /*if (model->isLockedCoin(txhash, out.i))
714             {
715                 COutPoint outpt(txhash, out.i);
716                 coinControl->UnSelect(outpt); // just to be sure
717                 itemOutput->setDisabled(true);
718                 itemOutput->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed"));
719             }*/
720               
721             // set checkbox
722             if (coinControl->IsSelected(txhash, out.i))
723                 itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Checked);
724         }
725
726         // amount
727         if (treeMode)
728         {
729             dPrioritySum = dPrioritySum / (nInputSum + 78);
730             itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
731             itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
732             itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " "));
733             itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum));
734             itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64)dPrioritySum), 20, " "));
735         }
736     }
737     
738     // expand all partially selected
739     if (treeMode)
740     {
741         for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
742             if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
743                 ui->treeWidget->topLevelItem(i)->setExpanded(true);
744     }
745     
746     // sort view
747     sortView(sortColumn, sortOrder);
748     ui->treeWidget->setEnabled(true);
749 }