Skip to content

Commit 246e878

Browse files
committed
Merge #18894: gui: Fix manual coin control with multiple wallets loaded
a8b5f1b gui: Fix manual coin control with multiple wallets loaded (João Barbosa) Pull request description: This PR ensures each loaded wallet has a dedicated coin control in the send view which is manipulated by the coin control dialog. This is an alternative to #17457. Two main differences are: - scope reduced - no unnecessary changes unrelated to the fix; - approach taken - coin control instance now belongs to the send view. All problems raised in #17457 reviews no longer apply due to the approach taken - bitcoin/bitcoin#17457 (review) and bitcoin/bitcoin#17457 (comment)) No change in behavior if only one wallet is loaded. Closes #15725. ACKs for top commit: jonasschnelli: utACK a8b5f1b ryanofsky: Code review ACK a8b5f1b. Code changes are very straightforward, just replacing global CCoinControl object with SendCoinsDialog member. Not sure if this means coin control settings are reset between payments. It would be good to note in the PR description or release notes if single wallet behavior is affected hebasto: ACK a8b5f1b Sjors: tACK a8b5f1b Tree-SHA512: 3ad9c51bab6f28ec0e90efbd6f43fa510c81dafb2eff0b8c3724efcee3e030054a10be013e27cefe35763374c5f6d7af8c02658736964f733d7e38b646b5df65
2 parents 8d17f8d + a8b5f1b commit 246e878

File tree

4 files changed

+45
-67
lines changed

4 files changed

+45
-67
lines changed

src/qt/coincontroldialog.cpp

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const {
4141
return QTreeWidgetItem::operator<(other);
4242
}
4343

44-
CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
44+
CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _model, const PlatformStyle *_platformStyle, QWidget *parent) :
4545
QDialog(parent),
4646
ui(new Ui::CoinControlDialog),
47-
model(nullptr),
47+
m_coin_control(coin_control),
48+
model(_model),
4849
platformStyle(_platformStyle)
4950
{
5051
ui->setupUi(this);
@@ -136,6 +137,13 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge
136137
sortView(settings.value("nCoinControlSortColumn").toInt(), (static_cast<Qt::SortOrder>(settings.value("nCoinControlSortOrder").toInt())));
137138

138139
GUIUtil::handleCloseWindowShortcut(this);
140+
141+
if(_model->getOptionsModel() && _model->getAddressTableModel())
142+
{
143+
updateView();
144+
updateLabelLocked();
145+
CoinControlDialog::updateLabels(m_coin_control, _model, this);
146+
}
139147
}
140148

141149
CoinControlDialog::~CoinControlDialog()
@@ -148,18 +156,6 @@ CoinControlDialog::~CoinControlDialog()
148156
delete ui;
149157
}
150158

151-
void CoinControlDialog::setModel(WalletModel *_model)
152-
{
153-
this->model = _model;
154-
155-
if(_model && _model->getOptionsModel() && _model->getAddressTableModel())
156-
{
157-
updateView();
158-
updateLabelLocked();
159-
CoinControlDialog::updateLabels(_model, this);
160-
}
161-
}
162-
163159
// ok button
164160
void CoinControlDialog::buttonBoxClicked(QAbstractButton* button)
165161
{
@@ -185,8 +181,8 @@ void CoinControlDialog::buttonSelectAllClicked()
185181
ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state);
186182
ui->treeWidget->setEnabled(true);
187183
if (state == Qt::Unchecked)
188-
coinControl()->UnSelectAll(); // just to be sure
189-
CoinControlDialog::updateLabels(model, this);
184+
m_coin_control.UnSelectAll(); // just to be sure
185+
CoinControlDialog::updateLabels(m_coin_control, model, this);
190186
}
191187

192188
// context menu
@@ -371,15 +367,15 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
371367
COutPoint outpt(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt());
372368

373369
if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked)
374-
coinControl()->UnSelect(outpt);
370+
m_coin_control.UnSelect(outpt);
375371
else if (item->isDisabled()) // locked (this happens if "check all" through parent node)
376372
item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
377373
else
378-
coinControl()->Select(outpt);
374+
m_coin_control.Select(outpt);
379375

380376
// selection changed -> update labels
381377
if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all
382-
CoinControlDialog::updateLabels(model, this);
378+
CoinControlDialog::updateLabels(m_coin_control, model, this);
383379
}
384380
}
385381

@@ -396,7 +392,7 @@ void CoinControlDialog::updateLabelLocked()
396392
else ui->labelLocked->setVisible(false);
397393
}
398394

399-
void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
395+
void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *model, QDialog* dialog)
400396
{
401397
if (!model)
402398
return;
@@ -428,7 +424,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
428424
bool fWitness = false;
429425

430426
std::vector<COutPoint> vCoinControl;
431-
coinControl()->ListSelected(vCoinControl);
427+
m_coin_control.ListSelected(vCoinControl);
432428

433429
size_t i = 0;
434430
for (const auto& out : model->wallet().getCoins(vCoinControl)) {
@@ -439,7 +435,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
439435
const COutPoint& outpt = vCoinControl[i++];
440436
if (out.is_spent)
441437
{
442-
coinControl()->UnSelect(outpt);
438+
m_coin_control.UnSelect(outpt);
443439
continue;
444440
}
445441

@@ -492,7 +488,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
492488
nBytes -= 34;
493489

494490
// Fee
495-
nPayFee = model->wallet().getMinimumFee(nBytes, *coinControl(), nullptr /* returned_target */, nullptr /* reason */);
491+
nPayFee = model->wallet().getMinimumFee(nBytes, m_coin_control, nullptr /* returned_target */, nullptr /* reason */);
496492

497493
if (nPayAmount > 0)
498494
{
@@ -584,12 +580,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
584580
label->setVisible(nChange < 0);
585581
}
586582

587-
CCoinControl* CoinControlDialog::coinControl()
588-
{
589-
static CCoinControl coin_control;
590-
return &coin_control;
591-
}
592-
593583
void CoinControlDialog::updateView()
594584
{
595585
if (!model || !model->getOptionsModel() || !model->getAddressTableModel())
@@ -689,13 +679,13 @@ void CoinControlDialog::updateView()
689679
// disable locked coins
690680
if (model->wallet().isLockedCoin(output))
691681
{
692-
coinControl()->UnSelect(output); // just to be sure
682+
m_coin_control.UnSelect(output); // just to be sure
693683
itemOutput->setDisabled(true);
694684
itemOutput->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
695685
}
696686

697687
// set checkbox
698-
if (coinControl()->IsSelected(output))
688+
if (m_coin_control.IsSelected(output))
699689
itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
700690
}
701691

src/qt/coincontroldialog.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,18 @@ class CoinControlDialog : public QDialog
4242
Q_OBJECT
4343

4444
public:
45-
explicit CoinControlDialog(const PlatformStyle *platformStyle, QWidget *parent = nullptr);
45+
explicit CoinControlDialog(CCoinControl& coin_control, WalletModel* model, const PlatformStyle *platformStyle, QWidget *parent = nullptr);
4646
~CoinControlDialog();
4747

48-
void setModel(WalletModel *model);
49-
5048
// static because also called from sendcoinsdialog
51-
static void updateLabels(WalletModel*, QDialog*);
49+
static void updateLabels(CCoinControl& m_coin_control, WalletModel*, QDialog*);
5250

5351
static QList<CAmount> payAmounts;
54-
static CCoinControl *coinControl();
5552
static bool fSubtractFeeFromAmount;
5653

5754
private:
5855
Ui::CoinControlDialog *ui;
56+
CCoinControl& m_coin_control;
5957
WalletModel *model;
6058
int sortColumn;
6159
Qt::SortOrder sortOrder;

src/qt/sendcoinsdialog.cpp

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
5757
ui(new Ui::SendCoinsDialog),
5858
clientModel(nullptr),
5959
model(nullptr),
60+
m_coin_control(new CCoinControl),
6061
fNewRecipientAllowed(true),
6162
fFeeMinimized(true),
6263
platformStyle(_platformStyle)
@@ -259,14 +260,9 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
259260
m_current_transaction = MakeUnique<WalletModelTransaction>(recipients);
260261
WalletModel::SendCoinsReturn prepareStatus;
261262

262-
// Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled
263-
CCoinControl ctrl;
264-
if (model->getOptionsModel()->getCoinControlFeatures())
265-
ctrl = *CoinControlDialog::coinControl();
263+
updateCoinControlState(*m_coin_control);
266264

267-
updateCoinControlState(ctrl);
268-
269-
prepareStatus = model->prepareTransaction(*m_current_transaction, ctrl);
265+
prepareStatus = model->prepareTransaction(*m_current_transaction, *m_coin_control);
270266

271267
// process prepareStatus and on error generate message shown to user
272268
processSendCoinsReturn(prepareStatus,
@@ -454,7 +450,7 @@ void SendCoinsDialog::on_sendButton_clicked()
454450
}
455451
if (!send_failure) {
456452
accept();
457-
CoinControlDialog::coinControl()->UnSelectAll();
453+
m_coin_control->UnSelectAll();
458454
coinControlUpdateLabels();
459455
}
460456
fNewRecipientAllowed = true;
@@ -466,7 +462,7 @@ void SendCoinsDialog::clear()
466462
m_current_transaction.reset();
467463

468464
// Clear coin control settings
469-
CoinControlDialog::coinControl()->UnSelectAll();
465+
m_coin_control->UnSelectAll();
470466
ui->checkBoxCoinControlChange->setChecked(false);
471467
ui->lineEditCoinControlChange->clear();
472468
coinControlUpdateLabels();
@@ -689,17 +685,11 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked()
689685

690686
void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
691687
{
692-
// Get CCoinControl instance if CoinControl is enabled or create a new one.
693-
CCoinControl coin_control;
694-
if (model->getOptionsModel()->getCoinControlFeatures()) {
695-
coin_control = *CoinControlDialog::coinControl();
696-
}
697-
698688
// Include watch-only for wallets without private key
699-
coin_control.fAllowWatchOnly = model->wallet().privateKeysDisabled();
689+
m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled();
700690

701691
// Calculate available amount to send.
702-
CAmount amount = model->wallet().getAvailableBalance(coin_control);
692+
CAmount amount = model->wallet().getAvailableBalance(*m_coin_control);
703693
for (int i = 0; i < ui->entries->count(); ++i) {
704694
SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
705695
if (e && !e->isHidden() && e != entry) {
@@ -758,12 +748,11 @@ void SendCoinsDialog::updateSmartFeeLabel()
758748
{
759749
if(!model || !model->getOptionsModel())
760750
return;
761-
CCoinControl coin_control;
762-
updateCoinControlState(coin_control);
763-
coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
751+
updateCoinControlState(*m_coin_control);
752+
m_coin_control->m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
764753
int returned_target;
765754
FeeReason reason;
766-
CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, coin_control, &returned_target, &reason));
755+
CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, *m_coin_control, &returned_target, &reason));
767756

768757
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
769758

@@ -834,16 +823,15 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked)
834823
ui->frameCoinControl->setVisible(checked);
835824

836825
if (!checked && model) // coin control features disabled
837-
CoinControlDialog::coinControl()->SetNull();
826+
m_coin_control->SetNull();
838827

839828
coinControlUpdateLabels();
840829
}
841830

842831
// Coin Control: button inputs -> show actual coin control dialog
843832
void SendCoinsDialog::coinControlButtonClicked()
844833
{
845-
CoinControlDialog dlg(platformStyle);
846-
dlg.setModel(model);
834+
CoinControlDialog dlg(*m_coin_control, model, platformStyle);
847835
dlg.exec();
848836
coinControlUpdateLabels();
849837
}
@@ -853,7 +841,7 @@ void SendCoinsDialog::coinControlChangeChecked(int state)
853841
{
854842
if (state == Qt::Unchecked)
855843
{
856-
CoinControlDialog::coinControl()->destChange = CNoDestination();
844+
m_coin_control->destChange = CNoDestination();
857845
ui->labelCoinControlChangeLabel->clear();
858846
}
859847
else
@@ -869,7 +857,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
869857
if (model && model->getAddressTableModel())
870858
{
871859
// Default to no change address until verified
872-
CoinControlDialog::coinControl()->destChange = CNoDestination();
860+
m_coin_control->destChange = CNoDestination();
873861
ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
874862

875863
const CTxDestination dest = DecodeDestination(text.toStdString());
@@ -892,7 +880,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
892880
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
893881

894882
if(btnRetVal == QMessageBox::Yes)
895-
CoinControlDialog::coinControl()->destChange = dest;
883+
m_coin_control->destChange = dest;
896884
else
897885
{
898886
ui->lineEditCoinControlChange->setText("");
@@ -911,7 +899,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
911899
else
912900
ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
913901

914-
CoinControlDialog::coinControl()->destChange = dest;
902+
m_coin_control->destChange = dest;
915903
}
916904
}
917905
}
@@ -923,7 +911,7 @@ void SendCoinsDialog::coinControlUpdateLabels()
923911
if (!model || !model->getOptionsModel())
924912
return;
925913

926-
updateCoinControlState(*CoinControlDialog::coinControl());
914+
updateCoinControlState(*m_coin_control);
927915

928916
// set pay amounts
929917
CoinControlDialog::payAmounts.clear();
@@ -941,10 +929,10 @@ void SendCoinsDialog::coinControlUpdateLabels()
941929
}
942930
}
943931

944-
if (CoinControlDialog::coinControl()->HasSelected())
932+
if (m_coin_control->HasSelected())
945933
{
946934
// actual coin control calculation
947-
CoinControlDialog::updateLabels(model, this);
935+
CoinControlDialog::updateLabels(*m_coin_control, model, this);
948936

949937
// show coin control stats
950938
ui->labelCoinControlAutomaticallySelected->hide();

src/qt/sendcoinsdialog.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <QString>
1313
#include <QTimer>
1414

15+
class CCoinControl;
1516
class ClientModel;
1617
class PlatformStyle;
1718
class SendCoinsEntry;
@@ -60,6 +61,7 @@ public Q_SLOTS:
6061
Ui::SendCoinsDialog *ui;
6162
ClientModel *clientModel;
6263
WalletModel *model;
64+
std::unique_ptr<CCoinControl> m_coin_control;
6365
std::unique_ptr<WalletModelTransaction> m_current_transaction;
6466
bool fNewRecipientAllowed;
6567
bool fFeeMinimized;

0 commit comments

Comments
 (0)