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