]> Git Repo - VerusCoin.git/blame - src/qt/sendcoinsdialog.cpp
Merge pull request #3211
[VerusCoin.git] / src / qt / sendcoinsdialog.cpp
CommitLineData
e592d43f
WL
1// Copyright (c) 2011-2013 The Bitcoin developers
2// Distributed under the MIT/X11 software license, see the accompanying
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
e285ffcd 8#include "bitcoinunits.h"
968d55aa 9#include "optionsmodel.h"
a5e6d723 10#include "sendcoinsentry.h"
db7f0234 11#include "guiutil.h"
b7bcaf94 12#include "askpassphrasedialog.h"
2bc15836 13#include "base58.h"
71ba4670 14#include "ui_interface.h"
3a7abc2c 15
992ff49b 16#include <QMessageBox>
dedf83a1 17#include <QTextDocument>
9a93c4c0 18#include <QScrollBar>
3f323a61 19
a5e6d723 20SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
df577886 21 QDialog(parent),
6630c1cb
WL
22 ui(new Ui::SendCoinsDialog),
23 model(0)
df577886
WL
24{
25 ui->setupUi(this);
83b82370 26
81605d90 27#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
527137e3 28 ui->addButton->setIcon(QIcon());
29 ui->clearButton->setIcon(QIcon());
30 ui->sendButton->setIcon(QIcon());
31#endif
32
a5e6d723 33 addEntry();
992ff49b 34
a5e6d723 35 connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
609acbf4 36 connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
7d145a0f
MC
37
38 fNewRecipientAllowed = true;
df577886
WL
39}
40
ef079e18 41void SendCoinsDialog::setModel(WalletModel *model)
6630c1cb
WL
42{
43 this->model = model;
a5e6d723 44
6728e007 45 if(model && model->getOptionsModel())
a5e6d723 46 {
6728e007 47 for(int i = 0; i < ui->entries->count(); ++i)
a5e6d723 48 {
6728e007
PK
49 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
50 if(entry)
51 {
52 entry->setModel(model);
53 }
a5e6d723 54 }
6728e007 55
8fdb7e10 56 setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance());
57 connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64)));
e0873daf 58 connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
dead0ff8 59 }
6630c1cb
WL
60}
61
df577886
WL
62SendCoinsDialog::~SendCoinsDialog()
63{
64 delete ui;
65}
3a7abc2c
WL
66
67void SendCoinsDialog::on_sendButton_clicked()
68{
bdd0c59a
PK
69 if(!model || !model->getOptionsModel())
70 return;
71
a5e6d723
WL
72 QList<SendCoinsRecipient> recipients;
73 bool valid = true;
dead0ff8 74
a5e6d723
WL
75 for(int i = 0; i < ui->entries->count(); ++i)
76 {
77 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
78 if(entry)
79 {
80 if(entry->validate())
81 {
82 recipients.append(entry->getValue());
83 }
84 else
85 {
86 valid = false;
87 }
88 }
89 }
6630c1cb 90
a5e6d723 91 if(!valid || recipients.isEmpty())
2097c09a 92 {
992ff49b 93 return;
2097c09a 94 }
0eba0044 95
a5e6d723
WL
96 // Format confirmation message
97 QStringList formatted;
98 foreach(const SendCoinsRecipient &rcp, recipients)
99 {
23b48d13
PK
100 // generate bold amount string
101 QString amount = "<b>" + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
102 amount.append("</b>");
103 // generate monospace address string
104 QString address = "<span style='font-family: monospace;'>" + rcp.address;
105 address.append("</span>");
106
107 QString recipientElement;
108
c6c97e0f 109 if (!rcp.paymentRequest.IsInitialized()) // normal payment
a41d5fe0 110 {
23b48d13 111 if(rcp.label.length() > 0) // label with address
9e8904f6 112 {
23b48d13
PK
113 recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label));
114 recipientElement.append(QString(" (%1)").arg(address));
9e8904f6 115 }
23b48d13 116 else // just address
9e8904f6 117 {
23b48d13 118 recipientElement = tr("%1 to %2").arg(amount, address);
9e8904f6 119 }
a41d5fe0 120 }
c6c97e0f 121 else if(!rcp.authenticatedMerchant.isEmpty()) // secure payment request
a41d5fe0 122 {
23b48d13 123 recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant));
a41d5fe0 124 }
c6c97e0f
PK
125 else // insecure payment request
126 {
127 recipientElement = tr("%1 to %2").arg(amount, address);
128 }
23b48d13
PK
129
130 formatted.append(recipientElement);
a5e6d723 131 }
38deedc1 132
7d145a0f
MC
133 fNewRecipientAllowed = false;
134
3479849d 135
b7bcaf94
WL
136 WalletModel::UnlockContext ctx(model->requestUnlock());
137 if(!ctx.isValid())
138 {
139 // Unlock wallet was cancelled
7d145a0f 140 fNewRecipientAllowed = true;
b7bcaf94
WL
141 return;
142 }
143
9e8904f6
JS
144 // prepare transaction for getting txFee earlier
145 WalletModelTransaction currentTransaction(recipients);
146 WalletModel::SendCoinsReturn prepareStatus = model->prepareTransaction(currentTransaction);
71ba4670
PK
147 // process prepareStatus and on error generate message shown to user
148 processSendCoinsReturn(prepareStatus,
149 BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee()));
9e8904f6
JS
150
151 if(prepareStatus.status != WalletModel::OK) {
152 fNewRecipientAllowed = true;
153 return;
154 }
155
156 qint64 txFee = currentTransaction.getTransactionFee();
157 QString questionString = tr("Are you sure you want to send?");
158 questionString.append("<br /><br />%1");
159
160 if(txFee > 0)
161 {
162 // append fee string if a fee is required
163 questionString.append("<hr /><span style='color:#aa0000;'>");
164 questionString.append(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
165 questionString.append("</span> ");
166 questionString.append(tr("added as transaction fee"));
167 }
168 if(txFee > 0 || recipients.count() > 1)
169 {
170 // add total amount string if there are more then one recipients or a fee is required
171 questionString.append("<hr />");
172 questionString.append(tr("Total Amount %1").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTotalTransactionAmount()+txFee)));
173 }
174
175 QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
176 questionString.arg(formatted.join("<br />")),
23b48d13 177 QMessageBox::Yes | QMessageBox::Cancel,
9e8904f6
JS
178 QMessageBox::Cancel);
179
180 if(retval != QMessageBox::Yes)
181 {
182 fNewRecipientAllowed = true;
183 return;
184 }
185
186 // now send the prepared transaction
71ba4670
PK
187 WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
188 // process sendStatus and on error generate message shown to user
189 processSendCoinsReturn(sendStatus);
190
191 if (sendStatus.status == WalletModel::OK)
9e8904f6 192 {
92f20d53 193 accept();
2097c09a 194 }
7d145a0f 195 fNewRecipientAllowed = true;
3a7abc2c
WL
196}
197
a5e6d723 198void SendCoinsDialog::clear()
3a7abc2c 199{
a5e6d723 200 // Remove entries until only one left
db7f0234 201 while(ui->entries->count())
a5e6d723
WL
202 {
203 delete ui->entries->takeAt(0)->widget();
204 }
db7f0234 205 addEntry();
4d1bb15e 206
a5e6d723 207 updateRemoveEnabled();
3479849d 208
3ddf10e5 209 ui->sendButton->setDefault(true);
3479849d
WL
210}
211
212void SendCoinsDialog::reject()
213{
214 clear();
215}
216
217void SendCoinsDialog::accept()
218{
219 clear();
220}
a5e6d723 221
db7f0234 222SendCoinsEntry *SendCoinsDialog::addEntry()
a5e6d723
WL
223{
224 SendCoinsEntry *entry = new SendCoinsEntry(this);
225 entry->setModel(model);
226 ui->entries->addWidget(entry);
227 connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
228
229 updateRemoveEnabled();
230
231 // Focus the field, so that entry can start immediately
232 entry->clear();
9a93c4c0
MC
233 entry->setFocus();
234 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
1ce04488 235 qApp->processEvents();
9a93c4c0 236 QScrollBar* bar = ui->scrollArea->verticalScrollBar();
e0873daf 237 if(bar)
9a93c4c0 238 bar->setSliderPosition(bar->maximum());
db7f0234 239 return entry;
a5e6d723
WL
240}
241
242void SendCoinsDialog::updateRemoveEnabled()
243{
244 // Remove buttons are enabled as soon as there is more than one send-entry
245 bool enabled = (ui->entries->count() > 1);
246 for(int i = 0; i < ui->entries->count(); ++i)
247 {
248 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
249 if(entry)
250 {
251 entry->setRemoveEnabled(enabled);
252 }
253 }
254 setupTabChain(0);
255}
256
257void SendCoinsDialog::removeEntry(SendCoinsEntry* entry)
258{
259 delete entry;
260 updateRemoveEnabled();
261}
262
263QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
264{
265 for(int i = 0; i < ui->entries->count(); ++i)
266 {
267 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
268 if(entry)
269 {
270 prev = entry->setupTabChain(prev);
271 }
272 }
273 QWidget::setTabOrder(prev, ui->addButton);
274 QWidget::setTabOrder(ui->addButton, ui->sendButton);
275 return ui->sendButton;
276}
db7f0234 277
311993ab
PK
278void SendCoinsDialog::setAddress(const QString &address)
279{
280 SendCoinsEntry *entry = 0;
281 // Replace the first entry if it is still unused
282 if(ui->entries->count() == 1)
283 {
284 SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
285 if(first->isClear())
286 {
287 entry = first;
288 }
289 }
290 if(!entry)
291 {
292 entry = addEntry();
293 }
294
295 entry->setAddress(address);
296}
297
db7f0234
WL
298void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv)
299{
e0873daf 300 if(!fNewRecipientAllowed)
7d145a0f
MC
301 return;
302
db7f0234
WL
303 SendCoinsEntry *entry = 0;
304 // Replace the first entry if it is still unused
305 if(ui->entries->count() == 1)
306 {
307 SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
308 if(first->isClear())
309 {
310 entry = first;
311 }
312 }
313 if(!entry)
314 {
315 entry = addEntry();
316 }
317
318 entry->setValue(rv);
319}
320
a41d5fe0 321bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv)
db7f0234 322{
23b48d13 323 QString strSendCoins = tr("Send Coins");
c6c97e0f 324 if (rv.paymentRequest.IsInitialized()) {
a41d5fe0
GA
325 // Expired payment request?
326 const payments::PaymentDetails& details = rv.paymentRequest.getDetails();
327 if (details.has_expires() && (int64)details.expires() < GetTime())
328 {
71ba4670
PK
329 emit message(strSendCoins, tr("Payment request expired"),
330 CClientUIInterface::MSG_WARNING);
a41d5fe0
GA
331 return false;
332 }
333 }
334 else {
2bc15836 335 CBitcoinAddress address(rv.address.toStdString());
a41d5fe0 336 if (!address.IsValid()) {
71ba4670
PK
337 emit message(strSendCoins, tr("Invalid payment address %1").arg(rv.address),
338 CClientUIInterface::MSG_WARNING);
2bc15836 339 return false;
a41d5fe0 340 }
db7f0234 341 }
93b7af30 342
a41d5fe0
GA
343 pasteEntry(rv);
344 return true;
db7f0234 345}
b8afa21f 346
8fdb7e10 347void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance)
b8afa21f
WL
348{
349 Q_UNUSED(unconfirmedBalance);
8fdb7e10 350 Q_UNUSED(immatureBalance);
dead0ff8 351
bdd0c59a
PK
352 if(model && model->getOptionsModel())
353 {
354 ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
355 }
b8afa21f 356}
e0873daf
PK
357
358void SendCoinsDialog::updateDisplayUnit()
359{
bdd0c59a 360 setBalance(model->getBalance(), 0, 0);
e0873daf 361}
71ba4670
PK
362
363void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
364{
365 QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
366 // Default to a warning message, override if error message is needed
367 msgParams.second = CClientUIInterface::MSG_WARNING;
368
369 // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
370 // WalletModel::TransactionCommitFailed is used only in WalletModel::sendCoins()
371 // all others are used only in WalletModel::prepareTransaction()
372 switch(sendCoinsReturn.status)
373 {
374 case WalletModel::InvalidAddress:
375 msgParams.first = tr("The recipient address is not valid, please recheck.");
376 break;
377 case WalletModel::InvalidAmount:
378 msgParams.first = tr("The amount to pay must be larger than 0.");
379 break;
380 case WalletModel::AmountExceedsBalance:
381 msgParams.first = tr("The amount exceeds your balance.");
382 break;
383 case WalletModel::AmountWithFeeExceedsBalance:
384 msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
385 break;
386 case WalletModel::DuplicateAddress:
387 msgParams.first = tr("Duplicate address found, can only send to each address once per send operation.");
388 break;
389 case WalletModel::TransactionCreationFailed:
390 msgParams.first = tr("Transaction creation failed!");
391 msgParams.second = CClientUIInterface::MSG_ERROR;
392 break;
393 case WalletModel::TransactionCommitFailed:
394 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.");
395 msgParams.second = CClientUIInterface::MSG_ERROR;
396 break;
397 // OK and Aborted are included to prevent a compiler warning.
398 case WalletModel::OK:
399 case WalletModel::Aborted:
400 default:
401 return;
402 }
403
404 emit message(tr("Send Coins"), msgParams.first, msgParams.second);
405}
This page took 0.203294 seconds and 4 git commands to generate.