Browse Source

Merge pull request #1205 from laanwj/2012_05_granular_ui_notifications

Finer-grained UI updates, move UI interface to boost::signals
pull/1/head
Wladimir J. van der Laan 10 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) @@ -435,7 +435,7 @@ Value stop(const Array& params, bool fHelp)
"stop\n"
"Stop Bitcoin server.");
// Shutdown will take long enough that the response should get back
QueueShutdown();
uiInterface.QueueShutdown();
return "Bitcoin server stopping";
}
@ -1928,7 +1928,7 @@ Value encryptwallet(const Array& params, bool fHelp) @@ -1928,7 +1928,7 @@ Value encryptwallet(const Array& params, bool fHelp)
// BDB seems to have a bad habit of writing old data into
// slack space in .dat files; that is bad if the old data is
// unencrypted private keys. So:
QueueShutdown();
uiInterface.QueueShutdown();
return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet";
}
@ -2620,7 +2620,7 @@ void ThreadRPCServer2(void* parg) @@ -2620,7 +2620,7 @@ void ThreadRPCServer2(void* parg)
strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
else if (mapArgs.count("-daemon"))
strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
ThreadSafeMessageBox(strprintf(
uiInterface.ThreadSafeMessageBox(strprintf(
_("%s, you must set a rpcpassword in the configuration file:\n %s\n"
"It is recommended you use the following random password:\n"
"rpcuser=bitcoinrpc\n"
@ -2630,8 +2630,8 @@ void ThreadRPCServer2(void* parg) @@ -2630,8 +2630,8 @@ void ThreadRPCServer2(void* parg)
strWhatAmI.c_str(),
GetConfigFile().string().c_str(),
EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
_("Error"), wxOK | wxMODAL);
QueueShutdown();
_("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
uiInterface.QueueShutdown();
return;
}
@ -2650,9 +2650,9 @@ void ThreadRPCServer2(void* parg) @@ -2650,9 +2650,9 @@ void ThreadRPCServer2(void* parg)
}
catch(boost::system::system_error &e)
{
ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()),
_("Error"), wxOK | wxMODAL);
QueueShutdown();
uiInterface.ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()),
_("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
uiInterface.QueueShutdown();
return;
}

42
src/init.cpp

@ -22,6 +22,7 @@ using namespace std; @@ -22,6 +22,7 @@ using namespace std;
using namespace boost;
CWallet* pwalletMain;
CClientUIInterface uiInterface;
//////////////////////////////////////////////////////////////////////////////
//
@ -90,17 +91,6 @@ void HandleSIGTERM(int) @@ -90,17 +91,6 @@ void HandleSIGTERM(int)
// Start
//
#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 fRet = false;
@ -156,17 +146,33 @@ bool AppInit(int argc, char* argv[]) @@ -156,17 +146,33 @@ bool AppInit(int argc, char* argv[])
Shutdown(NULL);
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
bool static InitError(const std::string &str)
{
ThreadSafeMessageBox(str, _("Bitcoin"), wxOK | wxMODAL);
uiInterface.ThreadSafeMessageBox(str, _("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::MODAL);
return false;
}
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;
}
@ -367,7 +373,7 @@ bool AppInit2() @@ -367,7 +373,7 @@ bool AppInit2()
fprintf(stdout, "Bitcoin server starting\n");
int64 nStart;
InitMessage(_("Loading addresses..."));
uiInterface.InitMessage(_("Loading addresses..."));
printf("Loading addresses...\n");
nStart = GetTimeMillis();
@ -380,7 +386,7 @@ bool AppInit2() @@ -380,7 +386,7 @@ bool AppInit2()
printf("Loaded %i addresses from peers.dat %"PRI64d"ms\n",
addrman.size(), GetTimeMillis() - nStart);
InitMessage(_("Loading block index..."));
uiInterface.InitMessage(_("Loading block index..."));
printf("Loading block index...\n");
nStart = GetTimeMillis();
if (!LoadBlockIndex())
@ -406,7 +412,7 @@ bool AppInit2() @@ -406,7 +412,7 @@ bool AppInit2()
}
}
InitMessage(_("Loading wallet..."));
uiInterface.InitMessage(_("Loading wallet..."));
printf("Loading wallet...\n");
nStart = GetTimeMillis();
bool fFirstRun;
@ -474,14 +480,14 @@ bool AppInit2() @@ -474,14 +480,14 @@ bool AppInit2()
}
if (pindexBest != pindexRescan)
{
InitMessage(_("Rescanning..."));
uiInterface.InitMessage(_("Rescanning..."));
printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight);
nStart = GetTimeMillis();
pwalletMain->ScanForWalletTransactions(pindexRescan, true);
printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart);
}
InitMessage(_("Done loading"));
uiInterface.InitMessage(_("Done loading"));
printf("Done loading\n");
//// debug print

1
src/init.h

@ -10,7 +10,6 @@ @@ -10,7 +10,6 @@
extern CWallet* pwalletMain;
void Shutdown(void* parg);
bool AppInit(int argc, char* argv[]);
bool AppInit2();
std::string HelpMessage();

15
src/keystore.cpp

@ -73,6 +73,20 @@ bool CCryptoKeyStore::SetCrypted() @@ -73,6 +73,20 @@ bool CCryptoKeyStore::SetCrypted()
return true;
}
bool CCryptoKeyStore::Lock()
{
if (!SetCrypted())
return false;
{
LOCK(cs_KeyStore);
vMasterKey.clear();
}
NotifyStatusChanged(this);
return true;
}
bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
{
{
@ -99,6 +113,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) @@ -99,6 +113,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
}
vMasterKey = vMasterKeyIn;
}
NotifyStatusChanged(this);
return true;
}

19
src/keystore.h

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
#include "crypter.h"
#include "sync.h"
#include "base58.h"
#include <boost/signals2/signal.hpp>
class CScript;
@ -143,18 +144,7 @@ public: @@ -143,18 +144,7 @@ public:
return result;
}
bool Lock()
{
if (!SetCrypted())
return false;
{
LOCK(cs_KeyStore);
vMasterKey.clear();
}
return true;
}
bool Lock();
virtual bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
bool AddKey(const CKey& key);
@ -185,6 +175,11 @@ public: @@ -185,6 +175,11 @@ public:
mi++;
}
}
/* Wallet status (encrypted, locked) changed.
* Note: Called without locks held.
*/
boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged;
};
#endif

26
src/main.cpp

@ -946,7 +946,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) @@ -946,7 +946,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
{
bnBestInvalidWork = pindexNew->bnChainWork;
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: 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) @@ -1647,7 +1647,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
hashPrevBestCoinBase = vtx[0].GetHash();
}
MainFrameRepaint();
uiInterface.NotifyBlocksChanged();
return true;
}
@ -1858,8 +1858,8 @@ bool CheckDiskSpace(uint64 nAdditionalBytes) @@ -1858,8 +1858,8 @@ bool CheckDiskSpace(uint64 nAdditionalBytes)
string strMessage = _("Warning: Disk space is low");
strMiscWarning = strMessage;
printf("*** %s\n", strMessage.c_str());
ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION | wxMODAL);
QueueShutdown();
uiInterface.ThreadSafeMessageBox(strMessage, "Bitcoin", CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL);
uiInterface.QueueShutdown();
return false;
}
return true;
@ -2176,6 +2176,18 @@ string GetWarnings(string strFor) @@ -2176,6 +2176,18 @@ string GetWarnings(string strFor)
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()
{
if (!CheckSignature())
@ -2192,11 +2204,13 @@ bool CAlert::ProcessAlert() @@ -2192,11 +2204,13 @@ bool CAlert::ProcessAlert()
if (Cancels(alert))
{
printf("cancelling alert %d\n", alert.nID);
uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
mapAlerts.erase(mi++);
}
else if (!alert.IsInEffect())
{
printf("expiring alert %d\n", alert.nID);
uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
mapAlerts.erase(mi++);
}
else
@ -2216,10 +2230,12 @@ bool CAlert::ProcessAlert() @@ -2216,10 +2230,12 @@ bool CAlert::ProcessAlert()
// Add to mapAlerts
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());
MainFrameRepaint();
return true;
}

5
src/main.h

@ -1574,6 +1574,11 @@ public: @@ -1574,6 +1574,11 @@ public:
}
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

2
src/net.cpp

@ -705,7 +705,7 @@ void ThreadSocketHandler2(void* parg) @@ -705,7 +705,7 @@ void ThreadSocketHandler2(void* parg)
if (vNodes.size() != nPrevNodeCount)
{
nPrevNodeCount = vNodes.size();
MainFrameRepaint();
uiInterface.NotifyNumConnectionsChanged(vNodes.size());
}

33
src/noui.cpp

@ -3,42 +3,33 @@ @@ -3,42 +3,33 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include "ui_interface.h"
#include "init.h"
#include "bitcoinrpc.h"
#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());
fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str());
return 4;
}
bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
static bool noui_ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
{
return true;
}
void MainFrameRepaint()
{
}
void AddressBookRepaint()
{
}
void InitMessage(const std::string &message)
{
}
std::string _(const char* psz)
{
return psz;
}
void QueueShutdown()
static void noui_QueueShutdown()
{
// Without UI, Shutdown can simply be started in a new thread
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) @@ -132,6 +132,10 @@ void AddressBookPage::setModel(AddressTableModel *model)
connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
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)
{
// Auto-select first row when in sending mode
@ -193,20 +197,11 @@ void AddressBookPage::on_newAddressButton_clicked() @@ -193,20 +197,11 @@ void AddressBookPage::on_newAddressButton_clicked()
EditAddressDialog dlg(
tab == SendingTab ?
EditAddressDialog::NewSendingAddress :
EditAddressDialog::NewReceivingAddress);
EditAddressDialog::NewReceivingAddress, this);
dlg.setModel(model);
if(dlg.exec())
{
// Select row for newly created address
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());
}
newAddressToSelect = dlg.getAddress();
}
}
@ -338,3 +333,15 @@ void AddressBookPage::contextualMenu(const QPoint &point) @@ -338,3 +333,15 @@ void AddressBookPage::contextualMenu(const QPoint &point)
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; @@ -13,6 +13,7 @@ class QTableView;
class QItemSelection;
class QSortFilterProxyModel;
class QMenu;
class QModelIndex;
QT_END_NAMESPACE
/** Widget that shows a list of sending or receiving addresses.
@ -51,6 +52,7 @@ private: @@ -51,6 +52,7 @@ private:
QSortFilterProxyModel *proxyModel;
QMenu *contextMenu;
QAction *deleteAction;
QString newAddressToSelect;
private slots:
void on_deleteButton_clicked();
@ -67,6 +69,9 @@ private slots: @@ -67,6 +69,9 @@ private slots:
void onCopyLabelAction();
/** Edit currently selected address entry */
void onEditAction();
/** New entry/entries were added to address table */
void selectNewAddress(const QModelIndex &parent, int begin, int end);
};
#endif // ADDRESSBOOKDIALOG_H

85
src/qt/addresstablemodel.cpp

@ -26,20 +26,36 @@ struct AddressTableEntry @@ -26,20 +26,36 @@ struct AddressTableEntry
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
class AddressTablePriv
{
public:
CWallet *wallet;
QList<AddressTableEntry> cachedAddressTable;
AddressTableModel *parent;
AddressTablePriv(CWallet *wallet):
wallet(wallet) {}
AddressTablePriv(CWallet *wallet, AddressTableModel *parent):
wallet(wallet), parent(parent) {}
void refreshAddressTable()
{
cachedAddressTable.clear();
{
LOCK(wallet->cs_wallet);
BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, std::string)& item, wallet->mapAddressBook)
@ -54,6 +70,53 @@ public: @@ -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()
{
return cachedAddressTable.size();
@ -76,7 +139,7 @@ AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) : @@ -76,7 +139,7 @@ AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) :
QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
{
columns << tr("Label") << tr("Address");
priv = new AddressTablePriv(wallet);
priv = new AddressTablePriv(wallet, this);
priv->refreshAddressTable();
}
@ -158,7 +221,6 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu @@ -158,7 +221,6 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu
{
case Label:
wallet->SetAddressBookName(rec->address.toStdString(), value.toString().toStdString());
rec->label = value.toString();
break;
case Address:
// Refuse to set invalid address, set error status and return false
@ -177,12 +239,9 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu @@ -177,12 +239,9 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu
// Add new entry with new address
wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString());
}
rec->address = value.toString();
}
break;
}
emit dataChanged(index, index);
return true;
}
@ -232,12 +291,10 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & pa @@ -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
beginResetModel();
priv->refreshAddressTable();
endResetModel();
priv->updateEntry(address, label, isMine, status);
}
QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
@ -341,3 +398,7 @@ int AddressTableModel::lookupAddress(const QString &address) const @@ -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: @@ -74,13 +74,18 @@ private:
QStringList columns;
EditStatus editStatus;
/** Notify listeners that data changed. */
void emitDataChanged(int index);
signals:
void defaultAddressChanged(const QString &address);
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

43
src/qt/bitcoin.cpp

@ -36,15 +36,13 @@ Q_IMPORT_PLUGIN(qtaccessiblewidgets) @@ -36,15 +36,13 @@ Q_IMPORT_PLUGIN(qtaccessiblewidgets)
// Need a global reference for the notifications to find the GUI
static BitcoinGUI *guiref;
static QSplashScreen *splashref;
static WalletModel *walletmodel;
static ClientModel *clientmodel;
int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style)
static void ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style)
{
// Message from network thread
if(guiref)
{
bool modal = (style & wxMODAL);
bool modal = (style & CClientUIInterface::MODAL);
// in case of modal message, use blocking connection to wait for user to click OK
QMetaObject::invokeMethod(guiref, "error",
modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection,
@ -57,10 +55,9 @@ int ThreadSafeMessageBox(const std::string& message, const std::string& caption, @@ -57,10 +55,9 @@ int ThreadSafeMessageBox(const std::string& message, const std::string& caption,
printf("%s: %s\n", caption.c_str(), message.c_str());
fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str());
}
return 4;
}
bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
static bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
{
if(!guiref)
return false;
@ -75,7 +72,7 @@ bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption) @@ -75,7 +72,7 @@ bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption)
return payFee;
}
void ThreadSafeHandleURI(const std::string& strURI)
static void ThreadSafeHandleURI(const std::string& strURI)
{
if(!guiref)
return;
@ -84,21 +81,7 @@ void ThreadSafeHandleURI(const std::string& strURI) @@ -84,21 +81,7 @@ void ThreadSafeHandleURI(const std::string& strURI)
Q_ARG(QString, QString::fromStdString(strURI)));
}
void MainFrameRepaint()
{
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)
static void InitMessage(const std::string &message)
{
if(splashref)
{
@ -107,7 +90,7 @@ void InitMessage(const std::string &message) @@ -107,7 +90,7 @@ void InitMessage(const std::string &message)
}
}
void QueueShutdown()
static void QueueShutdown()
{
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
}
@ -115,7 +98,7 @@ void QueueShutdown() @@ -115,7 +98,7 @@ void QueueShutdown()
/*
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();
}
@ -266,6 +249,14 @@ int main(int argc, char *argv[]) @@ -266,6 +249,14 @@ int main(int argc, char *argv[])
if (translator.load(lang_territory, ":/translations/"))
app.installTranslator(&translator);
// Subscribe to global signals from core
uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox);
uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee);
uiInterface.ThreadSafeHandleURI.connect(ThreadSafeHandleURI);
uiInterface.InitMessage.connect(InitMessage);
uiInterface.QueueShutdown.connect(QueueShutdown);
uiInterface.Translate.connect(Translate);
// Show help message immediately after parsing command-line options (for "-lang") and setting locale,
// but before showing splash screen.
if (mapArgs.count("-?") || mapArgs.count("--help"))
@ -307,9 +298,7 @@ int main(int argc, char *argv[]) @@ -307,9 +298,7 @@ int main(int argc, char *argv[])
splash.finish(&window);
ClientModel clientModel(&optionsModel);
clientmodel = &clientModel;
WalletModel walletModel(pwalletMain, &optionsModel);
walletmodel = &walletModel;
window.setClientModel(&clientModel);
window.setWalletModel(&walletModel);
@ -351,8 +340,6 @@ int main(int argc, char *argv[]) @@ -351,8 +340,6 @@ int main(int argc, char *argv[])
window.setClientModel(0);
window.setWalletModel(0);
guiref = 0;
clientmodel = 0;
walletmodel = 0;
}
Shutdown(NULL);
}

9
src/qt/bitcoingui.cpp

@ -350,11 +350,11 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) @@ -350,11 +350,11 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
setNumConnections(clientModel->getNumConnections());
connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
setNumBlocks(clientModel->getNumBlocks());
connect(clientModel, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int)));
setNumBlocks(clientModel->getNumBlocks(), clientModel->getNumBlocksOfPeers());
connect(clientModel, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int)));
// 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);
}
@ -493,7 +493,7 @@ void BitcoinGUI::setNumConnections(int count) @@ -493,7 +493,7 @@ void BitcoinGUI::setNumConnections(int 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
if (!clientModel || clientModel->getNumConnections() == 0)
@ -504,7 +504,6 @@ void BitcoinGUI::setNumBlocks(int count) @@ -504,7 +504,6 @@ void BitcoinGUI::setNumBlocks(int count)
return;
}
int nTotalBlocks = clientModel->getNumBlocksOfPeers();
QString tooltip;
if(count < nTotalBlocks)

2
src/qt/bitcoingui.h

@ -111,7 +111,7 @@ public slots: @@ -111,7 +111,7 @@ public slots:
/** Set number of connections shown in the UI */
void setNumConnections(int count);
/** 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.
@param[in] status current encryption status
@see WalletModel::EncryptionStatus

102
src/qt/clientmodel.cpp

@ -5,15 +5,30 @@ @@ -5,15 +5,30 @@
#include "transactiontablemodel.h"
#include "main.h"
static const int64 nClientStartupTime = GetTime();
#include "ui_interface.h"
#include <QDateTime>
#include <QTimer>
static const int64 nClientStartupTime = GetTime();
ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
QObject(parent), optionsModel(optionsModel),
cachedNumConnections(0), cachedNumBlocks(0)
cachedNumBlocks(0), cachedNumBlocksOfPeers(0), pollTimer(0)
{
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
@ -37,27 +52,42 @@ QDateTime ClientModel::getLastBlockDate() const @@ -37,27 +52,42 @@ QDateTime ClientModel::getLastBlockDate() const
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();
QString newStatusBar = getStatusBarWarnings();
int newNumBlocksOfPeers = getNumBlocksOfPeers();
if(cachedNumConnections != newNumConnections)
emit numConnectionsChanged(newNumConnections);
if(cachedNumBlocks != newNumBlocks || cachedStatusBar != newStatusBar)
if(cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers)
emit numBlocksChanged(newNumBlocks, newNumBlocksOfPeers);
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,
// so that the view updates the status bar.
// TODO: It should send a notification.
// (However, this might generate looped notifications and needs to be thought through and tested carefully)
// error(tr("Network Alert"), newStatusBar);
emit numBlocksChanged(newNumBlocks);
uint256 hash_256;
hash_256.SetHex(hash.toStdString());
CAlert alert = CAlert::getAlertByHash(hash_256);
if(!alert.IsNull())
{
emit error(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), false);
}
}
cachedNumConnections = newNumConnections;
cachedNumBlocks = newNumBlocks;
cachedStatusBar = newStatusBar;
// Emit a numBlocksChanged when the status message changes,
// so that the view recomputes and updates the status bar.
emit numBlocksChanged(getNumBlocks(), getNumBlocksOfPeers());
}
bool ClientModel::isTestNet() const
@ -104,3 +134,41 @@ QDateTime ClientModel::formatClientStartupTime() const @@ -104,3 +134,41 @@ QDateTime ClientModel::formatClientStartupTime() const
{
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; @@ -10,6 +10,7 @@ class CWallet;
QT_BEGIN_NAMESPACE
class QDateTime;
class QTimer;
QT_END_NAMESPACE
/** Model for Bitcoin network client. */
@ -18,6 +19,7 @@ class ClientModel : public QObject @@ -18,6 +19,7 @@ class ClientModel : public QObject
Q_OBJECT
public:
explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0);
~ClientModel();
OptionsModel *getOptionsModel();
@ -44,23 +46,26 @@ public: @@ -44,23 +46,26 @@ public:
private:
OptionsModel *optionsModel;
int cachedNumConnections;
int cachedNumBlocks;
QString cachedStatusBar;
int cachedNumBlocksOfPeers;
int numBlocksAtStartup;
QTimer *pollTimer;
void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
signals:
void numConnectionsChanged(int count);
void numBlocksChanged(int count);
void numBlocksChanged(int count, int countOfPeers);
//! Asynchronous error notification
void error(const QString &title, const QString &message, bool modal);
public slots:
private slots:
void update();
void updateTimer();
void updateNumConnections(int numConnections);
void updateAlert(const QString &hash, int status);
};
#endif // CLIENTMODEL_H

4
src/qt/qtipcserver.cpp

@ -31,7 +31,7 @@ void ipcThread(void* parg) @@ -31,7 +31,7 @@ void ipcThread(void* parg)
ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(100);
if(mq->timed_receive(&strBuf, sizeof(strBuf), nSize, nPriority, d))
{
ThreadSafeHandleURI(std::string(strBuf, nSize));
uiInterface.ThreadSafeHandleURI(std::string(strBuf, nSize));
Sleep(1000);
}
if (fShutdown)
@ -69,7 +69,7 @@ void ipcInit() @@ -69,7 +69,7 @@ void ipcInit()
ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(1);
if(mq->timed_receive(&strBuf, sizeof(strBuf), nSize, nPriority, d))
{
ThreadSafeHandleURI(std::string(strBuf, nSize));
uiInterface.ThreadSafeHandleURI(std::string(strBuf, nSize));
}
else
break;

7
src/qt/rpcconsole.cpp

@ -157,7 +157,7 @@ void RPCConsole::setClientModel(ClientModel *model) @@ -157,7 +157,7 @@ void RPCConsole::setClientModel(ClientModel *model)
{
// Subscribe to information, replies, messages, errors
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
ui->clientVersion->setText(model->formatFullVersion());
@ -168,7 +168,7 @@ void RPCConsole::setClientModel(ClientModel *model) @@ -168,7 +168,7 @@ void RPCConsole::setClientModel(ClientModel *model)
setNumConnections(model->getNumConnections());
ui->isTestNet->setChecked(model->isTestNet());
setNumBlocks(model->getNumBlocks());
setNumBlocks(model->getNumBlocks(), model->getNumBlocksOfPeers());
}
}
@ -235,9 +235,10 @@ void RPCConsole::setNumConnections(int count) @@ -235,9 +235,10 @@ void RPCConsole::setNumConnections(int 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->totalBlocks->setText(QString::number(countOfPeers));
if(clientModel)
{
// 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: @@ -42,7 +42,7 @@ public slots:
/** Set number of connections shown in the UI */
void setNumConnections(int count);
/** Set number of blocks shown in the UI */
void setNumBlocks(int count);
void setNumBlocks(int count, int countOfPeers);
/** Go forward or back in history */
void browseHistory(int offset);
/** Scroll console view to end */

181
src/qt/transactionrecord.cpp

@ -40,114 +40,111 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * @@ -40,114 +40,111 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
uint256 hash = wtx.GetHash();
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)
{
//
// Credit
//
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if(wallet->IsMine(txout))
{
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);
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);
// 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
}
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;
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
fAllFromMe = fAllFromMe && wallet->IsMine(txin);
// Payment to self
int64 nChange = wtx.GetChange();
bool fAllToMe = true;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
fAllToMe = fAllToMe && wallet->IsMine(txout);
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
-(nDebit - nChange), nCredit - nChange));
}
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
int64 nChange = wtx.GetChange();
const CTxOut& txout = wtx.vout[nOut];
TransactionRecord sub(hash, nTime);
sub.idx = parts.size();
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
-(nDebit - nChange), nCredit - nChange));
}
else if (fAllFromMe)
{
//
// Debit
//
int64 nTxFee = nDebit - wtx.GetValueOut();
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;
}
for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
CBitcoinAddress address;
if (ExtractAddress(txout.scriptPubKey, address))
{
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);
// 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));
}
}
else
{
//
// Mixed debit transaction, can't break down payees
//
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
}
}

146
src/qt/transactiontablemodel.cpp

@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
#include "bitcoinunits.h"
#include "wallet.h"
#include "ui_interface.h"
#include <QLocale>
#include <QList>