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