diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index 493775163..761287125 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -1,185 +1,173 @@ // Copyright (c) 2019 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 WalletController::WalletController(interfaces::Node &node, const PlatformStyle *platform_style, OptionsModel *options_model, QObject *parent) : QObject(parent), m_node(node), m_platform_style(platform_style), m_options_model(options_model) { m_handler_load_wallet = m_node.handleLoadWallet( [this](std::unique_ptr wallet) { getOrCreateWallet(std::move(wallet)); }); for (std::unique_ptr &wallet : m_node.getWallets()) { getOrCreateWallet(std::move(wallet)); } m_activity_thread.start(); } // Not using the default destructor because not all member types definitions are // available in the header, just forward declared. WalletController::~WalletController() { m_activity_thread.quit(); m_activity_thread.wait(); } std::vector WalletController::getOpenWallets() const { QMutexLocker locker(&m_mutex); return m_wallets; } std::map WalletController::listWalletDir() const { QMutexLocker locker(&m_mutex); std::map wallets; for (const std::string &name : m_node.listWalletDir()) { wallets[name] = false; } for (WalletModel *wallet_model : m_wallets) { auto it = wallets.find(wallet_model->wallet().getWalletName()); if (it != wallets.end()) { it->second = true; } } return wallets; } OpenWalletActivity *WalletController::openWallet(const CChainParams ¶ms, const std::string &name, QWidget *parent) { OpenWalletActivity *activity = new OpenWalletActivity(this, name, params); activity->moveToThread(&m_activity_thread); return activity; } void WalletController::closeWallet(WalletModel *wallet_model, QWidget *parent) { QMessageBox box(parent); box.setWindowTitle(tr("Close wallet")); box.setText(tr("Are you sure you wish to close wallet %1?") .arg(wallet_model->getDisplayName())); box.setInformativeText( tr("Closing the wallet for too long can result in having to resync the " "entire chain if pruning is enabled.")); box.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); box.setDefaultButton(QMessageBox::Yes); if (box.exec() != QMessageBox::Yes) { return; } // First remove wallet from node. wallet_model->wallet().remove(); // Now release the model. removeAndDeleteWallet(wallet_model); } WalletModel *WalletController::getOrCreateWallet( std::unique_ptr wallet) { QMutexLocker locker(&m_mutex); // Return model instance if exists. if (!m_wallets.empty()) { std::string name = wallet->getWalletName(); for (WalletModel *wallet_model : m_wallets) { if (wallet_model->wallet().getWalletName() == name) { return wallet_model; } } } // Instantiate model and register it. WalletModel *wallet_model = new WalletModel( std::move(wallet), m_node, m_platform_style, m_options_model, nullptr); + // Handler callback runs in a different thread so fix wallet model thread + // affinity. + wallet_model->moveToThread(thread()); + wallet_model->setParent(this); m_wallets.push_back(wallet_model); connect(wallet_model, &WalletModel::unload, [this, wallet_model] { // Defer removeAndDeleteWallet when no modal widget is active. // TODO: remove this workaround by removing usage of QDiallog::exec. if (QApplication::activeModalWidget()) { connect( qApp, &QApplication::focusWindowChanged, wallet_model, [this, wallet_model]() { if (!QApplication::activeModalWidget()) { removeAndDeleteWallet(wallet_model); } }, Qt::QueuedConnection); } else { removeAndDeleteWallet(wallet_model); } }); // Re-emit coinsSent signal from wallet model. connect(wallet_model, &WalletModel::coinsSent, this, &WalletController::coinsSent); // Notify walletAdded signal on the GUI thread. - if (QThread::currentThread() == thread()) { - addWallet(wallet_model); - } else { - // Handler callback runs in a different thread so fix wallet model - // thread affinity. - wallet_model->moveToThread(thread()); - bool invoked = - QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, - Q_ARG(WalletModel *, wallet_model)); - assert(invoked); - } + Q_EMIT walletAdded(wallet_model); return wallet_model; } -void WalletController::addWallet(WalletModel *wallet_model) { - // Take ownership of the wallet model and register it. - wallet_model->setParent(this); - Q_EMIT walletAdded(wallet_model); -} - void WalletController::removeAndDeleteWallet(WalletModel *wallet_model) { // Unregister wallet model. { QMutexLocker locker(&m_mutex); m_wallets.erase( std::remove(m_wallets.begin(), m_wallets.end(), wallet_model)); } Q_EMIT walletRemoved(wallet_model); // Currently this can trigger the unload since the model can hold the last // CWallet shared pointer. delete wallet_model; } OpenWalletActivity::OpenWalletActivity(WalletController *wallet_controller, const std::string &name, const CChainParams ¶ms) : m_wallet_controller(wallet_controller), m_name(name), m_chain_params(params) {} void OpenWalletActivity::open() { std::string error, warning; std::unique_ptr wallet = m_wallet_controller->m_node.loadWallet(m_chain_params, m_name, error, warning); if (!warning.empty()) { Q_EMIT message(QMessageBox::Warning, QString::fromStdString(warning)); } if (wallet) { Q_EMIT opened( m_wallet_controller->getOrCreateWallet(std::move(wallet))); } else { Q_EMIT message(QMessageBox::Critical, QString::fromStdString(error)); } Q_EMIT finished(); } diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h index b79674905..03a0cfb29 100644 --- a/src/qt/walletcontroller.h +++ b/src/qt/walletcontroller.h @@ -1,100 +1,97 @@ // Copyright (c) 2019 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_WALLETCONTROLLER_H #define BITCOIN_QT_WALLETCONTROLLER_H #include #include #include #include #include #include #include #include class OptionsModel; class PlatformStyle; namespace interfaces { class Handler; class Node; } // namespace interfaces class OpenWalletActivity; /** * Controller between interfaces::Node, WalletModel instances and the GUI. */ class WalletController : public QObject { Q_OBJECT WalletModel *getOrCreateWallet(std::unique_ptr wallet); void removeAndDeleteWallet(WalletModel *wallet_model); public: WalletController(interfaces::Node &node, const PlatformStyle *platform_style, OptionsModel *options_model, QObject *parent); ~WalletController(); //! Returns wallet models currently open. std::vector getOpenWallets() const; //! Returns all wallet names in the wallet dir mapped to whether the wallet //! is loaded. std::map listWalletDir() const; OpenWalletActivity *openWallet(const CChainParams ¶ms, const std::string &name, QWidget *parent = nullptr); void closeWallet(WalletModel *wallet_model, QWidget *parent = nullptr); -private Q_SLOTS: - void addWallet(WalletModel *wallet_model); - Q_SIGNALS: void walletAdded(WalletModel *wallet_model); void walletRemoved(WalletModel *wallet_model); void coinsSent(WalletModel *wallet_model, SendCoinsRecipient recipient, QByteArray transaction); private: QThread m_activity_thread; interfaces::Node &m_node; const PlatformStyle *const m_platform_style; OptionsModel *const m_options_model; mutable QMutex m_mutex; std::vector m_wallets; std::unique_ptr m_handler_load_wallet; friend class OpenWalletActivity; }; class OpenWalletActivity : public QObject { Q_OBJECT public: OpenWalletActivity(WalletController *wallet_controller, const std::string &name, const CChainParams ¶ms); public Q_SLOTS: void open(); Q_SIGNALS: void message(QMessageBox::Icon icon, const QString text); void finished(); void opened(WalletModel *wallet_model); private: WalletController *const m_wallet_controller; std::string const m_name; const CChainParams &m_chain_params; }; #endif // BITCOIN_QT_WALLETCONTROLLER_H