Bitcoin ABC  0.26.3
P2P Digital Currency
addressbookpage.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2016 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)
6 #include <config/bitcoin-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  const QString m_type;
25 
26 public:
27  AddressBookSortFilterProxyModel(const QString &type, QObject *parent)
28  : QSortFilterProxyModel(parent), m_type(type) {
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  auto model = sourceModel();
37  auto label = model->index(row, AddressTableModel::Label, parent);
38 
39  if (model->data(label, AddressTableModel::TypeRole).toString() !=
40  m_type) {
41  return false;
42  }
43 
44  auto address = model->index(row, AddressTableModel::Address, parent);
45 
46  if (filterRegExp().indexIn(model->data(address).toString()) < 0 &&
47  filterRegExp().indexIn(model->data(label).toString()) < 0) {
48  return false;
49  }
50 
51  return true;
52  }
53 };
54 
56  Tabs _tab, QWidget *parent)
57  : QDialog(parent), ui(new Ui::AddressBookPage), model(nullptr), mode(_mode),
58  tab(_tab) {
59  ui->setupUi(this);
60 
61  if (!platformStyle->getImagesOnButtons()) {
62  ui->newAddress->setIcon(QIcon());
63  ui->copyAddress->setIcon(QIcon());
64  ui->deleteAddress->setIcon(QIcon());
65  ui->exportButton->setIcon(QIcon());
66  } else {
67  ui->newAddress->setIcon(platformStyle->SingleColorIcon(":/icons/add"));
68  ui->copyAddress->setIcon(
69  platformStyle->SingleColorIcon(":/icons/editcopy"));
70  ui->deleteAddress->setIcon(
71  platformStyle->SingleColorIcon(":/icons/remove"));
72  ui->exportButton->setIcon(
73  platformStyle->SingleColorIcon(":/icons/export"));
74  }
75 
76  switch (mode) {
77  case ForSelection:
78  switch (tab) {
79  case SendingTab:
80  setWindowTitle(tr("Choose the address to send coins to"));
81  break;
82  case ReceivingTab:
83  setWindowTitle(
84  tr("Choose the address to receive coins with"));
85  break;
86  }
87  connect(ui->tableView, &QTableView::doubleClicked, this,
88  &QDialog::accept);
89  ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
90  ui->tableView->setFocus();
91  ui->closeButton->setText(tr("C&hoose"));
92  ui->exportButton->hide();
93  break;
94  case ForEditing:
95  switch (tab) {
96  case SendingTab:
97  setWindowTitle(tr("Sending addresses"));
98  break;
99  case ReceivingTab:
100  setWindowTitle(tr("Receiving addresses"));
101  break;
102  }
103  break;
104  }
105  switch (tab) {
106  case SendingTab:
107  ui->labelExplanation->setText(
108  tr("These are your Bitcoin addresses for sending payments. "
109  "Always check the amount and the receiving address before "
110  "sending coins."));
111  ui->deleteAddress->setVisible(true);
112  ui->newAddress->setVisible(true);
113  break;
114  case ReceivingTab:
115  ui->labelExplanation->setText(
116  tr("These are your Bitcoin addresses for receiving payments. "
117  "Use the 'Create new receiving address' button in the "
118  "receive tab to create new addresses."));
119  ui->deleteAddress->setVisible(false);
120  ui->newAddress->setVisible(false);
121  break;
122  }
123 
124  // Context menu actions
125  QAction *copyAddressAction = new QAction(tr("&Copy Address"), this);
126  QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
127  QAction *editAction = new QAction(tr("&Edit"), this);
128  deleteAction = new QAction(ui->deleteAddress->text(), this);
129 
130  // Build context menu
131  contextMenu = new QMenu(this);
132  contextMenu->addAction(copyAddressAction);
133  contextMenu->addAction(copyLabelAction);
134  contextMenu->addAction(editAction);
135  if (tab == SendingTab) {
136  contextMenu->addAction(deleteAction);
137  }
138  contextMenu->addSeparator();
139 
140  // Connect signals for context menu actions
141  connect(copyAddressAction, &QAction::triggered, this,
143  connect(copyLabelAction, &QAction::triggered, this,
145  connect(editAction, &QAction::triggered, this,
147  connect(deleteAction, &QAction::triggered, this,
149 
150  connect(ui->tableView, &QWidget::customContextMenuRequested, this,
152 
153  connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::accept);
154 
156 }
157 
159  delete ui;
160 }
161 
163  this->model = _model;
164  if (!_model) {
165  return;
166  }
167 
171  proxyModel->setSourceModel(_model);
172 
173  connect(ui->searchLineEdit, &QLineEdit::textChanged, proxyModel,
174  &QSortFilterProxyModel::setFilterWildcard);
175 
176  ui->tableView->setModel(proxyModel);
177  ui->tableView->sortByColumn(0, Qt::AscendingOrder);
178 
179  // Set column widths
180  ui->tableView->horizontalHeader()->setSectionResizeMode(
181  AddressTableModel::Label, QHeaderView::Stretch);
182  ui->tableView->horizontalHeader()->setSectionResizeMode(
183  AddressTableModel::Address, QHeaderView::ResizeToContents);
184 
185  connect(ui->tableView->selectionModel(),
186  &QItemSelectionModel::selectionChanged, this,
188 
189  // Select row for newly created address
190  connect(_model, &AddressTableModel::rowsInserted, this,
192 
194 }
195 
198 }
199 
202 }
203 
205  if (!model) {
206  return;
207  }
208 
209  if (!ui->tableView->selectionModel()) {
210  return;
211  }
212  QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
213  if (indexes.isEmpty()) {
214  return;
215  }
216 
220  this);
221  dlg.setModel(model);
222  QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
223  dlg.loadRow(origIndex.row());
224  dlg.exec();
225 }
226 
228  if (!model) {
229  return;
230  }
231 
232  if (tab == ReceivingTab) {
233  return;
234  }
235 
237  dlg.setModel(model);
238  if (dlg.exec()) {
240  }
241 }
242 
244  QTableView *table = ui->tableView;
245  if (!table->selectionModel()) {
246  return;
247  }
248 
249  QModelIndexList indexes = table->selectionModel()->selectedRows();
250  if (!indexes.isEmpty()) {
251  table->model()->removeRow(indexes.at(0).row());
252  }
253 }
254 
256  // Set button states based on selected tab and selection
257  QTableView *table = ui->tableView;
258  if (!table->selectionModel()) {
259  return;
260  }
261 
262  if (table->selectionModel()->hasSelection()) {
263  switch (tab) {
264  case SendingTab:
265  // In sending tab, allow deletion of selection
266  ui->deleteAddress->setEnabled(true);
267  ui->deleteAddress->setVisible(true);
268  deleteAction->setEnabled(true);
269  break;
270  case ReceivingTab:
271  // Deleting receiving addresses, however, is not allowed
272  ui->deleteAddress->setEnabled(false);
273  ui->deleteAddress->setVisible(false);
274  deleteAction->setEnabled(false);
275  break;
276  }
277  ui->copyAddress->setEnabled(true);
278  } else {
279  ui->deleteAddress->setEnabled(false);
280  ui->copyAddress->setEnabled(false);
281  }
282 }
283 
284 void AddressBookPage::done(int retval) {
285  QTableView *table = ui->tableView;
286  if (!table->selectionModel() || !table->model()) {
287  return;
288  }
289 
290  // Figure out which address was selected, and return it
291  QModelIndexList indexes =
292  table->selectionModel()->selectedRows(AddressTableModel::Address);
293 
294  for (const QModelIndex &index : indexes) {
295  QVariant address = table->model()->data(index);
296  returnValue = address.toString();
297  }
298 
299  if (returnValue.isEmpty()) {
300  // If no address entry selected, return rejected
301  retval = Rejected;
302  }
303 
304  QDialog::done(retval);
305 }
306 
308  // CSV is currently the only supported format
309  QString filename =
310  GUIUtil::getSaveFileName(this, tr("Export Address List"), QString(),
311  tr("Comma separated file (*.csv)"), nullptr);
312 
313  if (filename.isNull()) {
314  return;
315  }
316 
317  CSVModelWriter writer(filename);
318 
319  // name, column, role
320  writer.setModel(proxyModel);
321  writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
322  writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
323 
324  if (!writer.write()) {
325  QMessageBox::critical(this, tr("Exporting Failed"),
326  tr("There was an error trying to save the "
327  "address list to %1. Please try again.")
328  .arg(filename));
329  }
330 }
331 
332 void AddressBookPage::contextualMenu(const QPoint &point) {
333  QModelIndex index = ui->tableView->indexAt(point);
334  if (index.isValid()) {
335  contextMenu->exec(QCursor::pos());
336  }
337 }
338 
339 void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin,
340  int /*end*/) {
341  QModelIndex idx = proxyModel->mapFromSource(
342  model->index(begin, AddressTableModel::Address, parent));
343  if (idx.isValid() &&
344  (idx.data(Qt::EditRole).toString() == newAddressToSelect)) {
345  // Select row of newly created address, once
346  ui->tableView->setFocus();
347  ui->tableView->selectRow(idx.row());
348  newAddressToSelect.clear();
349  }
350 }
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
QAction * deleteAction
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:20
void handleCloseWindowShortcut(QWidget *w)
Definition: guiutil.cpp:404
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:260
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:291