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