Bitcoin Core 22.99.0
P2P Digital Currency
addressbookpage.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2020 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#if defined(HAVE_CONFIG_H)
7#endif
8
11
13#include <qt/csvmodelwriter.h>
15#include <qt/guiutil.h>
16#include <qt/platformstyle.h>
17
18#include <QIcon>
19#include <QMenu>
20#include <QMessageBox>
21#include <QSortFilterProxyModel>
22
23class AddressBookSortFilterProxyModel final : public QSortFilterProxyModel
24{
25 const QString m_type;
26
27public:
28 AddressBookSortFilterProxyModel(const QString& type, QObject* parent)
29 : QSortFilterProxyModel(parent)
30 , m_type(type)
31 {
32 setDynamicSortFilter(true);
33 setFilterCaseSensitivity(Qt::CaseInsensitive);
34 setSortCaseSensitivity(Qt::CaseInsensitive);
35 }
36
37protected:
38 bool filterAcceptsRow(int row, const QModelIndex& parent) const override
39 {
40 auto model = sourceModel();
41 auto label = model->index(row, AddressTableModel::Label, parent);
42
43 if (model->data(label, AddressTableModel::TypeRole).toString() != m_type) {
44 return false;
45 }
46
47 auto address = model->index(row, AddressTableModel::Address, parent);
48
49 if (filterRegExp().indexIn(model->data(address).toString()) < 0 &&
50 filterRegExp().indexIn(model->data(label).toString()) < 0) {
51 return false;
52 }
53
54 return true;
55 }
56};
57
58AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode, Tabs _tab, QWidget *parent) :
59 QDialog(parent, GUIUtil::dialog_flags),
60 ui(new Ui::AddressBookPage),
61 model(nullptr),
62 mode(_mode),
63 tab(_tab)
64{
65 ui->setupUi(this);
66
67 if (!platformStyle->getImagesOnButtons()) {
68 ui->newAddress->setIcon(QIcon());
69 ui->copyAddress->setIcon(QIcon());
70 ui->deleteAddress->setIcon(QIcon());
71 ui->exportButton->setIcon(QIcon());
72 } else {
73 ui->newAddress->setIcon(platformStyle->SingleColorIcon(":/icons/add"));
74 ui->copyAddress->setIcon(platformStyle->SingleColorIcon(":/icons/editcopy"));
75 ui->deleteAddress->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
76 ui->exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
77 }
78
79 switch(mode)
80 {
81 case ForSelection:
82 switch(tab)
83 {
84 case SendingTab: setWindowTitle(tr("Choose the address to send coins to")); break;
85 case ReceivingTab: setWindowTitle(tr("Choose the address to receive coins with")); break;
86 }
87 connect(ui->tableView, &QTableView::doubleClicked, this, &QDialog::accept);
88 ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
89 ui->tableView->setFocus();
90 ui->closeButton->setText(tr("C&hoose"));
91 ui->exportButton->hide();
92 break;
93 case ForEditing:
94 switch(tab)
95 {
96 case SendingTab: setWindowTitle(tr("Sending addresses")); break;
97 case ReceivingTab: setWindowTitle(tr("Receiving addresses")); break;
98 }
99 break;
100 }
101 switch(tab)
102 {
103 case SendingTab:
104 ui->labelExplanation->setText(tr("These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins."));
105 ui->deleteAddress->setVisible(true);
106 ui->newAddress->setVisible(true);
107 break;
108 case ReceivingTab:
109 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'."));
110 ui->deleteAddress->setVisible(false);
111 ui->newAddress->setVisible(false);
112 break;
113 }
114
115 // Build context menu
116 contextMenu = new QMenu(this);
117 contextMenu->addAction(tr("&Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
118 contextMenu->addAction(tr("Copy &Label"), this, &AddressBookPage::onCopyLabelAction);
119 contextMenu->addAction(tr("&Edit"), this, &AddressBookPage::onEditAction);
120
121 if (tab == SendingTab) {
122 contextMenu->addAction(tr("&Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
123 }
124
125 connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
126 connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::accept);
127
129}
130
132{
133 delete ui;
134}
135
137{
138 this->model = _model;
139 if(!_model)
140 return;
141
144 proxyModel->setSourceModel(_model);
145
146 connect(ui->searchLineEdit, &QLineEdit::textChanged, proxyModel, &QSortFilterProxyModel::setFilterWildcard);
147
148 ui->tableView->setModel(proxyModel);
149 ui->tableView->sortByColumn(0, Qt::AscendingOrder);
150
151 // Set column widths
152 ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
153 ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
154
155 connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
157
158 // Select row for newly created address
159 connect(_model, &AddressTableModel::rowsInserted, this, &AddressBookPage::selectNewAddress);
160
162}
163
165{
167}
168
170{
172}
173
175{
176 if(!model)
177 return;
178
179 if(!ui->tableView->selectionModel())
180 return;
181 QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
182 if(indexes.isEmpty())
183 return;
184
185 auto dlg = new EditAddressDialog(
186 tab == SendingTab ?
189 dlg->setModel(model);
190 QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
191 dlg->loadRow(origIndex.row());
193}
194
196{
197 if(!model)
198 return;
199
200 if (tab == ReceivingTab) {
201 return;
202 }
203
205 dlg.setModel(model);
206 if(dlg.exec())
207 {
209 }
210}
211
213{
214 QTableView *table = ui->tableView;
215 if(!table->selectionModel())
216 return;
217
218 QModelIndexList indexes = table->selectionModel()->selectedRows();
219 if(!indexes.isEmpty())
220 {
221 table->model()->removeRow(indexes.at(0).row());
222 }
223}
224
226{
227 // Set button states based on selected tab and selection
228 QTableView *table = ui->tableView;
229 if(!table->selectionModel())
230 return;
231
232 if(table->selectionModel()->hasSelection())
233 {
234 switch(tab)
235 {
236 case SendingTab:
237 // In sending tab, allow deletion of selection
238 ui->deleteAddress->setEnabled(true);
239 ui->deleteAddress->setVisible(true);
240 break;
241 case ReceivingTab:
242 // Deleting receiving addresses, however, is not allowed
243 ui->deleteAddress->setEnabled(false);
244 ui->deleteAddress->setVisible(false);
245 break;
246 }
247 ui->copyAddress->setEnabled(true);
248 }
249 else
250 {
251 ui->deleteAddress->setEnabled(false);
252 ui->copyAddress->setEnabled(false);
253 }
254}
255
256void AddressBookPage::done(int retval)
257{
258 QTableView *table = ui->tableView;
259 if(!table->selectionModel() || !table->model())
260 return;
261
262 // Figure out which address was selected, and return it
263 QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
264
265 for (const QModelIndex& index : indexes) {
266 QVariant address = table->model()->data(index);
267 returnValue = address.toString();
268 }
269
270 if(returnValue.isEmpty())
271 {
272 // If no address entry selected, return rejected
273 retval = Rejected;
274 }
275
276 QDialog::done(retval);
277}
278
280{
281 // CSV is currently the only supported format
282 QString filename = GUIUtil::getSaveFileName(this,
283 tr("Export Address List"), QString(),
284 /*: Expanded name of the CSV file format.
285 See: https://en.wikipedia.org/wiki/Comma-separated_values. */
286 tr("Comma separated file") + QLatin1String(" (*.csv)"), nullptr);
287
288 if (filename.isNull())
289 return;
290
291 CSVModelWriter writer(filename);
292
293 // name, column, role
294 writer.setModel(proxyModel);
295 writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
296 writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
297
298 if(!writer.write()) {
299 QMessageBox::critical(this, tr("Exporting Failed"),
300 /*: An error message. %1 is a stand-in argument for the name
301 of the file we attempted to save to. */
302 tr("There was an error trying to save the address list to %1. Please try again.").arg(filename));
303 }
304}
305
306void AddressBookPage::contextualMenu(const QPoint &point)
307{
308 QModelIndex index = ui->tableView->indexAt(point);
309 if(index.isValid())
310 {
311 contextMenu->exec(QCursor::pos());
312 }
313}
314
315void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int /*end*/)
316{
317 QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
318 if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
319 {
320 // Select row of newly created address, once
321 ui->tableView->setFocus();
322 ui->tableView->selectRow(idx.row());
323 newAddressToSelect.clear();
324 }
325}
Widget that shows a list of sending or receiving addresses.
Ui::AddressBookPage * ui
void onEditAction()
Edit currently selected address entry (no button)
@ ForEditing
Open address book for editing.
@ ForSelection
Open address book to pick address.
void setModel(AddressTableModel *model)
void onCopyLabelAction()
Copy label of currently selected address entry to clipboard (no button)
QString newAddressToSelect
void done(int retval) override
AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent=nullptr)
void on_copyAddress_clicked()
Copy address of currently selected address entry to clipboard.
void on_exportButton_clicked()
Export button clicked.
void on_deleteAddress_clicked()
Delete currently selected address entry.
void contextualMenu(const QPoint &point)
Spawn contextual menu (right mouse menu) for address book entry.
AddressTableModel * model
void selectionChanged()
Set button states based on selected tab and selection.
void selectNewAddress(const QModelIndex &parent, int begin, int)
New entry/entries were added to address table.
void on_newAddress_clicked()
Create a new address for receiving coins and / or add a new address book entry.
AddressBookSortFilterProxyModel * proxyModel
bool filterAcceptsRow(int row, const QModelIndex &parent) const override
AddressBookSortFilterProxyModel(const QString &type, QObject *parent)
Qt model of the address book in the core.
@ TypeRole
Type of address (Send or Receive)
@ Address
Bitcoin address.
@ Label
User specified label.
QModelIndex index(int row, int column, const QModelIndex &parent) const override
static const QString Send
Specifies send address.
static const QString Receive
Specifies receive address.
Export a Qt table model to a CSV file.
bool write()
Perform export of the model to CSV.
void setModel(const QAbstractItemModel *model)
void addColumn(const QString &title, int column, int role=Qt::EditRole)
Dialog for editing an address and associated information.
void setModel(AddressTableModel *model)
QString getAddress() const
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
bool getImagesOnButtons() const
Definition: platformstyle.h:21
QPushButton * newAddress
void setupUi(QWidget *AddressBookPage)
QPushButton * copyAddress
QLineEdit * searchLineEdit
QPushButton * exportButton
QPushButton * deleteAddress
QPushButton * closeButton
Utility functions used by the Bitcoin Qt UI.
Definition: bitcoingui.h:59
void handleCloseWindowShortcut(QWidget *w)
Definition: guiutil.cpp:409
void copyEntryData(const QAbstractItemView *view, int column, int role)
Copy a field of the currently selected entry of a view to the clipboard.
Definition: guiutil.cpp:248
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedSuffixOut)
Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix when ...
Definition: guiutil.cpp:286
constexpr auto dialog_flags
Definition: guiutil.h:60
void ShowModalDialogAndDeleteOnClose(QDialog *dialog)
Shows a QDialog instance asynchronously, and deletes it on close.
Definition: guiutil.cpp:980