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