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 
9 #include <qt/addressbookpage.h>
10 #include <qt/forms/ui_addressbookpage.h>
11 
12 #include <qt/addresstablemodel.h>
13 #include <qt/csvmodelwriter.h>
14 #include <qt/editaddressdialog.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 
23 class AddressBookSortFilterProxyModel final : public QSortFilterProxyModel
24 {
25  const QString m_type;
26 
27 public:
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 
37 protected:
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 
58 AddressBookPage::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  EditAddressDialog dlg(
186  tab == SendingTab ?
189  dlg.setModel(model);
190  QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
191  dlg.loadRow(origIndex.row());
192  dlg.exec();
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 
256 void 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 
306 void 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 
315 void 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 }
AddressBookSortFilterProxyModel::AddressBookSortFilterProxyModel
AddressBookSortFilterProxyModel(const QString &type, QObject *parent)
Definition: addressbookpage.cpp:28
AddressBookPage::setModel
void setModel(AddressTableModel *model)
Definition: addressbookpage.cpp:136
AddressBookPage::SendingTab
@ SendingTab
Definition: addressbookpage.h:32
EditAddressDialog::loadRow
void loadRow(int row)
Definition: editaddressdialog.cpp:66
csvmodelwriter.h
AddressBookPage::on_deleteAddress_clicked
void on_deleteAddress_clicked()
Delete currently selected address entry.
Definition: addressbookpage.cpp:212
AddressBookPage::newAddressToSelect
QString newAddressToSelect
Definition: addressbookpage.h:58
AddressBookPage::on_exportButton_clicked
void on_exportButton_clicked()
Export button clicked.
Definition: addressbookpage.cpp:279
AddressBookPage::contextualMenu
void contextualMenu(const QPoint &point)
Spawn contextual menu (right mouse menu) for address book entry.
Definition: addressbookpage.cpp:306
PlatformStyle::SingleColorIcon
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
Definition: platformstyle.cpp:105
addressbookpage.h
CSVModelWriter::write
bool write()
Perform export of the model to CSV.
Definition: csvmodelwriter.cpp:49
EditAddressDialog::EditReceivingAddress
@ EditReceivingAddress
Definition: editaddressdialog.h:29
CSVModelWriter::addColumn
void addColumn(const QString &title, int column, int role=Qt::EditRole)
Definition: csvmodelwriter.cpp:22
AddressTableModel
Qt model of the address book in the core.
Definition: addresstablemodel.h:23
GUIUtil
Utility functions used by the Bitcoin Qt UI.
Definition: bitcoingui.h:59
AddressTableModel::Receive
static const QString Receive
Specifies receive address.
Definition: addresstablemodel.h:51
AddressBookPage::on_newAddress_clicked
void on_newAddress_clicked()
Create a new address for receiving coins and / or add a new address book entry.
Definition: addressbookpage.cpp:195
AddressBookPage::contextMenu
QMenu * contextMenu
Definition: addressbookpage.h:57
EditAddressDialog::setModel
void setModel(AddressTableModel *model)
Definition: editaddressdialog.cpp:55
bitcoin-config.h
GUIUtil::dialog_flags
constexpr auto dialog_flags
Definition: guiutil.h:59
EditAddressDialog::EditSendingAddress
@ EditSendingAddress
Definition: editaddressdialog.h:30
AddressBookSortFilterProxyModel::m_type
const QString m_type
Definition: addressbookpage.cpp:25
AddressBookPage::proxyModel
AddressBookSortFilterProxyModel * proxyModel
Definition: addressbookpage.h:56
AddressBookPage::Mode
Mode
Definition: addressbookpage.h:36
AddressBookPage::onEditAction
void onEditAction()
Edit currently selected address entry (no button)
Definition: addressbookpage.cpp:174
CSVModelWriter
Export a Qt table model to a CSV file.
Definition: csvmodelwriter.h:18
AddressBookPage::done
void done(int retval) override
Definition: addressbookpage.cpp:256
AddressBookPage::on_copyAddress_clicked
void on_copyAddress_clicked()
Copy address of currently selected address entry to clipboard.
Definition: addressbookpage.cpp:164
EditAddressDialog
Dialog for editing an address and associated information.
Definition: editaddressdialog.h:22
AddressBookPage::model
AddressTableModel * model
Definition: addressbookpage.h:52
AddressBookPage::returnValue
QString returnValue
Definition: addressbookpage.h:55
PlatformStyle::getImagesOnButtons
bool getImagesOnButtons() const
Definition: platformstyle.h:21
CSVModelWriter::setModel
void setModel(const QAbstractItemModel *model)
Definition: csvmodelwriter.cpp:17
AddressBookPage::selectionChanged
void selectionChanged()
Set button states based on selected tab and selection.
Definition: addressbookpage.cpp:225
AddressBookPage::ui
Ui::AddressBookPage * ui
Definition: addressbookpage.h:51
AddressBookPage
Widget that shows a list of sending or receiving addresses.
Definition: addressbookpage.h:26
AddressBookPage::selectNewAddress
void selectNewAddress(const QModelIndex &parent, int begin, int)
New entry/entries were added to address table.
Definition: addressbookpage.cpp:315
AddressTableModel::index
QModelIndex index(int row, int column, const QModelIndex &parent) const override
Definition: addresstablemodel.cpp:322
guiutil.h
AddressTableModel::Label
@ Label
User specified label.
Definition: addresstablemodel.h:32
EditAddressDialog::NewSendingAddress
@ NewSendingAddress
Definition: editaddressdialog.h:28
AddressBookPage::~AddressBookPage
~AddressBookPage()
Definition: addressbookpage.cpp:131
editaddressdialog.h
GUIUtil::getSaveFileName
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:279
platformstyle.h
Ui
Definition: addressbookpage.h:14
AddressTableModel::TypeRole
@ TypeRole
Type of address (Send or Receive)
Definition: addresstablemodel.h:37
GUIUtil::copyEntryData
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:247
AddressBookPage::tab
Tabs tab
Definition: addressbookpage.h:54
AddressBookSortFilterProxyModel
Definition: addressbookpage.cpp:23
AddressBookPage::AddressBookPage
AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent=nullptr)
Definition: addressbookpage.cpp:58
EditAddressDialog::getAddress
QString getAddress() const
Definition: editaddressdialog.cpp:157
AddressTableModel::Send
static const QString Send
Specifies send address.
Definition: addresstablemodel.h:50
GUIUtil::handleCloseWindowShortcut
void handleCloseWindowShortcut(QWidget *w)
Definition: guiutil.cpp:402
AddressBookPage::onCopyLabelAction
void onCopyLabelAction()
Copy label of currently selected address entry to clipboard (no button)
Definition: addressbookpage.cpp:169
AddressBookPage::mode
Mode mode
Definition: addressbookpage.h:53
PlatformStyle
Definition: platformstyle.h:13
addresstablemodel.h
AddressBookPage::Tabs
Tabs
Definition: addressbookpage.h:31
AddressBookSortFilterProxyModel::filterAcceptsRow
bool filterAcceptsRow(int row, const QModelIndex &parent) const override
Definition: addressbookpage.cpp:38
AddressBookPage::ForEditing
@ ForEditing
Open address book for editing.
Definition: addressbookpage.h:38
AddressTableModel::Address
@ Address
Bitcoin address.
Definition: addresstablemodel.h:33
AddressBookPage::ForSelection
@ ForSelection
Open address book to pick address.
Definition: addressbookpage.h:37
AddressBookPage::ReceivingTab
@ ReceivingTab
Definition: addressbookpage.h:33