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