LCOV - code coverage report
Current view: top level - src/qt - coincontroldialog.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 0 459 0.0 %
Date: 2015-10-12 22:39:14 Functions: 0 26 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11