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