45155d3010
Having the export button at the top was confusing people into thinking the entire wallet was exported. This commit moves the export button to the address book, receiving addresses and transaction tabs separately.
390 lines
12 KiB
C++
390 lines
12 KiB
C++
#include "addressbookpage.h"
|
|
#include "ui_addressbookpage.h"
|
|
|
|
#include "addresstablemodel.h"
|
|
#include "optionsmodel.h"
|
|
#include "bitcoingui.h"
|
|
#include "editaddressdialog.h"
|
|
#include "csvmodelwriter.h"
|
|
#include "guiutil.h"
|
|
|
|
#ifdef USE_QRCODE
|
|
#include "qrcodedialog.h"
|
|
#endif
|
|
|
|
#include <QSortFilterProxyModel>
|
|
#include <QClipboard>
|
|
#include <QMessageBox>
|
|
#include <QMenu>
|
|
|
|
AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) :
|
|
QDialog(parent),
|
|
ui(new Ui::AddressBookPage),
|
|
model(0),
|
|
optionsModel(0),
|
|
mode(mode),
|
|
tab(tab)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
|
|
ui->newAddress->setIcon(QIcon());
|
|
ui->copyAddress->setIcon(QIcon());
|
|
ui->deleteAddress->setIcon(QIcon());
|
|
ui->verifyMessage->setIcon(QIcon());
|
|
ui->signMessage->setIcon(QIcon());
|
|
ui->exportButton->setIcon(QIcon());
|
|
#endif
|
|
|
|
#ifndef USE_QRCODE
|
|
ui->showQRCode->setVisible(false);
|
|
#endif
|
|
|
|
switch(mode)
|
|
{
|
|
case ForSending:
|
|
connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept()));
|
|
ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
ui->tableView->setFocus();
|
|
ui->exportButton->hide();
|
|
break;
|
|
case ForEditing:
|
|
ui->buttonBox->setVisible(false);
|
|
break;
|
|
}
|
|
switch(tab)
|
|
{
|
|
case SendingTab:
|
|
ui->labelExplanation->setText(tr("These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins."));
|
|
ui->deleteAddress->setVisible(true);
|
|
ui->signMessage->setVisible(false);
|
|
break;
|
|
case ReceivingTab:
|
|
ui->labelExplanation->setText(tr("These are your Bitcoin 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."));
|
|
ui->deleteAddress->setVisible(false);
|
|
ui->signMessage->setVisible(true);
|
|
break;
|
|
}
|
|
|
|
// Context menu actions
|
|
QAction *copyAddressAction = new QAction(ui->copyAddress->text(), this);
|
|
QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
|
|
QAction *editAction = new QAction(tr("&Edit"), this);
|
|
QAction *sendCoinsAction = new QAction(tr("Send &Coins"), this);
|
|
QAction *showQRCodeAction = new QAction(ui->showQRCode->text(), this);
|
|
QAction *signMessageAction = new QAction(ui->signMessage->text(), this);
|
|
QAction *verifyMessageAction = new QAction(ui->verifyMessage->text(), this);
|
|
deleteAction = new QAction(ui->deleteAddress->text(), this);
|
|
|
|
// Build context menu
|
|
contextMenu = new QMenu();
|
|
contextMenu->addAction(copyAddressAction);
|
|
contextMenu->addAction(copyLabelAction);
|
|
contextMenu->addAction(editAction);
|
|
if(tab == SendingTab)
|
|
contextMenu->addAction(deleteAction);
|
|
contextMenu->addSeparator();
|
|
if(tab == SendingTab)
|
|
contextMenu->addAction(sendCoinsAction);
|
|
#ifdef USE_QRCODE
|
|
contextMenu->addAction(showQRCodeAction);
|
|
#endif
|
|
if(tab == ReceivingTab)
|
|
contextMenu->addAction(signMessageAction);
|
|
else if(tab == SendingTab)
|
|
contextMenu->addAction(verifyMessageAction);
|
|
|
|
// Connect signals for context menu actions
|
|
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyAddress_clicked()));
|
|
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction()));
|
|
connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction()));
|
|
connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteAddress_clicked()));
|
|
connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(onSendCoinsAction()));
|
|
connect(showQRCodeAction, SIGNAL(triggered()), this, SLOT(on_showQRCode_clicked()));
|
|
connect(signMessageAction, SIGNAL(triggered()), this, SLOT(on_signMessage_clicked()));
|
|
connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(on_verifyMessage_clicked()));
|
|
|
|
connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
|
|
|
|
// Pass through accept action from button box
|
|
connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
|
}
|
|
|
|
AddressBookPage::~AddressBookPage()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void AddressBookPage::setModel(AddressTableModel *model)
|
|
{
|
|
this->model = model;
|
|
if(!model)
|
|
return;
|
|
|
|
proxyModel = new QSortFilterProxyModel(this);
|
|
proxyModel->setSourceModel(model);
|
|
proxyModel->setDynamicSortFilter(true);
|
|
proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
|
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
|
switch(tab)
|
|
{
|
|
case ReceivingTab:
|
|
// Receive filter
|
|
proxyModel->setFilterRole(AddressTableModel::TypeRole);
|
|
proxyModel->setFilterFixedString(AddressTableModel::Receive);
|
|
break;
|
|
case SendingTab:
|
|
// Send filter
|
|
proxyModel->setFilterRole(AddressTableModel::TypeRole);
|
|
proxyModel->setFilterFixedString(AddressTableModel::Send);
|
|
break;
|
|
}
|
|
ui->tableView->setModel(proxyModel);
|
|
ui->tableView->sortByColumn(0, Qt::AscendingOrder);
|
|
|
|
// Set column widths
|
|
ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
|
|
ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
|
|
|
|
connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
|
this, SLOT(selectionChanged()));
|
|
|
|
// Select row for newly created address
|
|
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(selectNewAddress(QModelIndex,int,int)));
|
|
|
|
selectionChanged();
|
|
}
|
|
|
|
void AddressBookPage::setOptionsModel(OptionsModel *optionsModel)
|
|
{
|
|
this->optionsModel = optionsModel;
|
|
}
|
|
|
|
void AddressBookPage::on_copyAddress_clicked()
|
|
{
|
|
GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address);
|
|
}
|
|
|
|
void AddressBookPage::onCopyLabelAction()
|
|
{
|
|
GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Label);
|
|
}
|
|
|
|
void AddressBookPage::onEditAction()
|
|
{
|
|
if(!ui->tableView->selectionModel())
|
|
return;
|
|
QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
|
|
if(indexes.isEmpty())
|
|
return;
|
|
|
|
EditAddressDialog dlg(
|
|
tab == SendingTab ?
|
|
EditAddressDialog::EditSendingAddress :
|
|
EditAddressDialog::EditReceivingAddress);
|
|
dlg.setModel(model);
|
|
QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
|
|
dlg.loadRow(origIndex.row());
|
|
dlg.exec();
|
|
}
|
|
|
|
void AddressBookPage::on_signMessage_clicked()
|
|
{
|
|
QTableView *table = ui->tableView;
|
|
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
|
|
|
|
foreach (QModelIndex index, indexes)
|
|
{
|
|
QString address = index.data().toString();
|
|
emit signMessage(address);
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::on_verifyMessage_clicked()
|
|
{
|
|
QTableView *table = ui->tableView;
|
|
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
|
|
|
|
foreach (QModelIndex index, indexes)
|
|
{
|
|
QString address = index.data().toString();
|
|
emit verifyMessage(address);
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::onSendCoinsAction()
|
|
{
|
|
QTableView *table = ui->tableView;
|
|
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
|
|
|
|
foreach (QModelIndex index, indexes)
|
|
{
|
|
QString address = index.data().toString();
|
|
emit sendCoins(address);
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::on_newAddress_clicked()
|
|
{
|
|
if(!model)
|
|
return;
|
|
|
|
EditAddressDialog dlg(
|
|
tab == SendingTab ?
|
|
EditAddressDialog::NewSendingAddress :
|
|
EditAddressDialog::NewReceivingAddress, this);
|
|
dlg.setModel(model);
|
|
if(dlg.exec())
|
|
{
|
|
newAddressToSelect = dlg.getAddress();
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::on_deleteAddress_clicked()
|
|
{
|
|
QTableView *table = ui->tableView;
|
|
if(!table->selectionModel())
|
|
return;
|
|
|
|
QModelIndexList indexes = table->selectionModel()->selectedRows();
|
|
if(!indexes.isEmpty())
|
|
{
|
|
table->model()->removeRow(indexes.at(0).row());
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::selectionChanged()
|
|
{
|
|
// Set button states based on selected tab and selection
|
|
QTableView *table = ui->tableView;
|
|
if(!table->selectionModel())
|
|
return;
|
|
|
|
if(table->selectionModel()->hasSelection())
|
|
{
|
|
switch(tab)
|
|
{
|
|
case SendingTab:
|
|
// In sending tab, allow deletion of selection
|
|
ui->deleteAddress->setEnabled(true);
|
|
ui->deleteAddress->setVisible(true);
|
|
deleteAction->setEnabled(true);
|
|
ui->signMessage->setEnabled(false);
|
|
ui->signMessage->setVisible(false);
|
|
ui->verifyMessage->setEnabled(true);
|
|
ui->verifyMessage->setVisible(true);
|
|
break;
|
|
case ReceivingTab:
|
|
// Deleting receiving addresses, however, is not allowed
|
|
ui->deleteAddress->setEnabled(false);
|
|
ui->deleteAddress->setVisible(false);
|
|
deleteAction->setEnabled(false);
|
|
ui->signMessage->setEnabled(true);
|
|
ui->signMessage->setVisible(true);
|
|
ui->verifyMessage->setEnabled(false);
|
|
ui->verifyMessage->setVisible(false);
|
|
break;
|
|
}
|
|
ui->copyAddress->setEnabled(true);
|
|
ui->showQRCode->setEnabled(true);
|
|
}
|
|
else
|
|
{
|
|
ui->deleteAddress->setEnabled(false);
|
|
ui->showQRCode->setEnabled(false);
|
|
ui->copyAddress->setEnabled(false);
|
|
ui->signMessage->setEnabled(false);
|
|
ui->verifyMessage->setEnabled(false);
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::done(int retval)
|
|
{
|
|
QTableView *table = ui->tableView;
|
|
if(!table->selectionModel() || !table->model())
|
|
return;
|
|
// When this is a tab/widget and not a model dialog, ignore "done"
|
|
if(mode == ForEditing)
|
|
return;
|
|
|
|
// Figure out which address was selected, and return it
|
|
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
|
|
|
|
foreach (QModelIndex index, indexes)
|
|
{
|
|
QVariant address = table->model()->data(index);
|
|
returnValue = address.toString();
|
|
}
|
|
|
|
if(returnValue.isEmpty())
|
|
{
|
|
// If no address entry selected, return rejected
|
|
retval = Rejected;
|
|
}
|
|
|
|
QDialog::done(retval);
|
|
}
|
|
|
|
void AddressBookPage::on_exportButton_clicked()
|
|
{
|
|
// CSV is currently the only supported format
|
|
QString filename = GUIUtil::getSaveFileName(
|
|
this,
|
|
tr("Export Address Book Data"), QString(),
|
|
tr("Comma separated file (*.csv)"));
|
|
|
|
if (filename.isNull()) return;
|
|
|
|
CSVModelWriter writer(filename);
|
|
|
|
// name, column, role
|
|
writer.setModel(proxyModel);
|
|
writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
|
|
writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
|
|
|
|
if(!writer.write())
|
|
{
|
|
QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename),
|
|
QMessageBox::Abort, QMessageBox::Abort);
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::on_showQRCode_clicked()
|
|
{
|
|
#ifdef USE_QRCODE
|
|
QTableView *table = ui->tableView;
|
|
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
|
|
|
|
foreach (QModelIndex index, indexes)
|
|
{
|
|
QString address = index.data().toString();
|
|
QString label = index.sibling(index.row(), 0).data(Qt::EditRole).toString();
|
|
|
|
QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this);
|
|
dialog->setModel(optionsModel);
|
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
dialog->show();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void AddressBookPage::contextualMenu(const QPoint &point)
|
|
{
|
|
QModelIndex index = ui->tableView->indexAt(point);
|
|
if(index.isValid())
|
|
{
|
|
contextMenu->exec(QCursor::pos());
|
|
}
|
|
}
|
|
|
|
void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int /*end*/)
|
|
{
|
|
QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
|
|
if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
|
|
{
|
|
// Select row of newly created address, once
|
|
ui->tableView->setFocus();
|
|
ui->tableView->selectRow(idx.row());
|
|
newAddressToSelect.clear();
|
|
}
|
|
}
|