2013-03-22 18:32:49 +01:00
|
|
|
/*
|
|
|
|
* Qt4 bitcoin GUI.
|
|
|
|
*
|
|
|
|
* W.J. van der Laan 2011-2012
|
|
|
|
* The Bitcoin Developers 2011-2013
|
|
|
|
*/
|
|
|
|
#include "walletview.h"
|
|
|
|
#include "bitcoingui.h"
|
|
|
|
#include "transactiontablemodel.h"
|
|
|
|
#include "addressbookpage.h"
|
|
|
|
#include "sendcoinsdialog.h"
|
|
|
|
#include "signverifymessagedialog.h"
|
|
|
|
#include "optionsdialog.h"
|
|
|
|
#include "aboutdialog.h"
|
|
|
|
#include "clientmodel.h"
|
|
|
|
#include "walletmodel.h"
|
|
|
|
#include "editaddressdialog.h"
|
|
|
|
#include "optionsmodel.h"
|
|
|
|
#include "transactiondescdialog.h"
|
|
|
|
#include "addresstablemodel.h"
|
|
|
|
#include "transactionview.h"
|
|
|
|
#include "overviewpage.h"
|
|
|
|
#include "bitcoinunits.h"
|
|
|
|
#include "guiconstants.h"
|
|
|
|
#include "askpassphrasedialog.h"
|
|
|
|
#include "guiutil.h"
|
|
|
|
#include "ui_interface.h"
|
|
|
|
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
#include <QActionGroup>
|
|
|
|
#include <QAction>
|
|
|
|
#include <QLabel>
|
|
|
|
#include <QDesktopServices>
|
|
|
|
#include <QFileDialog>
|
|
|
|
|
|
|
|
WalletView::WalletView(QWidget *parent, BitcoinGUI *_gui):
|
|
|
|
QStackedWidget(parent),
|
|
|
|
gui(_gui),
|
|
|
|
clientModel(0),
|
|
|
|
walletModel(0),
|
|
|
|
encryptWalletAction(0),
|
|
|
|
changePassphraseAction(0)
|
|
|
|
{
|
|
|
|
// Create actions for the toolbar, menu bar and tray/dock icon
|
|
|
|
createActions();
|
|
|
|
|
|
|
|
// Create tabs
|
|
|
|
overviewPage = new OverviewPage();
|
|
|
|
|
|
|
|
transactionsPage = new QWidget(this);
|
|
|
|
QVBoxLayout *vbox = new QVBoxLayout();
|
|
|
|
transactionView = new TransactionView(this);
|
|
|
|
vbox->addWidget(transactionView);
|
|
|
|
transactionsPage->setLayout(vbox);
|
|
|
|
|
|
|
|
addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab);
|
|
|
|
|
|
|
|
receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab);
|
|
|
|
|
|
|
|
sendCoinsPage = new SendCoinsDialog(gui);
|
|
|
|
|
|
|
|
signVerifyMessageDialog = new SignVerifyMessageDialog(gui);
|
|
|
|
|
|
|
|
addWidget(overviewPage);
|
|
|
|
addWidget(transactionsPage);
|
|
|
|
addWidget(addressBookPage);
|
|
|
|
addWidget(receiveCoinsPage);
|
|
|
|
addWidget(sendCoinsPage);
|
|
|
|
|
|
|
|
// Clicking on a transaction on the overview page simply sends you to transaction history page
|
|
|
|
connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage()));
|
|
|
|
connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex)));
|
|
|
|
|
|
|
|
// Double-clicking on a transaction on the transaction history page shows details
|
|
|
|
connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails()));
|
|
|
|
|
2013-04-01 14:43:50 +02:00
|
|
|
// Clicking on "Send Coins" in the address book sends you to the send coins tab
|
|
|
|
connect(addressBookPage, SIGNAL(sendCoins(QString)), this, SLOT(gotoSendCoinsPage(QString)));
|
|
|
|
// Clicking on "Verify Message" in the address book opens the verify message tab in the Sign/Verify Message dialog
|
2013-03-22 18:32:49 +01:00
|
|
|
connect(addressBookPage, SIGNAL(verifyMessage(QString)), this, SLOT(gotoVerifyMessageTab(QString)));
|
2013-04-01 14:43:50 +02:00
|
|
|
// Clicking on "Sign Message" in the receive coins page opens the sign message tab in the Sign/Verify Message dialog
|
2013-03-22 18:32:49 +01:00
|
|
|
connect(receiveCoinsPage, SIGNAL(signMessage(QString)), this, SLOT(gotoSignMessageTab(QString)));
|
|
|
|
|
|
|
|
gotoOverviewPage();
|
|
|
|
}
|
|
|
|
|
|
|
|
WalletView::~WalletView()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::createActions()
|
|
|
|
{
|
|
|
|
QActionGroup *tabGroup = new QActionGroup(this);
|
|
|
|
|
|
|
|
overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this);
|
|
|
|
overviewAction->setStatusTip(tr("Show general overview of wallet"));
|
|
|
|
overviewAction->setToolTip(overviewAction->statusTip());
|
|
|
|
overviewAction->setCheckable(true);
|
|
|
|
overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1));
|
|
|
|
tabGroup->addAction(overviewAction);
|
|
|
|
|
|
|
|
sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this);
|
|
|
|
sendCoinsAction->setStatusTip(tr("Send coins to a Bitcoin address"));
|
|
|
|
sendCoinsAction->setToolTip(sendCoinsAction->statusTip());
|
|
|
|
sendCoinsAction->setCheckable(true);
|
|
|
|
sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2));
|
|
|
|
tabGroup->addAction(sendCoinsAction);
|
|
|
|
|
|
|
|
receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive coins"), this);
|
|
|
|
receiveCoinsAction->setStatusTip(tr("Show the list of addresses for receiving payments"));
|
|
|
|
receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip());
|
|
|
|
receiveCoinsAction->setCheckable(true);
|
|
|
|
receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3));
|
|
|
|
tabGroup->addAction(receiveCoinsAction);
|
|
|
|
|
|
|
|
historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this);
|
|
|
|
historyAction->setStatusTip(tr("Browse transaction history"));
|
|
|
|
historyAction->setToolTip(historyAction->statusTip());
|
|
|
|
historyAction->setCheckable(true);
|
|
|
|
historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4));
|
|
|
|
tabGroup->addAction(historyAction);
|
|
|
|
|
|
|
|
addressBookAction = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this);
|
|
|
|
addressBookAction->setStatusTip(tr("Edit the list of stored addresses and labels"));
|
|
|
|
addressBookAction->setToolTip(addressBookAction->statusTip());
|
|
|
|
addressBookAction->setCheckable(true);
|
|
|
|
addressBookAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5));
|
|
|
|
tabGroup->addAction(addressBookAction);
|
|
|
|
|
|
|
|
connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage()));
|
|
|
|
connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage()));
|
|
|
|
connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage()));
|
|
|
|
connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
|
|
|
|
connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage()));
|
|
|
|
|
|
|
|
encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this);
|
|
|
|
encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet"));
|
|
|
|
encryptWalletAction->setCheckable(true);
|
|
|
|
backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this);
|
|
|
|
backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
|
|
|
|
changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this);
|
|
|
|
changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption"));
|
|
|
|
signMessageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this);
|
|
|
|
signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
|
|
|
|
verifyMessageAction = new QAction(QIcon(":/icons/transaction_0"), tr("&Verify message..."), this);
|
|
|
|
verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
|
|
|
|
|
|
|
|
exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this);
|
|
|
|
exportAction->setStatusTip(tr("Export the data in the current tab to a file"));
|
|
|
|
exportAction->setToolTip(exportAction->statusTip());
|
|
|
|
|
|
|
|
connect(encryptWalletAction, SIGNAL(triggered(bool)), this, SLOT(encryptWallet(bool)));
|
|
|
|
connect(backupWalletAction, SIGNAL(triggered()), this, SLOT(backupWallet()));
|
|
|
|
connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase()));
|
|
|
|
connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab()));
|
|
|
|
connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::setBitcoinGUI(BitcoinGUI *gui)
|
|
|
|
{
|
|
|
|
this->gui = gui;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::setClientModel(ClientModel *clientModel)
|
|
|
|
{
|
|
|
|
this->clientModel = clientModel;
|
|
|
|
if(clientModel)
|
|
|
|
{
|
|
|
|
overviewPage->setClientModel(clientModel);
|
|
|
|
addressBookPage->setOptionsModel(clientModel->getOptionsModel());
|
|
|
|
receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::setWalletModel(WalletModel *walletModel)
|
|
|
|
{
|
|
|
|
this->walletModel = walletModel;
|
|
|
|
if(walletModel)
|
|
|
|
{
|
|
|
|
// Receive and report messages from wallet thread
|
|
|
|
connect(walletModel, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int)));
|
|
|
|
|
|
|
|
// Put transaction list in tabs
|
|
|
|
transactionView->setModel(walletModel);
|
|
|
|
overviewPage->setWalletModel(walletModel);
|
|
|
|
addressBookPage->setModel(walletModel->getAddressTableModel());
|
|
|
|
receiveCoinsPage->setModel(walletModel->getAddressTableModel());
|
|
|
|
sendCoinsPage->setModel(walletModel);
|
|
|
|
signVerifyMessageDialog->setModel(walletModel);
|
|
|
|
|
|
|
|
setEncryptionStatus();
|
|
|
|
connect(walletModel, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int)));
|
|
|
|
|
|
|
|
// Balloon pop-up for new transaction
|
|
|
|
connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
|
|
|
|
this, SLOT(incomingTransaction(QModelIndex,int,int)));
|
|
|
|
|
|
|
|
// Ask for passphrase if needed
|
|
|
|
connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::incomingTransaction(const QModelIndex& parent, int start, int /*end*/)
|
|
|
|
{
|
|
|
|
// Prevent balloon-spam when initial block download is in progress
|
|
|
|
if(!walletModel || !clientModel || clientModel->inInitialBlockDownload())
|
|
|
|
return;
|
|
|
|
|
|
|
|
TransactionTableModel *ttm = walletModel->getTransactionTableModel();
|
|
|
|
|
|
|
|
QString date = ttm->index(start, TransactionTableModel::Date, parent)
|
|
|
|
.data().toString();
|
|
|
|
qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent)
|
|
|
|
.data(Qt::EditRole).toULongLong();
|
|
|
|
QString type = ttm->index(start, TransactionTableModel::Type, parent)
|
|
|
|
.data().toString();
|
|
|
|
QString address = ttm->index(start, TransactionTableModel::ToAddress, parent)
|
|
|
|
.data().toString();
|
|
|
|
|
|
|
|
gui->incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::gotoOverviewPage()
|
|
|
|
{
|
|
|
|
overviewAction->setChecked(true);
|
|
|
|
setCurrentWidget(overviewPage);
|
|
|
|
|
|
|
|
exportAction->setEnabled(false);
|
|
|
|
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::gotoHistoryPage()
|
|
|
|
{
|
|
|
|
historyAction->setChecked(true);
|
|
|
|
setCurrentWidget(transactionsPage);
|
|
|
|
|
|
|
|
exportAction->setEnabled(true);
|
|
|
|
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
|
|
|
|
connect(exportAction, SIGNAL(triggered()), transactionView, SLOT(exportClicked()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::gotoAddressBookPage()
|
|
|
|
{
|
|
|
|
addressBookAction->setChecked(true);
|
|
|
|
setCurrentWidget(addressBookPage);
|
|
|
|
|
|
|
|
exportAction->setEnabled(true);
|
|
|
|
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
|
|
|
|
connect(exportAction, SIGNAL(triggered()), addressBookPage, SLOT(exportClicked()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::gotoReceiveCoinsPage()
|
|
|
|
{
|
|
|
|
receiveCoinsAction->setChecked(true);
|
|
|
|
setCurrentWidget(receiveCoinsPage);
|
|
|
|
|
|
|
|
exportAction->setEnabled(true);
|
|
|
|
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
|
|
|
|
connect(exportAction, SIGNAL(triggered()), receiveCoinsPage, SLOT(exportClicked()));
|
|
|
|
}
|
|
|
|
|
2013-04-01 14:43:50 +02:00
|
|
|
void WalletView::gotoSendCoinsPage(QString addr)
|
2013-03-22 18:32:49 +01:00
|
|
|
{
|
|
|
|
sendCoinsAction->setChecked(true);
|
|
|
|
setCurrentWidget(sendCoinsPage);
|
|
|
|
|
|
|
|
exportAction->setEnabled(false);
|
|
|
|
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
|
2013-04-01 14:43:50 +02:00
|
|
|
|
|
|
|
if(!addr.isEmpty())
|
|
|
|
sendCoinsPage->setAddress(addr);
|
2013-03-22 18:32:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::gotoSignMessageTab(QString addr)
|
|
|
|
{
|
|
|
|
// call show() in showTab_SM()
|
|
|
|
signVerifyMessageDialog->showTab_SM(true);
|
|
|
|
|
|
|
|
if(!addr.isEmpty())
|
|
|
|
signVerifyMessageDialog->setAddress_SM(addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::gotoVerifyMessageTab(QString addr)
|
|
|
|
{
|
|
|
|
// call show() in showTab_VM()
|
|
|
|
signVerifyMessageDialog->showTab_VM(true);
|
|
|
|
|
|
|
|
if(!addr.isEmpty())
|
|
|
|
signVerifyMessageDialog->setAddress_VM(addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WalletView::handleURI(const QString& strURI)
|
|
|
|
{
|
|
|
|
// URI has to be valid
|
|
|
|
if (sendCoinsPage->handleURI(strURI))
|
|
|
|
{
|
|
|
|
gotoSendCoinsPage();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::showOutOfSyncWarning(bool fShow)
|
|
|
|
{
|
|
|
|
overviewPage->showOutOfSyncWarning(fShow);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::setEncryptionStatus()
|
|
|
|
{
|
|
|
|
gui->setEncryptionStatus(walletModel->getEncryptionStatus());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::encryptWallet(bool status)
|
|
|
|
{
|
|
|
|
if(!walletModel)
|
|
|
|
return;
|
|
|
|
AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt:
|
|
|
|
AskPassphraseDialog::Decrypt, this);
|
|
|
|
dlg.setModel(walletModel);
|
|
|
|
dlg.exec();
|
|
|
|
|
|
|
|
setEncryptionStatus();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::backupWallet()
|
|
|
|
{
|
|
|
|
QString saveDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
|
|
|
|
QString filename = QFileDialog::getSaveFileName(this, tr("Backup Wallet"), saveDir, tr("Wallet Data (*.dat)"));
|
|
|
|
if(!filename.isEmpty()) {
|
|
|
|
if(!walletModel->backupWallet(filename)) {
|
|
|
|
gui->message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to the new location."),
|
|
|
|
CClientUIInterface::MSG_ERROR);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
gui->message(tr("Backup Successful"), tr("The wallet data was successfully saved to the new location."),
|
|
|
|
CClientUIInterface::MSG_INFORMATION);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::changePassphrase()
|
|
|
|
{
|
|
|
|
AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
|
|
|
|
dlg.setModel(walletModel);
|
|
|
|
dlg.exec();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletView::unlockWallet()
|
|
|
|
{
|
|
|
|
if(!walletModel)
|
|
|
|
return;
|
|
|
|
// Unlock wallet when requested by wallet model
|
|
|
|
if(walletModel->getEncryptionStatus() == WalletModel::Locked)
|
|
|
|
{
|
|
|
|
AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this);
|
|
|
|
dlg.setModel(walletModel);
|
|
|
|
dlg.exec();
|
|
|
|
}
|
|
|
|
}
|