Browse Source

Merge pull request #1205 from laanwj/2012_05_granular_ui_notifications

Finer-grained UI updates, move UI interface to boost::signals
miguelfreitas
Wladimir J. van der Laan 13 years ago
parent
commit
5a8398e55a
  1. 16
      src/bitcoinrpc.cpp
  2. 42
      src/init.cpp
  3. 1
      src/init.h
  4. 15
      src/keystore.cpp
  5. 19
      src/keystore.h
  6. 26
      src/main.cpp
  7. 5
      src/main.h
  8. 2
      src/net.cpp
  9. 33
      src/noui.cpp
  10. 29
      src/qt/addressbookpage.cpp
  11. 5
      src/qt/addressbookpage.h
  12. 85
      src/qt/addresstablemodel.cpp
  13. 9
      src/qt/addresstablemodel.h
  14. 43
      src/qt/bitcoin.cpp
  15. 9
      src/qt/bitcoingui.cpp
  16. 2
      src/qt/bitcoingui.h
  17. 102
      src/qt/clientmodel.cpp
  18. 17
      src/qt/clientmodel.h
  19. 4
      src/qt/qtipcserver.cpp
  20. 7
      src/qt/rpcconsole.cpp
  21. 2
      src/qt/rpcconsole.h
  22. 181
      src/qt/transactionrecord.cpp
  23. 146
      src/qt/transactiontablemodel.cpp
  24. 6
      src/qt/transactiontablemodel.h
  25. 74
      src/qt/walletmodel.cpp
  26. 11
      src/qt/walletmodel.h
  27. 2
      src/rpcdump.cpp
  28. 4
      src/test/test_bitcoin.cpp
  29. 132
      src/ui_interface.h
  30. 2
      src/util.cpp
  31. 35
      src/wallet.cpp
  32. 21
      src/wallet.h

16
src/bitcoinrpc.cpp

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

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

1
src/init.h

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

15
src/keystore.cpp

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

19
src/keystore.h

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

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

5
src/main.h

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

2
src/net.cpp

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

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

29
src/qt/addressbookpage.cpp

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

5
src/qt/addressbookpage.h

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

85
src/qt/addresstablemodel.cpp

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

9
src/qt/addresstablemodel.h

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

43
src/qt/bitcoin.cpp

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

9
src/qt/bitcoingui.cpp

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

2
src/qt/bitcoingui.h

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

102
src/qt/clientmodel.cpp

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

17
src/qt/clientmodel.h

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

4
src/qt/qtipcserver.cpp

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

7
src/qt/rpcconsole.cpp

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

2
src/qt/rpcconsole.h

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

181
src/qt/transactionrecord.cpp

@ -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))
// 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())
{ {
TransactionRecord sub(hash, nTime); // Generated
CBitcoinAddress address; sub.type = TransactionRecord::Generated;
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 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 }
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)
{ {
bool fAllFromMe = true; // Payment to self
BOOST_FOREACH(const CTxIn& txin, wtx.vin) int64 nChange = wtx.GetChange();
fAllFromMe = fAllFromMe && wallet->IsMine(txin);
bool fAllToMe = true; parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
BOOST_FOREACH(const CTxOut& txout, wtx.vout) -(nDebit - nChange), nCredit - nChange));
fAllToMe = fAllToMe && wallet->IsMine(txout); }
else if (fAllFromMe)
{
//
// Debit
//
int64 nTxFee = nDebit - wtx.GetValueOut();
if (fAllFromMe && fAllToMe) for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
{ {
// Payment to self const CTxOut& txout = wtx.vout[nOut];
int64 nChange = wtx.GetChange(); TransactionRecord sub(hash, nTime);
sub.idx = parts.size();
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", if(wallet->IsMine(txout))
-(nDebit - nChange), nCredit - nChange)); {
} // Ignore parts sent to self, as this is usually the change
else if (fAllFromMe) // from a transaction sent back to our own address.
{ continue;
// }
// Debit
//
int64 nTxFee = nDebit - wtx.GetValueOut();
for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) CBitcoinAddress address;
if (ExtractAddress(txout.scriptPubKey, address))
{ {
const CTxOut& txout = wtx.vout[nOut]; // Sent to Bitcoin Address
TransactionRecord sub(hash, nTime); sub.type = TransactionRecord::SendToAddress;
sub.idx = parts.size(); sub.address = address.ToString();
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
{
// 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
// {
// Mixed debit transaction, can't break down payees //
// // Mixed debit transaction, can't break down payees
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0)); //
} parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
} }
} }

146
src/qt/transactiontablemodel.cpp

@ -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
bool inModel = false;
if(lower != upper)
{
inModel = true;
}
#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:
// Removed -- remove entire transaction from table if(!inModel)
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
cachedWallet.erase(lower, upper);
parent->endRemoveRows();
}
else if(inWallet && inModel)
{ {
// Updated -- nothing to do, status update will take care of this OutputDebugStringF("Warning: updateWallet: Got CT_DELETED, but transaction is not in model\n");
break;
} }
// 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);
{ }
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));
} }

6
src/qt/transactiontablemodel.h

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

74
src/qt/walletmodel.cpp

@ -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()
{
EncryptionStatus newEncryptionStatus = getEncryptionStatus();
if(cachedEncryptionStatus != 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 newBalance = getBalance();
qint64 newUnconfirmedBalance = getUnconfirmedBalance(); qint64 newUnconfirmedBalance = getUnconfirmedBalance();
int newNumTransactions = getNumTransactions(); int newNumTransactions = getNumTransactions();
EncryptionStatus newEncryptionStatus = getEncryptionStatus();
if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance) if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance)
emit balanceChanged(newBalance, newUnconfirmedBalance); emit balanceChanged(newBalance, newUnconfirmedBalance);
if(cachedNumTransactions != newNumTransactions) if(cachedNumTransactions != newNumTransactions)
emit numTransactionsChanged(newNumTransactions); emit numTransactionsChanged(newNumTransactions);
if(cachedEncryptionStatus != newEncryptionStatus)
emit encryptionStatusChanged(newEncryptionStatus);
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()
{ {

11
src/qt/walletmodel.h

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

2
src/rpcdump.cpp

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

4
src/test/test_bitcoin.cpp

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

132
src/ui_interface.h

@ -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 /** General change type (added, updated, removed). */
#define wxAPPLY 0x00000020 enum ChangeType
#define wxCLOSE 0x00000040 {
#define wxOK_DEFAULT 0x00000000 CT_NEW,
#define wxYES_DEFAULT 0x00000000 CT_UPDATED,
#define wxNO_DEFAULT 0x00000080 CT_DELETED
#define wxCANCEL_DEFAULT 0x80000000 };
#define wxICON_EXCLAMATION 0x00000100
#define wxICON_HAND 0x00000200 /** Signals for UI communication. */
#define wxICON_WARNING wxICON_EXCLAMATION class CClientUIInterface
#define wxICON_ERROR wxICON_HAND {
#define wxICON_QUESTION 0x00000400 public:
#define wxICON_INFORMATION 0x00000800 /** Flags for CClientUIInterface::ThreadSafeMessageBox */
#define wxICON_STOP wxICON_HAND enum MessageBoxFlags
#define wxICON_ASTERISK wxICON_INFORMATION {
#define wxICON_MASK (0x00000100|0x00000200|0x00000400|0x00000800) YES = 0x00000002,
#define wxFORWARD 0x00001000 OK = 0x00000004,
#define wxBACKWARD 0x00002000 NO = 0x00000008,
#define wxRESET 0x00004000 YES_NO = (YES|NO),
#define wxHELP 0x00008000 CANCEL = 0x00000010,
#define wxMORE 0x00010000 APPLY = 0x00000020,
#define wxSETUP 0x00020000 CLOSE = 0x00000040,
// Force blocking, modal message box dialog (not just notification) OK_DEFAULT = 0x00000000,
#define wxMODAL 0x00040000 YES_DEFAULT = 0x00000000,
NO_DEFAULT = 0x00000080,
/* These UI communication functions are implemented in bitcoin.cpp (for ui) and noui.cpp (no ui) */ CANCEL_DEFAULT = 0x80000000,
ICON_EXCLAMATION = 0x00000100,
extern int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK); ICON_HAND = 0x00000200,
extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption); ICON_WARNING = ICON_EXCLAMATION,
extern void ThreadSafeHandleURI(const std::string& strURI); ICON_ERROR = ICON_HAND,
extern void MainFrameRepaint(); ICON_QUESTION = 0x00000400,
extern void AddressBookRepaint(); ICON_INFORMATION = 0x00000800,
extern void QueueShutdown(); ICON_STOP = ICON_HAND,
extern void InitMessage(const std::string &message); ICON_ASTERISK = ICON_INFORMATION,
extern std::string _(const char* psz); 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

2
src/util.cpp

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

35
src/wallet.cpp

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

@ -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…
Cancel
Save