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