Bitcoin Core  0.18.99
P2P Digital Currency
addressbookpage.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2018 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
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."));
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 }
140 
142 {
143  delete ui;
144 }
145 
147 {
148  this->model = _model;
149  if(!_model)
150  return;
151 
154  proxyModel->setSourceModel(_model);
155 
156  connect(ui->searchLineEdit, &QLineEdit::textChanged, proxyModel, &QSortFilterProxyModel::setFilterWildcard);
157 
158  ui->tableView->setModel(proxyModel);
159  ui->tableView->sortByColumn(0, Qt::AscendingOrder);
160 
161  // Set column widths
162  ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
163  ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
164 
165  connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
167 
168  // Select row for newly created address
169  connect(_model, &AddressTableModel::rowsInserted, this, &AddressBookPage::selectNewAddress);
170 
172 }
173 
175 {
177 }
178 
180 {
182 }
183 
185 {
186  if(!model)
187  return;
188 
189  if(!ui->tableView->selectionModel())
190  return;
191  QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
192  if(indexes.isEmpty())
193  return;
194 
195  EditAddressDialog dlg(
196  tab == SendingTab ?
199  dlg.setModel(model);
200  QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
201  dlg.loadRow(origIndex.row());
202  dlg.exec();
203 }
204 
206 {
207  if(!model)
208  return;
209 
210  if (tab == ReceivingTab) {
211  return;
212  }
213 
215  dlg.setModel(model);
216  if(dlg.exec())
217  {
219  }
220 }
221 
223 {
224  QTableView *table = ui->tableView;
225  if(!table->selectionModel())
226  return;
227 
228  QModelIndexList indexes = table->selectionModel()->selectedRows();
229  if(!indexes.isEmpty())
230  {
231  table->model()->removeRow(indexes.at(0).row());
232  }
233 }
234 
236 {
237  // Set button states based on selected tab and selection
238  QTableView *table = ui->tableView;
239  if(!table->selectionModel())
240  return;
241 
242  if(table->selectionModel()->hasSelection())
243  {
244  switch(tab)
245  {
246  case SendingTab:
247  // In sending tab, allow deletion of selection
248  ui->deleteAddress->setEnabled(true);
249  ui->deleteAddress->setVisible(true);
250  deleteAction->setEnabled(true);
251  break;
252  case ReceivingTab:
253  // Deleting receiving addresses, however, is not allowed
254  ui->deleteAddress->setEnabled(false);
255  ui->deleteAddress->setVisible(false);
256  deleteAction->setEnabled(false);
257  break;
258  }
259  ui->copyAddress->setEnabled(true);
260  }
261  else
262  {
263  ui->deleteAddress->setEnabled(false);
264  ui->copyAddress->setEnabled(false);
265  }
266 }
267 
268 void AddressBookPage::done(int retval)
269 {
270  QTableView *table = ui->tableView;
271  if(!table->selectionModel() || !table->model())
272  return;
273 
274  // Figure out which address was selected, and return it
275  QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
276 
277  for (const QModelIndex& index : indexes) {
278  QVariant address = table->model()->data(index);
279  returnValue = address.toString();
280  }
281 
282  if(returnValue.isEmpty())
283  {
284  // If no address entry selected, return rejected
285  retval = Rejected;
286  }
287 
288  QDialog::done(retval);
289 }
290 
292 {
293  // CSV is currently the only supported format
294  QString filename = GUIUtil::getSaveFileName(this,
295  tr("Export Address List"), QString(),
296  tr("Comma separated file (*.csv)"), nullptr);
297 
298  if (filename.isNull())
299  return;
300 
301  CSVModelWriter writer(filename);
302 
303  // name, column, role
304  writer.setModel(proxyModel);
305  writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
306  writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
307 
308  if(!writer.write()) {
309  QMessageBox::critical(this, tr("Exporting Failed"),
310  tr("There was an error trying to save the address list to %1. Please try again.").arg(filename));
311  }
312 }
313 
314 void AddressBookPage::contextualMenu(const QPoint &point)
315 {
316  QModelIndex index = ui->tableView->indexAt(point);
317  if(index.isValid())
318  {
319  contextMenu->exec(QCursor::pos());
320  }
321 }
322 
323 void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int /*end*/)
324 {
325  QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
326  if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
327  {
328  // Select row of newly created address, once
329  ui->tableView->setFocus();
330  ui->tableView->selectRow(idx.row());
331  newAddressToSelect.clear();
332  }
333 }
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)
QModelIndex index(int row, int column, const QModelIndex &parent) const
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 done(int retval)
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 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:255
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:230
void contextualMenu(const QPoint &point)
Spawn contextual menu (right mouse menu) for address book entry.
void on_deleteAddress_clicked()
Delete currently selected address entry.
bool write()
Perform export of the model to CSV.
bool getImagesOnButtons() const
Definition: platformstyle.h:21
Type of address (Send or Receive)