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/bitcoingui.h>
14 #include <qt/csvmodelwriter.h>
15 #include <qt/editaddressdialog.h>
16 #include <qt/guiutil.h>
17 #include <qt/platformstyle.h>
18 
19 #include <QIcon>
20 #include <QMenu>
21 #include <QMessageBox>
22 #include <QSortFilterProxyModel>
23 
24 class AddressBookSortFilterProxyModel final : public QSortFilterProxyModel
25 {
26  const QString m_type;
27 
28 public:
29  AddressBookSortFilterProxyModel(const QString& type, QObject* parent)
30  : QSortFilterProxyModel(parent)
31  , m_type(type)
32  {
33  setDynamicSortFilter(true);
34  setFilterCaseSensitivity(Qt::CaseInsensitive);
35  setSortCaseSensitivity(Qt::CaseInsensitive);
36  }
37 
38 protected:
39  bool filterAcceptsRow(int row, const QModelIndex& parent) const
40  {
41  auto model = sourceModel();
42  auto label = model->index(row, AddressTableModel::Label, parent);
43 
44  if (model->data(label, AddressTableModel::TypeRole).toString() != m_type) {
45  return false;
46  }
47 
48  auto address = model->index(row, AddressTableModel::Address, parent);
49 
50  if (filterRegExp().indexIn(model->data(address).toString()) < 0 &&
51  filterRegExp().indexIn(model->data(label).toString()) < 0) {
52  return false;
53  }
54 
55  return true;
56  }
57 };
58 
59 AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode, Tabs _tab, QWidget *parent) :
60  QDialog(parent),
61  ui(new Ui::AddressBookPage),
62  model(nullptr),
63  mode(_mode),
64  tab(_tab)
65 {
66  ui->setupUi(this);
67 
68  if (!platformStyle->getImagesOnButtons()) {
69  ui->newAddress->setIcon(QIcon());
70  ui->copyAddress->setIcon(QIcon());
71  ui->deleteAddress->setIcon(QIcon());
72  ui->exportButton->setIcon(QIcon());
73  } else {
74  ui->newAddress->setIcon(platformStyle->SingleColorIcon(":/icons/add"));
75  ui->copyAddress->setIcon(platformStyle->SingleColorIcon(":/icons/editcopy"));
76  ui->deleteAddress->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
77  ui->exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
78  }
79 
80  switch(mode)
81  {
82  case ForSelection:
83  switch(tab)
84  {
85  case SendingTab: setWindowTitle(tr("Choose the address to send coins to")); break;
86  case ReceivingTab: setWindowTitle(tr("Choose the address to receive coins with")); break;
87  }
88  connect(ui->tableView, &QTableView::doubleClicked, this, &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  {
97  case SendingTab: setWindowTitle(tr("Sending addresses")); break;
98  case ReceivingTab: setWindowTitle(tr("Receiving addresses")); break;
99  }
100  break;
101  }
102  switch(tab)
103  {
104  case SendingTab:
105  ui->labelExplanation->setText(tr("These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins."));
106  ui->deleteAddress->setVisible(true);
107  ui->newAddress->setVisible(true);
108  break;
109  case ReceivingTab:
110  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."));
111  ui->deleteAddress->setVisible(false);
112  ui->newAddress->setVisible(false);
113  break;
114  }
115 
116  // Context menu actions
117  QAction *copyAddressAction = new QAction(tr("&Copy Address"), this);
118  QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
119  QAction *editAction = new QAction(tr("&Edit"), this);
120  deleteAction = new QAction(ui->deleteAddress->text(), this);
121 
122  // Build context menu
123  contextMenu = new QMenu(this);
124  contextMenu->addAction(copyAddressAction);
125  contextMenu->addAction(copyLabelAction);
126  contextMenu->addAction(editAction);
127  if(tab == SendingTab)
128  contextMenu->addAction(deleteAction);
129  contextMenu->addSeparator();
130 
131  // Connect signals for context menu actions
132  connect(copyAddressAction, &QAction::triggered, this, &AddressBookPage::on_copyAddress_clicked);
133  connect(copyLabelAction, &QAction::triggered, this, &AddressBookPage::onCopyLabelAction);
134  connect(editAction, &QAction::triggered, this, &AddressBookPage::onEditAction);
135  connect(deleteAction, &QAction::triggered, this, &AddressBookPage::on_deleteAddress_clicked);
136 
137  connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
138 
139  connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::accept);
140 }
141 
143 {
144  delete ui;
145 }
146 
148 {
149  this->model = _model;
150  if(!_model)
151  return;
152 
155  proxyModel->setSourceModel(_model);
156 
157  connect(ui->searchLineEdit, &QLineEdit::textChanged, proxyModel, &QSortFilterProxyModel::setFilterWildcard);
158 
159  ui->tableView->setModel(proxyModel);
160  ui->tableView->sortByColumn(0, Qt::AscendingOrder);
161 
162  // Set column widths
163  ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
164  ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
165 
166  connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
168 
169  // Select row for newly created address
170  connect(_model, &AddressTableModel::rowsInserted, this, &AddressBookPage::selectNewAddress);
171 
173 }
174 
176 {
178 }
179 
181 {
183 }
184 
186 {
187  if(!model)
188  return;
189 
190  if(!ui->tableView->selectionModel())
191  return;
192  QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
193  if(indexes.isEmpty())
194  return;
195 
196  EditAddressDialog dlg(
197  tab == SendingTab ?
200  dlg.setModel(model);
201  QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
202  dlg.loadRow(origIndex.row());
203  dlg.exec();
204 }
205 
207 {
208  if(!model)
209  return;
210 
211  if (tab == ReceivingTab) {
212  return;
213  }
214 
216  dlg.setModel(model);
217  if(dlg.exec())
218  {
220  }
221 }
222 
224 {
225  QTableView *table = ui->tableView;
226  if(!table->selectionModel())
227  return;
228 
229  QModelIndexList indexes = table->selectionModel()->selectedRows();
230  if(!indexes.isEmpty())
231  {
232  table->model()->removeRow(indexes.at(0).row());
233  }
234 }
235 
237 {
238  // Set button states based on selected tab and selection
239  QTableView *table = ui->tableView;
240  if(!table->selectionModel())
241  return;
242 
243  if(table->selectionModel()->hasSelection())
244  {
245  switch(tab)
246  {
247  case SendingTab:
248  // In sending tab, allow deletion of selection
249  ui->deleteAddress->setEnabled(true);
250  ui->deleteAddress->setVisible(true);
251  deleteAction->setEnabled(true);
252  break;
253  case ReceivingTab:
254  // Deleting receiving addresses, however, is not allowed
255  ui->deleteAddress->setEnabled(false);
256  ui->deleteAddress->setVisible(false);
257  deleteAction->setEnabled(false);
258  break;
259  }
260  ui->copyAddress->setEnabled(true);
261  }
262  else
263  {
264  ui->deleteAddress->setEnabled(false);
265  ui->copyAddress->setEnabled(false);
266  }
267 }
268 
269 void AddressBookPage::done(int retval)
270 {
271  QTableView *table = ui->tableView;
272  if(!table->selectionModel() || !table->model())
273  return;
274 
275  // Figure out which address was selected, and return it
276  QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
277 
278  for (const QModelIndex& index : indexes) {
279  QVariant address = table->model()->data(index);
280  returnValue = address.toString();
281  }
282 
283  if(returnValue.isEmpty())
284  {
285  // If no address entry selected, return rejected
286  retval = Rejected;
287  }
288 
289  QDialog::done(retval);
290 }
291 
293 {
294  // CSV is currently the only supported format
295  QString filename = GUIUtil::getSaveFileName(this,
296  tr("Export Address List"), QString(),
297  tr("Comma separated file (*.csv)"), nullptr);
298 
299  if (filename.isNull())
300  return;
301 
302  CSVModelWriter writer(filename);
303 
304  // name, column, role
305  writer.setModel(proxyModel);
306  writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
307  writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
308 
309  if(!writer.write()) {
310  QMessageBox::critical(this, tr("Exporting Failed"),
311  tr("There was an error trying to save the address list to %1. Please try again.").arg(filename));
312  }
313 }
314 
315 void AddressBookPage::contextualMenu(const QPoint &point)
316 {
317  QModelIndex index = ui->tableView->indexAt(point);
318  if(index.isValid())
319  {
320  contextMenu->exec(QCursor::pos());
321  }
322 }
323 
324 void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int /*end*/)
325 {
326  QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
327  if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
328  {
329  // Select row of newly created address, once
330  ui->tableView->setFocus();
331  ui->tableView->selectRow(idx.row());
332  newAddressToSelect.clear();
333  }
334 }
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:254
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:229
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)