diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 62b9816f7..58b031bae 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -1,322 +1,345 @@ // Copyright (c) 2011-2016 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include +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 { + 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), ui(new Ui::AddressBookPage), model(0), 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, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(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); break; case ReceivingTab: ui->labelExplanation->setText( tr("These are your Bitcoin addresses for receiving payments. " "It is recommended to use a new receiving address for each " "transaction.")); ui->deleteAddress->setVisible(false); break; } // Context menu actions QAction *copyAddressAction = new QAction(tr("&Copy Address"), this); QAction *copyLabelAction = new QAction(tr("Copy &Label"), this); QAction *editAction = new QAction(tr("&Edit"), this); deleteAction = new QAction(ui->deleteAddress->text(), this); // Build context menu contextMenu = new QMenu(this); contextMenu->addAction(copyAddressAction); contextMenu->addAction(copyLabelAction); contextMenu->addAction(editAction); if (tab == SendingTab) { contextMenu->addAction(deleteAction); } contextMenu->addSeparator(); // Connect signals for context menu actions connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyAddress_clicked())); connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction())); connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction())); connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteAddress_clicked())); connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(accept())); } AddressBookPage::~AddressBookPage() { delete ui; } void AddressBookPage::setModel(AddressTableModel *_model) { this->model = _model; if (!_model) { return; } - proxyModel = new QSortFilterProxyModel(this); + auto type = tab == ReceivingTab ? AddressTableModel::Receive + : AddressTableModel::Send; + proxyModel = new AddressBookSortFilterProxyModel(type, this); proxyModel->setSourceModel(_model); - proxyModel->setDynamicSortFilter(true); - proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); - proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - switch (tab) { - case ReceivingTab: - // Receive filter - proxyModel->setFilterRole(AddressTableModel::TypeRole); - proxyModel->setFilterFixedString(AddressTableModel::Receive); - break; - case SendingTab: - // Send filter - proxyModel->setFilterRole(AddressTableModel::TypeRole); - proxyModel->setFilterFixedString(AddressTableModel::Send); - break; - } + + connect(ui->searchLineEdit, SIGNAL(textChanged(QString)), proxyModel, + SLOT(setFilterWildcard(QString))); + 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(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(selectionChanged())); // Select row for newly created address connect(_model, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(selectNewAddress(QModelIndex, int, int))); 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; } EditAddressDialog dlg(tab == SendingTab ? EditAddressDialog::NewSendingAddress : EditAddressDialog::NewReceivingAddress, 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); deleteAction->setEnabled(true); break; case ReceivingTab: // Deleting receiving addresses, however, is not allowed ui->deleteAddress->setEnabled(false); ui->deleteAddress->setVisible(false); deleteAction->setEnabled(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 (*.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"), tr("There was an error trying to save the " "address list to %1. Please try again.") .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(); } } diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 6cb7b4d7d..0fbb904a0 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -1,88 +1,88 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_ADDRESSBOOKPAGE_H #define BITCOIN_QT_ADDRESSBOOKPAGE_H #include +class AddressBookSortFilterProxyModel; class AddressTableModel; class PlatformStyle; namespace Ui { class AddressBookPage; } QT_BEGIN_NAMESPACE class QItemSelection; class QMenu; class QModelIndex; -class QSortFilterProxyModel; QT_END_NAMESPACE /** Widget that shows a list of sending or receiving addresses. */ class AddressBookPage : public QDialog { Q_OBJECT public: enum Tabs { SendingTab = 0, ReceivingTab = 1 }; enum Mode { ForSelection, /**< Open address book to pick address */ ForEditing /**< Open address book for editing */ }; explicit AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent); ~AddressBookPage(); void setModel(AddressTableModel *model); const QString &getReturnValue() const { return returnValue; } public Q_SLOTS: void done(int retval) override; private: Ui::AddressBookPage *ui; AddressTableModel *model; Mode mode; Tabs tab; QString returnValue; - QSortFilterProxyModel *proxyModel; + AddressBookSortFilterProxyModel *proxyModel; QMenu *contextMenu; // to be able to explicitly disable it QAction *deleteAction; QString newAddressToSelect; private Q_SLOTS: /** Delete currently selected address entry */ void on_deleteAddress_clicked(); /** * Create a new address for receiving coins and / or add a new address book * entry. */ void on_newAddress_clicked(); /** Copy address of currently selected address entry to clipboard */ void on_copyAddress_clicked(); /** * Copy label of currently selected address entry to clipboard (no button) */ void onCopyLabelAction(); /** Edit currently selected address entry (no button) */ void onEditAction(); /** Export button clicked */ void on_exportButton_clicked(); /** Set button states based on selected tab and selection */ void selectionChanged(); /** Spawn contextual menu (right mouse menu) for address book entry */ void contextualMenu(const QPoint &point); /** New entry/entries were added to address table */ void selectNewAddress(const QModelIndex &parent, int begin, int /*end*/); Q_SIGNALS: void sendCoins(QString addr); }; #endif // BITCOIN_QT_ADDRESSBOOKPAGE_H diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui index 264edeb72..7ac216286 100644 --- a/src/qt/forms/addressbookpage.ui +++ b/src/qt/forms/addressbookpage.ui @@ -1,159 +1,166 @@ AddressBookPage 0 0 760 380 Qt::PlainText true + + + + Enter address or label to search + + + Qt::CustomContextMenu Right-click to edit address or label false true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false Create a new address &New :/icons/add:/icons/add false Copy the currently selected address to the system clipboard &Copy :/icons/editcopy:/icons/editcopy false Delete the currently selected address from the list &Delete :/icons/remove:/icons/remove false Qt::Horizontal 40 20 Export the data in the current tab to a file &Export :/icons/export:/icons/export false 0 0 C&lose false