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