Update CMakeLists.txt - play with openssl
[novacoin.git] / src / qt / sendcoinsdialog.cpp
1 #include "sendcoinsdialog.h"
2 #include "ui_sendcoinsdialog.h"
3
4 #include "init.h"
5 #include "base58.h"
6 #include "walletmodel.h"
7 #include "addresstablemodel.h"
8 #include "addressbookpage.h"
9
10 #include "bitcoinunits.h"
11 #include "addressbookpage.h"
12 #include "optionsmodel.h"
13 #include "sendcoinsentry.h"
14 #include "guiutil.h"
15 #include "dialogwindowflags.h"
16 #include "askpassphrasedialog.h"
17
18 #include "coincontrol.h"
19 #include "coincontroldialog.h"
20
21 #include <QMessageBox>
22 #include <QLocale>
23 #include <QTextDocument>
24 #include <QScrollBar>
25 #include <QClipboard>
26
27 SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
28     QDialog(parent, DIALOGWINDOWHINTS),
29     ui(new Ui::SendCoinsDialog),
30     model(0),
31     coinControl(0)
32 {
33     ui->setupUi(this);
34
35 #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
36     ui->addButton->setIcon(QIcon());
37     ui->clearButton->setIcon(QIcon());
38     ui->sendButton->setIcon(QIcon());
39 #endif
40
41 #if QT_VERSION >= 0x040700
42     /* Do not move this to the XML file, Qt before 4.7 will choke on it */
43     ui->lineEditCoinControlChange->setPlaceholderText(tr("Enter a NovaCoin address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5)"));
44 #endif
45
46     addEntry();
47
48     connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
49     connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
50
51     // Coin Control
52     ui->lineEditCoinControlChange->setFont(GUIUtil::bitcoinAddressFont());
53     connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
54     connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
55
56     // Coin Control: clipboard actions
57     QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
58     QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
59     QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
60     QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
61     QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
62     QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this);
63     QAction *clipboardLowOutputAction = new QAction(tr("Copy low output"), this);
64     QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
65     connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity()));
66     connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount()));
67     connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee()));
68     connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee()));
69     connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes()));
70     connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardPriority()));
71     connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput()));
72     connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange()));
73     ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
74     ui->labelCoinControlAmount->addAction(clipboardAmountAction);
75     ui->labelCoinControlFee->addAction(clipboardFeeAction);
76     ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
77     ui->labelCoinControlBytes->addAction(clipboardBytesAction);
78     ui->labelCoinControlPriority->addAction(clipboardPriorityAction);
79     ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
80     ui->labelCoinControlChange->addAction(clipboardChangeAction);
81
82     fNewRecipientAllowed = true;
83
84     coinControl = new CoinControlDialog(0);
85     connect(coinControl, SIGNAL(beforeClose()), this, SLOT(coinControlUpdateLabels()));
86 }
87
88 void SendCoinsDialog::setModel(WalletModel *model)
89 {
90     this->model = model;
91
92     for(int i = 0; i < ui->entries->count(); ++i)
93     {
94         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
95         if(entry)
96         {
97             entry->setModel(model);
98         }
99     }
100     if(model && model->getOptionsModel())
101     {
102         setBalance(model->getBalance(), model->getBalanceWatchOnly(), model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance());
103         connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64)));
104         connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
105
106         // Coin Control
107         connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
108         connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
109         connect(model->getOptionsModel(), SIGNAL(transactionFeeChanged(qint64)), this, SLOT(coinControlUpdateLabels()));
110         ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures());
111         coinControlUpdateLabels();
112     }
113 }
114
115 SendCoinsDialog::~SendCoinsDialog()
116 {
117     delete coinControl;
118     delete ui;
119 }
120
121 void SendCoinsDialog::on_sendButton_clicked()
122 {
123     QList<SendCoinsRecipient> recipients;
124     bool valid = true;
125
126     if(!model)
127         return;
128
129     if (ui->lineEditCoinControlChange->isEnabled())
130     {
131         if(!ui->lineEditCoinControlChange->hasAcceptableInput() ||
132            (model && !model->validateAddress(ui->lineEditCoinControlChange->text())))
133         {
134             CoinControlDialog::coinControl->destChange = CBitcoinAddress();
135             ui->lineEditCoinControlChange->setValid(false);
136             valid = false;
137         }
138         else
139             CoinControlDialog::coinControl->destChange = CBitcoinAddress(ui->lineEditCoinControlChange->text().toStdString());
140     }
141
142     for(int i = 0; i < ui->entries->count(); ++i)
143     {
144         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
145         if(entry)
146         {
147             if(entry->validate())
148             {
149                 recipients.append(entry->getValue());
150             }
151             else
152             {
153                 valid = false;
154             }
155         }
156     }
157
158     if(!valid || recipients.isEmpty())
159     {
160         return;
161     }
162
163     // Format confirmation message
164     QStringList formatted;
165     foreach(const SendCoinsRecipient &rcp, recipients)
166     {
167 #if QT_VERSION < 0x050000
168         formatted.append(tr("<b>%1</b> to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), Qt::escape(rcp.label), rcp.address));
169 #else
170         formatted.append(tr("<b>%1</b> to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), rcp.label.toHtmlEscaped(), rcp.address));
171 #endif
172     }
173
174     fNewRecipientAllowed = false;
175
176     QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
177                           tr("Are you sure you want to send %1?").arg(formatted.join(tr(" and "))),
178           QMessageBox::Yes|QMessageBox::Cancel,
179           QMessageBox::Cancel);
180
181     if(retval != QMessageBox::Yes)
182     {
183         fNewRecipientAllowed = true;
184         return;
185     }
186
187     WalletModel::UnlockContext ctx(model->requestUnlock());
188     if(!ctx.isValid())
189     {
190         // Unlock wallet was cancelled
191         fNewRecipientAllowed = true;
192         return;
193     }
194
195     WalletModel::SendCoinsReturn sendstatus;
196
197     if (!model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures())
198         sendstatus = model->sendCoins(recipients);
199     else
200         sendstatus = model->sendCoins(recipients, CoinControlDialog::coinControl);
201
202     switch(sendstatus.status)
203     {
204     case WalletModel::InvalidAddress:
205         QMessageBox::warning(this, tr("Send Coins"),
206             tr("The recipient address is not valid, please recheck."),
207             QMessageBox::Ok, QMessageBox::Ok);
208         break;
209     case WalletModel::InvalidAmount:
210         QMessageBox::warning(this, tr("Send Coins"),
211             tr("The amount to pay must be larger than 0."),
212             QMessageBox::Ok, QMessageBox::Ok);
213         break;
214     case WalletModel::AmountExceedsBalance:
215         QMessageBox::warning(this, tr("Send Coins"),
216             tr("The amount exceeds your balance."),
217             QMessageBox::Ok, QMessageBox::Ok);
218         break;
219     case WalletModel::AmountWithFeeExceedsBalance:
220         QMessageBox::warning(this, tr("Send Coins"),
221             tr("The total exceeds your balance when the %1 transaction fee is included.").
222             arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, sendstatus.fee)),
223             QMessageBox::Ok, QMessageBox::Ok);
224         break;
225     case WalletModel::DuplicateAddress:
226         QMessageBox::warning(this, tr("Send Coins"),
227             tr("Duplicate address found, can only send to each address once per send operation."),
228             QMessageBox::Ok, QMessageBox::Ok);
229         break;
230     case WalletModel::TransactionCreationFailed:
231         QMessageBox::warning(this, tr("Send Coins"),
232             tr("Error: Transaction creation failed."),
233             QMessageBox::Ok, QMessageBox::Ok);
234         break;
235     case WalletModel::TransactionCommitFailed:
236         QMessageBox::warning(this, tr("Send Coins"),
237             tr("Error: 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."),
238             QMessageBox::Ok, QMessageBox::Ok);
239         break;
240     case WalletModel::Aborted: // User aborted, nothing to do
241         break;
242     case WalletModel::OK:
243         accept();
244         CoinControlDialog::coinControl->UnSelectAll();
245         coinControlUpdateLabels();
246         break;
247     }
248     fNewRecipientAllowed = true;
249 }
250
251 void SendCoinsDialog::clear()
252 {
253     // Remove entries until only one left
254     while(ui->entries->count())
255     {
256         delete ui->entries->takeAt(0)->widget();
257     }
258     addEntry();
259
260     updateRemoveEnabled();
261
262     ui->sendButton->setDefault(true);
263 }
264
265 void SendCoinsDialog::reject()
266 {
267     clear();
268 }
269
270 void SendCoinsDialog::accept()
271 {
272     clear();
273 }
274
275 SendCoinsEntry *SendCoinsDialog::addEntry()
276 {
277     SendCoinsEntry *entry = new SendCoinsEntry(this);
278     entry->setModel(model);
279     ui->entries->addWidget(entry);
280     connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
281     connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels()));
282
283     updateRemoveEnabled();
284
285     // Focus the field, so that entry can start immediately
286     entry->clear();
287     entry->setFocus();
288     ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
289     QCoreApplication::instance()->processEvents();
290     QScrollBar* bar = ui->scrollArea->verticalScrollBar();
291     if(bar)
292         bar->setSliderPosition(bar->maximum());
293     return entry;
294 }
295
296 void SendCoinsDialog::updateRemoveEnabled()
297 {
298     // Remove buttons are enabled as soon as there is more than one send-entry
299     bool enabled = (ui->entries->count() > 1);
300     for(int i = 0; i < ui->entries->count(); ++i)
301     {
302         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
303         if(entry)
304         {
305             entry->setRemoveEnabled(enabled);
306         }
307     }
308     setupTabChain(0);
309     coinControlUpdateLabels();
310 }
311
312 void SendCoinsDialog::removeEntry(SendCoinsEntry* entry)
313 {
314     delete entry;
315     updateRemoveEnabled();
316 }
317
318 QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
319 {
320     for(int i = 0; i < ui->entries->count(); ++i)
321     {
322         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
323         if(entry)
324         {
325             prev = entry->setupTabChain(prev);
326         }
327     }
328     QWidget::setTabOrder(prev, ui->addButton);
329     QWidget::setTabOrder(ui->addButton, ui->sendButton);
330     return ui->sendButton;
331 }
332
333 void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv)
334 {
335     if(!fNewRecipientAllowed)
336         return;
337
338     SendCoinsEntry *entry = 0;
339     // Replace the first entry if it is still unused
340     if(ui->entries->count() == 1)
341     {
342         SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
343         if(first->isClear())
344         {
345             entry = first;
346         }
347     }
348     if(!entry)
349     {
350         entry = addEntry();
351     }
352
353     entry->setValue(rv);
354 }
355
356 bool SendCoinsDialog::handleURI(const QString &uri)
357 {
358     SendCoinsRecipient rv;
359     // URI has to be valid
360     if (GUIUtil::parseBitcoinURI(uri, &rv))
361     {
362         CBitcoinAddress address(rv.address.toStdString());
363         if (!address.IsValid())
364             return false;
365         pasteEntry(rv);
366         return true;
367     }
368
369     return false;
370 }
371
372 void SendCoinsDialog::setBalance(qint64 total, qint64 watchOnly, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
373 {
374     Q_UNUSED(stake);
375     Q_UNUSED(unconfirmedBalance);
376     Q_UNUSED(immatureBalance);
377     if(!model || !model->getOptionsModel())
378         return;
379
380     int unit = model->getOptionsModel()->getDisplayUnit();
381     ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, total - watchOnly));
382 }
383
384 void SendCoinsDialog::updateDisplayUnit()
385 {
386     if(model && model->getOptionsModel())
387     {
388         // Update labelBalance with the current balance and the current unit
389         ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getBalance() - model->getBalanceWatchOnly()));
390     }
391 }
392
393 // Coin Control: copy label "Quantity" to clipboard
394 void SendCoinsDialog::coinControlClipboardQuantity()
395 {
396     QApplication::clipboard()->setText(ui->labelCoinControlQuantity->text());
397 }
398
399 // Coin Control: copy label "Amount" to clipboard
400 void SendCoinsDialog::coinControlClipboardAmount()
401 {
402     QApplication::clipboard()->setText(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
403 }
404
405 // Coin Control: copy label "Fee" to clipboard
406 void SendCoinsDialog::coinControlClipboardFee()
407 {
408     QApplication::clipboard()->setText(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")));
409 }
410
411 // Coin Control: copy label "After fee" to clipboard
412 void SendCoinsDialog::coinControlClipboardAfterFee()
413 {
414     QApplication::clipboard()->setText(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")));
415 }
416
417 // Coin Control: copy label "Bytes" to clipboard
418 void SendCoinsDialog::coinControlClipboardBytes()
419 {
420     QApplication::clipboard()->setText(ui->labelCoinControlBytes->text());
421 }
422
423 // Coin Control: copy label "Priority" to clipboard
424 void SendCoinsDialog::coinControlClipboardPriority()
425 {
426     QApplication::clipboard()->setText(ui->labelCoinControlPriority->text());
427 }
428
429 // Coin Control: copy label "Low output" to clipboard
430 void SendCoinsDialog::coinControlClipboardLowOutput()
431 {
432     QApplication::clipboard()->setText(ui->labelCoinControlLowOutput->text());
433 }
434
435 // Coin Control: copy label "Change" to clipboard
436 void SendCoinsDialog::coinControlClipboardChange()
437 {
438     QApplication::clipboard()->setText(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")));
439 }
440
441 // Coin Control: settings menu - coin control enabled/disabled by user
442 void SendCoinsDialog::coinControlFeatureChanged(bool checked)
443 {
444     ui->frameCoinControl->setVisible(checked);
445
446     if (!checked && model) // coin control features disabled
447         CoinControlDialog::coinControl->SetNull();
448 }
449
450 // Coin Control: button inputs -> show actual coin control dialog
451 void SendCoinsDialog::coinControlButtonClicked()
452 {
453     coinControl->setModel(model);
454     coinControl->setWindowModality(Qt::ApplicationModal);
455     coinControl->show();
456 }
457
458 // Coin Control: checkbox custom change address
459 void SendCoinsDialog::coinControlChangeChecked(int state)
460 {
461     if (model)
462     {
463         if (state == Qt::Checked)
464             CoinControlDialog::coinControl->destChange = CBitcoinAddress(ui->lineEditCoinControlChange->text().toStdString());
465         else
466             CoinControlDialog::coinControl->destChange = CBitcoinAddress();
467     }
468
469     ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
470 //    ui->labelCoinControlChangeLabel->setEnabled((state == Qt::Checked));
471     ui->addressBookButton->setEnabled((state == Qt::Checked));
472     ui->pasteButton->setEnabled((state == Qt::Checked));
473 }
474
475 void SendCoinsDialog::on_pasteButton_clicked()
476 {
477     // Paste text from clipboard into recipient field
478     ui->lineEditCoinControlChange->setText(QApplication::clipboard()->text());
479 }
480
481 void SendCoinsDialog::on_addressBookButton_clicked()
482 {
483     if(!model)
484         return;
485     AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::ReceivingTab, this);
486     dlg.setModel(model->getAddressTableModel());
487     if(dlg.exec())
488     {
489         ui->lineEditCoinControlChange->setText(dlg.getReturnValue());
490     }
491 }
492
493 // Coin Control: update labels
494 void SendCoinsDialog::coinControlUpdateLabels()
495 {
496     if (!model || !model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures())
497         return;
498
499     // set pay amounts
500     CoinControlDialog::payAmounts.clear();
501     for(int i = 0; i < ui->entries->count(); ++i)
502     {
503         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
504         if(entry)
505             CoinControlDialog::payAmounts.append(entry->getValue().amount);
506     }
507
508     if (CoinControlDialog::coinControl->HasSelected())
509     {
510         // actual coin control calculation
511         CoinControlDialog::updateLabels(model, this);
512
513         // show coin control stats
514         ui->labelCoinControlAutomaticallySelected->hide();
515         ui->widgetCoinControl->show();
516     }
517     else
518     {
519         // hide coin control stats
520         ui->labelCoinControlAutomaticallySelected->show();
521         ui->widgetCoinControl->hide();
522         ui->labelCoinControlInsuffFunds->hide();
523     }
524 }