2013-11-04 16:20:43 +01:00
// Copyright (c) 2011-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2011-05-12 14:49:42 +02:00
# include "sendcoinsdialog.h"
2011-05-12 14:44:52 +02:00
# include "ui_sendcoinsdialog.h"
2013-01-23 21:51:02 +01:00
2011-07-25 21:35:45 +02:00
# include "bitcoinunits.h"
2013-04-13 07:13:08 +02:00
# include "guiutil.h"
2011-06-01 09:34:12 +02:00
# include "optionsmodel.h"
2011-07-16 19:01:05 +02:00
# include "sendcoinsentry.h"
2013-04-13 07:13:08 +02:00
# include "walletmodel.h"
2012-08-07 19:19:14 +02:00
# include "base58.h"
2013-10-30 15:37:41 +01:00
# include "ui_interface.h"
2011-05-12 17:55:24 +02:00
2011-05-15 19:31:20 +02:00
# include <QMessageBox>
2011-12-07 06:00:04 +01:00
# include <QScrollBar>
2013-04-13 07:13:08 +02:00
# include <QTextDocument>
2011-05-12 20:16:42 +02:00
2011-07-16 19:01:05 +02:00
SendCoinsDialog : : SendCoinsDialog ( QWidget * parent ) :
2011-05-12 14:44:52 +02:00
QDialog ( parent ) ,
2011-05-30 20:20:12 +02:00
ui ( new Ui : : SendCoinsDialog ) ,
model ( 0 )
2011-05-12 14:44:52 +02:00
{
ui - > setupUi ( this ) ;
2011-07-08 19:25:35 +02:00
2012-09-21 19:06:53 +02:00
# ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
2011-10-07 13:21:45 +02:00
ui - > addButton - > setIcon ( QIcon ( ) ) ;
ui - > clearButton - > setIcon ( QIcon ( ) ) ;
ui - > sendButton - > setIcon ( QIcon ( ) ) ;
# endif
2011-07-16 19:01:05 +02:00
addEntry ( ) ;
2011-05-15 19:31:20 +02:00
2011-07-16 19:01:05 +02:00
connect ( ui - > addButton , SIGNAL ( clicked ( ) ) , this , SLOT ( addEntry ( ) ) ) ;
2011-09-27 16:46:19 +02:00
connect ( ui - > clearButton , SIGNAL ( clicked ( ) ) , this , SLOT ( clear ( ) ) ) ;
2011-12-24 05:27:12 +01:00
fNewRecipientAllowed = true ;
2011-05-12 14:44:52 +02:00
}
2011-06-30 18:05:29 +02:00
void SendCoinsDialog : : setModel ( WalletModel * model )
2011-05-30 20:20:12 +02:00
{
this - > model = model ;
2011-07-16 19:01:05 +02:00
2013-08-23 13:07:20 +02:00
if ( model & & model - > getOptionsModel ( ) )
2011-07-16 19:01:05 +02:00
{
2013-08-23 13:07:20 +02:00
for ( int i = 0 ; i < ui - > entries - > count ( ) ; + + i )
2011-07-16 19:01:05 +02:00
{
2013-08-23 13:07:20 +02:00
SendCoinsEntry * entry = qobject_cast < SendCoinsEntry * > ( ui - > entries - > itemAt ( i ) - > widget ( ) ) ;
if ( entry )
{
entry - > setModel ( model ) ;
}
2011-07-16 19:01:05 +02:00
}
2013-08-23 13:07:20 +02:00
2012-02-14 12:08:00 +01:00
setBalance ( model - > getBalance ( ) , model - > getUnconfirmedBalance ( ) , model - > getImmatureBalance ( ) ) ;
connect ( model , SIGNAL ( balanceChanged ( qint64 , qint64 , qint64 ) ) , this , SLOT ( setBalance ( qint64 , qint64 , qint64 ) ) ) ;
2012-06-09 15:41:21 +02:00
connect ( model - > getOptionsModel ( ) , SIGNAL ( displayUnitChanged ( int ) ) , this , SLOT ( updateDisplayUnit ( ) ) ) ;
2011-11-08 21:18:36 +01:00
}
2011-05-30 20:20:12 +02:00
}
2011-05-12 14:44:52 +02:00
SendCoinsDialog : : ~ SendCoinsDialog ( )
{
delete ui ;
}
2011-05-12 17:55:24 +02:00
void SendCoinsDialog : : on_sendButton_clicked ( )
{
2013-08-24 15:07:17 +02:00
if ( ! model | | ! model - > getOptionsModel ( ) )
return ;
2011-07-16 19:01:05 +02:00
QList < SendCoinsRecipient > recipients ;
bool valid = true ;
2011-11-08 21:18:36 +01:00
2011-07-16 19:01:05 +02:00
for ( int i = 0 ; i < ui - > entries - > count ( ) ; + + i )
{
SendCoinsEntry * entry = qobject_cast < SendCoinsEntry * > ( ui - > entries - > itemAt ( i ) - > widget ( ) ) ;
if ( entry )
{
if ( entry - > validate ( ) )
{
recipients . append ( entry - > getValue ( ) ) ;
}
else
{
valid = false ;
}
}
}
2011-05-30 20:20:12 +02:00
2011-07-16 19:01:05 +02:00
if ( ! valid | | recipients . isEmpty ( ) )
2011-05-14 17:25:05 +02:00
{
2011-05-15 19:31:20 +02:00
return ;
2011-05-14 17:25:05 +02:00
}
2011-05-27 21:43:05 +02:00
2011-07-16 19:01:05 +02:00
// Format confirmation message
QStringList formatted ;
foreach ( const SendCoinsRecipient & rcp , recipients )
{
2013-09-13 16:49:35 +02:00
// generate bold amount string
QString amount = " <b> " + BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , rcp . amount ) ;
amount . append ( " </b> " ) ;
// generate monospace address string
QString address = " <span style='font-family: monospace;'> " + rcp . address ;
address . append ( " </span> " ) ;
QString recipientElement ;
2013-10-24 16:02:39 +02:00
if ( ! rcp . paymentRequest . IsInitialized ( ) ) // normal payment
2013-07-22 08:50:39 +02:00
{
2013-09-13 16:49:35 +02:00
if ( rcp . label . length ( ) > 0 ) // label with address
2013-08-30 20:04:48 +02:00
{
2013-09-13 16:49:35 +02:00
recipientElement = tr ( " %1 to %2 " ) . arg ( amount , GUIUtil : : HtmlEscape ( rcp . label ) ) ;
recipientElement . append ( QString ( " (%1) " ) . arg ( address ) ) ;
2013-08-30 20:04:48 +02:00
}
2013-09-13 16:49:35 +02:00
else // just address
2013-08-30 20:04:48 +02:00
{
2013-09-13 16:49:35 +02:00
recipientElement = tr ( " %1 to %2 " ) . arg ( amount , address ) ;
2013-08-30 20:04:48 +02:00
}
2013-07-22 08:50:39 +02:00
}
2013-10-24 16:02:39 +02:00
else if ( ! rcp . authenticatedMerchant . isEmpty ( ) ) // secure payment request
2013-07-22 08:50:39 +02:00
{
2013-09-13 16:49:35 +02:00
recipientElement = tr ( " %1 to %2 " ) . arg ( amount , GUIUtil : : HtmlEscape ( rcp . authenticatedMerchant ) ) ;
2013-07-22 08:50:39 +02:00
}
2013-10-24 16:02:39 +02:00
else // insecure payment request
{
recipientElement = tr ( " %1 to %2 " ) . arg ( amount , address ) ;
}
2013-09-13 16:49:35 +02:00
formatted . append ( recipientElement ) ;
2011-07-16 19:01:05 +02:00
}
2011-06-25 19:32:36 +02:00
2011-12-24 05:27:12 +01:00
fNewRecipientAllowed = false ;
2011-07-07 17:33:15 +02:00
2011-08-24 22:07:26 +02:00
WalletModel : : UnlockContext ctx ( model - > requestUnlock ( ) ) ;
if ( ! ctx . isValid ( ) )
{
// Unlock wallet was cancelled
2011-12-24 05:27:12 +01:00
fNewRecipientAllowed = true ;
2011-08-24 22:07:26 +02:00
return ;
}
2013-08-30 20:04:48 +02:00
// prepare transaction for getting txFee earlier
WalletModelTransaction currentTransaction ( recipients ) ;
WalletModel : : SendCoinsReturn prepareStatus = model - > prepareTransaction ( currentTransaction ) ;
2013-10-30 15:37:41 +01:00
// process prepareStatus and on error generate message shown to user
processSendCoinsReturn ( prepareStatus ,
BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , currentTransaction . getTransactionFee ( ) ) ) ;
2013-08-30 20:04:48 +02:00
if ( prepareStatus . status ! = WalletModel : : OK ) {
fNewRecipientAllowed = true ;
return ;
}
qint64 txFee = currentTransaction . getTransactionFee ( ) ;
QString questionString = tr ( " Are you sure you want to send? " ) ;
questionString . append ( " <br /><br />%1 " ) ;
if ( txFee > 0 )
{
// append fee string if a fee is required
questionString . append ( " <hr /><span style='color:#aa0000;'> " ) ;
questionString . append ( BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , txFee ) ) ;
questionString . append ( " </span> " ) ;
questionString . append ( tr ( " added as transaction fee " ) ) ;
}
if ( txFee > 0 | | recipients . count ( ) > 1 )
{
// add total amount string if there are more then one recipients or a fee is required
questionString . append ( " <hr /> " ) ;
questionString . append ( tr ( " Total Amount %1 " ) . arg ( BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , currentTransaction . getTotalTransactionAmount ( ) + txFee ) ) ) ;
}
QMessageBox : : StandardButton retval = QMessageBox : : question ( this , tr ( " Confirm send coins " ) ,
questionString . arg ( formatted . join ( " <br /> " ) ) ,
2013-09-13 16:49:35 +02:00
QMessageBox : : Yes | QMessageBox : : Cancel ,
2013-08-30 20:04:48 +02:00
QMessageBox : : Cancel ) ;
if ( retval ! = QMessageBox : : Yes )
{
fNewRecipientAllowed = true ;
return ;
}
// now send the prepared transaction
2013-10-30 15:37:41 +01:00
WalletModel : : SendCoinsReturn sendStatus = model - > sendCoins ( currentTransaction ) ;
// process sendStatus and on error generate message shown to user
processSendCoinsReturn ( sendStatus ) ;
if ( sendStatus . status = = WalletModel : : OK )
2013-08-30 20:04:48 +02:00
{
2011-05-31 22:24:53 +02:00
accept ( ) ;
2011-05-14 17:25:05 +02:00
}
2011-12-24 05:27:12 +01:00
fNewRecipientAllowed = true ;
2011-05-12 17:55:24 +02:00
}
2011-07-16 19:01:05 +02:00
void SendCoinsDialog : : clear ( )
2011-05-12 17:55:24 +02:00
{
2011-07-16 19:01:05 +02:00
// Remove entries until only one left
2011-08-07 16:04:48 +02:00
while ( ui - > entries - > count ( ) )
2011-07-16 19:01:05 +02:00
{
delete ui - > entries - > takeAt ( 0 ) - > widget ( ) ;
}
2011-08-07 16:04:48 +02:00
addEntry ( ) ;
2011-05-13 22:00:27 +02:00
2011-07-16 19:01:05 +02:00
updateRemoveEnabled ( ) ;
2011-07-07 17:33:15 +02:00
2011-07-08 19:51:24 +02:00
ui - > sendButton - > setDefault ( true ) ;
2011-07-07 17:33:15 +02:00
}
void SendCoinsDialog : : reject ( )
{
clear ( ) ;
}
void SendCoinsDialog : : accept ( )
{
clear ( ) ;
}
2011-07-16 19:01:05 +02:00
2011-08-07 16:04:48 +02:00
SendCoinsEntry * SendCoinsDialog : : addEntry ( )
2011-07-16 19:01:05 +02:00
{
SendCoinsEntry * entry = new SendCoinsEntry ( this ) ;
entry - > setModel ( model ) ;
ui - > entries - > addWidget ( entry ) ;
connect ( entry , SIGNAL ( removeEntry ( SendCoinsEntry * ) ) , this , SLOT ( removeEntry ( SendCoinsEntry * ) ) ) ;
updateRemoveEnabled ( ) ;
// Focus the field, so that entry can start immediately
entry - > clear ( ) ;
2011-12-07 06:00:04 +01:00
entry - > setFocus ( ) ;
ui - > scrollAreaWidgetContents - > resize ( ui - > scrollAreaWidgetContents - > sizeHint ( ) ) ;
2013-04-02 17:30:14 +02:00
qApp - > processEvents ( ) ;
2011-12-07 06:00:04 +01:00
QScrollBar * bar = ui - > scrollArea - > verticalScrollBar ( ) ;
2012-06-09 15:41:21 +02:00
if ( bar )
2011-12-07 06:00:04 +01:00
bar - > setSliderPosition ( bar - > maximum ( ) ) ;
2011-08-07 16:04:48 +02:00
return entry ;
2011-07-16 19:01:05 +02:00
}
void SendCoinsDialog : : updateRemoveEnabled ( )
{
// Remove buttons are enabled as soon as there is more than one send-entry
bool enabled = ( ui - > entries - > count ( ) > 1 ) ;
for ( int i = 0 ; i < ui - > entries - > count ( ) ; + + i )
{
SendCoinsEntry * entry = qobject_cast < SendCoinsEntry * > ( ui - > entries - > itemAt ( i ) - > widget ( ) ) ;
if ( entry )
{
entry - > setRemoveEnabled ( enabled ) ;
}
}
setupTabChain ( 0 ) ;
}
void SendCoinsDialog : : removeEntry ( SendCoinsEntry * entry )
{
delete entry ;
updateRemoveEnabled ( ) ;
}
QWidget * SendCoinsDialog : : setupTabChain ( QWidget * prev )
{
for ( int i = 0 ; i < ui - > entries - > count ( ) ; + + i )
{
SendCoinsEntry * entry = qobject_cast < SendCoinsEntry * > ( ui - > entries - > itemAt ( i ) - > widget ( ) ) ;
if ( entry )
{
prev = entry - > setupTabChain ( prev ) ;
}
}
QWidget : : setTabOrder ( prev , ui - > addButton ) ;
QWidget : : setTabOrder ( ui - > addButton , ui - > sendButton ) ;
return ui - > sendButton ;
}
2011-08-07 16:04:48 +02:00
2013-01-25 18:46:53 +01:00
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 < SendCoinsEntry * > ( ui - > entries - > itemAt ( 0 ) - > widget ( ) ) ;
if ( first - > isClear ( ) )
{
entry = first ;
}
}
if ( ! entry )
{
entry = addEntry ( ) ;
}
entry - > setAddress ( address ) ;
}
2011-08-07 16:04:48 +02:00
void SendCoinsDialog : : pasteEntry ( const SendCoinsRecipient & rv )
{
2012-06-09 15:41:21 +02:00
if ( ! fNewRecipientAllowed )
2011-12-24 05:27:12 +01:00
return ;
2011-08-07 16:04:48 +02:00
SendCoinsEntry * entry = 0 ;
// Replace the first entry if it is still unused
if ( ui - > entries - > count ( ) = = 1 )
{
SendCoinsEntry * first = qobject_cast < SendCoinsEntry * > ( ui - > entries - > itemAt ( 0 ) - > widget ( ) ) ;
if ( first - > isClear ( ) )
{
entry = first ;
}
}
if ( ! entry )
{
entry = addEntry ( ) ;
}
entry - > setValue ( rv ) ;
}
2013-07-22 08:50:39 +02:00
bool SendCoinsDialog : : handlePaymentRequest ( const SendCoinsRecipient & rv )
2011-08-07 16:04:48 +02:00
{
2013-09-13 16:49:35 +02:00
QString strSendCoins = tr ( " Send Coins " ) ;
2013-10-24 16:02:39 +02:00
if ( rv . paymentRequest . IsInitialized ( ) ) {
2013-07-22 08:50:39 +02:00
// Expired payment request?
const payments : : PaymentDetails & details = rv . paymentRequest . getDetails ( ) ;
2013-04-13 07:13:08 +02:00
if ( details . has_expires ( ) & & ( int64_t ) details . expires ( ) < GetTime ( ) )
2013-07-22 08:50:39 +02:00
{
2013-10-30 15:37:41 +01:00
emit message ( strSendCoins , tr ( " Payment request expired " ) ,
CClientUIInterface : : MSG_WARNING ) ;
2013-07-22 08:50:39 +02:00
return false ;
}
}
else {
2012-08-07 19:19:14 +02:00
CBitcoinAddress address ( rv . address . toStdString ( ) ) ;
2013-07-22 08:50:39 +02:00
if ( ! address . IsValid ( ) ) {
2013-10-30 15:37:41 +01:00
emit message ( strSendCoins , tr ( " Invalid payment address %1 " ) . arg ( rv . address ) ,
CClientUIInterface : : MSG_WARNING ) ;
2012-08-07 19:19:14 +02:00
return false ;
2013-07-22 08:50:39 +02:00
}
2011-08-07 16:04:48 +02:00
}
2012-03-28 14:55:29 +02:00
2013-07-22 08:50:39 +02:00
pasteEntry ( rv ) ;
return true ;
2011-08-07 16:04:48 +02:00
}
2011-09-22 19:02:01 +02:00
2012-02-14 12:08:00 +01:00
void SendCoinsDialog : : setBalance ( qint64 balance , qint64 unconfirmedBalance , qint64 immatureBalance )
2011-09-22 19:02:01 +02:00
{
Q_UNUSED ( unconfirmedBalance ) ;
2012-02-14 12:08:00 +01:00
Q_UNUSED ( immatureBalance ) ;
2011-11-08 21:18:36 +01:00
2013-08-24 15:07:17 +02:00
if ( model & & model - > getOptionsModel ( ) )
{
ui - > labelBalance - > setText ( BitcoinUnits : : formatWithUnit ( model - > getOptionsModel ( ) - > getDisplayUnit ( ) , balance ) ) ;
}
2011-09-22 19:02:01 +02:00
}
2012-06-09 15:41:21 +02:00
void SendCoinsDialog : : updateDisplayUnit ( )
{
2013-08-24 15:07:17 +02:00
setBalance ( model - > getBalance ( ) , 0 , 0 ) ;
2012-06-09 15:41:21 +02:00
}
2013-10-30 15:37:41 +01: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.
// 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, can only send to each address once per send operation. " ) ;
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! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here. " ) ;
msgParams . second = CClientUIInterface : : MSG_ERROR ;
break ;
// OK and Aborted are included to prevent a compiler warning.
case WalletModel : : OK :
case WalletModel : : Aborted :
default :
return ;
}
emit message ( tr ( " Send Coins " ) , msgParams . first , msgParams . second ) ;
}