2020-10-07 10:36:04 +00:00
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
2023-01-02 19:30:11 +00:00
|
|
|
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
2020-10-07 10:36:04 +00:00
|
|
|
|
2021-06-28 17:48:23 +00:00
|
|
|
#include "ReceiveWidget.h"
|
|
|
|
#include "ui_ReceiveWidget.h"
|
2020-10-07 10:36:04 +00:00
|
|
|
|
|
|
|
#include <QMenu>
|
2021-05-02 18:22:38 +00:00
|
|
|
#include <QMessageBox>
|
2020-10-07 10:36:04 +00:00
|
|
|
|
2021-10-01 17:10:46 +00:00
|
|
|
#include "dialog/PaymentRequestDialog.h"
|
2021-06-28 17:48:23 +00:00
|
|
|
#include "dialog/QrCodeDialog.h"
|
2023-11-16 15:26:58 +00:00
|
|
|
#include "utils/config.h"
|
2021-06-28 17:48:23 +00:00
|
|
|
#include "utils/Icons.h"
|
2023-03-28 20:42:12 +00:00
|
|
|
#include "utils/Utils.h"
|
2021-06-28 17:48:23 +00:00
|
|
|
|
2023-03-01 02:05:56 +00:00
|
|
|
ReceiveWidget::ReceiveWidget(Wallet *wallet, QWidget *parent)
|
2021-06-28 17:48:23 +00:00
|
|
|
: QWidget(parent)
|
|
|
|
, ui(new Ui::ReceiveWidget)
|
2023-03-01 02:05:56 +00:00
|
|
|
, m_wallet(wallet)
|
2020-10-07 10:36:04 +00:00
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
2023-03-01 02:05:56 +00:00
|
|
|
m_model = m_wallet->subaddressModel();
|
|
|
|
m_proxyModel = new SubaddressProxyModel(this, m_wallet->subaddress());
|
2021-05-18 15:59:18 +00:00
|
|
|
m_proxyModel->setSourceModel(m_model);
|
|
|
|
|
|
|
|
ui->addresses->setModel(m_proxyModel);
|
|
|
|
ui->addresses->setColumnHidden(SubaddressModel::isUsed, true);
|
2023-03-07 20:36:15 +00:00
|
|
|
ui->addresses->header()->setSectionResizeMode(SubaddressModel::Index, QHeaderView::ResizeToContents);
|
|
|
|
ui->addresses->header()->setSectionResizeMode(SubaddressModel::Address, QHeaderView::ResizeToContents);
|
|
|
|
ui->addresses->header()->setSectionResizeMode(SubaddressModel::Label, QHeaderView::Stretch);
|
2021-05-18 15:59:18 +00:00
|
|
|
|
|
|
|
connect(ui->addresses->selectionModel(), &QItemSelectionModel::currentChanged, [=](QModelIndex current, QModelIndex prev){
|
|
|
|
this->updateQrCode();
|
|
|
|
});
|
|
|
|
connect(m_model, &SubaddressModel::modelReset, [this](){
|
|
|
|
this->updateQrCode();
|
|
|
|
});
|
|
|
|
|
2021-05-23 22:02:36 +00:00
|
|
|
ui->qrCode->setCursor(Qt::PointingHandCursor);
|
|
|
|
|
2020-10-07 10:36:04 +00:00
|
|
|
// header context menu
|
|
|
|
ui->addresses->header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
m_headerMenu = new QMenu(this);
|
2023-11-16 15:26:58 +00:00
|
|
|
auto subMenu = new QMenu(this);
|
|
|
|
subMenu->setTitle("Columns");
|
|
|
|
|
|
|
|
this->addOption(m_headerMenu, "Show used addresses", Config::showUsedAddresses, [this](bool show){
|
|
|
|
m_proxyModel->invalidate();
|
|
|
|
});
|
|
|
|
this->addOption(m_headerMenu, "Show hidden addresses", Config::showHiddenAddresses, [this](bool show){
|
|
|
|
m_proxyModel->invalidate();
|
|
|
|
});
|
|
|
|
this->addOption(m_headerMenu, "Show full addresses", Config::showFullAddresses, [this](bool show){
|
|
|
|
m_proxyModel->invalidate();
|
|
|
|
});
|
|
|
|
this->addOption(m_headerMenu, "Show change address", Config::showChangeAddresses, [this](bool show){
|
|
|
|
m_proxyModel->invalidate();
|
|
|
|
});
|
|
|
|
|
|
|
|
m_headerMenu->addMenu(subMenu);
|
|
|
|
this->addOption(subMenu, "Show index", Config::showAddressIndex, [this](bool show){
|
|
|
|
ui->addresses->setColumnHidden(0, !show);
|
|
|
|
});
|
|
|
|
this->addOption(subMenu, "Show labels", Config::showAddressLabels, [this](bool show){
|
|
|
|
ui->addresses->setColumnHidden(2, !show);
|
|
|
|
});
|
|
|
|
|
2020-10-07 10:36:04 +00:00
|
|
|
connect(ui->addresses->header(), &QHeaderView::customContextMenuRequested, this, &ReceiveWidget::showHeaderMenu);
|
2023-11-16 15:26:58 +00:00
|
|
|
ui->toolBtn_options->setMenu(m_headerMenu);
|
2020-10-07 10:36:04 +00:00
|
|
|
|
|
|
|
// context menu
|
|
|
|
ui->addresses->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
m_showTransactionsAction = new QAction("Show transactions");
|
|
|
|
connect(m_showTransactionsAction, &QAction::triggered, this, &ReceiveWidget::onShowTransactions);
|
|
|
|
connect(ui->addresses, &QTreeView::customContextMenuRequested, this, &ReceiveWidget::showContextMenu);
|
|
|
|
|
2021-05-02 18:22:38 +00:00
|
|
|
connect(ui->btn_generateSubaddress, &QPushButton::clicked, this, &ReceiveWidget::generateSubaddress);
|
2020-10-07 10:36:04 +00:00
|
|
|
|
|
|
|
connect(ui->qrCode, &ClickableLabel::clicked, this, &ReceiveWidget::showQrCodeDialog);
|
2021-05-23 14:58:18 +00:00
|
|
|
connect(ui->search, &QLineEdit::textChanged, this, &ReceiveWidget::setSearchFilter);
|
2021-03-14 21:12:02 +00:00
|
|
|
|
2021-10-01 17:10:46 +00:00
|
|
|
connect(ui->btn_createPaymentRequest, &QPushButton::clicked, this, &ReceiveWidget::createPaymentRequest);
|
2020-10-07 10:36:04 +00:00
|
|
|
}
|
|
|
|
|
2023-11-16 15:26:58 +00:00
|
|
|
void ReceiveWidget::addOption(QMenu *menu, const QString &text, Config::ConfigKey key, const std::function<void(bool show)>& func) {
|
|
|
|
// QMenu takes ownership of the returned QAction.
|
|
|
|
QAction *action = menu->addAction(text, func);
|
|
|
|
action->setCheckable(true);
|
|
|
|
bool toggled = conf()->get(key).toBool();
|
|
|
|
action->setChecked(toggled);
|
|
|
|
func(toggled);
|
|
|
|
connect(action, &QAction::toggled, [key](bool toggled){
|
|
|
|
conf()->set(key, toggled);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-05-23 14:58:18 +00:00
|
|
|
void ReceiveWidget::setSearchbarVisible(bool visible) {
|
2023-11-16 15:26:58 +00:00
|
|
|
ui->frame_search->setVisible(visible);
|
2021-05-23 14:58:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ReceiveWidget::focusSearchbar() {
|
|
|
|
ui->search->setFocusPolicy(Qt::StrongFocus);
|
|
|
|
ui->search->setFocus();
|
|
|
|
}
|
|
|
|
|
2020-10-07 10:36:04 +00:00
|
|
|
void ReceiveWidget::copyAddress() {
|
|
|
|
QModelIndex index = ui->addresses->currentIndex();
|
2023-03-28 20:42:12 +00:00
|
|
|
Utils::copyColumn(&index, SubaddressModel::Address);
|
2020-10-07 10:36:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ReceiveWidget::copyLabel() {
|
|
|
|
QModelIndex index = ui->addresses->currentIndex();
|
2023-03-28 20:42:12 +00:00
|
|
|
Utils::copyColumn(&index, SubaddressModel::Label);
|
2020-10-07 10:36:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ReceiveWidget::editLabel() {
|
|
|
|
QModelIndex index = ui->addresses->currentIndex().siblingAtColumn(m_model->ModelColumn::Label);
|
|
|
|
ui->addresses->setCurrentIndex(index);
|
|
|
|
ui->addresses->edit(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReceiveWidget::showContextMenu(const QPoint &point) {
|
2023-11-16 15:26:58 +00:00
|
|
|
SubaddressRow* row = this->currentEntry();
|
2021-03-14 21:12:02 +00:00
|
|
|
if (!row) return;
|
|
|
|
|
2020-10-07 10:36:04 +00:00
|
|
|
auto *menu = new QMenu(ui->addresses);
|
|
|
|
|
2021-10-23 13:25:29 +00:00
|
|
|
menu->addAction("Copy address", this, &ReceiveWidget::copyAddress);
|
|
|
|
menu->addAction("Copy label", this, &ReceiveWidget::copyLabel);
|
|
|
|
menu->addAction("Edit label", this, &ReceiveWidget::editLabel);
|
2020-10-07 10:36:04 +00:00
|
|
|
|
2023-11-16 15:26:58 +00:00
|
|
|
if (row->isUsed()) {
|
2020-10-07 10:36:04 +00:00
|
|
|
menu->addAction(m_showTransactionsAction);
|
|
|
|
}
|
|
|
|
|
2023-11-16 15:26:58 +00:00
|
|
|
QAction *actionPin = menu->addAction("Pin address", [this](bool toggled){
|
|
|
|
SubaddressRow* row = this->currentEntry();
|
|
|
|
if (!row) return;
|
|
|
|
|
|
|
|
QString address = row->getAddress();
|
|
|
|
m_wallet->subaddress()->setPinned(address, toggled);
|
|
|
|
m_proxyModel->invalidate();
|
|
|
|
});
|
|
|
|
actionPin->setCheckable(true);
|
|
|
|
actionPin->setChecked(row->isPinned());
|
|
|
|
|
|
|
|
QAction *actionHide = menu->addAction("Hide address", [this](bool toggled){
|
|
|
|
SubaddressRow* row = this->currentEntry();
|
|
|
|
if (!row) return;
|
|
|
|
|
|
|
|
QString address = row->getAddress();
|
|
|
|
m_wallet->subaddress()->setHidden(address, toggled);
|
|
|
|
m_proxyModel->invalidate();
|
|
|
|
});
|
|
|
|
actionHide->setCheckable(true);
|
|
|
|
actionHide->setChecked(row->isHidden());
|
2021-03-14 21:12:02 +00:00
|
|
|
|
2023-03-01 02:05:56 +00:00
|
|
|
if (m_wallet->isHwBacked()) {
|
2021-05-02 18:22:38 +00:00
|
|
|
menu->addAction("Show on device", this, &ReceiveWidget::showOnDevice);
|
|
|
|
}
|
|
|
|
|
2020-10-07 10:36:04 +00:00
|
|
|
menu->popup(ui->addresses->viewport()->mapToGlobal(point));
|
|
|
|
}
|
|
|
|
|
2021-10-01 17:10:46 +00:00
|
|
|
void ReceiveWidget::createPaymentRequest() {
|
|
|
|
QModelIndex index = ui->addresses->currentIndex();
|
|
|
|
if (!index.isValid()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString address = index.model()->data(index.siblingAtColumn(SubaddressModel::Address), Qt::UserRole).toString();
|
|
|
|
|
2023-03-01 02:05:56 +00:00
|
|
|
PaymentRequestDialog dialog{this, m_wallet, address};
|
2021-10-01 17:10:46 +00:00
|
|
|
dialog.exec();
|
|
|
|
}
|
|
|
|
|
2020-10-07 10:36:04 +00:00
|
|
|
void ReceiveWidget::onShowTransactions() {
|
|
|
|
QModelIndex index = ui->addresses->currentIndex();
|
|
|
|
if (!index.isValid()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString address = index.model()->data(index.siblingAtColumn(SubaddressModel::Address), Qt::UserRole).toString();
|
|
|
|
emit showTransactions(address);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReceiveWidget::setSearchFilter(const QString &filter) {
|
|
|
|
m_proxyModel->setSearchFilter(filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReceiveWidget::showHeaderMenu(const QPoint& position)
|
|
|
|
{
|
2023-11-16 15:26:58 +00:00
|
|
|
Q_UNUSED(position)
|
2020-10-07 10:36:04 +00:00
|
|
|
m_headerMenu->exec(QCursor::pos());
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:22:38 +00:00
|
|
|
void ReceiveWidget::showOnDevice() {
|
2023-11-16 15:26:58 +00:00
|
|
|
SubaddressRow* row = this->currentEntry();
|
2021-05-02 18:22:38 +00:00
|
|
|
if (!row) return;
|
2023-11-16 15:26:58 +00:00
|
|
|
m_wallet->deviceShowAddressAsync(m_wallet->currentSubaddressAccount(), row->getRow(), "");
|
2021-05-02 18:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ReceiveWidget::generateSubaddress() {
|
2023-03-01 02:05:56 +00:00
|
|
|
bool r = m_wallet->subaddress()->addRow(m_wallet->currentSubaddressAccount(), "");
|
2021-05-02 18:22:38 +00:00
|
|
|
if (!r) {
|
2023-11-16 15:26:58 +00:00
|
|
|
Utils::showError(this, "Failed to generate subaddress", m_wallet->subaddress()->getError());
|
2021-05-02 18:22:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-14 11:11:54 +00:00
|
|
|
void ReceiveWidget::updateQrCode(){
|
|
|
|
QModelIndex index = ui->addresses->currentIndex();
|
|
|
|
if (!index.isValid()) {
|
|
|
|
ui->qrCode->clear();
|
2021-10-01 17:10:46 +00:00
|
|
|
ui->btn_createPaymentRequest->hide();
|
2020-11-14 11:11:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString address = index.model()->data(index.siblingAtColumn(SubaddressModel::Address), Qt::UserRole).toString();
|
2020-10-07 10:36:04 +00:00
|
|
|
const QrCode qrc(address, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::MEDIUM);
|
|
|
|
|
|
|
|
int width = ui->qrCode->width() - 4;
|
|
|
|
if (qrc.isValid()) {
|
|
|
|
ui->qrCode->setPixmap(qrc.toPixmap(1).scaled(width, width, Qt::KeepAspectRatio));
|
2021-10-01 17:10:46 +00:00
|
|
|
ui->btn_createPaymentRequest->show();
|
2020-10-07 10:36:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReceiveWidget::showQrCodeDialog() {
|
|
|
|
QModelIndex index = ui->addresses->currentIndex();
|
2020-11-14 11:11:54 +00:00
|
|
|
if (!index.isValid()) {
|
|
|
|
return;
|
|
|
|
}
|
2020-10-07 10:36:04 +00:00
|
|
|
QString address = index.model()->data(index.siblingAtColumn(SubaddressModel::Address), Qt::UserRole).toString();
|
2020-10-16 03:05:05 +00:00
|
|
|
QrCode qr(address, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::HIGH);
|
2021-07-03 22:06:24 +00:00
|
|
|
QrCodeDialog dialog{this, &qr, "Address"};
|
|
|
|
dialog.exec();
|
2020-10-07 10:36:04 +00:00
|
|
|
}
|
|
|
|
|
2023-11-16 15:26:58 +00:00
|
|
|
SubaddressRow* ReceiveWidget::currentEntry() {
|
2021-03-14 21:12:02 +00:00
|
|
|
QModelIndexList list = ui->addresses->selectionModel()->selectedRows();
|
|
|
|
if (list.size() == 1) {
|
|
|
|
return m_model->entryFromIndex(m_proxyModel->mapToSource(list.first()));
|
|
|
|
} else {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-27 12:13:05 +00:00
|
|
|
ReceiveWidget::~ReceiveWidget() = default;
|