Merge branch '0.5.x' into 0.6.0.x
[novacoin.git] / src / qt / sendcoinsdialog.cpp
1 #include "sendcoinsdialog.h"
2 #include "ui_sendcoinsdialog.h"
3 #include "walletmodel.h"
4 #include "bitcoinunits.h"
5 #include "addressbookpage.h"
6 #include "optionsmodel.h"
7 #include "sendcoinsentry.h"
8 #include "guiutil.h"
9 #include "askpassphrasedialog.h"
10
11 #include <QMessageBox>
12 #include <QLocale>
13 #include <QTextDocument>
14 #include <QScrollBar>
15
16 SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
17     QDialog(parent),
18     ui(new Ui::SendCoinsDialog),
19     model(0)
20 {
21     ui->setupUi(this);
22
23 #ifdef Q_WS_MAC // Icons on push buttons are very uncommon on Mac
24     ui->addButton->setIcon(QIcon());
25     ui->clearButton->setIcon(QIcon());
26     ui->sendButton->setIcon(QIcon());
27 #endif
28
29     addEntry();
30
31     connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
32     connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
33
34     fNewRecipientAllowed = true;
35 }
36
37 void SendCoinsDialog::setModel(WalletModel *model)
38 {
39     this->model = model;
40
41     for(int i = 0; i < ui->entries->count(); ++i)
42     {
43         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
44         if(entry)
45         {
46             entry->setModel(model);
47         }
48     }
49     if(model)
50     {
51         setBalance(model->getBalance(), model->getUnconfirmedBalance());
52         connect(model, SIGNAL(balanceChanged(qint64, qint64)), this, SLOT(setBalance(qint64, qint64)));
53     }
54 }
55
56 SendCoinsDialog::~SendCoinsDialog()
57 {
58     delete ui;
59 }
60
61 void SendCoinsDialog::on_sendButton_clicked()
62 {
63     QList<SendCoinsRecipient> recipients;
64     bool valid = true;
65
66     if(!model)
67         return;
68
69     for(int i = 0; i < ui->entries->count(); ++i)
70     {
71         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
72         if(entry)
73         {
74             if(entry->validate())
75             {
76                 recipients.append(entry->getValue());
77             }
78             else
79             {
80                 valid = false;
81             }
82         }
83     }
84
85     if(!valid || recipients.isEmpty())
86     {
87         return;
88     }
89
90     // Format confirmation message
91     QStringList formatted;
92     foreach(const SendCoinsRecipient &rcp, recipients)
93     {
94         formatted.append(tr("<b>%1</b> to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), Qt::escape(rcp.label), rcp.address));
95     }
96
97     fNewRecipientAllowed = false;
98
99     QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
100                           tr("Are you sure you want to send %1?").arg(formatted.join(tr(" and "))),
101           QMessageBox::Yes|QMessageBox::Cancel,
102           QMessageBox::Cancel);
103
104     if(retval != QMessageBox::Yes)
105     {
106         fNewRecipientAllowed = true;
107         return;
108     }
109
110     WalletModel::UnlockContext ctx(model->requestUnlock());
111     if(!ctx.isValid())
112     {
113         // Unlock wallet was cancelled
114         fNewRecipientAllowed = true;
115         return;
116     }
117
118     WalletModel::SendCoinsReturn sendstatus = model->sendCoins(recipients);
119     switch(sendstatus.status)
120     {
121     case WalletModel::InvalidAddress:
122         QMessageBox::warning(this, tr("Send Coins"),
123             tr("The recepient address is not valid, please recheck."),
124             QMessageBox::Ok, QMessageBox::Ok);
125         break;
126     case WalletModel::InvalidAmount:
127         QMessageBox::warning(this, tr("Send Coins"),
128             tr("The amount to pay must be larger than 0."),
129             QMessageBox::Ok, QMessageBox::Ok);
130         break;
131     case WalletModel::AmountExceedsBalance:
132         QMessageBox::warning(this, tr("Send Coins"),
133             tr("Amount exceeds your balance"),
134             QMessageBox::Ok, QMessageBox::Ok);
135         break;
136     case WalletModel::AmountWithFeeExceedsBalance:
137         QMessageBox::warning(this, tr("Send Coins"),
138             tr("Total exceeds your balance when the %1 transaction fee is included").
139             arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, sendstatus.fee)),
140             QMessageBox::Ok, QMessageBox::Ok);
141         break;
142     case WalletModel::DuplicateAddress:
143         QMessageBox::warning(this, tr("Send Coins"),
144             tr("Duplicate address found, can only send to each address once in one send operation"),
145             QMessageBox::Ok, QMessageBox::Ok);
146         break;
147     case WalletModel::TransactionCreationFailed:
148         QMessageBox::warning(this, tr("Send Coins"),
149             tr("Error: Transaction creation failed  "),
150             QMessageBox::Ok, QMessageBox::Ok);
151         break;
152     case WalletModel::TransactionCommitFailed:
153         QMessageBox::warning(this, tr("Send Coins"),
154             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."),
155             QMessageBox::Ok, QMessageBox::Ok);
156         break;
157     case WalletModel::Aborted: // User aborted, nothing to do
158         break;
159     case WalletModel::OK:
160         accept();
161         break;
162     }
163     fNewRecipientAllowed = true;
164 }
165
166 void SendCoinsDialog::clear()
167 {
168     // Remove entries until only one left
169     while(ui->entries->count())
170     {
171         delete ui->entries->takeAt(0)->widget();
172     }
173     addEntry();
174
175     updateRemoveEnabled();
176
177     ui->sendButton->setDefault(true);
178 }
179
180 void SendCoinsDialog::reject()
181 {
182     clear();
183 }
184
185 void SendCoinsDialog::accept()
186 {
187     clear();
188 }
189
190 SendCoinsEntry *SendCoinsDialog::addEntry()
191 {
192     SendCoinsEntry *entry = new SendCoinsEntry(this);
193     entry->setModel(model);
194     ui->entries->addWidget(entry);
195     connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
196
197     updateRemoveEnabled();
198
199     // Focus the field, so that entry can start immediately
200     entry->clear();
201     entry->setFocus();
202     ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
203     QCoreApplication::instance()->processEvents();
204     QScrollBar* bar = ui->scrollArea->verticalScrollBar();
205     if (bar)
206         bar->setSliderPosition(bar->maximum());
207     return entry;
208 }
209
210 void SendCoinsDialog::updateRemoveEnabled()
211 {
212     // Remove buttons are enabled as soon as there is more than one send-entry
213     bool enabled = (ui->entries->count() > 1);
214     for(int i = 0; i < ui->entries->count(); ++i)
215     {
216         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
217         if(entry)
218         {
219             entry->setRemoveEnabled(enabled);
220         }
221     }
222     setupTabChain(0);
223 }
224
225 void SendCoinsDialog::removeEntry(SendCoinsEntry* entry)
226 {
227     delete entry;
228     updateRemoveEnabled();
229 }
230
231 QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
232 {
233     for(int i = 0; i < ui->entries->count(); ++i)
234     {
235         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
236         if(entry)
237         {
238             prev = entry->setupTabChain(prev);
239         }
240     }
241     QWidget::setTabOrder(prev, ui->addButton);
242     QWidget::setTabOrder(ui->addButton, ui->sendButton);
243     return ui->sendButton;
244 }
245
246 void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv)
247 {
248     if (!fNewRecipientAllowed)
249         return;
250
251     SendCoinsEntry *entry = 0;
252     // Replace the first entry if it is still unused
253     if(ui->entries->count() == 1)
254     {
255         SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
256         if(first->isClear())
257         {
258             entry = first;
259         }
260     }
261     if(!entry)
262     {
263         entry = addEntry();
264     }
265
266     entry->setValue(rv);
267 }
268
269
270 void SendCoinsDialog::handleURI(const QString &uri)
271 {
272     SendCoinsRecipient rv;
273     if(!GUIUtil::parseBitcoinURI(uri, &rv))
274     {
275         return;
276     }
277     pasteEntry(rv);
278 }
279
280 void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance)
281 {
282     Q_UNUSED(unconfirmedBalance);
283     if(!model || !model->getOptionsModel())
284         return;
285
286     int unit = model->getOptionsModel()->getDisplayUnit();
287     ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance));
288 }