changeset 2640:60b19c042950 draft

Convert UI interface to boost::signals2. - Signals now go directly from the core to WalletModel/ClientModel. - WalletModel subscribes to signals on CWallet: Prepares for multi-wallet support, by no longer assuming an implicit global wallet. - Gets rid of noui.cpp, the few lines that were left are merged into init.cpp - Rename wxXXX message flags to MF_XXX, to make them UI indifferent. - ThreadSafeMessageBox no longer returns the value `4` which was never used, converted to void.
author Wladimir J. van der Laan <laanwj@gmail.com>
date Sun, 06 May 2012 19:40:58 +0200
parents c4ef3fe2ae15
children 8c808b8dcf02
files src/bitcoinrpc.cpp src/init.cpp src/keystore.cpp src/keystore.h src/main.cpp src/makefile.linux-mingw src/makefile.mingw src/makefile.osx src/makefile.unix src/net.cpp src/noui.cpp src/qt/bitcoin.cpp src/qt/clientmodel.cpp src/qt/clientmodel.h src/qt/qtipcserver.cpp src/qt/walletmodel.cpp src/qt/walletmodel.h src/ui_interface.h src/util.cpp src/wallet.cpp src/wallet.h
diffstat 21 files changed, 265 insertions(+), 224 deletions(-) [+]
line wrap: on
line diff
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -435,7 +435,7 @@
             "stop\n"
             "Stop Bitcoin server.");
     // Shutdown will take long enough that the response should get back
-    QueueShutdown();
+    uiInterface.QueueShutdown();
     return "Bitcoin server stopping";
 }
 
@@ -1928,7 +1928,7 @@
     // BDB seems to have a bad habit of writing old data into
     // slack space in .dat files; that is bad if the old data is
     // unencrypted private keys.  So:
-    QueueShutdown();
+    uiInterface.QueueShutdown();
     return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet";
 }
 
@@ -2620,7 +2620,7 @@
             strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
         else if (mapArgs.count("-daemon"))
             strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
-        ThreadSafeMessageBox(strprintf(
+        uiInterface.ThreadSafeMessageBox(strprintf(
             _("%s, you must set a rpcpassword in the configuration file:\n %s\n"
               "It is recommended you use the following random password:\n"
               "rpcuser=bitcoinrpc\n"
@@ -2630,8 +2630,8 @@
                 strWhatAmI.c_str(),
                 GetConfigFile().string().c_str(),
                 EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
-            _("Error"), wxOK | wxMODAL);
-        QueueShutdown();
+            _("Error"), MF_OK | MF_MODAL);
+        uiInterface.QueueShutdown();
         return;
     }
 
@@ -2650,9 +2650,9 @@
     }
     catch(boost::system::system_error &e)
     {
-        ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()),
-                             _("Error"), wxOK | wxMODAL);
-        QueueShutdown();
+        uiInterface.ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()),
+                             _("Error"), MF_OK | MF_MODAL);
+        uiInterface.QueueShutdown();
         return;
     }
 
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -22,6 +22,7 @@
 using namespace boost;
 
 CWallet* pwalletMain;
+CClientUIInterface uiInterface;
 
 //////////////////////////////////////////////////////////////////////////////
 //
@@ -90,9 +91,33 @@
 // Start
 //
 #if !defined(QT_GUI)
+static int noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style)
+{
+    printf("%s: %s\n", caption.c_str(), message.c_str());
+    fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str());
+    return 4;
+}
+
+static bool noui_ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
+{
+    return true;
+}
+
+static void noui_QueueShutdown()
+{
+    // Without UI, Shutdown can simply be started in a new thread
+    CreateThread(Shutdown, NULL);
+}
+
 int main(int argc, char* argv[])
 {
     bool fRet = false;
+
+    // Connect bitcoind signal handlers
+    uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox);
+    uiInterface.ThreadSafeAskFee.connect(noui_ThreadSafeAskFee);
+    uiInterface.QueueShutdown.connect(noui_QueueShutdown);
+
     fRet = AppInit(argc, argv);
 
     if (fRet && fDaemon)
@@ -160,13 +185,13 @@
 
 bool static InitError(const std::string &str)
 {
-    ThreadSafeMessageBox(str, _("Bitcoin"), wxOK | wxMODAL);
+    uiInterface.ThreadSafeMessageBox(str, _("Bitcoin"), MF_OK|MF_MODAL);
     return false;
 }
 
 bool static InitWarning(const std::string &str)
 {
-    ThreadSafeMessageBox(str, _("Bitcoin"), wxOK | wxICON_EXCLAMATION | wxMODAL);
+    uiInterface.ThreadSafeMessageBox(str, _("Bitcoin"), MF_OK | MF_ICON_EXCLAMATION | MF_MODAL);
     return true;
 }
 
@@ -367,7 +392,7 @@
         fprintf(stdout, "Bitcoin server starting\n");
     int64 nStart;
 
-    InitMessage(_("Loading addresses..."));
+    uiInterface.InitMessage(_("Loading addresses..."));
     printf("Loading addresses...\n");
     nStart = GetTimeMillis();
 
@@ -380,7 +405,7 @@
     printf("Loaded %i addresses from peers.dat  %"PRI64d"ms\n",
            addrman.size(), GetTimeMillis() - nStart);
 
-    InitMessage(_("Loading block index..."));
+    uiInterface.InitMessage(_("Loading block index..."));
     printf("Loading block index...\n");
     nStart = GetTimeMillis();
     if (!LoadBlockIndex())
@@ -406,7 +431,7 @@
         }
     }
 
-    InitMessage(_("Loading wallet..."));
+    uiInterface.InitMessage(_("Loading wallet..."));
     printf("Loading wallet...\n");
     nStart = GetTimeMillis();
     bool fFirstRun;
@@ -474,14 +499,14 @@
     }
     if (pindexBest != pindexRescan)
     {
-        InitMessage(_("Rescanning..."));
+        uiInterface.InitMessage(_("Rescanning..."));
         printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight);
         nStart = GetTimeMillis();
         pwalletMain->ScanForWalletTransactions(pindexRescan, true);
         printf(" rescan      %15"PRI64d"ms\n", GetTimeMillis() - nStart);
     }
 
-    InitMessage(_("Done loading"));
+    uiInterface.InitMessage(_("Done loading"));
     printf("Done loading\n");
 
     //// debug print
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -5,7 +5,6 @@
 
 #include "keystore.h"
 #include "script.h"
-#include "ui_interface.h"
 
 bool CKeyStore::GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char> &vchPubKeyOut) const
 {
@@ -84,7 +83,7 @@
         vMasterKey.clear();
     }
 
-    NotifyKeyStoreStatusChanged(this);
+    NotifyStatusChanged(this);
     return true;
 }
 
@@ -114,7 +113,7 @@
         }
         vMasterKey = vMasterKeyIn;
     }
-    NotifyKeyStoreStatusChanged(this);
+    NotifyStatusChanged(this);
     return true;
 }
 
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -8,6 +8,7 @@
 #include "crypter.h"
 #include "sync.h"
 #include "base58.h"
+#include <boost/signals2/signal.hpp>
 
 class CScript;
 
@@ -174,6 +175,11 @@
             mi++;
         }
     }
+
+    /* Wallet status (encrypted, locked) changed.
+     * Note: Called without locks held.
+     */
+    boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged;
 };
 
 #endif
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -946,7 +946,7 @@
     {
         bnBestInvalidWork = pindexNew->bnChainWork;
         CTxDB().WriteBestInvalidWork(bnBestInvalidWork);
-        NotifyBlocksChanged();
+        uiInterface.NotifyBlocksChanged();
     }
     printf("InvalidChainFound: invalid block=%s  height=%d  work=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, pindexNew->bnChainWork.ToString().c_str());
     printf("InvalidChainFound:  current best=%s  height=%d  work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str());
@@ -1647,7 +1647,7 @@
         hashPrevBestCoinBase = vtx[0].GetHash();
     }
 
-    NotifyBlocksChanged();
+    uiInterface.NotifyBlocksChanged();
     return true;
 }
 
@@ -1858,8 +1858,8 @@
         string strMessage = _("Warning: Disk space is low");
         strMiscWarning = strMessage;
         printf("*** %s\n", strMessage.c_str());
-        ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION | wxMODAL);
-        QueueShutdown();
+        uiInterface.ThreadSafeMessageBox(strMessage, "Bitcoin", MF_OK | MF_ICON_EXCLAMATION | MF_MODAL);
+        uiInterface.QueueShutdown();
         return false;
     }
     return true;
@@ -2204,13 +2204,13 @@
             if (Cancels(alert))
             {
                 printf("cancelling alert %d\n", alert.nID);
-                NotifyAlertChanged((*mi).first, CT_DELETED);
+                uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
                 mapAlerts.erase(mi++);
             }
             else if (!alert.IsInEffect())
             {
                 printf("expiring alert %d\n", alert.nID);
-                NotifyAlertChanged((*mi).first, CT_DELETED);
+                uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
                 mapAlerts.erase(mi++);
             }
             else
@@ -2232,7 +2232,7 @@
         mapAlerts.insert(make_pair(GetHash(), *this));
         // Notify UI if it applies to me
         if(AppliesToMe())
-            NotifyAlertChanged(GetHash(), CT_NEW);
+            uiInterface.NotifyAlertChanged(GetHash(), CT_NEW);
     }
 
     printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
--- a/src/makefile.linux-mingw
+++ b/src/makefile.linux-mingw
@@ -64,8 +64,7 @@
     obj/sync.o \
     obj/util.o \
     obj/wallet.o \
-    obj/walletdb.o \
-    obj/noui.o
+    obj/walletdb.o
 
 all: bitcoind.exe
 
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -61,8 +61,7 @@
     obj/sync.o \
     obj/util.o \
     obj/wallet.o \
-    obj/walletdb.o \
-    obj/noui.o
+    obj/walletdb.o
 
 
 all: bitcoind.exe
--- a/src/makefile.osx
+++ b/src/makefile.osx
@@ -88,8 +88,7 @@
     obj/sync.o \
     obj/util.o \
     obj/wallet.o \
-    obj/walletdb.o \
-    obj/noui.o
+    obj/walletdb.o
 
 ifdef USE_UPNP
 	DEFS += -DUSE_UPNP=$(USE_UPNP)
--- a/src/makefile.unix
+++ b/src/makefile.unix
@@ -108,8 +108,7 @@
     obj/sync.o \
     obj/util.o \
     obj/wallet.o \
-    obj/walletdb.o \
-    obj/noui.o
+    obj/walletdb.o
 
 
 all: bitcoind
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -705,7 +705,7 @@
         if (vNodes.size() != nPrevNodeCount)
         {
             nPrevNodeCount = vNodes.size();
-            NotifyNumConnectionsChanged(vNodes.size());
+            uiInterface.NotifyNumConnectionsChanged(vNodes.size());
         }
 
 
deleted file mode 100644
--- a/src/noui.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2012 The Bitcoin developers
-// Distributed under the MIT/X11 software license, see the accompanying
-// file license.txt or http://www.opensource.org/licenses/mit-license.php.
-#include "ui_interface.h"
-
-#include <string>
-#include "init.h"
-
-int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style)
-{
-    printf("%s: %s\n", caption.c_str(), message.c_str());
-    fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str());
-    return 4;
-}
-
-bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
-{
-    return true;
-}
-
-void InitMessage(const std::string &message)
-{
-}
-
-std::string _(const char* psz)
-{
-    return psz;
-}
-
-void QueueShutdown()
-{
-    // Without UI, Shutdown can simply be started in a new thread
-    CreateThread(Shutdown, NULL);
-}
-
-void NotifyBlocksChanged()
-{
-}
-
-void NotifyKeyStoreStatusChanged(CBasicKeyStore *wallet)
-{
-}
-
-void NotifyAddressBookChanged(CWallet *wallet, const std::string &address, const std::string &label, EntryStatus status)
-{
-}
-
-void NotifyTransactionChanged(CWallet *wallet, const uint256 &hashTx, EntryStatus status)
-{
-}
-
-void NotifyNumConnectionsChanged(int newNumConnections)
-{
-}
-
-void NotifyAlertChanged(const uint256 &hash, EntryStatus status)
-{
-}
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -36,15 +36,13 @@
 // Need a global reference for the notifications to find the GUI
 static BitcoinGUI *guiref;
 static QSplashScreen *splashref;
-static WalletModel *walletmodel;
-static ClientModel *clientmodel;
 
-int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style)
+static void ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style)
 {
     // Message from network thread
     if(guiref)
     {
-        bool modal = (style & wxMODAL);
+        bool modal = (style & MF_MODAL);
         // in case of modal message, use blocking connection to wait for user to click OK
         QMetaObject::invokeMethod(guiref, "error",
                                    modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection,
@@ -57,10 +55,9 @@
         printf("%s: %s\n", caption.c_str(), message.c_str());
         fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str());
     }
-    return 4;
 }
 
-bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
+static bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
 {
     if(!guiref)
         return false;
@@ -75,7 +72,7 @@
     return payFee;
 }
 
-void ThreadSafeHandleURI(const std::string& strURI)
+static void ThreadSafeHandleURI(const std::string& strURI)
 {
     if(!guiref)
         return;
@@ -84,7 +81,7 @@
                                Q_ARG(QString, QString::fromStdString(strURI)));
 }
 
-void InitMessage(const std::string &message)
+static void InitMessage(const std::string &message)
 {
     if(splashref)
     {
@@ -93,7 +90,7 @@
     }
 }
 
-void QueueShutdown()
+static void QueueShutdown()
 {
     QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
 }
@@ -101,66 +98,11 @@
 /*
    Translate string to current locale using Qt.
  */
-std::string _(const char* psz)
+static std::string Translate(const char* psz)
 {
     return QCoreApplication::translate("bitcoin-core", psz).toStdString();
 }
 
-void NotifyBlocksChanged()
-{
-    // This notification is too frequent. Don't trigger a signal.
-    // Don't remove it, though, as it might be useful later.
-}
-
-void NotifyKeyStoreStatusChanged(CBasicKeyStore *wallet)
-{
-    // This currently ignores the wallet argument. When multiple wallet support is implemented, this
-    // parameter should be mapped to a specific WalletModel for that wallet.
-    OutputDebugStringF("NotifyKeyStoreStatusChanged\n");
-    if(walletmodel)
-        QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection);
-}
-
-void NotifyAddressBookChanged(CWallet *wallet, const std::string &address, const std::string &label, ChangeType status)
-{
-    // This currently ignores the wallet argument. When multiple wallet support is implemented, this
-    // parameter should be mapped to a specific WalletModel for that wallet.
-    OutputDebugStringF("NotifyAddressBookChanged %s %s status=%i\n", address.c_str(), label.c_str(), status);
-    if(walletmodel)
-        QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
-                                  Q_ARG(QString, QString::fromStdString(address)),
-                                  Q_ARG(QString, QString::fromStdString(label)),
-                                  Q_ARG(int, status));
-}
-
-void NotifyTransactionChanged(CWallet *wallet, const uint256 &hash, ChangeType status)
-{
-    // This currently ignores the wallet argument. When multiple wallet support is implemented, this
-    // parameter should be mapped to a specific WalletModel for that wallet.
-    OutputDebugStringF("NotifyTransactionChanged %s status=%i\n", hash.GetHex().c_str(), status);
-    if(walletmodel)
-        QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection,
-                                  Q_ARG(QString, QString::fromStdString(hash.GetHex())),
-                                  Q_ARG(int, status));
-}
-
-void NotifyNumConnectionsChanged(int newNumConnections)
-{
-    // Too noisy: OutputDebugStringF("NotifyNumConnectionsChanged %i\n", newNumConnections);
-    if(clientmodel)
-        QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
-                                  Q_ARG(int, newNumConnections));
-}
-
-void NotifyAlertChanged(const uint256 &hash, ChangeType status)
-{
-    OutputDebugStringF("NotifyAlertChanged %s status=%i\n", hash.GetHex().c_str(), status);
-    if(clientmodel)
-        QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection,
-                                  Q_ARG(QString, QString::fromStdString(hash.GetHex())),
-                                  Q_ARG(int, status));
-}
-
 /* Handle runaway exceptions. Shows a message box with the problem and quits the program.
  */
 static void handleRunawayException(std::exception *e)
@@ -307,6 +249,14 @@
     if (translator.load(lang_territory, ":/translations/"))
         app.installTranslator(&translator);
 
+    // Subscribe to global signals from core
+    uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox);
+    uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee);
+    uiInterface.ThreadSafeHandleURI.connect(ThreadSafeHandleURI);
+    uiInterface.InitMessage.connect(InitMessage);
+    uiInterface.QueueShutdown.connect(QueueShutdown);
+    uiInterface.Translate.connect(Translate);
+
     // Show help message immediately after parsing command-line options (for "-lang") and setting locale,
     // but before showing splash screen.
     if (mapArgs.count("-?") || mapArgs.count("--help"))
@@ -348,9 +298,7 @@
                     splash.finish(&window);
 
                 ClientModel clientModel(&optionsModel);
-                clientmodel = &clientModel;
                 WalletModel walletModel(pwalletMain, &optionsModel);
-                walletmodel = &walletModel;
 
                 window.setClientModel(&clientModel);
                 window.setWalletModel(&walletModel);
@@ -392,8 +340,6 @@
                 window.setClientModel(0);
                 window.setWalletModel(0);
                 guiref = 0;
-                clientmodel = 0;
-                walletmodel = 0;
             }
             Shutdown(NULL);
         }
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -22,6 +22,13 @@
     pollTimer->setInterval(MODEL_UPDATE_DELAY);
     pollTimer->start();
     connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
+
+    subscribeToCoreSignals();
+}
+
+ClientModel::~ClientModel()
+{
+    unsubscribeFromCoreSignals();
 }
 
 int ClientModel::getNumConnections() const
@@ -127,3 +134,41 @@
 {
     return QDateTime::fromTime_t(nClientStartupTime);
 }
+
+// Handlers for core signals
+static void NotifyBlocksChanged(ClientModel *clientmodel)
+{
+    // This notification is too frequent. Don't trigger a signal.
+    // Don't remove it, though, as it might be useful later.
+}
+
+static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
+{
+    // Too noisy: OutputDebugStringF("NotifyNumConnectionsChanged %i\n", newNumConnections);
+    QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
+                              Q_ARG(int, newNumConnections));
+}
+
+static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, ChangeType status)
+{
+    OutputDebugStringF("NotifyAlertChanged %s status=%i\n", hash.GetHex().c_str(), status);
+    QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection,
+                              Q_ARG(QString, QString::fromStdString(hash.GetHex())),
+                              Q_ARG(int, status));
+}
+
+void ClientModel::subscribeToCoreSignals()
+{
+    // Connect signals to client
+    uiInterface.NotifyBlocksChanged.connect(boost::bind(NotifyBlocksChanged, this));
+    uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
+    uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
+}
+
+void ClientModel::unsubscribeFromCoreSignals()
+{
+    // Disconnect signals from client
+    uiInterface.NotifyBlocksChanged.disconnect(boost::bind(NotifyBlocksChanged, this));
+    uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
+    uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
+}
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -19,6 +19,7 @@
     Q_OBJECT
 public:
     explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0);
+    ~ClientModel();
 
     OptionsModel *getOptionsModel();
 
@@ -52,6 +53,8 @@
 
     QTimer *pollTimer;
 
+    void subscribeToCoreSignals();
+    void unsubscribeFromCoreSignals();
 signals:
     void numConnectionsChanged(int count);
     void numBlocksChanged(int count, int countOfPeers);
--- a/src/qt/qtipcserver.cpp
+++ b/src/qt/qtipcserver.cpp
@@ -31,7 +31,7 @@
         ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(100);
         if(mq->timed_receive(&strBuf, sizeof(strBuf), nSize, nPriority, d))
         {
-            ThreadSafeHandleURI(std::string(strBuf, nSize));
+            uiInterface.ThreadSafeHandleURI(std::string(strBuf, nSize));
             Sleep(1000);
         }
         if (fShutdown)
@@ -69,7 +69,7 @@
             ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(1);
             if(mq->timed_receive(&strBuf, sizeof(strBuf), nSize, nPriority, d))
             {
-                ThreadSafeHandleURI(std::string(strBuf, nSize));
+                uiInterface.ThreadSafeHandleURI(std::string(strBuf, nSize));
             }
             else
                 break;
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -18,6 +18,13 @@
 {
     addressTableModel = new AddressTableModel(wallet, this);
     transactionTableModel = new TransactionTableModel(wallet, this);
+
+    subscribeToCoreSignals();
+}
+
+WalletModel::~WalletModel()
+{
+    unsubscribeFromCoreSignals();
 }
 
 qint64 WalletModel::getBalance() const
@@ -147,7 +154,7 @@
             }
             return TransactionCreationFailed;
         }
-        if(!ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString()))
+        if(!uiInterface.ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString()))
         {
             return Aborted;
         }
@@ -254,6 +261,46 @@
     return BackupWallet(*wallet, filename.toLocal8Bit().data());
 }
 
+// Handlers for core signals
+static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet)
+{
+    OutputDebugStringF("NotifyKeyStoreStatusChanged\n");
+    QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection);
+}
+
+static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, const std::string &address, const std::string &label, ChangeType status)
+{
+    OutputDebugStringF("NotifyAddressBookChanged %s %s status=%i\n", address.c_str(), label.c_str(), status);
+    QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
+                              Q_ARG(QString, QString::fromStdString(address)),
+                              Q_ARG(QString, QString::fromStdString(label)),
+                              Q_ARG(int, status));
+}
+
+static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status)
+{
+    OutputDebugStringF("NotifyTransactionChanged %s status=%i\n", hash.GetHex().c_str(), status);
+    QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection,
+                              Q_ARG(QString, QString::fromStdString(hash.GetHex())),
+                              Q_ARG(int, status));
+}
+
+void WalletModel::subscribeToCoreSignals()
+{
+    // Connect signals to wallet
+    wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
+    wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4));
+    wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
+}
+
+void WalletModel::unsubscribeFromCoreSignals()
+{
+    // Disconnect signals from wallet
+    wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
+    wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4));
+    wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
+}
+
 // WalletModel::UnlockContext implementation
 WalletModel::UnlockContext WalletModel::requestUnlock()
 {
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -24,6 +24,7 @@
     Q_OBJECT
 public:
     explicit WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0);
+    ~WalletModel();
 
     enum StatusCode // Returned by sendCoins
     {
@@ -118,6 +119,8 @@
     qint64 cachedNumTransactions;
     EncryptionStatus cachedEncryptionStatus;
 
+    void subscribeToCoreSignals();
+    void unsubscribeFromCoreSignals();
 signals:
     // Signal that balance in wallet changed
     void balanceChanged(qint64 balance, qint64 unconfirmedBalance);
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -6,40 +6,47 @@
 
 #include <string>
 #include "util.h" // for int64
+#include <boost/signals2/signal.hpp>
+#include <boost/signals2/last_value.hpp>
 
 class CBasicKeyStore;
 class CWallet;
 class uint256;
 
-#define wxYES                   0x00000002
-#define wxOK                    0x00000004
-#define wxNO                    0x00000008
-#define wxYES_NO                (wxYES|wxNO)
-#define wxCANCEL                0x00000010
-#define wxAPPLY                 0x00000020
-#define wxCLOSE                 0x00000040
-#define wxOK_DEFAULT            0x00000000
-#define wxYES_DEFAULT           0x00000000
-#define wxNO_DEFAULT            0x00000080
-#define wxCANCEL_DEFAULT        0x80000000
-#define wxICON_EXCLAMATION      0x00000100
-#define wxICON_HAND             0x00000200
-#define wxICON_WARNING          wxICON_EXCLAMATION
-#define wxICON_ERROR            wxICON_HAND
-#define wxICON_QUESTION         0x00000400
-#define wxICON_INFORMATION      0x00000800
-#define wxICON_STOP             wxICON_HAND
-#define wxICON_ASTERISK         wxICON_INFORMATION
-#define wxICON_MASK             (0x00000100|0x00000200|0x00000400|0x00000800)
-#define wxFORWARD               0x00001000
-#define wxBACKWARD              0x00002000
-#define wxRESET                 0x00004000
-#define wxHELP                  0x00008000
-#define wxMORE                  0x00010000
-#define wxSETUP                 0x00020000
-// Force blocking, modal message box dialog (not just notification)
-#define wxMODAL                 0x00040000
+/** Flags for CClientUIInterface::ThreadSafeMessageBox */
+enum MessageBoxFlags
+{
+    MF_YES                   = 0x00000002,
+    MF_OK                    = 0x00000004,
+    MF_NO                    = 0x00000008,
+    MF_YES_NO                = (MF_YES|MF_NO),
+    MF_CANCEL                = 0x00000010,
+    MF_APPLY                 = 0x00000020,
+    MF_CLOSE                 = 0x00000040,
+    MF_OK_DEFAULT            = 0x00000000,
+    MF_YES_DEFAULT           = 0x00000000,
+    MF_NO_DEFAULT            = 0x00000080,
+    MF_CANCEL_DEFAULT        = 0x80000000,
+    MF_ICON_EXCLAMATION      = 0x00000100,
+    MF_ICON_HAND             = 0x00000200,
+    MF_ICON_WARNING          = MF_ICON_EXCLAMATION,
+    MF_ICON_ERROR            = MF_ICON_HAND,
+    MF_ICON_QUESTION         = 0x00000400,
+    MF_ICON_INFORMATION      = 0x00000800,
+    MF_ICON_STOP             = MF_ICON_HAND,
+    MF_ICON_ASTERISK         = MF_ICON_INFORMATION,
+    MF_ICON_MASK             = (0x00000100|0x00000200|0x00000400|0x00000800),
+    MF_FORWARD               = 0x00001000,
+    MF_BACKWARD              = 0x00002000,
+    MF_RESET                 = 0x00004000,
+    MF_HELP                  = 0x00008000,
+    MF_MORE                  = 0x00010000,
+    MF_SETUP                 = 0x00020000,
+// Force blocking, modal message box dialog (not just OS notification)
+    MF_MODAL                 = 0x00040000
+};
 
+/** General change type (added, updated, removed). */
 enum ChangeType
 {
     CT_NEW,
@@ -47,39 +54,51 @@
     CT_DELETED
 };
 
-/* These UI communication functions are implemented in bitcoin.cpp (for ui) and noui.cpp (no ui) */
+/** Signals for UI communication. */
+class CClientUIInterface
+{
+public:
+    /** Show message box. */
+    boost::signals2::signal<void (const std::string& message, const std::string& caption, int style)> ThreadSafeMessageBox;
+
+    /** Ask the user whether he want to pay a fee or not. */
+    boost::signals2::signal<bool (int64 nFeeRequired, const std::string& strCaption), boost::signals2::last_value<bool> > ThreadSafeAskFee;
 
-extern int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK);
-extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption);
-extern void ThreadSafeHandleURI(const std::string& strURI);
-extern void QueueShutdown();
-extern void InitMessage(const std::string &message);
-extern std::string _(const char* psz);
+    /** Handle an URL passed on the command line. */
+    boost::signals2::signal<void (const std::string& strURI)> ThreadSafeHandleURI;
+
+    /** Progress message during initialization. */
+    boost::signals2::signal<void (const std::string &message)> InitMessage;
 
-/* Block chain changed. */
-extern void NotifyBlocksChanged();
+    /** Initiate client shutdown. */
+    boost::signals2::signal<void ()> QueueShutdown;
 
-/* Wallet status (encrypted, locked) changed.
- * Note: Called without locks held.
- */
-extern void NotifyKeyStoreStatusChanged(CBasicKeyStore *wallet);
+    /** Translate a message to the native language of the user. */
+    boost::signals2::signal<std::string (const char* psz)> Translate;
 
-/* Address book entry changed.
- * Note: called with lock cs_wallet held.
- */
-extern void NotifyAddressBookChanged(CWallet *wallet, const std::string &address, const std::string &label, ChangeType status);
+    /** Block chain changed. */
+    boost::signals2::signal<void ()> NotifyBlocksChanged;
+
+    /** Number of network connections changed. */
+    boost::signals2::signal<void (int newNumConnections)> NotifyNumConnectionsChanged;
 
-/* Wallet transaction added, removed or updated.
- * Note: called with lock cs_wallet held.
+    /**
+     * New, updated or cancelled alert.
+     * @note called with lock cs_mapAlerts held.
+     */
+    boost::signals2::signal<void (const uint256 &hash, ChangeType status)> NotifyAlertChanged;
+};
+
+extern CClientUIInterface uiInterface;
+
+/**
+ * Translation function: Call Translate signal on UI interface, which returns a boost::optional result.
+ * If no translation slot is registered, nothing is returned, and simply return the input.
  */
-extern void NotifyTransactionChanged(CWallet *wallet, const uint256 &hashTx, ChangeType status);
-
-/* Number of connections changed. */
-extern void NotifyNumConnectionsChanged(int newNumConnections);
-
-/* New, updated or cancelled alert.
- * Note: called with lock cs_mapAlerts held.
- */
-extern void NotifyAlertChanged(const uint256 &hash, ChangeType status);
+inline std::string _(const char* psz)
+{
+    boost::optional<std::string> rv = uiInterface.Translate(psz);
+    return rv ? (*rv) : psz;
+}
 
 #endif
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -1000,7 +1000,7 @@
                     string strMessage = _("Warning: Please check that your computer's date and time are correct.  If your clock is wrong Bitcoin will not work properly.");
                     strMiscWarning = strMessage;
                     printf("*** %s\n", strMessage.c_str());
-                    ThreadSafeMessageBox(strMessage+" ", string("Bitcoin"), wxOK | wxICON_EXCLAMATION);
+                    uiInterface.ThreadSafeMessageBox(strMessage+" ", string("Bitcoin"), MF_OK | MF_ICON_EXCLAMATION);
                 }
             }
         }
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -276,7 +276,7 @@
         CDB::Rewrite(strWalletFile);
 
     }
-    NotifyKeyStoreStatusChanged(this);
+    NotifyStatusChanged(this);
 
     return true;
 }
@@ -1229,7 +1229,7 @@
         return strError;
     }
 
-    if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending...")))
+    if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired, _("Sending...")))
         return "ABORTED";
 
     if (!CommitTransaction(wtxNew, reservekey))
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -9,6 +9,7 @@
 #include "key.h"
 #include "keystore.h"
 #include "script.h"
+#include "ui_interface.h"
 
 class CWalletTx;
 class CReserveKey;
@@ -261,6 +262,16 @@
 
     // get the current wallet format (the oldest client version guaranteed to understand this wallet)
     int GetVersion() { return nWalletVersion; }
+
+    /** Address book entry changed.
+     * @note called with lock cs_wallet held.
+     */
+    boost::signals2::signal<void (CWallet *wallet, const std::string &address, const std::string &label, ChangeType status)> NotifyAddressBookChanged;
+
+    /** Wallet transaction added, removed or updated.
+     * @note called with lock cs_wallet held.
+     */
+    boost::signals2::signal<void (CWallet *wallet, const uint256 &hashTx, ChangeType status)> NotifyTransactionChanged;
 };
 
 /** A key allocated from the key pool. */