diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -9,3 +9,4 @@ GUI if no name is provided by the `-wallet` option on start up. - It is now possible to unload wallets dynamically at runtime. This feature is currently only available through the RPC interface. + - Wallets dynamically unloaded will now be reflected in the gui. diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -228,6 +228,10 @@ // Get default change type. virtual OutputType getDefaultChangeType() = 0; + //! Register handler for unload message. + using UnloadFn = std::function; + virtual std::unique_ptr handleUnload(UnloadFn fn) = 0; + //! Register handler for show progress messages. using ShowProgressFn = std::function; diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -395,6 +395,9 @@ OutputType getDefaultChangeType() override { return m_wallet.m_default_change_type; } + std::unique_ptr handleUnload(UnloadFn fn) override { + return MakeHandler(m_wallet.NotifyUnload.connect(fn)); + } std::unique_ptr handleShowProgress(ShowProgressFn fn) override { return MakeHandler(m_wallet.ShowProgress.connect(fn)); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -230,6 +230,7 @@ /// quits the program. void handleRunawayException(const QString &message); void addWallet(WalletModel *walletModel); + void removeWallet(); Q_SIGNALS: void requestedInitialize(Config *config, RPCServer *rpcServer, @@ -468,11 +469,22 @@ paymentServer, SLOT(fetchPaymentACK(WalletModel *, const SendCoinsRecipient &, QByteArray))); + connect(walletModel, SIGNAL(unload()), this, SLOT(removeWallet())); m_wallet_models.push_back(walletModel); #endif } +void BitcoinApplication::removeWallet() { +#ifdef ENABLE_WALLET + WalletModel *walletModel = static_cast(sender()); + m_wallet_models.erase( + std::find(m_wallet_models.begin(), m_wallet_models.end(), walletModel)); + window->removeWallet(walletModel); + walletModel->deleteLater(); +#endif +} + void BitcoinApplication::initializeResult(bool success) { qDebug() << __func__ << ": Initialization result: " << success; returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE; @@ -497,11 +509,13 @@ #ifdef ENABLE_WALLET 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))); + WalletModel *wallet_model = + new WalletModel(std::move(wallet), m_node, platformStyle, + optionsModel, nullptr); + // Fix wallet model thread affinity. + wallet_model->moveToThread(thread()); + QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, + Q_ARG(WalletModel *, wallet_model)); }); for (auto &wallet : m_node.getWallets()) { diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -77,6 +77,7 @@ * list of transactions, address book and sending functionality. */ bool addWallet(WalletModel *walletModel); + bool removeWallet(WalletModel *walletModel); void removeAllWallets(); #endif // ENABLE_WALLET bool enableWallet = false; @@ -128,6 +129,8 @@ QAction *openRPCConsoleAction = nullptr; QAction *openAction = nullptr; QAction *showHelpMessageAction = nullptr; + QAction *m_wallet_selector_label_action = nullptr; + QAction *m_wallet_selector_action = nullptr; QLabel *m_wallet_selector_label = nullptr; QComboBox *m_wallet_selector = nullptr; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -505,6 +505,17 @@ m_wallet_selector = new QComboBox(); connect(m_wallet_selector, SIGNAL(currentIndexChanged(int)), this, SLOT(setCurrentWalletBySelectorIndex(int))); + + m_wallet_selector_label = new QLabel(); + m_wallet_selector_label->setText(tr("Wallet:") + " "); + m_wallet_selector_label->setBuddy(m_wallet_selector); + + m_wallet_selector_label_action = + appToolBar->addWidget(m_wallet_selector_label); + m_wallet_selector_action = appToolBar->addWidget(m_wallet_selector); + + m_wallet_selector_label_action->setVisible(false); + m_wallet_selector_action->setVisible(false); #endif } } @@ -587,16 +598,30 @@ setWalletActionsEnabled(true); m_wallet_selector->addItem(display_name, 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); + m_wallet_selector_label_action->setVisible(true); + m_wallet_selector_action->setVisible(true); } rpcConsole->addWallet(walletModel); return walletFrame->addWallet(walletModel); } +bool BitcoinGUI::removeWallet(WalletModel *walletModel) { + if (!walletFrame) { + return false; + } + QString name = walletModel->getWalletName(); + int index = m_wallet_selector->findData(name); + m_wallet_selector->removeItem(index); + if (m_wallet_selector->count() == 0) { + setWalletActionsEnabled(false); + } else if (m_wallet_selector->count() == 1) { + m_wallet_selector_label_action->setVisible(false); + m_wallet_selector_action->setVisible(false); + } + rpcConsole->removeWallet(walletModel); + return walletFrame->removeWallet(name); +} + bool BitcoinGUI::setCurrentWallet(const QString &name) { if (!walletFrame) return false; return walletFrame->setCurrentWallet(name); diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -57,6 +57,7 @@ void setClientModel(ClientModel *model); void addWallet(WalletModel *const walletModel); + void removeWallet(WalletModel *const walletModel); enum MessageClass { MC_ERROR, MC_DEBUG, CMD_REQUEST, CMD_REPLY, CMD_ERROR }; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -834,6 +834,15 @@ ui->WalletSelectorLabel->setVisible(true); } } + +void RPCConsole::removeWallet(WalletModel *const walletModel) { + const QString name = walletModel->getWalletName(); + ui->WalletSelector->removeItem(ui->WalletSelector->findData(name)); + if (ui->WalletSelector->count() == 2) { + ui->WalletSelector->setVisible(false); + ui->WalletSelectorLabel->setVisible(false); + } +} #endif static QString categoryClass(int category) { diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -224,6 +224,7 @@ private: std::unique_ptr m_wallet; + std::unique_ptr m_handler_unload; std::unique_ptr m_handler_status_changed; std::unique_ptr m_handler_address_book_changed; std::unique_ptr m_handler_transaction_changed; @@ -279,6 +280,9 @@ // Watch-only address added void notifyWatchonlyChanged(bool fHaveWatchonly); + // Signal that wallet is about to be removed + void unload(); + public Q_SLOTS: /** Wallet status might have changed. */ void updateStatus(); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -338,6 +338,11 @@ } // Handlers for core signals +static void NotifyUnload(WalletModel *walletModel) { + qDebug() << "NotifyUnload"; + QMetaObject::invokeMethod(walletModel, "unload", Qt::QueuedConnection); +} + static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel) { qDebug() << "NotifyKeyStoreStatusChanged"; QMetaObject::invokeMethod(walletmodel, "updateStatus", @@ -389,6 +394,7 @@ void WalletModel::subscribeToCoreSignals() { // Connect signals to wallet + m_handler_unload = m_wallet->handleUnload(std::bind(&NotifyUnload, this)); m_handler_status_changed = m_wallet->handleStatusChanged( std::bind(&NotifyKeyStoreStatusChanged, this)); m_handler_address_book_changed = m_wallet->handleAddressBookChanged( @@ -406,6 +412,7 @@ void WalletModel::unsubscribeFromCoreSignals() { // Disconnect signals from wallet + m_handler_unload->disconnect(); m_handler_status_changed->disconnect(); m_handler_address_book_changed->disconnect(); m_handler_transaction_changed->disconnect();