diff --git a/src/interface/node.cpp b/src/interface/node.cpp index eee5b934c3..9b4cbc51a6 100644 --- a/src/interface/node.cpp +++ b/src/interface/node.cpp @@ -1,85 +1,92 @@ // Copyright (c) 2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include class HTTPRPCRequestProcessor; namespace interface { namespace { class NodeImpl : public Node { void parseParameters(int argc, const char *const argv[]) override { gArgs.ParseParameters(argc, argv); } void readConfigFile(const std::string &conf_path) override { gArgs.ReadConfigFile(conf_path); } bool softSetArg(const std::string &arg, const std::string &value) override { return gArgs.SoftSetArg(arg, value); } bool softSetBoolArg(const std::string &arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); } void selectParams(const std::string &network) override { SelectParams(network); } void initLogging() override { InitLogging(); } void initParameterInteraction() override { InitParameterInteraction(); } std::string getWarnings(const std::string &type) override { return GetWarnings(type); } bool baseInitialize(Config &config, RPCServer &rpcServer) override { return AppInitBasicSetup() && AppInitParameterInteraction(config, rpcServer) && AppInitSanityChecks() && AppInitLockDataDirectory(); } bool appInitMain(Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor) override { return AppInitMain(config, httpRPCRequestProcessor); } void appShutdown() override { Interrupt(); Shutdown(); } void startShutdown() override { StartShutdown(); } + bool shutdownRequested() override { return ShutdownRequested(); } void mapPort(bool use_upnp) override { if (use_upnp) { StartMapPort(); } else { InterruptMapPort(); StopMapPort(); } } bool getProxy(Network net, proxyType &proxy_info) override { return GetProxy(net, proxy_info); } std::unique_ptr handleInitMessage(InitMessageFn fn) override { return MakeHandler(::uiInterface.InitMessage.connect(fn)); } + std::unique_ptr handleMessageBox(MessageBoxFn fn) override { + return MakeHandler(::uiInterface.ThreadSafeMessageBox.connect(fn)); + } + std::unique_ptr handleQuestion(QuestionFn fn) override { + return MakeHandler(::uiInterface.ThreadSafeQuestion.connect(fn)); + } }; } // namespace std::unique_ptr MakeNode() { return std::make_unique(); } } // namespace interface diff --git a/src/interface/node.h b/src/interface/node.h index 2f8f4051d2..9b4c10f8b7 100644 --- a/src/interface/node.h +++ b/src/interface/node.h @@ -1,83 +1,98 @@ // Copyright (c) 2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_INTERFACE_NODE_H #define BITCOIN_INTERFACE_NODE_H #include // For Network #include #include #include class Config; class HTTPRPCRequestProcessor; class proxyType; class RPCServer; namespace interface { class Handler; //! Top-level interface for a bitcoin node (bitcoind process). class Node { public: virtual ~Node() {} //! Set command line arguments. virtual void parseParameters(int argc, const char *const argv[]) = 0; //! Set a command line argument if it doesn't already have a value virtual bool softSetArg(const std::string &arg, const std::string &value) = 0; //! Set a command line boolean argument if it doesn't already have a value virtual bool softSetBoolArg(const std::string &arg, bool value) = 0; //! Load settings from configuration file. virtual void readConfigFile(const std::string &conf_path) = 0; //! Choose network parameters. virtual void selectParams(const std::string &network) = 0; //! Init logging. virtual void initLogging() = 0; //! Init parameter interaction. virtual void initParameterInteraction() = 0; //! Get warnings. virtual std::string getWarnings(const std::string &type) = 0; //! Initialize app dependencies. virtual bool baseInitialize(Config &config, RPCServer &rpcServer) = 0; //! Start node. virtual bool appInitMain(Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor) = 0; //! Stop node. virtual void appShutdown() = 0; //! Start shutdown. virtual void startShutdown() = 0; + //! Return whether shutdown was requested. + virtual bool shutdownRequested() = 0; + //! Map port. virtual void mapPort(bool use_upnp) = 0; //! Get proxy. virtual bool getProxy(Network net, proxyType &proxy_info) = 0; //! Register handler for init messages. using InitMessageFn = std::function; virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0; + + //! Register handler for message box messages. + using MessageBoxFn = + std::function; + virtual std::unique_ptr handleMessageBox(MessageBoxFn fn) = 0; + + //! Register handler for question messages. + using QuestionFn = std::function; + virtual std::unique_ptr handleQuestion(QuestionFn fn) = 0; }; //! Return implementation of Node interface. std::unique_ptr MakeNode(); } // namespace interface #endif // BITCOIN_INTERFACE_NODE_H diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 71629f0991..0bdfc213cb 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -1,799 +1,799 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "bitcoingui.h" #include "chainparams.h" #include "clientmodel.h" #include "config.h" #include "guiconstants.h" #include "guiutil.h" #include "httprpc.h" #include "intro.h" #include "networkstyle.h" #include "optionsmodel.h" #include "platformstyle.h" #include "splashscreen.h" #include "utilitydialog.h" #include "winshutdownmonitor.h" #ifdef ENABLE_WALLET #include "paymentserver.h" #include "walletmodel.h" #endif #include "init.h" #include "interface/handler.h" #include "interface/node.h" #include "rpc/server.h" #include "ui_interface.h" #include "uint256.h" #include "util.h" #include "warnings.h" #ifdef ENABLE_WALLET #include "wallet/wallet.h" #endif #include "walletinitinterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(QT_STATICPLUGIN) #include #if QT_VERSION < 0x050400 Q_IMPORT_PLUGIN(AccessibleFactory) #endif #if defined(QT_QPA_PLATFORM_XCB) Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_WINDOWS) Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_COCOA) Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); #endif #endif // Declare meta types used for QMetaObject::invokeMethod Q_DECLARE_METATYPE(bool *) Q_DECLARE_METATYPE(Amount) Q_DECLARE_METATYPE(uint256) // Config is non-copyable so we can only register pointers to it Q_DECLARE_METATYPE(Config *) static void InitMessage(const std::string &message) { LogPrintf("init message: %s\n", message); } /** * Translate string to current locale using Qt. */ static std::string Translate(const char *psz) { return QCoreApplication::translate("bitcoin-abc", psz).toStdString(); } static QString GetLangTerritory() { QSettings settings; // Get desired locale (e.g. "de_DE") // 1) System default language QString lang_territory = QLocale::system().name(); // 2) Language from QSettings QString lang_territory_qsettings = settings.value("language", "").toString(); if (!lang_territory_qsettings.isEmpty()) { lang_territory = lang_territory_qsettings; } // 3) -lang command line argument lang_territory = QString::fromStdString( gArgs.GetArg("-lang", lang_territory.toStdString())); return lang_territory; } /** Set up translations */ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator) { // Remove old translators QApplication::removeTranslator(&qtTranslatorBase); QApplication::removeTranslator(&qtTranslator); QApplication::removeTranslator(&translatorBase); QApplication::removeTranslator(&translator); // Get desired locale (e.g. "de_DE") // 1) System default language QString lang_territory = GetLangTerritory(); // Convert to "de" only by truncating "_DE" QString lang = lang_territory; lang.truncate(lang_territory.lastIndexOf('_')); // Load language files for configured locale: // - First load the translator for the base language, without territory // - Then load the more specific locale translator // Load e.g. qt_de.qm if (qtTranslatorBase.load( "qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { QApplication::installTranslator(&qtTranslatorBase); } // Load e.g. qt_de_DE.qm if (qtTranslator.load( "qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { QApplication::installTranslator(&qtTranslator); } // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in // bitcoin.qrc) if (translatorBase.load(lang, ":/translations/")) { QApplication::installTranslator(&translatorBase); } // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in // bitcoin.qrc) if (translator.load(lang_territory, ":/translations/")) { QApplication::installTranslator(&translator); } } /* qDebug() message handler --> debug.log */ void DebugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { Q_UNUSED(context); if (type == QtDebugMsg) { LogPrint(BCLog::QT, "GUI: %s\n", msg.toStdString()); } else { LogPrintf("GUI: %s\n", msg.toStdString()); } } /** * Class encapsulating Bitcoin ABC startup and shutdown. * Allows running startup and shutdown in a different thread from the UI thread. */ class BitcoinABC : public QObject { Q_OBJECT public: explicit BitcoinABC(interface::Node &node); public Q_SLOTS: void initialize(Config *config, HTTPRPCRequestProcessor *httpRPCRequestProcessor); void shutdown(); Q_SIGNALS: void initializeResult(bool success); void shutdownResult(); void runawayException(const QString &message); private: /// Pass fatal exception message to UI thread void handleRunawayException(const std::exception *e); interface::Node &m_node; }; /** Main Bitcoin application object */ class BitcoinApplication : public QApplication { Q_OBJECT public: explicit BitcoinApplication(interface::Node &node, int &argc, char **argv); ~BitcoinApplication(); #ifdef ENABLE_WALLET /// Create payment server void createPaymentServer(); #endif /// parameter interaction/setup based on rules void parameterSetup(); /// Create options model void createOptionsModel(bool resetSettings); /// Create main window void createWindow(const Config *, const NetworkStyle *networkStyle); /// Create splash screen void createSplashScreen(const NetworkStyle *networkStyle); /// Request core initialization void requestInitialize(Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor); /// Request core shutdown void requestShutdown(Config &config); /// Get process return value int getReturnValue() const { return returnValue; } /// Get window identifier of QMainWindow (BitcoinGUI) WId getMainWinId() const; public Q_SLOTS: void initializeResult(bool success); void shutdownResult(); /// Handle runaway exceptions. Shows a message box with the problem and /// quits the program. void handleRunawayException(const QString &message); Q_SIGNALS: void requestedInitialize(Config *config, HTTPRPCRequestProcessor *httpRPCRequestProcessor); void requestedShutdown(); void stopThread(); void splashFinished(QWidget *window); private: QThread *coreThread; interface::Node &m_node; OptionsModel *optionsModel; ClientModel *clientModel; BitcoinGUI *window; QTimer *pollShutdownTimer; #ifdef ENABLE_WALLET PaymentServer *paymentServer; std::vector m_wallet_models; #endif int returnValue; const PlatformStyle *platformStyle; std::unique_ptr shutdownWindow; void startThread(); }; #include "bitcoin.moc" BitcoinABC::BitcoinABC(interface::Node &node) : QObject(), m_node(node) {} void BitcoinABC::handleRunawayException(const std::exception *e) { PrintExceptionContinue(e, "Runaway exception"); Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings("gui"))); } void BitcoinABC::initialize(Config *cfg, HTTPRPCRequestProcessor *httpRPCRequestProcessor) { Config &config(*cfg); try { qDebug() << __func__ << ": Running initialization in thread"; bool rv = m_node.appInitMain(config, *httpRPCRequestProcessor); Q_EMIT initializeResult(rv); } catch (const std::exception &e) { handleRunawayException(&e); } catch (...) { handleRunawayException(nullptr); } } void BitcoinABC::shutdown() { try { qDebug() << __func__ << ": Running Shutdown in thread"; m_node.appShutdown(); qDebug() << __func__ << ": Shutdown finished"; Q_EMIT shutdownResult(); } catch (const std::exception &e) { handleRunawayException(&e); } catch (...) { handleRunawayException(nullptr); } } BitcoinApplication::BitcoinApplication(interface::Node &node, int &argc, char **argv) : QApplication(argc, argv), coreThread(0), m_node(node), optionsModel(0), clientModel(0), window(0), pollShutdownTimer(0), #ifdef ENABLE_WALLET paymentServer(0), m_wallet_models(), #endif returnValue(0) { setQuitOnLastWindowClosed(false); // UI per-platform customization. // This must be done inside the BitcoinApplication constructor, or after it, // because PlatformStyle::instantiate requires a QApplication. std::string platformName; platformName = gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM); platformStyle = PlatformStyle::instantiate(QString::fromStdString(platformName)); // Fall back to "other" if specified name not found. if (!platformStyle) { platformStyle = PlatformStyle::instantiate("other"); } assert(platformStyle); } BitcoinApplication::~BitcoinApplication() { if (coreThread) { qDebug() << __func__ << ": Stopping thread"; Q_EMIT stopThread(); coreThread->wait(); qDebug() << __func__ << ": Stopped thread"; } delete window; window = 0; #ifdef ENABLE_WALLET delete paymentServer; paymentServer = 0; #endif delete optionsModel; optionsModel = 0; delete platformStyle; platformStyle = 0; } #ifdef ENABLE_WALLET void BitcoinApplication::createPaymentServer() { paymentServer = new PaymentServer(this); } #endif void BitcoinApplication::createOptionsModel(bool resetSettings) { optionsModel = new OptionsModel(m_node, nullptr, resetSettings); } void BitcoinApplication::createWindow(const Config *config, const NetworkStyle *networkStyle) { - window = new BitcoinGUI(config, platformStyle, networkStyle, 0); + window = new BitcoinGUI(m_node, config, platformStyle, networkStyle, 0); pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); } void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) { SplashScreen *splash = new SplashScreen(0, networkStyle); // We don't hold a direct pointer to the splash screen after creation, but // the splash screen will take care of deleting itself when slotFinish // happens. splash->show(); connect(this, SIGNAL(splashFinished(QWidget *)), splash, SLOT(slotFinish(QWidget *))); connect(this, SIGNAL(requestedShutdown()), splash, SLOT(close())); } void BitcoinApplication::startThread() { if (coreThread) { return; } coreThread = new QThread(this); BitcoinABC *executor = new BitcoinABC(m_node); executor->moveToThread(coreThread); /* communication to and from thread */ connect(executor, SIGNAL(initializeResult(bool)), this, SLOT(initializeResult(bool))); connect(executor, SIGNAL(shutdownResult()), this, SLOT(shutdownResult())); connect(executor, SIGNAL(runawayException(QString)), this, SLOT(handleRunawayException(QString))); // Note on how Qt works: it tries to directly invoke methods if the signal // is emitted on the same thread that the target object 'lives' on. // But if the target object 'lives' on another thread (executor here does) // the SLOT will be invoked asynchronously at a later time in the thread // of the target object. So.. we pass a pointer around. If you pass // a reference around (even if it's non-const) you'll get Qt generating // code to copy-construct the parameter in question (Q_DECLARE_METATYPE // and qRegisterMetaType generate this code). For the Config class, // which is noncopyable, we can't do this. So.. we have to pass // pointers to Config around. Make sure Config &/Config * isn't a // temporary (eg it lives somewhere aside from the stack) or this will // crash because initialize() gets executed in another thread at some // unspecified time (after) requestedInitialize() is emitted! connect(this, SIGNAL(requestedInitialize(Config *, HTTPRPCRequestProcessor *)), executor, SLOT(initialize(Config *, HTTPRPCRequestProcessor *))); connect(this, SIGNAL(requestedShutdown()), executor, SLOT(shutdown())); /* make sure executor object is deleted in its own thread */ connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater())); connect(this, SIGNAL(stopThread()), coreThread, SLOT(quit())); coreThread->start(); } void BitcoinApplication::parameterSetup() { m_node.initLogging(); m_node.initParameterInteraction(); } void BitcoinApplication::requestInitialize( Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor) { qDebug() << __func__ << ": Requesting initialize"; startThread(); // IMPORTANT: config must NOT be a reference to a temporary because below // signal may be connected to a slot that will be executed as a queued // connection in another thread! Q_EMIT requestedInitialize(&config, &httpRPCRequestProcessor); } void BitcoinApplication::requestShutdown(Config &config) { // Show a simple window indicating shutdown status. Do this first as some of // the steps may take some time below, for example the RPC console may still // be executing a command. shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window)); qDebug() << __func__ << ": Requesting shutdown"; startThread(); window->hide(); window->setClientModel(0); pollShutdownTimer->stop(); #ifdef ENABLE_WALLET window->removeAllWallets(); for (WalletModel *walletModel : m_wallet_models) { delete walletModel; } m_wallet_models.clear(); #endif delete clientModel; clientModel = 0; m_node.startShutdown(); // Request shutdown from core thread Q_EMIT requestedShutdown(); } void BitcoinApplication::initializeResult(bool success) { qDebug() << __func__ << ": Initialization result: " << success; returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE; if (!success) { // Make sure splash screen doesn't stick around during shutdown. Q_EMIT splashFinished(window); // Exit first main loop invocation. quit(); return; } // Log this only after AppInit2 finishes, as then logging setup is // guaranteed complete. qWarning() << "Platform customization:" << platformStyle->getName(); #ifdef ENABLE_WALLET PaymentServer::LoadRootCAs(); paymentServer->setOptionsModel(optionsModel); #endif clientModel = new ClientModel(optionsModel); window->setClientModel(clientModel); #ifdef ENABLE_WALLET bool fFirstWallet = true; for (CWalletRef pwallet : vpwallets) { WalletModel *const walletModel = new WalletModel(platformStyle, pwallet, optionsModel); window->addWallet(walletModel); if (fFirstWallet) { window->setCurrentWallet(walletModel->getWalletName()); fFirstWallet = false; } connect(walletModel, SIGNAL(coinsSent(CWallet *, SendCoinsRecipient, QByteArray)), paymentServer, SLOT(fetchPaymentACK(CWallet *, const SendCoinsRecipient &, QByteArray))); m_wallet_models.push_back(walletModel); } #endif // If -min option passed, start window minimized. if (gArgs.GetBoolArg("-min", false)) { window->showMinimized(); } else { window->show(); } Q_EMIT splashFinished(window); #ifdef ENABLE_WALLET // Now that initialization/startup is done, process any command-line // bitcoincash: URIs or payment requests: connect(paymentServer, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), window, SLOT(handlePaymentRequest(SendCoinsRecipient))); connect(window, SIGNAL(receivedURI(QString)), paymentServer, SLOT(handleURIOrFile(QString))); connect(paymentServer, SIGNAL(message(QString, QString, unsigned int)), window, SLOT(message(QString, QString, unsigned int))); QTimer::singleShot(100, paymentServer, SLOT(uiReady())); #endif pollShutdownTimer->start(200); } void BitcoinApplication::shutdownResult() { // Exit second main loop invocation after shutdown finished. quit(); } void BitcoinApplication::handleRunawayException(const QString &message) { QMessageBox::critical( 0, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. Bitcoin can no longer continue " "safely and will quit.") + QString("\n\n") + message); ::exit(EXIT_FAILURE); } WId BitcoinApplication::getMainWinId() const { if (!window) { return 0; } return window->winId(); } #ifndef BITCOIN_QT_TEST static void MigrateSettings() { assert(!QApplication::applicationName().isEmpty()); static const QString legacyAppName("Bitcoin-Qt"), #ifdef Q_OS_DARWIN // Macs and/or iOS et al use a domain-style name for Settings // files. All other platforms use a simple orgname. This // difference is documented in the QSettings class documentation. legacyOrg("bitcoin.org"); #else legacyOrg("Bitcoin"); #endif QSettings // below picks up settings file location based on orgname,appname legacy(legacyOrg, legacyAppName), // default c'tor below picks up settings file location based on // QApplication::applicationName(), et al -- which was already set // in main() abc; #ifdef Q_OS_DARWIN // Disable bogus OSX keys from MacOS system-wide prefs that may cloud our // judgement ;) (this behavior is also documented in QSettings docs) legacy.setFallbacksEnabled(false); abc.setFallbacksEnabled(false); #endif const QStringList legacyKeys(legacy.allKeys()); // We only migrate settings if we have Core settings but no Bitcoin-ABC // settings if (!legacyKeys.isEmpty() && abc.allKeys().isEmpty()) { for (const QString &key : legacyKeys) { // now, copy settings over abc.setValue(key, legacy.value(key)); } } } int main(int argc, char *argv[]) { SetupEnvironment(); std::unique_ptr node = interface::MakeNode(); /// 1. Parse command-line options. These take precedence over anything else. // Command-line options take precedence: node->parseParameters(argc, argv); // Do not refer to data directory yet, this can be overridden by // Intro::pickDataDirectory /// 2. Basic Qt initialization (not dependent on parameters or /// configuration) Q_INIT_RESOURCE(bitcoin); Q_INIT_RESOURCE(bitcoin_locale); BitcoinApplication app(*node, argc, argv); #if QT_VERSION > 0x050100 // Generate high-dpi pixmaps QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif #if QT_VERSION >= 0x050600 QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif #ifdef Q_OS_MAC QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); #endif // Register meta types used for QMetaObject::invokeMethod qRegisterMetaType(); // Need to pass name here as Amount is a typedef (see // http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType) // IMPORTANT if it is no longer a typedef use the normal variant above qRegisterMetaType("Amount"); qRegisterMetaType>("std::function"); // Need to register any types Qt doesn't know about if you intend // to use them with the signal/slot mechanism Qt provides. Even pointers. // Note that class Config is noncopyable and so we can't register a // non-pointer version of it with Qt, because Qt expects to be able to // copy-construct non-pointers to objects for invoking slots // behind-the-scenes in the 'Queued' connection case. qRegisterMetaType(); /// 3. Application identification // must be set before OptionsModel is initialized or translations are // loaded, as it is used to locate QSettings. // Note: If you move these calls somewhere else, be sure to bring // MigrateSettings() below along for the ride. QApplication::setOrganizationName(QAPP_ORG_NAME); QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN); QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT); // Migrate settings from core's/our old GUI settings to Bitcoin ABC // only if core's exist but Bitcoin ABC's doesn't. // NOTE -- this function needs to be called *after* the above 3 lines // that set the app orgname and app name! If you move the above 3 lines // to elsewhere, take this call with you! MigrateSettings(); GUIUtil::SubstituteFonts(GetLangTerritory()); /// 4. Initialization of translations, so that intro dialog is in user's /// language. Now that QSettings are accessible, initialize translations. QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); translationInterface.Translate.connect(Translate); // Show help message immediately after parsing command-line options (for // "-lang") and setting locale, but before showing splash screen. if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { HelpMessageDialog help(nullptr, gArgs.IsArgSet("-version")); help.showOrPrint(); return EXIT_SUCCESS; } /// 5. Now that settings and translations are available, ask user for data /// directory. User language is set up: pick a data directory. if (!Intro::pickDataDirectory()) { return EXIT_SUCCESS; } /// 6. Determine availability of data directory and parse bitcoin.conf /// - Do not call GetDataDir(true) before this step finishes. if (!fs::is_directory(GetDataDir(false))) { QMessageBox::critical( 0, QObject::tr(PACKAGE_NAME), QObject::tr( "Error: Specified data directory \"%1\" does not exist.") .arg(QString::fromStdString(gArgs.GetArg("-datadir", "")))); return EXIT_FAILURE; } try { node->readConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); } catch (const std::exception &e) { QMessageBox::critical( 0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: Cannot parse configuration file: %1. Only use " "key=value syntax.") .arg(e.what())); return EXIT_FAILURE; } /// 7. Determine network (and switch to network specific options) // - Do not call Params() before this step. // - Do this after parsing the configuration file, as the network can be // switched there. // - QSettings() will use the new application name after this, resulting in // network-specific settings. // - Needs to be done before createOptionsModel. // Check for -testnet or -regtest parameter (Params() calls are only valid // after this clause) try { node->selectParams(gArgs.GetChainName()); } catch (std::exception &e) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: %1").arg(e.what())); return EXIT_FAILURE; } #ifdef ENABLE_WALLET // Parse URIs on command line -- this can affect Params() PaymentServer::ipcParseCommandLine(argc, argv); #endif QScopedPointer networkStyle(NetworkStyle::instantiate( QString::fromStdString(Params().NetworkIDString()))); assert(!networkStyle.isNull()); // Allow for separate UI settings for testnets QApplication::setApplicationName(networkStyle->getAppName()); // Re-initialize translations after changing application name (language in // network-specific settings can be different) initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); #ifdef ENABLE_WALLET /// 8. URI IPC sending // - Do this early as we don't want to bother initializing if we are just // calling IPC // - Do this *after* setting up the data directory, as the data directory // hash is used in the name // of the server. // - Do this after creating app and setting up translations, so errors are // translated properly. if (PaymentServer::ipcSendCommandLine()) { exit(EXIT_SUCCESS); } // Start up the payment server early, too, so impatient users that click on // bitcoincash: links repeatedly have their payment requests routed to this // process: app.createPaymentServer(); #endif /// 9. Main GUI initialization // Install global event filter that makes sure that long tooltips can be // word-wrapped. app.installEventFilter( new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); #if defined(Q_OS_WIN) // Install global event filter for processing Windows session related // Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION) qApp->installNativeEventFilter(new WinShutdownMonitor()); #endif // Install qDebug() message handler to route to debug.log qInstallMessageHandler(DebugMessageHandler); // Allow parameter interaction before we create the options model app.parameterSetup(); // Load GUI settings from QSettings app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false)); // Subscribe to global signals from core std::unique_ptr handler = node->handleInitMessage(InitMessage); // Get global config Config &config = const_cast(GetConfig()); if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) { app.createSplashScreen(networkStyle.data()); } RPCServer rpcServer; HTTPRPCRequestProcessor httpRPCRequestProcessor(config, rpcServer); try { app.createWindow(&config, networkStyle.data()); // Perform base initialization before spinning up // initialization/shutdown thread. This is acceptable because this // function only contains steps that are quick to execute, so the GUI // thread won't be held up. if (!node->baseInitialize(config, rpcServer)) { // A dialog with detailed error will have been shown by InitError() return EXIT_FAILURE; } app.requestInitialize(config, httpRPCRequestProcessor); #if defined(Q_OS_WIN) WinShutdownMonitor::registerShutdownBlockReason( QObject::tr("%1 didn't yet exit safely...") .arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId()); #endif app.exec(); app.requestShutdown(config); app.exec(); return app.getReturnValue(); } catch (const std::exception &e) { PrintExceptionContinue(&e, "Runaway exception"); app.handleRunawayException( QString::fromStdString(node->getWarnings("gui"))); } catch (...) { PrintExceptionContinue(nullptr, "Runaway exception"); app.handleRunawayException( QString::fromStdString(node->getWarnings("gui"))); } return EXIT_FAILURE; } #endif // BITCOIN_QT_TEST diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 4c427fc6f5..14980640af 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1,1335 +1,1335 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "bitcoingui.h" #include "bitcoinunits.h" #include "clientmodel.h" #include "guiconstants.h" #include "guiutil.h" #include "modaloverlay.h" #include "networkstyle.h" #include "notificator.h" #include "openuridialog.h" #include "optionsdialog.h" #include "optionsmodel.h" #include "platformstyle.h" #include "rpcconsole.h" #include "utilitydialog.h" #ifdef ENABLE_WALLET #include "walletframe.h" #include "walletmodel.h" #include "walletview.h" #endif // ENABLE_WALLET #ifdef Q_OS_MAC #include "macdockiconhandler.h" #endif #include "chainparams.h" #include "init.h" +#include "interface/handler.h" +#include "interface/node.h" #include "ui_interface.h" #include "util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::string BitcoinGUI::DEFAULT_UIPLATFORM = #if defined(Q_OS_MAC) "macosx" #elif defined(Q_OS_WIN) "windows" #else "other" #endif ; -BitcoinGUI::BitcoinGUI(const Config *configIn, +BitcoinGUI::BitcoinGUI(interface::Node &node, const Config *configIn, const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) - : QMainWindow(parent), enableWallet(false), platformStyle(_platformStyle), - config(configIn) { + : QMainWindow(parent), enableWallet(false), m_node(node), + platformStyle(_platformStyle), config(configIn) { QSettings settings; if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { // Restore failed (perhaps missing setting), center the window move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); } QString windowTitle = tr(PACKAGE_NAME) + " - "; #ifdef ENABLE_WALLET enableWallet = WalletModel::isWalletEnabled(); #endif // ENABLE_WALLET if (enableWallet) { windowTitle += tr("Wallet"); } else { windowTitle += tr("Node"); } windowTitle += " " + networkStyle->getTitleAddText(); #ifndef Q_OS_MAC QApplication::setWindowIcon(networkStyle->getTrayAndWindowIcon()); setWindowIcon(networkStyle->getTrayAndWindowIcon()); #else MacDockIconHandler::instance()->setIcon(networkStyle->getAppIcon()); #endif setWindowTitle(windowTitle); rpcConsole = new RPCConsole(_platformStyle, 0); helpMessageDialog = new HelpMessageDialog(this, false); #ifdef ENABLE_WALLET if (enableWallet) { /** Create wallet frame and make it the central widget */ walletFrame = new WalletFrame(_platformStyle, config, this); setCentralWidget(walletFrame); } else #endif // ENABLE_WALLET { /** * When compiled without wallet or -disablewallet is provided, the * central widget is the rpc console. */ setCentralWidget(rpcConsole); } // Accept D&D of URIs setAcceptDrops(true); // Create actions for the toolbar, menu bar and tray/dock icon // Needs walletFrame to be initialized createActions(); // Create application menu bar createMenuBar(); // Create the toolbars createToolBars(); // Create system tray icon and notification createTrayIcon(networkStyle); // Create status bar statusBar(); // Disable size grip because it looks ugly and nobody needs it statusBar()->setSizeGripEnabled(false); // Status bar notification icons QFrame *frameBlocks = new QFrame(); frameBlocks->setContentsMargins(0, 0, 0, 0); frameBlocks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); frameBlocksLayout->setContentsMargins(3, 0, 3, 0); frameBlocksLayout->setSpacing(3); unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle); labelWalletEncryptionIcon = new QLabel(); labelWalletHDStatusIcon = new QLabel(); connectionsControl = new GUIUtil::ClickableLabel(); labelBlocksIcon = new GUIUtil::ClickableLabel(); if (enableWallet) { frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(unitDisplayControl); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelWalletEncryptionIcon); frameBlocksLayout->addWidget(labelWalletHDStatusIcon); } frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(connectionsControl); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelBlocksIcon); frameBlocksLayout->addStretch(); // Progress bar and label for blocks download progressBarLabel = new QLabel(); progressBarLabel->setVisible(false); progressBar = new GUIUtil::ProgressBar(); 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://doc.qt.io/qt-5/gallery.html QString curStyle = QApplication::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); // Install event filter to be able to catch status tip events // (QEvent::StatusTip) this->installEventFilter(this); // Initially wallet actions should be disabled setWalletActionsEnabled(false); // Subscribe to notifications from core subscribeToCoreSignals(); connect(connectionsControl, SIGNAL(clicked(QPoint)), this, SLOT(toggleNetworkActive())); modalOverlay = new ModalOverlay(this->centralWidget()); #ifdef ENABLE_WALLET if (enableWallet) { connect(walletFrame, SIGNAL(requestedSyncWarningInfo()), this, SLOT(showModalOverlay())); connect(labelBlocksIcon, SIGNAL(clicked(QPoint)), this, SLOT(showModalOverlay())); connect(progressBar, SIGNAL(clicked(QPoint)), this, SLOT(showModalOverlay())); } #endif } BitcoinGUI::~BitcoinGUI() { // Unsubscribe from notifications from core unsubscribeFromCoreSignals(); QSettings settings; settings.setValue("MainWindowGeometry", saveGeometry()); // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) if (trayIcon) { trayIcon->hide(); } #ifdef Q_OS_MAC delete appMenuBar; MacDockIconHandler::cleanup(); #endif delete rpcConsole; } void BitcoinGUI::createActions() { QActionGroup *tabGroup = new QActionGroup(this); overviewAction = new QAction(platformStyle->SingleColorIcon(":/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( platformStyle->SingleColorIcon(":/icons/send"), tr("&Send"), 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); sendCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/send"), sendCoinsAction->text(), this); sendCoinsMenuAction->setStatusTip(sendCoinsAction->statusTip()); sendCoinsMenuAction->setToolTip(sendCoinsMenuAction->statusTip()); receiveCoinsAction = new QAction( platformStyle->SingleColorIcon(":/icons/receiving_addresses"), tr("&Receive"), this); receiveCoinsAction->setStatusTip( tr("Request payments (generates QR codes and %1: URIs)") .arg(GUIUtil::bitcoinURIScheme(*config))); receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); receiveCoinsAction->setCheckable(true); receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); tabGroup->addAction(receiveCoinsAction); receiveCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/receiving_addresses"), receiveCoinsAction->text(), this); receiveCoinsMenuAction->setStatusTip(receiveCoinsAction->statusTip()); receiveCoinsMenuAction->setToolTip(receiveCoinsMenuAction->statusTip()); historyAction = new QAction(platformStyle->SingleColorIcon(":/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); #ifdef ENABLE_WALLET // These showNormalIfMinimized are needed because Send Coins and Receive // Coins can be triggered from the tray menu, and need to show the GUI to be // useful. 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(sendCoinsMenuAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(sendCoinsMenuAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage())); connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); connect(receiveCoinsMenuAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(receiveCoinsMenuAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage())); #endif // ENABLE_WALLET quitAction = new QAction(platformStyle->TextColorIcon(":/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(platformStyle->TextColorIcon(":/icons/about"), tr("&About %1").arg(tr(PACKAGE_NAME)), this); aboutAction->setStatusTip( tr("Show information about %1").arg(tr(PACKAGE_NAME))); aboutAction->setMenuRole(QAction::AboutRole); aboutAction->setEnabled(false); aboutQtAction = new QAction(platformStyle->TextColorIcon(":/icons/about_qt"), tr("About &Qt"), this); aboutQtAction->setStatusTip(tr("Show information about Qt")); aboutQtAction->setMenuRole(QAction::AboutQtRole); optionsAction = new QAction(platformStyle->TextColorIcon(":/icons/options"), tr("&Options..."), this); optionsAction->setStatusTip( tr("Modify configuration options for %1").arg(tr(PACKAGE_NAME))); optionsAction->setMenuRole(QAction::PreferencesRole); optionsAction->setEnabled(false); toggleHideAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&Show / Hide"), this); toggleHideAction->setStatusTip(tr("Show or hide the main Window")); encryptWalletAction = new QAction(platformStyle->TextColorIcon(":/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(platformStyle->TextColorIcon(":/icons/filesave"), tr("&Backup Wallet..."), this); backupWalletAction->setStatusTip(tr("Backup wallet to another location")); changePassphraseAction = new QAction(platformStyle->TextColorIcon(":/icons/key"), tr("&Change Passphrase..."), this); changePassphraseAction->setStatusTip( tr("Change the passphrase used for wallet encryption")); signMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/edit"), tr("Sign &message..."), this); signMessageAction->setStatusTip( tr("Sign messages with your Bitcoin addresses to prove you own them")); verifyMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/verify"), tr("&Verify message..."), this); verifyMessageAction->setStatusTip( tr("Verify messages to ensure they were signed with specified Bitcoin " "addresses")); openRPCConsoleAction = new QAction(platformStyle->TextColorIcon(":/icons/debugwindow"), tr("&Debug window"), this); openRPCConsoleAction->setStatusTip( tr("Open debugging and diagnostic console")); // initially disable the debug window menu item openRPCConsoleAction->setEnabled(false); usedSendingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Sending addresses..."), this); usedSendingAddressesAction->setStatusTip( tr("Show the list of used sending addresses and labels")); usedReceivingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Receiving addresses..."), this); usedReceivingAddressesAction->setStatusTip( tr("Show the list of used receiving addresses and labels")); openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this); openAction->setStatusTip(tr("Open a %1: URI or payment request") .arg(GUIUtil::bitcoinURIScheme(*config))); showHelpMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/info"), tr("&Command-line options"), this); showHelpMessageAction->setMenuRole(QAction::NoRole); showHelpMessageAction->setStatusTip( tr("Show the %1 help message to get a list with possible Bitcoin " "command-line options") .arg(tr(PACKAGE_NAME))); 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(showHelpMessageAction, SIGNAL(triggered()), this, SLOT(showHelpMessageClicked())); connect(openRPCConsoleAction, SIGNAL(triggered()), this, SLOT(showDebugWindow())); // prevents an open debug window from becoming stuck/unusable on client // shutdown connect(quitAction, SIGNAL(triggered()), rpcConsole, SLOT(hide())); #ifdef ENABLE_WALLET if (walletFrame) { connect(encryptWalletAction, SIGNAL(triggered(bool)), walletFrame, SLOT(encryptWallet(bool))); connect(backupWalletAction, SIGNAL(triggered()), walletFrame, SLOT(backupWallet())); connect(changePassphraseAction, SIGNAL(triggered()), walletFrame, SLOT(changePassphrase())); connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab())); connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); connect(usedSendingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedSendingAddresses())); connect(usedReceivingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedReceivingAddresses())); connect(openAction, SIGNAL(triggered()), this, SLOT(openClicked())); } #endif // ENABLE_WALLET new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C), this, SLOT(showDebugWindowActivateConsole())); new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_D), this, SLOT(showDebugWindow())); } 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")); if (walletFrame) { file->addAction(openAction); file->addAction(backupWalletAction); file->addAction(signMessageAction); file->addAction(verifyMessageAction); file->addSeparator(); file->addAction(usedSendingAddressesAction); file->addAction(usedReceivingAddressesAction); file->addSeparator(); } file->addAction(quitAction); QMenu *settings = appMenuBar->addMenu(tr("&Settings")); if (walletFrame) { settings->addAction(encryptWalletAction); settings->addAction(changePassphraseAction); settings->addSeparator(); } settings->addAction(optionsAction); QMenu *help = appMenuBar->addMenu(tr("&Help")); if (walletFrame) { help->addAction(openRPCConsoleAction); } help->addAction(showHelpMessageAction); help->addSeparator(); help->addAction(aboutAction); help->addAction(aboutQtAction); } void BitcoinGUI::createToolBars() { if (walletFrame) { QToolBar *toolbar = addToolBar(tr("Tabs toolbar")); appToolBar = toolbar; toolbar->setContextMenuPolicy(Qt::PreventContextMenu); toolbar->setMovable(false); toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolbar->addAction(overviewAction); toolbar->addAction(sendCoinsAction); toolbar->addAction(receiveCoinsAction); toolbar->addAction(historyAction); overviewAction->setChecked(true); #ifdef ENABLE_WALLET QWidget *spacer = new QWidget(); spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); toolbar->addWidget(spacer); m_wallet_selector = new QComboBox(); connect(m_wallet_selector, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(setCurrentWallet(const QString &))); #endif } } void BitcoinGUI::setClientModel(ClientModel *_clientModel) { this->clientModel = _clientModel; if (_clientModel) { // 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 createTrayIconMenu(); // Keep up to date with client updateNetworkState(); connect(_clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); connect(_clientModel, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool))); modalOverlay->setKnownBestHeight( _clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(_clientModel->getHeaderTipTime())); setNumBlocks(_clientModel->getNumBlocks(), _clientModel->getLastBlockDate(), _clientModel->getVerificationProgress(nullptr), false); connect(_clientModel, SIGNAL(numBlocksChanged(int, QDateTime, double, bool)), this, SLOT(setNumBlocks(int, QDateTime, double, bool))); // Receive and report messages from client model connect(_clientModel, SIGNAL(message(QString, QString, unsigned int)), this, SLOT(message(QString, QString, unsigned int))); // Show progress dialog connect(_clientModel, SIGNAL(showProgress(QString, int)), this, SLOT(showProgress(QString, int))); rpcConsole->setClientModel(_clientModel); #ifdef ENABLE_WALLET if (walletFrame) { walletFrame->setClientModel(_clientModel); } #endif // ENABLE_WALLET unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel()); OptionsModel *optionsModel = _clientModel->getOptionsModel(); if (optionsModel) { // be aware of the tray icon disable state change reported by the // OptionsModel object. connect(optionsModel, SIGNAL(hideTrayIconChanged(bool)), this, SLOT(setTrayIconVisible(bool))); // initialize the disable state of the tray icon with the current // value in the model. setTrayIconVisible(optionsModel->getHideTrayIcon()); } } else { // Disable possibility to show main window via action toggleHideAction->setEnabled(false); if (trayIconMenu) { // Disable context menu on tray icon trayIconMenu->clear(); } // Propagate cleared model to child objects rpcConsole->setClientModel(nullptr); #ifdef ENABLE_WALLET if (walletFrame) { walletFrame->setClientModel(nullptr); } #endif // ENABLE_WALLET unitDisplayControl->setOptionsModel(nullptr); } } #ifdef ENABLE_WALLET bool BitcoinGUI::addWallet(WalletModel *walletModel) { if (!walletFrame) return false; const QString name = walletModel->getWalletName(); setWalletActionsEnabled(true); m_wallet_selector->addItem(name); if (m_wallet_selector->count() == 2) { m_wallet_selector_label = new QLabel(); m_wallet_selector_label->setText(tr("Wallet:") + " "); m_wallet_selector_label->setBuddy(m_wallet_selector); appToolBar->addWidget(m_wallet_selector_label); appToolBar->addWidget(m_wallet_selector); } rpcConsole->addWallet(walletModel); return walletFrame->addWallet(walletModel); } bool BitcoinGUI::setCurrentWallet(const QString &name) { if (!walletFrame) return false; return walletFrame->setCurrentWallet(name); } void BitcoinGUI::removeAllWallets() { if (!walletFrame) return; setWalletActionsEnabled(false); walletFrame->removeAllWallets(); } #endif // ENABLE_WALLET void BitcoinGUI::setWalletActionsEnabled(bool enabled) { overviewAction->setEnabled(enabled); sendCoinsAction->setEnabled(enabled); sendCoinsMenuAction->setEnabled(enabled); receiveCoinsAction->setEnabled(enabled); receiveCoinsMenuAction->setEnabled(enabled); historyAction->setEnabled(enabled); encryptWalletAction->setEnabled(enabled); backupWalletAction->setEnabled(enabled); changePassphraseAction->setEnabled(enabled); signMessageAction->setEnabled(enabled); verifyMessageAction->setEnabled(enabled); usedSendingAddressesAction->setEnabled(enabled); usedReceivingAddressesAction->setEnabled(enabled); openAction->setEnabled(enabled); } void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle) { #ifndef Q_OS_MAC trayIcon = new QSystemTrayIcon(this); QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " + networkStyle->getTitleAddText(); trayIcon->setToolTip(toolTip); trayIcon->setIcon(networkStyle->getTrayAndWindowIcon()); trayIcon->hide(); #endif notificator = new Notificator(QApplication::applicationName(), trayIcon, this); } void BitcoinGUI::createTrayIconMenu() { #ifndef Q_OS_MAC // return if trayIcon is unset (only on non-Mac OSes) if (!trayIcon) return; 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(); dockIconHandler->setMainWindow(static_cast(this)); trayIconMenu = dockIconHandler->dockMenu(); #endif // Configuration of the tray icon (or dock icon) icon menu trayIconMenu->addAction(toggleHideAction); trayIconMenu->addSeparator(); trayIconMenu->addAction(sendCoinsMenuAction); trayIconMenu->addAction(receiveCoinsMenuAction); 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 toggleHidden(); } } #endif void BitcoinGUI::optionsClicked() { if (!clientModel || !clientModel->getOptionsModel()) return; OptionsDialog dlg(this, enableWallet); dlg.setModel(clientModel->getOptionsModel()); dlg.exec(); } void BitcoinGUI::aboutClicked() { if (!clientModel) return; HelpMessageDialog dlg(this, true); dlg.exec(); } void BitcoinGUI::showDebugWindow() { rpcConsole->showNormal(); rpcConsole->show(); rpcConsole->raise(); rpcConsole->activateWindow(); } void BitcoinGUI::showDebugWindowActivateConsole() { rpcConsole->setTabFocus(RPCConsole::TAB_CONSOLE); showDebugWindow(); } void BitcoinGUI::showHelpMessageClicked() { helpMessageDialog->show(); } #ifdef ENABLE_WALLET void BitcoinGUI::openClicked() { OpenURIDialog dlg(config, this); if (dlg.exec()) { Q_EMIT receivedURI(dlg.getURI()); } } void BitcoinGUI::gotoOverviewPage() { overviewAction->setChecked(true); if (walletFrame) walletFrame->gotoOverviewPage(); } void BitcoinGUI::gotoHistoryPage() { historyAction->setChecked(true); if (walletFrame) walletFrame->gotoHistoryPage(); } void BitcoinGUI::gotoReceiveCoinsPage() { receiveCoinsAction->setChecked(true); if (walletFrame) walletFrame->gotoReceiveCoinsPage(); } void BitcoinGUI::gotoSendCoinsPage(QString addr) { sendCoinsAction->setChecked(true); if (walletFrame) walletFrame->gotoSendCoinsPage(addr); } void BitcoinGUI::gotoSignMessageTab(QString addr) { if (walletFrame) walletFrame->gotoSignMessageTab(addr); } void BitcoinGUI::gotoVerifyMessageTab(QString addr) { if (walletFrame) walletFrame->gotoVerifyMessageTab(addr); } #endif // ENABLE_WALLET void BitcoinGUI::updateNetworkState() { int count = clientModel->getNumConnections(); 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; } QString tooltip; if (clientModel->getNetworkActive()) { tooltip = tr("%n active connection(s) to Bitcoin network", "", count) + QString(".
") + tr("Click to disable network activity."); } else { tooltip = tr("Network activity disabled.") + QString("
") + tr("Click to enable network activity again."); icon = ":/icons/network_disabled"; } // Don't word-wrap this (fixed-width) tooltip tooltip = QString("") + tooltip + QString(""); connectionsControl->setToolTip(tooltip); connectionsControl->setPixmap(platformStyle->SingleColorIcon(icon).pixmap( STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); } void BitcoinGUI::setNumConnections(int count) { updateNetworkState(); } void BitcoinGUI::setNetworkActive(bool networkActive) { updateNetworkState(); } void BitcoinGUI::updateHeadersSyncProgressLabel() { int64_t headersTipTime = clientModel->getHeaderTipTime(); int headersTipHeight = clientModel->getHeaderTipHeight(); int estHeadersLeft = (GetTime() - headersTipTime) / Params().GetConsensus().nPowTargetSpacing; if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC) { progressBarLabel->setText( tr("Syncing Headers (%1%)...") .arg(QString::number(100.0 / (headersTipHeight + estHeadersLeft) * headersTipHeight, 'f', 1))); } } void BitcoinGUI::setNumBlocks(int count, const QDateTime &blockDate, double nVerificationProgress, bool header) { if (modalOverlay) { if (header) { modalOverlay->setKnownBestHeight(count, blockDate); } else { modalOverlay->tipUpdate(count, blockDate, nVerificationProgress); } } if (!clientModel) { return; } // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait // until chain-sync starts -> garbled text) statusBar()->clearMessage(); // Acquire current block source enum BlockSource blockSource = clientModel->getBlockSource(); switch (blockSource) { case BlockSource::NETWORK: if (header) { updateHeadersSyncProgressLabel(); return; } progressBarLabel->setText(tr("Synchronizing with network...")); updateHeadersSyncProgressLabel(); break; case BlockSource::DISK: if (header) { progressBarLabel->setText(tr("Indexing blocks on disk...")); } else { progressBarLabel->setText(tr("Processing blocks on disk...")); } break; case BlockSource::REINDEX: progressBarLabel->setText(tr("Reindexing blocks on disk...")); break; case BlockSource::NONE: if (header) { return; } progressBarLabel->setText(tr("Connecting to peers...")); break; } QString tooltip; QDateTime currentDate = QDateTime::currentDateTime(); qint64 secs = blockDate.secsTo(currentDate); tooltip = tr("Processed %n block(s) of transaction history.", "", count); // Set icon state: spinning if catching up, tick otherwise if (secs < 90 * 60) { tooltip = tr("Up to date") + QString(".
") + tooltip; labelBlocksIcon->setPixmap( platformStyle->SingleColorIcon(":/icons/synced") .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); #ifdef ENABLE_WALLET if (walletFrame) { walletFrame->showOutOfSyncWarning(false); modalOverlay->showHide(true, true); } #endif // ENABLE_WALLET progressBarLabel->setVisible(false); progressBar->setVisible(false); } else { QString timeBehindText = GUIUtil::formatNiceTimeOffset(secs); progressBarLabel->setVisible(true); progressBar->setFormat(tr("%1 behind").arg(timeBehindText)); progressBar->setMaximum(1000000000); progressBar->setValue(nVerificationProgress * 1000000000.0 + 0.5); progressBar->setVisible(true); tooltip = tr("Catching up...") + QString("
") + tooltip; if (count != prevBlocks) { labelBlocksIcon->setPixmap( platformStyle ->SingleColorIcon(QString(":/movies/spinner-%1") .arg(spinnerFrame, 3, 10, QChar('0'))) .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES; } prevBlocks = count; #ifdef ENABLE_WALLET if (walletFrame) { walletFrame->showOutOfSyncWarning(true); modalOverlay->showHide(); } #endif // ENABLE_WALLET tooltip += QString("
"); tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText); tooltip += QString("
"); tooltip += tr("Transactions after this will not yet be visible."); } // Don't word-wrap this (fixed-width) tooltip tooltip = QString("") + tooltip + QString(""); labelBlocksIcon->setToolTip(tooltip); progressBarLabel->setToolTip(tooltip); progressBar->setToolTip(tooltip); } void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, bool *ret) { // default title QString strTitle = tr("Bitcoin"); // Default to information icon int nMBoxIcon = QMessageBox::Information; int nNotifyIcon = Notificator::Information; QString msgType; // Prefer supplied title over style based title if (!title.isEmpty()) { msgType = title; } else { switch (style) { case CClientUIInterface::MSG_ERROR: msgType = tr("Error"); break; case CClientUIInterface::MSG_WARNING: msgType = tr("Warning"); break; case CClientUIInterface::MSG_INFORMATION: msgType = tr("Information"); break; default: break; } } // Append title to "Bitcoin - " if (!msgType.isEmpty()) { strTitle += " - " + msgType; } // 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; showNormalIfMinimized(); QMessageBox mBox(static_cast(nMBoxIcon), strTitle, message, buttons, this); int r = mBox.exec(); if (ret != nullptr) { *ret = r == QMessageBox::Ok; } } else notificator->notify(static_cast(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() && clientModel->getOptionsModel()->getMinimizeToTray()) { QWindowStateChangeEvent *wsevt = static_cast(e); if (!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized()) { QTimer::singleShot(0, this, SLOT(hide())); e->ignore(); } } } #endif } void BitcoinGUI::closeEvent(QCloseEvent *event) { #ifndef Q_OS_MAC // Ignored on Mac if (clientModel && clientModel->getOptionsModel()) { if (!clientModel->getOptionsModel()->getMinimizeOnClose()) { // close rpcConsole in case it was open to make some space for the // shutdown window rpcConsole->close(); QApplication::quit(); } else { QMainWindow::showMinimized(); event->ignore(); } } #else QMainWindow::closeEvent(event); #endif } void BitcoinGUI::showEvent(QShowEvent *event) { // enable the debug window when the main window shows up openRPCConsoleAction->setEnabled(true); aboutAction->setEnabled(true); optionsAction->setEnabled(true); } #ifdef ENABLE_WALLET void BitcoinGUI::incomingTransaction(const QString &date, int unit, const Amount amount, const QString &type, const QString &address, const QString &label, const QString &walletName) { // On new transaction, make an info balloon QString msg = tr("Date: %1\n").arg(date) + tr("Amount: %1\n") .arg(BitcoinUnits::formatWithUnit(unit, amount, true)); if (WalletModel::isMultiwallet() && !walletName.isEmpty()) { msg += tr("Wallet: %1\n").arg(walletName); } msg += tr("Type: %1\n").arg(type); if (!label.isEmpty()) { msg += tr("Label: %1\n").arg(label); } else if (!address.isEmpty()) { msg += tr("Address: %1\n").arg(address); } message(amount < Amount::zero() ? tr("Sent transaction") : tr("Incoming transaction"), msg, CClientUIInterface::MSG_INFORMATION); } #endif // ENABLE_WALLET void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event) { // Accept only URIs if (event->mimeData()->hasUrls()) { event->acceptProposedAction(); } } void BitcoinGUI::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls()) { for (const QUrl &uri : event->mimeData()->urls()) { Q_EMIT receivedURI(uri.toString()); } } 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); } #ifdef ENABLE_WALLET bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient &recipient) { // URI has to be valid if (walletFrame && walletFrame->handlePaymentRequest(recipient)) { showNormalIfMinimized(); gotoSendCoinsPage(); return true; } return false; } void BitcoinGUI::setHDStatus(int hdEnabled) { labelWalletHDStatusIcon->setPixmap( platformStyle ->SingleColorIcon(hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled") .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelWalletHDStatusIcon->setToolTip( hdEnabled ? tr("HD key generation is enabled") : tr("HD key generation is disabled")); // eventually disable the QLabel to set its opacity to 50% labelWalletHDStatusIcon->setEnabled(hdEnabled); } void BitcoinGUI::setEncryptionStatus(int status) { switch (status) { case WalletModel::Unencrypted: labelWalletEncryptionIcon->hide(); encryptWalletAction->setChecked(false); changePassphraseAction->setEnabled(false); encryptWalletAction->setEnabled(true); break; case WalletModel::Unlocked: labelWalletEncryptionIcon->show(); labelWalletEncryptionIcon->setPixmap( platformStyle->SingleColorIcon(":/icons/lock_open") .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelWalletEncryptionIcon->setToolTip( tr("Wallet is encrypted and currently unlocked")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); encryptWalletAction->setEnabled( false); // TODO: decrypt currently not supported break; case WalletModel::Locked: labelWalletEncryptionIcon->show(); labelWalletEncryptionIcon->setPixmap( platformStyle->SingleColorIcon(":/icons/lock_closed") .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelWalletEncryptionIcon->setToolTip( tr("Wallet is encrypted and currently locked")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); encryptWalletAction->setEnabled( false); // TODO: decrypt currently not supported break; } } void BitcoinGUI::updateWalletStatus() { if (!walletFrame) { return; } WalletView *const walletView = walletFrame->currentWalletView(); if (!walletView) { return; } WalletModel *const walletModel = walletView->getWalletModel(); setEncryptionStatus(walletModel->getEncryptionStatus()); setHDStatus(walletModel->hdEnabled()); } #endif // ENABLE_WALLET void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) { if (!clientModel) { return; } // 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); } void BitcoinGUI::detectShutdown() { - if (ShutdownRequested()) { + if (m_node.shutdownRequested()) { if (rpcConsole) { rpcConsole->hide(); } qApp->quit(); } } void BitcoinGUI::showProgress(const QString &title, int nProgress) { if (nProgress == 0) { progressDialog = new QProgressDialog(title, "", 0, 100); progressDialog->setWindowModality(Qt::ApplicationModal); progressDialog->setMinimumDuration(0); progressDialog->setCancelButton(0); progressDialog->setAutoClose(false); progressDialog->setValue(0); } else if (progressDialog) { if (nProgress == 100) { progressDialog->close(); progressDialog->deleteLater(); } else { progressDialog->setValue(nProgress); } } } void BitcoinGUI::setTrayIconVisible(bool fHideTrayIcon) { if (trayIcon) { trayIcon->setVisible(!fHideTrayIcon); } } void BitcoinGUI::showModalOverlay() { if (modalOverlay && (progressBar->isVisible() || modalOverlay->isLayerVisible())) { modalOverlay->toggleVisibility(); } } static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string &message, const std::string &caption, unsigned int style) { bool modal = (style & CClientUIInterface::MODAL); // The SECURE flag has no effect in the Qt GUI. // bool secure = (style & CClientUIInterface::SECURE); style &= ~CClientUIInterface::SECURE; bool ret = false; // In case of modal message, use blocking connection to wait for user to // click a button QMetaObject::invokeMethod(gui, "message", modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(caption)), Q_ARG(QString, QString::fromStdString(message)), Q_ARG(unsigned int, style), Q_ARG(bool *, &ret)); return ret; } void BitcoinGUI::subscribeToCoreSignals() { // Connect signals to client - uiInterface.ThreadSafeMessageBox.connect( + m_handler_message_box = m_node.handleMessageBox( boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); - uiInterface.ThreadSafeQuestion.connect( + m_handler_question = m_node.handleQuestion( boost::bind(ThreadSafeMessageBox, this, _1, _3, _4)); } void BitcoinGUI::unsubscribeFromCoreSignals() { // Disconnect signals from client - uiInterface.ThreadSafeMessageBox.disconnect( - boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); - uiInterface.ThreadSafeQuestion.disconnect( - boost::bind(ThreadSafeMessageBox, this, _1, _3, _4)); + m_handler_message_box->disconnect(); + m_handler_question->disconnect(); } void BitcoinGUI::toggleNetworkActive() { if (clientModel) { clientModel->setNetworkActive(!clientModel->getNetworkActive()); } } UnitDisplayStatusBarControl::UnitDisplayStatusBarControl( const PlatformStyle *platformStyle) : optionsModel(0), menu(0) { createContextMenu(); setToolTip(tr("Unit to show amounts in. Click to select another unit.")); QList units = BitcoinUnits::availableUnits(); int max_width = 0; const QFontMetrics fm(font()); for (const BitcoinUnits::Unit unit : units) { max_width = qMax(max_width, fm.width(BitcoinUnits::name(unit))); } setMinimumSize(max_width, 0); setAlignment(Qt::AlignRight | Qt::AlignVCenter); setStyleSheet(QString("QLabel { color : %1 }") .arg(platformStyle->SingleColor().name())); } /** So that it responds to button clicks */ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event) { onDisplayUnitsClicked(event->pos()); } /** Creates context menu, its actions, and wires up all the relevant signals for * mouse events. */ void UnitDisplayStatusBarControl::createContextMenu() { menu = new QMenu(this); for (BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) { QAction *menuAction = new QAction(QString(BitcoinUnits::name(u)), this); menuAction->setData(QVariant(u)); menu->addAction(menuAction); } connect(menu, SIGNAL(triggered(QAction *)), this, SLOT(onMenuSelection(QAction *))); } /** Lets the control know about the Options Model (and its signals) */ void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *_optionsModel) { if (_optionsModel) { this->optionsModel = _optionsModel; // be aware of a display unit change reported by the OptionsModel // object. connect(_optionsModel, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit(int))); // initialize the display units label with the current value in the // model. updateDisplayUnit(_optionsModel->getDisplayUnit()); } } /** When Display Units are changed on OptionsModel it will refresh the display * text of the control on the status bar */ void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits) { setText(BitcoinUnits::name(newUnits)); } /** Shows context menu with Display Unit options by the mouse coordinates */ void UnitDisplayStatusBarControl::onDisplayUnitsClicked(const QPoint &point) { QPoint globalPos = mapToGlobal(point); menu->exec(globalPos); } /** Tells underlying optionsModel to update its current display unit. */ void UnitDisplayStatusBarControl::onMenuSelection(QAction *action) { if (action) { optionsModel->setDisplayUnit(action->data()); } } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 4b31de6cb8..8a75840ef5 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -1,303 +1,314 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_BITCOINGUI_H #define BITCOIN_QT_BITCOINGUI_H #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "amount.h" #include #include #include #include #include #include +#include + class ClientModel; class NetworkStyle; class Notificator; class OptionsModel; class PlatformStyle; class RPCConsole; class SendCoinsRecipient; class UnitDisplayStatusBarControl; class WalletFrame; class WalletModel; class HelpMessageDialog; class ModalOverlay; class Config; class CWallet; +namespace interface { +class Handler; +class Node; +} + QT_BEGIN_NAMESPACE class QAction; class QComboBox; class QProgressBar; class QProgressDialog; QT_END_NAMESPACE /** * Bitcoin GUI main class. This class represents the main window of the Bitcoin * UI. It communicates with both the client and wallet models to give the user * an up-to-date view of the current core state. */ class BitcoinGUI : public QMainWindow { Q_OBJECT public: static const std::string DEFAULT_UIPLATFORM; - explicit BitcoinGUI(const Config *, const PlatformStyle *platformStyle, + explicit BitcoinGUI(interface::Node &node, const Config *, + const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0); ~BitcoinGUI(); /** * Set the client model. * The client model represents the part of the core that communicates with * the P2P network, and is wallet-agnostic. */ void setClientModel(ClientModel *clientModel); #ifdef ENABLE_WALLET /** * Set the wallet model. * The wallet model represents a bitcoin wallet, and offers access to the * list of transactions, address book and sending functionality. */ bool addWallet(WalletModel *walletModel); void removeAllWallets(); #endif // ENABLE_WALLET bool enableWallet = false; protected: void changeEvent(QEvent *e) override; void closeEvent(QCloseEvent *event) override; void showEvent(QShowEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override; void dropEvent(QDropEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override; private: + interface::Node &m_node; + std::unique_ptr m_handler_message_box; + std::unique_ptr m_handler_question; ClientModel *clientModel = nullptr; WalletFrame *walletFrame = nullptr; UnitDisplayStatusBarControl *unitDisplayControl = nullptr; QLabel *labelWalletEncryptionIcon = nullptr; QLabel *labelWalletHDStatusIcon = nullptr; QLabel *connectionsControl = nullptr; QLabel *labelBlocksIcon = nullptr; QLabel *progressBarLabel = nullptr; QProgressBar *progressBar = nullptr; QProgressDialog *progressDialog = nullptr; QMenuBar *appMenuBar = nullptr; QToolBar *appToolBar = nullptr; QAction *overviewAction = nullptr; QAction *historyAction = nullptr; QAction *quitAction = nullptr; QAction *sendCoinsAction = nullptr; QAction *sendCoinsMenuAction = nullptr; QAction *usedSendingAddressesAction = nullptr; QAction *usedReceivingAddressesAction = nullptr; QAction *signMessageAction = nullptr; QAction *verifyMessageAction = nullptr; QAction *aboutAction = nullptr; QAction *receiveCoinsAction = nullptr; QAction *receiveCoinsMenuAction = nullptr; QAction *optionsAction = nullptr; QAction *toggleHideAction = nullptr; QAction *encryptWalletAction = nullptr; QAction *backupWalletAction = nullptr; QAction *changePassphraseAction = nullptr; QAction *aboutQtAction = nullptr; QAction *openRPCConsoleAction = nullptr; QAction *openAction = nullptr; QAction *showHelpMessageAction = nullptr; QLabel *m_wallet_selector_label = nullptr; QComboBox *m_wallet_selector = nullptr; QSystemTrayIcon *trayIcon = nullptr; QMenu *trayIconMenu = nullptr; Notificator *notificator = nullptr; RPCConsole *rpcConsole = nullptr; HelpMessageDialog *helpMessageDialog = nullptr; ModalOverlay *modalOverlay = nullptr; /** Keep track of previous number of blocks, to detect progress */ int prevBlocks = 0; int spinnerFrame = 0; const PlatformStyle *platformStyle; const Config *config; /** Create the main UI actions. */ void createActions(); /** Create the menu bar and sub-menus. */ void createMenuBar(); /** Create the toolbars */ void createToolBars(); /** Create system tray icon and notification */ void createTrayIcon(const NetworkStyle *networkStyle); /** Create system tray menu (or setup the dock menu) */ void createTrayIconMenu(); /** Enable or disable all wallet-related actions */ void setWalletActionsEnabled(bool enabled); /** Connect core signals to GUI client */ void subscribeToCoreSignals(); /** Disconnect core signals from GUI client */ void unsubscribeFromCoreSignals(); /** Update UI with latest network info from model. */ void updateNetworkState(); void updateHeadersSyncProgressLabel(); Q_SIGNALS: /** Signal raised when a URI was entered or dragged to the GUI */ void receivedURI(const QString &uri); public Q_SLOTS: /** Set number of connections shown in the UI */ void setNumConnections(int count); /** Set network state shown in the UI */ void setNetworkActive(bool networkActive); /** Set number of blocks and last block date shown in the UI */ void setNumBlocks(int count, const QDateTime &blockDate, double nVerificationProgress, bool headers); /** Notify the user of an event from the core network or transaction handling code. @param[in] title the message box / notification title @param[in] message the displayed text @param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes) @see CClientUIInterface::MessageBoxFlags @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) */ void message(const QString &title, const QString &message, unsigned int style, bool *ret = nullptr); #ifdef ENABLE_WALLET bool setCurrentWallet(const QString &name); /** Set the UI status indicators based on the currently selected wallet. */ void updateWalletStatus(); private: /** Set the encryption status as shown in the UI. @param[in] status current encryption status @see WalletModel::EncryptionStatus */ void setEncryptionStatus(int status); /** Set the hd-enabled status as shown in the UI. @param[in] status current hd enabled status @see WalletModel::EncryptionStatus */ void setHDStatus(int hdEnabled); public Q_SLOTS: bool handlePaymentRequest(const SendCoinsRecipient &recipient); /** Show incoming transaction notification for new transactions. */ void incomingTransaction(const QString &date, int unit, const Amount amount, const QString &type, const QString &address, const QString &label, const QString &walletName); #endif // ENABLE_WALLET private Q_SLOTS: #ifdef ENABLE_WALLET /** Switch to overview (home) page */ void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ void gotoSendCoinsPage(QString addr = ""); /** Show Sign/Verify Message dialog and switch to sign message tab */ void gotoSignMessageTab(QString addr = ""); /** Show Sign/Verify Message dialog and switch to verify message tab */ void gotoVerifyMessageTab(QString addr = ""); /** Show open dialog */ void openClicked(); #endif // ENABLE_WALLET /** Show configuration dialog */ void optionsClicked(); /** Show about dialog */ void aboutClicked(); /** Show debug window */ void showDebugWindow(); /** Show debug window and set focus to the console */ void showDebugWindowActivateConsole(); /** Show help message dialog */ void showHelpMessageClicked(); #ifndef Q_OS_MAC /** Handle tray icon clicked */ void trayIconActivated(QSystemTrayIcon::ActivationReason reason); #endif /** Show window if hidden, unminimize when minimized, rise when obscured or * show if hidden and fToggleHidden is true */ void showNormalIfMinimized(bool fToggleHidden = false); /** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */ void toggleHidden(); /** called by a timer to check if fRequestShutdown has been set **/ void detectShutdown(); /** Show progress dialog e.g. for verifychain */ void showProgress(const QString &title, int nProgress); /** When hideTrayIcon setting is changed in OptionsModel hide or show the * icon accordingly. */ void setTrayIconVisible(bool); /** Toggle networking */ void toggleNetworkActive(); void showModalOverlay(); }; class UnitDisplayStatusBarControl : public QLabel { Q_OBJECT public: explicit UnitDisplayStatusBarControl(const PlatformStyle *platformStyle); /** Lets the control know about the Options Model (and its signals) */ void setOptionsModel(OptionsModel *optionsModel); protected: /** So that it responds to left-button clicks */ void mousePressEvent(QMouseEvent *event) override; private: OptionsModel *optionsModel; QMenu *menu; /** Shows context menu with Display Unit options by the mouse coordinates */ void onDisplayUnitsClicked(const QPoint &point); /** Creates context menu, its actions, and wires up all the relevant signals * for mouse events. */ void createContextMenu(); private Q_SLOTS: /** When Display Units are changed on OptionsModel it will refresh the * display text of the control on the status bar */ void updateDisplayUnit(int newUnits); /** Tells underlying optionsModel to update its current display unit. */ void onMenuSelection(QAction *action); }; #endif // BITCOIN_QT_BITCOINGUI_H