X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fqt%2Fbitcoingui.cpp;h=db888a8c891a739e25ad2d3a5562cd3d0db62909;hb=f8ea0dd6459856f2df18ca2ad532d49432a087dd;hp=4d3efe26db93e3c19a0e5678af367a6928ea3da8;hpb=0f3981bea94cea957f0de4b128f7feffbfc2d9c6;p=novacoin.git diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 4d3efe2..db888a8 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1,346 +1,643 @@ /* * Qt4 bitcoin GUI. * - * W.J. van der Laan 2011 + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2012 */ #include "bitcoingui.h" #include "transactiontablemodel.h" -#include "addressbookdialog.h" +#include "addressbookpage.h" #include "sendcoinsdialog.h" +#include "signverifymessagedialog.h" +#include "secondauthdialog.h" +#include "multisigdialog.h" #include "optionsdialog.h" #include "aboutdialog.h" #include "clientmodel.h" -#include "guiutil.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 "ui_interface.h" +#include "rpcconsole.h" +#include "mintingview.h" -#include "main.h" +#ifdef Q_OS_MAC +#include "macdockiconhandler.h" +#endif #include +#if QT_VERSION < 0x050000 #include +#endif #include #include #include #include #include -#include #include #include #include -#include #include #include -#include #include -#include -#include #include - -#include +#include +#include +#include +#include +#include +#if QT_VERSION < 0x050000 +#include +#else +#include +#endif +#include +#include +#if QT_VERSION < 0x050000 +#include +#endif +#include +#include #include +extern bool fWalletUnlockMintOnly; +extern uint64_t nStakeInputsMapSize; + BitcoinGUI::BitcoinGUI(QWidget *parent): - QMainWindow(parent), trayIcon(0) + QMainWindow(parent), + clientModel(0), + walletModel(0), + signVerifyMessageDialog(0), + secondAuthDialog(0), + multisigPage(0), + encryptWalletAction(0), + lockWalletAction(0), + unlockWalletAction(0), + unlockWalletMiningAction(0), + changePassphraseAction(0), + aboutQtAction(0), + trayIcon(0), + notificator(0), + rpcConsole(0), + aboutDialog(0), + optionsDialog(0) { resize(850, 550); - setWindowTitle(tr("Bitcoin")); + setWindowTitle(tr("NovaCoin") + " - " + tr("Wallet")); +#ifndef Q_OS_MAC + qApp->setWindowIcon(QIcon(":icons/bitcoin")); setWindowIcon(QIcon(":icons/bitcoin")); +#else + setUnifiedTitleAndToolBarOnMac(true); + QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); +#endif - createActions(); + int nQtStyle = GetArg("-qtstyle", 0); + if(nQtStyle < 0) nQtStyle = 0; - // Menus - QMenu *file = menuBar()->addMenu("&File"); - file->addAction(sendcoins); - file->addSeparator(); - file->addAction(quit); - - QMenu *settings = menuBar()->addMenu("&Settings"); - settings->addAction(receivingAddresses); - settings->addAction(options); + if(!nQtStyle) { + resize(850, 550); + qApp->setStyleSheet(""); + } else if(nQtStyle == 1) { + resize(850, 525); +#ifndef Q_OS_MAC + qApp->setStyleSheet("QToolBar QToolButton { text-align: center; width: 100%; \ + padding-left: 5px; padding-right: 5px; padding-top: 2px; padding-bottom: 2px; \ + margin-top: 2px; } \ + QToolBar QToolButton:hover { font-weight: bold; } \ + #toolbar { border: none; height: 100%; min-width: 150px; max-width: 150px; } \ + QMenuBar { min-height: 20px; }"); +#else + qApp->setStyleSheet("QToolBar QToolButton { text-align: center; width: 100%; \ + padding-left: 5px; padding-right: 5px; padding-top: 2px; padding-bottom: 2px; \ + margin-top: 2px; } \ + QToolBar QToolButton:hover { font-weight: bold; background-color: transparent; } \ + #toolbar { border: none; height: 100%; min-width: 150px; max-width: 150px; }"); +#endif + } else { + resize(850, 525); +#ifndef Q_OS_MAC + qApp->setStyleSheet("QToolBar QToolButton { text-align: center; width: 100%; \ + color: white; background-color: grey; padding-left: 5px; padding-right: 5px; \ + padding-top: 2px; padding-bottom: 2px; margin-top: 2px; } \ + QToolBar QToolButton:hover { font-weight: bold; \ + background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 2, \ + stop: 0 #808080, stop: 1 #d2d2d2); } \ + #toolbar { border: none; height: 100%; min-width: 150px; max-width: 150px; \ + background-color: grey; } \ + QMenuBar { color: white; background-color: grey; } \ + QMenuBar::item { color: white; background-color: grey; \ + padding-top: 6px; padding-bottom: 6px; \ + padding-left: 10px; padding-right: 10px; } \ + QMenuBar::item:selected { background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 2, \ + stop: 0 #808080, stop: 1 #d2d2d2); } \ + QMenu { border: 1px solid; color: black; background-color: grey; } \ + QMenu::item { background-color: grey; } \ + QMenu::item:disabled { color: gray; } \ + QMenu::item:enabled:selected { color: white; background-color: grey; } \ + QMenu::separator { height: 4px; }"); +#else + qApp->setStyleSheet("QToolBar QToolButton { text-align: center; width: 100%; \ + color: white; padding-left: 5px; padding-right: 5px; \ + padding-top: 2px; padding-bottom: 2px; margin-top: 2px; } \ + QToolBar QToolButton:hover { font-weight: bold; \ + background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 2, \ + stop: 0 #808080, stop: 1 #d2d2d2); } \ + #toolbar { border: none; height: 100%; min-width: 150px; max-width: 150px; \ + background-color: grey; }"); +#endif + } - QMenu *help = menuBar()->addMenu("&Help"); - help->addAction(about); - - // Toolbar - QToolBar *toolbar = addToolBar("Main toolbar"); - toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - toolbar->addAction(sendcoins); - toolbar->addAction(addressbook); - - // Address:
: New... : Paste to clipboard - QHBoxLayout *hbox_address = new QHBoxLayout(); - hbox_address->addWidget(new QLabel(tr("Your Bitcoin address:"))); - address = new QLineEdit(); - address->setReadOnly(true); - address->setFont(GUIUtil::bitcoinAddressFont()); - address->setToolTip(tr("Your current default receiving address")); - hbox_address->addWidget(address); - - QPushButton *button_new = new QPushButton(tr("&New...")); - button_new->setToolTip(tr("Create new receiving address")); - QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); - button_clipboard->setToolTip(tr("Copy current receiving address to the system clipboard")); - hbox_address->addWidget(button_new); - hbox_address->addWidget(button_clipboard); - - // Balance: - QHBoxLayout *hbox_balance = new QHBoxLayout(); - hbox_balance->addWidget(new QLabel(tr("Balance:"))); - hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ - - labelBalance = new QLabel(); - labelBalance->setFont(QFont("Monospace")); - labelBalance->setToolTip(tr("Your current balance")); - hbox_balance->addWidget(labelBalance); - hbox_balance->addStretch(1); - + // Accept D&D of URIs + setAcceptDrops(true); + + // Create actions for the toolbar, menu bar and tray/dock icon + createActions(nQtStyle); + + // Create application menu bar + createMenuBar(); + + // Create the toolbars + createToolBars(nQtStyle); + + // Create the tray icon (or setup the dock icon) + createTrayIcon(); + + // Create tabs + overviewPage = new OverviewPage(); + + transactionsPage = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(); - vbox->addLayout(hbox_address); - vbox->addLayout(hbox_balance); + transactionView = new TransactionView(this); + vbox->addWidget(transactionView); + transactionsPage->setLayout(vbox); - vbox->addWidget(createTabs()); + mintingPage = new QWidget(this); + QVBoxLayout *vboxMinting = new QVBoxLayout(); + mintingView = new MintingView(this); + vboxMinting->addWidget(mintingView); + mintingPage->setLayout(vboxMinting); + + addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); + + receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab); + + sendCoinsPage = new SendCoinsDialog(this); + + signVerifyMessageDialog = new SignVerifyMessageDialog(0); + + secondAuthDialog = new SecondAuthDialog(0); + + multisigPage = new MultisigDialog(0); + + centralWidget = new QStackedWidget(this); + centralWidget->addWidget(overviewPage); + centralWidget->addWidget(transactionsPage); + centralWidget->addWidget(mintingPage); + centralWidget->addWidget(addressBookPage); + centralWidget->addWidget(receiveCoinsPage); + centralWidget->addWidget(sendCoinsPage); + setCentralWidget(centralWidget); - QWidget *centralwidget = new QWidget(this); - centralwidget->setLayout(vbox); - setCentralWidget(centralwidget); - // Create status bar statusBar(); - labelConnections = new QLabel(); - labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelConnections->setMinimumWidth(150); - labelConnections->setToolTip(tr("Number of connections to other clients")); - - labelBlocks = new QLabel(); - labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelBlocks->setMinimumWidth(130); - labelBlocks->setToolTip(tr("Number of blocks in the block chain")); - - labelTransactions = new QLabel(); - labelTransactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelTransactions->setMinimumWidth(130); - labelTransactions->setToolTip(tr("Number of transactions in your wallet")); - - statusBar()->addPermanentWidget(labelConnections); - statusBar()->addPermanentWidget(labelBlocks); - statusBar()->addPermanentWidget(labelTransactions); - - // Action bindings - connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); - connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); + // Status bar notification icons + QFrame *frameBlocks = new QFrame(); + frameBlocks->setContentsMargins(0,0,0,0); + frameBlocks->setMinimumWidth(72); + frameBlocks->setMaximumWidth(72); + QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); + frameBlocksLayout->setContentsMargins(3,0,3,0); + frameBlocksLayout->setSpacing(3); + labelEncryptionIcon = new QLabel(); + labelMiningIcon = new QLabel(); + labelConnectionsIcon = new QLabel(); + labelBlocksIcon = new QLabel(); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelEncryptionIcon); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelMiningIcon); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelConnectionsIcon); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelBlocksIcon); + frameBlocksLayout->addStretch(); - createTrayIcon(); + // 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(0); + connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show())); + connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(raise())); + + aboutDialog = new AboutDialog(0); + optionsDialog = new OptionsDialog(0); + + // Clicking on "Verify Message" in the address book sends you to the verify message tab + connect(addressBookPage, SIGNAL(verifyMessage(QString)), this, SLOT(gotoVerifyMessageTab(QString))); + // Clicking on "Sign Message" in the receive coins page sends you to the sign message tab + connect(receiveCoinsPage, SIGNAL(signMessage(QString)), this, SLOT(gotoSignMessageTab(QString))); + + gotoOverviewPage(); } -void BitcoinGUI::createActions() +BitcoinGUI::~BitcoinGUI() { - quit = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this); - quit->setToolTip(tr("Quit application")); - sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); - sendcoins->setToolTip(tr("Send coins to a bitcoin address")); - addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); - addressbook->setToolTip(tr("Edit the list of stored addresses and labels")); - about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); - about->setToolTip(tr("Show information about Bitcoin")); - receivingAddresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); - receivingAddresses->setToolTip(tr("Show the list of receiving addresses and edit their labels")); - options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); - options->setToolTip(tr("Modify configuration options for bitcoin")); - openBitcoin = new QAction(QIcon(":/icons/bitcoin"), "Open &Bitcoin", this); - openBitcoin->setToolTip(tr("Show the Bitcoin window")); + if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) + trayIcon->hide(); +#ifdef Q_OS_MAC + delete appMenuBar; +#endif - connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); - connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); - connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); - connect(receivingAddresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); - connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); - connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); - connect(openBitcoin, SIGNAL(triggered()), this, SLOT(show())); + delete rpcConsole; + delete aboutDialog; + delete optionsDialog; + delete multisigPage; + delete secondAuthDialog; + delete signVerifyMessageDialog; } -void BitcoinGUI::setModel(ClientModel *model) +void BitcoinGUI::createActions(int nQtStyle) { - this->model = model; + QActionGroup *tabGroup = new QActionGroup(this); - // Keep up to date with client - setBalance(model->getBalance()); - connect(model, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); + overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this); + overviewAction->setToolTip(tr("Show general overview of wallet")); + overviewAction->setCheckable(true); + overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1)); + tabGroup->addAction(overviewAction); - setNumConnections(model->getNumConnections()); - connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); + sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); + sendCoinsAction->setToolTip(tr("Send coins to a NovaCoin address")); + sendCoinsAction->setCheckable(true); + sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); + tabGroup->addAction(sendCoinsAction); - setNumTransactions(model->getNumTransactions()); - connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); + receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive coins"), this); + receiveCoinsAction->setToolTip(tr("Show the list of addresses for receiving payments")); + receiveCoinsAction->setCheckable(true); + receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); + tabGroup->addAction(receiveCoinsAction); - setNumBlocks(model->getNumBlocks()); - connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); + historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this); + historyAction->setToolTip(tr("Browse transaction history")); + historyAction->setCheckable(true); + historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); + tabGroup->addAction(historyAction); - setAddress(model->getAddress()); - connect(model, SIGNAL(addressChanged(QString)), this, SLOT(setAddress(QString))); + mintingAction = new QAction(QIcon(":/icons/history"), tr("&Minting"), this); + mintingAction->setToolTip(tr("Show your minting capacity")); + mintingAction->setCheckable(true); + mintingAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); + tabGroup->addAction(mintingAction); - // Report errors from network/worker thread - connect(model, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); + addressBookAction = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); + addressBookAction->setToolTip(tr("Edit the list of stored addresses and labels")); + addressBookAction->setCheckable(true); + addressBookAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_6)); + tabGroup->addAction(addressBookAction); + + multisigAction = new QAction(QIcon(":/icons/send"), tr("Multisig"), this); + multisigAction->setStatusTip(tr("Open window for working with multisig addresses")); + tabGroup->addAction(multisigAction); + + 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(mintingAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(mintingAction, SIGNAL(triggered()), this, SLOT(gotoMintingPage())); + connect(addressBookAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage())); + connect(multisigAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(multisigAction, SIGNAL(triggered()), this, SLOT(gotoMultisigPage())); + + 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 NovaCoin"), this); + aboutAction->setStatusTip(tr("Show information about NovaCoin")); + aboutAction->setMenuRole(QAction::AboutRole); +#if QT_VERSION < 0x050000 + aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); +#else + aboutQtAction = new QAction(QIcon(":/qt-project.org/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); +#endif + 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 NovaCoin")); + optionsAction->setMenuRole(QAction::PreferencesRole); + toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this); + encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); + encryptWalletAction->setStatusTip(tr("Encrypt or decrypt wallet")); + encryptWalletAction->setCheckable(true); + backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this); + backupWalletAction->setStatusTip(tr("Backup wallet to another location")); + dumpWalletAction = new QAction(QIcon(":/icons/dump"), tr("&Dump Wallet..."), this); + dumpWalletAction->setStatusTip(tr("Dump keys to a text file")); + importWalletAction = new QAction(QIcon(":/icons/import"), tr("&Import Wallet..."), this); + importWalletAction->setStatusTip(tr("Import keys into a wallet")); + 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 Novacoin 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 Novacoin addresses")); + secondAuthAction = new QAction(QIcon(":/icons/key"), tr("Second &auth..."), this); + secondAuthAction->setStatusTip(tr("Second auth with your Novacoin addresses")); - // Put transaction list in tabs - setTabsModel(model->getTransactionTableModel()); + lockWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Lock wallet"), this); + lockWalletAction->setStatusTip(tr("Lock wallet")); + lockWalletAction->setCheckable(true); + + unlockWalletAction = new QAction(QIcon(":/icons/lock_open"), tr("Unlo&ck wallet"), this); + unlockWalletAction->setStatusTip(tr("Unlock wallet")); + unlockWalletAction->setCheckable(true); + + unlockWalletMiningAction = new QAction(QIcon(":/icons/mining_active"), tr("Unlo&ck wallet for mining"), this); + unlockWalletMiningAction->setStatusTip(tr("Unlock wallet for mining")); + unlockWalletMiningAction->setCheckable(true); + + exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this); + exportAction->setStatusTip(tr("Export the data in the current tab to a file")); + 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(lockWalletAction, SIGNAL(triggered(bool)), this, SLOT(lockWallet())); + connect(unlockWalletAction, SIGNAL(triggered(bool)), this, SLOT(unlockWallet())); + connect(unlockWalletMiningAction, SIGNAL(triggered(bool)), this, SLOT(unlockWalletMining(bool))); + connect(backupWalletAction, SIGNAL(triggered()), this, SLOT(backupWallet())); + connect(dumpWalletAction, SIGNAL(triggered()), this, SLOT(dumpWallet())); + connect(importWalletAction, SIGNAL(triggered()), this, SLOT(importWallet())); + connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase())); + connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab())); + connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); + connect(secondAuthAction, SIGNAL(triggered()), this, SLOT(gotoSecondAuthPage())); } -void BitcoinGUI::createTrayIcon() +void BitcoinGUI::createMenuBar() { - QMenu *trayIconMenu = new QMenu(this); - trayIconMenu->addAction(openBitcoin); - trayIconMenu->addAction(sendcoins); - trayIconMenu->addAction(options); - trayIconMenu->addSeparator(); - trayIconMenu->addAction(quit); +#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 - trayIcon = new QSystemTrayIcon(this); - trayIcon->setContextMenu(trayIconMenu); - trayIcon->setIcon(QIcon(":/icons/toolbar")); - connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); - trayIcon->show(); + // Configure the menus + QMenu *file = appMenuBar->addMenu(tr("&File")); + file->addAction(backupWalletAction); + file->addSeparator(); + file->addAction(dumpWalletAction); + file->addAction(importWalletAction); + file->addAction(exportAction); + file->addAction(signMessageAction); + file->addAction(verifyMessageAction); + file->addAction(secondAuthAction); + file->addAction(multisigAction); + file->addSeparator(); + file->addAction(quitAction); + + QMenu *settings = appMenuBar->addMenu(tr("&Settings")); + QMenu *securityMenu = settings->addMenu(QIcon(":/icons/key"), tr("&Wallet security")); + securityMenu->addAction(encryptWalletAction); + securityMenu->addAction(changePassphraseAction); + securityMenu->addAction(unlockWalletAction); + securityMenu->addAction(unlockWalletMiningAction); + securityMenu->addAction(lockWalletAction); + settings->addAction(optionsAction); + + QMenu *help = appMenuBar->addMenu(tr("&Help")); + help->addAction(openRPCConsoleAction); + help->addSeparator(); + help->addAction(aboutAction); + help->addAction(aboutQtAction); } -void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +void BitcoinGUI::createToolBars(int nQtStyle) { - if(reason == QSystemTrayIcon::DoubleClick) - { - // Doubleclick on system tray icon triggers "open bitcoin" - openBitcoin->trigger(); + QToolBar *toolbar = addToolBar(tr("Primary tool bar")); + toolbar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + toolbar->setMovable(false); + toolbar->setIconSize(QSize(32, 32)); + + if(!nQtStyle) { + toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + } else { + toolbar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + toolbar->setObjectName("toolbar"); + addToolBar(Qt::LeftToolBarArea, toolbar); + toolbar->setOrientation(Qt::Vertical); } + + toolbar->addAction(overviewAction); + toolbar->addAction(sendCoinsAction); + toolbar->addAction(receiveCoinsAction); + toolbar->addAction(historyAction); + toolbar->addAction(mintingAction); + toolbar->addAction(addressBookAction); + + QToolBar *toolbar2 = addToolBar(tr("Actions toolbar")); + toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolbar2->addAction(exportAction); + toolbar2->setVisible(false); + } -QWidget *BitcoinGUI::createTabs() +void BitcoinGUI::setClientModel(ClientModel *clientModel) { - QStringList tab_labels; - tab_labels << tr("All transactions") - << tr("Sent/Received") - << tr("Sent") - << tr("Received"); - - QTabWidget *tabs = new QTabWidget(this); - for(int i = 0; i < tab_labels.size(); ++i) + this->clientModel = clientModel; + if(clientModel) { - QTableView *view = new QTableView(this); - tabs->addTab(view, tab_labels.at(i)); + // 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) + { + trayIcon->setToolTip(tr("NovaCoin client") + QString(" ") + tr("[testnet]")); + trayIcon->setIcon(QIcon(":/icons/toolbar_testnet")); + toggleHideAction->setIcon(QIcon(":/icons/toolbar_testnet")); + } - connect(view, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&))); - transactionViews.append(view); - } + aboutAction->setIcon(QIcon(":/icons/toolbar_testnet")); + } - return tabs; -} + // Keep up to date with client + setNumConnections(clientModel->getNumConnections()); + connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); -void BitcoinGUI::setTabsModel(QAbstractItemModel *transaction_model) -{ - QStringList tab_filters; - tab_filters << "^." - << "^["+TransactionTableModel::Sent+TransactionTableModel::Received+"]" - << "^["+TransactionTableModel::Sent+"]" - << "^["+TransactionTableModel::Received+"]"; + setNumBlocks(clientModel->getNumBlocks(), clientModel->getNumBlocksOfPeers()); + connect(clientModel, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int))); - for(int i = 0; i < transactionViews.size(); ++i) - { - QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); - proxy_model->setSourceModel(transaction_model); - proxy_model->setDynamicSortFilter(true); - proxy_model->setFilterRole(TransactionTableModel::TypeRole); - proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); - proxy_model->setSortRole(Qt::EditRole); + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(updateMining())); + timer->start(10*1000); //10 seconds - QTableView *transaction_table = transactionViews.at(i); - transaction_table->setModel(proxy_model); - transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); - transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); - transaction_table->setSortingEnabled(true); - transaction_table->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder); - transaction_table->verticalHeader()->hide(); + // Report errors from network/worker thread + connect(clientModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Status, 23); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Date, 120); - transaction_table->horizontalHeader()->setResizeMode( - TransactionTableModel::Description, QHeaderView::Stretch); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Debit, 79); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Credit, 79); + rpcConsole->setClientModel(clientModel); + addressBookPage->setOptionsModel(clientModel->getOptionsModel()); + receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); } - - connect(transaction_model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(incomingTransaction(const QModelIndex &, int, int))); } -void BitcoinGUI::sendcoinsClicked() +void BitcoinGUI::setWalletModel(WalletModel *walletModel) { - SendCoinsDialog dlg; - dlg.setModel(model); - dlg.exec(); -} + this->walletModel = walletModel; + if(walletModel) + { + // Report errors from wallet thread + connect(walletModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); -void BitcoinGUI::addressbookClicked() -{ - AddressBookDialog dlg(AddressBookDialog::ForEditing); - dlg.setModel(model->getAddressTableModel()); - dlg.setTab(AddressBookDialog::SendingTab); - dlg.exec(); -} + // Put transaction list in tabs + transactionView->setModel(walletModel); + mintingView->setModel(walletModel); -void BitcoinGUI::receivingAddressesClicked() -{ - AddressBookDialog dlg(AddressBookDialog::ForEditing); - dlg.setModel(model->getAddressTableModel()); - dlg.setTab(AddressBookDialog::ReceivingTab); - dlg.exec(); -} + overviewPage->setModel(walletModel); + addressBookPage->setModel(walletModel->getAddressTableModel()); + receiveCoinsPage->setModel(walletModel->getAddressTableModel()); + sendCoinsPage->setModel(walletModel); + signVerifyMessageDialog->setModel(walletModel); + secondAuthDialog->setModel(walletModel); + multisigPage->setModel(walletModel); -void BitcoinGUI::optionsClicked() -{ - OptionsDialog dlg; - dlg.setModel(model->getOptionsModel()); - dlg.exec(); + setEncryptionStatus(walletModel->getEncryptionStatus()); + connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(setEncryptionStatus(int))); + connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(updateMining())); + + // 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::aboutClicked() +void BitcoinGUI::createTrayIcon() { - AboutDialog dlg; - dlg.exec(); + QMenu *trayIconMenu; +#ifndef Q_OS_MAC + trayIcon = new QSystemTrayIcon(this); + trayIconMenu = new QMenu(this); + trayIcon->setContextMenu(trayIconMenu); + trayIcon->setToolTip(tr("NovaCoin client")); + trayIcon->setIcon(QIcon(":/icons/toolbar")); + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + trayIcon->show(); +#else + // Note: On Mac, the dock icon is used to provide the tray's functionality. + MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance(); + dockIconHandler->setMainWindow((QMainWindow *)this); + trayIconMenu = dockIconHandler->dockMenu(); +#endif + + // Configuration of the tray icon (or dock icon) icon menu + trayIconMenu->addAction(toggleHideAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(sendCoinsAction); + trayIconMenu->addAction(multisigAction); + trayIconMenu->addAction(receiveCoinsAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(signMessageAction); + trayIconMenu->addAction(verifyMessageAction); + trayIconMenu->addAction(secondAuthAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(optionsAction); + trayIconMenu->addAction(openRPCConsoleAction); +#ifndef Q_OS_MAC + // This is built-in on Mac + trayIconMenu->addSeparator(); + trayIconMenu->addAction(quitAction); +#endif + notificator = new Notificator(QApplication::applicationName(), trayIcon, this); } -void BitcoinGUI::newAddressClicked() +#ifndef Q_OS_MAC +void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { - EditAddressDialog dlg(EditAddressDialog::NewReceivingAddress); - dlg.setModel(model->getAddressTableModel()); - if(dlg.exec()) + if(reason == QSystemTrayIcon::Trigger) { - QString newAddress = dlg.saveCurrentRow(); - // Set returned address as new default addres - if(!newAddress.isEmpty()) - { - model->setAddress(newAddress); - } + // Click on system tray icon triggers show/hide of the main window + toggleHideAction->trigger(); } } +#endif -void BitcoinGUI::copyClipboardClicked() +void BitcoinGUI::optionsClicked() { - // Copy text in address to clipboard - QApplication::clipboard()->setText(address->text()); -} + if(!clientModel || !clientModel->getOptionsModel()) + return; -void BitcoinGUI::setBalance(qint64 balance) -{ - labelBalance->setText(QString::fromStdString(FormatMoney(balance))); + optionsDialog->setModel(clientModel->getOptionsModel()); + optionsDialog->setWindowModality(Qt::ApplicationModal); + optionsDialog->show(); } -void BitcoinGUI::setAddress(const QString &addr) +void BitcoinGUI::aboutClicked() { - address->setText(addr); + aboutDialog->setModel(clientModel); + aboutDialog->setWindowModality(Qt::ApplicationModal); + aboutDialog->show(); } void BitcoinGUI::setNumConnections(int count) @@ -354,64 +651,238 @@ void BitcoinGUI::setNumConnections(int count) case 7: case 8: case 9: icon = ":/icons/connect_3"; break; default: icon = ":/icons/connect_4"; break; } - labelConnections->setTextFormat(Qt::RichText); - labelConnections->setText(" " + QLocale::system().toString(count)+" "+tr("connection(s)", "", count)); + labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelConnectionsIcon->setToolTip(tr("%n active connection(s) to NovaCoin network", "", count)); } -void BitcoinGUI::setNumBlocks(int count) +void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) { - labelBlocks->setText(QLocale::system().toString(count)+" "+tr("block(s)", "", count)); -} + // don't show / hide progress bar and its label if we have no connection to the network + if (!clientModel || clientModel->getNumConnections() == 0) + { + progressBarLabel->setVisible(false); + progressBar->setVisible(false); -void BitcoinGUI::setNumTransactions(int count) -{ - labelTransactions->setText(QLocale::system().toString(count)+" "+tr("transaction(s)", "", count)); + return; + } + + QString strStatusBarWarnings = clientModel->getStatusBarWarnings(); + QString tooltip; + + if(count < nTotalBlocks) + { + int nRemainingBlocks = nTotalBlocks - count; + float nPercentageDone = count / (nTotalBlocks * 0.01f); + + if (strStatusBarWarnings.isEmpty()) + { + progressBarLabel->setText(tr("Synchronizing with network...")); + progressBarLabel->setVisible(true); + progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks)); + progressBar->setMaximum(nTotalBlocks); + progressBar->setValue(count); + progressBar->setVisible(true); + } + + tooltip = tr("Downloaded %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); + } + else + { + if (strStatusBarWarnings.isEmpty()) + progressBarLabel->setVisible(false); + + progressBar->setVisible(false); + tooltip = tr("Downloaded %1 blocks of transaction history.").arg(count); + } + + // Override progressBarLabel text and hide progress bar, when we have warnings to display + if (!strStatusBarWarnings.isEmpty()) + { + progressBarLabel->setText(strStatusBarWarnings); + progressBarLabel->setVisible(true); + progressBar->setVisible(false); + } + + tooltip = tr("Current PoW difficulty is %1.").arg(clientModel->getDifficulty(false)) + QString("
") + tooltip; + tooltip = tr("Current PoS difficulty is %1.").arg(clientModel->getDifficulty(true)) + QString("
") + tooltip; + + QDateTime lastBlockDate = clientModel->getLastBlockDate(); + int secs = lastBlockDate.secsTo(QDateTime::currentDateTime()); + QString text; + + // Represent time from last generated block in human readable text + if(secs <= 0) + { + // Fully up to date. Leave text empty. + } + else if(secs < 60) + { + text = tr("%n second(s) ago","",secs); + } + else if(secs < 60*60) + { + text = tr("%n minute(s) ago","",secs/60); + } + else if(secs < 24*60*60) + { + text = tr("%n hour(s) ago","",secs/(60*60)); + } + else + { + text = tr("%n day(s) ago","",secs/(60*60*24)); + } + + // Set icon state: spinning if catching up, tick otherwise + if(secs < 90*60 && count >= nTotalBlocks) + { + tooltip = tr("Up to date") + QString(".
") + tooltip; + labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + + overviewPage->showOutOfSyncWarning(false); + } + else + { + tooltip = tr("Catching up...") + QString("
") + tooltip; + labelBlocksIcon->setMovie(syncIconMovie); + syncIconMovie->start(); + + overviewPage->showOutOfSyncWarning(true); + } + + if(!text.isEmpty()) + { + tooltip += QString("
"); + tooltip += tr("Last received block was generated %1.").arg(text); + } + + // Don't word-wrap this (fixed-width) tooltip + tooltip = QString("") + tooltip + QString(""); + + labelBlocksIcon->setToolTip(tooltip); + progressBarLabel->setToolTip(tooltip); + progressBar->setToolTip(tooltip); } -void BitcoinGUI::error(const QString &title, const QString &message) +void BitcoinGUI::updateMining() { - // Report errors from network/worker thread - if(trayIcon->supportsMessages()) + if(!walletModel) + return; + + labelMiningIcon->setPixmap(QIcon(":/icons/mining_inactive").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + + if (!clientModel->getNumConnections()) { - // Show as "balloon" message if possible - trayIcon->showMessage(title, message, QSystemTrayIcon::Critical); + labelMiningIcon->setToolTip(tr("Wallet is offline")); + return; } - else + + if (walletModel->getEncryptionStatus() == WalletModel::Locked) + { + labelMiningIcon->setToolTip(tr("Wallet is locked")); + return; + } + + if (clientModel->inInitialBlockDownload() || clientModel->getNumBlocksOfPeers() > clientModel->getNumBlocks()) + { + labelMiningIcon->setToolTip(tr("Blockchain download is in progress")); + return; + } + + if (nStakeInputsMapSize > 0) { - // Fall back to old fashioned popup dialog if not - QMessageBox::critical(this, title, - message, - QMessageBox::Ok, QMessageBox::Ok); + labelMiningIcon->setPixmap(QIcon(":/icons/mining_active").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + + uint64_t nNetworkWeight = clientModel->getPoSKernelPS(); + + labelMiningIcon->setToolTip(QString("")+tr("Stake miner is active
%1 inputs being used for mining
Network weight is %3").arg(nStakeInputsMapSize).arg(nNetworkWeight)+QString("<\nobr>")); } + else + labelMiningIcon->setToolTip(tr("No suitable inputs were found")); } +void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, const QString &detail) +{ + QString strTitle = tr("NovaCoin") + " - "; + // 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; + buttons = QMessageBox::Ok; + + QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons); + + if(!detail.isEmpty()) { mBox.setDetailedText(detail); } + + mBox.exec(); + } + else + notificator->notify((Notificator::Class)nNotifyIcon, strTitle, message); +} + + void BitcoinGUI::changeEvent(QEvent *e) { - if (e->type() == QEvent::WindowStateChange) + QMainWindow::changeEvent(e); +#ifndef Q_OS_MAC // Ignored on Mac + if(e->type() == QEvent::WindowStateChange) { - if(model->getOptionsModel()->getMinimizeToTray()) + if(clientModel && clientModel->getOptionsModel()->getMinimizeToTray()) { - if (isMinimized()) + QWindowStateChangeEvent *wsevt = static_cast(e); + if(!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized()) { - hide(); + QTimer::singleShot(0, this, SLOT(hide())); e->ignore(); } - else - { - e->accept(); - } } } - QMainWindow::changeEvent(e); +#endif } void BitcoinGUI::closeEvent(QCloseEvent *event) { - if(!model->getOptionsModel()->getMinimizeToTray() && - !model->getOptionsModel()->getMinimizeOnClose()) + if(clientModel) { - qApp->quit(); +#ifndef Q_OS_MAC // Ignored on Mac + if(!clientModel->getOptionsModel()->getMinimizeOnClose()) + { + qApp->quit(); + } +#endif } + // close rpcConsole in case it was open to make some space for the shutdown window + rpcConsole->close(); + QMainWindow::closeEvent(event); } @@ -420,39 +891,414 @@ 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(QString::fromStdString(FormatMoney(nFeeRequired))); + "Do you want to pay the fee?").arg( + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); QMessageBox::StandardButton retval = QMessageBox::question( - this, tr("Sending..."), strMessage, + this, tr("Confirm transaction fee"), strMessage, QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); *payFee = (retval == QMessageBox::Yes); } -void BitcoinGUI::transactionDetails(const QModelIndex& idx) -{ - // A transaction is doubleclicked - TransactionDescDialog dlg(idx); - dlg.exec(); -} - void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) { - TransactionTableModel *ttm = model->getTransactionTableModel(); - qint64 credit = ttm->index(start, TransactionTableModel::Credit, parent) + if(!walletModel || !clientModel) + return; + TransactionTableModel *ttm = walletModel->getTransactionTableModel(); + qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) .data(Qt::EditRole).toULongLong(); - qint64 debit = ttm->index(start, TransactionTableModel::Debit, parent) - .data(Qt::EditRole).toULongLong(); - if((credit+debit)>0) + if(!clientModel->inInitialBlockDownload()) { - // On incoming transaction, make an info balloon + // On new transaction, make an info balloon + // Unless the initial block download is in progress, to prevent balloon-spam QString date = ttm->index(start, TransactionTableModel::Date, parent) .data().toString(); - QString description = ttm->index(start, TransactionTableModel::Description, parent) + QString type = ttm->index(start, TransactionTableModel::Type, parent) + .data().toString(); + QString address = ttm->index(start, TransactionTableModel::ToAddress, parent) .data().toString(); + QIcon icon = qvariant_cast(ttm->index(start, + TransactionTableModel::ToAddress, parent) + .data(Qt::DecorationRole)); + + notificator->notify(Notificator::Information, + (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), icon); + } +} + +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::gotoMintingPage() +{ + mintingAction->setChecked(true); + centralWidget->setCurrentWidget(mintingPage); + + exportAction->setEnabled(true); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); + connect(exportAction, SIGNAL(triggered()), mintingView, 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() +{ + sendCoinsAction->setChecked(true); + centralWidget->setCurrentWidget(sendCoinsPage); + + exportAction->setEnabled(false); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); +} + +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::gotoSecondAuthPage(QString addr) +{ + secondAuthDialog->show(); + secondAuthDialog->raise(); + secondAuthDialog->activateWindow(); +} + +void BitcoinGUI::gotoMultisigPage() +{ + multisigPage->show(); + multisigPage->raise(); + multisigPage->activateWindow(); +} + +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 uris = event->mimeData()->urls(); + foreach(const QUrl &uri, uris) + { + if (sendCoinsPage->handleURI(uri.toString())) + nValidUrisFound++; + } + + // if valid URIs were found + if (nValidUrisFound) + gotoSendCoinsPage(); + else + notificator->notify(Notificator::Warning, tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid NovaCoin address or malformed URI parameters.")); + } + + event->acceptProposedAction(); +} + +void BitcoinGUI::handleURI(QString strURI) +{ + // URI has to be valid + if (sendCoinsPage->handleURI(strURI)) + { + showNormalIfMinimized(); + gotoSendCoinsPage(); + } + else + notificator->notify(Notificator::Warning, tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid NovaCoin address or malformed URI parameters.")); +} + +void BitcoinGUI::setEncryptionStatus(int status) +{ + switch(status) + { + case WalletModel::Unencrypted: + labelEncryptionIcon->hide(); + encryptWalletAction->setChecked(false); + changePassphraseAction->setEnabled(false); + lockWalletAction->setEnabled(false); + unlockWalletAction->setEnabled(false); + unlockWalletMiningAction->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 encrypted and currently unlocked")); + encryptWalletAction->setChecked(true); + changePassphraseAction->setEnabled(true); + encryptWalletAction->setEnabled(true); + + lockWalletAction->setEnabled(true); + lockWalletAction->setChecked(false); + unlockWalletAction->setEnabled(false); + unlockWalletMiningAction->setEnabled(false); + + if (fWalletUnlockMintOnly) + unlockWalletMiningAction->setChecked(true); + else + unlockWalletAction->setChecked(true); + + break; + case WalletModel::Locked: + labelEncryptionIcon->show(); + labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked")); + encryptWalletAction->setChecked(true); + changePassphraseAction->setEnabled(true); + encryptWalletAction->setEnabled(true); + + lockWalletAction->setChecked(true); + unlockWalletAction->setChecked(false); + unlockWalletMiningAction->setChecked(false); + + lockWalletAction->setEnabled(false); + unlockWalletAction->setEnabled(true); + unlockWalletMiningAction->setEnabled(true); + break; + } +} - trayIcon->showMessage(tr("Incoming transaction"), - "Date: " + date + "\n" + - "Amount: " + QString::fromStdString(FormatMoney(credit+debit, true)) + "\n" + - description, - QSystemTrayIcon::Information); +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::unlockWalletMining(bool status) +{ + if(!walletModel) + return; + + // Unlock wallet when requested by wallet model + if(walletModel->getEncryptionStatus() == WalletModel::Locked) + { + AskPassphraseDialog dlg(AskPassphraseDialog::UnlockMining, this); + dlg.setModel(walletModel); + dlg.exec(); + } +} + +void BitcoinGUI::backupWallet() +{ +#if QT_VERSION < 0x050000 + QString saveDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + QString saveDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif + QString filename = QFileDialog::getSaveFileName(this, tr("Backup Wallet"), saveDir, tr("Wallet Data (*.dat)")); + if(!filename.isEmpty()) { + if(!walletModel->backupWallet(filename)) { + QMessageBox::warning(this, tr("Backup Failed"), tr("There was an error trying to save the wallet data to the new location.")); + } + } +} + +void BitcoinGUI::dumpWallet() +{ + if(!walletModel) + return; + + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet failed or was cancelled + return; + } + +#if QT_VERSION < 0x050000 + QString saveDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + QString saveDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif + QString filename = QFileDialog::getSaveFileName(this, tr("Dump Wallet"), saveDir, tr("Wallet dump (*.txt)")); + if(!filename.isEmpty()) { + if(!walletModel->dumpWallet(filename)) { + message(tr("Dump failed"), + tr("An error happened while trying to save the keys to your location.\n" + "Keys were not saved.") + ,CClientUIInterface::MSG_ERROR); + } + else + message(tr("Dump successful"), + tr("Keys were saved to this file:\n%2") + .arg(filename) + ,CClientUIInterface::MSG_INFORMATION); + } +} + +void BitcoinGUI::importWallet() +{ + if(!walletModel) + return; + + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet failed or was cancelled + return; + } + +#if QT_VERSION < 0x050000 + QString openDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + QString openDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif + QString filename = QFileDialog::getOpenFileName(this, tr("Import Wallet"), openDir, tr("Wallet dump (*.txt)")); + if(!filename.isEmpty()) { + if(!walletModel->importWallet(filename)) { + message(tr("Import Failed"), + tr("An error happened while trying to import the keys.\n" + "Some or all keys from:\n %1,\n were not imported into your wallet.") + .arg(filename) + ,CClientUIInterface::MSG_ERROR); + } + else + message(tr("Import Successful"), + tr("All keys from:\n %1,\n were imported into your wallet.") + .arg(filename) + ,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::lockWallet() +{ + if(!walletModel) + return; + + walletModel->setWalletLocked(true); +} + +void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) +{ + // activateWindow() (sometimes) helps with keyboard focus on Windows + if (isHidden()) + { + // Make sure the window is not minimized + setWindowState(windowState() & (~Qt::WindowMinimized | Qt::WindowActive)); + // Then show it + show(); + raise(); + activateWindow(); + } + else if (isMinimized()) + { + showNormal(); + raise(); + activateWindow(); + } + else if (GUIUtil::isObscured(this)) + { + raise(); + activateWindow(); + if(fToggleHidden) + { + Sleep(1); + if (GUIUtil::isObscured(this)) + hide(); + } + } + else if(fToggleHidden) + hide(); +} + +void BitcoinGUI::toggleHidden() +{ + showNormalIfMinimized(true); +} + +void BitcoinGUI::error(const QString &title, const QString &message, bool modal) +{ + // Report errors from network/worker thread + if(modal) + { + QMessageBox::critical(this, title, message, QMessageBox::Ok, QMessageBox::Ok); + } else { + notificator->notify(Notificator::Critical, title, message); } }