Merge pull request #1205 from laanwj/2012_05_granular_ui_notifications
Finer-grained UI updates, move UI interface to boost::signals
This commit is contained in:
commit
5a8398e55a
32 changed files with 700 additions and 399 deletions
|
@ -435,7 +435,7 @@ Value stop(const Array& params, bool fHelp)
|
||||||
"stop\n"
|
"stop\n"
|
||||||
"Stop Bitcoin server.");
|
"Stop Bitcoin server.");
|
||||||
// Shutdown will take long enough that the response should get back
|
// Shutdown will take long enough that the response should get back
|
||||||
QueueShutdown();
|
uiInterface.QueueShutdown();
|
||||||
return "Bitcoin server stopping";
|
return "Bitcoin server stopping";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1928,7 +1928,7 @@ Value encryptwallet(const Array& params, bool fHelp)
|
||||||
// BDB seems to have a bad habit of writing old data into
|
// 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
|
// slack space in .dat files; that is bad if the old data is
|
||||||
// unencrypted private keys. So:
|
// unencrypted private keys. So:
|
||||||
QueueShutdown();
|
uiInterface.QueueShutdown();
|
||||||
return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet";
|
return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2620,7 +2620,7 @@ void ThreadRPCServer2(void* parg)
|
||||||
strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
|
strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
|
||||||
else if (mapArgs.count("-daemon"))
|
else if (mapArgs.count("-daemon"))
|
||||||
strWhatAmI = strprintf(_("To use the %s option"), "\"-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"
|
_("%s, you must set a rpcpassword in the configuration file:\n %s\n"
|
||||||
"It is recommended you use the following random password:\n"
|
"It is recommended you use the following random password:\n"
|
||||||
"rpcuser=bitcoinrpc\n"
|
"rpcuser=bitcoinrpc\n"
|
||||||
|
@ -2630,8 +2630,8 @@ void ThreadRPCServer2(void* parg)
|
||||||
strWhatAmI.c_str(),
|
strWhatAmI.c_str(),
|
||||||
GetConfigFile().string().c_str(),
|
GetConfigFile().string().c_str(),
|
||||||
EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
|
EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
|
||||||
_("Error"), wxOK | wxMODAL);
|
_("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
|
||||||
QueueShutdown();
|
uiInterface.QueueShutdown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2650,9 +2650,9 @@ void ThreadRPCServer2(void* parg)
|
||||||
}
|
}
|
||||||
catch(boost::system::system_error &e)
|
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()),
|
uiInterface.ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()),
|
||||||
_("Error"), wxOK | wxMODAL);
|
_("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
|
||||||
QueueShutdown();
|
uiInterface.QueueShutdown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
42
src/init.cpp
42
src/init.cpp
|
@ -22,6 +22,7 @@ using namespace std;
|
||||||
using namespace boost;
|
using namespace boost;
|
||||||
|
|
||||||
CWallet* pwalletMain;
|
CWallet* pwalletMain;
|
||||||
|
CClientUIInterface uiInterface;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -90,17 +91,6 @@ void HandleSIGTERM(int)
|
||||||
// Start
|
// Start
|
||||||
//
|
//
|
||||||
#if !defined(QT_GUI)
|
#if !defined(QT_GUI)
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
bool fRet = false;
|
|
||||||
fRet = AppInit(argc, argv);
|
|
||||||
|
|
||||||
if (fRet && fDaemon)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AppInit(int argc, char* argv[])
|
bool AppInit(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
bool fRet = false;
|
bool fRet = false;
|
||||||
|
@ -156,17 +146,33 @@ bool AppInit(int argc, char* argv[])
|
||||||
Shutdown(NULL);
|
Shutdown(NULL);
|
||||||
return fRet;
|
return fRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void noui_connect();
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
bool fRet = false;
|
||||||
|
|
||||||
|
// Connect bitcoind signal handlers
|
||||||
|
noui_connect();
|
||||||
|
|
||||||
|
fRet = AppInit(argc, argv);
|
||||||
|
|
||||||
|
if (fRet && fDaemon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool static InitError(const std::string &str)
|
bool static InitError(const std::string &str)
|
||||||
{
|
{
|
||||||
ThreadSafeMessageBox(str, _("Bitcoin"), wxOK | wxMODAL);
|
uiInterface.ThreadSafeMessageBox(str, _("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::MODAL);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool static InitWarning(const std::string &str)
|
bool static InitWarning(const std::string &str)
|
||||||
{
|
{
|
||||||
ThreadSafeMessageBox(str, _("Bitcoin"), wxOK | wxICON_EXCLAMATION | wxMODAL);
|
uiInterface.ThreadSafeMessageBox(str, _("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,7 +373,7 @@ bool AppInit2()
|
||||||
fprintf(stdout, "Bitcoin server starting\n");
|
fprintf(stdout, "Bitcoin server starting\n");
|
||||||
int64 nStart;
|
int64 nStart;
|
||||||
|
|
||||||
InitMessage(_("Loading addresses..."));
|
uiInterface.InitMessage(_("Loading addresses..."));
|
||||||
printf("Loading addresses...\n");
|
printf("Loading addresses...\n");
|
||||||
nStart = GetTimeMillis();
|
nStart = GetTimeMillis();
|
||||||
|
|
||||||
|
@ -380,7 +386,7 @@ bool AppInit2()
|
||||||
printf("Loaded %i addresses from peers.dat %"PRI64d"ms\n",
|
printf("Loaded %i addresses from peers.dat %"PRI64d"ms\n",
|
||||||
addrman.size(), GetTimeMillis() - nStart);
|
addrman.size(), GetTimeMillis() - nStart);
|
||||||
|
|
||||||
InitMessage(_("Loading block index..."));
|
uiInterface.InitMessage(_("Loading block index..."));
|
||||||
printf("Loading block index...\n");
|
printf("Loading block index...\n");
|
||||||
nStart = GetTimeMillis();
|
nStart = GetTimeMillis();
|
||||||
if (!LoadBlockIndex())
|
if (!LoadBlockIndex())
|
||||||
|
@ -406,7 +412,7 @@ bool AppInit2()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InitMessage(_("Loading wallet..."));
|
uiInterface.InitMessage(_("Loading wallet..."));
|
||||||
printf("Loading wallet...\n");
|
printf("Loading wallet...\n");
|
||||||
nStart = GetTimeMillis();
|
nStart = GetTimeMillis();
|
||||||
bool fFirstRun;
|
bool fFirstRun;
|
||||||
|
@ -474,14 +480,14 @@ bool AppInit2()
|
||||||
}
|
}
|
||||||
if (pindexBest != pindexRescan)
|
if (pindexBest != pindexRescan)
|
||||||
{
|
{
|
||||||
InitMessage(_("Rescanning..."));
|
uiInterface.InitMessage(_("Rescanning..."));
|
||||||
printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight);
|
printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight);
|
||||||
nStart = GetTimeMillis();
|
nStart = GetTimeMillis();
|
||||||
pwalletMain->ScanForWalletTransactions(pindexRescan, true);
|
pwalletMain->ScanForWalletTransactions(pindexRescan, true);
|
||||||
printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart);
|
printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
InitMessage(_("Done loading"));
|
uiInterface.InitMessage(_("Done loading"));
|
||||||
printf("Done loading\n");
|
printf("Done loading\n");
|
||||||
|
|
||||||
//// debug print
|
//// debug print
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
extern CWallet* pwalletMain;
|
extern CWallet* pwalletMain;
|
||||||
|
|
||||||
void Shutdown(void* parg);
|
void Shutdown(void* parg);
|
||||||
bool AppInit(int argc, char* argv[]);
|
|
||||||
bool AppInit2();
|
bool AppInit2();
|
||||||
std::string HelpMessage();
|
std::string HelpMessage();
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,20 @@ bool CCryptoKeyStore::SetCrypted()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CCryptoKeyStore::Lock()
|
||||||
|
{
|
||||||
|
if (!SetCrypted())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
LOCK(cs_KeyStore);
|
||||||
|
vMasterKey.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
NotifyStatusChanged(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
|
bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
@ -99,6 +113,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
|
||||||
}
|
}
|
||||||
vMasterKey = vMasterKeyIn;
|
vMasterKey = vMasterKeyIn;
|
||||||
}
|
}
|
||||||
|
NotifyStatusChanged(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "crypter.h"
|
#include "crypter.h"
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
|
#include <boost/signals2/signal.hpp>
|
||||||
|
|
||||||
class CScript;
|
class CScript;
|
||||||
|
|
||||||
|
@ -143,18 +144,7 @@ public:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Lock()
|
bool Lock();
|
||||||
{
|
|
||||||
if (!SetCrypted())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
{
|
|
||||||
LOCK(cs_KeyStore);
|
|
||||||
vMasterKey.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
|
virtual bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
|
||||||
bool AddKey(const CKey& key);
|
bool AddKey(const CKey& key);
|
||||||
|
@ -185,6 +175,11 @@ public:
|
||||||
mi++;
|
mi++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wallet status (encrypted, locked) changed.
|
||||||
|
* Note: Called without locks held.
|
||||||
|
*/
|
||||||
|
boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
26
src/main.cpp
26
src/main.cpp
|
@ -946,7 +946,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
|
||||||
{
|
{
|
||||||
bnBestInvalidWork = pindexNew->bnChainWork;
|
bnBestInvalidWork = pindexNew->bnChainWork;
|
||||||
CTxDB().WriteBestInvalidWork(bnBestInvalidWork);
|
CTxDB().WriteBestInvalidWork(bnBestInvalidWork);
|
||||||
MainFrameRepaint();
|
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: 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());
|
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 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
|
||||||
hashPrevBestCoinBase = vtx[0].GetHash();
|
hashPrevBestCoinBase = vtx[0].GetHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
MainFrameRepaint();
|
uiInterface.NotifyBlocksChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1858,8 +1858,8 @@ bool CheckDiskSpace(uint64 nAdditionalBytes)
|
||||||
string strMessage = _("Warning: Disk space is low");
|
string strMessage = _("Warning: Disk space is low");
|
||||||
strMiscWarning = strMessage;
|
strMiscWarning = strMessage;
|
||||||
printf("*** %s\n", strMessage.c_str());
|
printf("*** %s\n", strMessage.c_str());
|
||||||
ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION | wxMODAL);
|
uiInterface.ThreadSafeMessageBox(strMessage, "Bitcoin", CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL);
|
||||||
QueueShutdown();
|
uiInterface.QueueShutdown();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -2176,6 +2176,18 @@ string GetWarnings(string strFor)
|
||||||
return "error";
|
return "error";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CAlert CAlert::getAlertByHash(const uint256 &hash)
|
||||||
|
{
|
||||||
|
CAlert retval;
|
||||||
|
{
|
||||||
|
LOCK(cs_mapAlerts);
|
||||||
|
map<uint256, CAlert>::iterator mi = mapAlerts.find(hash);
|
||||||
|
if(mi != mapAlerts.end())
|
||||||
|
retval = mi->second;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
bool CAlert::ProcessAlert()
|
bool CAlert::ProcessAlert()
|
||||||
{
|
{
|
||||||
if (!CheckSignature())
|
if (!CheckSignature())
|
||||||
|
@ -2192,11 +2204,13 @@ bool CAlert::ProcessAlert()
|
||||||
if (Cancels(alert))
|
if (Cancels(alert))
|
||||||
{
|
{
|
||||||
printf("cancelling alert %d\n", alert.nID);
|
printf("cancelling alert %d\n", alert.nID);
|
||||||
|
uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
|
||||||
mapAlerts.erase(mi++);
|
mapAlerts.erase(mi++);
|
||||||
}
|
}
|
||||||
else if (!alert.IsInEffect())
|
else if (!alert.IsInEffect())
|
||||||
{
|
{
|
||||||
printf("expiring alert %d\n", alert.nID);
|
printf("expiring alert %d\n", alert.nID);
|
||||||
|
uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
|
||||||
mapAlerts.erase(mi++);
|
mapAlerts.erase(mi++);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2216,10 +2230,12 @@ bool CAlert::ProcessAlert()
|
||||||
|
|
||||||
// Add to mapAlerts
|
// Add to mapAlerts
|
||||||
mapAlerts.insert(make_pair(GetHash(), *this));
|
mapAlerts.insert(make_pair(GetHash(), *this));
|
||||||
|
// Notify UI if it applies to me
|
||||||
|
if(AppliesToMe())
|
||||||
|
uiInterface.NotifyAlertChanged(GetHash(), CT_NEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
|
printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
|
||||||
MainFrameRepaint();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1574,6 +1574,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProcessAlert();
|
bool ProcessAlert();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get copy of (active) alert object by hash. Returns a null alert if it is not found.
|
||||||
|
*/
|
||||||
|
static CAlert getAlertByHash(const uint256 &hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CTxMemPool
|
class CTxMemPool
|
||||||
|
|
|
@ -705,7 +705,7 @@ void ThreadSocketHandler2(void* parg)
|
||||||
if (vNodes.size() != nPrevNodeCount)
|
if (vNodes.size() != nPrevNodeCount)
|
||||||
{
|
{
|
||||||
nPrevNodeCount = vNodes.size();
|
nPrevNodeCount = vNodes.size();
|
||||||
MainFrameRepaint();
|
uiInterface.NotifyNumConnectionsChanged(vNodes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
33
src/noui.cpp
33
src/noui.cpp
|
@ -3,42 +3,33 @@
|
||||||
// Distributed under the MIT/X11 software license, see the accompanying
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||||
#include "ui_interface.h"
|
#include "ui_interface.h"
|
||||||
|
#include "init.h"
|
||||||
|
#include "bitcoinrpc.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "init.h"
|
|
||||||
|
|
||||||
int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style)
|
static int noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style)
|
||||||
{
|
{
|
||||||
printf("%s: %s\n", caption.c_str(), message.c_str());
|
printf("%s: %s\n", caption.c_str(), message.c_str());
|
||||||
fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str());
|
fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str());
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
|
static bool noui_ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainFrameRepaint()
|
static void noui_QueueShutdown()
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBookRepaint()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// Without UI, Shutdown can simply be started in a new thread
|
||||||
CreateThread(Shutdown, NULL);
|
CreateThread(Shutdown, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void noui_connect()
|
||||||
|
{
|
||||||
|
// Connect bitcoind signal handlers
|
||||||
|
uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox);
|
||||||
|
uiInterface.ThreadSafeAskFee.connect(noui_ThreadSafeAskFee);
|
||||||
|
uiInterface.QueueShutdown.connect(noui_QueueShutdown);
|
||||||
|
}
|
||||||
|
|
|
@ -132,6 +132,10 @@ void AddressBookPage::setModel(AddressTableModel *model)
|
||||||
connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
||||||
this, SLOT(selectionChanged()));
|
this, SLOT(selectionChanged()));
|
||||||
|
|
||||||
|
// Select row for newly created address
|
||||||
|
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
|
||||||
|
this, SLOT(selectNewAddress(QModelIndex,int,int)));
|
||||||
|
|
||||||
if(mode == ForSending)
|
if(mode == ForSending)
|
||||||
{
|
{
|
||||||
// Auto-select first row when in sending mode
|
// Auto-select first row when in sending mode
|
||||||
|
@ -193,20 +197,11 @@ void AddressBookPage::on_newAddressButton_clicked()
|
||||||
EditAddressDialog dlg(
|
EditAddressDialog dlg(
|
||||||
tab == SendingTab ?
|
tab == SendingTab ?
|
||||||
EditAddressDialog::NewSendingAddress :
|
EditAddressDialog::NewSendingAddress :
|
||||||
EditAddressDialog::NewReceivingAddress);
|
EditAddressDialog::NewReceivingAddress, this);
|
||||||
dlg.setModel(model);
|
dlg.setModel(model);
|
||||||
if(dlg.exec())
|
if(dlg.exec())
|
||||||
{
|
{
|
||||||
// Select row for newly created address
|
newAddressToSelect = dlg.getAddress();
|
||||||
QString address = dlg.getAddress();
|
|
||||||
QModelIndexList lst = proxyModel->match(proxyModel->index(0,
|
|
||||||
AddressTableModel::Address, QModelIndex()),
|
|
||||||
Qt::EditRole, address, 1, Qt::MatchExactly);
|
|
||||||
if(!lst.isEmpty())
|
|
||||||
{
|
|
||||||
ui->tableView->setFocus();
|
|
||||||
ui->tableView->selectRow(lst.at(0).row());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,3 +333,15 @@ void AddressBookPage::contextualMenu(const QPoint &point)
|
||||||
contextMenu->exec(QCursor::pos());
|
contextMenu->exec(QCursor::pos());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int end)
|
||||||
|
{
|
||||||
|
QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
|
||||||
|
if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
|
||||||
|
{
|
||||||
|
// Select row of newly created address, once
|
||||||
|
ui->tableView->setFocus();
|
||||||
|
ui->tableView->selectRow(idx.row());
|
||||||
|
newAddressToSelect.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ class QTableView;
|
||||||
class QItemSelection;
|
class QItemSelection;
|
||||||
class QSortFilterProxyModel;
|
class QSortFilterProxyModel;
|
||||||
class QMenu;
|
class QMenu;
|
||||||
|
class QModelIndex;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
/** Widget that shows a list of sending or receiving addresses.
|
/** Widget that shows a list of sending or receiving addresses.
|
||||||
|
@ -51,6 +52,7 @@ private:
|
||||||
QSortFilterProxyModel *proxyModel;
|
QSortFilterProxyModel *proxyModel;
|
||||||
QMenu *contextMenu;
|
QMenu *contextMenu;
|
||||||
QAction *deleteAction;
|
QAction *deleteAction;
|
||||||
|
QString newAddressToSelect;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_deleteButton_clicked();
|
void on_deleteButton_clicked();
|
||||||
|
@ -67,6 +69,9 @@ private slots:
|
||||||
void onCopyLabelAction();
|
void onCopyLabelAction();
|
||||||
/** Edit currently selected address entry */
|
/** Edit currently selected address entry */
|
||||||
void onEditAction();
|
void onEditAction();
|
||||||
|
|
||||||
|
/** New entry/entries were added to address table */
|
||||||
|
void selectNewAddress(const QModelIndex &parent, int begin, int end);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ADDRESSBOOKDIALOG_H
|
#endif // ADDRESSBOOKDIALOG_H
|
||||||
|
|
|
@ -26,20 +26,36 @@ struct AddressTableEntry
|
||||||
type(type), label(label), address(address) {}
|
type(type), label(label), address(address) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AddressTableEntryLessThan
|
||||||
|
{
|
||||||
|
bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
|
||||||
|
{
|
||||||
|
return a.address < b.address;
|
||||||
|
}
|
||||||
|
bool operator()(const AddressTableEntry &a, const QString &b) const
|
||||||
|
{
|
||||||
|
return a.address < b;
|
||||||
|
}
|
||||||
|
bool operator()(const QString &a, const AddressTableEntry &b) const
|
||||||
|
{
|
||||||
|
return a < b.address;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Private implementation
|
// Private implementation
|
||||||
class AddressTablePriv
|
class AddressTablePriv
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CWallet *wallet;
|
CWallet *wallet;
|
||||||
QList<AddressTableEntry> cachedAddressTable;
|
QList<AddressTableEntry> cachedAddressTable;
|
||||||
|
AddressTableModel *parent;
|
||||||
|
|
||||||
AddressTablePriv(CWallet *wallet):
|
AddressTablePriv(CWallet *wallet, AddressTableModel *parent):
|
||||||
wallet(wallet) {}
|
wallet(wallet), parent(parent) {}
|
||||||
|
|
||||||
void refreshAddressTable()
|
void refreshAddressTable()
|
||||||
{
|
{
|
||||||
cachedAddressTable.clear();
|
cachedAddressTable.clear();
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK(wallet->cs_wallet);
|
LOCK(wallet->cs_wallet);
|
||||||
BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, std::string)& item, wallet->mapAddressBook)
|
BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, std::string)& item, wallet->mapAddressBook)
|
||||||
|
@ -54,6 +70,53 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateEntry(const QString &address, const QString &label, bool isMine, int status)
|
||||||
|
{
|
||||||
|
// Find address / label in model
|
||||||
|
QList<AddressTableEntry>::iterator lower = qLowerBound(
|
||||||
|
cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
|
||||||
|
QList<AddressTableEntry>::iterator upper = qUpperBound(
|
||||||
|
cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
|
||||||
|
int lowerIndex = (lower - cachedAddressTable.begin());
|
||||||
|
int upperIndex = (upper - cachedAddressTable.begin());
|
||||||
|
bool inModel = (lower != upper);
|
||||||
|
AddressTableEntry::Type newEntryType = isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending;
|
||||||
|
|
||||||
|
switch(status)
|
||||||
|
{
|
||||||
|
case CT_NEW:
|
||||||
|
if(inModel)
|
||||||
|
{
|
||||||
|
OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_NOW, but entry is already in model\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
|
||||||
|
cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
|
||||||
|
parent->endInsertRows();
|
||||||
|
break;
|
||||||
|
case CT_UPDATED:
|
||||||
|
if(!inModel)
|
||||||
|
{
|
||||||
|
OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lower->type = newEntryType;
|
||||||
|
lower->label = label;
|
||||||
|
parent->emitDataChanged(lowerIndex);
|
||||||
|
break;
|
||||||
|
case CT_DELETED:
|
||||||
|
if(!inModel)
|
||||||
|
{
|
||||||
|
OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry is not in model\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
|
||||||
|
cachedAddressTable.erase(lower, upper);
|
||||||
|
parent->endRemoveRows();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int size()
|
int size()
|
||||||
{
|
{
|
||||||
return cachedAddressTable.size();
|
return cachedAddressTable.size();
|
||||||
|
@ -76,7 +139,7 @@ AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) :
|
||||||
QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
|
QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
|
||||||
{
|
{
|
||||||
columns << tr("Label") << tr("Address");
|
columns << tr("Label") << tr("Address");
|
||||||
priv = new AddressTablePriv(wallet);
|
priv = new AddressTablePriv(wallet, this);
|
||||||
priv->refreshAddressTable();
|
priv->refreshAddressTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +221,6 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu
|
||||||
{
|
{
|
||||||
case Label:
|
case Label:
|
||||||
wallet->SetAddressBookName(rec->address.toStdString(), value.toString().toStdString());
|
wallet->SetAddressBookName(rec->address.toStdString(), value.toString().toStdString());
|
||||||
rec->label = value.toString();
|
|
||||||
break;
|
break;
|
||||||
case Address:
|
case Address:
|
||||||
// Refuse to set invalid address, set error status and return false
|
// Refuse to set invalid address, set error status and return false
|
||||||
|
@ -177,12 +239,9 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu
|
||||||
// Add new entry with new address
|
// Add new entry with new address
|
||||||
wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString());
|
wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
rec->address = value.toString();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
emit dataChanged(index, index);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -232,12 +291,10 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & pa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddressTableModel::update()
|
void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status)
|
||||||
{
|
{
|
||||||
// Update address book model from Bitcoin core
|
// Update address book model from Bitcoin core
|
||||||
beginResetModel();
|
priv->updateEntry(address, label, isMine, status);
|
||||||
priv->refreshAddressTable();
|
|
||||||
endResetModel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
|
QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
|
||||||
|
@ -341,3 +398,7 @@ int AddressTableModel::lookupAddress(const QString &address) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddressTableModel::emitDataChanged(int idx)
|
||||||
|
{
|
||||||
|
emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
|
||||||
|
}
|
||||||
|
|
|
@ -74,13 +74,18 @@ private:
|
||||||
QStringList columns;
|
QStringList columns;
|
||||||
EditStatus editStatus;
|
EditStatus editStatus;
|
||||||
|
|
||||||
|
/** Notify listeners that data changed. */
|
||||||
|
void emitDataChanged(int index);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void defaultAddressChanged(const QString &address);
|
void defaultAddressChanged(const QString &address);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/* Update address list from core. Invalidates any indices.
|
/* Update address list from core.
|
||||||
*/
|
*/
|
||||||
void update();
|
void updateEntry(const QString &address, const QString &label, bool isMine, int status);
|
||||||
|
|
||||||
|
friend class AddressTablePriv;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ADDRESSTABLEMODEL_H
|
#endif // ADDRESSTABLEMODEL_H
|
||||||
|
|
|
@ -36,15 +36,13 @@ Q_IMPORT_PLUGIN(qtaccessiblewidgets)
|
||||||
// Need a global reference for the notifications to find the GUI
|
// Need a global reference for the notifications to find the GUI
|
||||||
static BitcoinGUI *guiref;
|
static BitcoinGUI *guiref;
|
||||||
static QSplashScreen *splashref;
|
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
|
// Message from network thread
|
||||||
if(guiref)
|
if(guiref)
|
||||||
{
|
{
|
||||||
bool modal = (style & wxMODAL);
|
bool modal = (style & CClientUIInterface::MODAL);
|
||||||
// in case of modal message, use blocking connection to wait for user to click OK
|
// in case of modal message, use blocking connection to wait for user to click OK
|
||||||
QMetaObject::invokeMethod(guiref, "error",
|
QMetaObject::invokeMethod(guiref, "error",
|
||||||
modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection,
|
modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection,
|
||||||
|
@ -57,10 +55,9 @@ int ThreadSafeMessageBox(const std::string& message, const std::string& caption,
|
||||||
printf("%s: %s\n", caption.c_str(), message.c_str());
|
printf("%s: %s\n", caption.c_str(), message.c_str());
|
||||||
fprintf(stderr, "%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)
|
if(!guiref)
|
||||||
return false;
|
return false;
|
||||||
|
@ -75,7 +72,7 @@ bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
|
||||||
return payFee;
|
return payFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadSafeHandleURI(const std::string& strURI)
|
static void ThreadSafeHandleURI(const std::string& strURI)
|
||||||
{
|
{
|
||||||
if(!guiref)
|
if(!guiref)
|
||||||
return;
|
return;
|
||||||
|
@ -84,21 +81,7 @@ void ThreadSafeHandleURI(const std::string& strURI)
|
||||||
Q_ARG(QString, QString::fromStdString(strURI)));
|
Q_ARG(QString, QString::fromStdString(strURI)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainFrameRepaint()
|
static void InitMessage(const std::string &message)
|
||||||
{
|
|
||||||
if(clientmodel)
|
|
||||||
QMetaObject::invokeMethod(clientmodel, "update", Qt::QueuedConnection);
|
|
||||||
if(walletmodel)
|
|
||||||
QMetaObject::invokeMethod(walletmodel, "update", Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBookRepaint()
|
|
||||||
{
|
|
||||||
if(walletmodel)
|
|
||||||
QMetaObject::invokeMethod(walletmodel, "updateAddressList", Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitMessage(const std::string &message)
|
|
||||||
{
|
{
|
||||||
if(splashref)
|
if(splashref)
|
||||||
{
|
{
|
||||||
|
@ -107,7 +90,7 @@ void InitMessage(const std::string &message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QueueShutdown()
|
static void QueueShutdown()
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
@ -115,7 +98,7 @@ void QueueShutdown()
|
||||||
/*
|
/*
|
||||||
Translate string to current locale using Qt.
|
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();
|
return QCoreApplication::translate("bitcoin-core", psz).toStdString();
|
||||||
}
|
}
|
||||||
|
@ -266,6 +249,14 @@ int main(int argc, char *argv[])
|
||||||
if (translator.load(lang_territory, ":/translations/"))
|
if (translator.load(lang_territory, ":/translations/"))
|
||||||
app.installTranslator(&translator);
|
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,
|
// Show help message immediately after parsing command-line options (for "-lang") and setting locale,
|
||||||
// but before showing splash screen.
|
// but before showing splash screen.
|
||||||
if (mapArgs.count("-?") || mapArgs.count("--help"))
|
if (mapArgs.count("-?") || mapArgs.count("--help"))
|
||||||
|
@ -307,9 +298,7 @@ int main(int argc, char *argv[])
|
||||||
splash.finish(&window);
|
splash.finish(&window);
|
||||||
|
|
||||||
ClientModel clientModel(&optionsModel);
|
ClientModel clientModel(&optionsModel);
|
||||||
clientmodel = &clientModel;
|
|
||||||
WalletModel walletModel(pwalletMain, &optionsModel);
|
WalletModel walletModel(pwalletMain, &optionsModel);
|
||||||
walletmodel = &walletModel;
|
|
||||||
|
|
||||||
window.setClientModel(&clientModel);
|
window.setClientModel(&clientModel);
|
||||||
window.setWalletModel(&walletModel);
|
window.setWalletModel(&walletModel);
|
||||||
|
@ -351,8 +340,6 @@ int main(int argc, char *argv[])
|
||||||
window.setClientModel(0);
|
window.setClientModel(0);
|
||||||
window.setWalletModel(0);
|
window.setWalletModel(0);
|
||||||
guiref = 0;
|
guiref = 0;
|
||||||
clientmodel = 0;
|
|
||||||
walletmodel = 0;
|
|
||||||
}
|
}
|
||||||
Shutdown(NULL);
|
Shutdown(NULL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -350,11 +350,11 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
|
||||||
setNumConnections(clientModel->getNumConnections());
|
setNumConnections(clientModel->getNumConnections());
|
||||||
connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
|
connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
|
||||||
|
|
||||||
setNumBlocks(clientModel->getNumBlocks());
|
setNumBlocks(clientModel->getNumBlocks(), clientModel->getNumBlocksOfPeers());
|
||||||
connect(clientModel, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int)));
|
connect(clientModel, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int)));
|
||||||
|
|
||||||
// Report errors from network/worker thread
|
// Report errors from network/worker thread
|
||||||
connect(clientModel, SIGNAL(error(QString,QString, bool)), this, SLOT(error(QString,QString,bool)));
|
connect(clientModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool)));
|
||||||
|
|
||||||
rpcConsole->setClientModel(clientModel);
|
rpcConsole->setClientModel(clientModel);
|
||||||
}
|
}
|
||||||
|
@ -493,7 +493,7 @@ void BitcoinGUI::setNumConnections(int count)
|
||||||
labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
|
labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitcoinGUI::setNumBlocks(int count)
|
void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks)
|
||||||
{
|
{
|
||||||
// don't show / hide progressBar and it's label if we have no connection(s) to the network
|
// don't show / hide progressBar and it's label if we have no connection(s) to the network
|
||||||
if (!clientModel || clientModel->getNumConnections() == 0)
|
if (!clientModel || clientModel->getNumConnections() == 0)
|
||||||
|
@ -504,7 +504,6 @@ void BitcoinGUI::setNumBlocks(int count)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nTotalBlocks = clientModel->getNumBlocksOfPeers();
|
|
||||||
QString tooltip;
|
QString tooltip;
|
||||||
|
|
||||||
if(count < nTotalBlocks)
|
if(count < nTotalBlocks)
|
||||||
|
|
|
@ -111,7 +111,7 @@ public slots:
|
||||||
/** Set number of connections shown in the UI */
|
/** Set number of connections shown in the UI */
|
||||||
void setNumConnections(int count);
|
void setNumConnections(int count);
|
||||||
/** Set number of blocks shown in the UI */
|
/** Set number of blocks shown in the UI */
|
||||||
void setNumBlocks(int count);
|
void setNumBlocks(int count, int countOfPeers);
|
||||||
/** Set the encryption status as shown in the UI.
|
/** Set the encryption status as shown in the UI.
|
||||||
@param[in] status current encryption status
|
@param[in] status current encryption status
|
||||||
@see WalletModel::EncryptionStatus
|
@see WalletModel::EncryptionStatus
|
||||||
|
|
|
@ -5,15 +5,30 @@
|
||||||
#include "transactiontablemodel.h"
|
#include "transactiontablemodel.h"
|
||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
static const int64 nClientStartupTime = GetTime();
|
#include "ui_interface.h"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
static const int64 nClientStartupTime = GetTime();
|
||||||
|
|
||||||
ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
|
ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
|
||||||
QObject(parent), optionsModel(optionsModel),
|
QObject(parent), optionsModel(optionsModel),
|
||||||
cachedNumConnections(0), cachedNumBlocks(0)
|
cachedNumBlocks(0), cachedNumBlocksOfPeers(0), pollTimer(0)
|
||||||
{
|
{
|
||||||
numBlocksAtStartup = -1;
|
numBlocksAtStartup = -1;
|
||||||
|
|
||||||
|
pollTimer = new QTimer();
|
||||||
|
pollTimer->setInterval(MODEL_UPDATE_DELAY);
|
||||||
|
pollTimer->start();
|
||||||
|
connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
|
||||||
|
|
||||||
|
subscribeToCoreSignals();
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientModel::~ClientModel()
|
||||||
|
{
|
||||||
|
unsubscribeFromCoreSignals();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ClientModel::getNumConnections() const
|
int ClientModel::getNumConnections() const
|
||||||
|
@ -37,27 +52,42 @@ QDateTime ClientModel::getLastBlockDate() const
|
||||||
return QDateTime::fromTime_t(pindexBest->GetBlockTime());
|
return QDateTime::fromTime_t(pindexBest->GetBlockTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientModel::update()
|
void ClientModel::updateTimer()
|
||||||
{
|
{
|
||||||
int newNumConnections = getNumConnections();
|
// Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change.
|
||||||
|
// Periodically check and update with a timer.
|
||||||
int newNumBlocks = getNumBlocks();
|
int newNumBlocks = getNumBlocks();
|
||||||
QString newStatusBar = getStatusBarWarnings();
|
int newNumBlocksOfPeers = getNumBlocksOfPeers();
|
||||||
|
|
||||||
if(cachedNumConnections != newNumConnections)
|
if(cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers)
|
||||||
emit numConnectionsChanged(newNumConnections);
|
emit numBlocksChanged(newNumBlocks, newNumBlocksOfPeers);
|
||||||
if(cachedNumBlocks != newNumBlocks || cachedStatusBar != newStatusBar)
|
|
||||||
|
cachedNumBlocks = newNumBlocks;
|
||||||
|
cachedNumBlocksOfPeers = newNumBlocksOfPeers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientModel::updateNumConnections(int numConnections)
|
||||||
|
{
|
||||||
|
emit numConnectionsChanged(numConnections);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientModel::updateAlert(const QString &hash, int status)
|
||||||
|
{
|
||||||
|
// Show error message notification for new alert
|
||||||
|
if(status == CT_NEW)
|
||||||
{
|
{
|
||||||
// Simply emit a numBlocksChanged for now in case the status message changes,
|
uint256 hash_256;
|
||||||
// so that the view updates the status bar.
|
hash_256.SetHex(hash.toStdString());
|
||||||
// TODO: It should send a notification.
|
CAlert alert = CAlert::getAlertByHash(hash_256);
|
||||||
// (However, this might generate looped notifications and needs to be thought through and tested carefully)
|
if(!alert.IsNull())
|
||||||
// error(tr("Network Alert"), newStatusBar);
|
{
|
||||||
emit numBlocksChanged(newNumBlocks);
|
emit error(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedNumConnections = newNumConnections;
|
// Emit a numBlocksChanged when the status message changes,
|
||||||
cachedNumBlocks = newNumBlocks;
|
// so that the view recomputes and updates the status bar.
|
||||||
cachedStatusBar = newStatusBar;
|
emit numBlocksChanged(getNumBlocks(), getNumBlocksOfPeers());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientModel::isTestNet() const
|
bool ClientModel::isTestNet() const
|
||||||
|
@ -104,3 +134,41 @@ QDateTime ClientModel::formatClientStartupTime() const
|
||||||
{
|
{
|
||||||
return QDateTime::fromTime_t(nClientStartupTime);
|
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));
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ class CWallet;
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QDateTime;
|
class QDateTime;
|
||||||
|
class QTimer;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
/** Model for Bitcoin network client. */
|
/** Model for Bitcoin network client. */
|
||||||
|
@ -18,6 +19,7 @@ class ClientModel : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0);
|
explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0);
|
||||||
|
~ClientModel();
|
||||||
|
|
||||||
OptionsModel *getOptionsModel();
|
OptionsModel *getOptionsModel();
|
||||||
|
|
||||||
|
@ -44,23 +46,26 @@ public:
|
||||||
private:
|
private:
|
||||||
OptionsModel *optionsModel;
|
OptionsModel *optionsModel;
|
||||||
|
|
||||||
int cachedNumConnections;
|
|
||||||
int cachedNumBlocks;
|
int cachedNumBlocks;
|
||||||
QString cachedStatusBar;
|
int cachedNumBlocksOfPeers;
|
||||||
|
|
||||||
int numBlocksAtStartup;
|
int numBlocksAtStartup;
|
||||||
|
|
||||||
|
QTimer *pollTimer;
|
||||||
|
|
||||||
|
void subscribeToCoreSignals();
|
||||||
|
void unsubscribeFromCoreSignals();
|
||||||
signals:
|
signals:
|
||||||
void numConnectionsChanged(int count);
|
void numConnectionsChanged(int count);
|
||||||
void numBlocksChanged(int count);
|
void numBlocksChanged(int count, int countOfPeers);
|
||||||
|
|
||||||
//! Asynchronous error notification
|
//! Asynchronous error notification
|
||||||
void error(const QString &title, const QString &message, bool modal);
|
void error(const QString &title, const QString &message, bool modal);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
void updateTimer();
|
||||||
private slots:
|
void updateNumConnections(int numConnections);
|
||||||
void update();
|
void updateAlert(const QString &hash, int status);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CLIENTMODEL_H
|
#endif // CLIENTMODEL_H
|
||||||
|
|
|
@ -31,7 +31,7 @@ void ipcThread(void* parg)
|
||||||
ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(100);
|
ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(100);
|
||||||
if(mq->timed_receive(&strBuf, sizeof(strBuf), nSize, nPriority, d))
|
if(mq->timed_receive(&strBuf, sizeof(strBuf), nSize, nPriority, d))
|
||||||
{
|
{
|
||||||
ThreadSafeHandleURI(std::string(strBuf, nSize));
|
uiInterface.ThreadSafeHandleURI(std::string(strBuf, nSize));
|
||||||
Sleep(1000);
|
Sleep(1000);
|
||||||
}
|
}
|
||||||
if (fShutdown)
|
if (fShutdown)
|
||||||
|
@ -69,7 +69,7 @@ void ipcInit()
|
||||||
ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(1);
|
ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(1);
|
||||||
if(mq->timed_receive(&strBuf, sizeof(strBuf), nSize, nPriority, d))
|
if(mq->timed_receive(&strBuf, sizeof(strBuf), nSize, nPriority, d))
|
||||||
{
|
{
|
||||||
ThreadSafeHandleURI(std::string(strBuf, nSize));
|
uiInterface.ThreadSafeHandleURI(std::string(strBuf, nSize));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -157,7 +157,7 @@ void RPCConsole::setClientModel(ClientModel *model)
|
||||||
{
|
{
|
||||||
// Subscribe to information, replies, messages, errors
|
// Subscribe to information, replies, messages, errors
|
||||||
connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
|
connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
|
||||||
connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int)));
|
connect(model, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int)));
|
||||||
|
|
||||||
// Provide initial values
|
// Provide initial values
|
||||||
ui->clientVersion->setText(model->formatFullVersion());
|
ui->clientVersion->setText(model->formatFullVersion());
|
||||||
|
@ -168,7 +168,7 @@ void RPCConsole::setClientModel(ClientModel *model)
|
||||||
setNumConnections(model->getNumConnections());
|
setNumConnections(model->getNumConnections());
|
||||||
ui->isTestNet->setChecked(model->isTestNet());
|
ui->isTestNet->setChecked(model->isTestNet());
|
||||||
|
|
||||||
setNumBlocks(model->getNumBlocks());
|
setNumBlocks(model->getNumBlocks(), model->getNumBlocksOfPeers());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,9 +235,10 @@ void RPCConsole::setNumConnections(int count)
|
||||||
ui->numberOfConnections->setText(QString::number(count));
|
ui->numberOfConnections->setText(QString::number(count));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPCConsole::setNumBlocks(int count)
|
void RPCConsole::setNumBlocks(int count, int countOfPeers)
|
||||||
{
|
{
|
||||||
ui->numberOfBlocks->setText(QString::number(count));
|
ui->numberOfBlocks->setText(QString::number(count));
|
||||||
|
ui->totalBlocks->setText(QString::number(countOfPeers));
|
||||||
if(clientModel)
|
if(clientModel)
|
||||||
{
|
{
|
||||||
// If there is no current number available display N/A instead of 0, which can't ever be true
|
// If there is no current number available display N/A instead of 0, which can't ever be true
|
||||||
|
|
|
@ -42,7 +42,7 @@ public slots:
|
||||||
/** Set number of connections shown in the UI */
|
/** Set number of connections shown in the UI */
|
||||||
void setNumConnections(int count);
|
void setNumConnections(int count);
|
||||||
/** Set number of blocks shown in the UI */
|
/** Set number of blocks shown in the UI */
|
||||||
void setNumBlocks(int count);
|
void setNumBlocks(int count, int countOfPeers);
|
||||||
/** Go forward or back in history */
|
/** Go forward or back in history */
|
||||||
void browseHistory(int offset);
|
void browseHistory(int offset);
|
||||||
/** Scroll console view to end */
|
/** Scroll console view to end */
|
||||||
|
|
|
@ -40,8 +40,6 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
|
||||||
uint256 hash = wtx.GetHash();
|
uint256 hash = wtx.GetHash();
|
||||||
std::map<std::string, std::string> mapValue = wtx.mapValue;
|
std::map<std::string, std::string> mapValue = wtx.mapValue;
|
||||||
|
|
||||||
if (showTransaction(wtx))
|
|
||||||
{
|
|
||||||
if (nNet > 0 || wtx.IsCoinBase())
|
if (nNet > 0 || wtx.IsCoinBase())
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
|
@ -149,7 +147,6 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
|
||||||
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
|
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "bitcoinunits.h"
|
#include "bitcoinunits.h"
|
||||||
|
|
||||||
#include "wallet.h"
|
#include "wallet.h"
|
||||||
|
#include "ui_interface.h"
|
||||||
|
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
@ -66,14 +67,13 @@ public:
|
||||||
*/
|
*/
|
||||||
void refreshWallet()
|
void refreshWallet()
|
||||||
{
|
{
|
||||||
#ifdef WALLET_UPDATE_DEBUG
|
OutputDebugStringF("refreshWallet\n");
|
||||||
qDebug() << "refreshWallet";
|
|
||||||
#endif
|
|
||||||
cachedWallet.clear();
|
cachedWallet.clear();
|
||||||
{
|
{
|
||||||
LOCK(wallet->cs_wallet);
|
LOCK(wallet->cs_wallet);
|
||||||
for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
|
for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
|
||||||
{
|
{
|
||||||
|
if(TransactionRecord::showTransaction(it->second))
|
||||||
cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second));
|
cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,28 +82,18 @@ public:
|
||||||
/* Update our model of the wallet incrementally, to synchronize our model of the wallet
|
/* Update our model of the wallet incrementally, to synchronize our model of the wallet
|
||||||
with that of the core.
|
with that of the core.
|
||||||
|
|
||||||
Call with list of hashes of transactions that were added, removed or changed.
|
Call with transaction that was added, removed or changed.
|
||||||
*/
|
*/
|
||||||
void updateWallet(const QList<uint256> &updated)
|
void updateWallet(const uint256 &hash, int status)
|
||||||
{
|
{
|
||||||
// Walk through updated transactions, update model as needed.
|
OutputDebugStringF("updateWallet %s %i\n", hash.ToString().c_str(), status);
|
||||||
#ifdef WALLET_UPDATE_DEBUG
|
|
||||||
qDebug() << "updateWallet";
|
|
||||||
#endif
|
|
||||||
// Sort update list, and iterate through it in reverse, so that model updates
|
|
||||||
// can be emitted from end to beginning (so that earlier updates will not influence
|
|
||||||
// the indices of latter ones).
|
|
||||||
QList<uint256> updated_sorted = updated;
|
|
||||||
qSort(updated_sorted);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK(wallet->cs_wallet);
|
LOCK(wallet->cs_wallet);
|
||||||
for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx)
|
|
||||||
{
|
|
||||||
const uint256 &hash = updated_sorted.at(update_idx);
|
|
||||||
// Find transaction in wallet
|
// Find transaction in wallet
|
||||||
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
|
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
|
||||||
bool inWallet = mi != wallet->mapWallet.end();
|
bool inWallet = mi != wallet->mapWallet.end();
|
||||||
|
|
||||||
// Find bounds of this transaction in model
|
// Find bounds of this transaction in model
|
||||||
QList<TransactionRecord>::iterator lower = qLowerBound(
|
QList<TransactionRecord>::iterator lower = qLowerBound(
|
||||||
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
|
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
|
||||||
|
@ -111,20 +101,36 @@ public:
|
||||||
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
|
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
|
||||||
int lowerIndex = (lower - cachedWallet.begin());
|
int lowerIndex = (lower - cachedWallet.begin());
|
||||||
int upperIndex = (upper - cachedWallet.begin());
|
int upperIndex = (upper - cachedWallet.begin());
|
||||||
|
bool inModel = (lower != upper);
|
||||||
|
|
||||||
// Determine if transaction is in model already
|
// Determine whether to show transaction or not
|
||||||
bool inModel = false;
|
bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second));
|
||||||
if(lower != upper)
|
|
||||||
|
if(status == CT_UPDATED)
|
||||||
{
|
{
|
||||||
inModel = true;
|
if(showTransaction && !inModel)
|
||||||
|
status = CT_NEW; /* Not in model, but want to show, treat as new */
|
||||||
|
if(!showTransaction && inModel)
|
||||||
|
status = CT_DELETED; /* In model, but want to hide, treat as deleted */
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WALLET_UPDATE_DEBUG
|
OutputDebugStringF(" inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n",
|
||||||
qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel
|
inWallet, inModel, lowerIndex, upperIndex, showTransaction, status);
|
||||||
<< lowerIndex << "-" << upperIndex;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(inWallet && !inModel)
|
switch(status)
|
||||||
|
{
|
||||||
|
case CT_NEW:
|
||||||
|
if(inModel)
|
||||||
|
{
|
||||||
|
OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is already in model\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!inWallet)
|
||||||
|
{
|
||||||
|
OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is not in wallet\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(showTransaction)
|
||||||
{
|
{
|
||||||
// Added -- insert at the right position
|
// Added -- insert at the right position
|
||||||
QList<TransactionRecord> toInsert =
|
QList<TransactionRecord> toInsert =
|
||||||
|
@ -141,17 +147,22 @@ public:
|
||||||
parent->endInsertRows();
|
parent->endInsertRows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(!inWallet && inModel)
|
break;
|
||||||
|
case CT_DELETED:
|
||||||
|
if(!inModel)
|
||||||
{
|
{
|
||||||
|
OutputDebugStringF("Warning: updateWallet: Got CT_DELETED, but transaction is not in model\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
// Removed -- remove entire transaction from table
|
// Removed -- remove entire transaction from table
|
||||||
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
|
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
|
||||||
cachedWallet.erase(lower, upper);
|
cachedWallet.erase(lower, upper);
|
||||||
parent->endRemoveRows();
|
parent->endRemoveRows();
|
||||||
}
|
break;
|
||||||
else if(inWallet && inModel)
|
case CT_UPDATED:
|
||||||
{
|
// Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for
|
||||||
// Updated -- nothing to do, status update will take care of this
|
// visible transactions.
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,14 +220,15 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren
|
||||||
QAbstractTableModel(parent),
|
QAbstractTableModel(parent),
|
||||||
wallet(wallet),
|
wallet(wallet),
|
||||||
walletModel(parent),
|
walletModel(parent),
|
||||||
priv(new TransactionTablePriv(wallet, this))
|
priv(new TransactionTablePriv(wallet, this)),
|
||||||
|
cachedNumBlocks(0)
|
||||||
{
|
{
|
||||||
columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount");
|
columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount");
|
||||||
|
|
||||||
priv->refreshWallet();
|
priv->refreshWallet();
|
||||||
|
|
||||||
QTimer *timer = new QTimer(this);
|
QTimer *timer = new QTimer(this);
|
||||||
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
|
connect(timer, SIGNAL(timeout()), this, SLOT(updateConfirmations()));
|
||||||
timer->start(MODEL_UPDATE_DELAY);
|
timer->start(MODEL_UPDATE_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,29 +237,23 @@ TransactionTableModel::~TransactionTableModel()
|
||||||
delete priv;
|
delete priv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionTableModel::update()
|
void TransactionTableModel::updateTransaction(const QString &hash, int status)
|
||||||
{
|
{
|
||||||
QList<uint256> updated;
|
uint256 updated;
|
||||||
|
updated.SetHex(hash.toStdString());
|
||||||
|
|
||||||
// Check if there are changes to wallet map
|
priv->updateWallet(updated, status);
|
||||||
{
|
}
|
||||||
TRY_LOCK(wallet->cs_wallet, lockWallet);
|
|
||||||
if (lockWallet && !wallet->vWalletUpdated.empty())
|
|
||||||
{
|
|
||||||
BOOST_FOREACH(uint256 hash, wallet->vWalletUpdated)
|
|
||||||
{
|
|
||||||
updated.append(hash);
|
|
||||||
}
|
|
||||||
wallet->vWalletUpdated.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!updated.empty())
|
void TransactionTableModel::updateConfirmations()
|
||||||
|
{
|
||||||
|
if(nBestHeight != cachedNumBlocks)
|
||||||
{
|
{
|
||||||
priv->updateWallet(updated);
|
cachedNumBlocks = nBestHeight;
|
||||||
|
// Blocks came in since last poll.
|
||||||
// Status (number of confirmations) and (possibly) description
|
// Invalidate status (number of confirmations) and (possibly) description
|
||||||
// columns changed for all rows.
|
// for all rows. Qt is smart enough to only actually request the data for the
|
||||||
|
// visible rows.
|
||||||
emit dataChanged(index(0, Status), index(priv->size()-1, Status));
|
emit dataChanged(index(0, Status), index(priv->size()-1, Status));
|
||||||
emit dataChanged(index(0, ToAddress), index(priv->size()-1, ToAddress));
|
emit dataChanged(index(0, ToAddress), index(priv->size()-1, ToAddress));
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ private:
|
||||||
WalletModel *walletModel;
|
WalletModel *walletModel;
|
||||||
QStringList columns;
|
QStringList columns;
|
||||||
TransactionTablePriv *priv;
|
TransactionTablePriv *priv;
|
||||||
|
int cachedNumBlocks;
|
||||||
|
|
||||||
QString lookupAddress(const std::string &address, bool tooltip) const;
|
QString lookupAddress(const std::string &address, bool tooltip) const;
|
||||||
QVariant addressColor(const TransactionRecord *wtx) const;
|
QVariant addressColor(const TransactionRecord *wtx) const;
|
||||||
|
@ -72,8 +73,9 @@ private:
|
||||||
QVariant txStatusDecoration(const TransactionRecord *wtx) const;
|
QVariant txStatusDecoration(const TransactionRecord *wtx) const;
|
||||||
QVariant txAddressDecoration(const TransactionRecord *wtx) const;
|
QVariant txAddressDecoration(const TransactionRecord *wtx) const;
|
||||||
|
|
||||||
private slots:
|
public slots:
|
||||||
void update();
|
void updateTransaction(const QString &hash, int status);
|
||||||
|
void updateConfirmations();
|
||||||
|
|
||||||
friend class TransactionTablePriv;
|
friend class TransactionTablePriv;
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,13 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p
|
||||||
{
|
{
|
||||||
addressTableModel = new AddressTableModel(wallet, this);
|
addressTableModel = new AddressTableModel(wallet, this);
|
||||||
transactionTableModel = new TransactionTableModel(wallet, this);
|
transactionTableModel = new TransactionTableModel(wallet, this);
|
||||||
|
|
||||||
|
subscribeToCoreSignals();
|
||||||
|
}
|
||||||
|
|
||||||
|
WalletModel::~WalletModel()
|
||||||
|
{
|
||||||
|
unsubscribeFromCoreSignals();
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 WalletModel::getBalance() const
|
qint64 WalletModel::getBalance() const
|
||||||
|
@ -40,30 +47,38 @@ int WalletModel::getNumTransactions() const
|
||||||
return numTransactions;
|
return numTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WalletModel::update()
|
void WalletModel::updateStatus()
|
||||||
{
|
{
|
||||||
qint64 newBalance = getBalance();
|
|
||||||
qint64 newUnconfirmedBalance = getUnconfirmedBalance();
|
|
||||||
int newNumTransactions = getNumTransactions();
|
|
||||||
EncryptionStatus newEncryptionStatus = getEncryptionStatus();
|
EncryptionStatus newEncryptionStatus = getEncryptionStatus();
|
||||||
|
|
||||||
if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance)
|
|
||||||
emit balanceChanged(newBalance, newUnconfirmedBalance);
|
|
||||||
|
|
||||||
if(cachedNumTransactions != newNumTransactions)
|
|
||||||
emit numTransactionsChanged(newNumTransactions);
|
|
||||||
|
|
||||||
if(cachedEncryptionStatus != newEncryptionStatus)
|
if(cachedEncryptionStatus != newEncryptionStatus)
|
||||||
emit encryptionStatusChanged(newEncryptionStatus);
|
emit encryptionStatusChanged(newEncryptionStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WalletModel::updateTransaction(const QString &hash, int status)
|
||||||
|
{
|
||||||
|
if(transactionTableModel)
|
||||||
|
transactionTableModel->updateTransaction(hash, status);
|
||||||
|
|
||||||
|
// Balance and number of transactions might have changed
|
||||||
|
qint64 newBalance = getBalance();
|
||||||
|
qint64 newUnconfirmedBalance = getUnconfirmedBalance();
|
||||||
|
int newNumTransactions = getNumTransactions();
|
||||||
|
|
||||||
|
if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance)
|
||||||
|
emit balanceChanged(newBalance, newUnconfirmedBalance);
|
||||||
|
if(cachedNumTransactions != newNumTransactions)
|
||||||
|
emit numTransactionsChanged(newNumTransactions);
|
||||||
|
|
||||||
cachedBalance = newBalance;
|
cachedBalance = newBalance;
|
||||||
cachedUnconfirmedBalance = newUnconfirmedBalance;
|
cachedUnconfirmedBalance = newUnconfirmedBalance;
|
||||||
cachedNumTransactions = newNumTransactions;
|
cachedNumTransactions = newNumTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WalletModel::updateAddressList()
|
void WalletModel::updateAddressBook(const QString &address, const QString &label, bool isMine, int status)
|
||||||
{
|
{
|
||||||
addressTableModel->update();
|
if(addressTableModel)
|
||||||
|
addressTableModel->updateEntry(address, label, isMine, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WalletModel::validateAddress(const QString &address)
|
bool WalletModel::validateAddress(const QString &address)
|
||||||
|
@ -139,7 +154,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
|
||||||
}
|
}
|
||||||
return TransactionCreationFailed;
|
return TransactionCreationFailed;
|
||||||
}
|
}
|
||||||
if(!ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString()))
|
if(!uiInterface.ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString()))
|
||||||
{
|
{
|
||||||
return Aborted;
|
return Aborted;
|
||||||
}
|
}
|
||||||
|
@ -246,6 +261,47 @@ bool WalletModel::backupWallet(const QString &filename)
|
||||||
return BackupWallet(*wallet, filename.toLocal8Bit().data());
|
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, bool isMine, ChangeType status)
|
||||||
|
{
|
||||||
|
OutputDebugStringF("NotifyAddressBookChanged %s %s isMine=%i status=%i\n", address.c_str(), label.c_str(), isMine, status);
|
||||||
|
QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
|
||||||
|
Q_ARG(QString, QString::fromStdString(address)),
|
||||||
|
Q_ARG(QString, QString::fromStdString(label)),
|
||||||
|
Q_ARG(bool, isMine),
|
||||||
|
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, _5));
|
||||||
|
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, _5));
|
||||||
|
wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
|
||||||
|
}
|
||||||
|
|
||||||
// WalletModel::UnlockContext implementation
|
// WalletModel::UnlockContext implementation
|
||||||
WalletModel::UnlockContext WalletModel::requestUnlock()
|
WalletModel::UnlockContext WalletModel::requestUnlock()
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,6 +24,7 @@ class WalletModel : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0);
|
explicit WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0);
|
||||||
|
~WalletModel();
|
||||||
|
|
||||||
enum StatusCode // Returned by sendCoins
|
enum StatusCode // Returned by sendCoins
|
||||||
{
|
{
|
||||||
|
@ -118,6 +119,8 @@ private:
|
||||||
qint64 cachedNumTransactions;
|
qint64 cachedNumTransactions;
|
||||||
EncryptionStatus cachedEncryptionStatus;
|
EncryptionStatus cachedEncryptionStatus;
|
||||||
|
|
||||||
|
void subscribeToCoreSignals();
|
||||||
|
void unsubscribeFromCoreSignals();
|
||||||
signals:
|
signals:
|
||||||
// Signal that balance in wallet changed
|
// Signal that balance in wallet changed
|
||||||
void balanceChanged(qint64 balance, qint64 unconfirmedBalance);
|
void balanceChanged(qint64 balance, qint64 unconfirmedBalance);
|
||||||
|
@ -137,8 +140,12 @@ signals:
|
||||||
void error(const QString &title, const QString &message, bool modal);
|
void error(const QString &title, const QString &message, bool modal);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void update();
|
/* Wallet status might have changed */
|
||||||
void updateAddressList();
|
void updateStatus();
|
||||||
|
/* New transaction, or transaction changed status */
|
||||||
|
void updateTransaction(const QString &hash, int status);
|
||||||
|
/* New, updated or removed address book entry */
|
||||||
|
void updateAddressBook(const QString &address, const QString &label, bool isMine, int status);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -73,8 +73,6 @@ Value importprivkey(const Array& params, bool fHelp)
|
||||||
pwalletMain->ReacceptWalletTransactions();
|
pwalletMain->ReacceptWalletTransactions();
|
||||||
}
|
}
|
||||||
|
|
||||||
MainFrameRepaint();
|
|
||||||
|
|
||||||
return Value::null;
|
return Value::null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,15 @@
|
||||||
#include "wallet.h"
|
#include "wallet.h"
|
||||||
|
|
||||||
CWallet* pwalletMain;
|
CWallet* pwalletMain;
|
||||||
|
CClientUIInterface uiInterface;
|
||||||
|
|
||||||
extern bool fPrintToConsole;
|
extern bool fPrintToConsole;
|
||||||
|
extern void noui_connect();
|
||||||
|
|
||||||
struct TestingSetup {
|
struct TestingSetup {
|
||||||
TestingSetup() {
|
TestingSetup() {
|
||||||
fPrintToConsole = true; // don't want to write to debug.log file
|
fPrintToConsole = true; // don't want to write to debug.log file
|
||||||
|
noui_connect();
|
||||||
pwalletMain = new CWallet();
|
pwalletMain = new CWallet();
|
||||||
RegisterWallet(pwalletMain);
|
RegisterWallet(pwalletMain);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,45 +6,99 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "util.h" // for int64
|
#include "util.h" // for int64
|
||||||
|
#include <boost/signals2/signal.hpp>
|
||||||
|
#include <boost/signals2/last_value.hpp>
|
||||||
|
|
||||||
#define wxYES 0x00000002
|
class CBasicKeyStore;
|
||||||
#define wxOK 0x00000004
|
class CWallet;
|
||||||
#define wxNO 0x00000008
|
class uint256;
|
||||||
#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
|
|
||||||
|
|
||||||
/* These UI communication functions are implemented in bitcoin.cpp (for ui) and noui.cpp (no ui) */
|
/** General change type (added, updated, removed). */
|
||||||
|
enum ChangeType
|
||||||
|
{
|
||||||
|
CT_NEW,
|
||||||
|
CT_UPDATED,
|
||||||
|
CT_DELETED
|
||||||
|
};
|
||||||
|
|
||||||
extern int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK);
|
/** Signals for UI communication. */
|
||||||
extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption);
|
class CClientUIInterface
|
||||||
extern void ThreadSafeHandleURI(const std::string& strURI);
|
{
|
||||||
extern void MainFrameRepaint();
|
public:
|
||||||
extern void AddressBookRepaint();
|
/** Flags for CClientUIInterface::ThreadSafeMessageBox */
|
||||||
extern void QueueShutdown();
|
enum MessageBoxFlags
|
||||||
extern void InitMessage(const std::string &message);
|
{
|
||||||
extern std::string _(const char* psz);
|
YES = 0x00000002,
|
||||||
|
OK = 0x00000004,
|
||||||
|
NO = 0x00000008,
|
||||||
|
YES_NO = (YES|NO),
|
||||||
|
CANCEL = 0x00000010,
|
||||||
|
APPLY = 0x00000020,
|
||||||
|
CLOSE = 0x00000040,
|
||||||
|
OK_DEFAULT = 0x00000000,
|
||||||
|
YES_DEFAULT = 0x00000000,
|
||||||
|
NO_DEFAULT = 0x00000080,
|
||||||
|
CANCEL_DEFAULT = 0x80000000,
|
||||||
|
ICON_EXCLAMATION = 0x00000100,
|
||||||
|
ICON_HAND = 0x00000200,
|
||||||
|
ICON_WARNING = ICON_EXCLAMATION,
|
||||||
|
ICON_ERROR = ICON_HAND,
|
||||||
|
ICON_QUESTION = 0x00000400,
|
||||||
|
ICON_INFORMATION = 0x00000800,
|
||||||
|
ICON_STOP = ICON_HAND,
|
||||||
|
ICON_ASTERISK = ICON_INFORMATION,
|
||||||
|
ICON_MASK = (0x00000100|0x00000200|0x00000400|0x00000800),
|
||||||
|
FORWARD = 0x00001000,
|
||||||
|
BACKWARD = 0x00002000,
|
||||||
|
RESET = 0x00004000,
|
||||||
|
HELP = 0x00008000,
|
||||||
|
MORE = 0x00010000,
|
||||||
|
SETUP = 0x00020000,
|
||||||
|
// Force blocking, modal message box dialog (not just OS notification)
|
||||||
|
MODAL = 0x00040000
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
|
||||||
|
/** Initiate client shutdown. */
|
||||||
|
boost::signals2::signal<void ()> QueueShutdown;
|
||||||
|
|
||||||
|
/** Translate a message to the native language of the user. */
|
||||||
|
boost::signals2::signal<std::string (const char* psz)> Translate;
|
||||||
|
|
||||||
|
/** Block chain changed. */
|
||||||
|
boost::signals2::signal<void ()> NotifyBlocksChanged;
|
||||||
|
|
||||||
|
/** Number of network connections changed. */
|
||||||
|
boost::signals2::signal<void (int newNumConnections)> NotifyNumConnectionsChanged;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
inline std::string _(const char* psz)
|
||||||
|
{
|
||||||
|
boost::optional<std::string> rv = uiInterface.Translate(psz);
|
||||||
|
return rv ? (*rv) : psz;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1000,7 +1000,7 @@ void AddTimeData(const CNetAddr& ip, int64 nTime)
|
||||||
string strMessage = _("Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly.");
|
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;
|
strMiscWarning = strMessage;
|
||||||
printf("*** %s\n", strMessage.c_str());
|
printf("*** %s\n", strMessage.c_str());
|
||||||
ThreadSafeMessageBox(strMessage+" ", string("Bitcoin"), wxOK | wxICON_EXCLAMATION);
|
uiInterface.ThreadSafeMessageBox(strMessage+" ", string("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,7 +274,9 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
|
||||||
// Need to completely rewrite the wallet file; if we don't, bdb might keep
|
// Need to completely rewrite the wallet file; if we don't, bdb might keep
|
||||||
// bits of the unencrypted private key in slack space in the database file.
|
// bits of the unencrypted private key in slack space in the database file.
|
||||||
CDB::Rewrite(strWalletFile);
|
CDB::Rewrite(strWalletFile);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
NotifyStatusChanged(this);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -297,7 +299,7 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx)
|
||||||
printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
|
printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
|
||||||
wtx.MarkSpent(txin.prevout.n);
|
wtx.MarkSpent(txin.prevout.n);
|
||||||
wtx.WriteToDisk();
|
wtx.WriteToDisk();
|
||||||
vWalletUpdated.push_back(txin.prevout.hash);
|
NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,15 +375,12 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Notify UI
|
|
||||||
vWalletUpdated.push_back(hash);
|
|
||||||
|
|
||||||
// since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
|
// since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
|
||||||
WalletUpdateSpent(wtx);
|
WalletUpdateSpent(wtx);
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh UI
|
// Notify UI of new or updated transaction
|
||||||
MainFrameRepaint();
|
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1183,7 +1182,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
|
||||||
coin.BindWallet(this);
|
coin.BindWallet(this);
|
||||||
coin.MarkSpent(txin.prevout.n);
|
coin.MarkSpent(txin.prevout.n);
|
||||||
coin.WriteToDisk();
|
coin.WriteToDisk();
|
||||||
vWalletUpdated.push_back(coin.GetHash());
|
NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fFileBacked)
|
if (fFileBacked)
|
||||||
|
@ -1202,7 +1201,6 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
|
||||||
}
|
}
|
||||||
wtxNew.RelayWalletTransaction();
|
wtxNew.RelayWalletTransaction();
|
||||||
}
|
}
|
||||||
MainFrameRepaint();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1231,13 +1229,12 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew,
|
||||||
return strError;
|
return strError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending...")))
|
if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired, _("Sending...")))
|
||||||
return "ABORTED";
|
return "ABORTED";
|
||||||
|
|
||||||
if (!CommitTransaction(wtxNew, reservekey))
|
if (!CommitTransaction(wtxNew, reservekey))
|
||||||
return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
|
return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
|
||||||
|
|
||||||
MainFrameRepaint();
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1290,8 +1287,9 @@ int CWallet::LoadWallet(bool& fFirstRunRet)
|
||||||
|
|
||||||
bool CWallet::SetAddressBookName(const CBitcoinAddress& address, const string& strName)
|
bool CWallet::SetAddressBookName(const CBitcoinAddress& address, const string& strName)
|
||||||
{
|
{
|
||||||
|
std::map<CBitcoinAddress, std::string>::iterator mi = mapAddressBook.find(address);
|
||||||
mapAddressBook[address] = strName;
|
mapAddressBook[address] = strName;
|
||||||
AddressBookRepaint();
|
NotifyAddressBookChanged(this, address.ToString(), strName, HaveKey(address), (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED);
|
||||||
if (!fFileBacked)
|
if (!fFileBacked)
|
||||||
return false;
|
return false;
|
||||||
return CWalletDB(strWalletFile).WriteName(address.ToString(), strName);
|
return CWalletDB(strWalletFile).WriteName(address.ToString(), strName);
|
||||||
|
@ -1300,7 +1298,7 @@ bool CWallet::SetAddressBookName(const CBitcoinAddress& address, const string& s
|
||||||
bool CWallet::DelAddressBookName(const CBitcoinAddress& address)
|
bool CWallet::DelAddressBookName(const CBitcoinAddress& address)
|
||||||
{
|
{
|
||||||
mapAddressBook.erase(address);
|
mapAddressBook.erase(address);
|
||||||
AddressBookRepaint();
|
NotifyAddressBookChanged(this, address.ToString(), "", HaveKey(address), CT_DELETED);
|
||||||
if (!fFileBacked)
|
if (!fFileBacked)
|
||||||
return false;
|
return false;
|
||||||
return CWalletDB(strWalletFile).EraseName(address.ToString());
|
return CWalletDB(strWalletFile).EraseName(address.ToString());
|
||||||
|
@ -1558,3 +1556,14 @@ void CWallet::GetAllReserveAddresses(set<CBitcoinAddress>& setAddress)
|
||||||
setAddress.insert(address);
|
setAddress.insert(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CWallet::UpdatedTransaction(const uint256 &hashTx)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
LOCK(cs_wallet);
|
||||||
|
// Only notify UI if this transaction is in this wallet
|
||||||
|
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);
|
||||||
|
if (mi != mapWallet.end())
|
||||||
|
NotifyTransactionChanged(this, hashTx, CT_UPDATED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
21
src/wallet.h
21
src/wallet.h
|
@ -9,6 +9,7 @@
|
||||||
#include "key.h"
|
#include "key.h"
|
||||||
#include "keystore.h"
|
#include "keystore.h"
|
||||||
#include "script.h"
|
#include "script.h"
|
||||||
|
#include "ui_interface.h"
|
||||||
|
|
||||||
class CWalletTx;
|
class CWalletTx;
|
||||||
class CReserveKey;
|
class CReserveKey;
|
||||||
|
@ -102,8 +103,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<uint256, CWalletTx> mapWallet;
|
std::map<uint256, CWalletTx> mapWallet;
|
||||||
std::vector<uint256> vWalletUpdated;
|
|
||||||
|
|
||||||
std::map<uint256, int> mapRequestCount;
|
std::map<uint256, int> mapRequestCount;
|
||||||
|
|
||||||
std::map<CBitcoinAddress, std::string> mapAddressBook;
|
std::map<CBitcoinAddress, std::string> mapAddressBook;
|
||||||
|
@ -232,13 +231,7 @@ public:
|
||||||
|
|
||||||
bool DelAddressBookName(const CBitcoinAddress& address);
|
bool DelAddressBookName(const CBitcoinAddress& address);
|
||||||
|
|
||||||
void UpdatedTransaction(const uint256 &hashTx)
|
void UpdatedTransaction(const uint256 &hashTx);
|
||||||
{
|
|
||||||
{
|
|
||||||
LOCK(cs_wallet);
|
|
||||||
vWalletUpdated.push_back(hashTx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintWallet(const CBlock& block);
|
void PrintWallet(const CBlock& block);
|
||||||
|
|
||||||
|
@ -269,6 +262,16 @@ public:
|
||||||
|
|
||||||
// get the current wallet format (the oldest client version guaranteed to understand this wallet)
|
// get the current wallet format (the oldest client version guaranteed to understand this wallet)
|
||||||
int GetVersion() { return nWalletVersion; }
|
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, bool isMine, 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. */
|
/** A key allocated from the key pool. */
|
||||||
|
|
Loading…
Add table
Reference in a new issue