Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescowens committed Apr 25, 2021
1 parent 8827b9a commit ade13d1
Show file tree
Hide file tree
Showing 9 changed files with 620 additions and 102 deletions.
4 changes: 4 additions & 0 deletions src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ QT_TS = \
QT_FORMS_UI = \
qt/forms/aboutdialog.ui \
qt/forms/coincontroldialog.ui \
qt/forms/consolidateunspentdialog.ui \
qt/forms/diagnosticsdialog.ui \
qt/forms/optionsdialog.ui \
qt/forms/rpcconsole.ui \
Expand Down Expand Up @@ -111,6 +112,7 @@ QT_MOC_CPP = \
qt/moc_clientmodel.cpp \
qt/moc_coincontroldialog.cpp \
qt/moc_coincontroltreewidget.cpp \
qt/moc_consolidateunspentdialog.cpp \
qt/moc_csvmodelwriter.cpp \
qt/moc_diagnosticsdialog.cpp \
qt/moc_editaddressdialog.cpp \
Expand Down Expand Up @@ -181,6 +183,7 @@ GRIDCOINRESEARCH_QT_H = \
qt/clientmodel.h \
qt/coincontroldialog.h \
qt/coincontroltreewidget.h \
qt/consolidateunspentdialog.h \
qt/csvmodelwriter.h \
qt/decoration.h \
qt/diagnosticsdialog.h \
Expand Down Expand Up @@ -243,6 +246,7 @@ GRIDCOINRESEARCH_QT_CPP = \
qt/clientmodel.cpp \
qt/coincontroldialog.cpp \
qt/coincontroltreewidget.cpp \
qt/consolidateunspentdialog.cpp \
qt/csvmodelwriter.cpp \
qt/decoration.cpp \
qt/diagnosticsdialog.cpp \
Expand Down
208 changes: 194 additions & 14 deletions src/qt/coincontroldialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

#include "init.h"
#include "bitcoinunits.h"
#include "walletmodel.h"
#include "addresstablemodel.h"
#include "optionsmodel.h"
#include "policy/fees.h"
#include "validation.h"
#include "wallet/coincontrol.h"
#include "consolidateunspentdialog.h"

#include <QApplication>
#include <QCheckBox>
Expand Down Expand Up @@ -106,9 +106,18 @@ CoinControlDialog::CoinControlDialog(QWidget *parent) :
// (un)select all
connect(ui->selectAllPushButton, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked()));

ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84);
ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100);
ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170);
// filter mode
connect(ui->filterModePushButton, SIGNAL(clicked()), this, SLOT(buttonFilterModeClicked()));

// filter
connect(ui->filterPushButton, SIGNAL(clicked()), this, SLOT(buttonFilterClicked()));

// consolidate
connect(ui->consolidateButton, SIGNAL(clicked()), this, SLOT(buttonConsolidateClicked()));

ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 150);
ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 170);
ui->treeWidget->setColumnWidth(COLUMN_LABEL, 200);
ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 290);
ui->treeWidget->setColumnWidth(COLUMN_DATE, 110);
ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 100);
Expand Down Expand Up @@ -153,26 +162,192 @@ void CoinControlDialog::buttonBoxClicked(QAbstractButton* button)
{
if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
done(QDialog::Accepted); // closes the dialog

if (m_consolidationAddress.second.size())
{
SendCoinsRecipient consolidationRecipient;

qint64 amount = 0;
bool parse_status = false;

consolidationRecipient.label = m_consolidationAddress.first;
consolidationRecipient.address = m_consolidationAddress.second;
parse_status = BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(),
ui->coinControlAfterFeeLabel->text()
.left(ui->coinControlAfterFeeLabel->text().indexOf(" ")),
&amount);

if (parse_status) consolidationRecipient.amount = amount;

emit selectedConsolidationRecipientSignal(consolidationRecipient);
}
}

// (un)select all
void CoinControlDialog::buttonSelectAllClicked()
{
Qt::CheckState state = Qt::Checked;
ui->treeWidget->setEnabled(false);
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != m_ToState)
ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, m_ToState);
ui->treeWidget->setEnabled(true);

if (m_ToState == Qt::Checked)
{
m_ToState = Qt::Unchecked;
}
else
{
m_ToState = Qt::Checked;
}

if (m_ToState == Qt::Checked)
{
ui->selectAllPushButton->setText("Select all");
}
else
{
ui->selectAllPushButton->setText("Select none");

}

CoinControlDialog::updateLabels(model, this);
}

void CoinControlDialog::buttonFilterModeClicked()
{
if (m_FilterMode)
{
m_FilterMode = false;
ui->filterModePushButton->setText(">=");
}
else
{
m_FilterMode = true;
ui->filterModePushButton->setText("<=");
}
}


void CoinControlDialog::buttonFilterClicked()
{
filterInputsByValue(m_FilterMode, ui->maxMinOutputValue->value(), 200);
}

bool CoinControlDialog::filterInputsByValue(const bool& less, const CAmount& inputFilterValue,
const unsigned int& inputCountLimit)
{
QTreeWidgetItemIterator iter(ui->treeWidget);

// If less is true, then we are choosing the smallest inputs upward, and so the map comparator needs to be "less than".
// If less is false, then we are choosing the largest inputs downward, and so the map comparator needs to be "greater
// than".
auto comp = [less](CAmount a, CAmount b)
{
if (less)
{
return (a < b);
}
else
{
return (a > b);
}
};

std::multimap<CAmount, std::pair<QTreeWidgetItem*, COutPoint>, decltype(comp)> input_map(comp);

bool culled_inputs = false;

while (*iter)
{
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != Qt::Unchecked)
CAmount input_value = (*iter)->text(COLUMN_AMOUNT_INT64).toLongLong();
COutPoint outpoint(uint256S((*iter)->text(COLUMN_TXHASH).toStdString()), (*iter)->text(COLUMN_VOUT_INDEX).toUInt());

if ((*iter)->text(COLUMN_TXHASH).length() == 64)
{
state = Qt::Unchecked;
break;
if ((*iter)->checkState(COLUMN_CHECKBOX) == Qt::Checked
&& ((less && input_value <= inputFilterValue) || (!less && input_value >= inputFilterValue)))
{
input_map.insert(std::make_pair(input_value, std::make_pair(*iter, outpoint)));
}
else
{
(*iter)->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
coinControl->UnSelect(outpoint);
}
}

++iter;
}
ui->treeWidget->setEnabled(false);
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != state)
ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state);
ui->treeWidget->setEnabled(true);
CoinControlDialog::updateLabels(model, this);

// The second loop is to limit the number of selected outputs to the inputCountLimit.
unsigned int input_count = 0;

for (auto& input : input_map)
{
if (input_count >= inputCountLimit)
{
LogPrint(BCLog::LogFlags::MISC, "INFO: %s: Culled input %u with value %f.",
__func__, input_count, (double) input.first / COIN);

if (coinControl->IsSelected(input.second.second.hash, input.second.second.n))
{
input.second.first->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);

culled_inputs = true;
coinControl->UnSelect(input.second.second);
}

}

++input_count;
}

// If the number of inputs selected was limited, then true is returned.
return culled_inputs;
}

void CoinControlDialog::buttonConsolidateClicked()
{
ConsolidateUnspentDialog* consolidateUnspentDialog = new ConsolidateUnspentDialog(this);
consolidateUnspentDialog->SetOutputWarningVisible(false);


std::map<QString, QString> addressList;

bool culled_inputs = false;

// This consolidate unspent process has to work a little differently than the rpc one. Note that we are re-applying
// the filter here, because the inputs could have been manually selected, or filtered and then modified. The only
// reason to reapply the filter is to ensure the 200 input maximum is not exceeded for the purpose of consolidation.
CAmount outputFilterValue = 0;

bool maxMinOutputValueValid = false;

outputFilterValue = ui->maxMinOutputValue->value(&maxMinOutputValueValid);

// If the Bitcoin amount field is not filled in for the filter, then fix up the value to max money for filter mode less,
// and zero for filter mode greater than.
if (!maxMinOutputValueValid) outputFilterValue = m_FilterMode ? MAX_MONEY: 0;

culled_inputs = filterInputsByValue(m_FilterMode, outputFilterValue, 200);

for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i)
{
QString label = ui->treeWidget->topLevelItem(i)->text(COLUMN_LABEL);
QString address = ui->treeWidget->topLevelItem(i)->text(COLUMN_ADDRESS);

if (label !="(change)") addressList[address] = label;
}

if (!addressList.empty()) consolidateUnspentDialog->SetAddressList(addressList);

if (culled_inputs) consolidateUnspentDialog->SetOutputWarningVisible(true);

consolidateUnspentDialog->show();

connect(consolidateUnspentDialog, SIGNAL(selectedConsolidationAddressSignal(std::pair<QString, QString>)),
this, SLOT(selectedConsolidationAddressSlot(std::pair<QString, QString>)));
}

// context menu
Expand Down Expand Up @@ -740,3 +915,8 @@ void CoinControlDialog::updateView()
sortView(sortColumn, sortOrder);
ui->treeWidget->setEnabled(true);
}

void CoinControlDialog::selectedConsolidationAddressSlot(std::pair<QString, QString> address)
{
m_consolidationAddress = address;
}
18 changes: 18 additions & 0 deletions src/qt/coincontroldialog.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#ifndef COINCONTROLDIALOG_H
#define COINCONTROLDIALOG_H

#include "walletmodel.h"
#include "amount.h"

#include <QAbstractButton>
#include <QAction>
#include <QDialog>
Expand Down Expand Up @@ -33,6 +36,12 @@ class CoinControlDialog : public QDialog
static QList<qint64> payAmounts;
static CCoinControl *coinControl;

signals:
void selectedConsolidationRecipientSignal(SendCoinsRecipient consolidationRecipient);

public slots:
bool filterInputsByValue(const bool& less, const CAmount& inputFilterValue, const unsigned int &inputCountLimit);

private:
Ui::CoinControlDialog *ui;
WalletModel *model;
Expand All @@ -45,6 +54,11 @@ class CoinControlDialog : public QDialog
//QAction *lockAction;
//QAction *unlockAction;

std::pair<QString, QString> m_consolidationAddress;
qint64 m_consolidationAmount = 0;
Qt::CheckState m_ToState = Qt::Checked;
bool m_FilterMode = true;

QString strPad(QString, int, QString);
void sortView(int, Qt::SortOrder);
void updateView();
Expand Down Expand Up @@ -86,6 +100,10 @@ private slots:
void headerSectionClicked(int);
void buttonBoxClicked(QAbstractButton*);
void buttonSelectAllClicked();
void buttonFilterModeClicked();
void buttonFilterClicked();
void buttonConsolidateClicked();
void selectedConsolidationAddressSlot(std::pair<QString, QString> address);
//void updateLabelLocked();
};

Expand Down
Loading

0 comments on commit ade13d1

Please sign in to comment.