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