diff --git a/doc/release-notes.md b/doc/release-notes.md
index cc45eb6ec..d7eb55572 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,5 +1,7 @@
Bitcoin ABC version 0.20.5 is now available from:
This release includes the following features and fixes:
+ - Wallets loaded dynamically through the RPC interface may now be displayed in
+ the bitcoin-qt GUI.
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 33196b62f..38b550782 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -1,861 +1,876 @@
// 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
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef ENABLE_WALLET
#include
#include
#endif
#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
#include
// 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.
*/
const std::function G_TRANSLATION_FUN =
[](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(interfaces::Node &node);
public Q_SLOTS:
void initialize(Config *config, RPCServer *rpcServer,
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);
interfaces::Node &m_node;
};
/** Main Bitcoin application object */
class BitcoinApplication : public QApplication {
Q_OBJECT
public:
explicit BitcoinApplication(interfaces::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, RPCServer &rpcServer,
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;
/// Setup platform style
void setupPlatformStyle();
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);
+ void addWallet(WalletModel *walletModel);
Q_SIGNALS:
void requestedInitialize(Config *config, RPCServer *rpcServer,
HTTPRPCRequestProcessor *httpRPCRequestProcessor);
void requestedShutdown();
void stopThread();
void splashFinished(QWidget *window);
private:
QThread *coreThread;
interfaces::Node &m_node;
OptionsModel *optionsModel;
ClientModel *clientModel;
BitcoinGUI *window;
QTimer *pollShutdownTimer;
#ifdef ENABLE_WALLET
PaymentServer *paymentServer;
std::vector m_wallet_models;
+ std::unique_ptr m_handler_load_wallet;
#endif
int returnValue;
const PlatformStyle *platformStyle;
std::unique_ptr shutdownWindow;
void startThread();
};
#include
BitcoinABC::BitcoinABC(interfaces::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 *config, RPCServer *rpcServer,
HTTPRPCRequestProcessor *httpRPCRequestProcessor) {
try {
qDebug() << __func__ << ": Running initialization in thread";
bool rv =
m_node.appInitMain(*config, *rpcServer, *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(interfaces::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), platformStyle(0) {
setQuitOnLastWindowClosed(false);
}
void BitcoinApplication::setupPlatformStyle() {
// 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(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(m_node, 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 *, RPCServer *,
HTTPRPCRequestProcessor *)),
executor,
SLOT(initialize(Config *, RPCServer *, 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, RPCServer &rpcServer,
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, &rpcServer, &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 (const 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::addWallet(WalletModel *walletModel) {
+#ifdef ENABLE_WALLET
+ window->addWallet(walletModel);
+
+ if (m_wallet_models.empty()) {
+ window->setCurrentWallet(walletModel->getWalletName());
+ }
+
+ connect(walletModel,
+ SIGNAL(coinsSent(WalletModel *, SendCoinsRecipient, QByteArray)),
+ paymentServer,
+ SLOT(fetchPaymentACK(WalletModel *, const SendCoinsRecipient &,
+ QByteArray)));
+
+ m_wallet_models.push_back(walletModel);
+#endif
+}
+
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 AppInitMain 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(m_node, optionsModel);
window->setClientModel(clientModel);
#ifdef ENABLE_WALLET
- bool fFirstWallet = true;
- auto wallets = m_node.getWallets();
- for (auto &wallet : wallets) {
- WalletModel *const walletModel = new WalletModel(
- std::move(wallet), m_node, platformStyle, optionsModel);
-
- window->addWallet(walletModel);
- if (fFirstWallet) {
- window->setCurrentWallet(walletModel->getWalletName());
- fFirstWallet = false;
- }
-
- connect(
- walletModel,
- SIGNAL(coinsSent(WalletModel *, SendCoinsRecipient, QByteArray)),
- paymentServer,
- SLOT(fetchPaymentACK(WalletModel *, const SendCoinsRecipient &,
- QByteArray)));
-
- m_wallet_models.push_back(walletModel);
+ m_handler_load_wallet = m_node.handleLoadWallet(
+ [this](std::unique_ptr wallet) {
+ QMetaObject::invokeMethod(
+ this, "addWallet", Qt::QueuedConnection,
+ Q_ARG(WalletModel *,
+ new WalletModel(std::move(wallet), m_node, platformStyle,
+ optionsModel)));
+ });
+
+ for (auto &wallet : m_node.getWallets()) {
+ addWallet(new WalletModel(std::move(wallet), m_node, platformStyle,
+ optionsModel));
}
#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();
}
static void SetupUIArgs() {
#ifdef ENABLE_WALLET
gArgs.AddArg("-allowselfsignedrootcertificates",
strprintf("Allow self signed root certificates (default: %d)",
DEFAULT_SELFSIGNED_ROOTCERTS),
true, OptionsCategory::GUI);
#endif
gArgs.AddArg(
"-choosedatadir",
strprintf(QObject::tr("Choose data directory on startup (default: %d)")
.toStdString(),
DEFAULT_CHOOSE_DATADIR),
false, OptionsCategory::GUI);
gArgs.AddArg(
"-lang=",
QObject::tr(
"Set language, for example \"de_DE\" (default: system locale)")
.toStdString(),
false, OptionsCategory::GUI);
gArgs.AddArg("-min", QObject::tr("Start minimized").toStdString(), false,
OptionsCategory::GUI);
gArgs.AddArg(
"-rootcertificates=",
QObject::tr(
"Set SSL root certificates for payment request (default: -system-)")
.toStdString(),
false, OptionsCategory::GUI);
gArgs.AddArg(
"-splash",
strprintf(QObject::tr("Show splash screen on startup (default: %d)")
.toStdString(),
DEFAULT_SPLASHSCREEN),
false, OptionsCategory::GUI);
gArgs.AddArg(
"-resetguisettings",
QObject::tr("Reset all settings changed in the GUI").toStdString(),
false, OptionsCategory::GUI);
gArgs.AddArg("-uiplatform",
strprintf("Select platform to customize UI for (one of "
"windows, macosx, other; default: %s)",
BitcoinGUI::DEFAULT_UIPLATFORM),
true, OptionsCategory::GUI);
}
#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[]) {
#ifdef WIN32
util::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
SetupEnvironment();
std::unique_ptr node = interfaces::MakeNode();
// Do not refer to data directory yet, this can be overridden by
// Intro::pickDataDirectory
/// 1. 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");
+#ifdef ENABLE_WALLET
+ qRegisterMetaType("WalletModel*");
+#endif
// 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();
/// 2. Parse command-line options. We do this after qt in order to show an
/// error if there are problems parsing these
// Command-line options take precedence:
node->setupServerArgs();
SetupUIArgs();
std::string error;
if (!node->parseParameters(argc, argv, error)) {
QMessageBox::critical(
0, QObject::tr(PACKAGE_NAME),
QObject::tr("Error parsing command line arguments: %1.")
.arg(QString::fromStdString(error)));
return EXIT_FAILURE;
}
// Now that the QApplication is setup and we have parsed our parameters, we
// can set the platform style
app.setupPlatformStyle();
/// 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();
/// 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);
// 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(*node, 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(*node)) {
return EXIT_SUCCESS;
}
/// 6. Determine availability of data and blocks 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;
}
if (!node->readConfigFiles(error)) {
QMessageBox::critical(
0, QObject::tr(PACKAGE_NAME),
QObject::tr("Error: Cannot parse configuration file: %1.")
.arg(QString::fromStdString(error)));
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(*node, 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)) {
// A dialog with detailed error will have been shown by InitError()
return EXIT_FAILURE;
}
app.requestInitialize(config, rpcServer, 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/walletframe.cpp b/src/qt/walletframe.cpp
index 28d57c50b..7d1db87ae 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -1,205 +1,209 @@
// 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.
#include
#include
#include
#include
#include
#include
#include
#include
WalletFrame::WalletFrame(const PlatformStyle *_platformStyle, BitcoinGUI *_gui)
: QFrame(_gui), gui(_gui), platformStyle(_platformStyle) {
// Leave HBox hook for adding a list view later
QHBoxLayout *walletFrameLayout = new QHBoxLayout(this);
setContentsMargins(0, 0, 0, 0);
walletStack = new QStackedWidget(this);
walletFrameLayout->setContentsMargins(0, 0, 0, 0);
walletFrameLayout->addWidget(walletStack);
QLabel *noWallet = new QLabel(tr("No wallet has been loaded."));
noWallet->setAlignment(Qt::AlignCenter);
walletStack->addWidget(noWallet);
}
WalletFrame::~WalletFrame() {}
void WalletFrame::setClientModel(ClientModel *_clientModel) {
this->clientModel = _clientModel;
}
bool WalletFrame::addWallet(WalletModel *walletModel) {
if (!gui || !clientModel || !walletModel) {
return false;
}
const QString name = walletModel->getWalletName();
if (mapWalletViews.count(name) > 0) {
return false;
}
WalletView *walletView = new WalletView(platformStyle, walletModel, this);
walletView->setBitcoinGUI(gui);
walletView->setClientModel(clientModel);
walletView->showOutOfSyncWarning(bOutOfSync);
- /* TODO we should goto the currently selected page once dynamically adding
- * wallets is supported */
- walletView->gotoOverviewPage();
+ WalletView *current_wallet_view = currentWalletView();
+ if (current_wallet_view) {
+ walletView->setCurrentIndex(current_wallet_view->currentIndex());
+ } else {
+ walletView->gotoOverviewPage();
+ }
+
walletStack->addWidget(walletView);
mapWalletViews[name] = walletView;
// Ensure a walletView is able to show the main window
connect(walletView, SIGNAL(showNormalIfMinimized()), gui,
SLOT(showNormalIfMinimized()));
connect(walletView, SIGNAL(outOfSyncWarningClicked()), this,
SLOT(outOfSyncWarningClicked()));
return true;
}
bool WalletFrame::setCurrentWallet(const QString &name) {
if (mapWalletViews.count(name) == 0) {
return false;
}
WalletView *walletView = mapWalletViews.value(name);
walletStack->setCurrentWidget(walletView);
assert(walletView);
walletView->updateEncryptionStatus();
return true;
}
bool WalletFrame::removeWallet(const QString &name) {
if (mapWalletViews.count(name) == 0) {
return false;
}
WalletView *walletView = mapWalletViews.take(name);
walletStack->removeWidget(walletView);
return true;
}
void WalletFrame::removeAllWallets() {
QMap::const_iterator i;
for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) {
walletStack->removeWidget(i.value());
}
mapWalletViews.clear();
}
bool WalletFrame::handlePaymentRequest(const SendCoinsRecipient &recipient) {
WalletView *walletView = currentWalletView();
if (!walletView) {
return false;
}
return walletView->handlePaymentRequest(recipient);
}
void WalletFrame::showOutOfSyncWarning(bool fShow) {
bOutOfSync = fShow;
QMap::const_iterator i;
for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) {
i.value()->showOutOfSyncWarning(fShow);
}
}
void WalletFrame::gotoOverviewPage() {
QMap::const_iterator i;
for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) {
i.value()->gotoOverviewPage();
}
}
void WalletFrame::gotoHistoryPage() {
QMap::const_iterator i;
for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) {
i.value()->gotoHistoryPage();
}
}
void WalletFrame::gotoReceiveCoinsPage() {
QMap::const_iterator i;
for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) {
i.value()->gotoReceiveCoinsPage();
}
}
void WalletFrame::gotoSendCoinsPage(QString addr) {
QMap::const_iterator i;
for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) {
i.value()->gotoSendCoinsPage(addr);
}
}
void WalletFrame::gotoSignMessageTab(QString addr) {
WalletView *walletView = currentWalletView();
if (walletView) {
walletView->gotoSignMessageTab(addr);
}
}
void WalletFrame::gotoVerifyMessageTab(QString addr) {
WalletView *walletView = currentWalletView();
if (walletView) {
walletView->gotoVerifyMessageTab(addr);
}
}
void WalletFrame::encryptWallet(bool status) {
WalletView *walletView = currentWalletView();
if (walletView) {
walletView->encryptWallet(status);
}
}
void WalletFrame::backupWallet() {
WalletView *walletView = currentWalletView();
if (walletView) {
walletView->backupWallet();
}
}
void WalletFrame::changePassphrase() {
WalletView *walletView = currentWalletView();
if (walletView) {
walletView->changePassphrase();
}
}
void WalletFrame::unlockWallet() {
WalletView *walletView = currentWalletView();
if (walletView) {
walletView->unlockWallet();
}
}
void WalletFrame::usedSendingAddresses() {
WalletView *walletView = currentWalletView();
if (walletView) {
walletView->usedSendingAddresses();
}
}
void WalletFrame::usedReceivingAddresses() {
WalletView *walletView = currentWalletView();
if (walletView) {
walletView->usedReceivingAddresses();
}
}
WalletView *WalletFrame::currentWalletView() {
return qobject_cast(walletStack->currentWidget());
}
void WalletFrame::outOfSyncWarningClicked() {
Q_EMIT requestedSyncWarningInfo();
}