diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 74e25f87a..8e136521c 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -1,1276 +1,1283 @@ SendCoinsEntry 0 0 729 150 Qt::TabFocus false This is a normal payment. QFrame::NoFrame 8 4 12 8 Pay &To: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter payTo 0 The Bitcoin address to send the payment to Choose previously used address :/icons/address-book:/icons/address-book 22 22 Alt+A Paste address from clipboard :/icons/editpaste:/icons/editpaste 22 22 Alt+P Remove this entry :/icons/remove:/icons/remove 22 22 &Label: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter addAsLabel Enter a label for this address to add it to the list of used addresses A&mount: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter payAmount - + The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally. S&ubtract fee from amount + + + + Use available balance + + + Message: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::PlainText Qt::Horizontal 0 0 0 255 255 127 255 255 255 255 255 191 127 127 63 170 170 84 0 0 0 255 255 255 0 0 0 255 255 255 255 255 127 0 0 0 255 255 191 255 255 220 0 0 0 0 0 0 255 255 127 255 255 255 255 255 191 127 127 63 170 170 84 0 0 0 255 255 255 0 0 0 255 255 255 255 255 127 0 0 0 255 255 191 255 255 220 0 0 0 127 127 63 255 255 127 255 255 255 255 255 191 127 127 63 170 170 84 127 127 63 255 255 255 127 127 63 255 255 127 255 255 127 0 0 0 255 255 127 255 255 220 0 0 0 This is an unauthenticated payment request. true QFrame::NoFrame 12 Pay To: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 Remove this entry :/icons/remove:/icons/remove Memo: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::PlainText A&mount: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter payAmount_is false 0 0 0 140 232 119 230 255 224 185 243 171 70 116 59 93 155 79 0 0 0 155 255 147 0 0 0 119 255 233 140 232 119 0 0 0 197 243 187 125 194 122 255 255 220 0 0 0 0 0 0 140 232 119 230 255 224 185 243 171 70 116 59 93 155 79 0 0 0 155 255 147 0 0 0 119 255 233 140 232 119 0 0 0 197 243 187 125 194 122 255 255 220 0 0 0 70 116 59 140 232 119 230 255 224 185 243 171 70 116 59 93 155 79 70 116 59 155 255 147 70 116 59 140 232 119 140 232 119 0 0 0 140 232 119 125 194 122 255 255 220 0 0 0 This is an authenticated payment request. true QFrame::NoFrame 12 Pay To: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 Qt::PlainText Remove this entry :/icons/remove:/icons/remove Memo: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::PlainText A&mount: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter payAmount_s false QValidatedLineEdit QLineEdit
qvalidatedlineedit.h
BitcoinAmountField QLineEdit
bitcoinamountfield.h
1
payTo addressBookButton pasteButton deleteButton addAsLabel payAmount payAmount_is deleteButton_is payAmount_s deleteButton_s
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 659848ac7..23ba782f4 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,972 +1,999 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" #include "addresstablemodel.h" #include "bitcoinunits.h" #include "clientmodel.h" #include "coincontroldialog.h" #include "guiutil.h" #include "optionsmodel.h" #include "platformstyle.h" #include "sendcoinsentry.h" #include "walletmodel.h" #include "chainparams.h" #include "dstencode.h" #include "txmempool.h" #include "ui_interface.h" #include "validation.h" // mempool and minRelayTxFee #include "wallet/coincontrol.h" #include "wallet/wallet.h" #include #include #include #include #include #define SEND_CONFIRM_DELAY 3 SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) : QDialog(parent), ui(new Ui::SendCoinsDialog), clientModel(0), model(0), fNewRecipientAllowed(true), fFeeMinimized(true), platformStyle(_platformStyle) { ui->setupUi(this); if (!_platformStyle->getImagesOnButtons()) { ui->addButton->setIcon(QIcon()); ui->clearButton->setIcon(QIcon()); ui->sendButton->setIcon(QIcon()); } else { ui->addButton->setIcon(_platformStyle->SingleColorIcon(":/icons/add")); ui->clearButton->setIcon( _platformStyle->SingleColorIcon(":/icons/remove")); ui->sendButton->setIcon( _platformStyle->SingleColorIcon(":/icons/send")); } GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this); addEntry(); connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry())); connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); // Coin Control 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 *clipboardLowOutputAction = new QAction(tr("Copy dust"), 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(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->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); ui->labelCoinControlChange->addAction(clipboardChangeAction); // init transaction fee section QSettings settings; if (!settings.contains("fFeeSectionMinimized")) settings.setValue("fFeeSectionMinimized", true); // compatibility if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) { // custom settings.setValue("nFeeRadio", 1); } if (!settings.contains("nFeeRadio")) { // recommended settings.setValue("nFeeRadio", 0); } // compatibility if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) { // total at least settings.setValue("nCustomFeeRadio", 1); } if (!settings.contains("nCustomFeeRadio")) { // per kilobyte settings.setValue("nCustomFeeRadio", 0); } if (!settings.contains("nSmartFeeSliderPosition")) settings.setValue("nSmartFeeSliderPosition", 0); if (!settings.contains("nTransactionFee")) settings.setValue("nTransactionFee", qint64(DEFAULT_TRANSACTION_FEE / SATOSHI)); if (!settings.contains("fPayOnlyMinFee")) settings.setValue("fPayOnlyMinFee", false); ui->groupFee->setId(ui->radioSmartFee, 0); ui->groupFee->setId(ui->radioCustomFee, 1); ui->groupFee ->button( std::max(0, std::min(1, settings.value("nFeeRadio").toInt()))) ->setChecked(true); ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0); ui->groupCustomFee->setId(ui->radioCustomAtLeast, 1); ui->groupCustomFee ->button(std::max( 0, std::min(1, settings.value("nCustomFeeRadio").toInt()))) ->setChecked(true); ui->customFee->setValue( int64_t(settings.value("nTransactionFee").toLongLong()) * SATOSHI); ui->checkBoxMinimumFee->setChecked( settings.value("fPayOnlyMinFee").toBool()); minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool()); } void SendCoinsDialog::setClientModel(ClientModel *_clientModel) { this->clientModel = _clientModel; if (_clientModel) { connect(_clientModel, SIGNAL(numBlocksChanged(int, QDateTime, double, bool)), this, SLOT(updateSmartFeeLabel())); } } void SendCoinsDialog::setModel(WalletModel *_model) { this->model = _model; if (_model && _model->getOptionsModel()) { for (int i = 0; i < ui->entries->count(); ++i) { SendCoinsEntry *entry = qobject_cast( ui->entries->itemAt(i)->widget()); if (entry) { entry->setModel(_model); } } setBalance(_model->getBalance(), _model->getUnconfirmedBalance(), _model->getImmatureBalance(), _model->getWatchBalance(), _model->getWatchUnconfirmedBalance(), _model->getWatchImmatureBalance()); connect(_model, SIGNAL(balanceChanged(Amount, Amount, Amount, Amount, Amount, Amount)), this, SLOT(setBalance(Amount, Amount, Amount, Amount, Amount, Amount))); connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); updateDisplayUnit(); // Coin Control connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(_model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool))); ui->frameCoinControl->setVisible( _model->getOptionsModel()->getCoinControlFeatures()); coinControlUpdateLabels(); // fee section connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateSmartFeeLabel())); connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateGlobalFeeVariables())); connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables())); connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(updateGlobalFeeVariables())); connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000)); updateFeeSectionControls(); updateMinFeeLabel(); updateSmartFeeLabel(); updateGlobalFeeVariables(); // set the smartfee-sliders default value (wallets default conf.target // or last stored value) QSettings settings; if (settings.value("nSmartFeeSliderPosition").toInt() == 0) ui->sliderSmartFee->setValue(ui->sliderSmartFee->maximum() - model->getDefaultConfirmTarget() + 2); else ui->sliderSmartFee->setValue( settings.value("nSmartFeeSliderPosition").toInt()); } } SendCoinsDialog::~SendCoinsDialog() { QSettings settings; settings.setValue("fFeeSectionMinimized", fFeeMinimized); settings.setValue("nFeeRadio", ui->groupFee->checkedId()); settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId()); settings.setValue("nSmartFeeSliderPosition", ui->sliderSmartFee->value()); settings.setValue("nTransactionFee", qint64(ui->customFee->value() / SATOSHI)); settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked()); delete ui; } void SendCoinsDialog::on_sendButton_clicked() { if (!model || !model->getOptionsModel()) return; QList recipients; bool valid = true; for (int i = 0; i < ui->entries->count(); ++i) { SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); if (entry) { if (entry->validate()) { recipients.append(entry->getValue()); } else { valid = false; } } } if (!valid || recipients.isEmpty()) { return; } fNewRecipientAllowed = false; WalletModel::UnlockContext ctx(model->requestUnlock()); if (!ctx.isValid()) { // Unlock wallet was cancelled fNewRecipientAllowed = true; return; } // prepare transaction for getting txFee earlier WalletModelTransaction currentTransaction(recipients); WalletModel::SendCoinsReturn prepareStatus; // Always use a CCoinControl instance, use the CoinControlDialog instance if // CoinControl has been enabled CCoinControl ctrl; if (model->getOptionsModel()->getCoinControlFeatures()) ctrl = *CoinControlDialog::coinControl; if (ui->radioSmartFee->isChecked()) ctrl.nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2; else ctrl.nConfirmTarget = 0; prepareStatus = model->prepareTransaction(currentTransaction, &ctrl); // process prepareStatus and on error generate message shown to user processSendCoinsReturn( prepareStatus, BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee())); if (prepareStatus.status != WalletModel::OK) { fNewRecipientAllowed = true; return; } Amount txFee = currentTransaction.getTransactionFee(); // Format confirmation message QStringList formatted; for (const SendCoinsRecipient &rcp : currentTransaction.getRecipients()) { // generate bold amount string QString amount = "" + BitcoinUnits::formatHtmlWithUnit( model->getOptionsModel()->getDisplayUnit(), rcp.amount); amount.append(""); // generate monospace address string QString address = "" + rcp.address; address.append(""); QString recipientElement; // normal payment if (!rcp.paymentRequest.IsInitialized()) { if (rcp.label.length() > 0) { // label with address recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label)); recipientElement.append(QString(" (%1)").arg(address)); } else { // just address recipientElement = tr("%1 to %2").arg(amount, address); } } else if (!rcp.authenticatedMerchant.isEmpty()) { // authenticated payment request recipientElement = tr("%1 to %2") .arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)); } else { // unauthenticated payment request recipientElement = tr("%1 to %2").arg(amount, address); } formatted.append(recipientElement); } QString questionString = tr("Are you sure you want to send?"); questionString.append("

%1"); if (txFee > Amount::zero()) { // append fee string if a fee is required questionString.append("
"); questionString.append(BitcoinUnits::formatHtmlWithUnit( model->getOptionsModel()->getDisplayUnit(), txFee)); questionString.append(" "); questionString.append(tr("added as transaction fee")); // append transaction size questionString.append( " (" + QString::number( (double)currentTransaction.getTransactionSize() / 1000) + " kB)"); } // add total amount in all subdivision units questionString.append("
"); Amount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee; QStringList alternativeUnits; for (BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) { if (u != model->getOptionsModel()->getDisplayUnit()) alternativeUnits.append( BitcoinUnits::formatHtmlWithUnit(u, totalAmount)); } questionString.append( tr("Total Amount %1") .arg(BitcoinUnits::formatHtmlWithUnit( model->getOptionsModel()->getDisplayUnit(), totalAmount))); questionString.append( QString("
(=%2)
") .arg(alternativeUnits.join(" " + tr("or") + "
"))); SendConfirmationDialog confirmationDialog( tr("Confirm send coins"), questionString.arg(formatted.join("
")), SEND_CONFIRM_DELAY, this); confirmationDialog.exec(); QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result(); if (retval != QMessageBox::Yes) { fNewRecipientAllowed = true; return; } // now send the prepared transaction WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction); // process sendStatus and on error generate message shown to user processSendCoinsReturn(sendStatus); if (sendStatus.status == WalletModel::OK) { accept(); CoinControlDialog::coinControl->UnSelectAll(); coinControlUpdateLabels(); } fNewRecipientAllowed = true; } void SendCoinsDialog::clear() { // Remove entries until only one left while (ui->entries->count()) { ui->entries->takeAt(0)->widget()->deleteLater(); } addEntry(); updateTabsAndLabels(); } void SendCoinsDialog::reject() { clear(); } void SendCoinsDialog::accept() { clear(); } SendCoinsEntry *SendCoinsDialog::addEntry() { SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this); entry->setModel(model); ui->entries->addWidget(entry); connect(entry, SIGNAL(removeEntry(SendCoinsEntry *)), this, SLOT(removeEntry(SendCoinsEntry *))); + connect(entry, SIGNAL(useAvailableBalance(SendCoinsEntry *)), this, + SLOT(useAvailableBalance(SendCoinsEntry *))); connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels())); connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this, SLOT(coinControlUpdateLabels())); // Focus the field, so that entry can start immediately entry->clear(); entry->setFocus(); ui->scrollAreaWidgetContents->resize( ui->scrollAreaWidgetContents->sizeHint()); qApp->processEvents(); QScrollBar *bar = ui->scrollArea->verticalScrollBar(); if (bar) bar->setSliderPosition(bar->maximum()); updateTabsAndLabels(); return entry; } void SendCoinsDialog::updateTabsAndLabels() { setupTabChain(0); coinControlUpdateLabels(); } void SendCoinsDialog::removeEntry(SendCoinsEntry *entry) { entry->hide(); // If the last entry is about to be removed add an empty one if (ui->entries->count() == 1) addEntry(); entry->deleteLater(); updateTabsAndLabels(); } QWidget *SendCoinsDialog::setupTabChain(QWidget *prev) { for (int i = 0; i < ui->entries->count(); ++i) { SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); if (entry) { prev = entry->setupTabChain(prev); } } QWidget::setTabOrder(prev, ui->sendButton); QWidget::setTabOrder(ui->sendButton, ui->clearButton); QWidget::setTabOrder(ui->clearButton, ui->addButton); return ui->addButton; } void SendCoinsDialog::setAddress(const QString &address) { SendCoinsEntry *entry = 0; // Replace the first entry if it is still unused if (ui->entries->count() == 1) { SendCoinsEntry *first = qobject_cast(ui->entries->itemAt(0)->widget()); if (first->isClear()) { entry = first; } } if (!entry) { entry = addEntry(); } entry->setAddress(address); } 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(ui->entries->itemAt(0)->widget()); if (first->isClear()) { entry = first; } } if (!entry) { entry = addEntry(); } entry->setValue(rv); updateTabsAndLabels(); } bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) { // Just paste the entry, all pre-checks are done in paymentserver.cpp. pasteEntry(rv); return true; } void SendCoinsDialog::setBalance(const Amount balance, const Amount unconfirmedBalance, const Amount immatureBalance, const Amount watchBalance, const Amount watchUnconfirmedBalance, const Amount watchImmatureBalance) { Q_UNUSED(unconfirmedBalance); Q_UNUSED(immatureBalance); Q_UNUSED(watchBalance); Q_UNUSED(watchUnconfirmedBalance); Q_UNUSED(watchImmatureBalance); if (model && model->getOptionsModel()) { ui->labelBalance->setText(BitcoinUnits::formatWithUnit( model->getOptionsModel()->getDisplayUnit(), balance)); } } void SendCoinsDialog::updateDisplayUnit() { setBalance(model->getBalance(), Amount::zero(), Amount::zero(), Amount::zero(), Amount::zero(), Amount::zero()); ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); updateMinFeeLabel(); updateSmartFeeLabel(); } void SendCoinsDialog::processSendCoinsReturn( const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg) { QPair msgParams; // Default to a warning message, override if error message is needed msgParams.second = CClientUIInterface::MSG_WARNING; // This comment is specific to SendCoinsDialog usage of // WalletModel::SendCoinsReturn. // WalletModel::TransactionCommitFailed is used only in // WalletModel::sendCoins() all others are used only in // WalletModel::prepareTransaction() switch (sendCoinsReturn.status) { case WalletModel::InvalidAddress: msgParams.first = tr("The recipient address is not valid. Please recheck."); break; case WalletModel::InvalidAmount: msgParams.first = tr("The amount to pay must be larger than 0."); break; case WalletModel::AmountExceedsBalance: msgParams.first = tr("The amount exceeds your balance."); break; case WalletModel::AmountWithFeeExceedsBalance: msgParams.first = tr("The total exceeds your balance when the %1 " "transaction fee is included.") .arg(msgArg); break; case WalletModel::DuplicateAddress: msgParams.first = tr("Duplicate address found: addresses should " "only be used once each."); break; case WalletModel::TransactionCreationFailed: msgParams.first = tr("Transaction creation failed!"); msgParams.second = CClientUIInterface::MSG_ERROR; break; case WalletModel::TransactionCommitFailed: msgParams.first = tr("The transaction was rejected with the following reason: %1") .arg(sendCoinsReturn.reasonCommitFailed); msgParams.second = CClientUIInterface::MSG_ERROR; break; case WalletModel::AbsurdFee: msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.") .arg(BitcoinUnits::formatWithUnit( model->getOptionsModel()->getDisplayUnit(), maxTxFee)); break; case WalletModel::PaymentRequestExpired: msgParams.first = tr("Payment request expired."); msgParams.second = CClientUIInterface::MSG_ERROR; break; // included to prevent a compiler warning. case WalletModel::OK: default: return; } Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second); } void SendCoinsDialog::minimizeFeeSection(bool fMinimize) { ui->labelFeeMinimized->setVisible(fMinimize); ui->buttonChooseFee->setVisible(fMinimize); ui->buttonMinimizeFee->setVisible(!fMinimize); ui->frameFeeSelection->setVisible(!fMinimize); ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0); fFeeMinimized = fMinimize; } void SendCoinsDialog::on_buttonChooseFee_clicked() { minimizeFeeSection(false); } void SendCoinsDialog::on_buttonMinimizeFee_clicked() { updateFeeMinimizedLabel(); minimizeFeeSection(true); } +void SendCoinsDialog::useAvailableBalance(SendCoinsEntry *entry) { + // Get CCoinControl instance if CoinControl is enabled or create a new one. + CCoinControl coin_control; + if (model->getOptionsModel()->getCoinControlFeatures()) { + coin_control = *CoinControlDialog::coinControl; + } + + // Calculate available amount to send. + Amount amount = model->getBalance(&coin_control); + for (int i = 0; i < ui->entries->count(); ++i) { + SendCoinsEntry *e = + qobject_cast(ui->entries->itemAt(i)->widget()); + if (e && !e->isHidden() && e != entry) { + amount -= e->getValue().amount; + } + } + + if (amount > Amount::zero()) { + entry->checkSubtractFeeFromAmount(); + entry->setAmount(amount); + } else { + entry->setAmount(Amount::zero()); + } +} + void SendCoinsDialog::setMinimumFee() { ui->radioCustomPerKilobyte->setChecked(true); ui->customFee->setValue(CWallet::GetRequiredFee(1000)); } void SendCoinsDialog::updateFeeSectionControls() { ui->sliderSmartFee->setEnabled(ui->radioSmartFee->isChecked()); ui->labelSmartFee->setEnabled(ui->radioSmartFee->isChecked()); ui->labelSmartFee2->setEnabled(ui->radioSmartFee->isChecked()); ui->labelSmartFee3->setEnabled(ui->radioSmartFee->isChecked()); ui->labelFeeEstimation->setEnabled(ui->radioSmartFee->isChecked()); ui->labelSmartFeeNormal->setEnabled(ui->radioSmartFee->isChecked()); ui->labelSmartFeeFast->setEnabled(ui->radioSmartFee->isChecked()); ui->confirmationTargetLabel->setEnabled(ui->radioSmartFee->isChecked()); ui->checkBoxMinimumFee->setEnabled(ui->radioCustomFee->isChecked()); ui->labelMinFeeWarning->setEnabled(ui->radioCustomFee->isChecked()); ui->radioCustomPerKilobyte->setEnabled( ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); ui->radioCustomAtLeast->setEnabled( ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked() && CoinControlDialog::coinControl->HasSelected()); ui->customFee->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); } void SendCoinsDialog::updateGlobalFeeVariables() { if (ui->radioSmartFee->isChecked()) { int nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2; payTxFee = CFeeRate(Amount::zero()); // set nMinimumTotalFee to 0 to not accidentally pay a custom fee CoinControlDialog::coinControl->nMinimumTotalFee = Amount::zero(); // show the estimated required time for confirmation ui->confirmationTargetLabel->setText( GUIUtil::formatDurationStr( nConfirmTarget * model->getChainParams().GetConsensus().nPowTargetSpacing) + " / " + tr("%n block(s)", "", nConfirmTarget)); } else { payTxFee = CFeeRate(Amount(ui->customFee->value())); // if user has selected to set a minimum absolute fee, pass the value to // coincontrol // set nMinimumTotalFee to 0 in case of user has selected that the fee // is per KB CoinControlDialog::coinControl->nMinimumTotalFee = ui->radioCustomAtLeast->isChecked() ? ui->customFee->value() : Amount::zero(); } } void SendCoinsDialog::updateFeeMinimizedLabel() { if (!model || !model->getOptionsModel()) return; if (ui->radioSmartFee->isChecked()) ui->labelFeeMinimized->setText(ui->labelSmartFee->text()); else { ui->labelFeeMinimized->setText( BitcoinUnits::formatWithUnit( model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : "")); } } void SendCoinsDialog::updateMinFeeLabel() { if (model && model->getOptionsModel()) ui->checkBoxMinimumFee->setText( tr("Pay only the required fee of %1") .arg(BitcoinUnits::formatWithUnit( model->getOptionsModel()->getDisplayUnit(), CWallet::GetRequiredFee(1000)) + "/kB")); } void SendCoinsDialog::updateSmartFeeLabel() { if (!model || !model->getOptionsModel()) return; int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2; int estimateFoundAtBlocks = nBlocksToConfirm; CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks); // not enough data => minfee if (feeRate <= CFeeRate(Amount::zero())) { ui->labelSmartFee->setText( BitcoinUnits::formatWithUnit( model->getOptionsModel()->getDisplayUnit(), std::max(CWallet::fallbackFee.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB"); // (Smart fee not initialized yet. This usually takes a few blocks...) ui->labelSmartFee2->show(); ui->labelFeeEstimation->setText(""); } else { ui->labelSmartFee->setText( BitcoinUnits::formatWithUnit( model->getOptionsModel()->getDisplayUnit(), std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB"); ui->labelSmartFee2->hide(); ui->labelFeeEstimation->setText( tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks)); } updateFeeMinimizedLabel(); } // Coin Control: copy label "Quantity" to clipboard void SendCoinsDialog::coinControlClipboardQuantity() { GUIUtil::setClipboard(ui->labelCoinControlQuantity->text()); } // Coin Control: copy label "Amount" to clipboard void SendCoinsDialog::coinControlClipboardAmount() { GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left( ui->labelCoinControlAmount->text().indexOf(" "))); } // Coin Control: copy label "Fee" to clipboard void SendCoinsDialog::coinControlClipboardFee() { GUIUtil::setClipboard( ui->labelCoinControlFee->text() .left(ui->labelCoinControlFee->text().indexOf(" ")) .replace(ASYMP_UTF8, "")); } // Coin Control: copy label "After fee" to clipboard void SendCoinsDialog::coinControlClipboardAfterFee() { GUIUtil::setClipboard( ui->labelCoinControlAfterFee->text() .left(ui->labelCoinControlAfterFee->text().indexOf(" ")) .replace(ASYMP_UTF8, "")); } // Coin Control: copy label "Bytes" to clipboard void SendCoinsDialog::coinControlClipboardBytes() { GUIUtil::setClipboard( ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, "")); } // Coin Control: copy label "Dust" to clipboard void SendCoinsDialog::coinControlClipboardLowOutput() { GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text()); } // Coin Control: copy label "Change" to clipboard void SendCoinsDialog::coinControlClipboardChange() { GUIUtil::setClipboard( ui->labelCoinControlChange->text() .left(ui->labelCoinControlChange->text().indexOf(" ")) .replace(ASYMP_UTF8, "")); } // Coin Control: settings menu - coin control enabled/disabled by user void SendCoinsDialog::coinControlFeatureChanged(bool checked) { ui->frameCoinControl->setVisible(checked); // coin control features disabled if (!checked && model) CoinControlDialog::coinControl->SetNull(); // make sure we set back the confirmation target updateGlobalFeeVariables(); coinControlUpdateLabels(); } // Coin Control: button inputs -> show actual coin control dialog void SendCoinsDialog::coinControlButtonClicked() { CoinControlDialog dlg(platformStyle); dlg.setModel(model); dlg.exec(); coinControlUpdateLabels(); } // Coin Control: checkbox custom change address void SendCoinsDialog::coinControlChangeChecked(int state) { if (state == Qt::Unchecked) { CoinControlDialog::coinControl->destChange = CNoDestination(); ui->labelCoinControlChangeLabel->clear(); } else { // use this to re-validate an already entered address coinControlChangeEdited(ui->lineEditCoinControlChange->text()); } ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked)); } // Coin Control: custom change address changed void SendCoinsDialog::coinControlChangeEdited(const QString &text) { if (model && model->getAddressTableModel()) { // Default to no change address until verified CoinControlDialog::coinControl->destChange = CNoDestination(); ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); const CTxDestination dest = DecodeDestination(text.toStdString(), model->getChainParams()); if (text.isEmpty()) { // Nothing entered ui->labelCoinControlChangeLabel->setText(""); } else if (!IsValidDestination(dest)) { // Invalid address ui->labelCoinControlChangeLabel->setText( tr("Warning: Invalid Bitcoin address")); } else { // Valid address if (!model->IsSpendable(dest)) { ui->labelCoinControlChangeLabel->setText( tr("Warning: Unknown change address")); // confirmation dialog QMessageBox::StandardButton btnRetVal = QMessageBox::question( this, tr("Confirm custom change address"), tr("The address you selected for change is not part of " "this wallet. Any or all funds in your wallet may be " "sent to this address. Are you sure?"), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if (btnRetVal == QMessageBox::Yes) CoinControlDialog::coinControl->destChange = dest; else { ui->lineEditCoinControlChange->setText(""); ui->labelCoinControlChangeLabel->setStyleSheet( "QLabel{color:black;}"); ui->labelCoinControlChangeLabel->setText(""); } } else { // Known change address ui->labelCoinControlChangeLabel->setStyleSheet( "QLabel{color:black;}"); // Query label QString associatedLabel = model->getAddressTableModel()->labelForAddress(text); if (!associatedLabel.isEmpty()) ui->labelCoinControlChangeLabel->setText(associatedLabel); else ui->labelCoinControlChangeLabel->setText(tr("(no label)")); CoinControlDialog::coinControl->destChange = dest; } } } } // Coin Control: update labels void SendCoinsDialog::coinControlUpdateLabels() { if (!model || !model->getOptionsModel()) return; if (model->getOptionsModel()->getCoinControlFeatures()) { // enable minimum absolute fee UI controls ui->radioCustomAtLeast->setVisible(true); // only enable the feature if inputs are selected ui->radioCustomAtLeast->setEnabled( ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked() && CoinControlDialog::coinControl->HasSelected()); } else { // in case coin control is disabled (=default), hide minimum absolute // fee UI controls ui->radioCustomAtLeast->setVisible(false); return; } // set pay amounts CoinControlDialog::payAmounts.clear(); CoinControlDialog::fSubtractFeeFromAmount = false; for (int i = 0; i < ui->entries->count(); ++i) { SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); if (entry && !entry->isHidden()) { SendCoinsRecipient rcp = entry->getValue(); CoinControlDialog::payAmounts.append(rcp.amount); if (rcp.fSubtractFeeFromAmount) CoinControlDialog::fSubtractFeeFromAmount = true; } } 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(); } } SendConfirmationDialog::SendConfirmationDialog(const QString &title, const QString &text, int _secDelay, QWidget *parent) : QMessageBox(QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::Cancel, parent), secDelay(_secDelay) { setDefaultButton(QMessageBox::Cancel); yesButton = button(QMessageBox::Yes); updateYesButton(); connect(&countDownTimer, SIGNAL(timeout()), this, SLOT(countDown())); } int SendConfirmationDialog::exec() { updateYesButton(); countDownTimer.start(1000); return QMessageBox::exec(); } void SendConfirmationDialog::countDown() { secDelay--; updateYesButton(); if (secDelay <= 0) { countDownTimer.stop(); } } void SendConfirmationDialog::updateYesButton() { if (secDelay > 0) { yesButton->setEnabled(false); yesButton->setText(tr("Yes") + " (" + QString::number(secDelay) + ")"); } else { yesButton->setEnabled(true); yesButton->setText(tr("Yes")); } } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index edbfbeda2..866d8eb0b 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -1,128 +1,129 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_SENDCOINSDIALOG_H #define BITCOIN_QT_SENDCOINSDIALOG_H #include "walletmodel.h" #include #include #include #include class ClientModel; class OptionsModel; class PlatformStyle; class SendCoinsEntry; class SendCoinsRecipient; namespace Ui { class SendCoinsDialog; } QT_BEGIN_NAMESPACE class QUrl; QT_END_NAMESPACE /** Dialog for sending bitcoins */ class SendCoinsDialog : public QDialog { Q_OBJECT public: explicit SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent = 0); ~SendCoinsDialog(); void setClientModel(ClientModel *clientModel); void setModel(WalletModel *model); /** * Set up the tab chain manually, as Qt messes up the tab chain by default * in some cases (issue * https://bugreports.qt-project.org/browse/QTBUG-10907). */ QWidget *setupTabChain(QWidget *prev); void setAddress(const QString &address); void pasteEntry(const SendCoinsRecipient &rv); bool handlePaymentRequest(const SendCoinsRecipient &recipient); public Q_SLOTS: void clear(); void reject() override; void accept() override; SendCoinsEntry *addEntry(); void updateTabsAndLabels(); void setBalance(const Amount balance, const Amount unconfirmedBalance, const Amount immatureBalance, const Amount watchOnlyBalance, const Amount watchUnconfBalance, const Amount watchImmatureBalance); private: Ui::SendCoinsDialog *ui; ClientModel *clientModel; WalletModel *model; bool fNewRecipientAllowed; bool fFeeMinimized; const PlatformStyle *platformStyle; // Process WalletModel::SendCoinsReturn and generate a pair consisting of a // message and message flags for use in Q_EMIT message(). // Additional parameter msgArg can be used via .arg(msgArg). void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg = QString()); void minimizeFeeSection(bool fMinimize); void updateFeeMinimizedLabel(); private Q_SLOTS: void on_sendButton_clicked(); void on_buttonChooseFee_clicked(); void on_buttonMinimizeFee_clicked(); void removeEntry(SendCoinsEntry *entry); + void useAvailableBalance(SendCoinsEntry *entry); void updateDisplayUnit(); void coinControlFeatureChanged(bool); void coinControlButtonClicked(); void coinControlChangeChecked(int); void coinControlChangeEdited(const QString &); void coinControlUpdateLabels(); void coinControlClipboardQuantity(); void coinControlClipboardAmount(); void coinControlClipboardFee(); void coinControlClipboardAfterFee(); void coinControlClipboardBytes(); void coinControlClipboardLowOutput(); void coinControlClipboardChange(); void setMinimumFee(); void updateFeeSectionControls(); void updateMinFeeLabel(); void updateSmartFeeLabel(); void updateGlobalFeeVariables(); Q_SIGNALS: // Fired when a message should be reported to the user void message(const QString &title, const QString &message, unsigned int style); }; class SendConfirmationDialog : public QMessageBox { Q_OBJECT public: SendConfirmationDialog(const QString &title, const QString &text, int secDelay = 0, QWidget *parent = 0); int exec(); private Q_SLOTS: void countDown(); void updateYesButton(); private: QAbstractButton *yesButton; QTimer countDownTimer; int secDelay; }; #endif // BITCOIN_QT_SENDCOINSDIALOG_H diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 9bf0b124e..b67f691ec 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -1,262 +1,280 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "sendcoinsentry.h" #include "ui_sendcoinsentry.h" #include "addressbookpage.h" #include "addresstablemodel.h" #include "config.h" #include "guiutil.h" #include "optionsmodel.h" #include "platformstyle.h" #include "walletmodel.h" #include #include SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *parent) : QStackedWidget(parent), ui(new Ui::SendCoinsEntry), model(0), platformStyle(_platformStyle) { ui->setupUi(this); ui->addressBookButton->setIcon( platformStyle->SingleColorIcon(":/icons/address-book")); ui->pasteButton->setIcon( platformStyle->SingleColorIcon(":/icons/editpaste")); ui->deleteButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); ui->deleteButton_is->setIcon( platformStyle->SingleColorIcon(":/icons/remove")); ui->deleteButton_s->setIcon( platformStyle->SingleColorIcon(":/icons/remove")); ui->messageTextLabel->setToolTip( tr("A message that was attached to the %1 URI which will be" " stored with the transaction for your reference. Note: " "This message will not be sent over the Bitcoin network.") .arg(GUIUtil::bitcoinURIScheme(GetConfig()))); setCurrentWidget(ui->SendCoins); if (platformStyle->getUseExtraSpacing()) ui->payToLayout->setSpacing(4); #if QT_VERSION >= 0x040700 ui->addAsLabel->setPlaceholderText( tr("Enter a label for this address to add it to your address book")); #endif // normal bitcoin address field GUIUtil::setupAddressWidget(ui->payTo, this); // just a label for displaying bitcoin address(es) ui->payTo_is->setFont(GUIUtil::fixedPitchFont()); // Connect signals connect(ui->payAmount, SIGNAL(valueChanged()), this, SIGNAL(payAmountChanged())); connect(ui->checkboxSubtractFeeFromAmount, SIGNAL(toggled(bool)), this, SIGNAL(subtractFeeFromAmountChanged())); connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked())); connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked())); connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked())); + connect(ui->useAvailableBalanceButton, SIGNAL(clicked()), this, + SLOT(useAvailableBalanceClicked())); } SendCoinsEntry::~SendCoinsEntry() { delete ui; } void SendCoinsEntry::on_pasteButton_clicked() { // Paste text from clipboard into recipient field ui->payTo->setText(QApplication::clipboard()->text()); } void SendCoinsEntry::on_addressBookButton_clicked() { if (!model) return; AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::SendingTab, this); dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) { ui->payTo->setText(dlg.getReturnValue()); ui->payAmount->setFocus(); } } void SendCoinsEntry::on_payTo_textChanged(const QString &address) { updateLabel(address); } void SendCoinsEntry::setModel(WalletModel *_model) { this->model = _model; if (_model && _model->getOptionsModel()) connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); clear(); } void SendCoinsEntry::clear() { // clear UI elements for normal payment ui->payTo->clear(); ui->addAsLabel->clear(); ui->payAmount->clear(); ui->checkboxSubtractFeeFromAmount->setCheckState(Qt::Unchecked); ui->messageTextLabel->clear(); ui->messageTextLabel->hide(); ui->messageLabel->hide(); // clear UI elements for unauthenticated payment request ui->payTo_is->clear(); ui->memoTextLabel_is->clear(); ui->payAmount_is->clear(); // clear UI elements for authenticated payment request ui->payTo_s->clear(); ui->memoTextLabel_s->clear(); ui->payAmount_s->clear(); // update the display unit, to not use the default ("BCH") updateDisplayUnit(); } +void SendCoinsEntry::checkSubtractFeeFromAmount() { + ui->checkboxSubtractFeeFromAmount->setChecked(true); +} + void SendCoinsEntry::deleteClicked() { Q_EMIT removeEntry(this); } +void SendCoinsEntry::useAvailableBalanceClicked() { + Q_EMIT useAvailableBalance(this); +} + bool SendCoinsEntry::validate() { - if (!model) return false; + if (!model) { + return false; + } // Check input validity bool retval = true; // Skip checks for payment request - if (recipient.paymentRequest.IsInitialized()) return retval; + if (recipient.paymentRequest.IsInitialized()) { + return retval; + } if (!model->validateAddress(ui->payTo->text())) { ui->payTo->setValid(false); retval = false; } if (!ui->payAmount->validate()) { retval = false; } // Sending a zero amount is invalid if (ui->payAmount->value(0) <= Amount::zero()) { ui->payAmount->setValid(false); retval = false; } // Reject dust outputs: if (retval && GUIUtil::isDust(ui->payTo->text(), ui->payAmount->value(), model->getChainParams())) { ui->payAmount->setValid(false); retval = false; } return retval; } SendCoinsRecipient SendCoinsEntry::getValue() { // Payment request if (recipient.paymentRequest.IsInitialized()) return recipient; // Normal payment recipient.address = ui->payTo->text(); recipient.label = ui->addAsLabel->text(); recipient.amount = ui->payAmount->value(); recipient.message = ui->messageTextLabel->text(); recipient.fSubtractFeeFromAmount = (ui->checkboxSubtractFeeFromAmount->checkState() == Qt::Checked); return recipient; } QWidget *SendCoinsEntry::setupTabChain(QWidget *prev) { QWidget::setTabOrder(prev, ui->payTo); QWidget::setTabOrder(ui->payTo, ui->addAsLabel); QWidget *w = ui->payAmount->setupTabChain(ui->addAsLabel); QWidget::setTabOrder(w, ui->checkboxSubtractFeeFromAmount); QWidget::setTabOrder(ui->checkboxSubtractFeeFromAmount, ui->addressBookButton); QWidget::setTabOrder(ui->addressBookButton, ui->pasteButton); QWidget::setTabOrder(ui->pasteButton, ui->deleteButton); return ui->deleteButton; } void SendCoinsEntry::setValue(const SendCoinsRecipient &value) { recipient = value; if (recipient.paymentRequest.IsInitialized()) // payment request { if (recipient.authenticatedMerchant.isEmpty()) // unauthenticated { ui->payTo_is->setText(recipient.address); ui->memoTextLabel_is->setText(recipient.message); ui->payAmount_is->setValue(recipient.amount); ui->payAmount_is->setReadOnly(true); setCurrentWidget(ui->SendCoins_UnauthenticatedPaymentRequest); } else // authenticated { ui->payTo_s->setText(recipient.authenticatedMerchant); ui->memoTextLabel_s->setText(recipient.message); ui->payAmount_s->setValue(recipient.amount); ui->payAmount_s->setReadOnly(true); setCurrentWidget(ui->SendCoins_AuthenticatedPaymentRequest); } } else // normal payment { // message ui->messageTextLabel->setText(recipient.message); ui->messageTextLabel->setVisible(!recipient.message.isEmpty()); ui->messageLabel->setVisible(!recipient.message.isEmpty()); ui->addAsLabel->clear(); ui->payTo->setText( recipient.address); // this may set a label from addressbook if (!recipient.label.isEmpty()) // if a label had been set from the // addressbook, don't overwrite with an // empty label ui->addAsLabel->setText(recipient.label); ui->payAmount->setValue(recipient.amount); } } void SendCoinsEntry::setAddress(const QString &address) { ui->payTo->setText(address); ui->payAmount->setFocus(); } +void SendCoinsEntry::setAmount(const Amount amount) { + ui->payAmount->setValue(amount); +} + bool SendCoinsEntry::isClear() { return ui->payTo->text().isEmpty() && ui->payTo_is->text().isEmpty() && ui->payTo_s->text().isEmpty(); } void SendCoinsEntry::setFocus() { ui->payTo->setFocus(); } void SendCoinsEntry::updateDisplayUnit() { if (model && model->getOptionsModel()) { // Update payAmount with the current unit ui->payAmount->setDisplayUnit( model->getOptionsModel()->getDisplayUnit()); ui->payAmount_is->setDisplayUnit( model->getOptionsModel()->getDisplayUnit()); ui->payAmount_s->setDisplayUnit( model->getOptionsModel()->getDisplayUnit()); } } bool SendCoinsEntry::updateLabel(const QString &address) { if (!model) return false; // Fill in label from address book, if address has an associated label QString associatedLabel = model->getAddressTableModel()->labelForAddress(address); if (!associatedLabel.isEmpty()) { ui->addAsLabel->setText(associatedLabel); return true; } return false; } diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index f7a2cf602..d9c8017fc 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -1,74 +1,78 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_SENDCOINSENTRY_H #define BITCOIN_QT_SENDCOINSENTRY_H #include "walletmodel.h" #include class WalletModel; class PlatformStyle; namespace Ui { class SendCoinsEntry; } /** * A single entry in the dialog for sending bitcoins. * Stacked widget, with different UIs for payment requests * with a strong payee identity. */ class SendCoinsEntry : public QStackedWidget { Q_OBJECT public: explicit SendCoinsEntry(const PlatformStyle *platformStyle, QWidget *parent = 0); ~SendCoinsEntry(); void setModel(WalletModel *model); bool validate(); SendCoinsRecipient getValue(); /** Return whether the entry is still empty and unedited */ bool isClear(); void setValue(const SendCoinsRecipient &value); void setAddress(const QString &address); + void setAmount(const Amount amount); /** Set up the tab chain manually, as Qt messes up the tab chain by default * in some cases * (issue https://bugreports.qt-project.org/browse/QTBUG-10907). */ QWidget *setupTabChain(QWidget *prev); void setFocus(); public Q_SLOTS: void clear(); + void checkSubtractFeeFromAmount(); Q_SIGNALS: void removeEntry(SendCoinsEntry *entry); + void useAvailableBalance(SendCoinsEntry *entry); void payAmountChanged(); void subtractFeeFromAmountChanged(); private Q_SLOTS: void deleteClicked(); + void useAvailableBalanceClicked(); void on_payTo_textChanged(const QString &address); void on_addressBookButton_clicked(); void on_pasteButton_clicked(); void updateDisplayUnit(); private: SendCoinsRecipient recipient; Ui::SendCoinsEntry *ui; WalletModel *model; const PlatformStyle *platformStyle; bool updateLabel(const QString &address); }; #endif // BITCOIN_QT_SENDCOINSENTRY_H