#include <QMenu>
#include <QMessageBox>
+#include "dialog/PaymentRequestDialog.h"
#include "dialog/QrCodeDialog.h"
#include "model/ModelUtils.h"
#include "utils/Icons.h"
connect(ui->check_showUsed, &QCheckBox::clicked, this, &ReceiveWidget::setShowUsedAddresses);
connect(ui->check_showHidden, &QCheckBox::clicked, this, &ReceiveWidget::setShowHiddenAddresses);
+
+ connect(ui->btn_createPaymentRequest, &QPushButton::clicked, this, &ReceiveWidget::createPaymentRequest);
}
void ReceiveWidget::setSearchbarVisible(bool visible) {
menu->popup(ui->addresses->viewport()->mapToGlobal(point));
}
+void ReceiveWidget::createPaymentRequest() {
+ QModelIndex index = ui->addresses->currentIndex();
+ if (!index.isValid()) {
+ return;
+ }
+
+ QString address = index.model()->data(index.siblingAtColumn(SubaddressModel::Address), Qt::UserRole).toString();
+
+ PaymentRequestDialog dialog{this, m_ctx, address};
+ dialog.exec();
+}
+
void ReceiveWidget::onShowTransactions() {
QModelIndex index = ui->addresses->currentIndex();
if (!index.isValid()) {
QModelIndex index = ui->addresses->currentIndex();
if (!index.isValid()) {
ui->qrCode->clear();
+ ui->btn_createPaymentRequest->hide();
return;
}
int width = ui->qrCode->width() - 4;
if (qrc.isValid()) {
ui->qrCode->setPixmap(qrc.toPixmap(1).scaled(width, width, Qt::KeepAspectRatio));
+ ui->btn_createPaymentRequest->show();
}
}
void setShowHiddenAddresses(bool show);
void setSearchFilter(const QString &filter);
void onShowTransactions();
+ void createPaymentRequest();
signals:
void showTransactions(const QString& address);
</property>
</widget>
</item>
+ <item>
+ <widget class="QPushButton" name="btn_createPaymentRequest">
+ <property name="text">
+ <string>Payment Request</string>
+ </property>
+ </widget>
+ </item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
QString amount_rx = R"(^\d{0,8}[\.,]\d{0,12}|(all)$)";
QRegExp rx;
rx.setPattern(amount_rx);
- QValidator *validator = new QRegExpValidator(rx, this);
+ QValidator *validator = new QRegExpValidator(rx, this);
ui->lineAmount->setValidator(validator);
connect(m_ctx.get(), &AppContext::initiateTransaction, this, &SendWidget::onInitiateTransaction);
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2020-2021, The Monero Project.
+
+#include "PaymentRequestDialog.h"
+#include "ui_PaymentRequestDialog.h"
+
+#include <QClipboard>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QRegExpValidator>
+
+#include "WalletManager.h"
+
+PaymentRequestDialog::PaymentRequestDialog(QWidget *parent, QSharedPointer<AppContext> ctx, QString address)
+ : QDialog(parent)
+ , ui(new Ui::PaymentRequestDialog)
+ , m_ctx(std::move(ctx))
+ , m_address(std::move(address))
+{
+ ui->setupUi(this);
+
+ QString amount_rx = R"(^\d{0,8}[\.]\d{0,12}|(all)$)";
+ QRegExp rx;
+ rx.setPattern(amount_rx);
+ QValidator *validator = new QRegExpValidator(rx, this);
+ ui->line_amountXMR->setValidator(validator);
+
+ connect(ui->line_amountXMR, &QLineEdit::textChanged, this, &PaymentRequestDialog::updatePaymentRequest);
+ connect(ui->line_description, &QLineEdit::textChanged, this, &PaymentRequestDialog::updatePaymentRequest);
+ connect(ui->line_recipient, &QLineEdit::textChanged, this, &PaymentRequestDialog::updatePaymentRequest);
+
+ connect(ui->btn_copyLink, &QPushButton::clicked, this, &PaymentRequestDialog::copyLink);
+ connect(ui->btn_copyImage, &QPushButton::clicked, this, &PaymentRequestDialog::copyImage);
+ connect(ui->btn_saveImage, &QPushButton::clicked, this, &PaymentRequestDialog::saveImage);
+
+ this->updatePaymentRequest();
+
+ ui->line_amountXMR->setFocus();
+
+ this->adjustSize();
+}
+
+void PaymentRequestDialog::updatePaymentRequest() {
+ QString description = ui->line_description->text();
+ QString recipient = ui->line_recipient->text();
+ quint64 amount = WalletManager::amountFromString(ui->line_amountXMR->text());
+
+ QString uri = m_ctx->wallet->make_uri(m_address, amount, description, recipient);
+
+ ui->line_paymentRequestUri->setText(uri);
+ ui->line_paymentRequestUri->setCursorPosition(0);
+
+ // TODO: memory leak, cba to refactor now
+ m_qrCode = new QrCode(uri, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::MEDIUM);
+ if (m_qrCode->isValid()) {
+ ui->qrWidget->setQrCode(m_qrCode);
+ }
+}
+
+void PaymentRequestDialog::copyLink() {
+ Utils::copyToClipboard(ui->line_paymentRequestUri->text());
+ QMessageBox::information(this, "Information", "Payment request link copied to clipboard.");
+}
+
+void PaymentRequestDialog::copyImage() {
+ QApplication::clipboard()->setPixmap(m_qrCode->toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio));
+ QMessageBox::information(this, "Information", "QR code copied to clipboard.");
+}
+
+void PaymentRequestDialog::saveImage() {
+ QString filename = QFileDialog::getSaveFileName(this, "Select where to save file", QDir::current().filePath("qrcode.png"));
+ if (filename.isEmpty()) {
+ return;
+ }
+
+ QFile file(filename);
+ file.open(QIODevice::WriteOnly);
+ m_qrCode->toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio).save(&file, "PNG");
+ QMessageBox::information(this, "Information", "QR code saved to file");
+}
+
+PaymentRequestDialog::~PaymentRequestDialog() = default;
\ No newline at end of file
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2020-2021, The Monero Project.
+
+#ifndef FEATHER_PAYMENTREQUESTDIALOG_H
+#define FEATHER_PAYMENTREQUESTDIALOG_H
+
+#include <QDialog>
+
+#include "appcontext.h"
+#include "qrcode/QrCode.h"
+
+namespace Ui {
+ class PaymentRequestDialog;
+}
+
+class PaymentRequestDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit PaymentRequestDialog(QWidget *parent, QSharedPointer<AppContext> ctx, QString address);
+ ~PaymentRequestDialog() override;
+
+private slots:
+ void updatePaymentRequest();
+ void copyLink();
+ void copyImage();
+ void saveImage();
+
+private:
+ QScopedPointer<Ui::PaymentRequestDialog> ui;
+ QSharedPointer<AppContext> m_ctx;
+ QString m_address;
+ QrCode *m_qrCode;
+};
+
+#endif //FEATHER_PAYMENTREQUESTDIALOG_H
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PaymentRequestDialog</class>
+ <widget class="QDialog" name="PaymentRequestDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>658</width>
+ <height>761</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Create Payment Request</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QrCodeWidget" name="qrWidget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="line_paymentRequestUri">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Amount:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLineEdit" name="line_amountXMR">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>XMR</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Description:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="line_description"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Your name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="line_recipient"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="btn_copyLink">
+ <property name="text">
+ <string>Copy Link</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btn_copyImage">
+ <property name="text">
+ <string>Copy Image</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btn_saveImage">
+ <property name="text">
+ <string>Save Image</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QrCodeWidget</class>
+ <extends>QWidget</extends>
+ <header>widgets/QrCodeWidget.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>PaymentRequestDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>PaymentRequestDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
return result;
}
+QString Wallet::make_uri(const QString &address, quint64 &amount, const QString &description,
+ const QString &recipient) const
+{
+ std::string error;
+ std::string uri = m_walletImpl->make_uri(address.toStdString(), "", amount, description.toStdString(), recipient.toStdString(), error);
+ return QString::fromStdString(uri);
+}
+
bool Wallet::rescanSpent()
{
QMutexLocker locker(&m_asyncMutex);
bool parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector<QString> &unknown_parameters, QString &error) const;
QVariantMap parse_uri_to_object(const QString &uri) const;
+ QString make_uri(const QString &address, quint64 &amount, const QString &description, const QString &recipient) const;
+
//! Namespace your cacheAttribute keys to avoid collisions
bool setCacheAttribute(const QString &key, const QString &val);
QString getCacheAttribute(const QString &key) const;
}
}
}
+}
+
+bool QrCodeWidget::hasHeightForWidth() const {
+ return true;
+}
+
+int QrCodeWidget::heightForWidth(int w) const {
+ return w;
}
\ No newline at end of file
protected:
void paintEvent(QPaintEvent *event) override;
+ int heightForWidth(int w) const override;
+ bool hasHeightForWidth() const override;
private:
QrCode *m_qrcode = nullptr;