mirror of
				https://git.wownero.com/wowlet/wowlet.git
				synced 2024-08-15 01:03:14 +00:00 
			
		
		
		
	Multi destination transactions
This commit is contained in:
		
							parent
							
								
									9fc77f3bc9
								
							
						
					
					
						commit
						045d9ec2d2
					
				
					 18 changed files with 411 additions and 45 deletions
				
			
		| 
						 | 
					@ -178,7 +178,7 @@ void AppContext::initWS() {
 | 
				
			||||||
    this->ws->start();
 | 
					    this->ws->start();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void AppContext::onCancelTransaction(PendingTransaction *tx, const QString &address) {
 | 
					void AppContext::onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address) {
 | 
				
			||||||
    // tx cancelled by user
 | 
					    // tx cancelled by user
 | 
				
			||||||
    double amount = tx->amount() / globals::cdiv;
 | 
					    double amount = tx->amount() / globals::cdiv;
 | 
				
			||||||
    emit createTransactionCancelled(address, amount);
 | 
					    emit createTransactionCancelled(address, amount);
 | 
				
			||||||
| 
						 | 
					@ -239,6 +239,30 @@ void AppContext::onCreateTransaction(const QString &address, quint64 amount, con
 | 
				
			||||||
    emit initiateTransaction();
 | 
					    emit initiateTransaction();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AppContext::onCreateTransactionMultiDest(const QVector<QString> &addresses, const QVector<quint64> &amounts, const QString &description) {
 | 
				
			||||||
 | 
					    this->tmpTxDescription = description;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this->currentWallet == nullptr) {
 | 
				
			||||||
 | 
					        emit createTransactionError("Cannot create transaction; no wallet loaded");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    quint64 total_amount = 0;
 | 
				
			||||||
 | 
					    for (auto &amount : amounts) {
 | 
				
			||||||
 | 
					        total_amount += amount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto unlocked_balance = this->currentWallet->unlockedBalance();
 | 
				
			||||||
 | 
					    if (total_amount > unlocked_balance) {
 | 
				
			||||||
 | 
					        emit createTransactionError("Not enough money to spend");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    qDebug() << "Creating tx";
 | 
				
			||||||
 | 
					    this->currentWallet->createTransactionMultiDestAsync(addresses, amounts, this->tx_priority);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    emit initiateTransaction();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void AppContext::onCreateTransactionError(const QString &msg) {
 | 
					void AppContext::onCreateTransactionError(const QString &msg) {
 | 
				
			||||||
    this->tmpTxDescription = "";
 | 
					    this->tmpTxDescription = "";
 | 
				
			||||||
    emit endTransaction();
 | 
					    emit endTransaction();
 | 
				
			||||||
| 
						 | 
					@ -750,15 +774,18 @@ void AppContext::onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, q
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void AppContext::onTransactionCreated(PendingTransaction *tx, const QString &address, const QString &paymentId, quint32 mixin) {
 | 
					void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector<QString> &address) {
 | 
				
			||||||
    if(address == this->donationAddress)
 | 
					    for (auto &addr : address) {
 | 
				
			||||||
 | 
					        if (addr == this->donationAddress) {
 | 
				
			||||||
            this->donationSending = true;
 | 
					            this->donationSending = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Let UI know that the transaction was constructed
 | 
					    // Let UI know that the transaction was constructed
 | 
				
			||||||
    emit endTransaction();
 | 
					    emit endTransaction();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // tx created, but not sent yet. ask user to verify first.
 | 
					    // tx created, but not sent yet. ask user to verify first.
 | 
				
			||||||
    emit createTransactionSuccess(tx, address, mixin);
 | 
					    emit createTransactionSuccess(tx, address);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){
 | 
					void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -115,7 +115,8 @@ public slots:
 | 
				
			||||||
    void onOpenWallet(const QString& path, const QString &password);
 | 
					    void onOpenWallet(const QString& path, const QString &password);
 | 
				
			||||||
    void onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all);
 | 
					    void onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all);
 | 
				
			||||||
    void onCreateTransaction(XmrToOrder *order);
 | 
					    void onCreateTransaction(XmrToOrder *order);
 | 
				
			||||||
    void onCancelTransaction(PendingTransaction *tx, const QString &address);
 | 
					    void onCreateTransactionMultiDest(const QVector<QString> &addresses, const QVector<quint64> &amounts, const QString &description);
 | 
				
			||||||
 | 
					    void onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address);
 | 
				
			||||||
    void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const;
 | 
					    void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const;
 | 
				
			||||||
    void onCreateTransactionError(const QString &msg);
 | 
					    void onCreateTransactionError(const QString &msg);
 | 
				
			||||||
    void onOpenAliasResolve(const QString &openAlias);
 | 
					    void onOpenAliasResolve(const QString &openAlias);
 | 
				
			||||||
| 
						 | 
					@ -136,7 +137,7 @@ private slots:
 | 
				
			||||||
    void onWalletOpened(Wallet *wallet);
 | 
					    void onWalletOpened(Wallet *wallet);
 | 
				
			||||||
    void onWalletNewBlock(quint64 blockheight, quint64 targetHeight);
 | 
					    void onWalletNewBlock(quint64 blockheight, quint64 targetHeight);
 | 
				
			||||||
    void onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight);
 | 
					    void onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight);
 | 
				
			||||||
    void onTransactionCreated(PendingTransaction *tx, const QString &address, const QString &paymentId, quint32 mixin);
 | 
					    void onTransactionCreated(PendingTransaction *tx, const QVector<QString> &address);
 | 
				
			||||||
    void onTransactionCommitted(bool status, PendingTransaction *t, const QStringList& txid);
 | 
					    void onTransactionCommitted(bool status, PendingTransaction *t, const QStringList& txid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
| 
						 | 
					@ -159,8 +160,8 @@ signals:
 | 
				
			||||||
    void walletOpenPasswordNeeded(bool invalidPassword, QString path);
 | 
					    void walletOpenPasswordNeeded(bool invalidPassword, QString path);
 | 
				
			||||||
    void transactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid);
 | 
					    void transactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid);
 | 
				
			||||||
    void createTransactionError(QString message);
 | 
					    void createTransactionError(QString message);
 | 
				
			||||||
    void createTransactionCancelled(QString address, double amount);
 | 
					    void createTransactionCancelled(const QVector<QString> &address, double amount);
 | 
				
			||||||
    void createTransactionSuccess(PendingTransaction *tx, const QString &address, const quint32 &mixin);
 | 
					    void createTransactionSuccess(PendingTransaction *tx, const QVector<QString> &address);
 | 
				
			||||||
    void redditUpdated(QList<QSharedPointer<RedditPost>> &posts);
 | 
					    void redditUpdated(QList<QSharedPointer<RedditPost>> &posts);
 | 
				
			||||||
    void nodesUpdated(QList<QSharedPointer<FeatherNode>> &nodes);
 | 
					    void nodesUpdated(QList<QSharedPointer<FeatherNode>> &nodes);
 | 
				
			||||||
    void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries);
 | 
					    void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -185,17 +185,11 @@
 | 
				
			||||||
   <item>
 | 
					   <item>
 | 
				
			||||||
    <widget class="QTextEdit" name="outputs">
 | 
					    <widget class="QTextEdit" name="outputs">
 | 
				
			||||||
     <property name="sizePolicy">
 | 
					     <property name="sizePolicy">
 | 
				
			||||||
      <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
 | 
					      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
 | 
				
			||||||
       <horstretch>0</horstretch>
 | 
					       <horstretch>0</horstretch>
 | 
				
			||||||
       <verstretch>0</verstretch>
 | 
					       <verstretch>0</verstretch>
 | 
				
			||||||
      </sizepolicy>
 | 
					      </sizepolicy>
 | 
				
			||||||
     </property>
 | 
					     </property>
 | 
				
			||||||
     <property name="maximumSize">
 | 
					 | 
				
			||||||
      <size>
 | 
					 | 
				
			||||||
       <width>16777215</width>
 | 
					 | 
				
			||||||
       <height>100</height>
 | 
					 | 
				
			||||||
      </size>
 | 
					 | 
				
			||||||
     </property>
 | 
					 | 
				
			||||||
     <property name="readOnly">
 | 
					     <property name="readOnly">
 | 
				
			||||||
      <bool>true</bool>
 | 
					      <bool>true</bool>
 | 
				
			||||||
     </property>
 | 
					     </property>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,14 +9,13 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <QMessageBox>
 | 
					#include <QMessageBox>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TxConfDialog::TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QString &address, const QString &description, int mixin, QWidget *parent)
 | 
					TxConfDialog::TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QString &address, const QString &description, QWidget *parent)
 | 
				
			||||||
        : QDialog(parent)
 | 
					        : QDialog(parent)
 | 
				
			||||||
        , ui(new Ui::TxConfDialog)
 | 
					        , ui(new Ui::TxConfDialog)
 | 
				
			||||||
        , m_ctx(ctx)
 | 
					        , m_ctx(ctx)
 | 
				
			||||||
        , m_tx(tx)
 | 
					        , m_tx(tx)
 | 
				
			||||||
        , m_address(address)
 | 
					        , m_address(address)
 | 
				
			||||||
        , m_description(description)
 | 
					        , m_description(description)
 | 
				
			||||||
        , m_mixin(mixin)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    ui->setupUi(this);
 | 
					    ui->setupUi(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ class TxConfDialog : public QDialog
 | 
				
			||||||
Q_OBJECT
 | 
					Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    explicit TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QString &address, const QString &description, int mixin, QWidget *parent = nullptr);
 | 
					    explicit TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QString &address, const QString &description, QWidget *parent = nullptr);
 | 
				
			||||||
    ~TxConfDialog() override;
 | 
					    ~TxConfDialog() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool showAdvanced = false;
 | 
					    bool showAdvanced = false;
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,6 @@ private:
 | 
				
			||||||
    PendingTransaction *m_tx;
 | 
					    PendingTransaction *m_tx;
 | 
				
			||||||
    QString m_address;
 | 
					    QString m_address;
 | 
				
			||||||
    QString m_description;
 | 
					    QString m_description;
 | 
				
			||||||
    int m_mixin;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif //FEATHER_TXCONFDIALOG_H
 | 
					#endif //FEATHER_TXCONFDIALOG_H
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -591,7 +591,40 @@ void Wallet::createTransactionAsync(const QString &dst_addr, const QString &paym
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    m_scheduler.run([this, dst_addr, payment_id, amount, mixin_count, priority] {
 | 
					    m_scheduler.run([this, dst_addr, payment_id, amount, mixin_count, priority] {
 | 
				
			||||||
        PendingTransaction *tx = createTransaction(dst_addr, payment_id, amount, mixin_count, priority);
 | 
					        PendingTransaction *tx = createTransaction(dst_addr, payment_id, amount, mixin_count, priority);
 | 
				
			||||||
        emit transactionCreated(tx, dst_addr, payment_id, mixin_count);
 | 
					        QVector<QString> address {dst_addr};
 | 
				
			||||||
 | 
					        emit transactionCreated(tx, address);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PendingTransaction* Wallet::createTransactionMultiDest(const QVector<QString> &dst_addr, const QVector<quint64> &amount,
 | 
				
			||||||
 | 
					                                                       PendingTransaction::Priority priority)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::vector<std::string> dests;
 | 
				
			||||||
 | 
					    for (auto &addr : dst_addr) {
 | 
				
			||||||
 | 
					        dests.push_back(addr.toStdString());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<uint64_t> amounts;
 | 
				
			||||||
 | 
					    for (auto &a : amount) {
 | 
				
			||||||
 | 
					        amounts.push_back(a);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO: remove mixin count
 | 
				
			||||||
 | 
					    Monero::PendingTransaction * ptImpl = m_walletImpl->createTransactionMultDest(dests, "", amounts, 11, static_cast<Monero::PendingTransaction::Priority>(priority));
 | 
				
			||||||
 | 
					    PendingTransaction * result = new PendingTransaction(ptImpl);
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Wallet::createTransactionMultiDestAsync(const QVector<QString> &dst_addr, const QVector<quint64> &amount,
 | 
				
			||||||
 | 
					                                             PendingTransaction::Priority priority)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    m_scheduler.run([this, dst_addr, amount, priority] {
 | 
				
			||||||
 | 
					        PendingTransaction *tx = createTransactionMultiDest(dst_addr, amount, priority);
 | 
				
			||||||
 | 
					        QVector<QString> addresses;
 | 
				
			||||||
 | 
					        for (auto &addr : dst_addr) {
 | 
				
			||||||
 | 
					            addresses.push_back(addr);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        emit transactionCreated(tx, addresses);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -612,7 +645,8 @@ void Wallet::createTransactionAllAsync(const QString &dst_addr, const QString &p
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    m_scheduler.run([this, dst_addr, payment_id, mixin_count, priority] {
 | 
					    m_scheduler.run([this, dst_addr, payment_id, mixin_count, priority] {
 | 
				
			||||||
        PendingTransaction *tx = createTransactionAll(dst_addr, payment_id, mixin_count, priority);
 | 
					        PendingTransaction *tx = createTransactionAll(dst_addr, payment_id, mixin_count, priority);
 | 
				
			||||||
        emit transactionCreated(tx, dst_addr, payment_id, mixin_count);
 | 
					        QVector<QString> address {dst_addr};
 | 
				
			||||||
 | 
					        emit transactionCreated(tx, address);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -630,7 +664,8 @@ void Wallet::createTransactionSingleAsync(const QString &key_image, const QStrin
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    m_scheduler.run([this, key_image, dst_addr, outputs, priority] {
 | 
					    m_scheduler.run([this, key_image, dst_addr, outputs, priority] {
 | 
				
			||||||
        PendingTransaction *tx = createTransactionSingle(key_image, dst_addr, outputs, priority);
 | 
					        PendingTransaction *tx = createTransactionSingle(key_image, dst_addr, outputs, priority);
 | 
				
			||||||
        emit transactionCreated(tx, dst_addr, "", 10); // todo: return true mixincount
 | 
					        QVector<QString> address {dst_addr};
 | 
				
			||||||
 | 
					        emit transactionCreated(tx, address);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -645,7 +680,8 @@ void Wallet::createSweepUnmixableTransactionAsync()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    m_scheduler.run([this] {
 | 
					    m_scheduler.run([this] {
 | 
				
			||||||
        PendingTransaction *tx = createSweepUnmixableTransaction();
 | 
					        PendingTransaction *tx = createSweepUnmixableTransaction();
 | 
				
			||||||
        emit transactionCreated(tx, "", "", 0);
 | 
					        QVector<QString> address {""};
 | 
				
			||||||
 | 
					        emit transactionCreated(tx, address);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -266,6 +266,15 @@ public:
 | 
				
			||||||
                                            quint64 amount, quint32 mixin_count,
 | 
					                                            quint64 amount, quint32 mixin_count,
 | 
				
			||||||
                                            PendingTransaction::Priority priority);
 | 
					                                            PendingTransaction::Priority priority);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //! creates multi-destination transaction
 | 
				
			||||||
 | 
					    Q_INVOKABLE PendingTransaction * createTransactionMultiDest(const QVector<QString> &dst_addr, const QVector<quint64> &amount,
 | 
				
			||||||
 | 
					                                                                PendingTransaction::Priority priority);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //! creates async multi-destination transaction
 | 
				
			||||||
 | 
					    Q_INVOKABLE void createTransactionMultiDestAsync(const QVector<QString> &dst_addr, const QVector<quint64> &amount,
 | 
				
			||||||
 | 
					                                                     PendingTransaction::Priority priority);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //! creates transaction with all outputs
 | 
					    //! creates transaction with all outputs
 | 
				
			||||||
    Q_INVOKABLE PendingTransaction * createTransactionAll(const QString &dst_addr, const QString &payment_id,
 | 
					    Q_INVOKABLE PendingTransaction * createTransactionAll(const QString &dst_addr, const QString &payment_id,
 | 
				
			||||||
                                                          quint32 mixin_count, PendingTransaction::Priority priority);
 | 
					                                                          quint32 mixin_count, PendingTransaction::Priority priority);
 | 
				
			||||||
| 
						 | 
					@ -449,7 +458,7 @@ signals:
 | 
				
			||||||
    void deviceShowAddressShowed();
 | 
					    void deviceShowAddressShowed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // emitted when transaction is created async
 | 
					    // emitted when transaction is created async
 | 
				
			||||||
    void transactionCreated(PendingTransaction * transaction, QString address, QString paymentId, quint32 mixinCount);
 | 
					    void transactionCreated(PendingTransaction * transaction, QVector<QString> address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void connectionStatusChanged(int status) const;
 | 
					    void connectionStatusChanged(int status) const;
 | 
				
			||||||
    void currentSubaddressAccountChanged() const;
 | 
					    void currentSubaddressAccountChanged() const;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -161,6 +161,7 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qInstallMessageHandler(Utils::applicationLogHandler);
 | 
					    qInstallMessageHandler(Utils::applicationLogHandler);
 | 
				
			||||||
 | 
					    qRegisterMetaType<QVector<QString>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto *mainWindow = new MainWindow(ctx);
 | 
					    auto *mainWindow = new MainWindow(ctx);
 | 
				
			||||||
    return QApplication::exec();
 | 
					    return QApplication::exec();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -74,6 +74,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
 | 
				
			||||||
    connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::menuQuitClicked);
 | 
					    connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::menuQuitClicked);
 | 
				
			||||||
    connect(ui->actionSettings, &QAction::triggered, this, &MainWindow::menuSettingsClicked);
 | 
					    connect(ui->actionSettings, &QAction::triggered, this, &MainWindow::menuSettingsClicked);
 | 
				
			||||||
    connect(ui->actionCalculator, &QAction::triggered, this, &MainWindow::showCalcWindow);
 | 
					    connect(ui->actionCalculator, &QAction::triggered, this, &MainWindow::showCalcWindow);
 | 
				
			||||||
 | 
					    connect(ui->actionPay_to_many, &QAction::triggered, this, &MainWindow::payToMany);
 | 
				
			||||||
    connect(ui->actionWallet_cache_debug, &QAction::triggered, this, &MainWindow::showWalletCacheDebugDialog);
 | 
					    connect(ui->actionWallet_cache_debug, &QAction::triggered, this, &MainWindow::showWalletCacheDebugDialog);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
 | 
					#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
 | 
				
			||||||
| 
						 | 
					@ -153,6 +154,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Send widget
 | 
					    // Send widget
 | 
				
			||||||
    connect(ui->sendWidget, &SendWidget::createTransaction, m_ctx, QOverload<const QString &, quint64, const QString &, bool>::of(&AppContext::onCreateTransaction));
 | 
					    connect(ui->sendWidget, &SendWidget::createTransaction, m_ctx, QOverload<const QString &, quint64, const QString &, bool>::of(&AppContext::onCreateTransaction));
 | 
				
			||||||
 | 
					    connect(ui->sendWidget, &SendWidget::createTransactionMultiDest, m_ctx, &AppContext::onCreateTransactionMultiDest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Nodes
 | 
					    // Nodes
 | 
				
			||||||
    connect(m_ctx->nodes, &Nodes::nodeExhausted, this, &MainWindow::showNodeExhaustedMessage);
 | 
					    connect(m_ctx->nodes, &Nodes::nodeExhausted, this, &MainWindow::showNodeExhaustedMessage);
 | 
				
			||||||
| 
						 | 
					@ -737,7 +739,7 @@ void MainWindow::onConnectionStatusChanged(int status)
 | 
				
			||||||
    m_statusBtnConnectionStatusIndicator->setIcon(QIcon(statusIcon));
 | 
					    m_statusBtnConnectionStatusIndicator->setIcon(QIcon(statusIcon));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QString &address, const quint32 &mixin) {
 | 
					void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QVector<QString> &address) {
 | 
				
			||||||
    auto tx_status = tx->status();
 | 
					    auto tx_status = tx->status();
 | 
				
			||||||
    auto err = QString("Can't create transaction: ");
 | 
					    auto err = QString("Can't create transaction: ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -761,7 +763,16 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QStrin
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        const auto &description = m_ctx->tmpTxDescription;
 | 
					        const auto &description = m_ctx->tmpTxDescription;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto *dialog = new TxConfDialog(m_ctx, tx, address, description, mixin, this);
 | 
					        // Show advanced dialog on multi-destination transactions
 | 
				
			||||||
 | 
					        if (address.size() > 1) {
 | 
				
			||||||
 | 
					            auto *dialog_adv = new TxConfAdvDialog(m_ctx, description, this);
 | 
				
			||||||
 | 
					            dialog_adv->setTransaction(tx);
 | 
				
			||||||
 | 
					            dialog_adv->exec();
 | 
				
			||||||
 | 
					            dialog_adv->deleteLater();
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto *dialog = new TxConfDialog(m_ctx, tx, address[0], description, this);
 | 
				
			||||||
        switch (dialog->exec()) {
 | 
					        switch (dialog->exec()) {
 | 
				
			||||||
            case QDialog::Rejected:
 | 
					            case QDialog::Rejected:
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
| 
						 | 
					@ -1061,6 +1072,15 @@ void MainWindow::showCalcWindow() {
 | 
				
			||||||
    m_windowCalc->show();
 | 
					    m_windowCalc->show();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::payToMany() {
 | 
				
			||||||
 | 
					    ui->tabWidget->setCurrentIndex(Tabs::SEND);
 | 
				
			||||||
 | 
					    ui->sendWidget->payToMany();
 | 
				
			||||||
 | 
					    QMessageBox::information(this, "Pay to many", "Enter a list of outputs in the 'Pay to' field.\n"
 | 
				
			||||||
 | 
					                                                  "One output per line.\n"
 | 
				
			||||||
 | 
					                                                  "Format: address, amount\n"
 | 
				
			||||||
 | 
					                                                  "A maximum of 16 addresses may be specified.");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MainWindow::showSendScreen(const CCSEntry &entry) {
 | 
					void MainWindow::showSendScreen(const CCSEntry &entry) {
 | 
				
			||||||
    ui->sendWidget->fill(entry);
 | 
					    ui->sendWidget->fill(entry);
 | 
				
			||||||
    ui->tabWidget->setCurrentIndex(Tabs::SEND);
 | 
					    ui->tabWidget->setCurrentIndex(Tabs::SEND);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -104,6 +104,7 @@ public slots:
 | 
				
			||||||
    void showViewOnlyDialog();
 | 
					    void showViewOnlyDialog();
 | 
				
			||||||
    void donateButtonClicked();
 | 
					    void donateButtonClicked();
 | 
				
			||||||
    void showCalcWindow();
 | 
					    void showCalcWindow();
 | 
				
			||||||
 | 
					    void payToMany();
 | 
				
			||||||
    void showWalletCacheDebugDialog();
 | 
					    void showWalletCacheDebugDialog();
 | 
				
			||||||
    void showSendTab();
 | 
					    void showSendTab();
 | 
				
			||||||
    void showHistoryTab();
 | 
					    void showHistoryTab();
 | 
				
			||||||
| 
						 | 
					@ -141,7 +142,7 @@ public slots:
 | 
				
			||||||
    void onWalletClosed(WalletWizard::Page page = WalletWizard::Page_Menu);
 | 
					    void onWalletClosed(WalletWizard::Page page = WalletWizard::Page_Menu);
 | 
				
			||||||
    void onConnectionStatusChanged(int status);
 | 
					    void onConnectionStatusChanged(int status);
 | 
				
			||||||
    void onCreateTransactionError(const QString &message);
 | 
					    void onCreateTransactionError(const QString &message);
 | 
				
			||||||
    void onCreateTransactionSuccess(PendingTransaction *tx, const QString &address, const quint32 &mixin);
 | 
					    void onCreateTransactionSuccess(PendingTransaction *tx, const QVector<QString> &address);
 | 
				
			||||||
    void onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid);
 | 
					    void onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -190,7 +190,7 @@
 | 
				
			||||||
        <item>
 | 
					        <item>
 | 
				
			||||||
         <widget class="SendWidget" name="sendWidget" native="true">
 | 
					         <widget class="SendWidget" name="sendWidget" native="true">
 | 
				
			||||||
          <property name="sizePolicy">
 | 
					          <property name="sizePolicy">
 | 
				
			||||||
           <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
 | 
					           <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
 | 
				
			||||||
            <horstretch>0</horstretch>
 | 
					            <horstretch>0</horstretch>
 | 
				
			||||||
            <verstretch>0</verstretch>
 | 
					            <verstretch>0</verstretch>
 | 
				
			||||||
           </sizepolicy>
 | 
					           </sizepolicy>
 | 
				
			||||||
| 
						 | 
					@ -446,6 +446,7 @@
 | 
				
			||||||
    <addaction name="menuLoad_signed_transaction"/>
 | 
					    <addaction name="menuLoad_signed_transaction"/>
 | 
				
			||||||
    <addaction name="actionImport_transaction"/>
 | 
					    <addaction name="actionImport_transaction"/>
 | 
				
			||||||
    <addaction name="separator"/>
 | 
					    <addaction name="separator"/>
 | 
				
			||||||
 | 
					    <addaction name="actionPay_to_many"/>
 | 
				
			||||||
    <addaction name="actionCalculator"/>
 | 
					    <addaction name="actionCalculator"/>
 | 
				
			||||||
    <addaction name="actionCreateDesktopEntry"/>
 | 
					    <addaction name="actionCreateDesktopEntry"/>
 | 
				
			||||||
   </widget>
 | 
					   </widget>
 | 
				
			||||||
| 
						 | 
					@ -726,6 +727,11 @@
 | 
				
			||||||
    <string>Wallet cache debug</string>
 | 
					    <string>Wallet cache debug</string>
 | 
				
			||||||
   </property>
 | 
					   </property>
 | 
				
			||||||
  </action>
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionPay_to_many">
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>Pay to many</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 </widget>
 | 
					 </widget>
 | 
				
			||||||
 <layoutdefault spacing="6" margin="11"/>
 | 
					 <layoutdefault spacing="6" margin="11"/>
 | 
				
			||||||
 <customwidgets>
 | 
					 <customwidgets>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,8 +24,8 @@ SendWidget::SendWidget(QWidget *parent) :
 | 
				
			||||||
    connect(ui->btnClear, &QPushButton::clicked, this, &SendWidget::clearClicked);
 | 
					    connect(ui->btnClear, &QPushButton::clicked, this, &SendWidget::clearClicked);
 | 
				
			||||||
    connect(ui->btnMax, &QPushButton::clicked, this, &SendWidget::btnMaxClicked);
 | 
					    connect(ui->btnMax, &QPushButton::clicked, this, &SendWidget::btnMaxClicked);
 | 
				
			||||||
    connect(ui->comboCurrencySelection, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SendWidget::currencyComboChanged);
 | 
					    connect(ui->comboCurrencySelection, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SendWidget::currencyComboChanged);
 | 
				
			||||||
    connect(ui->lineAmount, &QLineEdit::textEdited, this, &SendWidget::amountEdited);
 | 
					    connect(ui->lineAmount, &QLineEdit::textChanged, this, &SendWidget::amountEdited);
 | 
				
			||||||
    connect(ui->lineAddress, &QLineEdit::textEdited, this, &SendWidget::addressEdited);
 | 
					    connect(ui->lineAddress, &QPlainTextEdit::textChanged, this, &SendWidget::addressEdited);
 | 
				
			||||||
    connect(ui->btn_openAlias, &QPushButton::clicked, this, &SendWidget::aliasClicked);
 | 
					    connect(ui->btn_openAlias, &QPushButton::clicked, this, &SendWidget::aliasClicked);
 | 
				
			||||||
    ui->label_conversionAmount->setText("");
 | 
					    ui->label_conversionAmount->setText("");
 | 
				
			||||||
    ui->label_conversionAmount->hide();
 | 
					    ui->label_conversionAmount->hide();
 | 
				
			||||||
| 
						 | 
					@ -41,6 +41,7 @@ SendWidget::SendWidget(QWidget *parent) :
 | 
				
			||||||
                                  "You will be able to review the transaction fee before the transaction is broadcast.\n\n"
 | 
					                                  "You will be able to review the transaction fee before the transaction is broadcast.\n\n"
 | 
				
			||||||
                                  "To send all your balance, click the Max button to the right.");
 | 
					                                  "To send all your balance, click the Max button to the right.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ui->lineAddress->setNetType(m_ctx->networkType);
 | 
				
			||||||
    this->setupComboBox();
 | 
					    this->setupComboBox();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,8 +51,22 @@ void SendWidget::currencyComboChanged(int index) {
 | 
				
			||||||
    this->amountEdited(amount);
 | 
					    this->amountEdited(amount);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SendWidget::addressEdited(const QString &text) {
 | 
					void SendWidget::addressEdited() {
 | 
				
			||||||
    text.contains(".") ? ui->btn_openAlias->show() : ui->btn_openAlias->hide();
 | 
					    QVector<PartialTxOutput> outputs = ui->lineAddress->getOutputs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool freezeAmounts = outputs.size() > 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ui->lineAmount->setReadOnly(freezeAmounts);
 | 
				
			||||||
 | 
					    ui->lineAmount->setFrame(!freezeAmounts);
 | 
				
			||||||
 | 
					    ui->btnMax->setDisabled(freezeAmounts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (outputs.size() > 0) {
 | 
				
			||||||
 | 
					        ui->lineAmount->setText(WalletManager::displayAmount(ui->lineAddress->getTotal()));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        ui->lineAmount->setText("");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ui->btn_openAlias->setVisible(ui->lineAddress->isOpenAlias());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SendWidget::amountEdited(const QString &text) {
 | 
					void SendWidget::amountEdited(const QString &text) {
 | 
				
			||||||
| 
						 | 
					@ -69,7 +84,9 @@ void SendWidget::fill(double amount) {
 | 
				
			||||||
void SendWidget::fill(const QString &address, const QString &description, double amount) {
 | 
					void SendWidget::fill(const QString &address, const QString &description, double amount) {
 | 
				
			||||||
    ui->lineDescription->setText(description);
 | 
					    ui->lineDescription->setText(description);
 | 
				
			||||||
    ui->lineAddress->setText(address);
 | 
					    ui->lineAddress->setText(address);
 | 
				
			||||||
    ui->lineAddress->setCursorPosition(0);
 | 
					
 | 
				
			||||||
 | 
					    ui->lineAddress->moveCursor(QTextCursor::Start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (amount > 0)
 | 
					    if (amount > 0)
 | 
				
			||||||
        ui->lineAmount->setText(QString::number(amount));
 | 
					        ui->lineAmount->setText(QString::number(amount));
 | 
				
			||||||
    this->updateConversionLabel();
 | 
					    this->updateConversionLabel();
 | 
				
			||||||
| 
						 | 
					@ -77,7 +94,7 @@ void SendWidget::fill(const QString &address, const QString &description, double
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SendWidget::fillAddress(const QString &address) {
 | 
					void SendWidget::fillAddress(const QString &address) {
 | 
				
			||||||
    ui->lineAddress->setText(address);
 | 
					    ui->lineAddress->setText(address);
 | 
				
			||||||
    ui->lineAddress->setCursorPosition(0);
 | 
					    ui->lineAddress->moveCursor(QTextCursor::Start);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SendWidget::sendClicked() {
 | 
					void SendWidget::sendClicked() {
 | 
				
			||||||
| 
						 | 
					@ -96,6 +113,35 @@ void SendWidget::sendClicked() {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QVector<PartialTxOutput> outputs = ui->lineAddress->getOutputs();
 | 
				
			||||||
 | 
					    QVector<PayToLineError> errors = ui->lineAddress->getErrors();
 | 
				
			||||||
 | 
					    if (errors.size() > 0 && ui->lineAddress->isMultiline()) {
 | 
				
			||||||
 | 
					        QString errorText;
 | 
				
			||||||
 | 
					        for (auto &error: errors) {
 | 
				
			||||||
 | 
					            errorText += QString("Line #%1:\n%2\n").arg(QString::number(error.idx + 1), error.error);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        QMessageBox::warning(this, "Warning", QString("Invalid lines found:\n\n%1").arg(errorText));
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (outputs.size() > 0) { // multi destination transaction
 | 
				
			||||||
 | 
					        if (outputs.size() > 16) {
 | 
				
			||||||
 | 
					            QMessageBox::warning(this, "Warning", "Maximum number of outputs (16) exceeded.");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        QVector<QString> addresses;
 | 
				
			||||||
 | 
					        QVector<quint64> amounts;
 | 
				
			||||||
 | 
					        for (auto &output : outputs) {
 | 
				
			||||||
 | 
					            addresses.push_back(output.address);
 | 
				
			||||||
 | 
					            amounts.push_back(output.amount);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        emit createTransactionMultiDest(addresses, amounts, description);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quint64 amount;
 | 
					    quint64 amount;
 | 
				
			||||||
    if (currency == "XMR") {
 | 
					    if (currency == "XMR") {
 | 
				
			||||||
        amount = this->amount();
 | 
					        amount = this->amount();
 | 
				
			||||||
| 
						 | 
					@ -193,6 +239,10 @@ void SendWidget::clearFields() {
 | 
				
			||||||
    ui->label_conversionAmount->clear();
 | 
					    ui->label_conversionAmount->clear();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SendWidget::payToMany() {
 | 
				
			||||||
 | 
					    ui->lineAddress->payToMany();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SendWidget::onWalletClosed() {
 | 
					void SendWidget::onWalletClosed() {
 | 
				
			||||||
    this->clearFields();
 | 
					    this->clearFields();
 | 
				
			||||||
    ui->btnSend->setEnabled(true);
 | 
					    ui->btnSend->setEnabled(true);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,6 +22,7 @@ public:
 | 
				
			||||||
    void fill(const QString &address, const QString& description, double amount = 0);
 | 
					    void fill(const QString &address, const QString& description, double amount = 0);
 | 
				
			||||||
    void fill(double amount);
 | 
					    void fill(double amount);
 | 
				
			||||||
    void clearFields();
 | 
					    void clearFields();
 | 
				
			||||||
 | 
					    void payToMany();
 | 
				
			||||||
    ~SendWidget() override;
 | 
					    ~SendWidget() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public slots:
 | 
					public slots:
 | 
				
			||||||
| 
						 | 
					@ -30,7 +31,7 @@ public slots:
 | 
				
			||||||
    void aliasClicked();
 | 
					    void aliasClicked();
 | 
				
			||||||
    void btnMaxClicked();
 | 
					    void btnMaxClicked();
 | 
				
			||||||
    void amountEdited(const QString &text);
 | 
					    void amountEdited(const QString &text);
 | 
				
			||||||
    void addressEdited(const QString &text);
 | 
					    void addressEdited();
 | 
				
			||||||
    void currencyComboChanged(int index);
 | 
					    void currencyComboChanged(int index);
 | 
				
			||||||
    void fillAddress(const QString &address);
 | 
					    void fillAddress(const QString &address);
 | 
				
			||||||
    void updateConversionLabel();
 | 
					    void updateConversionLabel();
 | 
				
			||||||
| 
						 | 
					@ -45,6 +46,7 @@ public slots:
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
    void resolveOpenAlias(const QString &address);
 | 
					    void resolveOpenAlias(const QString &address);
 | 
				
			||||||
    void createTransaction(const QString &address, quint64 amount, const QString &description, bool all);
 | 
					    void createTransaction(const QString &address, quint64 amount, const QString &description, bool all);
 | 
				
			||||||
 | 
					    void createTransactionMultiDest(const QVector<QString> &addresses, const QVector<quint64> &amounts, const QString &description);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    void setupComboBox();
 | 
					    void setupComboBox();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@
 | 
				
			||||||
    <x>0</x>
 | 
					    <x>0</x>
 | 
				
			||||||
    <y>0</y>
 | 
					    <y>0</y>
 | 
				
			||||||
    <width>647</width>
 | 
					    <width>647</width>
 | 
				
			||||||
    <height>175</height>
 | 
					    <height>231</height>
 | 
				
			||||||
   </rect>
 | 
					   </rect>
 | 
				
			||||||
  </property>
 | 
					  </property>
 | 
				
			||||||
  <property name="windowTitle">
 | 
					  <property name="windowTitle">
 | 
				
			||||||
| 
						 | 
					@ -46,11 +46,7 @@
 | 
				
			||||||
    </widget>
 | 
					    </widget>
 | 
				
			||||||
   </item>
 | 
					   </item>
 | 
				
			||||||
   <item row="0" column="1">
 | 
					   <item row="0" column="1">
 | 
				
			||||||
    <widget class="QLineEdit" name="lineAddress">
 | 
					    <widget class="PayToEdit" name="lineAddress"/>
 | 
				
			||||||
     <property name="text">
 | 
					 | 
				
			||||||
      <string/>
 | 
					 | 
				
			||||||
     </property>
 | 
					 | 
				
			||||||
    </widget>
 | 
					 | 
				
			||||||
   </item>
 | 
					   </item>
 | 
				
			||||||
   <item row="1" column="0">
 | 
					   <item row="1" column="0">
 | 
				
			||||||
    <widget class="HelpLabel" name="label_Description">
 | 
					    <widget class="HelpLabel" name="label_Description">
 | 
				
			||||||
| 
						 | 
					@ -190,6 +186,11 @@
 | 
				
			||||||
   <extends>QLabel</extends>
 | 
					   <extends>QLabel</extends>
 | 
				
			||||||
   <header>components.h</header>
 | 
					   <header>components.h</header>
 | 
				
			||||||
  </customwidget>
 | 
					  </customwidget>
 | 
				
			||||||
 | 
					  <customwidget>
 | 
				
			||||||
 | 
					   <class>PayToEdit</class>
 | 
				
			||||||
 | 
					   <extends>QPlainTextEdit</extends>
 | 
				
			||||||
 | 
					   <header>widgets/PayToEdit.h</header>
 | 
				
			||||||
 | 
					  </customwidget>
 | 
				
			||||||
 </customwidgets>
 | 
					 </customwidgets>
 | 
				
			||||||
 <resources/>
 | 
					 <resources/>
 | 
				
			||||||
 <connections/>
 | 
					 <connections/>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,9 +24,9 @@ XmrToOrder::XmrToOrder(AppContext *ctx, UtilsNetworking *network, QString baseUr
 | 
				
			||||||
    connect(m_ctx, &AppContext::createTransactionCancelled, this, &XmrToOrder::onTransactionCancelled);
 | 
					    connect(m_ctx, &AppContext::createTransactionCancelled, this, &XmrToOrder::onTransactionCancelled);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void XmrToOrder::onTransactionCancelled(const QString &address, double amount) {
 | 
					void XmrToOrder::onTransactionCancelled(const QVector<QString> &address, double amount) {
 | 
				
			||||||
    // listener for all cancelled transactions - will try to match the exact amount to this order.
 | 
					    // listener for all cancelled transactions - will try to match the exact amount to this order.
 | 
				
			||||||
    if(this->incoming_amount_total != amount || this->receiving_subaddress != address) return;
 | 
					    if(this->incoming_amount_total != amount || this->receiving_subaddress != address[0]) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this->errorMsg = "TX cancelled by user";
 | 
					    this->errorMsg = "TX cancelled by user";
 | 
				
			||||||
    this->changeState(OrderState::Status_OrderFailed);
 | 
					    this->changeState(OrderState::Status_OrderFailed);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,7 +66,7 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public slots:
 | 
					public slots:
 | 
				
			||||||
    void onCountdown();
 | 
					    void onCountdown();
 | 
				
			||||||
    void onTransactionCancelled(const QString &address, double amount);
 | 
					    void onTransactionCancelled(const QVector<QString> &address, double amount);
 | 
				
			||||||
    void onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid);
 | 
					    void onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void onCreatedError();
 | 
					    void onCreatedError();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										150
									
								
								src/widgets/PayToEdit.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/widgets/PayToEdit.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,150 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: BSD-3-Clause
 | 
				
			||||||
 | 
					// Copyright (c) 2020-2021, The Monero Project.
 | 
				
			||||||
 | 
					// Copyright (c) 2012 thomasv@gitorious
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "PayToEdit.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QtGlobal>
 | 
				
			||||||
 | 
					#include <QScrollBar>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "utils/utils.h"
 | 
				
			||||||
 | 
					#include "model/ModelUtils.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "libwalletqt/WalletManager.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PayToEdit::PayToEdit(QWidget *parent) : QPlainTextEdit(parent)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    this->setFont(ModelUtils::getMonospaceFont());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    connect(this->document(), &QTextDocument::contentsChanged, this, &PayToEdit::updateSize);
 | 
				
			||||||
 | 
					    connect(this, &QPlainTextEdit::textChanged, this, &PayToEdit::checkText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this->updateSize();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PayToEdit::setNetType(NetworkType::Type netType) {
 | 
				
			||||||
 | 
					    m_netType = netType;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PayToEdit::setText(const QString &text) {
 | 
				
			||||||
 | 
					    this->setPlainText(text);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QString PayToEdit::text() {
 | 
				
			||||||
 | 
					    return this->toPlainText();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QVector<PayToLineError> PayToEdit::getErrors() {
 | 
				
			||||||
 | 
					    return m_errors;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QVector<PartialTxOutput> PayToEdit::getOutputs() {
 | 
				
			||||||
 | 
					    return m_outputs;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					quint64 PayToEdit::getTotal() {
 | 
				
			||||||
 | 
					    return m_total;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QStringList PayToEdit::lines() {
 | 
				
			||||||
 | 
					    return this->toPlainText().split("\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool PayToEdit::isMultiline() {
 | 
				
			||||||
 | 
					    return this->lines().size() > 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PayToEdit::payToMany() {
 | 
				
			||||||
 | 
					    this->setPlainText("\n\n\n");
 | 
				
			||||||
 | 
					    this->updateSize();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool PayToEdit::isOpenAlias() {
 | 
				
			||||||
 | 
					    if (this->isMultiline()) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    auto text = this->toPlainText().trimmed();
 | 
				
			||||||
 | 
					    if (!(text.contains('.') and (!text.contains(' ')))) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    auto parts = text.split(',');
 | 
				
			||||||
 | 
					    if (parts.size() > 0 and WalletManager::addressValid(parts[0], m_netType)) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PayToEdit::checkText() {
 | 
				
			||||||
 | 
					    m_errors.clear();
 | 
				
			||||||
 | 
					    m_outputs.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // filter out empty lines
 | 
				
			||||||
 | 
					    QStringList lines;
 | 
				
			||||||
 | 
					    for (auto &l : this->lines()) {
 | 
				
			||||||
 | 
					        if (!l.isEmpty()) {
 | 
				
			||||||
 | 
					            lines.push_back(l);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this->parseAsMultiline(lines);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PayToEdit::updateSize() {
 | 
				
			||||||
 | 
					    qreal lineHeight = QFontMetrics(this->document()->defaultFont()).height();
 | 
				
			||||||
 | 
					    qreal docHeight = this->document()->size().height();
 | 
				
			||||||
 | 
					    int h = int(docHeight * lineHeight + 11);
 | 
				
			||||||
 | 
					    h = qMin(qMax(h, m_heightMin), m_heightMax);
 | 
				
			||||||
 | 
					    this->setMinimumHeight(h);
 | 
				
			||||||
 | 
					    this->setMaximumHeight(h);
 | 
				
			||||||
 | 
					    this->verticalScrollBar()->hide();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PartialTxOutput PayToEdit::parseAddressAndAmount(const QString &line) {
 | 
				
			||||||
 | 
					    QStringList x = line.split(",");
 | 
				
			||||||
 | 
					    if (x.size() != 2) {
 | 
				
			||||||
 | 
					        return PartialTxOutput();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QString address = this->parseAddress(x[0]);
 | 
				
			||||||
 | 
					    quint64 amount = this->parseAmount(x[1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return PartialTxOutput(address, amount);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					quint64 PayToEdit::parseAmount(QString amount) {
 | 
				
			||||||
 | 
					    amount.replace(',', '.');
 | 
				
			||||||
 | 
					    if (amount.isEmpty()) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return WalletManager::amountFromString(amount.trimmed());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QString PayToEdit::parseAddress(QString address) {
 | 
				
			||||||
 | 
					    if (!WalletManager::addressValid(address.trimmed(), m_netType)) {
 | 
				
			||||||
 | 
					        return "";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return address;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PayToEdit::parseAsMultiline(const QStringList &lines) {
 | 
				
			||||||
 | 
					    m_outputs.clear();
 | 
				
			||||||
 | 
					    m_total = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int i = 0;
 | 
				
			||||||
 | 
					    for (auto &line : lines) {
 | 
				
			||||||
 | 
					        PartialTxOutput output = this->parseAddressAndAmount(line);
 | 
				
			||||||
 | 
					        if (output.address.isEmpty() && output.amount == 0) {
 | 
				
			||||||
 | 
					            m_errors.append(PayToLineError(line, "Expected two comma-separated values: (address, amount)", i, true));
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        } else if (output.address.isEmpty()) {
 | 
				
			||||||
 | 
					            m_errors.append(PayToLineError(line, "Invalid address", i, true));
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        } else if (output.amount == 0) {
 | 
				
			||||||
 | 
					            m_errors.append(PayToLineError(line, "Invalid amount", i, true));
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        m_outputs.append(output);
 | 
				
			||||||
 | 
					        m_total += output.amount;
 | 
				
			||||||
 | 
					        i += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										70
									
								
								src/widgets/PayToEdit.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/widgets/PayToEdit.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: BSD-3-Clause
 | 
				
			||||||
 | 
					// Copyright (c) 2020-2021, The Monero Project.
 | 
				
			||||||
 | 
					// Copyright (c) 2012 thomasv@gitorious
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef FEATHER_PAYTOEDIT_H
 | 
				
			||||||
 | 
					#define FEATHER_PAYTOEDIT_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QObject>
 | 
				
			||||||
 | 
					#include <QPlainTextEdit>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "utils/utils.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct PartialTxOutput {
 | 
				
			||||||
 | 
					    explicit PartialTxOutput(QString address = "", quint64 amount = 0)
 | 
				
			||||||
 | 
					        : address(address), amount(amount) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QString address;
 | 
				
			||||||
 | 
					    quint64 amount;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct PayToLineError {
 | 
				
			||||||
 | 
					    explicit PayToLineError(QString lineContent, QString error, int idx = 0, bool isMultiline = false)
 | 
				
			||||||
 | 
					        : lineContent(lineContent), error(error), idx(idx), isMultiline(isMultiline) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QString lineContent;
 | 
				
			||||||
 | 
					    QString error;
 | 
				
			||||||
 | 
					    int idx;
 | 
				
			||||||
 | 
					    bool isMultiline;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PayToEdit : public QPlainTextEdit
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    explicit PayToEdit(QWidget *parent = nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void setNetType(NetworkType::Type netType);
 | 
				
			||||||
 | 
					    void setText(const QString &text);
 | 
				
			||||||
 | 
					    QString text();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QVector<PayToLineError> getErrors();
 | 
				
			||||||
 | 
					    QVector<PartialTxOutput> getOutputs();
 | 
				
			||||||
 | 
					    quint64 getTotal();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QStringList lines();
 | 
				
			||||||
 | 
					    bool isMultiline();
 | 
				
			||||||
 | 
					    void payToMany();
 | 
				
			||||||
 | 
					    bool isOpenAlias();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    void checkText();
 | 
				
			||||||
 | 
					    void updateSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PartialTxOutput parseAddressAndAmount(const QString &line);
 | 
				
			||||||
 | 
					    quint64 parseAmount(QString amount);
 | 
				
			||||||
 | 
					    QString parseAddress(QString address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void parseAsMultiline(const QStringList &lines);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int m_heightMin = 0;
 | 
				
			||||||
 | 
					    int m_heightMax = 150;
 | 
				
			||||||
 | 
					    quint64 m_total = 0;
 | 
				
			||||||
 | 
					    NetworkType::Type m_netType = NetworkType::Type::MAINNET;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QVector<PayToLineError> m_errors;
 | 
				
			||||||
 | 
					    QVector<PartialTxOutput> m_outputs;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif //FEATHER_PAYTOEDIT_H
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue