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