123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- // ECOin - Copyright (c) - 2014/2024 - GPLv3 - epsylon@riseup.net (https://03c8.net)
- #include "sendcoinsdialog.h"
- #include "ui_sendcoinsdialog.h"
- #include "init.h"
- #include "walletmodel.h"
- #include "addresstablemodel.h"
- #include "addressbookpage.h"
- #include "ecoinunits.h"
- #include "addressbookpage.h"
- #include "optionsmodel.h"
- #include "sendcoinsentry.h"
- #include "guiutil.h"
- #include "askpassphrasedialog.h"
- #include "coincontrol.h"
- #include "coincontroldialog.h"
- #include <QMessageBox>
- #include <QLocale>
- #include <QTextDocument>
- #include <QScrollBar>
- #include <QClipboard>
- #include <QString>
- #include <QUrl>
- SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
- QDialog(parent),
- ui(new Ui::SendCoinsDialog),
- model(0)
- {
- ui->setupUi(this);
- #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
- ui->addButton->setIcon(QIcon());
- ui->clearButton->setIcon(QIcon());
- ui->sendButton->setIcon(QIcon());
- #endif
- #if QT_VERSION >= 0x040700
- /* Do not move this to the XML file, Qt before 4.7 will choke on it */
- ui->lineEditCoinControlChange->setPlaceholderText(tr("Enter a Ecoin address (e.g. EJiA1K71didR1ovdVUtse1AJVWye2V1jeV)"));
- #endif
- addEntry();
- connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
- connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
- // Coin Control
- ui->lineEditCoinControlChange->setFont(GUIUtil::ecoinAddressFont());
- connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
- connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
- connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &)));
- // Coin Control: clipboard actions
- QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
- QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
- QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
- QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
- QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
- QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this);
- QAction *clipboardLowOutputAction = new QAction(tr("Copy low output"), this);
- QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
- connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity()));
- connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount()));
- connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee()));
- connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee()));
- connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes()));
- connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardPriority()));
- connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput()));
- connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange()));
- ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
- ui->labelCoinControlAmount->addAction(clipboardAmountAction);
- ui->labelCoinControlFee->addAction(clipboardFeeAction);
- ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
- ui->labelCoinControlBytes->addAction(clipboardBytesAction);
- ui->labelCoinControlPriority->addAction(clipboardPriorityAction);
- ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
- ui->labelCoinControlChange->addAction(clipboardChangeAction);
- fNewRecipientAllowed = true;
- }
- void SendCoinsDialog::setModel(WalletModel *model)
- {
- this->model = model;
- for(int i = 0; i < ui->entries->count(); ++i)
- {
- SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
- if(entry)
- {
- entry->setModel(model);
- }
- }
- if(model && model->getOptionsModel())
- {
- setBalance(model->getBalance(), model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance());
- connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64)));
- connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
- // Coin Control
- connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
- connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
- connect(model->getOptionsModel(), SIGNAL(transactionFeeChanged(qint64)), this, SLOT(coinControlUpdateLabels()));
- ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures());
- coinControlUpdateLabels();
- }
- }
- SendCoinsDialog::~SendCoinsDialog()
- {
- delete ui;
- }
- void SendCoinsDialog::on_sendButton_clicked()
- {
- QList<SendCoinsRecipient> recipients;
- bool valid = true;
- if(!model)
- return;
- for(int i = 0; i < ui->entries->count(); ++i)
- {
- SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
- if(entry)
- {
- if(entry->validate())
- {
- recipients.append(entry->getValue());
- }
- else
- {
- valid = false;
- }
- }
- }
- if(!valid || recipients.isEmpty())
- {
- return;
- }
- // Format confirmation message
- QStringList formatted;
- foreach(const SendCoinsRecipient &rcp, recipients)
- {
- formatted.append(tr("<b>%1</b> to %2 (%3)").arg(EcoinUnits::formatWithUnit(EcoinUnits::ECO, rcp.amount), rcp.label.toHtmlEscaped(), rcp.address));
- }
- fNewRecipientAllowed = false;
- QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
- tr("Are you sure you want to send %1?").arg(formatted.join(tr(" and "))),
- QMessageBox::Yes|QMessageBox::Cancel,
- QMessageBox::Cancel);
- if(retval != QMessageBox::Yes)
- {
- fNewRecipientAllowed = true;
- return;
- }
- WalletModel::UnlockContext ctx(model->requestUnlock());
- if(!ctx.isValid())
- {
- // Unlock wallet was cancelled
- fNewRecipientAllowed = true;
- return;
- }
- WalletModel::SendCoinsReturn sendstatus;
- if (!model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures())
- sendstatus = model->sendCoins(recipients);
- else
- sendstatus = model->sendCoins(recipients, CoinControlDialog::coinControl);
- switch(sendstatus.status)
- {
- case WalletModel::InvalidAddress:
- QMessageBox::warning(this, tr("Send Coins"),
- tr("The recipient address is not valid, please recheck."),
- QMessageBox::Ok, QMessageBox::Ok);
- break;
- case WalletModel::InvalidAmount:
- QMessageBox::warning(this, tr("Send Coins"),
- tr("The amount to pay must be larger than 0."),
- QMessageBox::Ok, QMessageBox::Ok);
- break;
- case WalletModel::AmountExceedsBalance:
- QMessageBox::warning(this, tr("Send Coins"),
- tr("The amount exceeds your balance."),
- QMessageBox::Ok, QMessageBox::Ok);
- break;
- case WalletModel::AmountWithFeeExceedsBalance:
- QMessageBox::warning(this, tr("Send Coins"),
- tr("The total exceeds your balance when the %1 transaction fee is included.").
- arg(EcoinUnits::formatWithUnit(EcoinUnits::ECO, sendstatus.fee)),
- QMessageBox::Ok, QMessageBox::Ok);
- break;
- case WalletModel::DuplicateAddress:
- QMessageBox::warning(this, tr("Send Coins"),
- tr("Duplicate address found, can only send to each address once per send operation."),
- QMessageBox::Ok, QMessageBox::Ok);
- break;
- case WalletModel::TransactionCreationFailed:
- QMessageBox::warning(this, tr("Send Coins"),
- tr("Error: Transaction creation failed."),
- QMessageBox::Ok, QMessageBox::Ok);
- break;
- case WalletModel::TransactionCommitFailed:
- QMessageBox::warning(this, tr("Send Coins"),
- 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."),
- QMessageBox::Ok, QMessageBox::Ok);
- break;
- case WalletModel::Aborted: // User aborted, nothing to do
- break;
- case WalletModel::OK:
- accept();
- CoinControlDialog::coinControl->UnSelectAll();
- coinControlUpdateLabels();
- break;
- }
- fNewRecipientAllowed = true;
- }
- void SendCoinsDialog::clear()
- {
- // Remove entries until only one left
- while(ui->entries->count())
- {
- delete ui->entries->takeAt(0)->widget();
- }
- addEntry();
- updateRemoveEnabled();
- ui->sendButton->setDefault(true);
- }
- void SendCoinsDialog::reject()
- {
- clear();
- }
- void SendCoinsDialog::accept()
- {
- clear();
- }
- SendCoinsEntry *SendCoinsDialog::addEntry()
- {
- SendCoinsEntry *entry = new SendCoinsEntry(this);
- entry->setModel(model);
- ui->entries->addWidget(entry);
- connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
- connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels()));
- updateRemoveEnabled();
- // Focus the field, so that entry can start immediately
- entry->clear();
- entry->setFocus();
- ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
- QCoreApplication::instance()->processEvents();
- QScrollBar* bar = ui->scrollArea->verticalScrollBar();
- if(bar)
- bar->setSliderPosition(bar->maximum());
- return entry;
- }
- void SendCoinsDialog::updateRemoveEnabled()
- {
- // Remove buttons are enabled as soon as there is more than one send-entry
- bool enabled = (ui->entries->count() > 1);
- for(int i = 0; i < ui->entries->count(); ++i)
- {
- SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
- if(entry)
- {
- entry->setRemoveEnabled(enabled);
- }
- }
- setupTabChain(0);
- coinControlUpdateLabels();
- }
- void SendCoinsDialog::removeEntry(SendCoinsEntry* entry)
- {
- delete entry;
- updateRemoveEnabled();
- }
- QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
- {
- for(int i = 0; i < ui->entries->count(); ++i)
- {
- SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
- if(entry)
- {
- prev = entry->setupTabChain(prev);
- }
- }
- QWidget::setTabOrder(prev, ui->addButton);
- QWidget::setTabOrder(ui->addButton, ui->sendButton);
- return ui->sendButton;
- }
- void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv)
- {
- if(!fNewRecipientAllowed)
- return;
- SendCoinsEntry *entry = 0;
- // Replace the first entry if it is still unused
- if(ui->entries->count() == 1)
- {
- SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
- if(first->isClear())
- {
- entry = first;
- }
- }
- if(!entry)
- {
- entry = addEntry();
- }
- entry->setValue(rv);
- }
- bool SendCoinsDialog::handleURI(const QString &uri)
- {
- SendCoinsRecipient rv;
- // URI has to be valid
- if (GUIUtil::parseEcoinURI(uri, &rv))
- {
- CEcoinAddress address(rv.address.toStdString());
- if (!address.IsValid())
- return false;
- pasteEntry(rv);
- return true;
- }
- return false;
- }
- void SendCoinsDialog::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
- {
- Q_UNUSED(stake);
- Q_UNUSED(unconfirmedBalance);
- Q_UNUSED(immatureBalance);
- if(!model || !model->getOptionsModel())
- return;
- int unit = model->getOptionsModel()->getDisplayUnit();
- ui->labelBalance->setText(EcoinUnits::formatWithUnit(unit, balance));
- }
- void SendCoinsDialog::updateDisplayUnit()
- {
- if(model && model->getOptionsModel())
- {
- // Update labelBalance with the current balance and the current unit
- ui->labelBalance->setText(EcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getBalance()));
- }
- }
- // Coin Control: copy label "Quantity" to clipboard
- void SendCoinsDialog::coinControlClipboardQuantity()
- {
- QApplication::clipboard()->setText(ui->labelCoinControlQuantity->text());
- }
- // Coin Control: copy label "Amount" to clipboard
- void SendCoinsDialog::coinControlClipboardAmount()
- {
- QApplication::clipboard()->setText(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
- }
- // Coin Control: copy label "Fee" to clipboard
- void SendCoinsDialog::coinControlClipboardFee()
- {
- QApplication::clipboard()->setText(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")));
- }
- // Coin Control: copy label "After fee" to clipboard
- void SendCoinsDialog::coinControlClipboardAfterFee()
- {
- QApplication::clipboard()->setText(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")));
- }
- // Coin Control: copy label "Bytes" to clipboard
- void SendCoinsDialog::coinControlClipboardBytes()
- {
- QApplication::clipboard()->setText(ui->labelCoinControlBytes->text());
- }
- // Coin Control: copy label "Priority" to clipboard
- void SendCoinsDialog::coinControlClipboardPriority()
- {
- QApplication::clipboard()->setText(ui->labelCoinControlPriority->text());
- }
- // Coin Control: copy label "Low output" to clipboard
- void SendCoinsDialog::coinControlClipboardLowOutput()
- {
- QApplication::clipboard()->setText(ui->labelCoinControlLowOutput->text());
- }
- // Coin Control: copy label "Change" to clipboard
- void SendCoinsDialog::coinControlClipboardChange()
- {
- QApplication::clipboard()->setText(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")));
- }
- // Coin Control: settings menu - coin control enabled/disabled by user
- void SendCoinsDialog::coinControlFeatureChanged(bool checked)
- {
- ui->frameCoinControl->setVisible(checked);
- if (!checked && model) // coin control features disabled
- CoinControlDialog::coinControl->SetNull();
- }
- // Coin Control: button inputs -> show actual coin control dialog
- void SendCoinsDialog::coinControlButtonClicked()
- {
- CoinControlDialog dlg;
- dlg.setModel(model);
- dlg.exec();
- coinControlUpdateLabels();
- }
- // Coin Control: checkbox custom change address
- void SendCoinsDialog::coinControlChangeChecked(int state)
- {
- if (model)
- {
- if (state == Qt::Checked)
- CoinControlDialog::coinControl->destChange = CEcoinAddress(ui->lineEditCoinControlChange->text().toStdString()).Get();
- else
- CoinControlDialog::coinControl->destChange = CNoDestination();
- }
- ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
- ui->labelCoinControlChangeLabel->setEnabled((state == Qt::Checked));
- }
- // Coin Control: custom change address changed
- void SendCoinsDialog::coinControlChangeEdited(const QString & text)
- {
- if (model)
- {
- CoinControlDialog::coinControl->destChange = CEcoinAddress(text.toStdString()).Get();
- // label for the change address
- ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
- if (text.isEmpty())
- ui->labelCoinControlChangeLabel->setText("");
- else if (!CEcoinAddress(text.toStdString()).IsValid())
- {
- ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
- ui->labelCoinControlChangeLabel->setText(tr("WARNING: Invalid Ecoin address"));
- }
- else
- {
- QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
- if (!associatedLabel.isEmpty())
- ui->labelCoinControlChangeLabel->setText(associatedLabel);
- else
- {
- CPubKey pubkey;
- CKeyID keyid;
- CEcoinAddress(text.toStdString()).GetKeyID(keyid);
- if (model->getPubKey(keyid, pubkey))
- ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
- else
- {
- ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
- ui->labelCoinControlChangeLabel->setText(tr("WARNING: unknown change address"));
- }
- }
- }
- }
- }
- // Coin Control: update labels
- void SendCoinsDialog::coinControlUpdateLabels()
- {
- if (!model || !model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures())
- return;
- // set pay amounts
- CoinControlDialog::payAmounts.clear();
- for(int i = 0; i < ui->entries->count(); ++i)
- {
- SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
- if(entry)
- CoinControlDialog::payAmounts.append(entry->getValue().amount);
- }
- if (CoinControlDialog::coinControl->HasSelected())
- {
- // actual coin control calculation
- CoinControlDialog::updateLabels(model, this);
- // show coin control stats
- ui->labelCoinControlAutomaticallySelected->hide();
- ui->widgetCoinControl->show();
- }
- else
- {
- // hide coin control stats
- ui->labelCoinControlAutomaticallySelected->show();
- ui->widgetCoinControl->hide();
- ui->labelCoinControlInsuffFunds->hide();
- }
- }
|