311993ab10
- allows to directly select an address from the addressbook, chose "send coins" from the context menu, which sends you to sendcoins tab and fills in the selected address
949 lines
34 KiB
C++
949 lines
34 KiB
C++
/*
|
|
* Qt4 bitcoin GUI.
|
|
*
|
|
* W.J. van der Laan 2011-2012
|
|
* The Bitcoin Developers 2011-2012
|
|
*/
|
|
|
|
#include <QApplication>
|
|
|
|
#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 "notificator.h"
|
|
#include "guiutil.h"
|
|
#include "rpcconsole.h"
|
|
#include "ui_interface.h"
|
|
|
|
#ifdef Q_OS_MAC
|
|
#include "macdockiconhandler.h"
|
|
#endif
|
|
|
|
#include <QMenuBar>
|
|
#include <QMenu>
|
|
#include <QIcon>
|
|
#include <QVBoxLayout>
|
|
#include <QToolBar>
|
|
#include <QStatusBar>
|
|
#include <QLabel>
|
|
#include <QMessageBox>
|
|
#include <QProgressBar>
|
|
#include <QStackedWidget>
|
|
#include <QDateTime>
|
|
#include <QMovie>
|
|
#include <QFileDialog>
|
|
#include <QDesktopServices>
|
|
#include <QTimer>
|
|
#include <QDragEnterEvent>
|
|
#include <QUrl>
|
|
#include <QMimeData>
|
|
#include <QStyle>
|
|
|
|
#include <iostream>
|
|
|
|
BitcoinGUI::BitcoinGUI(QWidget *parent):
|
|
QMainWindow(parent),
|
|
clientModel(0),
|
|
walletModel(0),
|
|
encryptWalletAction(0),
|
|
changePassphraseAction(0),
|
|
aboutQtAction(0),
|
|
trayIcon(0),
|
|
notificator(0),
|
|
rpcConsole(0),
|
|
prevBlocks(0)
|
|
{
|
|
resize(850, 550);
|
|
setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet"));
|
|
#ifndef Q_OS_MAC
|
|
qApp->setWindowIcon(QIcon(":icons/bitcoin"));
|
|
setWindowIcon(QIcon(":icons/bitcoin"));
|
|
#else
|
|
setUnifiedTitleAndToolBarOnMac(true);
|
|
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
|
|
#endif
|
|
// Accept D&D of URIs
|
|
setAcceptDrops(true);
|
|
|
|
// Create actions for the toolbar, menu bar and tray/dock icon
|
|
createActions();
|
|
|
|
// Create application menu bar
|
|
createMenuBar();
|
|
|
|
// Create the toolbars
|
|
createToolBars();
|
|
|
|
// Create system tray icon and notification
|
|
createTrayIcon();
|
|
|
|
// 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(this);
|
|
|
|
signVerifyMessageDialog = new SignVerifyMessageDialog(this);
|
|
|
|
centralWidget = new QStackedWidget(this);
|
|
centralWidget->addWidget(overviewPage);
|
|
centralWidget->addWidget(transactionsPage);
|
|
centralWidget->addWidget(addressBookPage);
|
|
centralWidget->addWidget(receiveCoinsPage);
|
|
centralWidget->addWidget(sendCoinsPage);
|
|
setCentralWidget(centralWidget);
|
|
|
|
// Create status bar
|
|
statusBar();
|
|
|
|
// Status bar notification icons
|
|
QFrame *frameBlocks = new QFrame();
|
|
frameBlocks->setContentsMargins(0,0,0,0);
|
|
frameBlocks->setMinimumWidth(56);
|
|
frameBlocks->setMaximumWidth(56);
|
|
QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks);
|
|
frameBlocksLayout->setContentsMargins(3,0,3,0);
|
|
frameBlocksLayout->setSpacing(3);
|
|
labelEncryptionIcon = new QLabel();
|
|
labelConnectionsIcon = new QLabel();
|
|
labelBlocksIcon = new QLabel();
|
|
frameBlocksLayout->addStretch();
|
|
frameBlocksLayout->addWidget(labelEncryptionIcon);
|
|
frameBlocksLayout->addStretch();
|
|
frameBlocksLayout->addWidget(labelConnectionsIcon);
|
|
frameBlocksLayout->addStretch();
|
|
frameBlocksLayout->addWidget(labelBlocksIcon);
|
|
frameBlocksLayout->addStretch();
|
|
|
|
// Progress bar and label for blocks download
|
|
progressBarLabel = new QLabel();
|
|
progressBarLabel->setVisible(false);
|
|
progressBar = new QProgressBar();
|
|
progressBar->setAlignment(Qt::AlignCenter);
|
|
progressBar->setVisible(false);
|
|
|
|
// Override style sheet for progress bar for styles that have a segmented progress bar,
|
|
// as they make the text unreadable (workaround for issue #1071)
|
|
// See https://qt-project.org/doc/qt-4.8/gallery.html
|
|
QString curStyle = qApp->style()->metaObject()->className();
|
|
if(curStyle == "QWindowsStyle" || curStyle == "QWindowsXPStyle")
|
|
{
|
|
progressBar->setStyleSheet("QProgressBar { background-color: #e8e8e8; border: 1px solid grey; border-radius: 7px; padding: 1px; text-align: center; } QProgressBar::chunk { background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #FF8000, stop: 1 orange); border-radius: 7px; margin: 0px; }");
|
|
}
|
|
|
|
statusBar()->addWidget(progressBarLabel);
|
|
statusBar()->addWidget(progressBar);
|
|
statusBar()->addPermanentWidget(frameBlocks);
|
|
|
|
syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this);
|
|
|
|
// 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()));
|
|
|
|
rpcConsole = new RPCConsole(this);
|
|
connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show()));
|
|
|
|
// 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
|
|
connect(addressBookPage, SIGNAL(verifyMessage(QString)), this, SLOT(gotoVerifyMessageTab(QString)));
|
|
// Clicking on "Sign Message" in the receive coins page opens the sign message tab in the Sign/Verify Message dialog
|
|
connect(receiveCoinsPage, SIGNAL(signMessage(QString)), this, SLOT(gotoSignMessageTab(QString)));
|
|
|
|
// Install event filter to be able to catch status tip events (QEvent::StatusTip)
|
|
this->installEventFilter(this);
|
|
|
|
gotoOverviewPage();
|
|
}
|
|
|
|
BitcoinGUI::~BitcoinGUI()
|
|
{
|
|
if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
|
|
trayIcon->hide();
|
|
#ifdef Q_OS_MAC
|
|
delete appMenuBar;
|
|
#endif
|
|
}
|
|
|
|
void BitcoinGUI::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(showNormalIfMinimized()));
|
|
connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage()));
|
|
connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
|
|
connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage()));
|
|
connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
|
|
connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage()));
|
|
connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
|
|
connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
|
|
connect(addressBookAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
|
|
connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage()));
|
|
|
|
quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this);
|
|
quitAction->setStatusTip(tr("Quit application"));
|
|
quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
|
|
quitAction->setMenuRole(QAction::QuitRole);
|
|
aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About Bitcoin"), this);
|
|
aboutAction->setStatusTip(tr("Show information about Bitcoin"));
|
|
aboutAction->setMenuRole(QAction::AboutRole);
|
|
aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this);
|
|
aboutQtAction->setStatusTip(tr("Show information about Qt"));
|
|
aboutQtAction->setMenuRole(QAction::AboutQtRole);
|
|
optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this);
|
|
optionsAction->setStatusTip(tr("Modify configuration options for Bitcoin"));
|
|
optionsAction->setMenuRole(QAction::PreferencesRole);
|
|
toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this);
|
|
toggleHideAction->setStatusTip(tr("Show or hide the main Window"));
|
|
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());
|
|
openRPCConsoleAction = new QAction(QIcon(":/icons/debugwindow"), tr("&Debug window"), this);
|
|
openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console"));
|
|
|
|
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
|
|
connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked()));
|
|
connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
|
|
connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked()));
|
|
connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden()));
|
|
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 BitcoinGUI::createMenuBar()
|
|
{
|
|
#ifdef Q_OS_MAC
|
|
// Create a decoupled menu bar on Mac which stays even if the window is closed
|
|
appMenuBar = new QMenuBar();
|
|
#else
|
|
// Get the main window's menu bar on other platforms
|
|
appMenuBar = menuBar();
|
|
#endif
|
|
|
|
// Configure the menus
|
|
QMenu *file = appMenuBar->addMenu(tr("&File"));
|
|
file->addAction(backupWalletAction);
|
|
file->addAction(exportAction);
|
|
file->addAction(signMessageAction);
|
|
file->addAction(verifyMessageAction);
|
|
file->addSeparator();
|
|
file->addAction(quitAction);
|
|
|
|
QMenu *settings = appMenuBar->addMenu(tr("&Settings"));
|
|
settings->addAction(encryptWalletAction);
|
|
settings->addAction(changePassphraseAction);
|
|
settings->addSeparator();
|
|
settings->addAction(optionsAction);
|
|
|
|
QMenu *help = appMenuBar->addMenu(tr("&Help"));
|
|
help->addAction(openRPCConsoleAction);
|
|
help->addSeparator();
|
|
help->addAction(aboutAction);
|
|
help->addAction(aboutQtAction);
|
|
}
|
|
|
|
void BitcoinGUI::createToolBars()
|
|
{
|
|
QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
|
|
toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
|
toolbar->addAction(overviewAction);
|
|
toolbar->addAction(sendCoinsAction);
|
|
toolbar->addAction(receiveCoinsAction);
|
|
toolbar->addAction(historyAction);
|
|
toolbar->addAction(addressBookAction);
|
|
|
|
QToolBar *toolbar2 = addToolBar(tr("Actions toolbar"));
|
|
toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
|
toolbar2->addAction(exportAction);
|
|
}
|
|
|
|
void BitcoinGUI::setClientModel(ClientModel *clientModel)
|
|
{
|
|
this->clientModel = clientModel;
|
|
if(clientModel)
|
|
{
|
|
// Replace some strings and icons, when using the testnet
|
|
if(clientModel->isTestNet())
|
|
{
|
|
setWindowTitle(windowTitle() + QString(" ") + tr("[testnet]"));
|
|
#ifndef Q_OS_MAC
|
|
qApp->setWindowIcon(QIcon(":icons/bitcoin_testnet"));
|
|
setWindowIcon(QIcon(":icons/bitcoin_testnet"));
|
|
#else
|
|
MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet"));
|
|
#endif
|
|
if(trayIcon)
|
|
{
|
|
// Just attach " [testnet]" to the existing tooltip
|
|
trayIcon->setToolTip(trayIcon->toolTip() + QString(" ") + tr("[testnet]"));
|
|
trayIcon->setIcon(QIcon(":/icons/toolbar_testnet"));
|
|
}
|
|
|
|
toggleHideAction->setIcon(QIcon(":/icons/toolbar_testnet"));
|
|
aboutAction->setIcon(QIcon(":/icons/toolbar_testnet"));
|
|
}
|
|
|
|
// Create system tray menu (or setup the dock menu) that late to prevent users from calling actions,
|
|
// while the client has not yet fully loaded
|
|
if(trayIcon)
|
|
createTrayIconMenu();
|
|
|
|
// Keep up to date with client
|
|
setNumConnections(clientModel->getNumConnections());
|
|
connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
|
|
|
|
setNumBlocks(clientModel->getNumBlocks(), clientModel->getNumBlocksOfPeers());
|
|
connect(clientModel, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int)));
|
|
|
|
// Receive and report messages from network/worker thread
|
|
connect(clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int)));
|
|
|
|
overviewPage->setClientModel(clientModel);
|
|
rpcConsole->setClientModel(clientModel);
|
|
addressBookPage->setOptionsModel(clientModel->getOptionsModel());
|
|
receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel());
|
|
}
|
|
}
|
|
|
|
void BitcoinGUI::setWalletModel(WalletModel *walletModel)
|
|
{
|
|
this->walletModel = walletModel;
|
|
if(walletModel)
|
|
{
|
|
// Receive and report messages from wallet thread
|
|
connect(walletModel, SIGNAL(message(QString,QString,unsigned int)), this, 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(walletModel->getEncryptionStatus());
|
|
connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, 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 BitcoinGUI::createTrayIcon()
|
|
{
|
|
#ifndef Q_OS_MAC
|
|
trayIcon = new QSystemTrayIcon(this);
|
|
|
|
trayIcon->setToolTip(tr("Bitcoin client"));
|
|
trayIcon->setIcon(QIcon(":/icons/toolbar"));
|
|
trayIcon->show();
|
|
#endif
|
|
|
|
notificator = new Notificator(qApp->applicationName(), trayIcon);
|
|
}
|
|
|
|
void BitcoinGUI::createTrayIconMenu()
|
|
{
|
|
QMenu *trayIconMenu;
|
|
#ifndef Q_OS_MAC
|
|
trayIconMenu = new QMenu(this);
|
|
trayIcon->setContextMenu(trayIconMenu);
|
|
|
|
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
|
this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
|
|
#else
|
|
// Note: On Mac, the dock icon is used to provide the tray's functionality.
|
|
MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
|
|
trayIconMenu = dockIconHandler->dockMenu();
|
|
#endif
|
|
|
|
// Configuration of the tray icon (or dock icon) icon menu
|
|
trayIconMenu->addAction(toggleHideAction);
|
|
trayIconMenu->addSeparator();
|
|
trayIconMenu->addAction(sendCoinsAction);
|
|
trayIconMenu->addAction(receiveCoinsAction);
|
|
trayIconMenu->addSeparator();
|
|
trayIconMenu->addAction(signMessageAction);
|
|
trayIconMenu->addAction(verifyMessageAction);
|
|
trayIconMenu->addSeparator();
|
|
trayIconMenu->addAction(optionsAction);
|
|
trayIconMenu->addAction(openRPCConsoleAction);
|
|
#ifndef Q_OS_MAC // This is built-in on Mac
|
|
trayIconMenu->addSeparator();
|
|
trayIconMenu->addAction(quitAction);
|
|
#endif
|
|
}
|
|
|
|
#ifndef Q_OS_MAC
|
|
void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
|
|
{
|
|
if(reason == QSystemTrayIcon::Trigger)
|
|
{
|
|
// Click on system tray icon triggers show/hide of the main window
|
|
toggleHideAction->trigger();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void BitcoinGUI::optionsClicked()
|
|
{
|
|
if(!clientModel || !clientModel->getOptionsModel())
|
|
return;
|
|
OptionsDialog dlg;
|
|
dlg.setModel(clientModel->getOptionsModel());
|
|
dlg.exec();
|
|
}
|
|
|
|
void BitcoinGUI::aboutClicked()
|
|
{
|
|
AboutDialog dlg;
|
|
dlg.setModel(clientModel);
|
|
dlg.exec();
|
|
}
|
|
|
|
void BitcoinGUI::setNumConnections(int count)
|
|
{
|
|
QString icon;
|
|
switch(count)
|
|
{
|
|
case 0: icon = ":/icons/connect_0"; break;
|
|
case 1: case 2: case 3: icon = ":/icons/connect_1"; break;
|
|
case 4: case 5: case 6: icon = ":/icons/connect_2"; break;
|
|
case 7: case 8: case 9: icon = ":/icons/connect_3"; break;
|
|
default: icon = ":/icons/connect_4"; break;
|
|
}
|
|
labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
|
|
labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
|
|
}
|
|
|
|
void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks)
|
|
{
|
|
// Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text)
|
|
statusBar()->clearMessage();
|
|
|
|
// don't show / hide progress bar and its label if we have no connection to the network
|
|
enum BlockSource blockSource = clientModel ? clientModel->getBlockSource() : BLOCK_SOURCE_NONE;
|
|
if (blockSource == BLOCK_SOURCE_NONE || (blockSource == BLOCK_SOURCE_NETWORK && clientModel->getNumConnections() == 0))
|
|
{
|
|
progressBarLabel->setVisible(false);
|
|
progressBar->setVisible(false);
|
|
|
|
return;
|
|
}
|
|
|
|
QString tooltip;
|
|
|
|
QString importText;
|
|
switch (blockSource) {
|
|
case BLOCK_SOURCE_NONE:
|
|
case BLOCK_SOURCE_NETWORK:
|
|
importText = tr("Synchronizing with network...");
|
|
break;
|
|
case BLOCK_SOURCE_DISK:
|
|
importText = tr("Importing blocks from disk...");
|
|
break;
|
|
case BLOCK_SOURCE_REINDEX:
|
|
importText = tr("Reindexing blocks on disk...");
|
|
}
|
|
|
|
QDateTime lastBlockDate = clientModel->getLastBlockDate();
|
|
QDateTime currentDate = QDateTime::currentDateTime();
|
|
int secs = lastBlockDate.secsTo(currentDate);
|
|
|
|
if(count < nTotalBlocks)
|
|
{
|
|
tooltip = tr("Processed %1 of %2 (estimated) blocks of transaction history.").arg(count).arg(nTotalBlocks);
|
|
}
|
|
else
|
|
{
|
|
tooltip = tr("Processed %1 blocks of transaction history.").arg(count);
|
|
}
|
|
|
|
// Set icon state: spinning if catching up, tick otherwise
|
|
if(secs < 90*60 && count >= nTotalBlocks)
|
|
{
|
|
tooltip = tr("Up to date") + QString(".<br>") + tooltip;
|
|
labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
|
|
|
|
overviewPage->showOutOfSyncWarning(false);
|
|
|
|
progressBarLabel->setVisible(false);
|
|
progressBar->setVisible(false);
|
|
}
|
|
else
|
|
{
|
|
// Represent time from last generated block in human readable text
|
|
QString timeBehindText;
|
|
if(secs < 48*60*60)
|
|
{
|
|
timeBehindText = tr("%n hour(s)","",secs/(60*60));
|
|
}
|
|
else if(secs < 14*24*60*60)
|
|
{
|
|
timeBehindText = tr("%n day(s)","",secs/(24*60*60));
|
|
}
|
|
else
|
|
{
|
|
timeBehindText = tr("%n week(s)","",secs/(7*24*60*60));
|
|
}
|
|
|
|
progressBarLabel->setText(importText);
|
|
progressBarLabel->setVisible(true);
|
|
progressBar->setFormat(tr("%1 behind").arg(timeBehindText));
|
|
progressBar->setMaximum(1000000000);
|
|
progressBar->setValue(clientModel->getVerificationProgress() * 1000000000.0 + 0.5);
|
|
progressBar->setVisible(true);
|
|
|
|
tooltip = tr("Catching up...") + QString("<br>") + tooltip;
|
|
labelBlocksIcon->setMovie(syncIconMovie);
|
|
if(count != prevBlocks)
|
|
syncIconMovie->jumpToNextFrame();
|
|
prevBlocks = count;
|
|
|
|
overviewPage->showOutOfSyncWarning(true);
|
|
|
|
tooltip += QString("<br>");
|
|
tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText);
|
|
tooltip += QString("<br>");
|
|
tooltip += tr("Transactions after this will not yet be visible.");
|
|
}
|
|
|
|
// Don't word-wrap this (fixed-width) tooltip
|
|
tooltip = QString("<nobr>") + tooltip + QString("</nobr>");
|
|
|
|
labelBlocksIcon->setToolTip(tooltip);
|
|
progressBarLabel->setToolTip(tooltip);
|
|
progressBar->setToolTip(tooltip);
|
|
}
|
|
|
|
void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, bool *ret)
|
|
{
|
|
QString strTitle = tr("Bitcoin") + " - ";
|
|
// Default to information icon
|
|
int nMBoxIcon = QMessageBox::Information;
|
|
int nNotifyIcon = Notificator::Information;
|
|
|
|
// Check for usage of predefined title
|
|
switch (style) {
|
|
case CClientUIInterface::MSG_ERROR:
|
|
strTitle += tr("Error");
|
|
break;
|
|
case CClientUIInterface::MSG_WARNING:
|
|
strTitle += tr("Warning");
|
|
break;
|
|
case CClientUIInterface::MSG_INFORMATION:
|
|
strTitle += tr("Information");
|
|
break;
|
|
default:
|
|
strTitle += title; // Use supplied title
|
|
}
|
|
|
|
// Check for error/warning icon
|
|
if (style & CClientUIInterface::ICON_ERROR) {
|
|
nMBoxIcon = QMessageBox::Critical;
|
|
nNotifyIcon = Notificator::Critical;
|
|
}
|
|
else if (style & CClientUIInterface::ICON_WARNING) {
|
|
nMBoxIcon = QMessageBox::Warning;
|
|
nNotifyIcon = Notificator::Warning;
|
|
}
|
|
|
|
// Display message
|
|
if (style & CClientUIInterface::MODAL) {
|
|
// Check for buttons, use OK as default, if none was supplied
|
|
QMessageBox::StandardButton buttons;
|
|
if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK)))
|
|
buttons = QMessageBox::Ok;
|
|
|
|
QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons);
|
|
int r = mBox.exec();
|
|
if (ret != NULL)
|
|
*ret = r == QMessageBox::Ok;
|
|
}
|
|
else
|
|
notificator->notify((Notificator::Class)nNotifyIcon, strTitle, message);
|
|
}
|
|
|
|
void BitcoinGUI::changeEvent(QEvent *e)
|
|
{
|
|
QMainWindow::changeEvent(e);
|
|
#ifndef Q_OS_MAC // Ignored on Mac
|
|
if(e->type() == QEvent::WindowStateChange)
|
|
{
|
|
if(clientModel && clientModel->getOptionsModel()->getMinimizeToTray())
|
|
{
|
|
QWindowStateChangeEvent *wsevt = static_cast<QWindowStateChangeEvent*>(e);
|
|
if(!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized())
|
|
{
|
|
QTimer::singleShot(0, this, SLOT(hide()));
|
|
e->ignore();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void BitcoinGUI::closeEvent(QCloseEvent *event)
|
|
{
|
|
if(clientModel)
|
|
{
|
|
#ifndef Q_OS_MAC // Ignored on Mac
|
|
if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
|
|
!clientModel->getOptionsModel()->getMinimizeOnClose())
|
|
{
|
|
qApp->quit();
|
|
}
|
|
#endif
|
|
}
|
|
QMainWindow::closeEvent(event);
|
|
}
|
|
|
|
void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee)
|
|
{
|
|
QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, "
|
|
"which goes to the nodes that process your transaction and helps to support the network. "
|
|
"Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired));
|
|
QMessageBox::StandardButton retval = QMessageBox::question(
|
|
this, tr("Confirm transaction fee"), strMessage,
|
|
QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes);
|
|
*payFee = (retval == QMessageBox::Yes);
|
|
}
|
|
|
|
void BitcoinGUI::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();
|
|
|
|
// On new transaction, make an info balloon
|
|
message((amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"),
|
|
tr("Date: %1\n"
|
|
"Amount: %2\n"
|
|
"Type: %3\n"
|
|
"Address: %4\n")
|
|
.arg(date)
|
|
.arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true))
|
|
.arg(type)
|
|
.arg(address), CClientUIInterface::MSG_INFORMATION);
|
|
}
|
|
|
|
void BitcoinGUI::gotoOverviewPage()
|
|
{
|
|
overviewAction->setChecked(true);
|
|
centralWidget->setCurrentWidget(overviewPage);
|
|
|
|
exportAction->setEnabled(false);
|
|
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
|
|
}
|
|
|
|
void BitcoinGUI::gotoHistoryPage()
|
|
{
|
|
historyAction->setChecked(true);
|
|
centralWidget->setCurrentWidget(transactionsPage);
|
|
|
|
exportAction->setEnabled(true);
|
|
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
|
|
connect(exportAction, SIGNAL(triggered()), transactionView, SLOT(exportClicked()));
|
|
}
|
|
|
|
void BitcoinGUI::gotoAddressBookPage()
|
|
{
|
|
addressBookAction->setChecked(true);
|
|
centralWidget->setCurrentWidget(addressBookPage);
|
|
|
|
exportAction->setEnabled(true);
|
|
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
|
|
connect(exportAction, SIGNAL(triggered()), addressBookPage, SLOT(exportClicked()));
|
|
}
|
|
|
|
void BitcoinGUI::gotoReceiveCoinsPage()
|
|
{
|
|
receiveCoinsAction->setChecked(true);
|
|
centralWidget->setCurrentWidget(receiveCoinsPage);
|
|
|
|
exportAction->setEnabled(true);
|
|
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
|
|
connect(exportAction, SIGNAL(triggered()), receiveCoinsPage, SLOT(exportClicked()));
|
|
}
|
|
|
|
void BitcoinGUI::gotoSendCoinsPage(QString addr)
|
|
{
|
|
sendCoinsAction->setChecked(true);
|
|
centralWidget->setCurrentWidget(sendCoinsPage);
|
|
|
|
exportAction->setEnabled(false);
|
|
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
|
|
|
|
if(!addr.isEmpty())
|
|
sendCoinsPage->setAddress(addr);
|
|
}
|
|
|
|
void BitcoinGUI::gotoSignMessageTab(QString addr)
|
|
{
|
|
// call show() in showTab_SM()
|
|
signVerifyMessageDialog->showTab_SM(true);
|
|
|
|
if(!addr.isEmpty())
|
|
signVerifyMessageDialog->setAddress_SM(addr);
|
|
}
|
|
|
|
void BitcoinGUI::gotoVerifyMessageTab(QString addr)
|
|
{
|
|
// call show() in showTab_VM()
|
|
signVerifyMessageDialog->showTab_VM(true);
|
|
|
|
if(!addr.isEmpty())
|
|
signVerifyMessageDialog->setAddress_VM(addr);
|
|
}
|
|
|
|
void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event)
|
|
{
|
|
// Accept only URIs
|
|
if(event->mimeData()->hasUrls())
|
|
event->acceptProposedAction();
|
|
}
|
|
|
|
void BitcoinGUI::dropEvent(QDropEvent *event)
|
|
{
|
|
if(event->mimeData()->hasUrls())
|
|
{
|
|
int nValidUrisFound = 0;
|
|
QList<QUrl> uris = event->mimeData()->urls();
|
|
foreach(const QUrl &uri, uris)
|
|
{
|
|
if (sendCoinsPage->handleURI(uri.toString()))
|
|
nValidUrisFound++;
|
|
}
|
|
|
|
// if valid URIs were found
|
|
if (nValidUrisFound)
|
|
gotoSendCoinsPage();
|
|
else
|
|
message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
|
|
CClientUIInterface::ICON_WARNING);
|
|
}
|
|
|
|
event->acceptProposedAction();
|
|
}
|
|
|
|
bool BitcoinGUI::eventFilter(QObject *object, QEvent *event)
|
|
{
|
|
// Catch status tip events
|
|
if (event->type() == QEvent::StatusTip)
|
|
{
|
|
// Prevent adding text from setStatusTip(), if we currently use the status bar for displaying other stuff
|
|
if (progressBarLabel->isVisible() && progressBar->isVisible())
|
|
return true;
|
|
}
|
|
return QMainWindow::eventFilter(object, event);
|
|
}
|
|
|
|
void BitcoinGUI::handleURI(QString strURI)
|
|
{
|
|
// URI has to be valid
|
|
if (sendCoinsPage->handleURI(strURI))
|
|
{
|
|
showNormalIfMinimized();
|
|
gotoSendCoinsPage();
|
|
}
|
|
else
|
|
message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
|
|
CClientUIInterface::ICON_WARNING);
|
|
}
|
|
|
|
void BitcoinGUI::setEncryptionStatus(int status)
|
|
{
|
|
switch(status)
|
|
{
|
|
case WalletModel::Unencrypted:
|
|
labelEncryptionIcon->hide();
|
|
encryptWalletAction->setChecked(false);
|
|
changePassphraseAction->setEnabled(false);
|
|
encryptWalletAction->setEnabled(true);
|
|
break;
|
|
case WalletModel::Unlocked:
|
|
labelEncryptionIcon->show();
|
|
labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
|
|
labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
|
|
encryptWalletAction->setChecked(true);
|
|
changePassphraseAction->setEnabled(true);
|
|
encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
|
|
break;
|
|
case WalletModel::Locked:
|
|
labelEncryptionIcon->show();
|
|
labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
|
|
labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
|
|
encryptWalletAction->setChecked(true);
|
|
changePassphraseAction->setEnabled(true);
|
|
encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
|
|
break;
|
|
}
|
|
}
|
|
|
|
void BitcoinGUI::encryptWallet(bool status)
|
|
{
|
|
if(!walletModel)
|
|
return;
|
|
AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt:
|
|
AskPassphraseDialog::Decrypt, this);
|
|
dlg.setModel(walletModel);
|
|
dlg.exec();
|
|
|
|
setEncryptionStatus(walletModel->getEncryptionStatus());
|
|
}
|
|
|
|
void BitcoinGUI::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)) {
|
|
message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to the new location."),
|
|
CClientUIInterface::MSG_ERROR);
|
|
}
|
|
else
|
|
message(tr("Backup Successful"), tr("The wallet data was successfully saved to the new location."),
|
|
CClientUIInterface::MSG_INFORMATION);
|
|
}
|
|
}
|
|
|
|
void BitcoinGUI::changePassphrase()
|
|
{
|
|
AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
|
|
dlg.setModel(walletModel);
|
|
dlg.exec();
|
|
}
|
|
|
|
void BitcoinGUI::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();
|
|
}
|
|
}
|
|
|
|
void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
|
|
{
|
|
// activateWindow() (sometimes) helps with keyboard focus on Windows
|
|
if (isHidden())
|
|
{
|
|
show();
|
|
activateWindow();
|
|
}
|
|
else if (isMinimized())
|
|
{
|
|
showNormal();
|
|
activateWindow();
|
|
}
|
|
else if (GUIUtil::isObscured(this))
|
|
{
|
|
raise();
|
|
activateWindow();
|
|
}
|
|
else if(fToggleHidden)
|
|
hide();
|
|
}
|
|
|
|
void BitcoinGUI::toggleHidden()
|
|
{
|
|
showNormalIfMinimized(true);
|
|
}
|