Bitcoin ABC  0.22.13
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 {
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 }
155 
157  delete ui;
158 }
159 
161  this->model = _model;
162  if (!_model) {
163  return;
164  }
165 
169  proxyModel->setSourceModel(_model);
170 
171  connect(ui->searchLineEdit, &QLineEdit::textChanged, proxyModel,
172  &QSortFilterProxyModel::setFilterWildcard);
173 
174  ui->tableView->setModel(proxyModel);
175  ui->tableView->sortByColumn(0, Qt::AscendingOrder);
176 
177  // Set column widths
178  ui->tableView->horizontalHeader()->setSectionResizeMode(
179  AddressTableModel::Label, QHeaderView::Stretch);
180  ui->tableView->horizontalHeader()->setSectionResizeMode(
181  AddressTableModel::Address, QHeaderView::ResizeToContents);
182 
183  connect(ui->tableView->selectionModel(),
184  &QItemSelectionModel::selectionChanged, this,
186 
187  // Select row for newly created address
188  connect(_model, &AddressTableModel::rowsInserted, this,
190 
192 }
193 
196 }
197 
200 }
201 
203  if (!model) {
204  return;
205  }
206 
207  if (!ui->tableView->selectionModel()) {
208  return;
209  }
210  QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
211  if (indexes.isEmpty()) {
212  return;
213  }
214 
218  this);
219  dlg.setModel(model);
220  QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
221  dlg.loadRow(origIndex.row());
222  dlg.exec();
223 }
224 
226  if (!model) {
227  return;
228  }
229 
230  if (tab == ReceivingTab) {
231  return;
232  }
233 
235  dlg.setModel(model);
236  if (dlg.exec()) {
238  }
239 }
240 
242  QTableView *table = ui->tableView;
243  if (!table->selectionModel()) {
244  return;
245  }
246 
247  QModelIndexList indexes = table->selectionModel()->selectedRows();
248  if (!indexes.isEmpty()) {
249  table->model()->removeRow(indexes.at(0).row());
250  }
251 }
252 
254  // Set button states based on selected tab and selection
255  QTableView *table = ui->tableView;
256  if (!table->selectionModel()) {
257  return;
258  }
259 
260  if (table->selectionModel()->hasSelection()) {
261  switch (tab) {
262  case SendingTab:
263  // In sending tab, allow deletion of selection
264  ui->deleteAddress->setEnabled(true);
265  ui->deleteAddress->setVisible(true);
266  deleteAction->setEnabled(true);
267  break;
268  case ReceivingTab:
269  // Deleting receiving addresses, however, is not allowed
270  ui->deleteAddress->setEnabled(false);
271  ui->deleteAddress->setVisible(false);
272  deleteAction->setEnabled(false);
273  break;
274  }
275  ui->copyAddress->setEnabled(true);
276  } else {
277  ui->deleteAddress->setEnabled(false);
278  ui->copyAddress->setEnabled(false);
279  }
280 }
281 
282 void AddressBookPage::done(int retval) {
283  QTableView *table = ui->tableView;
284  if (!table->selectionModel() || !table->model()) {
285  return;
286  }
287 
288  // Figure out which address was selected, and return it
289  QModelIndexList indexes =
290  table->selectionModel()->selectedRows(AddressTableModel::Address);
291 
292  for (const QModelIndex &index : indexes) {
293  QVariant address = table->model()->data(index);
294  returnValue = address.toString();
295  }
296 
297  if (returnValue.isEmpty()) {
298  // If no address entry selected, return rejected
299  retval = Rejected;
300  }
301 
302  QDialog::done(retval);
303 }
304 
306  // CSV is currently the only supported format
307  QString filename =
308  GUIUtil::getSaveFileName(this, tr("Export Address List"), QString(),
309  tr("Comma separated file (*.csv)"), nullptr);
310 
311  if (filename.isNull()) {
312  return;
313  }
314 
315  CSVModelWriter writer(filename);
316 
317  // name, column, role
318  writer.setModel(proxyModel);
319  writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
320  writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
321 
322  if (!writer.write()) {
323  QMessageBox::critical(this, tr("Exporting Failed"),
324  tr("There was an error trying to save the "
325  "address list to %1. Please try again.")
326  .arg(filename));
327  }
328 }
329 
330 void AddressBookPage::contextualMenu(const QPoint &point) {
331  QModelIndex index = ui->tableView->indexAt(point);
332  if (index.isValid()) {
333  contextMenu->exec(QCursor::pos());
334  }
335 }
336 
337 void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin,
338  int /*end*/) {
339  QModelIndex idx = proxyModel->mapFromSource(
340  model->index(begin, AddressTableModel::Address, parent));
341  if (idx.isValid() &&
342  (idx.data(Qt::EditRole).toString() == newAddressToSelect)) {
343  // Select row of newly created address, once
344  ui->tableView->setFocus();
345  ui->tableView->selectRow(idx.row());
346  newAddressToSelect.clear();
347  }
348 }
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)
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 on_exportButton_clicked()
Export button clicked.
Open address book for editing.
Open address book to pick address.
Export a Qt table model to a CSV file.
QString newAddressToSelect
Ui::AddressBookPage * ui
static const QString Send
Specifies send address.
void selectNewAddress(const QModelIndex &parent, int begin, int)
New entry/entries were added to address table.
QAction * deleteAction
bool done
void setModel(AddressTableModel *model)
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.
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 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:273
static const QString Receive
Specifies receive address.
Dialog for editing an address and associated information.
bool filterAcceptsRow(int row, const QModelIndex &parent) const
QString getAddress() const
User specified label.
void copyEntryData(QAbstractItemView *view, int column, int role)
Copy a field of the currently selected entry of a view to the clipboard.
Definition: guiutil.cpp:250
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:20
Type of address (Send or Receive)