16c157de3c316517e095994fa8d526253225a672 qt, refactor: Use better QMenu::addAction overloaded function (Hennadii Stepanov) 79311750b58d650d49a3f0edd59d31dd132ab8c0 qt: Do not assign Alt+<KEY> shortcuts to context menu actions (Hennadii Stepanov) 963e12058f3ca3cdaeefd9aa5a8305fa41afd1a0 qt: Drop menu separator that separates nothing (Hennadii Stepanov) 1398a6536c710368d9f1d0cf6e280fe63d07c9f0 qt, refactor: Make AddressBookPage::deleteAction a local variable (Hennadii Stepanov) Pull request description: This PR: 1. removes useless `Alt` + `<KEY>` shortcuts from context menu items 2. replaces 3 lines of code with the only call of [`QMenu::addAction`](https://doc.qt.io/qt-5/qmenu.html#addAction-5) for each context menu item (it became possible since https://github.com/bitcoin/bitcoin/pull/21286 was merged) 3. makes other minor cleanups No behavior change. ACKs for top commit: kristapsk: ACK 16c157de3c316517e095994fa8d526253225a672 promag: Code review ACK 16c157de3c316517e095994fa8d526253225a672. Nice code cleanup that takes advantage of more recent Qt API. jarolrod: ACK 16c157de3c316517e095994fa8d526253225a672 Tree-SHA512: e5555fe957058cc67b351aaf9f09fe3635edb2d07a2223d3093913a25607ae538f0a2fde84c0b0cd43e7475b248949548eb4a5d4b21d8f7391fa2fa8541c04ff
323 lines
10 KiB
C++
323 lines
10 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.
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config/bitcoin-config.h>
|
|
#endif
|
|
|
|
#include <qt/addressbookpage.h>
|
|
#include <qt/forms/ui_addressbookpage.h>
|
|
|
|
#include <qt/addresstablemodel.h>
|
|
#include <qt/csvmodelwriter.h>
|
|
#include <qt/editaddressdialog.h>
|
|
#include <qt/guiutil.h>
|
|
#include <qt/platformstyle.h>
|
|
|
|
#include <QIcon>
|
|
#include <QMenu>
|
|
#include <QMessageBox>
|
|
#include <QSortFilterProxyModel>
|
|
|
|
class AddressBookSortFilterProxyModel final : public QSortFilterProxyModel
|
|
{
|
|
const QString m_type;
|
|
|
|
public:
|
|
AddressBookSortFilterProxyModel(const QString& type, QObject* parent)
|
|
: QSortFilterProxyModel(parent)
|
|
, m_type(type)
|
|
{
|
|
setDynamicSortFilter(true);
|
|
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
|
setSortCaseSensitivity(Qt::CaseInsensitive);
|
|
}
|
|
|
|
protected:
|
|
bool filterAcceptsRow(int row, const QModelIndex& parent) const override
|
|
{
|
|
auto model = sourceModel();
|
|
auto label = model->index(row, AddressTableModel::Label, parent);
|
|
|
|
if (model->data(label, AddressTableModel::TypeRole).toString() != m_type) {
|
|
return false;
|
|
}
|
|
|
|
auto address = model->index(row, AddressTableModel::Address, parent);
|
|
|
|
if (filterRegExp().indexIn(model->data(address).toString()) < 0 &&
|
|
filterRegExp().indexIn(model->data(label).toString()) < 0) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode, Tabs _tab, QWidget *parent) :
|
|
QDialog(parent, GUIUtil::dialog_flags),
|
|
ui(new Ui::AddressBookPage),
|
|
model(nullptr),
|
|
mode(_mode),
|
|
tab(_tab)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
if (!platformStyle->getImagesOnButtons()) {
|
|
ui->newAddress->setIcon(QIcon());
|
|
ui->copyAddress->setIcon(QIcon());
|
|
ui->deleteAddress->setIcon(QIcon());
|
|
ui->exportButton->setIcon(QIcon());
|
|
} else {
|
|
ui->newAddress->setIcon(platformStyle->SingleColorIcon(":/icons/add"));
|
|
ui->copyAddress->setIcon(platformStyle->SingleColorIcon(":/icons/editcopy"));
|
|
ui->deleteAddress->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
|
|
ui->exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
|
|
}
|
|
|
|
switch(mode)
|
|
{
|
|
case ForSelection:
|
|
switch(tab)
|
|
{
|
|
case SendingTab: setWindowTitle(tr("Choose the address to send coins to")); break;
|
|
case ReceivingTab: setWindowTitle(tr("Choose the address to receive coins with")); break;
|
|
}
|
|
connect(ui->tableView, &QTableView::doubleClicked, this, &QDialog::accept);
|
|
ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
ui->tableView->setFocus();
|
|
ui->closeButton->setText(tr("C&hoose"));
|
|
ui->exportButton->hide();
|
|
break;
|
|
case ForEditing:
|
|
switch(tab)
|
|
{
|
|
case SendingTab: setWindowTitle(tr("Sending addresses")); break;
|
|
case ReceivingTab: setWindowTitle(tr("Receiving addresses")); break;
|
|
}
|
|
break;
|
|
}
|
|
switch(tab)
|
|
{
|
|
case SendingTab:
|
|
ui->labelExplanation->setText(tr("These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins."));
|
|
ui->deleteAddress->setVisible(true);
|
|
ui->newAddress->setVisible(true);
|
|
break;
|
|
case ReceivingTab:
|
|
ui->labelExplanation->setText(tr("These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.\nSigning is only possible with addresses of the type 'legacy'."));
|
|
ui->deleteAddress->setVisible(false);
|
|
ui->newAddress->setVisible(false);
|
|
break;
|
|
}
|
|
|
|
// Build context menu
|
|
contextMenu = new QMenu(this);
|
|
contextMenu->addAction(tr("Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
|
|
contextMenu->addAction(tr("Copy Label"), this, &AddressBookPage::onCopyLabelAction);
|
|
contextMenu->addAction(tr("Edit"), this, &AddressBookPage::onEditAction);
|
|
|
|
if (tab == SendingTab) {
|
|
contextMenu->addAction(tr("Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
|
|
}
|
|
|
|
connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
|
|
connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::accept);
|
|
|
|
GUIUtil::handleCloseWindowShortcut(this);
|
|
}
|
|
|
|
AddressBookPage::~AddressBookPage()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void AddressBookPage::setModel(AddressTableModel *_model)
|
|
{
|
|
this->model = _model;
|
|
if(!_model)
|
|
return;
|
|
|
|
auto type = tab == ReceivingTab ? AddressTableModel::Receive : AddressTableModel::Send;
|
|
proxyModel = new AddressBookSortFilterProxyModel(type, this);
|
|
proxyModel->setSourceModel(_model);
|
|
|
|
connect(ui->searchLineEdit, &QLineEdit::textChanged, proxyModel, &QSortFilterProxyModel::setFilterWildcard);
|
|
|
|
ui->tableView->setModel(proxyModel);
|
|
ui->tableView->sortByColumn(0, Qt::AscendingOrder);
|
|
|
|
// Set column widths
|
|
ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
|
|
ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
|
|
|
|
connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
|
|
this, &AddressBookPage::selectionChanged);
|
|
|
|
// Select row for newly created address
|
|
connect(_model, &AddressTableModel::rowsInserted, this, &AddressBookPage::selectNewAddress);
|
|
|
|
selectionChanged();
|
|
}
|
|
|
|
void AddressBookPage::on_copyAddress_clicked()
|
|
{
|
|
GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address);
|
|
}
|
|
|
|
void AddressBookPage::onCopyLabelAction()
|
|
{
|
|
GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Label);
|
|
}
|
|
|
|
void AddressBookPage::onEditAction()
|
|
{
|
|
if(!model)
|
|
return;
|
|
|
|
if(!ui->tableView->selectionModel())
|
|
return;
|
|
QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
|
|
if(indexes.isEmpty())
|
|
return;
|
|
|
|
EditAddressDialog dlg(
|
|
tab == SendingTab ?
|
|
EditAddressDialog::EditSendingAddress :
|
|
EditAddressDialog::EditReceivingAddress, this);
|
|
dlg.setModel(model);
|
|
QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
|
|
dlg.loadRow(origIndex.row());
|
|
dlg.exec();
|
|
}
|
|
|
|
void AddressBookPage::on_newAddress_clicked()
|
|
{
|
|
if(!model)
|
|
return;
|
|
|
|
if (tab == ReceivingTab) {
|
|
return;
|
|
}
|
|
|
|
EditAddressDialog dlg(EditAddressDialog::NewSendingAddress, this);
|
|
dlg.setModel(model);
|
|
if(dlg.exec())
|
|
{
|
|
newAddressToSelect = dlg.getAddress();
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::on_deleteAddress_clicked()
|
|
{
|
|
QTableView *table = ui->tableView;
|
|
if(!table->selectionModel())
|
|
return;
|
|
|
|
QModelIndexList indexes = table->selectionModel()->selectedRows();
|
|
if(!indexes.isEmpty())
|
|
{
|
|
table->model()->removeRow(indexes.at(0).row());
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::selectionChanged()
|
|
{
|
|
// Set button states based on selected tab and selection
|
|
QTableView *table = ui->tableView;
|
|
if(!table->selectionModel())
|
|
return;
|
|
|
|
if(table->selectionModel()->hasSelection())
|
|
{
|
|
switch(tab)
|
|
{
|
|
case SendingTab:
|
|
// In sending tab, allow deletion of selection
|
|
ui->deleteAddress->setEnabled(true);
|
|
ui->deleteAddress->setVisible(true);
|
|
break;
|
|
case ReceivingTab:
|
|
// Deleting receiving addresses, however, is not allowed
|
|
ui->deleteAddress->setEnabled(false);
|
|
ui->deleteAddress->setVisible(false);
|
|
break;
|
|
}
|
|
ui->copyAddress->setEnabled(true);
|
|
}
|
|
else
|
|
{
|
|
ui->deleteAddress->setEnabled(false);
|
|
ui->copyAddress->setEnabled(false);
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::done(int retval)
|
|
{
|
|
QTableView *table = ui->tableView;
|
|
if(!table->selectionModel() || !table->model())
|
|
return;
|
|
|
|
// Figure out which address was selected, and return it
|
|
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
|
|
|
|
for (const QModelIndex& index : indexes) {
|
|
QVariant address = table->model()->data(index);
|
|
returnValue = address.toString();
|
|
}
|
|
|
|
if(returnValue.isEmpty())
|
|
{
|
|
// If no address entry selected, return rejected
|
|
retval = Rejected;
|
|
}
|
|
|
|
QDialog::done(retval);
|
|
}
|
|
|
|
void AddressBookPage::on_exportButton_clicked()
|
|
{
|
|
// CSV is currently the only supported format
|
|
QString filename = GUIUtil::getSaveFileName(this,
|
|
tr("Export Address List"), QString(),
|
|
tr("Comma separated file", "Name of CSV file format") + QLatin1String(" (*.csv)"), nullptr);
|
|
|
|
if (filename.isNull())
|
|
return;
|
|
|
|
CSVModelWriter writer(filename);
|
|
|
|
// name, column, role
|
|
writer.setModel(proxyModel);
|
|
writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
|
|
writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
|
|
|
|
if(!writer.write()) {
|
|
QMessageBox::critical(this, tr("Exporting Failed"),
|
|
//: %1 is a name of the file (e.g., "addrbook.csv") that the bitcoin addresses were exported to.
|
|
tr("There was an error trying to save the address list to %1. Please try again.", "An error message.").arg(filename));
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::contextualMenu(const QPoint &point)
|
|
{
|
|
QModelIndex index = ui->tableView->indexAt(point);
|
|
if(index.isValid())
|
|
{
|
|
contextMenu->exec(QCursor::pos());
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int /*end*/)
|
|
{
|
|
QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
|
|
if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
|
|
{
|
|
// Select row of newly created address, once
|
|
ui->tableView->setFocus();
|
|
ui->tableView->selectRow(idx.row());
|
|
newAddressToSelect.clear();
|
|
}
|
|
}
|