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:
Wladimir J. van der Laan 2012-05-20 01:53:24 -07:00
commit 5a8398e55a
32 changed files with 700 additions and 399 deletions

View File

@ -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;
} }

View File

@ -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

View File

@ -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();

View File

@ -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;
} }

View File

@ -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

View File

@ -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;
} }

View File

@ -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

View File

@ -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());
} }

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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()));
}

View File

@ -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

View File

@ -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);
} }

View File

@ -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)

View File

@ -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

View File

@ -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));
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -40,114 +40,111 @@ 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()) //
// Credit
//
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
if(wallet->IsMine(txout))
{
TransactionRecord sub(hash, nTime);
CBitcoinAddress address;
sub.idx = parts.size(); // sequence number
sub.credit = txout.nValue;
if (wtx.IsCoinBase())
{
// Generated
sub.type = TransactionRecord::Generated;
}
else if (ExtractAddress(txout.scriptPubKey, address) && wallet->HaveKey(address))
{
// Received by Bitcoin Address
sub.type = TransactionRecord::RecvWithAddress;
sub.address = address.ToString();
}
else
{
// Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
sub.type = TransactionRecord::RecvFromOther;
sub.address = mapValue["from"];
}
parts.append(sub);
}
}
}
else
{
bool fAllFromMe = true;
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
fAllFromMe = fAllFromMe && wallet->IsMine(txin);
bool fAllToMe = true;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
fAllToMe = fAllToMe && wallet->IsMine(txout);
if (fAllFromMe && fAllToMe)
{
// Payment to self
int64 nChange = wtx.GetChange();
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
-(nDebit - nChange), nCredit - nChange));
}
else if (fAllFromMe)
{ {
// //
// Credit // Debit
// //
BOOST_FOREACH(const CTxOut& txout, wtx.vout) int64 nTxFee = nDebit - wtx.GetValueOut();
for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
{ {
const CTxOut& txout = wtx.vout[nOut];
TransactionRecord sub(hash, nTime);
sub.idx = parts.size();
if(wallet->IsMine(txout)) if(wallet->IsMine(txout))
{ {
TransactionRecord sub(hash, nTime); // Ignore parts sent to self, as this is usually the change
CBitcoinAddress address; // from a transaction sent back to our own address.
sub.idx = parts.size(); // sequence number continue;
sub.credit = txout.nValue;
if (wtx.IsCoinBase())
{
// Generated
sub.type = TransactionRecord::Generated;
}
else if (ExtractAddress(txout.scriptPubKey, address) && wallet->HaveKey(address))
{
// Received by Bitcoin Address
sub.type = TransactionRecord::RecvWithAddress;
sub.address = address.ToString();
}
else
{
// Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
sub.type = TransactionRecord::RecvFromOther;
sub.address = mapValue["from"];
}
parts.append(sub);
} }
CBitcoinAddress address;
if (ExtractAddress(txout.scriptPubKey, address))
{
// Sent to Bitcoin Address
sub.type = TransactionRecord::SendToAddress;
sub.address = address.ToString();
}
else
{
// Sent to IP, or other non-address transaction like OP_EVAL
sub.type = TransactionRecord::SendToOther;
sub.address = mapValue["to"];
}
int64 nValue = txout.nValue;
/* Add fee to first output */
if (nTxFee > 0)
{
nValue += nTxFee;
nTxFee = 0;
}
sub.debit = -nValue;
parts.append(sub);
} }
} }
else else
{ {
bool fAllFromMe = true; //
BOOST_FOREACH(const CTxIn& txin, wtx.vin) // Mixed debit transaction, can't break down payees
fAllFromMe = fAllFromMe && wallet->IsMine(txin); //
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
bool fAllToMe = true;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
fAllToMe = fAllToMe && wallet->IsMine(txout);
if (fAllFromMe && fAllToMe)
{
// Payment to self
int64 nChange = wtx.GetChange();
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
-(nDebit - nChange), nCredit - nChange));
}
else if (fAllFromMe)
{
//
// Debit
//
int64 nTxFee = nDebit - wtx.GetValueOut();
for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
{
const CTxOut& txout = wtx.vout[nOut];
TransactionRecord sub(hash, nTime);
sub.idx = parts.size();
if(wallet->IsMine(txout))
{
// Ignore parts sent to self, as this is usually the change
// from a transaction sent back to our own address.
continue;
}
CBitcoinAddress address;
if (ExtractAddress(txout.scriptPubKey, address))
{
// Sent to Bitcoin Address
sub.type = TransactionRecord::SendToAddress;
sub.address = address.ToString();
}
else
{
// Sent to IP, or other non-address transaction like OP_EVAL
sub.type = TransactionRecord::SendToOther;
sub.address = mapValue["to"];
}
int64 nValue = txout.nValue;
/* Add fee to first output */
if (nTxFee > 0)
{
nValue += nTxFee;
nTxFee = 0;
}
sub.debit = -nValue;
parts.append(sub);
}
}
else
{
//
// Mixed debit transaction, can't break down payees
//
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
}
} }
} }

View File

@ -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,15 +67,14 @@ 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)
{ {
cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second)); if(TransactionRecord::showTransaction(it->second))
cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second));
} }
} }
} }
@ -82,49 +82,55 @@ 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)
// Find transaction in wallet
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
bool inWallet = mi != wallet->mapWallet.end();
// Find bounds of this transaction in model
QList<TransactionRecord>::iterator lower = qLowerBound(
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
QList<TransactionRecord>::iterator upper = qUpperBound(
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
int lowerIndex = (lower - cachedWallet.begin());
int upperIndex = (upper - cachedWallet.begin());
bool inModel = (lower != upper);
// Determine whether to show transaction or not
bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second));
if(status == CT_UPDATED)
{ {
const uint256 &hash = updated_sorted.at(update_idx); if(showTransaction && !inModel)
// Find transaction in wallet status = CT_NEW; /* Not in model, but want to show, treat as new */
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash); if(!showTransaction && inModel)
bool inWallet = mi != wallet->mapWallet.end(); status = CT_DELETED; /* In model, but want to hide, treat as deleted */
// Find bounds of this transaction in model }
QList<TransactionRecord>::iterator lower = qLowerBound(
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
QList<TransactionRecord>::iterator upper = qUpperBound(
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
int lowerIndex = (lower - cachedWallet.begin());
int upperIndex = (upper - cachedWallet.begin());
// Determine if transaction is in model already OutputDebugStringF(" inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n",
bool inModel = false; inWallet, inModel, lowerIndex, upperIndex, showTransaction, status);
if(lower != upper)
switch(status)
{
case CT_NEW:
if(inModel)
{ {
inModel = true; OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is already in model\n");
break;
} }
if(!inWallet)
#ifdef WALLET_UPDATE_DEBUG {
qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is not in wallet\n");
<< lowerIndex << "-" << upperIndex; break;
#endif }
if(showTransaction)
if(inWallet && !inModel)
{ {
// 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)
{ {
// Removed -- remove entire transaction from table OutputDebugStringF("Warning: updateWallet: Got CT_DELETED, but transaction is not in model\n");
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); break;
cachedWallet.erase(lower, upper);
parent->endRemoveRows();
}
else if(inWallet && inModel)
{
// Updated -- nothing to do, status update will take care of this
} }
// Removed -- remove entire transaction from table
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
cachedWallet.erase(lower, upper);
parent->endRemoveRows();
break;
case CT_UPDATED:
// Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for
// 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);
}
void TransactionTableModel::updateConfirmations()
{
if(nBestHeight != cachedNumBlocks)
{ {
TRY_LOCK(wallet->cs_wallet, lockWallet); cachedNumBlocks = nBestHeight;
if (lockWallet && !wallet->vWalletUpdated.empty()) // Blocks came in since last poll.
{ // Invalidate status (number of confirmations) and (possibly) description
BOOST_FOREACH(uint256 hash, wallet->vWalletUpdated) // for all rows. Qt is smart enough to only actually request the data for the
{ // visible rows.
updated.append(hash);
}
wallet->vWalletUpdated.clear();
}
}
if(!updated.empty())
{
priv->updateWallet(updated);
// Status (number of confirmations) and (possibly) description
// columns changed for all 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));
} }

View File

@ -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;
}; };

View File

@ -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()
{ {

View File

@ -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);
}; };

View File

@ -73,8 +73,6 @@ Value importprivkey(const Array& params, bool fHelp)
pwalletMain->ReacceptWalletTransactions(); pwalletMain->ReacceptWalletTransactions();
} }
MainFrameRepaint();
return Value::null; return Value::null;
} }

View File

@ -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);
} }

View File

@ -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

View File

@ -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);
} }
} }
} }

View File

@ -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);
}
}

View File

@ -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. */