Bitcoin Core  0.20.99
P2P Digital Currency
addressbookpage.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2019 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),
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  // Context menu actions
116  QAction *copyAddressAction = new QAction(tr("&Copy Address"), this);
117  QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
118  QAction *editAction = new QAction(tr("&Edit"), this);
119  deleteAction = new QAction(ui->deleteAddress->text(), this);
120 
121  // Build context menu
122  contextMenu = new QMenu(this);
123  contextMenu->addAction(copyAddressAction);
124  contextMenu->addAction(copyLabelAction);
125  contextMenu->addAction(editAction);
126  if(tab == SendingTab)
127  contextMenu->addAction(deleteAction);
128  contextMenu->addSeparator();
129 
130  // Connect signals for context menu actions
131  connect(copyAddressAction, &QAction::triggered, this, &AddressBookPage::on_copyAddress_clicked);
132  connect(copyLabelAction, &QAction::triggered, this, &AddressBookPage::onCopyLabelAction);
133  connect(editAction, &QAction::triggered, this, &AddressBookPage::onEditAction);
134  connect(deleteAction, &QAction::triggered, this, &AddressBookPage::on_deleteAddress_clicked);
135 
136  connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
137 
138  connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::accept);
139 
141 }
142 
144 {
145  delete ui;
146 }
147 
149 {
150  this->model = _model;
151  if(!_model)
152  return;
153 
156  proxyModel->setSourceModel(_model);
157 
158  connect(ui->searchLineEdit, &QLineEdit::textChanged, proxyModel, &QSortFilterProxyModel::setFilterWildcard);
159 
160  ui->tableView->setModel(proxyModel);
161  ui->tableView->sortByColumn(0, Qt::AscendingOrder);
162 
163  // Set column widths
164  ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
165  ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
166 
167  connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
169 
170  // Select row for newly created address
171  connect(_model, &AddressTableModel::rowsInserted, this, &AddressBookPage::selectNewAddress);
172 
174 }
175 
177 {
179 }
180 
182 {
184 }
185 
187 {
188  if(!model)
189  return;
190 
191  if(!ui->tableView->selectionModel())
192  return;
193  QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
194  if(indexes.isEmpty())
195  return;
196 
197  EditAddressDialog dlg(
198  tab == SendingTab ?
201  dlg.setModel(model);
202  QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
203  dlg.loadRow(origIndex.row());
204  dlg.exec();
205 }
206 
208 {
209  if(!model)
210  return;
211 
212  if (tab == ReceivingTab) {
213  return;
214  }
215 
217  dlg.setModel(model);
218  if(dlg.exec())
219  {
221  }
222 }
223 
225 {
226  QTableView *table = ui->tableView;
227  if(!table->selectionModel())
228  return;
229 
230  QModelIndexList indexes = table->selectionModel()->selectedRows();
231  if(!indexes.isEmpty())
232  {
233  table->model()->removeRow(indexes.at(0).row());
234  }
235 }
236 
238 {
239  // Set button states based on selected tab and selection
240  QTableView *table = ui->tableView;
241  if(!table->selectionModel())
242  return;
243 
244  if(table->selectionModel()->hasSelection())
245  {
246  switch(tab)
247  {
248  case SendingTab:
249  // In sending tab, allow deletion of selection
250  ui->deleteAddress->setEnabled(true);
251  ui->deleteAddress->setVisible(true);
252  deleteAction->setEnabled(true);
253  break;
254  case ReceivingTab:
255  // Deleting receiving addresses, however, is not allowed
256  ui->deleteAddress->setEnabled(false);
257  ui->deleteAddress->setVisible(false);
258  deleteAction->setEnabled(false);
259  break;
260  }
261  ui->copyAddress->setEnabled(true);
262  }
263  else
264  {
265  ui->deleteAddress->setEnabled(false);
266  ui->copyAddress->setEnabled(false);
267  }
268 }
269 
270 void AddressBookPage::done(int retval)
271 {
272  QTableView *table = ui->tableView;
273  if(!table->selectionModel() || !table->model())
274  return;
275 
276  // Figure out which address was selected, and return it
277  QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
278 
279  for (const QModelIndex& index : indexes) {
280  QVariant address = table->model()->data(index);
281  returnValue = address.toString();
282  }
283 
284  if(returnValue.isEmpty())
285  {
286  // If no address entry selected, return rejected
287  retval = Rejected;
288  }
289 
290  QDialog::done(retval);
291 }
292 
294 {
295  // CSV is currently the only supported format
296  QString filename = GUIUtil::getSaveFileName(this,
297  tr("Export Address List"), QString(),
298  tr("Comma separated file (*.csv)"), nullptr);
299 
300  if (filename.isNull())
301  return;
302 
303  CSVModelWriter writer(filename);
304 
305  // name, column, role
306  writer.setModel(proxyModel);
307  writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
308  writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
309 
310  if(!writer.write()) {
311  QMessageBox::critical(this, tr("Exporting Failed"),
312  tr("There was an error trying to save the address list to %1. Please try again.").arg(filename));
313  }
314 }
315 
316 void AddressBookPage::contextualMenu(const QPoint &point)
317 {
318  QModelIndex index = ui->tableView->indexAt(point);
319  if(index.isValid())
320  {
321  contextMenu->exec(QCursor::pos());
322  }
323 }
324 
325 void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int /*end*/)
326 {
327  QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
328  if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
329  {
330  // Select row of newly created address, once
331  ui->tableView->setFocus();
332  ui->tableView->selectRow(idx.row());
333  newAddressToSelect.clear();
334  }
335 }
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)
void handleCloseWindowShortcut(QWidget *w)
Definition: guiutil.cpp:390
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:235
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:267
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)