diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 58b031bae..c62498fde 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -1,345 +1,348 @@ // 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); + ui->newAddress->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); + ui->newAddress->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; } auto type = tab == ReceivingTab ? AddressTableModel::Receive : AddressTableModel::Send; proxyModel = new AddressBookSortFilterProxyModel(type, this); proxyModel->setSourceModel(_model); 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); + 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); 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/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 474962c12..bebf8b8ef 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -1,152 +1,145 @@ // 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. #include #include #include #include #include #include EditAddressDialog::EditAddressDialog(Mode _mode, QWidget *parent) : QDialog(parent), ui(new Ui::EditAddressDialog), mapper(0), mode(_mode), model(0) { ui->setupUi(this); GUIUtil::setupAddressWidget(ui->addressEdit, this); switch (mode) { - case NewReceivingAddress: - setWindowTitle(tr("New receiving address")); - ui->addressEdit->setEnabled(false); - break; case NewSendingAddress: setWindowTitle(tr("New sending address")); break; case EditReceivingAddress: setWindowTitle(tr("Edit receiving address")); ui->addressEdit->setEnabled(false); break; case EditSendingAddress: setWindowTitle(tr("Edit sending address")); break; } mapper = new QDataWidgetMapper(this); mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); } EditAddressDialog::~EditAddressDialog() { delete ui; } void EditAddressDialog::setModel(AddressTableModel *_model) { this->model = _model; if (!_model) { return; } mapper->setModel(_model); mapper->addMapping(ui->labelEdit, AddressTableModel::Label); mapper->addMapping(ui->addressEdit, AddressTableModel::Address); } void EditAddressDialog::loadRow(int row) { mapper->setCurrentIndex(row); } bool EditAddressDialog::saveCurrentRow() { if (!model) { return false; } switch (mode) { - case NewReceivingAddress: case NewSendingAddress: address = model->addRow( - mode == NewSendingAddress ? AddressTableModel::Send - : AddressTableModel::Receive, - ui->labelEdit->text(), ui->addressEdit->text(), - model->GetDefaultAddressType()); + AddressTableModel::Send, ui->labelEdit->text(), + ui->addressEdit->text(), model->GetDefaultAddressType()); break; case EditReceivingAddress: case EditSendingAddress: if (mapper->submit()) { address = ui->addressEdit->text(); } break; } return !address.isEmpty(); } void EditAddressDialog::accept() { if (!model) { return; } if (!saveCurrentRow()) { switch (model->getEditStatus()) { case AddressTableModel::OK: // Failed with unknown reason. Just reject. break; case AddressTableModel::NO_CHANGES: // No changes were made during edit operation. Just reject. break; case AddressTableModel::INVALID_ADDRESS: QMessageBox::warning(this, windowTitle(), tr("The entered address \"%1\" is not a " "valid Bitcoin address.") .arg(ui->addressEdit->text()), QMessageBox::Ok, QMessageBox::Ok); break; case AddressTableModel::DUPLICATE_ADDRESS: QMessageBox::warning(this, windowTitle(), getDuplicateAddressWarning(), QMessageBox::Ok, QMessageBox::Ok); break; case AddressTableModel::WALLET_UNLOCK_FAILURE: QMessageBox::critical(this, windowTitle(), tr("Could not unlock wallet."), QMessageBox::Ok, QMessageBox::Ok); break; case AddressTableModel::KEY_GENERATION_FAILURE: QMessageBox::critical(this, windowTitle(), tr("New key generation failed."), QMessageBox::Ok, QMessageBox::Ok); break; } return; } QDialog::accept(); } QString EditAddressDialog::getDuplicateAddressWarning() const { QString dup_address = ui->addressEdit->text(); QString existing_label = model->labelForAddress(dup_address); QString existing_purpose = model->purposeForAddress(dup_address); if (existing_purpose == "receive" && (mode == NewSendingAddress || mode == EditSendingAddress)) { return tr("Address \"%1\" already exists as a receiving address with " "label " "\"%2\" and so cannot be added as a sending address.") .arg(dup_address) .arg(existing_label); } return tr("The entered address \"%1\" is already in the address book with " "label \"%2\".") .arg(dup_address) .arg(existing_label); } QString EditAddressDialog::getAddress() const { return address; } void EditAddressDialog::setAddress(const QString &_address) { this->address = _address; ui->addressEdit->setText(_address); } diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h index 1c96ac4c2..d3f5159f2 100644 --- a/src/qt/editaddressdialog.h +++ b/src/qt/editaddressdialog.h @@ -1,63 +1,62 @@ // 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_EDITADDRESSDIALOG_H #define BITCOIN_QT_EDITADDRESSDIALOG_H #include class AddressTableModel; namespace Ui { class EditAddressDialog; } QT_BEGIN_NAMESPACE class QDataWidgetMapper; QT_END_NAMESPACE /** * Dialog for editing an address and associated information. */ class EditAddressDialog : public QDialog { Q_OBJECT public: enum Mode { - NewReceivingAddress, NewSendingAddress, EditReceivingAddress, EditSendingAddress }; explicit EditAddressDialog(Mode mode, QWidget *parent = nullptr); ~EditAddressDialog(); void setModel(AddressTableModel *model); void loadRow(int row); QString getAddress() const; void setAddress(const QString &address); public Q_SLOTS: void accept() override; private: bool saveCurrentRow(); /** * Return a descriptive string when adding an already-existing address * fails. */ QString getDuplicateAddressWarning() const; Ui::EditAddressDialog *ui; QDataWidgetMapper *mapper; Mode mode; AddressTableModel *model; QString address; }; #endif // BITCOIN_QT_EDITADDRESSDIALOG_H