Feathercoin  0.5.0
P2P Digital Currency
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
sendcoinsdialog.cpp
Go to the documentation of this file.
1 #include "sendcoinsdialog.h"
2 #include "ui_sendcoinsdialog.h"
3 
4 #include "init.h"
5 #include "walletmodel.h"
6 #include "addresstablemodel.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 "coincontrol.h"
14 #include "coincontroldialog.h"
15 
16 #include <QMessageBox>
17 #include <QTextDocument>
18 #include <QScrollBar>
19 #include <QClipboard>
20 #ifdef USE_ZXING
21 #include "snapwidget.h"
22 #endif
23 
25  QDialog(parent),
26  ui(new Ui::SendCoinsDialog),
27  model(0)
28 {
29  ui->setupUi(this);
30 
31 #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
32  ui->addButton->setIcon(QIcon());
33  ui->clearButton->setIcon(QIcon());
34  ui->sendButton->setIcon(QIcon());
35  ui->sendQRButton->setIcon(QIcon());
36 #endif
37 #if QT_VERSION >= 0x040700
38  /* Do not move this to the XML file, Qt before 4.7 will choke on it */
39  ui->lineEditCoinControlChange->setPlaceholderText(tr("Enter a Feathercoin address (e.g. 6nqmPL9tX4Uz3uQhbz8GrgLfXQNQEXstVu)"));
40 #endif
41 
42 #ifndef USE_ZXING
43  this->ui->sendQRButton->hide();
44 #endif
45  addEntry();
46 
47  connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
48  connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
49 
50  // Coin Control
51  ui->lineEditCoinControlChange->setFont(GUIUtil::bitcoinAddressFont());
52  connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
53  connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
54  connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &)));
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 
86 {
87  this->model = model;
88 
89  for(int i = 0; i < ui->entries->count(); ++i)
90  {
91  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
92  if(entry)
93  {
94  entry->setModel(model);
95  }
96  }
97  if(model && model->getOptionsModel())
98  {
99  setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance());
100  connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64)));
101  connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
102 
103  // Coin Control
104  connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
105  connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
106  connect(model->getOptionsModel(), SIGNAL(transactionFeeChanged(qint64)), this, SLOT(coinControlUpdateLabels()));
107  ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures());
109  }
110 }
111 
113 {
114  delete ui;
115 }
116 
118 {
119 #ifdef USE_ZXING
120  SnapWidget* snap = new SnapWidget(this);
121  connect(snap, SIGNAL(finished(QString)), this, SLOT(onSnapClosed(QString)));
122 #endif
123 }
124 
126 {
127  emit sendCoins(s);
128 }
129 
131 {
132  QList<SendCoinsRecipient> recipients;
133  bool valid = true;
134 
135  if(!model)
136  return;
137 
138  for(int i = 0; i < ui->entries->count(); ++i)
139  {
140  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
141  if(entry)
142  {
143  if(entry->validate())
144  {
145  recipients.append(entry->getValue());
146  }
147  else
148  {
149  valid = false;
150  }
151  }
152  }
153 
154  if(!valid || recipients.isEmpty())
155  {
156  return;
157  }
158 
159  // Format confirmation message
160  QStringList formatted;
161  foreach(const SendCoinsRecipient &rcp, recipients)
162  {
163 #if QT_VERSION < 0x050000
164  formatted.append(tr("<b>%1</b> to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), Qt::escape(rcp.label), rcp.address));
165 #else
166  formatted.append(tr("<b>%1</b> to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), rcp.label.toHtmlEscaped(), rcp.address));
167 #endif
168  }
169 
170  fNewRecipientAllowed = false;
171 
172  QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
173  tr("Are you sure you want to send %1?").arg(formatted.join(tr(" and "))),
174  QMessageBox::Yes|QMessageBox::Cancel,
175  QMessageBox::Cancel);
176 
177  if(retval != QMessageBox::Yes)
178  {
179  fNewRecipientAllowed = true;
180  return;
181  }
182 
184  if(!ctx.isValid())
185  {
186  // Unlock wallet was cancelled
187  fNewRecipientAllowed = true;
188  return;
189  }
190 
191  WalletModel::SendCoinsReturn sendstatus;
193  sendstatus = model->sendCoins(recipients);
194  else
195  sendstatus = model->sendCoins(recipients, CoinControlDialog::coinControl);
196  switch(sendstatus.status)
197  {
199  QMessageBox::warning(this, tr("Send Coins"),
200  tr("The recipient address is not valid, please recheck."),
201  QMessageBox::Ok, QMessageBox::Ok);
202  break;
204  QMessageBox::warning(this, tr("Send Coins"),
205  tr("The amount to pay must be larger than 0."),
206  QMessageBox::Ok, QMessageBox::Ok);
207  break;
209  QMessageBox::warning(this, tr("Send Coins"),
210  tr("The amount exceeds your balance."),
211  QMessageBox::Ok, QMessageBox::Ok);
212  break;
214  QMessageBox::warning(this, tr("Send Coins"),
215  tr("The total exceeds your balance when the %1 transaction fee is included.").
217  QMessageBox::Ok, QMessageBox::Ok);
218  break;
220  QMessageBox::warning(this, tr("Send Coins"),
221  tr("Duplicate address found, can only send to each address once per send operation."),
222  QMessageBox::Ok, QMessageBox::Ok);
223  break;
225  QMessageBox::warning(this, tr("Send Coins"),
226  tr("Error: Transaction creation failed!"),
227  QMessageBox::Ok, QMessageBox::Ok);
228  break;
230  QMessageBox::warning(this, tr("Send Coins"),
231  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."),
232  QMessageBox::Ok, QMessageBox::Ok);
233  break;
234  case WalletModel::Aborted: // User aborted, nothing to do
235  break;
236  case WalletModel::OK:
237  accept();
240  break;
241  }
242  fNewRecipientAllowed = true;
243 }
244 
246 {
247  // Remove entries until only one left
248  while(ui->entries->count())
249  {
250  ui->entries->takeAt(0)->widget()->deleteLater();
251  }
252  addEntry();
253 
255 
256  ui->sendButton->setDefault(true);
257 }
258 
260 {
261  clear();
262 }
263 
265 {
266  clear();
267 }
268 
270 {
271  SendCoinsEntry *entry = new SendCoinsEntry(this);
272  entry->setModel(model);
273  ui->entries->addWidget(entry);
274  connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
275  connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels()));
276 
278 
279  // Focus the field, so that entry can start immediately
280  entry->clear();
281  entry->setFocus();
282  ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
283  qApp->processEvents();
284  QScrollBar* bar = ui->scrollArea->verticalScrollBar();
285  if(bar)
286  bar->setSliderPosition(bar->maximum());
287  return entry;
288 }
289 
291 {
292  // Remove buttons are enabled as soon as there is more than one send-entry
293  bool enabled = (ui->entries->count() > 1);
294  for(int i = 0; i < ui->entries->count(); ++i)
295  {
296  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
297  if(entry)
298  {
299  entry->setRemoveEnabled(enabled);
300  }
301  }
302  setupTabChain(0);
303 
305 }
306 
308 {
309  entry->deleteLater();
311 }
312 
314 {
315  for(int i = 0; i < ui->entries->count(); ++i)
316  {
317  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
318  if(entry)
319  {
320  prev = entry->setupTabChain(prev);
321  }
322  }
323  QWidget::setTabOrder(prev, ui->addButton);
324  QWidget::setTabOrder(ui->addButton, ui->sendButton);
325  return ui->sendButton;
326 }
327 
328 void SendCoinsDialog::setAddress(const QString &address)
329 {
330  SendCoinsEntry *entry = 0;
331  // Replace the first entry if it is still unused
332  if(ui->entries->count() == 1)
333  {
334  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
335  if(first->isClear())
336  {
337  entry = first;
338  }
339  }
340  if(!entry)
341  {
342  entry = addEntry();
343  }
344 
345  entry->setAddress(address);
346 }
347 
349 {
351  return;
352 
353  SendCoinsEntry *entry = 0;
354  // Replace the first entry if it is still unused
355  if(ui->entries->count() == 1)
356  {
357  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
358  if(first->isClear())
359  {
360  entry = first;
361  }
362  }
363  if(!entry)
364  {
365  entry = addEntry();
366  }
367 
368  entry->setValue(rv);
369 }
370 
371 bool SendCoinsDialog::handleURI(const QString &uri)
372 {
374  // URI has to be valid
375  if (GUIUtil::parseBitcoinURI(uri, &rv))
376  {
377  CBitcoinAddress address(rv.address.toStdString());
378  if (!address.IsValid())
379  return false;
380  pasteEntry(rv);
381  return true;
382  }
383 
384  return false;
385 }
386 
387 void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance)
388 {
389  Q_UNUSED(unconfirmedBalance);
390  Q_UNUSED(immatureBalance);
391  if(!model || !model->getOptionsModel())
392  return;
393 
394  int unit = model->getOptionsModel()->getDisplayUnit();
395  ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance));
396 }
397 
399 {
400  if(model && model->getOptionsModel())
401  {
402  // Update labelBalance with the current balance and the current unit
404  }
405 }
406 
407 // Coin Control: copy label "Quantity" to clipboard
409 {
410  GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
411 }
412 
413 // Coin Control: copy label "Amount" to clipboard
415 {
416  GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
417 }
418 
419 // Coin Control: copy label "Fee" to clipboard
421 {
422  GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")));
423 }
424 
425 // Coin Control: copy label "After fee" to clipboard
427 {
428  GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")));
429 }
430 
431 // Coin Control: copy label "Bytes" to clipboard
433 {
434  GUIUtil::setClipboard(ui->labelCoinControlBytes->text());
435 }
436 
437 // Coin Control: copy label "Priority" to clipboard
439 {
440  GUIUtil::setClipboard(ui->labelCoinControlPriority->text());
441 }
442 
443 // Coin Control: copy label "Low output" to clipboard
445 {
446  GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
447 }
448 
449 // Coin Control: copy label "Change" to clipboard
451 {
452  GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")));
453 }
454 
455 // Coin Control: settings menu - coin control enabled/disabled by user
457 {
458  ui->frameCoinControl->setVisible(checked);
459 
460  if (!checked && model) // coin control features disabled
462 }
463 
464 // Coin Control: button inputs -> show actual coin control dialog
466 {
467  CoinControlDialog dlg;
468  dlg.setModel(model);
469  dlg.exec();
471 }
472 
473 // Coin Control: checkbox custom change address
475 {
476  if (model)
477  {
478  if (state == Qt::Checked)
479  CoinControlDialog::coinControl->destChange = CBitcoinAddress(ui->lineEditCoinControlChange->text().toStdString()).Get();
480  else
482  }
483 
484  ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
485  ui->labelCoinControlChangeLabel->setVisible((state == Qt::Checked));
486 }
487 
488 // Coin Control: custom change address changed
490 {
491  if (model)
492  {
493  CoinControlDialog::coinControl->destChange = CBitcoinAddress(text.toStdString()).Get();
494 
495  // label for the change address
496  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
497  if (text.isEmpty())
498  ui->labelCoinControlChangeLabel->setText("");
499  else if (!CBitcoinAddress(text.toStdString()).IsValid())
500  {
501  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
502  ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
503  }
504  else
505  {
506  QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
507  if (!associatedLabel.isEmpty())
508  ui->labelCoinControlChangeLabel->setText(associatedLabel);
509  else
510  {
511  CPubKey pubkey;
512  CKeyID keyid;
513  CBitcoinAddress(text.toStdString()).GetKeyID(keyid);
514  if (model->getPubKey(keyid, pubkey))
515  ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
516  else
517  {
518  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
519  ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
520  }
521  }
522  }
523  }
524 }
525 
526 // Coin Control: update labels
528 {
530  return;
531 
532  // set pay amounts
534  for(int i = 0; i < ui->entries->count(); ++i)
535  {
536  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
537  if(entry)
539  }
540 
541  if (CoinControlDialog::coinControl->HasSelected())
542  {
543  // actual coin control calculation
545 
546  // show coin control stats
547  ui->labelCoinControlAutomaticallySelected->hide();
548  ui->widgetCoinControl->show();
549  }
550  else
551  {
552  // hide coin control stats
553  ui->labelCoinControlAutomaticallySelected->show();
554  ui->widgetCoinControl->hide();
555  ui->labelCoinControlInsuffFunds->hide();
556  }
557 }
558 
LRUHandle * prev
Definition: cache.cc:30
void removeEntry(SendCoinsEntry *entry)
void setValue(const SendCoinsRecipient &value)
SendCoinsReturn sendCoins(const QList< SendCoinsRecipient > &recipients, const CCoinControl *coinControl=NULL)
static QString formatWithUnit(int unit, qint64 amount, bool plussign=false)
Format as string (with unit)
void coinControlClipboardPriority()
qint64 getImmatureBalance() const
Definition: walletmodel.cpp:60
static CCoinControl * coinControl
void setFocus()
SendCoinsRecipient getValue()
UnlockContext requestUnlock()
Definition: aboutdialog.h:6
SendCoinsDialog(QWidget *parent=0)
void coinControlClipboardQuantity()
void coinControlClipboardAfterFee()
void setAddress(const QString &address)
AddressTableModel * getAddressTableModel()
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:84
bool validate()
A single entry in the dialog for sending bitcoins.
void coinControlFeatureChanged(bool)
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
bool getCoinControlFeatures()
void onSnapClosed(QString s)
Scan of QR code finished.
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
Ui::SendCoinsDialog * ui
SendCoinsEntry * addEntry()
void clear()
void on_sendQRButton_clicked()
void setAddress(const QString &address)
void coinControlClipboardChange()
void setClipboard(const QString &str)
Definition: guiutil.cpp:184
void SetNull()
Definition: coincontrol.h:15
CTxDestination destChange
Definition: coincontrol.h:8
qint64 getUnconfirmedBalance() const
Definition: walletmodel.cpp:55
An encapsulated public key.
Definition: key.h:40
bool getPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
MTState * state
Definition: db_test.cc:1708
bool handleURI(const QString &uri)
WalletModel * model
Dialog for sending bitcoins.
void coinControlChangeEdited(const QString &)
static void updateLabels(WalletModel *, QDialog *)
int getDisplayUnit()
Definition: optionsmodel.h:50
void coinControlUpdateLabels()
void UnSelectAll()
Definition: coincontrol.h:42
void setModel(WalletModel *model)
void setRemoveEnabled(bool enabled)
bool isClear()
Return whether the entry is still empty and unedited.
void coinControlClipboardLowOutput()
void setModel(WalletModel *model)
QString labelForAddress(const QString &address) const
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:36
A reference to a CKey: the Hash160 of its serialized public key.
Definition: key.h:24
qint64 getBalance(const CCoinControl *coinControl=NULL) const
Definition: walletmodel.cpp:39
void setModel(WalletModel *model)
static QList< qint64 > payAmounts
void coinControlClipboardBytes()
void coinControlClipboardAmount()
void sendCoins(QString addr)
void pasteEntry(const SendCoinsRecipient &rv)
void coinControlButtonClicked()
void * arg
Definition: env_posix.cc:716
void coinControlClipboardFee()
QFont bitcoinAddressFont()
Definition: guiutil.cpp:61
OptionsModel * getOptionsModel()
void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance)
void coinControlChangeChecked(int)