9ea82a4d8f4bccf200d136bba056c792ec88fe48
[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 "wallet.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(pwalletMain->ExtractAddress(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                 CTxDestination dest = address.Get();
482                 CKeyID *keyid = boost::get< CKeyID >(&dest);
483                 if (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     BOOST_FOREACH(PAIRTYPE(QString, vector<COutput>) 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         BOOST_FOREACH(const COutput& out, coins.second)
626         {
627             int nInputSize = 148; // 180 if uncompressed public key
628             nSum += out.tx->vout[out.i].nValue;
629             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(pwalletMain->ExtractAddress(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                     CTxDestination dest = outputAddress.Get();
655                     CKeyID *keyid = boost::get< CKeyID >(&dest);
656                     if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed())
657                         nInputSize = 180;
658                 }
659             }
660
661             // label
662             if (!(sAddress == sWalletAddress)) // change
663             {
664                 // tooltip from where the change comes from
665                 itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress));
666                 itemOutput->setText(COLUMN_LABEL, tr("(change)"));
667             }
668             else if (!treeMode)
669             {
670                 QString sLabel = "";
671                 if (model->getAddressTableModel())
672                     sLabel = model->getAddressTableModel()->labelForAddress(sAddress);
673                 if (sLabel.length() == 0)
674                     sLabel = tr("(no label)");
675                 itemOutput->setText(COLUMN_LABEL, sLabel); 
676             }
677
678             // amount
679             itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue));
680             itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly
681
682             // date
683             itemOutput->setText(COLUMN_DATE, QDateTime::fromTime_t(out.tx->GetTxTime()).toUTC().toString("yy-MM-dd hh:mm"));
684
685             // immature PoS reward
686             if (out.tx->IsCoinStake() && out.tx->GetBlocksToMaturity() > 0 && out.tx->GetDepthInMainChain() > 0) {
687               itemOutput->setBackground(COLUMN_CONFIRMATIONS, Qt::red);
688               itemOutput->setDisabled(true);
689             }
690
691             // confirmations
692             itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " "));
693
694             // priority
695             double dPriority = ((double)out.tx->vout[out.i].nValue  / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10
696             itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority));
697             itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " "));
698             dPrioritySum += (double)out.tx->vout[out.i].nValue  * (out.nDepth+1);
699             nInputSum    += nInputSize;
700
701             // List Mode Weight
702             itemOutput->setText(COLUMN_WEIGHT, strPad(QString::number(nTxWeight), 8, " "));
703
704             // transaction hash
705             uint256 txhash = out.tx->GetHash();
706             itemOutput->setText(COLUMN_TXHASH, txhash.GetHex().c_str());
707
708             // vout index
709             itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i));
710
711             // disable locked coins
712             /*if (model->isLockedCoin(txhash, out.i))
713             {
714                 COutPoint outpt(txhash, out.i);
715                 coinControl->UnSelect(outpt); // just to be sure
716                 itemOutput->setDisabled(true);
717                 itemOutput->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed"));
718             }*/
719
720             // set checkbox
721             if (coinControl->IsSelected(txhash, out.i))
722                 itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Checked);
723         }
724
725         // amount
726         if (treeMode)
727         {
728             dPrioritySum = dPrioritySum / (nInputSum + 78);
729             itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
730             itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
731             itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " "));
732             itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum));
733             itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPrioritySum), 20, " "));
734             itemWalletAddress->setText(COLUMN_WEIGHT, strPad(QString::number((uint64_t)nTxWeightSum),8," "));
735
736         }
737     }
738
739     // expand all partially selected
740     if (treeMode)
741     {
742         for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
743             if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
744                 ui->treeWidget->topLevelItem(i)->setExpanded(true);
745     }
746
747     // sort view
748     sortView(sortColumn, sortOrder);
749     ui->treeWidget->setEnabled(true);
750 }
751
752 void CoinControlDialog::keyPressEvent(QKeyEvent *event)
753 {
754 #ifdef ANDROID
755     if(event->key() == Qt::Key_Back)
756     {
757         close();
758     }
759 #else
760     if(event->key() == Qt::Key_Escape)
761     {
762         close();
763     }
764 #endif
765 }
766
767 void CoinControlDialog::closeEvent(QCloseEvent* e)
768 {
769     QWidget::closeEvent(e);
770     emit beforeClose();
771 }