diff options
author | Jonas Schnelli <dev@jonasschnelli.ch> | 2019-02-12 08:19:05 -1000 |
---|---|---|
committer | Jonas Schnelli <dev@jonasschnelli.ch> | 2019-02-12 08:20:50 -1000 |
commit | 7d3f255316fc0d45272a38e3fea206105f67dc45 (patch) | |
tree | 9ea3977ad966597372981d790b40650e2f820783 /src | |
parent | d8794a78a887a920276c7124f1c46d69592c6c4e (diff) | |
parent | 1951ea4342db4122032660139248b79a02c574f3 (diff) |
Merge #15153: gui: Add Open Wallet menu
1951ea434 gui: Show indeterminate progress dialog while opening walllet (João Barbosa)
8847cdaaa gui: Add OpenWalletActivity (João Barbosa)
4c8982a88 interfaces: Avoid interface instance if wallet is null (João Barbosa)
be82dea23 gui: Add thread to run background activity in WalletController (João Barbosa)
6c49a55b4 gui: Add Open Wallet menu (João Barbosa)
32a8c6abf gui: Add openWallet and getWalletsAvailableToOpen to WalletController (João Barbosa)
ab288b4e5 interfaces: Add loadWallet to Node (João Barbosa)
17abc0fd5 wallet: Factor out LoadWallet (João Barbosa)
Pull request description:
The *Open Wallet* menu has all the available wallets currently not loaded. The list of the available wallets comes from `listWalletDir`.
In the future the menu can be replaced by a custom dialog.
<img width="674" alt="screenshot 2019-01-12 at 12 17 02" src="https://user-images.githubusercontent.com/3534524/51073166-ac041480-1664-11e9-8302-be81702bc146.png">
Tree-SHA512: ebfd75eee0c8264863748899843afab67dadb7dff21313c11e3cb5b6108d954978dd1f1ae786bc07580c5a771ea4ab38d18c1643c9b9b3683ed53f0f6c582e38
Diffstat (limited to 'src')
-rw-r--r-- | src/dummywallet.cpp | 9 | ||||
-rw-r--r-- | src/interfaces/node.cpp | 5 | ||||
-rw-r--r-- | src/interfaces/node.h | 5 | ||||
-rw-r--r-- | src/interfaces/wallet.cpp | 2 | ||||
-rw-r--r-- | src/qt/bitcoin.cpp | 2 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 37 | ||||
-rw-r--r-- | src/qt/bitcoingui.h | 1 | ||||
-rw-r--r-- | src/qt/walletcontroller.cpp | 49 | ||||
-rw-r--r-- | src/qt/walletcontroller.h | 30 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 16 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 22 | ||||
-rw-r--r-- | src/wallet/wallet.h | 1 |
12 files changed, 163 insertions, 16 deletions
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp index 9211a7596b..8a76021a5b 100644 --- a/src/dummywallet.cpp +++ b/src/dummywallet.cpp @@ -8,6 +8,10 @@ class CWallet; +namespace interfaces { +class Chain; +} + class DummyWalletInit : public WalletInitInterface { public: @@ -43,6 +47,11 @@ std::vector<std::shared_ptr<CWallet>> GetWallets() throw std::logic_error("Wallet function called in non-wallet build."); } +std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning) +{ + throw std::logic_error("Wallet function called in non-wallet build."); +} + namespace interfaces { class Wallet; diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index a2b89c3f90..96bde7e9f2 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -42,6 +42,7 @@ class CWallet; fs::path GetWalletDir(); std::vector<fs::path> ListWalletDir(); std::vector<std::shared_ptr<CWallet>> GetWallets(); +std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning); namespace interfaces { @@ -252,6 +253,10 @@ public: } return wallets; } + std::unique_ptr<Wallet> loadWallet(const std::string& name, std::string& error, std::string& warning) override + { + return MakeWallet(LoadWallet(*m_interfaces.chain, name, error, warning)); + } std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override { return MakeHandler(::uiInterface.InitMessage_connect(fn)); diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 54c2d78338..76b93af234 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -192,6 +192,11 @@ public: //! Return interfaces for accessing wallets (if any). virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0; + //! Attempts to load a wallet from file or directory. + //! The loaded wallet is also notified to handlers previously registered + //! with handleLoadWallet. + virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, std::string& error, std::string& warning) = 0; + //! Register handler for init messages. using InitMessageFn = std::function<void(const std::string& message)>; virtual std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) = 0; diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index 03b47bd3b5..206227b101 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -525,7 +525,7 @@ public: } // namespace -std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return MakeUnique<WalletImpl>(wallet); } +std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? MakeUnique<WalletImpl>(wallet) : nullptr; } std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, std::vector<std::string> wallet_filenames) { diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 85d79ee26c..d6c6fd6e98 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -456,7 +456,7 @@ int GuiMain(int argc, char* argv[]) // IMPORTANT if it is no longer a typedef use the normal variant above qRegisterMetaType< CAmount >("CAmount"); qRegisterMetaType< std::function<void()> >("std::function<void()>"); - + qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon"); /// 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(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index ba7e8c7daf..f7a4bad916 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -334,6 +334,10 @@ void BitcoinGUI::createActions() openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this); openAction->setStatusTip(tr("Open a bitcoin: URI or payment request")); + m_open_wallet_action = new QAction(tr("Open Wallet"), this); + m_open_wallet_action->setMenu(new QMenu(this)); + m_open_wallet_action->setStatusTip(tr("Open a wallet")); + 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))); @@ -361,6 +365,37 @@ void BitcoinGUI::createActions() connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses); connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses); connect(openAction, &QAction::triggered, this, &BitcoinGUI::openClicked); + connect(m_open_wallet_action->menu(), &QMenu::aboutToShow, [this] { + m_open_wallet_action->menu()->clear(); + for (std::string path : m_wallet_controller->getWalletsAvailableToOpen()) { + QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path); + QAction* action = m_open_wallet_action->menu()->addAction(name); + connect(action, &QAction::triggered, [this, name, path] { + OpenWalletActivity* activity = m_wallet_controller->openWallet(path); + + QProgressDialog* dialog = new QProgressDialog(this); + dialog->setLabelText(tr("Opening Wallet <b>%1</b>...").arg(name.toHtmlEscaped())); + dialog->setRange(0, 0); + dialog->setCancelButton(nullptr); + dialog->setWindowModality(Qt::ApplicationModal); + dialog->show(); + + connect(activity, &OpenWalletActivity::message, this, [this] (QMessageBox::Icon icon, QString text) { + QMessageBox box; + box.setIcon(icon); + box.setText(tr("Open Wallet Failed")); + box.setInformativeText(text); + box.setStandardButtons(QMessageBox::Ok); + box.setDefaultButton(QMessageBox::Ok); + connect(this, &QObject::destroyed, &box, &QDialog::accept); + box.exec(); + }); + connect(activity, &OpenWalletActivity::opened, this, &BitcoinGUI::setCurrentWallet); + connect(activity, &OpenWalletActivity::finished, activity, &QObject::deleteLater); + connect(activity, &OpenWalletActivity::finished, dialog, &QObject::deleteLater); + }); + } + }); } #endif // ENABLE_WALLET @@ -382,6 +417,8 @@ void BitcoinGUI::createMenuBar() QMenu *file = appMenuBar->addMenu(tr("&File")); if(walletFrame) { + file->addAction(m_open_wallet_action); + file->addSeparator(); file->addAction(openAction); file->addAction(backupWalletAction); file->addAction(signMessageAction); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index c31cefe603..c226494020 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -147,6 +147,7 @@ private: QAction* openRPCConsoleAction = nullptr; QAction* openAction = nullptr; QAction* showHelpMessageAction = nullptr; + QAction* m_open_wallet_action{nullptr}; QAction* m_wallet_selector_label_action = nullptr; QAction* m_wallet_selector_action = nullptr; diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index df2b7a3f9b..3483c75970 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -9,6 +9,7 @@ #include <algorithm> +#include <QMessageBox> #include <QMutexLocker> #include <QThread> @@ -25,11 +26,17 @@ WalletController::WalletController(interfaces::Node& node, const PlatformStyle* for (std::unique_ptr<interfaces::Wallet>& 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() {} +WalletController::~WalletController() +{ + m_activity_thread.quit(); + m_activity_thread.wait(); +} std::vector<WalletModel*> WalletController::getWallets() const { @@ -37,6 +44,25 @@ std::vector<WalletModel*> WalletController::getWallets() const return m_wallets; } +std::vector<std::string> WalletController::getWalletsAvailableToOpen() const +{ + QMutexLocker locker(&m_mutex); + std::vector<std::string> wallets = m_node.listWalletDir(); + for (WalletModel* wallet_model : m_wallets) { + auto it = std::remove(wallets.begin(), wallets.end(), wallet_model->wallet().getWalletName()); + if (it != wallets.end()) wallets.erase(it); + } + return wallets; +} + +OpenWalletActivity* WalletController::openWallet(const std::string& name, QWidget* parent) +{ + OpenWalletActivity* activity = new OpenWalletActivity(this, name); + activity->moveToThread(&m_activity_thread); + QMetaObject::invokeMethod(activity, "open", Qt::QueuedConnection); + return activity; +} + WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet) { QMutexLocker locker(&m_mutex); @@ -93,3 +119,24 @@ void WalletController::removeAndDeleteWallet(WalletModel* wallet_model) // CWallet shared pointer. delete wallet_model; } + + +OpenWalletActivity::OpenWalletActivity(WalletController* wallet_controller, const std::string& name) + : m_wallet_controller(wallet_controller) + , m_name(name) +{} + +void OpenWalletActivity::open() +{ + std::string error, warning; + std::unique_ptr<interfaces::Wallet> wallet = m_wallet_controller->m_node.loadWallet(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 22b71b07ff..f19c0e1f3d 100644 --- a/src/qt/walletcontroller.h +++ b/src/qt/walletcontroller.h @@ -12,7 +12,9 @@ #include <memory> #include <vector> +#include <QMessageBox> #include <QMutex> +#include <QThread> class OptionsModel; class PlatformStyle; @@ -22,6 +24,8 @@ class Handler; class Node; } // namespace interfaces +class OpenWalletActivity; + /** * Controller between interfaces::Node, WalletModel instances and the GUI. */ @@ -37,6 +41,9 @@ public: ~WalletController(); std::vector<WalletModel*> getWallets() const; + std::vector<std::string> getWalletsAvailableToOpen() const; + + OpenWalletActivity* openWallet(const std::string& name, QWidget* parent = nullptr); private Q_SLOTS: void addWallet(WalletModel* wallet_model); @@ -48,12 +55,35 @@ Q_SIGNALS: 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<WalletModel*> m_wallets; std::unique_ptr<interfaces::Handler> m_handler_load_wallet; + + friend class OpenWalletActivity; +}; + +class OpenWalletActivity : public QObject +{ + Q_OBJECT + +public: + OpenWalletActivity(WalletController* wallet_controller, const std::string& name); + +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; }; #endif // BITCOIN_QT_WALLETCONTROLLER_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9bbbdc6132..a9fbed5423 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2555,7 +2555,6 @@ static UniValue loadwallet(const JSONRPCRequest& request) }.ToString()); WalletLocation location(request.params[0].get_str()); - std::string error; if (!location.Exists()) { throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + location.GetName() + " not found."); @@ -2567,18 +2566,9 @@ static UniValue loadwallet(const JSONRPCRequest& request) } } - std::string warning; - if (!CWallet::Verify(*g_rpc_interfaces->chain, location, false, error, warning)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error); - } - - std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location); - if (!wallet) { - throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed."); - } - AddWallet(wallet); - - wallet->postInitProcess(); + std::string error, warning; + std::shared_ptr<CWallet> const wallet = LoadWallet(*g_rpc_interfaces->chain, location, error, warning); + if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error); UniValue obj(UniValue::VOBJ); obj.pushKV("name", wallet->GetName()); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d38c15220f..063015d1d8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -130,6 +130,28 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet) } } +std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::string& warning) +{ + if (!CWallet::Verify(chain, location, false, error, warning)) { + error = "Wallet file verification failed: " + error; + return nullptr; + } + + std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location); + if (!wallet) { + error = "Wallet loading failed."; + return nullptr; + } + AddWallet(wallet); + wallet->postInitProcess(); + return wallet; +} + +std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning) +{ + return LoadWallet(chain, WalletLocation(name), error, warning); +} + const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index c455b7cdad..5846ac0f3e 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -66,6 +66,7 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet); bool HasWallets(); std::vector<std::shared_ptr<CWallet>> GetWallets(); std::shared_ptr<CWallet> GetWallet(const std::string& name); +std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::string& warning); //! Default for -keypool static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; |