]> Git Repo - VerusCoin.git/blame - src/qt/sendcoinsdialog.cpp
Merge pull request #5360
[VerusCoin.git] / src / qt / sendcoinsdialog.cpp
CommitLineData
f914f1a7 1// Copyright (c) 2011-2014 The Bitcoin Core developers
78253fcb 2// Distributed under the MIT software license, see the accompanying
e592d43f
WL
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
0fd01780 5#include "sendcoinsdialog.h"
df577886 6#include "ui_sendcoinsdialog.h"
32af5266 7
0689f46c 8#include "addresstablemodel.h"
e285ffcd 9#include "bitcoinunits.h"
c1c9d5b4 10#include "clientmodel.h"
0689f46c 11#include "coincontroldialog.h"
51ed9ec9 12#include "guiutil.h"
968d55aa 13#include "optionsmodel.h"
9b7d3fb1 14#include "scicon.h"
a5e6d723 15#include "sendcoinsentry.h"
51ed9ec9
BD
16#include "walletmodel.h"
17
2bc15836 18#include "base58.h"
6a86c24d 19#include "coincontrol.h"
0689f46c 20#include "ui_interface.h"
a328dd60 21#include "wallet.h"
3a7abc2c 22
992ff49b 23#include <QMessageBox>
9a93c4c0 24#include <QScrollBar>
c1c9d5b4 25#include <QSettings>
51ed9ec9 26#include <QTextDocument>
3f323a61 27
a5e6d723 28SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
df577886 29 QDialog(parent),
6630c1cb 30 ui(new Ui::SendCoinsDialog),
a328dd60
PK
31 clientModel(0),
32 model(0),
33 fNewRecipientAllowed(true),
34 fFeeMinimized(true)
df577886
WL
35{
36 ui->setupUi(this);
83b82370 37
81605d90 38#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
527137e3 39 ui->addButton->setIcon(QIcon());
40 ui->clearButton->setIcon(QIcon());
41 ui->sendButton->setIcon(QIcon());
9b7d3fb1
LD
42#else
43 ui->addButton->setIcon(SingleColorIcon(":/icons/add"));
44 ui->clearButton->setIcon(SingleColorIcon(":/icons/remove"));
45 ui->sendButton->setIcon(SingleColorIcon(":/icons/send"));
527137e3 46#endif
c78bd937
PK
47
48 GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
527137e3 49
a5e6d723 50 addEntry();
992ff49b 51
a5e6d723 52 connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
609acbf4 53 connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
7d145a0f 54
6a86c24d 55 // Coin Control
6a86c24d
CL
56 connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
57 connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
58 connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &)));
59
60 // Coin Control: clipboard actions
61 QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
62 QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
63 QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
64 QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
65 QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
66 QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this);
95a93836 67 QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
6a86c24d
CL
68 QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
69 connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity()));
70 connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount()));
71 connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee()));
72 connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee()));
73 connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes()));
74 connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardPriority()));
75 connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput()));
76 connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange()));
77 ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
78 ui->labelCoinControlAmount->addAction(clipboardAmountAction);
79 ui->labelCoinControlFee->addAction(clipboardFeeAction);
80 ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
81 ui->labelCoinControlBytes->addAction(clipboardBytesAction);
82 ui->labelCoinControlPriority->addAction(clipboardPriorityAction);
83 ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
84 ui->labelCoinControlChange->addAction(clipboardChangeAction);
85
c1c9d5b4
CL
86 // init transaction fee section
87 QSettings settings;
88 if (!settings.contains("fFeeSectionMinimized"))
89 settings.setValue("fFeeSectionMinimized", true);
90 if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
91 settings.setValue("nFeeRadio", 1); // custom
92 if (!settings.contains("nFeeRadio"))
93 settings.setValue("nFeeRadio", 0); // recommended
94 if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
95 settings.setValue("nCustomFeeRadio", 1); // total at least
96 if (!settings.contains("nCustomFeeRadio"))
97 settings.setValue("nCustomFeeRadio", 0); // per kilobyte
98 if (!settings.contains("nSmartFeeSliderPosition"))
99 settings.setValue("nSmartFeeSliderPosition", 0);
100 if (!settings.contains("nTransactionFee"))
101 settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
102 if (!settings.contains("fPayOnlyMinFee"))
103 settings.setValue("fPayOnlyMinFee", false);
104 if (!settings.contains("fSendFreeTransactions"))
105 settings.setValue("fSendFreeTransactions", false);
106 ui->groupFee->setId(ui->radioSmartFee, 0);
107 ui->groupFee->setId(ui->radioCustomFee, 1);
108 ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
109 ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0);
110 ui->groupCustomFee->setId(ui->radioCustomAtLeast, 1);
111 ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
112 ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
113 ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
114 ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
115 ui->checkBoxFreeTx->setChecked(settings.value("fSendFreeTransactions").toBool());
116 minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
df577886
WL
117}
118
c1c9d5b4
CL
119void SendCoinsDialog::setClientModel(ClientModel *clientModel)
120{
121 this->clientModel = clientModel;
a328dd60
PK
122
123 if (clientModel) {
8517e970 124 connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime)), this, SLOT(updateSmartFeeLabel()));
a328dd60 125 }
c1c9d5b4
CL
126}
127
ef079e18 128void SendCoinsDialog::setModel(WalletModel *model)
6630c1cb
WL
129{
130 this->model = model;
a5e6d723 131
6728e007 132 if(model && model->getOptionsModel())
a5e6d723 133 {
6728e007 134 for(int i = 0; i < ui->entries->count(); ++i)
a5e6d723 135 {
6728e007
PK
136 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
137 if(entry)
138 {
139 entry->setModel(model);
140 }
a5e6d723 141 }
6728e007 142
ffd40da3
J
143 setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(),
144 model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance());
c122f552 145 connect(model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)));
e0873daf 146 connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
c1c9d5b4 147 updateDisplayUnit();
6a86c24d
CL
148
149 // Coin Control
150 connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
151 connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
6a86c24d
CL
152 ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures());
153 coinControlUpdateLabels();
c1c9d5b4
CL
154
155 // fee section
c1c9d5b4
CL
156 connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateSmartFeeLabel()));
157 connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateGlobalFeeVariables()));
158 connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(coinControlUpdateLabels()));
159 connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
160 connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
161 connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
162 connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
163 connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
164 connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(updateGlobalFeeVariables()));
165 connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels()));
166 connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee()));
167 connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
168 connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
169 connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
170 connect(ui->checkBoxFreeTx, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
171 connect(ui->checkBoxFreeTx, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
172 ui->customFee->setSingleStep(CWallet::minTxFee.GetFeePerK());
173 updateFeeSectionControls();
174 updateMinFeeLabel();
175 updateSmartFeeLabel();
176 updateGlobalFeeVariables();
dead0ff8 177 }
6630c1cb
WL
178}
179
df577886
WL
180SendCoinsDialog::~SendCoinsDialog()
181{
c1c9d5b4
CL
182 QSettings settings;
183 settings.setValue("fFeeSectionMinimized", fFeeMinimized);
184 settings.setValue("nFeeRadio", ui->groupFee->checkedId());
185 settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId());
186 settings.setValue("nSmartFeeSliderPosition", ui->sliderSmartFee->value());
187 settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
188 settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
189 settings.setValue("fSendFreeTransactions", ui->checkBoxFreeTx->isChecked());
190
df577886
WL
191 delete ui;
192}
3a7abc2c
WL
193
194void SendCoinsDialog::on_sendButton_clicked()
195{
bdd0c59a
PK
196 if(!model || !model->getOptionsModel())
197 return;
198
a5e6d723
WL
199 QList<SendCoinsRecipient> recipients;
200 bool valid = true;
dead0ff8 201
a5e6d723
WL
202 for(int i = 0; i < ui->entries->count(); ++i)
203 {
204 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
205 if(entry)
206 {
207 if(entry->validate())
208 {
209 recipients.append(entry->getValue());
210 }
211 else
212 {
213 valid = false;
214 }
215 }
216 }
6630c1cb 217
a5e6d723 218 if(!valid || recipients.isEmpty())
2097c09a 219 {
992ff49b 220 return;
2097c09a 221 }
0eba0044 222
90a43c1e 223 fNewRecipientAllowed = false;
90a43c1e
CL
224 WalletModel::UnlockContext ctx(model->requestUnlock());
225 if(!ctx.isValid())
226 {
227 // Unlock wallet was cancelled
228 fNewRecipientAllowed = true;
229 return;
230 }
231
232 // prepare transaction for getting txFee earlier
233 WalletModelTransaction currentTransaction(recipients);
234 WalletModel::SendCoinsReturn prepareStatus;
235 if (model->getOptionsModel()->getCoinControlFeatures()) // coin control enabled
236 prepareStatus = model->prepareTransaction(currentTransaction, CoinControlDialog::coinControl);
237 else
238 prepareStatus = model->prepareTransaction(currentTransaction);
239
240 // process prepareStatus and on error generate message shown to user
241 processSendCoinsReturn(prepareStatus,
242 BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee()));
243
244 if(prepareStatus.status != WalletModel::OK) {
245 fNewRecipientAllowed = true;
246 return;
247 }
248
249 CAmount txFee = currentTransaction.getTransactionFee();
250
a5e6d723
WL
251 // Format confirmation message
252 QStringList formatted;
292623ad 253 foreach(const SendCoinsRecipient &rcp, currentTransaction.getRecipients())
a5e6d723 254 {
23b48d13 255 // generate bold amount string
70074029 256 QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
23b48d13
PK
257 amount.append("</b>");
258 // generate monospace address string
259 QString address = "<span style='font-family: monospace;'>" + rcp.address;
260 address.append("</span>");
261
262 QString recipientElement;
263
c6c97e0f 264 if (!rcp.paymentRequest.IsInitialized()) // normal payment
a41d5fe0 265 {
23b48d13 266 if(rcp.label.length() > 0) // label with address
9e8904f6 267 {
23b48d13
PK
268 recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label));
269 recipientElement.append(QString(" (%1)").arg(address));
9e8904f6 270 }
23b48d13 271 else // just address
9e8904f6 272 {
23b48d13 273 recipientElement = tr("%1 to %2").arg(amount, address);
9e8904f6 274 }
a41d5fe0 275 }
c6c97e0f 276 else if(!rcp.authenticatedMerchant.isEmpty()) // secure payment request
a41d5fe0 277 {
23b48d13 278 recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant));
a41d5fe0 279 }
c6c97e0f
PK
280 else // insecure payment request
281 {
282 recipientElement = tr("%1 to %2").arg(amount, address);
283 }
23b48d13
PK
284
285 formatted.append(recipientElement);
a5e6d723 286 }
38deedc1 287
9e8904f6
JS
288 QString questionString = tr("Are you sure you want to send?");
289 questionString.append("<br /><br />%1");
290
291 if(txFee > 0)
292 {
293 // append fee string if a fee is required
294 questionString.append("<hr /><span style='color:#aa0000;'>");
70074029 295 questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
9e8904f6
JS
296 questionString.append("</span> ");
297 questionString.append(tr("added as transaction fee"));
c1c9d5b4
CL
298
299 // append transaction size
300 questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)");
9e8904f6 301 }
fb0507fe
WL
302
303 // add total amount in all subdivision units
304 questionString.append("<hr />");
a372168e 305 CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
fb0507fe
WL
306 QStringList alternativeUnits;
307 foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits())
9e8904f6 308 {
fb0507fe 309 if(u != model->getOptionsModel()->getDisplayUnit())
70074029 310 alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
9e8904f6 311 }
fb0507fe 312 questionString.append(tr("Total Amount %1 (= %2)")
70074029 313 .arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount))
84b695cc 314 .arg(alternativeUnits.join(" " + tr("or") + " ")));
9e8904f6
JS
315
316 QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
317 questionString.arg(formatted.join("<br />")),
23b48d13 318 QMessageBox::Yes | QMessageBox::Cancel,
9e8904f6
JS
319 QMessageBox::Cancel);
320
321 if(retval != QMessageBox::Yes)
322 {
323 fNewRecipientAllowed = true;
324 return;
325 }
326
327 // now send the prepared transaction
71ba4670
PK
328 WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
329 // process sendStatus and on error generate message shown to user
330 processSendCoinsReturn(sendStatus);
331
332 if (sendStatus.status == WalletModel::OK)
9e8904f6 333 {
92f20d53 334 accept();
6a86c24d
CL
335 CoinControlDialog::coinControl->UnSelectAll();
336 coinControlUpdateLabels();
2097c09a 337 }
7d145a0f 338 fNewRecipientAllowed = true;
3a7abc2c
WL
339}
340
a5e6d723 341void SendCoinsDialog::clear()
3a7abc2c 342{
a5e6d723 343 // Remove entries until only one left
db7f0234 344 while(ui->entries->count())
a5e6d723 345 {
6c98cca9 346 ui->entries->takeAt(0)->widget()->deleteLater();
a5e6d723 347 }
db7f0234 348 addEntry();
4d1bb15e 349
84b695cc 350 updateTabsAndLabels();
3479849d
WL
351}
352
353void SendCoinsDialog::reject()
354{
355 clear();
356}
357
358void SendCoinsDialog::accept()
359{
360 clear();
361}
a5e6d723 362
db7f0234 363SendCoinsEntry *SendCoinsDialog::addEntry()
a5e6d723
WL
364{
365 SendCoinsEntry *entry = new SendCoinsEntry(this);
366 entry->setModel(model);
367 ui->entries->addWidget(entry);
368 connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
6a86c24d 369 connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels()));
292623ad 370 connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this, SLOT(coinControlUpdateLabels()));
a5e6d723 371
84b695cc 372 updateTabsAndLabels();
a5e6d723
WL
373
374 // Focus the field, so that entry can start immediately
375 entry->clear();
9a93c4c0
MC
376 entry->setFocus();
377 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
1ce04488 378 qApp->processEvents();
9a93c4c0 379 QScrollBar* bar = ui->scrollArea->verticalScrollBar();
e0873daf 380 if(bar)
9a93c4c0 381 bar->setSliderPosition(bar->maximum());
db7f0234 382 return entry;
a5e6d723
WL
383}
384
84b695cc 385void SendCoinsDialog::updateTabsAndLabels()
a5e6d723 386{
a5e6d723 387 setupTabChain(0);
6a86c24d 388 coinControlUpdateLabels();
a5e6d723
WL
389}
390
391void SendCoinsDialog::removeEntry(SendCoinsEntry* entry)
392{
24646ee7 393 entry->hide();
84b695cc 394
24646ee7
PK
395 // If the last entry is about to be removed add an empty one
396 if (ui->entries->count() == 1)
84b695cc
PK
397 addEntry();
398
24646ee7
PK
399 entry->deleteLater();
400
84b695cc 401 updateTabsAndLabels();
a5e6d723
WL
402}
403
404QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
405{
406 for(int i = 0; i < ui->entries->count(); ++i)
407 {
408 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
409 if(entry)
410 {
411 prev = entry->setupTabChain(prev);
412 }
413 }
69d03bc6
WL
414 QWidget::setTabOrder(prev, ui->sendButton);
415 QWidget::setTabOrder(ui->sendButton, ui->clearButton);
416 QWidget::setTabOrder(ui->clearButton, ui->addButton);
417 return ui->addButton;
a5e6d723 418}
db7f0234 419
311993ab
PK
420void SendCoinsDialog::setAddress(const QString &address)
421{
422 SendCoinsEntry *entry = 0;
423 // Replace the first entry if it is still unused
424 if(ui->entries->count() == 1)
425 {
426 SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
427 if(first->isClear())
428 {
429 entry = first;
430 }
431 }
432 if(!entry)
433 {
434 entry = addEntry();
435 }
436
437 entry->setAddress(address);
438}
439
db7f0234
WL
440void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv)
441{
e0873daf 442 if(!fNewRecipientAllowed)
7d145a0f
MC
443 return;
444
db7f0234
WL
445 SendCoinsEntry *entry = 0;
446 // Replace the first entry if it is still unused
447 if(ui->entries->count() == 1)
448 {
449 SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
450 if(first->isClear())
451 {
452 entry = first;
453 }
454 }
455 if(!entry)
456 {
457 entry = addEntry();
458 }
459
460 entry->setValue(rv);
84b695cc 461 updateTabsAndLabels();
db7f0234
WL
462}
463
a41d5fe0 464bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv)
db7f0234 465{
bdc83e8f
PK
466 // Just paste the entry, all pre-checks
467 // are done in paymentserver.cpp.
a41d5fe0
GA
468 pasteEntry(rv);
469 return true;
db7f0234 470}
b8afa21f 471
a328dd60 472void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
a372168e 473 const CAmount& watchBalance, const CAmount& watchUnconfirmedBalance, const CAmount& watchImmatureBalance)
b8afa21f
WL
474{
475 Q_UNUSED(unconfirmedBalance);
8fdb7e10 476 Q_UNUSED(immatureBalance);
ffd40da3
J
477 Q_UNUSED(watchBalance);
478 Q_UNUSED(watchUnconfirmedBalance);
479 Q_UNUSED(watchImmatureBalance);
dead0ff8 480
bdd0c59a
PK
481 if(model && model->getOptionsModel())
482 {
483 ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
484 }
b8afa21f 485}
e0873daf
PK
486
487void SendCoinsDialog::updateDisplayUnit()
488{
ffd40da3 489 setBalance(model->getBalance(), 0, 0, 0, 0, 0);
c1c9d5b4
CL
490 ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
491 updateMinFeeLabel();
492 updateSmartFeeLabel();
e0873daf 493}
71ba4670
PK
494
495void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
496{
497 QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
498 // Default to a warning message, override if error message is needed
499 msgParams.second = CClientUIInterface::MSG_WARNING;
500
501 // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
502 // WalletModel::TransactionCommitFailed is used only in WalletModel::sendCoins()
503 // all others are used only in WalletModel::prepareTransaction()
504 switch(sendCoinsReturn.status)
505 {
506 case WalletModel::InvalidAddress:
507 msgParams.first = tr("The recipient address is not valid, please recheck.");
508 break;
509 case WalletModel::InvalidAmount:
510 msgParams.first = tr("The amount to pay must be larger than 0.");
511 break;
512 case WalletModel::AmountExceedsBalance:
513 msgParams.first = tr("The amount exceeds your balance.");
514 break;
515 case WalletModel::AmountWithFeeExceedsBalance:
516 msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
517 break;
518 case WalletModel::DuplicateAddress:
519 msgParams.first = tr("Duplicate address found, can only send to each address once per send operation.");
520 break;
521 case WalletModel::TransactionCreationFailed:
522 msgParams.first = tr("Transaction creation failed!");
523 msgParams.second = CClientUIInterface::MSG_ERROR;
524 break;
525 case WalletModel::TransactionCommitFailed:
526 msgParams.first = tr("The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
527 msgParams.second = CClientUIInterface::MSG_ERROR;
528 break;
1371e6f5
DH
529 case WalletModel::AbsurdFee:
530 msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), 10000000));
c1c9d5b4 531 break;
6715efb9
PK
532 case WalletModel::PaymentRequestExpired:
533 msgParams.first = tr("Payment request expired!");
534 msgParams.second = CClientUIInterface::MSG_ERROR;
535 break;
4a61c394 536 // included to prevent a compiler warning.
71ba4670 537 case WalletModel::OK:
71ba4670
PK
538 default:
539 return;
540 }
541
542 emit message(tr("Send Coins"), msgParams.first, msgParams.second);
543}
6a86c24d 544
c1c9d5b4
CL
545void SendCoinsDialog::minimizeFeeSection(bool fMinimize)
546{
547 ui->labelFeeMinimized->setVisible(fMinimize);
548 ui->buttonChooseFee ->setVisible(fMinimize);
549 ui->buttonMinimizeFee->setVisible(!fMinimize);
550 ui->frameFeeSelection->setVisible(!fMinimize);
551 ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
552 fFeeMinimized = fMinimize;
553}
554
555void SendCoinsDialog::on_buttonChooseFee_clicked()
556{
557 minimizeFeeSection(false);
558}
559
560void SendCoinsDialog::on_buttonMinimizeFee_clicked()
561{
562 updateFeeMinimizedLabel();
563 minimizeFeeSection(true);
564}
565
566void SendCoinsDialog::setMinimumFee()
567{
568 ui->radioCustomPerKilobyte->setChecked(true);
569 ui->customFee->setValue(CWallet::minTxFee.GetFeePerK());
570}
571
572void SendCoinsDialog::updateFeeSectionControls()
573{
574 ui->sliderSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
575 ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
576 ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
577 ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
578 ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
579 ui->labelSmartFeeNormal ->setEnabled(ui->radioSmartFee->isChecked());
580 ui->labelSmartFeeFast ->setEnabled(ui->radioSmartFee->isChecked());
581 ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
582 ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
583 ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
584 ui->radioCustomAtLeast ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
585 ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
586}
587
588void SendCoinsDialog::updateGlobalFeeVariables()
589{
590 if (ui->radioSmartFee->isChecked())
591 {
592 nTxConfirmTarget = (int)25 - (int)std::max(0, std::min(24, ui->sliderSmartFee->value()));
593 payTxFee = CFeeRate(0);
594 }
595 else
596 {
597 nTxConfirmTarget = 25;
598 payTxFee = CFeeRate(ui->customFee->value());
599 fPayAtLeastCustomFee = ui->radioCustomAtLeast->isChecked();
600 }
601
602 fSendFreeTransactions = ui->checkBoxFreeTx->isChecked();
603}
604
605void SendCoinsDialog::updateFeeMinimizedLabel()
606{
607 if(!model || !model->getOptionsModel())
608 return;
609
610 if (ui->radioSmartFee->isChecked())
611 ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
612 else {
613 ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) +
614 ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : ""));
615 }
616}
617
618void SendCoinsDialog::updateMinFeeLabel()
619{
620 if (model && model->getOptionsModel())
621 ui->checkBoxMinimumFee->setText(tr("Pay only the minimum fee of %1").arg(
622 BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::minTxFee.GetFeePerK()) + "/kB")
623 );
624}
625
626void SendCoinsDialog::updateSmartFeeLabel()
627{
628 if(!model || !model->getOptionsModel())
629 return;
630
631 int nBlocksToConfirm = (int)25 - (int)std::max(0, std::min(24, ui->sliderSmartFee->value()));
632 CFeeRate feeRate = mempool.estimateFee(nBlocksToConfirm);
633 if (feeRate <= CFeeRate(0)) // not enough data => minfee
634 {
635 ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::minTxFee.GetFeePerK()) + "/kB");
636 ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
637 ui->labelFeeEstimation->setText("");
638 }
639 else
640 {
641 ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
642 ui->labelSmartFee2->hide();
2747f7cf 643 ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", nBlocksToConfirm));
c1c9d5b4
CL
644 }
645
646 updateFeeMinimizedLabel();
647}
648
6a86c24d
CL
649// Coin Control: copy label "Quantity" to clipboard
650void SendCoinsDialog::coinControlClipboardQuantity()
651{
652 GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
653}
654
655// Coin Control: copy label "Amount" to clipboard
656void SendCoinsDialog::coinControlClipboardAmount()
657{
658 GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
659}
660
661// Coin Control: copy label "Fee" to clipboard
662void SendCoinsDialog::coinControlClipboardFee()
663{
1d84aead 664 GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
6a86c24d
CL
665}
666
667// Coin Control: copy label "After fee" to clipboard
668void SendCoinsDialog::coinControlClipboardAfterFee()
669{
1d84aead 670 GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
6a86c24d
CL
671}
672
673// Coin Control: copy label "Bytes" to clipboard
674void SendCoinsDialog::coinControlClipboardBytes()
675{
1d84aead 676 GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
6a86c24d
CL
677}
678
679// Coin Control: copy label "Priority" to clipboard
680void SendCoinsDialog::coinControlClipboardPriority()
681{
682 GUIUtil::setClipboard(ui->labelCoinControlPriority->text());
683}
684
95a93836 685// Coin Control: copy label "Dust" to clipboard
6a86c24d
CL
686void SendCoinsDialog::coinControlClipboardLowOutput()
687{
688 GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
689}
690
691// Coin Control: copy label "Change" to clipboard
692void SendCoinsDialog::coinControlClipboardChange()
693{
1d84aead 694 GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
6a86c24d
CL
695}
696
697// Coin Control: settings menu - coin control enabled/disabled by user
698void SendCoinsDialog::coinControlFeatureChanged(bool checked)
699{
700 ui->frameCoinControl->setVisible(checked);
701
702 if (!checked && model) // coin control features disabled
703 CoinControlDialog::coinControl->SetNull();
edd735da
CL
704
705 if (checked)
706 coinControlUpdateLabels();
6a86c24d
CL
707}
708
709// Coin Control: button inputs -> show actual coin control dialog
710void SendCoinsDialog::coinControlButtonClicked()
711{
712 CoinControlDialog dlg;
713 dlg.setModel(model);
714 dlg.exec();
715 coinControlUpdateLabels();
716}
717
718// Coin Control: checkbox custom change address
719void SendCoinsDialog::coinControlChangeChecked(int state)
720{
834e14e5 721 if (state == Qt::Unchecked)
6a86c24d 722 {
834e14e5 723 CoinControlDialog::coinControl->destChange = CNoDestination();
834e14e5 724 ui->labelCoinControlChangeLabel->clear();
6a86c24d 725 }
834e14e5
PK
726 else
727 // use this to re-validate an already entered address
728 coinControlChangeEdited(ui->lineEditCoinControlChange->text());
6a86c24d
CL
729
730 ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
6a86c24d
CL
731}
732
733// Coin Control: custom change address changed
24646ee7 734void SendCoinsDialog::coinControlChangeEdited(const QString& text)
6a86c24d 735{
3380713a 736 if (model && model->getAddressTableModel())
6a86c24d 737 {
3380713a
PK
738 // Default to no change address until verified
739 CoinControlDialog::coinControl->destChange = CNoDestination();
740 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
741
742 CBitcoinAddress addr = CBitcoinAddress(text.toStdString());
6a86c24d 743
3380713a
PK
744 if (text.isEmpty()) // Nothing entered
745 {
6a86c24d 746 ui->labelCoinControlChangeLabel->setText("");
3380713a
PK
747 }
748 else if (!addr.IsValid()) // Invalid address
6a86c24d 749 {
6a86c24d
CL
750 ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
751 }
3380713a 752 else // Valid address
6a86c24d 753 {
3380713a
PK
754 CPubKey pubkey;
755 CKeyID keyid;
756 addr.GetKeyID(keyid);
757 if (!model->getPubKey(keyid, pubkey)) // Unknown change address
6a86c24d 758 {
3380713a
PK
759 ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
760 }
761 else // Known change address
762 {
763 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
764
765 // Query label
766 QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
767 if (!associatedLabel.isEmpty())
768 ui->labelCoinControlChangeLabel->setText(associatedLabel);
6a86c24d 769 else
3380713a
PK
770 ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
771
772 CoinControlDialog::coinControl->destChange = addr.Get();
6a86c24d
CL
773 }
774 }
775 }
776}
777
778// Coin Control: update labels
779void SendCoinsDialog::coinControlUpdateLabels()
780{
781 if (!model || !model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures())
782 return;
783
784 // set pay amounts
785 CoinControlDialog::payAmounts.clear();
292623ad 786 CoinControlDialog::fSubtractFeeFromAmount = false;
6a86c24d
CL
787 for(int i = 0; i < ui->entries->count(); ++i)
788 {
789 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
790 if(entry)
292623ad
CL
791 {
792 SendCoinsRecipient rcp = entry->getValue();
793 CoinControlDialog::payAmounts.append(rcp.amount);
794 if (rcp.fSubtractFeeFromAmount)
795 CoinControlDialog::fSubtractFeeFromAmount = true;
796 }
6a86c24d
CL
797 }
798
799 if (CoinControlDialog::coinControl->HasSelected())
800 {
801 // actual coin control calculation
802 CoinControlDialog::updateLabels(model, this);
803
804 // show coin control stats
805 ui->labelCoinControlAutomaticallySelected->hide();
806 ui->widgetCoinControl->show();
807 }
808 else
809 {
810 // hide coin control stats
811 ui->labelCoinControlAutomaticallySelected->show();
812 ui->widgetCoinControl->hide();
813 ui->labelCoinControlInsuffFunds->hide();
814 }
815}
This page took 0.309314 seconds and 4 git commands to generate.