PageRenderTime 40ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/qt/sendcoinsdialog.cpp

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