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