Feathercoin  0.5.0
P2P Digital Currency
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
addressbookpage.cpp
Go to the documentation of this file.
1 #include "addressbookpage.h"
2 #include "ui_addressbookpage.h"
3 
4 #include "addresstablemodel.h"
5 #include "optionsmodel.h"
6 #include "bitcoingui.h"
7 #include "editaddressdialog.h"
8 #include "csvmodelwriter.h"
9 #include "guiutil.h"
10 
11 #ifdef USE_QRCODE
12 #include "qrcodedialog.h"
13 #endif
14 #ifdef USE_ZXING
15 #include "snapwidget.h"
16 #endif
17 
18 #include <QSortFilterProxyModel>
19 #include <QClipboard>
20 #include <QMessageBox>
21 #include <QMenu>
22 
23 AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) :
24  QDialog(parent),
25  ui(new Ui::AddressBookPage),
26  model(0),
27  optionsModel(0),
28  mode(mode),
29  tab(tab)
30 {
31  ui->setupUi(this);
32 
33 #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
34  ui->newAddress->setIcon(QIcon());
35  ui->copyAddress->setIcon(QIcon());
36  ui->deleteAddress->setIcon(QIcon());
37  ui->verifyMessage->setIcon(QIcon());
38  ui->signMessage->setIcon(QIcon());
39  ui->exportButton->setIcon(QIcon());
40  ui->importQRCodeButton->setIcon(QIcon());
41 #endif
42 
43 #ifndef USE_QRCODE
44  ui->showQRCode->setVisible(false);
45 #else
46  ui->showQRCode->setVisible(true);
47 #endif
48 #ifndef USE_ZXING
49  ui->importQRCodeButton->setVisible(false);
50 #endif
51 
52  switch(mode)
53  {
54  case ForSending:
55  connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept()));
56  ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
57  ui->tableView->setFocus();
58  ui->exportButton->hide();
59  break;
60  case ForEditing:
61  ui->buttonBox->setVisible(false);
62  break;
63  }
64  switch(tab)
65  {
66  case SendingTab:
67  ui->labelExplanation->setText(tr("These are your Feathercoin addresses for sending payments. Always check the amount and the receiving address before sending coins."));
68  ui->deleteAddress->setVisible(true);
69  ui->signMessage->setVisible(false);
70 #ifdef USE_ZXING
71  ui->importQRCodeButton->setVisible(false);
72 #endif
73  break;
74  case ReceivingTab:
75  ui->labelExplanation->setText(tr("These are your Feathercoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you."));
76  ui->deleteAddress->setVisible(false);
77  ui->signMessage->setVisible(true);
78 #ifdef USE_ZXING
79  ui->importQRCodeButton->setVisible(true);
80 #endif
81  break;
82  }
83 
84  // Context menu actions
85  QAction *copyAddressAction = new QAction(ui->copyAddress->text(), this);
86  QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
87  QAction *editAction = new QAction(tr("&Edit"), this);
88  QAction *sendCoinsAction = new QAction(tr("Send &Coins"), this);
89  QAction *showQRCodeAction = new QAction(ui->showQRCode->text(), this);
90  QAction *signMessageAction = new QAction(ui->signMessage->text(), this);
91  QAction *verifyMessageAction = new QAction(ui->verifyMessage->text(), this);
92  deleteAction = new QAction(ui->deleteAddress->text(), this);
93 
94  // Build context menu
95  contextMenu = new QMenu();
96  contextMenu->addAction(copyAddressAction);
97  contextMenu->addAction(copyLabelAction);
98  contextMenu->addAction(editAction);
99  if(tab == SendingTab)
100  contextMenu->addAction(deleteAction);
101  contextMenu->addSeparator();
102  if(tab == SendingTab)
103  contextMenu->addAction(sendCoinsAction);
104 #ifdef USE_QRCODE
105  contextMenu->addAction(showQRCodeAction);
106 #endif
107  if(tab == ReceivingTab)
108  contextMenu->addAction(signMessageAction);
109  else if(tab == SendingTab)
110  contextMenu->addAction(verifyMessageAction);
111 
112  // Connect signals for context menu actions
113  connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyAddress_clicked()));
114  connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction()));
115  connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction()));
116  connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteAddress_clicked()));
117  connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(onSendCoinsAction()));
118  connect(showQRCodeAction, SIGNAL(triggered()), this, SLOT(on_showQRCode_clicked()));
119  connect(signMessageAction, SIGNAL(triggered()), this, SLOT(on_signMessage_clicked()));
120  connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(on_verifyMessage_clicked()));
121 
122  connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
123 
124  // Pass through accept action from button box
125  connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
126 }
127 
129 {
130  delete ui;
131 }
132 
134 {
135  this->model = model;
136  if(!model)
137  return;
138 
139  proxyModel = new QSortFilterProxyModel(this);
140  proxyModel->setSourceModel(model);
141  proxyModel->setDynamicSortFilter(true);
142  proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
143  proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
144  switch(tab)
145  {
146  case ReceivingTab:
147  // Receive filter
148  proxyModel->setFilterRole(AddressTableModel::TypeRole);
149  proxyModel->setFilterFixedString(AddressTableModel::Receive);
150  break;
151  case SendingTab:
152  // Send filter
153  proxyModel->setFilterRole(AddressTableModel::TypeRole);
154  proxyModel->setFilterFixedString(AddressTableModel::Send);
155  break;
156  }
157  ui->tableView->setModel(proxyModel);
158  ui->tableView->sortByColumn(0, Qt::AscendingOrder);
159 
160  // Set column widths
161 #if QT_VERSION < 0x050000
162  ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
163  ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
164 #else
165  ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
166  ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
167 #endif
168 
169  connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
170  this, SLOT(selectionChanged()));
171 
172  // Select row for newly created address
173  connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(selectNewAddress(QModelIndex,int,int)));
174 
176 }
177 
179 {
180  this->optionsModel = optionsModel;
181 }
182 
184 {
186 }
187 
189 {
191 }
192 
194 {
195  if(!ui->tableView->selectionModel())
196  return;
197  QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
198  if(indexes.isEmpty())
199  return;
200 
201  EditAddressDialog dlg(
202  tab == SendingTab ?
205  dlg.setModel(model);
206  QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
207  dlg.loadRow(origIndex.row());
208  dlg.exec();
209 }
210 
212 {
213  QTableView *table = ui->tableView;
214  QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
215 
216  foreach (QModelIndex index, indexes)
217  {
218  QString address = index.data().toString();
219  emit signMessage(address);
220  }
221 }
222 
224 {
225  QTableView *table = ui->tableView;
226  QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
227 
228  foreach (QModelIndex index, indexes)
229  {
230  QString address = index.data().toString();
231  emit verifyMessage(address);
232  }
233 }
234 
236 {
237  QTableView *table = ui->tableView;
238  QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
239 
240  foreach (QModelIndex index, indexes)
241  {
242  QString address = index.data().toString();
243  emit sendCoins(address);
244  }
245 }
246 
248 {
249  if(!model)
250  return;
251 
252  EditAddressDialog dlg(
253  tab == SendingTab ?
256  dlg.setModel(model);
257  if(dlg.exec())
258  {
260  }
261 }
262 
264 {
265  QTableView *table = ui->tableView;
266  if(!table->selectionModel())
267  return;
268 
269  QModelIndexList indexes = table->selectionModel()->selectedRows();
270  if(!indexes.isEmpty())
271  {
272  table->model()->removeRow(indexes.at(0).row());
273  }
274 }
275 
277 {
278  // Set button states based on selected tab and selection
279  QTableView *table = ui->tableView;
280  if(!table->selectionModel())
281  return;
282 
283  if(table->selectionModel()->hasSelection())
284  {
285  switch(tab)
286  {
287  case SendingTab:
288  // In sending tab, allow deletion of selection
289  ui->deleteAddress->setEnabled(true);
290  ui->deleteAddress->setVisible(true);
291  deleteAction->setEnabled(true);
292  ui->signMessage->setEnabled(false);
293  ui->signMessage->setVisible(false);
294  ui->verifyMessage->setEnabled(true);
295  ui->verifyMessage->setVisible(true);
296  break;
297  case ReceivingTab:
298  // Deleting receiving addresses, however, is not allowed
299  ui->deleteAddress->setEnabled(false);
300  ui->deleteAddress->setVisible(false);
301  deleteAction->setEnabled(false);
302  ui->signMessage->setEnabled(true);
303  ui->signMessage->setVisible(true);
304  ui->verifyMessage->setEnabled(false);
305  ui->verifyMessage->setVisible(false);
306  break;
307  }
308  ui->copyAddress->setEnabled(true);
309  ui->showQRCode->setEnabled(true);
310  ui->importQRCodeButton->setEnabled(true);
311  }
312  else
313  {
314  ui->deleteAddress->setEnabled(false);
315  ui->showQRCode->setEnabled(false);
316  ui->copyAddress->setEnabled(false);
317  ui->signMessage->setEnabled(false);
318  ui->verifyMessage->setEnabled(false);
319  }
320 }
321 
322 void AddressBookPage::done(int retval)
323 {
324  QTableView *table = ui->tableView;
325  if(!table->selectionModel() || !table->model())
326  return;
327  // When this is a tab/widget and not a model dialog, ignore "done"
328  if(mode == ForEditing)
329  return;
330 
331  // Figure out which address was selected, and return it
332  QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
333 
334  foreach (QModelIndex index, indexes)
335  {
336  QVariant address = table->model()->data(index);
337  returnValue = address.toString();
338  }
339 
340  if(returnValue.isEmpty())
341  {
342  // If no address entry selected, return rejected
343  retval = Rejected;
344  }
345 
346  QDialog::done(retval);
347 }
348 
350 {
351  // CSV is currently the only supported format
352  QString filename = GUIUtil::getSaveFileName(
353  this,
354  tr("Export Address Book Data"), QString(),
355  tr("Comma separated file (*.csv)"));
356 
357  if (filename.isNull()) return;
358 
359  CSVModelWriter writer(filename);
360 
361  // name, column, role
362  writer.setModel(proxyModel);
363  writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
364  writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
365 
366  if(!writer.write())
367  {
368  QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename),
369  QMessageBox::Abort, QMessageBox::Abort);
370  }
371 }
372 
374 {
375 #ifdef USE_ZXING
376  SnapWidget* snap = new SnapWidget(this);
377  connect(snap, SIGNAL(finished(QString)), this, SLOT(onSnapClosed(QString)));
378 #endif
379 }
380 
381 void AddressBookPage::onSnapClosed(QString privKey)
382 {
383  if (privKey.size() > 0)
384  //to do : some more parsing and validation is needed here
385  //todo: prompt for a label
386  //todo: display a dialog if it doesn't work
387  emit importWallet(privKey);
388 }
389 
391 {
392 #ifdef USE_QRCODE
393  QTableView *table = ui->tableView;
394  QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
395 
396  foreach (QModelIndex index, indexes)
397  {
398  QString address = index.data().toString();
399  QString label = index.sibling(index.row(), 0).data(Qt::EditRole).toString();
400 
401  QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this);
402  dialog->setModel(optionsModel);
403  dialog->setAttribute(Qt::WA_DeleteOnClose);
404  dialog->show();
405  }
406 #endif
407 }
408 
409 void AddressBookPage::contextualMenu(const QPoint &point)
410 {
411  QModelIndex index = ui->tableView->indexAt(point);
412  if(index.isValid())
413  {
414  contextMenu->exec(QCursor::pos());
415  }
416 }
417 
418 void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int /*end*/)
419 {
420  QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
421  if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
422  {
423  // Select row of newly created address, once
424  ui->tableView->setFocus();
425  ui->tableView->selectRow(idx.row());
426  newAddressToSelect.clear();
427  }
428 }
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
QString getAddress() const
void importWallet(QString addr)
void setModel(AddressTableModel *model)
Definition: aboutdialog.h:6
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:190
void onEditAction()
Edit currently selected address entry (no button)
AddressTableModel * model
void on_showQRCode_clicked()
Generate a QR Code from the currently selected address.
QSortFilterProxyModel * proxyModel
void on_exportButton_clicked()
Export button clicked.
Open address book for editing.
void onSnapClosed(QString s)
Scan of QR code finished.
Export a Qt table model to a CSV file.
QString newAddressToSelect
void verifyMessage(QString addr)
void on_importQRCodeButton_clicked()
Import from a QR Code into your wallet.
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.
Open address book to pick address for sending.
QAction * deleteAction
void onSendCoinsAction()
Open send coins dialog for currently selected address (no button)
void setModel(AddressTableModel *model)
void done(int retval)
void on_copyAddress_clicked()
Copy address of currently selected address entry to clipboard.
void setOptionsModel(OptionsModel *optionsModel)
Widget that shows a list of sending or receiving addresses.
Qt model of the address book in the core.
OptionsModel * optionsModel
void setModel(OptionsModel *model)
void selectionChanged()
Set button states based on selected tab and selection.
void setModel(const QAbstractItemModel *model)
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:12
static const QString Receive
Specifies receive address.
Dialog for editing an address and associated information.
void sendCoins(QString addr)
void signMessage(QString addr)
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:169
void on_signMessage_clicked()
Open the sign message tab in the Sign/Verify Message dialog with currently selected address...
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.
AddressBookPage(Mode mode, Tabs tab, QWidget *parent=0)
void * arg
Definition: env_posix.cc:716
Type of address (Send or Receive)
void on_verifyMessage_clicked()
Open the verify message tab in the Sign/Verify Message dialog with currently selected address...