2020-04-16 13:14:08 -04:00
// Copyright (c) 2011-2020 The Bitcoin Core developers
2014-12-13 01:09:33 -03:00
// Distributed under the MIT software license, see the accompanying
2013-11-04 12:20:43 -03:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-11-06 15:12:47 -03:00
# if defined(HAVE_CONFIG_H)
# include <config/bitcoin-config.h>
# endif
2017-11-09 21:57:53 -03:00
# include <qt/sendcoinsdialog.h>
2017-08-15 12:31:26 -03:00
# include <qt/forms/ui_sendcoinsdialog.h>
2013-01-23 17:51:02 -03:00
2017-11-09 21:57:53 -03:00
# include <qt/addresstablemodel.h>
# include <qt/bitcoinunits.h>
# include <qt/clientmodel.h>
# include <qt/coincontroldialog.h>
# include <qt/guiutil.h>
# include <qt/optionsmodel.h>
# include <qt/platformstyle.h>
# include <qt/sendcoinsentry.h>
2013-04-13 02:13:08 -03:00
2017-11-09 21:57:53 -03:00
# include <chainparams.h>
2018-04-07 04:42:02 -03:00
# include <interfaces/node.h>
2017-09-19 22:12:25 -03:00
# include <key_io.h>
2020-06-19 18:14:17 -04:00
# include <node/ui_interface.h>
2017-11-09 21:57:53 -03:00
# include <policy/fees.h>
2019-09-26 06:31:49 -03:00
# include <txmempool.h>
# include <wallet/coincontrol.h>
2017-11-09 21:57:53 -03:00
# include <wallet/fees.h>
2019-11-19 14:39:50 -03:00
# include <wallet/wallet.h>
2011-05-12 11:55:24 -04:00
2020-09-18 06:00:50 -03:00
# include <validation.h>
2017-02-17 05:41:57 -03:00
# include <QFontMetrics>
2011-12-07 02:00:04 -03:00
# include <QScrollBar>
2014-11-01 20:14:47 -03:00
# include <QSettings>
2013-04-13 02:13:08 -03:00
# include <QTextDocument>
2016-05-09 23:26:57 -03:00
2020-12-04 04:24:51 -03:00
static constexpr std : : array confTargets { 2 , 4 , 6 , 12 , 24 , 48 , 144 , 504 , 1008 } ;
2017-07-07 16:03:49 -04:00
int getConfTargetForIndex ( int index ) {
if ( index + 1 > static_cast < int > ( confTargets . size ( ) ) ) {
return confTargets . back ( ) ;
}
if ( index < 0 ) {
return confTargets [ 0 ] ;
}
return confTargets [ index ] ;
}
int getIndexForConfTarget ( int target ) {
for ( unsigned int i = 0 ; i < confTargets . size ( ) ; i + + ) {
if ( confTargets [ i ] > = target ) {
return i ;
}
}
return confTargets . size ( ) - 1 ;
}
2016-09-09 08:43:29 -03:00
SendCoinsDialog : : SendCoinsDialog ( const PlatformStyle * _platformStyle , QWidget * parent ) :
2011-05-12 08:44:52 -04:00
QDialog ( parent ) ,
2011-05-30 14:20:12 -04:00
ui ( new Ui : : SendCoinsDialog ) ,
2018-07-30 06:37:09 -04:00
clientModel ( nullptr ) ,
model ( nullptr ) ,
2020-05-05 18:56:21 -04:00
m_coin_control ( new CCoinControl ) ,
2014-11-20 09:06:10 -03:00
fNewRecipientAllowed ( true ) ,
2015-07-28 10:20:14 -03:00
fFeeMinimized ( true ) ,
2016-09-09 08:43:29 -03:00
platformStyle ( _platformStyle )
2011-05-12 08:44:52 -04:00
{
ui - > setupUi ( this ) ;
2011-07-08 13:25:35 -04:00
2016-09-09 08:43:29 -03:00
if ( ! _platformStyle - > getImagesOnButtons ( ) ) {
2015-07-28 10:20:14 -03:00
ui - > addButton - > setIcon ( QIcon ( ) ) ;
ui - > clearButton - > setIcon ( QIcon ( ) ) ;
ui - > sendButton - > setIcon ( QIcon ( ) ) ;
} else {
2016-09-09 08:43:29 -03:00
ui - > addButton - > setIcon ( _platformStyle - > SingleColorIcon ( " :/icons/add " ) ) ;
ui - > clearButton - > setIcon ( _platformStyle - > SingleColorIcon ( " :/icons/remove " ) ) ;
ui - > sendButton - > setIcon ( _platformStyle - > SingleColorIcon ( " :/icons/send " ) ) ;
2015-07-28 10:20:14 -03:00
}
2013-11-20 11:56:51 -03:00
GUIUtil : : setupAddressWidget ( ui - > lineEditCoinControlChange , this ) ;
2011-10-07 08:21:45 -03:00
2011-07-16 13:01:05 -04:00
addEntry ( ) ;
2011-05-15 13:31:20 -04:00
2018-06-24 11:18:22 -04:00
connect ( ui - > addButton , & QPushButton : : clicked , this , & SendCoinsDialog : : addEntry ) ;
connect ( ui - > clearButton , & QPushButton : : clicked , this , & SendCoinsDialog : : clear ) ;
2011-12-24 01:27:12 -03:00
2013-08-12 11:03:03 -04:00
// Coin Control
2018-06-24 11:18:22 -04:00
connect ( ui - > pushButtonCoinControl , & QPushButton : : clicked , this , & SendCoinsDialog : : coinControlButtonClicked ) ;
connect ( ui - > checkBoxCoinControlChange , & QCheckBox : : stateChanged , this , & SendCoinsDialog : : coinControlChangeChecked ) ;
connect ( ui - > lineEditCoinControlChange , & QValidatedLineEdit : : textEdited , this , & SendCoinsDialog : : coinControlChangeEdited ) ;
2013-08-12 11:03:03 -04:00
// 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 ) ;
2014-06-07 19:05:53 -04:00
QAction * clipboardLowOutputAction = new QAction ( tr ( " Copy dust " ) , this ) ;
2013-08-12 11:03:03 -04:00
QAction * clipboardChangeAction = new QAction ( tr ( " Copy change " ) , this ) ;
2018-06-24 11:18:22 -04:00
connect ( clipboardQuantityAction , & QAction : : triggered , this , & SendCoinsDialog : : coinControlClipboardQuantity ) ;
connect ( clipboardAmountAction , & QAction : : triggered , this , & SendCoinsDialog : : coinControlClipboardAmount ) ;
connect ( clipboardFeeAction , & QAction : : triggered , this , & SendCoinsDialog : : coinControlClipboardFee ) ;
connect ( clipboardAfterFeeAction , & QAction : : triggered , this , & SendCoinsDialog : : coinControlClipboardAfterFee ) ;
connect ( clipboardBytesAction , & QAction : : triggered , this , & SendCoinsDialog : : coinControlClipboardBytes ) ;
connect ( clipboardLowOutputAction , & QAction : : triggered , this , & SendCoinsDialog : : coinControlClipboardLowOutput ) ;
connect ( clipboardChangeAction , & QAction : : triggered , this , & SendCoinsDialog : : coinControlClipboardChange ) ;
2013-08-12 11:03:03 -04:00
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 ) ;
2014-11-01 20:14:47 -03:00
// init transaction fee section
QSettings settings ;
if ( ! settings . contains ( " fFeeSectionMinimized " ) )
settings . setValue ( " fFeeSectionMinimized " , true ) ;
if ( ! settings . contains ( " nFeeRadio " ) & & settings . contains ( " nTransactionFee " ) & & settings . value ( " nTransactionFee " ) . toLongLong ( ) > 0 ) // compatibility
settings . setValue ( " nFeeRadio " , 1 ) ; // custom
if ( ! settings . contains ( " nFeeRadio " ) )
settings . setValue ( " nFeeRadio " , 0 ) ; // recommended
if ( ! settings . contains ( " nSmartFeeSliderPosition " ) )
settings . setValue ( " nSmartFeeSliderPosition " , 0 ) ;
if ( ! settings . contains ( " nTransactionFee " ) )
2018-04-07 13:12:46 -03:00
settings . setValue ( " nTransactionFee " , ( qint64 ) DEFAULT_PAY_TX_FEE ) ;
2014-11-01 20:14:47 -03:00
ui - > groupFee - > setId ( ui - > radioSmartFee , 0 ) ;
ui - > groupFee - > setId ( ui - > radioCustomFee , 1 ) ;
ui - > groupFee - > button ( ( int ) std : : max ( 0 , std : : min ( 1 , settings . value ( " nFeeRadio " ) . toInt ( ) ) ) ) - > setChecked ( true ) ;
2018-10-30 10:09:10 -03:00
ui - > customFee - > SetAllowEmpty ( false ) ;
2014-11-01 20:14:47 -03:00
ui - > customFee - > setValue ( settings . value ( " nTransactionFee " ) . toLongLong ( ) ) ;
minimizeFeeSection ( settings . value ( " fFeeSectionMinimized " ) . toBool ( ) ) ;
2011-05-12 08:44:52 -04:00
}
2016-09-09 08:43:29 -03:00
void SendCoinsDialog : : setClientModel ( ClientModel * _clientModel )
2014-11-01 20:14:47 -03:00
{
2016-09-09 08:43:29 -03:00
this - > clientModel = _clientModel ;
2014-11-20 09:06:10 -03:00
2016-09-09 08:43:29 -03:00
if ( _clientModel ) {
2020-09-18 06:00:50 -03:00
connect ( _clientModel , & ClientModel : : numBlocksChanged , this , & SendCoinsDialog : : updateNumberOfBlocks ) ;
2014-11-20 09:06:10 -03:00
}
2014-11-01 20:14:47 -03:00
}
2016-09-09 08:43:29 -03:00
void SendCoinsDialog : : setModel ( WalletModel * _model )
2011-05-30 14:20:12 -04:00
{
2016-09-09 08:43:29 -03:00
this - > model = _model ;
2011-07-16 13:01:05 -04:00
2016-09-09 08:43:29 -03:00
if ( _model & & _model - > getOptionsModel ( ) )
2011-07-16 13:01:05 -04:00
{
2013-08-23 07:07:20 -04:00
for ( int i = 0 ; i < ui - > entries - > count ( ) ; + + i )
2011-07-16 13:01:05 -04:00
{
2013-08-23 07:07:20 -04:00
SendCoinsEntry * entry = qobject_cast < SendCoinsEntry * > ( ui - > entries - > itemAt ( i ) - > widget ( ) ) ;
if ( entry )
{
2016-09-09 08:43:29 -03:00
entry - > setModel ( _model ) ;
2013-08-23 07:07:20 -04:00
}
2011-07-16 13:01:05 -04:00
}
2013-08-23 07:07:20 -04:00
2018-04-07 04:42:02 -03:00
interfaces : : WalletBalances balances = _model - > wallet ( ) . getBalances ( ) ;
2018-03-31 07:41:33 -03:00
setBalance ( balances ) ;
2018-06-24 11:18:22 -04:00
connect ( _model , & WalletModel : : balanceChanged , this , & SendCoinsDialog : : setBalance ) ;
connect ( _model - > getOptionsModel ( ) , & OptionsModel : : displayUnitChanged , this , & SendCoinsDialog : : updateDisplayUnit ) ;
2014-11-01 20:14:47 -03:00
updateDisplayUnit ( ) ;
2013-08-12 11:03:03 -04:00
// Coin Control
2018-06-24 11:18:22 -04:00
connect ( _model - > getOptionsModel ( ) , & OptionsModel : : displayUnitChanged , this , & SendCoinsDialog : : coinControlUpdateLabels ) ;
connect ( _model - > getOptionsModel ( ) , & OptionsModel : : coinControlFeaturesChanged , this , & SendCoinsDialog : : coinControlFeatureChanged ) ;
2016-09-09 08:43:29 -03:00
ui - > frameCoinControl - > setVisible ( _model - > getOptionsModel ( ) - > getCoinControlFeatures ( ) ) ;
2013-08-12 11:03:03 -04:00
coinControlUpdateLabels ( ) ;
2014-11-01 20:14:47 -03:00
// fee section
2017-07-15 20:07:52 -04:00
for ( const int n : confTargets ) {
2017-07-07 16:03:49 -04:00
ui - > confTargetSelector - > addItem ( tr ( " %1 (%2 blocks) " ) . arg ( GUIUtil : : formatNiceTimeOffset ( n * Params ( ) . GetConsensus ( ) . nPowTargetSpacing ) ) . arg ( n ) ) ;
}
2018-06-24 11:18:22 -04:00
connect ( ui - > confTargetSelector , static_cast < void ( QComboBox : : * ) ( int ) > ( & QComboBox : : currentIndexChanged ) , this , & SendCoinsDialog : : updateSmartFeeLabel ) ;
connect ( ui - > confTargetSelector , static_cast < void ( QComboBox : : * ) ( int ) > ( & QComboBox : : currentIndexChanged ) , this , & SendCoinsDialog : : coinControlUpdateLabels ) ;
2020-08-09 13:10:07 -04:00
# if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
connect ( ui - > groupFee , & QButtonGroup : : idClicked , this , & SendCoinsDialog : : updateFeeSectionControls ) ;
connect ( ui - > groupFee , & QButtonGroup : : idClicked , this , & SendCoinsDialog : : coinControlUpdateLabels ) ;
# else
2018-06-24 11:18:22 -04:00
connect ( ui - > groupFee , static_cast < void ( QButtonGroup : : * ) ( int ) > ( & QButtonGroup : : buttonClicked ) , this , & SendCoinsDialog : : updateFeeSectionControls ) ;
connect ( ui - > groupFee , static_cast < void ( QButtonGroup : : * ) ( int ) > ( & QButtonGroup : : buttonClicked ) , this , & SendCoinsDialog : : coinControlUpdateLabels ) ;
2020-08-09 13:10:07 -04:00
# endif
2018-06-24 11:18:22 -04:00
connect ( ui - > customFee , & BitcoinAmountField : : valueChanged , this , & SendCoinsDialog : : coinControlUpdateLabels ) ;
connect ( ui - > optInRBF , & QCheckBox : : stateChanged , this , & SendCoinsDialog : : updateSmartFeeLabel ) ;
connect ( ui - > optInRBF , & QCheckBox : : stateChanged , this , & SendCoinsDialog : : coinControlUpdateLabels ) ;
2018-10-30 10:09:10 -03:00
CAmount requiredFee = model - > wallet ( ) . getRequiredFee ( 1000 ) ;
ui - > customFee - > SetMinValue ( requiredFee ) ;
if ( ui - > customFee - > value ( ) < requiredFee ) {
ui - > customFee - > setValue ( requiredFee ) ;
}
ui - > customFee - > setSingleStep ( requiredFee ) ;
2014-11-01 20:14:47 -03:00
updateFeeSectionControls ( ) ;
updateSmartFeeLabel ( ) ;
2016-10-21 05:53:37 -03:00
2017-04-17 20:03:45 -03:00
// set default rbf checkbox state
2017-12-22 05:18:05 -03:00
ui - > optInRBF - > setCheckState ( Qt : : Checked ) ;
2017-04-17 20:03:45 -03:00
2017-12-05 17:57:12 -03:00
if ( model - > wallet ( ) . privateKeysDisabled ( ) ) {
2019-09-26 06:31:49 -03:00
ui - > sendButton - > setText ( tr ( " Cr&eate Unsigned " ) ) ;
ui - > sendButton - > setToolTip ( tr ( " Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet. " ) . arg ( PACKAGE_NAME ) ) ;
}
2016-10-21 05:53:37 -03:00
// set the smartfee-sliders default value (wallets default conf.target or last stored value)
QSettings settings ;
2017-07-13 06:19:17 -04:00
if ( settings . value ( " nSmartFeeSliderPosition " ) . toInt ( ) ! = 0 ) {
// migrate nSmartFeeSliderPosition to nConfTarget
// nConfTarget is available since 0.15 (replaced nSmartFeeSliderPosition)
int nConfirmTarget = 25 - settings . value ( " nSmartFeeSliderPosition " ) . toInt ( ) ; // 25 == old slider range
settings . setValue ( " nConfTarget " , nConfirmTarget ) ;
settings . remove ( " nSmartFeeSliderPosition " ) ;
}
2017-07-07 16:03:49 -04:00
if ( settings . value ( " nConfTarget " ) . toInt ( ) = = 0 )
2018-04-07 13:12:46 -03:00
ui - > confTargetSelector - > setCurrentIndex ( getIndexForConfTarget ( model - > wallet ( ) . getConfirmTarget ( ) ) ) ;
2016-10-21 05:53:37 -03:00
else
2017-07-07 16:03:49 -04:00
ui - > confTargetSelector - > setCurrentIndex ( getIndexForConfTarget ( settings . value ( " nConfTarget " ) . toInt ( ) ) ) ;
2011-11-08 17:18:36 -03:00
}
2011-05-30 14:20:12 -04:00
}
2011-05-12 08:44:52 -04:00
SendCoinsDialog : : ~ SendCoinsDialog ( )
{
2014-11-01 20:14:47 -03:00
QSettings settings ;
settings . setValue ( " fFeeSectionMinimized " , fFeeMinimized ) ;
settings . setValue ( " nFeeRadio " , ui - > groupFee - > checkedId ( ) ) ;
2017-07-07 16:03:49 -04:00
settings . setValue ( " nConfTarget " , getConfTargetForIndex ( ui - > confTargetSelector - > currentIndex ( ) ) ) ;
2014-11-01 20:14:47 -03:00
settings . setValue ( " nTransactionFee " , ( qint64 ) ui - > customFee - > value ( ) ) ;
2011-05-12 08:44:52 -04:00
delete ui ;
}
2011-05-12 11:55:24 -04:00
2019-11-18 11:54:39 -03:00
bool SendCoinsDialog : : PrepareSendText ( QString & question_string , QString & informative_text , QString & detailed_text )
2011-05-12 11:55:24 -04:00
{
2011-07-16 13:01:05 -04:00
QList < SendCoinsRecipient > recipients ;
bool valid = true ;
2011-11-08 17:18:36 -03:00
2011-07-16 13:01:05 -04:00
for ( int i = 0 ; i < ui - > entries - > count ( ) ; + + i )
{
SendCoinsEntry * entry = qobject_cast < SendCoinsEntry * > ( ui - > entries - > itemAt ( i ) - > widget ( ) ) ;
if ( entry )
{
2017-04-18 18:06:13 -03:00
if ( entry - > validate ( model - > node ( ) ) )
2011-07-16 13:01:05 -04:00
{
recipients . append ( entry - > getValue ( ) ) ;
}
2018-12-14 01:39:05 -03:00
else if ( valid )
2011-07-16 13:01:05 -04:00
{
2018-12-14 01:39:05 -03:00
ui - > scrollArea - > ensureWidgetVisible ( entry ) ;
2011-07-16 13:01:05 -04:00
valid = false ;
}
}
}
2011-05-30 14:20:12 -04:00
2011-07-16 13:01:05 -04:00
if ( ! valid | | recipients . isEmpty ( ) )
2011-05-14 11:25:05 -04:00
{
2019-11-18 11:54:39 -03:00
return false ;
2011-05-14 11:25:05 -04:00
}
2011-05-27 15:43:05 -04:00
2014-06-09 21:14:13 -04:00
fNewRecipientAllowed = false ;
WalletModel : : UnlockContext ctx ( model - > requestUnlock ( ) ) ;
if ( ! ctx . isValid ( ) )
{
// Unlock wallet was cancelled
fNewRecipientAllowed = true ;
2019-11-18 11:54:39 -03:00
return false ;
2014-06-09 21:14:13 -04:00
}
// prepare transaction for getting txFee earlier
2019-11-18 11:54:39 -03:00
m_current_transaction = MakeUnique < WalletModelTransaction > ( recipients ) ;
2014-06-09 21:14:13 -04:00
WalletModel : : SendCoinsReturn prepareStatus ;
2016-10-21 05:53:37 -03:00
2020-05-05 18:56:21 -04:00
updateCoinControlState ( * m_coin_control ) ;
2017-06-28 19:24:28 -04:00
2020-05-05 18:56:21 -04:00
prepareStatus = model - > prepareTransaction ( * m_current_transaction , * m_coin_control ) ;
2014-06-09 21:14:13 -04:00
// process prepareStatus and on error generate message shown to user
processSendCoinsReturn ( prepareStatus ,
2019-11-18 11:54:39 -03:00
BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , m_current_transaction - > getTransactionFee ( ) ) ) ;
2014-06-09 21:14:13 -04:00
if ( prepareStatus . status ! = WalletModel : : OK ) {
fNewRecipientAllowed = true ;
2019-11-18 11:54:39 -03:00
return false ;
2014-06-09 21:14:13 -04:00
}
2019-11-18 11:54:39 -03:00
CAmount txFee = m_current_transaction - > getTransactionFee ( ) ;
2011-07-16 13:01:05 -04:00
QStringList formatted ;
2019-11-18 11:54:39 -03:00
for ( const SendCoinsRecipient & rcp : m_current_transaction - > getRecipients ( ) )
2011-07-16 13:01:05 -04:00
{
2019-04-24 16:36:15 -04:00
// generate amount string with wallet name in case of multiwallet
QString amount = BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , rcp . amount ) ;
2018-03-06 01:56:21 -03:00
if ( model - > isMultiwallet ( ) ) {
2019-09-08 17:40:05 -03:00
amount . append ( tr ( " from wallet '%1' " ) . arg ( GUIUtil : : HtmlEscape ( model - > getWalletName ( ) ) ) ) ;
2018-03-06 01:56:21 -03:00
}
2019-04-24 16:36:15 -04:00
// generate address string
QString address = rcp . address ;
2013-09-13 11:49:35 -03:00
QString recipientElement ;
2013-07-22 02:50:39 -04:00
{
2013-09-13 11:49:35 -03:00
if ( rcp . label . length ( ) > 0 ) // label with address
2013-08-30 14:04:48 -04:00
{
2019-09-08 17:40:37 -03:00
recipientElement . append ( tr ( " %1 to '%2' " ) . arg ( amount , GUIUtil : : HtmlEscape ( rcp . label ) ) ) ;
2013-09-13 11:49:35 -03:00
recipientElement . append ( QString ( " (%1) " ) . arg ( address ) ) ;
2013-08-30 14:04:48 -04:00
}
2013-09-13 11:49:35 -03:00
else // just address
2013-08-30 14:04:48 -04:00
{
2018-05-03 10:38:39 -03:00
recipientElement . append ( tr ( " %1 to %2 " ) . arg ( amount , address ) ) ;
2013-08-30 14:04:48 -04:00
}
2013-07-22 02:50:39 -04:00
}
2013-09-13 11:49:35 -03:00
formatted . append ( recipientElement ) ;
2011-07-16 13:01:05 -04:00
}
2011-06-25 13:32:36 -04:00
2017-12-05 17:57:12 -03:00
if ( model - > wallet ( ) . privateKeysDisabled ( ) ) {
2019-11-18 11:54:39 -03:00
question_string . append ( tr ( " Do you want to draft this transaction? " ) ) ;
2019-09-26 06:31:49 -03:00
} else {
2019-11-18 11:54:39 -03:00
question_string . append ( tr ( " Are you sure you want to send? " ) ) ;
2019-09-26 06:31:49 -03:00
}
2019-11-18 11:54:39 -03:00
question_string . append ( " <br /><span style='font-size:10pt;'> " ) ;
2017-12-05 17:57:12 -03:00
if ( model - > wallet ( ) . privateKeysDisabled ( ) ) {
2019-11-18 12:43:57 -03:00
question_string . append ( tr ( " Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet. " ) . arg ( PACKAGE_NAME ) ) ;
2019-09-26 06:31:49 -03:00
} else {
2019-11-18 11:54:39 -03:00
question_string . append ( tr ( " Please, review your transaction. " ) ) ;
2019-09-26 06:31:49 -03:00
}
2019-11-18 11:54:39 -03:00
question_string . append ( " </span>%1 " ) ;
2013-08-30 14:04:48 -04:00
if ( txFee > 0 )
{
// append fee string if a fee is required
2019-11-18 11:54:39 -03:00
question_string . append ( " <hr /><b> " ) ;
question_string . append ( tr ( " Transaction fee " ) ) ;
question_string . append ( " </b> " ) ;
2014-11-01 20:14:47 -03:00
// append transaction size
2019-11-18 11:54:39 -03:00
question_string . append ( " ( " + QString : : number ( ( double ) m_current_transaction - > getTransactionSize ( ) / 1000 ) + " kB): " ) ;
2018-05-03 10:38:39 -03:00
// append transaction fee value
2019-11-18 11:54:39 -03:00
question_string . append ( " <span style='color:#aa0000; font-weight:bold;'> " ) ;
question_string . append ( BitcoinUnits : : formatHtmlWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , txFee ) ) ;
question_string . append ( " </span><br /> " ) ;
2018-05-03 10:38:39 -03:00
// append RBF message according to transaction's signalling
2019-11-18 11:54:39 -03:00
question_string . append ( " <span style='font-size:10pt; font-weight:normal;'> " ) ;
2018-05-03 10:38:39 -03:00
if ( ui - > optInRBF - > isChecked ( ) ) {
2019-11-18 11:54:39 -03:00
question_string . append ( tr ( " You can increase the fee later (signals Replace-By-Fee, BIP-125). " ) ) ;
2018-05-03 10:38:39 -03:00
} else {
2019-11-18 11:54:39 -03:00
question_string . append ( tr ( " Not signalling Replace-By-Fee, BIP-125. " ) ) ;
2018-05-03 10:38:39 -03:00
}
2019-11-18 11:54:39 -03:00
question_string . append ( " </span> " ) ;
2013-08-30 14:04:48 -04:00
}
2013-11-17 10:43:23 -03:00
// add total amount in all subdivision units
2019-11-18 11:54:39 -03:00
question_string . append ( " <hr /> " ) ;
CAmount totalAmount = m_current_transaction - > getTotalTransactionAmount ( ) + txFee ;
2013-11-17 10:43:23 -03:00
QStringList alternativeUnits ;
2018-06-18 01:58:28 -04:00
for ( const BitcoinUnits : : Unit u : BitcoinUnits : : availableUnits ( ) )
2013-08-30 14:04:48 -04:00
{
2013-11-17 10:43:23 -03:00
if ( u ! = model - > getOptionsModel ( ) - > getDisplayUnit ( ) )
2014-05-09 18:50:09 -04:00
alternativeUnits . append ( BitcoinUnits : : formatHtmlWithUnit ( u , totalAmount ) ) ;
2013-08-30 14:04:48 -04:00
}
2019-11-18 11:54:39 -03:00
question_string . append ( QString ( " <b>%1</b>: <b>%2</b> " ) . arg ( tr ( " Total Amount " ) )
2015-11-06 09:22:00 -03:00
. arg ( BitcoinUnits : : formatHtmlWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , totalAmount ) ) ) ;
2019-11-18 11:54:39 -03:00
question_string . append ( QString ( " <br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span> " )
2018-05-03 10:38:39 -03:00
. arg ( alternativeUnits . join ( " " + tr ( " or " ) + " " ) ) ) ;
2016-04-05 11:55:41 -03:00
2019-05-18 15:06:43 -04:00
if ( formatted . size ( ) > 1 ) {
2019-11-18 11:54:39 -03:00
question_string = question_string . arg ( " " ) ;
2019-05-18 15:06:43 -04:00
informative_text = tr ( " To review recipient list click \" Show Details... \" " ) ;
detailed_text = formatted . join ( " \n \n " ) ;
} else {
2019-11-18 11:54:39 -03:00
question_string = question_string . arg ( " <br /><br /> " + formatted . at ( 0 ) ) ;
2019-05-18 15:06:43 -04:00
}
2019-11-18 11:54:39 -03:00
return true ;
}
void SendCoinsDialog : : on_sendButton_clicked ( )
{
if ( ! model | | ! model - > getOptionsModel ( ) )
return ;
QString question_string , informative_text , detailed_text ;
if ( ! PrepareSendText ( question_string , informative_text , detailed_text ) ) return ;
assert ( m_current_transaction ) ;
2017-12-05 17:57:12 -03:00
const QString confirmation = model - > wallet ( ) . privateKeysDisabled ( ) ? tr ( " Confirm transaction proposal " ) : tr ( " Confirm send coins " ) ;
2019-11-18 12:43:57 -03:00
const QString confirmButtonText = model - > wallet ( ) . privateKeysDisabled ( ) ? tr ( " Create Unsigned " ) : tr ( " Send " ) ;
2019-11-18 11:54:39 -03:00
SendConfirmationDialog confirmationDialog ( confirmation , question_string , informative_text , detailed_text , SEND_CONFIRM_DELAY , confirmButtonText , this ) ;
2016-05-09 23:26:57 -03:00
confirmationDialog . exec ( ) ;
2017-06-01 10:13:35 -04:00
QMessageBox : : StandardButton retval = static_cast < QMessageBox : : StandardButton > ( confirmationDialog . result ( ) ) ;
2013-08-30 14:04:48 -04:00
if ( retval ! = QMessageBox : : Yes )
{
fNewRecipientAllowed = true ;
return ;
}
2019-09-26 06:31:49 -03:00
bool send_failure = false ;
2017-12-05 17:57:12 -03:00
if ( model - > wallet ( ) . privateKeysDisabled ( ) ) {
2019-11-18 11:54:39 -03:00
CMutableTransaction mtx = CMutableTransaction { * ( m_current_transaction - > getWtx ( ) ) } ;
2019-09-26 06:31:49 -03:00
PartiallySignedTransaction psbtx ( mtx ) ;
bool complete = false ;
2020-01-31 23:12:14 -03:00
const TransactionError err = model - > wallet ( ) . fillPSBT ( SIGHASH_ALL , false /* sign */ , true /* bip32derivs */ , psbtx , complete , nullptr ) ;
2019-09-26 06:31:49 -03:00
assert ( ! complete ) ;
assert ( err = = TransactionError : : OK ) ;
// Serialize the PSBT
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
GUIUtil : : setClipboard ( EncodeBase64 ( ssTx . str ( ) ) . c_str ( ) ) ;
2019-11-18 12:43:57 -03:00
QMessageBox msgBox ;
msgBox . setText ( " Unsigned Transaction " ) ;
msgBox . setInformativeText ( " The PSBT has been copied to the clipboard. You can also save it. " ) ;
msgBox . setStandardButtons ( QMessageBox : : Save | QMessageBox : : Discard ) ;
msgBox . setDefaultButton ( QMessageBox : : Discard ) ;
switch ( msgBox . exec ( ) ) {
case QMessageBox : : Save : {
QString selectedFilter ;
QString fileNameSuggestion = " " ;
bool first = true ;
for ( const SendCoinsRecipient & rcp : m_current_transaction - > getRecipients ( ) ) {
if ( ! first ) {
fileNameSuggestion . append ( " - " ) ;
}
QString labelOrAddress = rcp . label . isEmpty ( ) ? rcp . address : rcp . label ;
QString amount = BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , rcp . amount ) ;
fileNameSuggestion . append ( labelOrAddress + " - " + amount ) ;
first = false ;
}
fileNameSuggestion . append ( " .psbt " ) ;
QString filename = GUIUtil : : getSaveFileName ( this ,
tr ( " Save Transaction Data " ) , fileNameSuggestion ,
tr ( " Partially Signed Transaction (Binary) (*.psbt) " ) , & selectedFilter ) ;
if ( filename . isEmpty ( ) ) {
return ;
}
std : : ofstream out ( filename . toLocal8Bit ( ) . data ( ) ) ;
out < < ssTx . str ( ) ;
out . close ( ) ;
Q_EMIT message ( tr ( " PSBT saved " ) , " PSBT saved to disk " , CClientUIInterface : : MSG_INFORMATION ) ;
break ;
}
case QMessageBox : : Discard :
break ;
default :
assert ( false ) ;
}
2019-09-26 06:31:49 -03:00
} else {
// now send the prepared transaction
2019-11-18 11:54:39 -03:00
WalletModel : : SendCoinsReturn sendStatus = model - > sendCoins ( * m_current_transaction ) ;
2019-09-26 06:31:49 -03:00
// process sendStatus and on error generate message shown to user
processSendCoinsReturn ( sendStatus ) ;
2013-10-30 11:37:41 -03:00
2019-09-26 06:31:49 -03:00
if ( sendStatus . status = = WalletModel : : OK ) {
2019-11-18 11:54:39 -03:00
Q_EMIT coinsSent ( m_current_transaction - > getWtx ( ) - > GetHash ( ) ) ;
2019-09-26 06:31:49 -03:00
} else {
send_failure = true ;
}
}
if ( ! send_failure ) {
2011-05-31 16:24:53 -04:00
accept ( ) ;
2020-05-05 18:56:21 -04:00
m_coin_control - > UnSelectAll ( ) ;
2013-08-12 11:03:03 -04:00
coinControlUpdateLabels ( ) ;
2011-05-14 11:25:05 -04:00
}
2011-12-24 01:27:12 -03:00
fNewRecipientAllowed = true ;
2019-11-18 12:43:57 -03:00
m_current_transaction . reset ( ) ;
2011-05-12 11:55:24 -04:00
}
2011-07-16 13:01:05 -04:00
void SendCoinsDialog : : clear ( )
2011-05-12 11:55:24 -04:00
{
2019-11-18 12:43:57 -03:00
m_current_transaction . reset ( ) ;
2018-02-14 10:23:43 -03:00
// Clear coin control settings
2020-05-05 18:56:21 -04:00
m_coin_control - > UnSelectAll ( ) ;
2018-02-14 10:23:43 -03:00
ui - > checkBoxCoinControlChange - > setChecked ( false ) ;
ui - > lineEditCoinControlChange - > clear ( ) ;
coinControlUpdateLabels ( ) ;
2011-07-16 13:01:05 -04:00
// Remove entries until only one left
2011-08-07 10:04:48 -04:00
while ( ui - > entries - > count ( ) )
2011-07-16 13:01:05 -04:00
{
2013-12-04 04:17:57 -03:00
ui - > entries - > takeAt ( 0 ) - > widget ( ) - > deleteLater ( ) ;
2011-07-16 13:01:05 -04:00
}
2011-08-07 10:04:48 -04:00
addEntry ( ) ;
2011-05-13 16:00:27 -04:00
2013-11-22 09:53:05 -03:00
updateTabsAndLabels ( ) ;
2011-07-07 11:33:15 -04:00
}
void SendCoinsDialog : : reject ( )
{
clear ( ) ;
}
void SendCoinsDialog : : accept ( )
{
clear ( ) ;
}
2011-07-16 13:01:05 -04:00
2011-08-07 10:04:48 -04:00
SendCoinsEntry * SendCoinsDialog : : addEntry ( )
2011-07-16 13:01:05 -04:00
{
2015-07-28 10:20:14 -03:00
SendCoinsEntry * entry = new SendCoinsEntry ( platformStyle , this ) ;
2011-07-16 13:01:05 -04:00
entry - > setModel ( model ) ;
ui - > entries - > addWidget ( entry ) ;
2018-06-24 11:18:22 -04:00
connect ( entry , & SendCoinsEntry : : removeEntry , this , & SendCoinsDialog : : removeEntry ) ;
connect ( entry , & SendCoinsEntry : : useAvailableBalance , this , & SendCoinsDialog : : useAvailableBalance ) ;
connect ( entry , & SendCoinsEntry : : payAmountChanged , this , & SendCoinsDialog : : coinControlUpdateLabels ) ;
connect ( entry , & SendCoinsEntry : : subtractFeeFromAmountChanged , this , & SendCoinsDialog : : coinControlUpdateLabels ) ;
2011-07-16 13:01:05 -04:00
// Focus the field, so that entry can start immediately
entry - > clear ( ) ;
2011-12-07 02:00:04 -03:00
entry - > setFocus ( ) ;
ui - > scrollAreaWidgetContents - > resize ( ui - > scrollAreaWidgetContents - > sizeHint ( ) ) ;
2013-04-02 12:30:14 -03:00
qApp - > processEvents ( ) ;
2011-12-07 02:00:04 -03:00
QScrollBar * bar = ui - > scrollArea - > verticalScrollBar ( ) ;
2012-06-09 09:41:21 -04:00
if ( bar )
2011-12-07 02:00:04 -03:00
bar - > setSliderPosition ( bar - > maximum ( ) ) ;
2016-01-04 05:44:36 -03:00
updateTabsAndLabels ( ) ;
2011-08-07 10:04:48 -04:00
return entry ;
2011-07-16 13:01:05 -04:00
}
2013-11-22 09:53:05 -03:00
void SendCoinsDialog : : updateTabsAndLabels ( )
2011-07-16 13:01:05 -04:00
{
2018-07-30 06:37:09 -04:00
setupTabChain ( nullptr ) ;
2013-08-12 11:03:03 -04:00
coinControlUpdateLabels ( ) ;
2011-07-16 13:01:05 -04:00
}
void SendCoinsDialog : : removeEntry ( SendCoinsEntry * entry )
{
2013-12-10 08:01:54 -03:00
entry - > hide ( ) ;
2013-11-22 09:53:05 -03:00
2013-12-10 08:01:54 -03:00
// If the last entry is about to be removed add an empty one
if ( ui - > entries - > count ( ) = = 1 )
2013-11-22 09:53:05 -03:00
addEntry ( ) ;
2013-12-10 08:01:54 -03:00
entry - > deleteLater ( ) ;
2013-11-22 09:53:05 -03:00
updateTabsAndLabels ( ) ;
2011-07-16 13:01:05 -04:00
}
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 ) ;
}
}
2014-01-29 10:41:41 -03:00
QWidget : : setTabOrder ( prev , ui - > sendButton ) ;
QWidget : : setTabOrder ( ui - > sendButton , ui - > clearButton ) ;
QWidget : : setTabOrder ( ui - > clearButton , ui - > addButton ) ;
return ui - > addButton ;
2011-07-16 13:01:05 -04:00
}
2011-08-07 10:04:48 -04:00
2013-01-25 14:46:53 -03:00
void SendCoinsDialog : : setAddress ( const QString & address )
{
2018-07-30 06:37:09 -04:00
SendCoinsEntry * entry = nullptr ;
2013-01-25 14:46:53 -03:00
// 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 - > setAddress ( address ) ;
}
2011-08-07 10:04:48 -04:00
void SendCoinsDialog : : pasteEntry ( const SendCoinsRecipient & rv )
{
2012-06-09 09:41:21 -04:00
if ( ! fNewRecipientAllowed )
2011-12-24 01:27:12 -03:00
return ;
2018-07-30 06:37:09 -04:00
SendCoinsEntry * entry = nullptr ;
2011-08-07 10:04:48 -04:00
// 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 ) ;
2013-11-22 09:53:05 -03:00
updateTabsAndLabels ( ) ;
2011-08-07 10:04:48 -04:00
}
2013-07-22 02:50:39 -04:00
bool SendCoinsDialog : : handlePaymentRequest ( const SendCoinsRecipient & rv )
2011-08-07 10:04:48 -04:00
{
2013-11-15 21:54:29 -03:00
// Just paste the entry, all pre-checks
// are done in paymentserver.cpp.
2013-07-22 02:50:39 -04:00
pasteEntry ( rv ) ;
return true ;
2011-08-07 10:04:48 -04:00
}
2011-09-22 14:02:01 -03:00
2018-04-07 04:42:02 -03:00
void SendCoinsDialog : : setBalance ( const interfaces : : WalletBalances & balances )
2011-09-22 14:02:01 -03:00
{
2013-08-24 09:07:17 -04:00
if ( model & & model - > getOptionsModel ( ) )
{
2019-11-26 07:43:53 -03:00
CAmount balance = balances . balance ;
2017-12-05 17:57:12 -03:00
if ( model - > wallet ( ) . privateKeysDisabled ( ) ) {
2019-11-26 07:43:53 -03:00
balance = balances . watch_only_balance ;
ui - > labelBalanceName - > setText ( tr ( " Watch-only balance: " ) ) ;
}
ui - > labelBalance - > setText ( BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , balance ) ) ;
2013-08-24 09:07:17 -04:00
}
2011-09-22 14:02:01 -03:00
}
2012-06-09 09:41:21 -04:00
void SendCoinsDialog : : updateDisplayUnit ( )
{
2018-03-31 07:41:33 -03:00
setBalance ( model - > wallet ( ) . getBalances ( ) ) ;
2014-11-01 20:14:47 -03:00
ui - > customFee - > setDisplayUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) ) ;
updateSmartFeeLabel ( ) ;
2012-06-09 09:41:21 -04:00
}
2013-10-30 11:37:41 -03:00
void SendCoinsDialog : : processSendCoinsReturn ( const WalletModel : : SendCoinsReturn & sendCoinsReturn , const QString & msgArg )
{
QPair < QString , CClientUIInterface : : MessageBoxFlags > 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.
2019-04-03 18:55:24 -03:00
// All status values are used only in WalletModel::prepareTransaction()
2013-10-30 11:37:41 -03:00
switch ( sendCoinsReturn . status )
{
case WalletModel : : InvalidAddress :
2015-04-28 11:48:28 -03:00
msgParams . first = tr ( " The recipient address is not valid. Please recheck. " ) ;
2013-10-30 11:37:41 -03:00
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 :
2015-04-28 11:47:17 -03:00
msgParams . first = tr ( " Duplicate address found: addresses should only be used once each. " ) ;
2013-10-30 11:37:41 -03:00
break ;
case WalletModel : : TransactionCreationFailed :
msgParams . first = tr ( " Transaction creation failed! " ) ;
msgParams . second = CClientUIInterface : : MSG_ERROR ;
break ;
2015-01-30 23:54:55 -03:00
case WalletModel : : AbsurdFee :
2019-02-20 15:45:16 -03:00
msgParams . first = tr ( " A fee higher than %1 is considered an absurdly high fee. " ) . arg ( BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , model - > wallet ( ) . getDefaultMaxTxFee ( ) ) ) ;
2014-11-01 20:14:47 -03:00
break ;
2015-01-08 10:42:04 -03:00
case WalletModel : : PaymentRequestExpired :
2015-02-03 18:44:33 -03:00
msgParams . first = tr ( " Payment request expired. " ) ;
2015-01-08 10:42:04 -03:00
msgParams . second = CClientUIInterface : : MSG_ERROR ;
break ;
2013-12-16 05:46:55 -03:00
// included to prevent a compiler warning.
2013-10-30 11:37:41 -03:00
case WalletModel : : OK :
default :
return ;
}
2015-07-14 08:59:05 -03:00
Q_EMIT message ( tr ( " Send Coins " ) , msgParams . first , msgParams . second ) ;
2013-10-30 11:37:41 -03:00
}
2013-08-12 11:03:03 -04:00
2014-11-01 20:14:47 -03:00
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 ) ;
}
2017-08-20 02:04:56 -03:00
void SendCoinsDialog : : useAvailableBalance ( SendCoinsEntry * entry )
{
2019-09-23 14:20:22 -03:00
// Include watch-only for wallets without private key
2020-05-05 18:56:21 -04:00
m_coin_control - > fAllowWatchOnly = model - > wallet ( ) . privateKeysDisabled ( ) ;
2019-09-23 14:20:22 -03:00
2017-08-20 02:04:56 -03:00
// Calculate available amount to send.
2020-05-05 18:56:21 -04:00
CAmount amount = model - > wallet ( ) . getAvailableBalance ( * m_coin_control ) ;
2017-08-20 02:04:56 -03:00
for ( int i = 0 ; i < ui - > entries - > count ( ) ; + + i ) {
SendCoinsEntry * e = qobject_cast < SendCoinsEntry * > ( ui - > entries - > itemAt ( i ) - > widget ( ) ) ;
if ( e & & ! e - > isHidden ( ) & & e ! = entry ) {
amount - = e - > getValue ( ) . amount ;
}
}
if ( amount > 0 ) {
entry - > checkSubtractFeeFromAmount ( ) ;
entry - > setAmount ( amount ) ;
} else {
entry - > setAmount ( 0 ) ;
}
}
2014-11-01 20:14:47 -03:00
void SendCoinsDialog : : updateFeeSectionControls ( )
{
2017-07-07 16:03:49 -04:00
ui - > confTargetSelector - > setEnabled ( ui - > radioSmartFee - > isChecked ( ) ) ;
2014-11-01 20:14:47 -03:00
ui - > labelSmartFee - > setEnabled ( ui - > radioSmartFee - > isChecked ( ) ) ;
ui - > labelSmartFee2 - > setEnabled ( ui - > radioSmartFee - > isChecked ( ) ) ;
ui - > labelSmartFee3 - > setEnabled ( ui - > radioSmartFee - > isChecked ( ) ) ;
ui - > labelFeeEstimation - > setEnabled ( ui - > radioSmartFee - > isChecked ( ) ) ;
2018-10-30 10:09:10 -03:00
ui - > labelCustomFeeWarning - > setEnabled ( ui - > radioCustomFee - > isChecked ( ) ) ;
ui - > labelCustomPerKilobyte - > setEnabled ( ui - > radioCustomFee - > isChecked ( ) ) ;
ui - > customFee - > setEnabled ( ui - > radioCustomFee - > isChecked ( ) ) ;
2014-11-01 20:14:47 -03:00
}
void SendCoinsDialog : : updateFeeMinimizedLabel ( )
{
if ( ! model | | ! model - > getOptionsModel ( ) )
return ;
if ( ui - > radioSmartFee - > isChecked ( ) )
ui - > labelFeeMinimized - > setText ( ui - > labelSmartFee - > text ( ) ) ;
else {
2017-09-15 01:07:45 -03:00
ui - > labelFeeMinimized - > setText ( BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , ui - > customFee - > value ( ) ) + " /kB " ) ;
2014-11-01 20:14:47 -03:00
}
}
2017-06-28 19:24:28 -04:00
void SendCoinsDialog : : updateCoinControlState ( CCoinControl & ctrl )
{
if ( ui - > radioCustomFee - > isChecked ( ) ) {
ctrl . m_feerate = CFeeRate ( ui - > customFee - > value ( ) ) ;
} else {
2017-06-29 11:29:34 -04:00
ctrl . m_feerate . reset ( ) ;
2017-06-28 19:24:28 -04:00
}
// Avoid using global defaults when sending money from the GUI
// Either custom fee will be used or if not selected, the confirmation target from dropdown box
ctrl . m_confirm_target = getConfTargetForIndex ( ui - > confTargetSelector - > currentIndex ( ) ) ;
2018-04-07 13:12:46 -03:00
ctrl . m_signal_bip125_rbf = ui - > optInRBF - > isChecked ( ) ;
2019-09-23 14:20:22 -03:00
// Include watch-only for wallets without private key
2017-12-05 17:57:12 -03:00
ctrl . fAllowWatchOnly = model - > wallet ( ) . privateKeysDisabled ( ) ;
2017-06-28 19:24:28 -04:00
}
2020-09-18 06:00:50 -03:00
void SendCoinsDialog : : updateNumberOfBlocks ( int count , const QDateTime & blockDate , double nVerificationProgress , bool headers , SynchronizationState sync_state ) {
if ( sync_state = = SynchronizationState : : POST_INIT ) {
updateSmartFeeLabel ( ) ;
}
}
2014-11-01 20:14:47 -03:00
void SendCoinsDialog : : updateSmartFeeLabel ( )
{
if ( ! model | | ! model - > getOptionsModel ( ) )
return ;
2020-05-05 18:56:21 -04:00
updateCoinControlState ( * m_coin_control ) ;
m_coin_control - > m_feerate . reset ( ) ; // Explicitly use only fee estimation rate for smart fee labels
2017-04-20 13:28:58 -03:00
int returned_target ;
FeeReason reason ;
2020-05-05 18:56:21 -04:00
CFeeRate feeRate = CFeeRate ( model - > wallet ( ) . getMinimumFee ( 1000 , * m_coin_control , & returned_target , & reason ) ) ;
2017-06-28 19:24:28 -04:00
2017-06-29 11:29:34 -04:00
ui - > labelSmartFee - > setText ( BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , feeRate . GetFeePerK ( ) ) + " /kB " ) ;
2017-04-20 13:28:58 -03:00
if ( reason = = FeeReason : : FALLBACK ) {
2014-11-01 20:14:47 -03:00
ui - > labelSmartFee2 - > show ( ) ; // (Smart fee not initialized yet. This usually takes a few blocks...)
ui - > labelFeeEstimation - > setText ( " " ) ;
2017-01-06 06:42:27 -03:00
ui - > fallbackFeeWarningLabel - > setVisible ( true ) ;
2017-02-17 05:18:32 -03:00
int lightness = ui - > fallbackFeeWarningLabel - > palette ( ) . color ( QPalette : : WindowText ) . lightness ( ) ;
QColor warning_colour ( 255 - ( lightness / 5 ) , 176 - ( lightness / 3 ) , 48 - ( lightness / 14 ) ) ;
ui - > fallbackFeeWarningLabel - > setStyleSheet ( " QLabel { color: " + warning_colour . name ( ) + " ; } " ) ;
2019-08-23 13:13:11 -04:00
ui - > fallbackFeeWarningLabel - > setIndent ( GUIUtil : : TextWidth ( QFontMetrics ( ui - > fallbackFeeWarningLabel - > font ( ) ) , " x " ) ) ;
2014-11-01 20:14:47 -03:00
}
else
{
ui - > labelSmartFee2 - > hide ( ) ;
2017-04-20 13:28:58 -03:00
ui - > labelFeeEstimation - > setText ( tr ( " Estimated to begin confirmation within %n block(s). " , " " , returned_target ) ) ;
2017-01-06 06:42:27 -03:00
ui - > fallbackFeeWarningLabel - > setVisible ( false ) ;
2014-11-01 20:14:47 -03:00
}
updateFeeMinimizedLabel ( ) ;
}
2013-08-12 11:03:03 -04:00
// 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 ( )
{
2015-01-12 19:26:29 -03:00
GUIUtil : : setClipboard ( ui - > labelCoinControlFee - > text ( ) . left ( ui - > labelCoinControlFee - > text ( ) . indexOf ( " " ) ) . replace ( ASYMP_UTF8 , " " ) ) ;
2013-08-12 11:03:03 -04:00
}
// Coin Control: copy label "After fee" to clipboard
void SendCoinsDialog : : coinControlClipboardAfterFee ( )
{
2015-01-12 19:26:29 -03:00
GUIUtil : : setClipboard ( ui - > labelCoinControlAfterFee - > text ( ) . left ( ui - > labelCoinControlAfterFee - > text ( ) . indexOf ( " " ) ) . replace ( ASYMP_UTF8 , " " ) ) ;
2013-08-12 11:03:03 -04:00
}
// Coin Control: copy label "Bytes" to clipboard
void SendCoinsDialog : : coinControlClipboardBytes ( )
{
2015-01-12 19:26:29 -03:00
GUIUtil : : setClipboard ( ui - > labelCoinControlBytes - > text ( ) . replace ( ASYMP_UTF8 , " " ) ) ;
2013-08-12 11:03:03 -04:00
}
2014-06-07 19:05:53 -04:00
// Coin Control: copy label "Dust" to clipboard
2013-08-12 11:03:03 -04:00
void SendCoinsDialog : : coinControlClipboardLowOutput ( )
{
GUIUtil : : setClipboard ( ui - > labelCoinControlLowOutput - > text ( ) ) ;
}
// Coin Control: copy label "Change" to clipboard
void SendCoinsDialog : : coinControlClipboardChange ( )
{
2015-01-12 19:26:29 -03:00
GUIUtil : : setClipboard ( ui - > labelCoinControlChange - > text ( ) . left ( ui - > labelCoinControlChange - > text ( ) . indexOf ( " " ) ) . replace ( ASYMP_UTF8 , " " ) ) ;
2013-08-12 11:03:03 -04:00
}
// 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
2020-05-05 18:56:21 -04:00
m_coin_control - > SetNull ( ) ;
2014-01-27 15:15:56 -03:00
2015-11-25 10:05:30 -03:00
coinControlUpdateLabels ( ) ;
2013-08-12 11:03:03 -04:00
}
// Coin Control: button inputs -> show actual coin control dialog
void SendCoinsDialog : : coinControlButtonClicked ( )
{
2020-05-05 18:56:21 -04:00
CoinControlDialog dlg ( * m_coin_control , model , platformStyle ) ;
2013-08-12 11:03:03 -04:00
dlg . exec ( ) ;
coinControlUpdateLabels ( ) ;
}
// Coin Control: checkbox custom change address
void SendCoinsDialog : : coinControlChangeChecked ( int state )
{
2013-11-20 11:49:34 -03:00
if ( state = = Qt : : Unchecked )
2013-08-12 11:03:03 -04:00
{
2020-05-05 18:56:21 -04:00
m_coin_control - > destChange = CNoDestination ( ) ;
2013-11-20 11:49:34 -03:00
ui - > labelCoinControlChangeLabel - > clear ( ) ;
2013-08-12 11:03:03 -04:00
}
2013-11-20 11:49:34 -03:00
else
// use this to re-validate an already entered address
coinControlChangeEdited ( ui - > lineEditCoinControlChange - > text ( ) ) ;
2013-08-12 11:03:03 -04:00
ui - > lineEditCoinControlChange - > setEnabled ( ( state = = Qt : : Checked ) ) ;
}
// Coin Control: custom change address changed
2013-12-10 08:01:54 -03:00
void SendCoinsDialog : : coinControlChangeEdited ( const QString & text )
2013-08-12 11:03:03 -04:00
{
2013-12-11 11:12:13 -03:00
if ( model & & model - > getAddressTableModel ( ) )
2013-08-12 11:03:03 -04:00
{
2013-12-11 11:12:13 -03:00
// Default to no change address until verified
2020-05-05 18:56:21 -04:00
m_coin_control - > destChange = CNoDestination ( ) ;
2013-12-11 11:12:13 -03:00
ui - > labelCoinControlChangeLabel - > setStyleSheet ( " QLabel{color:red;} " ) ;
2017-08-22 22:02:33 -03:00
const CTxDestination dest = DecodeDestination ( text . toStdString ( ) ) ;
2013-08-12 11:03:03 -04:00
2013-12-11 11:12:13 -03:00
if ( text . isEmpty ( ) ) // Nothing entered
{
2013-08-12 11:03:03 -04:00
ui - > labelCoinControlChangeLabel - > setText ( " " ) ;
2013-12-11 11:12:13 -03:00
}
2017-08-22 22:02:33 -03:00
else if ( ! IsValidDestination ( dest ) ) // Invalid address
2013-08-12 11:03:03 -04:00
{
ui - > labelCoinControlChangeLabel - > setText ( tr ( " Warning: Invalid Bitcoin address " ) ) ;
}
2013-12-11 11:12:13 -03:00
else // Valid address
2013-08-12 11:03:03 -04:00
{
2017-04-17 19:56:44 -03:00
if ( ! model - > wallet ( ) . isSpendable ( dest ) ) {
2013-12-11 11:12:13 -03:00
ui - > labelCoinControlChangeLabel - > setText ( tr ( " Warning: Unknown change address " ) ) ;
2016-12-23 06:24:29 -03:00
// 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 )
2020-05-05 18:56:21 -04:00
m_coin_control - > destChange = dest ;
2016-12-23 06:24:29 -03:00
else
{
ui - > lineEditCoinControlChange - > setText ( " " ) ;
ui - > labelCoinControlChangeLabel - > setStyleSheet ( " QLabel{color:black;} " ) ;
ui - > labelCoinControlChangeLabel - > setText ( " " ) ;
}
2013-12-11 11:12:13 -03:00
}
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 ) ;
2013-08-12 11:03:03 -04:00
else
2013-12-11 11:12:13 -03:00
ui - > labelCoinControlChangeLabel - > setText ( tr ( " (no label) " ) ) ;
2020-05-05 18:56:21 -04:00
m_coin_control - > destChange = dest ;
2013-08-12 11:03:03 -04:00
}
}
}
}
// Coin Control: update labels
void SendCoinsDialog : : coinControlUpdateLabels ( )
{
2015-11-25 10:05:30 -03:00
if ( ! model | | ! model - > getOptionsModel ( ) )
2013-08-12 11:03:03 -04:00
return ;
2020-05-05 18:56:21 -04:00
updateCoinControlState ( * m_coin_control ) ;
2017-06-28 19:24:28 -04:00
2013-08-12 11:03:03 -04:00
// set pay amounts
CoinControlDialog : : payAmounts . clear ( ) ;
2014-07-23 08:34:36 -04:00
CoinControlDialog : : fSubtractFeeFromAmount = false ;
2017-06-13 15:47:08 -04:00
2013-08-12 11:03:03 -04:00
for ( int i = 0 ; i < ui - > entries - > count ( ) ; + + i )
{
SendCoinsEntry * entry = qobject_cast < SendCoinsEntry * > ( ui - > entries - > itemAt ( i ) - > widget ( ) ) ;
2016-01-04 05:44:36 -03:00
if ( entry & & ! entry - > isHidden ( ) )
2014-07-23 08:34:36 -04:00
{
SendCoinsRecipient rcp = entry - > getValue ( ) ;
CoinControlDialog : : payAmounts . append ( rcp . amount ) ;
if ( rcp . fSubtractFeeFromAmount )
CoinControlDialog : : fSubtractFeeFromAmount = true ;
}
2013-08-12 11:03:03 -04:00
}
2020-05-05 18:56:21 -04:00
if ( m_coin_control - > HasSelected ( ) )
2013-08-12 11:03:03 -04:00
{
// actual coin control calculation
2020-05-05 18:56:21 -04:00
CoinControlDialog : : updateLabels ( * m_coin_control , model , this ) ;
2013-08-12 11:03:03 -04:00
// show coin control stats
ui - > labelCoinControlAutomaticallySelected - > hide ( ) ;
ui - > widgetCoinControl - > show ( ) ;
}
else
{
// hide coin control stats
ui - > labelCoinControlAutomaticallySelected - > show ( ) ;
ui - > widgetCoinControl - > hide ( ) ;
ui - > labelCoinControlInsuffFunds - > hide ( ) ;
}
}
2016-05-09 23:26:57 -03:00
2019-09-26 06:31:49 -03:00
SendConfirmationDialog : : SendConfirmationDialog ( const QString & title , const QString & text , const QString & informative_text , const QString & detailed_text , int _secDelay , const QString & _confirmButtonText , QWidget * parent )
: QMessageBox ( parent ) , secDelay ( _secDelay ) , confirmButtonText ( _confirmButtonText )
2016-05-09 23:26:57 -03:00
{
2019-04-24 14:56:10 -04:00
setIcon ( QMessageBox : : Question ) ;
setWindowTitle ( title ) ; // On macOS, the window title is ignored (as required by the macOS Guidelines).
setText ( text ) ;
setInformativeText ( informative_text ) ;
setDetailedText ( detailed_text ) ;
setStandardButtons ( QMessageBox : : Yes | QMessageBox : : Cancel ) ;
2016-05-09 23:26:57 -03:00
setDefaultButton ( QMessageBox : : Cancel ) ;
yesButton = button ( QMessageBox : : Yes ) ;
updateYesButton ( ) ;
2018-06-24 11:18:22 -04:00
connect ( & countDownTimer , & QTimer : : timeout , this , & SendConfirmationDialog : : countDown ) ;
2016-05-09 23:26:57 -03:00
}
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 ) ;
2019-09-26 06:31:49 -03:00
yesButton - > setText ( confirmButtonText + " ( " + QString : : number ( secDelay ) + " ) " ) ;
2016-05-09 23:26:57 -03:00
}
else
{
yesButton - > setEnabled ( true ) ;
2019-09-26 06:31:49 -03:00
yesButton - > setText ( confirmButtonText ) ;
2016-05-09 23:26:57 -03:00
}
}