bitcoin/src/qt/receivecoinsdialog.cpp
Jarol Rodriguez bb3da8fe41 qt: Disable requests context menu actions when appropriate
The recent requests table will allow you to copy data points even if they do not exist.
This PR implements checks to disable the 'copy label', 'copy message', and 'copy amount' context menu action if the respective fields are empty.
2021-02-23 11:38:22 -05:00

329 lines
12 KiB
C++

// Copyright (c) 2011-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <wallet/wallet.h>
#include <qt/receivecoinsdialog.h>
#include <qt/forms/ui_receivecoinsdialog.h>
#include <qt/addresstablemodel.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/receiverequestdialog.h>
#include <qt/recentrequeststablemodel.h>
#include <qt/walletmodel.h>
#include <QAction>
#include <QCursor>
#include <QMessageBox>
#include <QScrollBar>
#include <QSettings>
#include <QTextDocument>
ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
QDialog(parent, GUIUtil::dialog_flags),
ui(new Ui::ReceiveCoinsDialog),
model(nullptr),
platformStyle(_platformStyle)
{
ui->setupUi(this);
if (!_platformStyle->getImagesOnButtons()) {
ui->clearButton->setIcon(QIcon());
ui->receiveButton->setIcon(QIcon());
ui->showRequestButton->setIcon(QIcon());
ui->removeRequestButton->setIcon(QIcon());
} else {
ui->clearButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
ui->receiveButton->setIcon(_platformStyle->SingleColorIcon(":/icons/receiving_addresses"));
ui->showRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/edit"));
ui->removeRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
}
// context menu actions
QAction *copyURIAction = new QAction(tr("Copy URI"), this);
QAction* copyAddressAction = new QAction(tr("Copy address"), this);
copyLabelAction = new QAction(tr("Copy label"), this);
copyMessageAction = new QAction(tr("Copy message"), this);
copyAmountAction = new QAction(tr("Copy amount"), this);
// context menu
contextMenu = new QMenu(this);
contextMenu->addAction(copyURIAction);
contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyMessageAction);
contextMenu->addAction(copyAmountAction);
// context menu signals
connect(ui->recentRequestsView, &QWidget::customContextMenuRequested, this, &ReceiveCoinsDialog::showMenu);
connect(copyURIAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyURI);
connect(copyAddressAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyAddress);
connect(copyLabelAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyLabel);
connect(copyMessageAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyMessage);
connect(copyAmountAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyAmount);
connect(ui->clearButton, &QPushButton::clicked, this, &ReceiveCoinsDialog::clear);
QTableView* tableView = ui->recentRequestsView;
tableView->verticalHeader()->hide();
tableView->setAlternatingRowColors(true);
tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
tableView->setSelectionMode(QAbstractItemView::ContiguousSelection);
QSettings settings;
if (!tableView->horizontalHeader()->restoreState(settings.value("RecentRequestsViewHeaderState").toByteArray())) {
tableView->setColumnWidth(RecentRequestsTableModel::Date, DATE_COLUMN_WIDTH);
tableView->setColumnWidth(RecentRequestsTableModel::Label, LABEL_COLUMN_WIDTH);
tableView->setColumnWidth(RecentRequestsTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH);
tableView->horizontalHeader()->setMinimumSectionSize(MINIMUM_COLUMN_WIDTH);
tableView->horizontalHeader()->setStretchLastSection(true);
}
tableView->horizontalHeader()->setSortIndicator(RecentRequestsTableModel::Date, Qt::DescendingOrder);
}
void ReceiveCoinsDialog::setModel(WalletModel *_model)
{
this->model = _model;
if(_model && _model->getOptionsModel())
{
_model->getRecentRequestsTableModel()->sort(RecentRequestsTableModel::Date, Qt::DescendingOrder);
connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &ReceiveCoinsDialog::updateDisplayUnit);
updateDisplayUnit();
QTableView* tableView = ui->recentRequestsView;
tableView->setModel(_model->getRecentRequestsTableModel());
connect(tableView->selectionModel(),
&QItemSelectionModel::selectionChanged, this,
&ReceiveCoinsDialog::recentRequestsView_selectionChanged);
if (model->wallet().getDefaultAddressType() == OutputType::BECH32) {
ui->useBech32->setCheckState(Qt::Checked);
} else {
ui->useBech32->setCheckState(Qt::Unchecked);
}
// Set the button to be enabled or disabled based on whether the wallet can give out new addresses.
ui->receiveButton->setEnabled(model->wallet().canGetAddresses());
// Enable/disable the receive button if the wallet is now able/unable to give out new addresses.
connect(model, &WalletModel::canGetAddressesChanged, [this] {
ui->receiveButton->setEnabled(model->wallet().canGetAddresses());
});
}
}
ReceiveCoinsDialog::~ReceiveCoinsDialog()
{
QSettings settings;
settings.setValue("RecentRequestsViewHeaderState", ui->recentRequestsView->horizontalHeader()->saveState());
delete ui;
}
void ReceiveCoinsDialog::clear()
{
ui->reqAmount->clear();
ui->reqLabel->setText("");
ui->reqMessage->setText("");
updateDisplayUnit();
}
void ReceiveCoinsDialog::reject()
{
clear();
}
void ReceiveCoinsDialog::accept()
{
clear();
}
void ReceiveCoinsDialog::updateDisplayUnit()
{
if(model && model->getOptionsModel())
{
ui->reqAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
}
}
void ReceiveCoinsDialog::on_receiveButton_clicked()
{
if(!model || !model->getOptionsModel() || !model->getAddressTableModel() || !model->getRecentRequestsTableModel())
return;
QString address;
QString label = ui->reqLabel->text();
/* Generate new receiving address */
OutputType address_type;
if (ui->useBech32->isChecked()) {
address_type = OutputType::BECH32;
} else {
address_type = model->wallet().getDefaultAddressType();
if (address_type == OutputType::BECH32) {
address_type = OutputType::P2SH_SEGWIT;
}
}
address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, "", address_type);
switch(model->getAddressTableModel()->getEditStatus())
{
case AddressTableModel::EditStatus::OK: {
// Success
SendCoinsRecipient info(address, label,
ui->reqAmount->value(), ui->reqMessage->text());
ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setModel(model);
dialog->setInfo(info);
dialog->show();
/* Store request for later reference */
model->getRecentRequestsTableModel()->addNewRequest(info);
break;
}
case AddressTableModel::EditStatus::WALLET_UNLOCK_FAILURE:
QMessageBox::critical(this, windowTitle(),
tr("Could not unlock wallet."),
QMessageBox::Ok, QMessageBox::Ok);
break;
case AddressTableModel::EditStatus::KEY_GENERATION_FAILURE:
QMessageBox::critical(this, windowTitle(),
tr("Could not generate new %1 address").arg(QString::fromStdString(FormatOutputType(address_type))),
QMessageBox::Ok, QMessageBox::Ok);
break;
// These aren't valid return values for our action
case AddressTableModel::EditStatus::INVALID_ADDRESS:
case AddressTableModel::EditStatus::DUPLICATE_ADDRESS:
case AddressTableModel::EditStatus::NO_CHANGES:
assert(false);
}
clear();
}
void ReceiveCoinsDialog::on_recentRequestsView_doubleClicked(const QModelIndex &index)
{
const RecentRequestsTableModel *submodel = model->getRecentRequestsTableModel();
ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
dialog->setModel(model);
dialog->setInfo(submodel->entry(index.row()).recipient);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show();
}
void ReceiveCoinsDialog::recentRequestsView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
// Enable Show/Remove buttons only if anything is selected.
bool enable = !ui->recentRequestsView->selectionModel()->selectedRows().isEmpty();
ui->showRequestButton->setEnabled(enable);
ui->removeRequestButton->setEnabled(enable);
}
void ReceiveCoinsDialog::on_showRequestButton_clicked()
{
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
return;
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
for (const QModelIndex& index : selection) {
on_recentRequestsView_doubleClicked(index);
}
}
void ReceiveCoinsDialog::on_removeRequestButton_clicked()
{
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
return;
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
if(selection.empty())
return;
// correct for selection mode ContiguousSelection
QModelIndex firstIndex = selection.at(0);
model->getRecentRequestsTableModel()->removeRows(firstIndex.row(), selection.length(), firstIndex.parent());
}
QModelIndex ReceiveCoinsDialog::selectedRow()
{
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
return QModelIndex();
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
if(selection.empty())
return QModelIndex();
// correct for selection mode ContiguousSelection
QModelIndex firstIndex = selection.at(0);
return firstIndex;
}
// copy column of selected row to clipboard
void ReceiveCoinsDialog::copyColumnToClipboard(int column)
{
QModelIndex firstIndex = selectedRow();
if (!firstIndex.isValid()) {
return;
}
GUIUtil::setClipboard(model->getRecentRequestsTableModel()->index(firstIndex.row(), column).data(Qt::EditRole).toString());
}
// context menu
void ReceiveCoinsDialog::showMenu(const QPoint &point)
{
const QModelIndex sel = selectedRow();
if (!sel.isValid()) {
return;
}
// disable context menu actions when appropriate
const RecentRequestsTableModel* const submodel = model->getRecentRequestsTableModel();
const RecentRequestEntry& req = submodel->entry(sel.row());
copyLabelAction->setDisabled(req.recipient.label.isEmpty());
copyMessageAction->setDisabled(req.recipient.message.isEmpty());
copyAmountAction->setDisabled(req.recipient.amount == 0);
contextMenu->exec(QCursor::pos());
}
// context menu action: copy URI
void ReceiveCoinsDialog::copyURI()
{
QModelIndex sel = selectedRow();
if (!sel.isValid()) {
return;
}
const RecentRequestsTableModel * const submodel = model->getRecentRequestsTableModel();
const QString uri = GUIUtil::formatBitcoinURI(submodel->entry(sel.row()).recipient);
GUIUtil::setClipboard(uri);
}
// context menu action: copy address
void ReceiveCoinsDialog::copyAddress()
{
const QModelIndex sel = selectedRow();
if (!sel.isValid()) {
return;
}
const RecentRequestsTableModel* const submodel = model->getRecentRequestsTableModel();
const QString address = submodel->entry(sel.row()).recipient.address;
GUIUtil::setClipboard(address);
}
// context menu action: copy label
void ReceiveCoinsDialog::copyLabel()
{
copyColumnToClipboard(RecentRequestsTableModel::Label);
}
// context menu action: copy message
void ReceiveCoinsDialog::copyMessage()
{
copyColumnToClipboard(RecentRequestsTableModel::Message);
}
// context menu action: copy amount
void ReceiveCoinsDialog::copyAmount()
{
copyColumnToClipboard(RecentRequestsTableModel::Amount);
}