Merge pull request #1205 from laanwj/2012_05_granular_ui_notifications
Finer-grained UI updates, move UI interface to boost::signals
This commit is contained in:
commit
5a8398e55a
32 changed files with 700 additions and 399 deletions
|
@ -435,7 +435,7 @@ Value stop(const Array& params, bool fHelp)
|
|||
"stop\n"
|
||||
"Stop 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)
|
|||
// 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)
|
|||
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)
|
|||
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)
|
|||
}
|
||||
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
42
src/init.cpp
|
@ -22,6 +22,7 @@ using namespace std;
|
|||
using namespace boost;
|
||||
|
||||
CWallet* pwalletMain;
|
||||
CClientUIInterface uiInterface;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -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[])
|
|||
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()
|
|||
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()
|
|||
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()
|
|||
}
|
||||
}
|
||||
|
||||
InitMessage(_("Loading wallet..."));
|
||||
uiInterface.InitMessage(_("Loading wallet..."));
|
||||
printf("Loading wallet...\n");
|
||||
nStart = GetTimeMillis();
|
||||
bool fFirstRun;
|
||||
|
@ -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
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
extern CWallet* pwalletMain;
|
||||
|
||||
void Shutdown(void* parg);
|
||||
bool AppInit(int argc, char* argv[]);
|
||||
bool AppInit2();
|
||||
std::string HelpMessage();
|
||||
|
||||
|
|
|
@ -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)
|
|||
}
|
||||
vMasterKey = vMasterKeyIn;
|
||||
}
|
||||
NotifyStatusChanged(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
|||
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:
|
|||
mi++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wallet status (encrypted, locked) changed.
|
||||
* Note: Called without locks held.
|
||||
*/
|
||||
boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
26
src/main.cpp
26
src/main.cpp
|
@ -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)
|
|||
hashPrevBestCoinBase = vtx[0].GetHash();
|
||||
}
|
||||
|
||||
MainFrameRepaint();
|
||||
uiInterface.NotifyBlocksChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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)
|
|||
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()
|
|||
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()
|
|||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -705,7 +705,7 @@ void ThreadSocketHandler2(void* parg)
|
|||
if (vNodes.size() != nPrevNodeCount)
|
||||
{
|
||||
nPrevNodeCount = vNodes.size();
|
||||
MainFrameRepaint();
|
||||
uiInterface.NotifyNumConnectionsChanged(vNodes.size());
|
||||
}
|
||||
|
||||
|
||||
|
|
33
src/noui.cpp
33
src/noui.cpp
|
@ -3,42 +3,33 @@
|
|||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// 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);
|
||||
}
|
||||
|
|
|
@ -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()
|
|||
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)
|
|||
contextMenu->exec(QCursor::pos());
|
||||
}
|
||||
}
|
||||
|
||||
void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int end)
|
||||
{
|
||||
QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
|
||||
if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
|
||||
{
|
||||
// Select row of newly created address, once
|
||||
ui->tableView->setFocus();
|
||||
ui->tableView->selectRow(idx.row());
|
||||
newAddressToSelect.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ class QTableView;
|
|||
class QItemSelection;
|
||||
class QSortFilterProxyModel;
|
||||
class QMenu;
|
||||
class QModelIndex;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
/** Widget that shows a list of sending or receiving addresses.
|
||||
|
@ -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:
|
|||
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
|
||||
|
|
|
@ -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:
|
|||
}
|
||||
}
|
||||
|
||||
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) :
|
|||
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
|
|||
{
|
||||
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
|
|||
// 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
|
|||
}
|
||||
}
|
||||
|
||||
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
|
|||
}
|
||||
}
|
||||
|
||||
void AddressTableModel::emitDataChanged(int idx)
|
||||
{
|
||||
emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
|||
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)
|
|||
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)
|
|||
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)
|
|||
}
|
||||
}
|
||||
|
||||
void QueueShutdown()
|
||||
static void QueueShutdown()
|
||||
{
|
||||
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
|
||||
}
|
||||
|
@ -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[])
|
|||
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[])
|
|||
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[])
|
|||
window.setClientModel(0);
|
||||
window.setWalletModel(0);
|
||||
guiref = 0;
|
||||
clientmodel = 0;
|
||||
walletmodel = 0;
|
||||
}
|
||||
Shutdown(NULL);
|
||||
}
|
||||
|
|
|
@ -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)
|
|||
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)
|
|||
return;
|
||||
}
|
||||
|
||||
int nTotalBlocks = clientModel->getNumBlocksOfPeers();
|
||||
QString tooltip;
|
||||
|
||||
if(count < nTotalBlocks)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|||
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
|
|||
{
|
||||
return QDateTime::fromTime_t(nClientStartupTime);
|
||||
}
|
||||
|
||||
// Handlers for core signals
|
||||
static void NotifyBlocksChanged(ClientModel *clientmodel)
|
||||
{
|
||||
// This notification is too frequent. Don't trigger a signal.
|
||||
// Don't remove it, though, as it might be useful later.
|
||||
}
|
||||
|
||||
static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
|
||||
{
|
||||
// Too noisy: OutputDebugStringF("NotifyNumConnectionsChanged %i\n", newNumConnections);
|
||||
QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
|
||||
Q_ARG(int, newNumConnections));
|
||||
}
|
||||
|
||||
static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, ChangeType status)
|
||||
{
|
||||
OutputDebugStringF("NotifyAlertChanged %s status=%i\n", hash.GetHex().c_str(), status);
|
||||
QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection,
|
||||
Q_ARG(QString, QString::fromStdString(hash.GetHex())),
|
||||
Q_ARG(int, status));
|
||||
}
|
||||
|
||||
void ClientModel::subscribeToCoreSignals()
|
||||
{
|
||||
// Connect signals to client
|
||||
uiInterface.NotifyBlocksChanged.connect(boost::bind(NotifyBlocksChanged, this));
|
||||
uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
||||
uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
|
||||
}
|
||||
|
||||
void ClientModel::unsubscribeFromCoreSignals()
|
||||
{
|
||||
// Disconnect signals from client
|
||||
uiInterface.NotifyBlocksChanged.disconnect(boost::bind(NotifyBlocksChanged, this));
|
||||
uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
||||
uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ class CWallet;
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QDateTime;
|
||||
class QTimer;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
/** Model for Bitcoin network client. */
|
||||
|
@ -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:
|
|||
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
|
||||
|
|
|
@ -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()
|
|||
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;
|
||||
|
|
|
@ -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)
|
|||
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)
|
|||
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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
if(wallet->IsMine(txout))
|
||||
{
|
||||
TransactionRecord sub(hash, nTime);
|
||||
CBitcoinAddress address;
|
||||
sub.idx = parts.size(); // sequence number
|
||||
sub.credit = txout.nValue;
|
||||
if (wtx.IsCoinBase())
|
||||
{
|
||||
// Generated
|
||||
sub.type = TransactionRecord::Generated;
|
||||
}
|
||||
else if (ExtractAddress(txout.scriptPubKey, address) && wallet->HaveKey(address))
|
||||
{
|
||||
// Received by Bitcoin Address
|
||||
sub.type = TransactionRecord::RecvWithAddress;
|
||||
sub.address = address.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
|
||||
sub.type = TransactionRecord::RecvFromOther;
|
||||
sub.address = mapValue["from"];
|
||||
}
|
||||
|
||||
parts.append(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool fAllFromMe = true;
|
||||
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
||||
fAllFromMe = fAllFromMe && wallet->IsMine(txin);
|
||||
|
||||
bool fAllToMe = true;
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
fAllToMe = fAllToMe && wallet->IsMine(txout);
|
||||
|
||||
if (fAllFromMe && fAllToMe)
|
||||
{
|
||||
// Payment to self
|
||||
int64 nChange = wtx.GetChange();
|
||||
|
||||
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
|
||||
-(nDebit - nChange), nCredit - nChange));
|
||||
}
|
||||
else if (fAllFromMe)
|
||||
{
|
||||
//
|
||||
// Credit
|
||||
// Debit
|
||||
//
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
int64 nTxFee = nDebit - wtx.GetValueOut();
|
||||
|
||||
for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
|
||||
{
|
||||
const CTxOut& txout = wtx.vout[nOut];
|
||||
TransactionRecord sub(hash, nTime);
|
||||
sub.idx = parts.size();
|
||||
|
||||
if(wallet->IsMine(txout))
|
||||
{
|
||||
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);
|
||||
// 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
|
||||
{
|
||||
bool fAllFromMe = true;
|
||||
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
||||
fAllFromMe = fAllFromMe && wallet->IsMine(txin);
|
||||
|
||||
bool fAllToMe = true;
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
fAllToMe = fAllToMe && wallet->IsMine(txout);
|
||||
|
||||
if (fAllFromMe && fAllToMe)
|
||||
{
|
||||
// Payment to self
|
||||
int64 nChange = wtx.GetChange();
|
||||
|
||||
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
|
||||
-(nDebit - nChange), nCredit - nChange));
|
||||
}
|
||||
else if (fAllFromMe)
|
||||
{
|
||||
//
|
||||
// Debit
|
||||
//
|
||||
int64 nTxFee = nDebit - wtx.GetValueOut();
|
||||
|
||||
for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
|
||||
{
|
||||
const CTxOut& txout = wtx.vout[nOut];
|
||||
TransactionRecord sub(hash, nTime);
|
||||
sub.idx = parts.size();
|
||||
|
||||
if(wallet->IsMine(txout))
|
||||
{
|
||||
// Ignore parts sent to self, as this is usually the change
|
||||
// from a transaction sent back to our own address.
|
||||
continue;
|
||||
}
|
||||
|
||||
CBitcoinAddress address;
|
||||
if (ExtractAddress(txout.scriptPubKey, address))
|
||||
{
|
||||
// Sent to Bitcoin Address
|
||||
sub.type = TransactionRecord::SendToAddress;
|
||||
sub.address = address.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sent to IP, or other non-address transaction like OP_EVAL
|
||||
sub.type = TransactionRecord::SendToOther;
|
||||
sub.address = mapValue["to"];
|
||||
}
|
||||
|
||||
int64 nValue = txout.nValue;
|
||||
/* Add fee to first output */
|
||||
if (nTxFee > 0)
|
||||
{
|
||||
nValue += nTxFee;
|
||||
nTxFee = 0;
|
||||
}
|
||||
sub.debit = -nValue;
|
||||
|
||||
parts.append(sub);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Mixed debit transaction, can't break down payees
|
||||
//
|
||||
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
|
||||
}
|
||||
//
|
||||
// Mixed debit transaction, can't break down payees
|
||||
//
|
||||
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "bitcoinunits.h"
|
||||
|
||||
#include "wallet.h"
|
||||
#include "ui_interface.h"
|
||||
|
||||
#include <QLocale>
|
||||
#include <QList>
|
||||
|
@ -66,15 +67,14 @@ public:
|
|||
*/
|
||||
void refreshWallet()
|
||||
{
|
||||
#ifdef WALLET_UPDATE_DEBUG
|
||||
qDebug() << "refreshWallet";
|
||||
#endif
|
||||
OutputDebugStringF("refreshWallet\n");
|
||||
cachedWallet.clear();
|
||||
{
|
||||
LOCK(wallet->cs_wallet);
|
||||
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
|
||||
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.
|
||||
#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);
|
||||
|
||||
OutputDebugStringF("updateWallet %s %i\n", hash.ToString().c_str(), status);
|
||||
{
|
||||
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);
|
||||
// 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());
|
||||
if(showTransaction && !inModel)
|
||||
status = CT_NEW; /* Not in model, but want to show, treat as new */
|
||||
if(!showTransaction && inModel)
|
||||
status = CT_DELETED; /* In model, but want to hide, treat as deleted */
|
||||
}
|
||||
|
||||
// Determine if transaction is in model already
|
||||
bool inModel = false;
|
||||
if(lower != upper)
|
||||
OutputDebugStringF(" inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n",
|
||||
inWallet, inModel, lowerIndex, upperIndex, showTransaction, status);
|
||||
|
||||
switch(status)
|
||||
{
|
||||
case CT_NEW:
|
||||
if(inModel)
|
||||
{
|
||||
inModel = true;
|
||||
OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is already in model\n");
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef WALLET_UPDATE_DEBUG
|
||||
qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel
|
||||
<< lowerIndex << "-" << upperIndex;
|
||||
#endif
|
||||
|
||||
if(inWallet && !inModel)
|
||||
if(!inWallet)
|
||||
{
|
||||
OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is not in wallet\n");
|
||||
break;
|
||||
}
|
||||
if(showTransaction)
|
||||
{
|
||||
// Added -- insert at the right position
|
||||
QList<TransactionRecord> toInsert =
|
||||
|
@ -141,17 +147,22 @@ public:
|
|||
parent->endInsertRows();
|
||||
}
|
||||
}
|
||||
else if(!inWallet && inModel)
|
||||
break;
|
||||
case CT_DELETED:
|
||||
if(!inModel)
|
||||
{
|
||||
// Removed -- remove entire transaction from table
|
||||
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),
|
||||
wallet(wallet),
|
||||
walletModel(parent),
|
||||
priv(new TransactionTablePriv(wallet, this))
|
||||
priv(new TransactionTablePriv(wallet, this)),
|
||||
cachedNumBlocks(0)
|
||||
{
|
||||
columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount");
|
||||
|
||||
priv->refreshWallet();
|
||||
|
||||
QTimer *timer = new QTimer(this);
|
||||
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
|
||||
connect(timer, SIGNAL(timeout()), this, SLOT(updateConfirmations()));
|
||||
timer->start(MODEL_UPDATE_DELAY);
|
||||
}
|
||||
|
||||
|
@ -225,29 +237,23 @@ TransactionTableModel::~TransactionTableModel()
|
|||
delete priv;
|
||||
}
|
||||
|
||||
void TransactionTableModel::update()
|
||||
void TransactionTableModel::updateTransaction(const QString &hash, int status)
|
||||
{
|
||||
QList<uint256> updated;
|
||||
uint256 updated;
|
||||
updated.SetHex(hash.toStdString());
|
||||
|
||||
// Check if there are changes to wallet map
|
||||
priv->updateWallet(updated, status);
|
||||
}
|
||||
|
||||
void TransactionTableModel::updateConfirmations()
|
||||
{
|
||||
if(nBestHeight != cachedNumBlocks)
|
||||
{
|
||||
TRY_LOCK(wallet->cs_wallet, lockWallet);
|
||||
if (lockWallet && !wallet->vWalletUpdated.empty())
|
||||
{
|
||||
BOOST_FOREACH(uint256 hash, wallet->vWalletUpdated)
|
||||
{
|
||||
updated.append(hash);
|
||||
}
|
||||
wallet->vWalletUpdated.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if(!updated.empty())
|
||||
{
|
||||
priv->updateWallet(updated);
|
||||
|
||||
// Status (number of confirmations) and (possibly) description
|
||||
// columns changed for all rows.
|
||||
cachedNumBlocks = nBestHeight;
|
||||
// Blocks came in since last poll.
|
||||
// Invalidate status (number of confirmations) and (possibly) description
|
||||
// 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, ToAddress), index(priv->size()-1, ToAddress));
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ private:
|
|||
WalletModel *walletModel;
|
||||
QStringList columns;
|
||||
TransactionTablePriv *priv;
|
||||
int cachedNumBlocks;
|
||||
|
||||
QString lookupAddress(const std::string &address, bool tooltip) const;
|
||||
QVariant addressColor(const TransactionRecord *wtx) const;
|
||||
|
@ -72,8 +73,9 @@ private:
|
|||
QVariant txStatusDecoration(const TransactionRecord *wtx) const;
|
||||
QVariant txAddressDecoration(const TransactionRecord *wtx) const;
|
||||
|
||||
private slots:
|
||||
void update();
|
||||
public slots:
|
||||
void updateTransaction(const QString &hash, int status);
|
||||
void updateConfirmations();
|
||||
|
||||
friend class TransactionTablePriv;
|
||||
};
|
||||
|
|
|
@ -18,6 +18,13 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p
|
|||
{
|
||||
addressTableModel = new AddressTableModel(wallet, this);
|
||||
transactionTableModel = new TransactionTableModel(wallet, this);
|
||||
|
||||
subscribeToCoreSignals();
|
||||
}
|
||||
|
||||
WalletModel::~WalletModel()
|
||||
{
|
||||
unsubscribeFromCoreSignals();
|
||||
}
|
||||
|
||||
qint64 WalletModel::getBalance() const
|
||||
|
@ -40,30 +47,38 @@ int WalletModel::getNumTransactions() const
|
|||
return numTransactions;
|
||||
}
|
||||
|
||||
void WalletModel::update()
|
||||
void WalletModel::updateStatus()
|
||||
{
|
||||
qint64 newBalance = getBalance();
|
||||
qint64 newUnconfirmedBalance = getUnconfirmedBalance();
|
||||
int newNumTransactions = getNumTransactions();
|
||||
EncryptionStatus newEncryptionStatus = getEncryptionStatus();
|
||||
|
||||
if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance)
|
||||
emit balanceChanged(newBalance, newUnconfirmedBalance);
|
||||
|
||||
if(cachedNumTransactions != newNumTransactions)
|
||||
emit numTransactionsChanged(newNumTransactions);
|
||||
|
||||
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 newUnconfirmedBalance = getUnconfirmedBalance();
|
||||
int newNumTransactions = getNumTransactions();
|
||||
|
||||
if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance)
|
||||
emit balanceChanged(newBalance, newUnconfirmedBalance);
|
||||
if(cachedNumTransactions != newNumTransactions)
|
||||
emit numTransactionsChanged(newNumTransactions);
|
||||
|
||||
cachedBalance = newBalance;
|
||||
cachedUnconfirmedBalance = newUnconfirmedBalance;
|
||||
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)
|
||||
|
@ -139,7 +154,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
|
|||
}
|
||||
return TransactionCreationFailed;
|
||||
}
|
||||
if(!ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString()))
|
||||
if(!uiInterface.ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString()))
|
||||
{
|
||||
return Aborted;
|
||||
}
|
||||
|
@ -246,6 +261,47 @@ bool WalletModel::backupWallet(const QString &filename)
|
|||
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 WalletModel::requestUnlock()
|
||||
{
|
||||
|
|
|
@ -24,6 +24,7 @@ class WalletModel : public QObject
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0);
|
||||
~WalletModel();
|
||||
|
||||
enum StatusCode // Returned by sendCoins
|
||||
{
|
||||
|
@ -118,6 +119,8 @@ private:
|
|||
qint64 cachedNumTransactions;
|
||||
EncryptionStatus cachedEncryptionStatus;
|
||||
|
||||
void subscribeToCoreSignals();
|
||||
void unsubscribeFromCoreSignals();
|
||||
signals:
|
||||
// Signal that balance in wallet changed
|
||||
void balanceChanged(qint64 balance, qint64 unconfirmedBalance);
|
||||
|
@ -137,8 +140,12 @@ signals:
|
|||
void error(const QString &title, const QString &message, bool modal);
|
||||
|
||||
public slots:
|
||||
void update();
|
||||
void updateAddressList();
|
||||
/* Wallet status might have changed */
|
||||
void updateStatus();
|
||||
/* New transaction, or transaction changed status */
|
||||
void updateTransaction(const QString &hash, int status);
|
||||
/* New, updated or removed address book entry */
|
||||
void updateAddressBook(const QString &address, const QString &label, bool isMine, int status);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -73,8 +73,6 @@ Value importprivkey(const Array& params, bool fHelp)
|
|||
pwalletMain->ReacceptWalletTransactions();
|
||||
}
|
||||
|
||||
MainFrameRepaint();
|
||||
|
||||
return Value::null;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,15 @@
|
|||
#include "wallet.h"
|
||||
|
||||
CWallet* pwalletMain;
|
||||
CClientUIInterface uiInterface;
|
||||
|
||||
extern bool fPrintToConsole;
|
||||
extern void noui_connect();
|
||||
|
||||
struct TestingSetup {
|
||||
TestingSetup() {
|
||||
fPrintToConsole = true; // don't want to write to debug.log file
|
||||
noui_connect();
|
||||
pwalletMain = new CWallet();
|
||||
RegisterWallet(pwalletMain);
|
||||
}
|
||||
|
|
|
@ -6,45 +6,99 @@
|
|||
|
||||
#include <string>
|
||||
#include "util.h" // for int64
|
||||
#include <boost/signals2/signal.hpp>
|
||||
#include <boost/signals2/last_value.hpp>
|
||||
|
||||
#define wxYES 0x00000002
|
||||
#define wxOK 0x00000004
|
||||
#define wxNO 0x00000008
|
||||
#define wxYES_NO (wxYES|wxNO)
|
||||
#define wxCANCEL 0x00000010
|
||||
#define wxAPPLY 0x00000020
|
||||
#define wxCLOSE 0x00000040
|
||||
#define wxOK_DEFAULT 0x00000000
|
||||
#define wxYES_DEFAULT 0x00000000
|
||||
#define wxNO_DEFAULT 0x00000080
|
||||
#define wxCANCEL_DEFAULT 0x80000000
|
||||
#define wxICON_EXCLAMATION 0x00000100
|
||||
#define wxICON_HAND 0x00000200
|
||||
#define wxICON_WARNING wxICON_EXCLAMATION
|
||||
#define wxICON_ERROR wxICON_HAND
|
||||
#define wxICON_QUESTION 0x00000400
|
||||
#define wxICON_INFORMATION 0x00000800
|
||||
#define wxICON_STOP wxICON_HAND
|
||||
#define wxICON_ASTERISK wxICON_INFORMATION
|
||||
#define wxICON_MASK (0x00000100|0x00000200|0x00000400|0x00000800)
|
||||
#define wxFORWARD 0x00001000
|
||||
#define wxBACKWARD 0x00002000
|
||||
#define wxRESET 0x00004000
|
||||
#define wxHELP 0x00008000
|
||||
#define wxMORE 0x00010000
|
||||
#define wxSETUP 0x00020000
|
||||
// Force blocking, modal message box dialog (not just notification)
|
||||
#define wxMODAL 0x00040000
|
||||
class CBasicKeyStore;
|
||||
class CWallet;
|
||||
class uint256;
|
||||
|
||||
/* These UI communication functions are implemented in bitcoin.cpp (for ui) and noui.cpp (no ui) */
|
||||
/** General change type (added, updated, removed). */
|
||||
enum ChangeType
|
||||
{
|
||||
CT_NEW,
|
||||
CT_UPDATED,
|
||||
CT_DELETED
|
||||
};
|
||||
|
||||
extern int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK);
|
||||
extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption);
|
||||
extern void ThreadSafeHandleURI(const std::string& strURI);
|
||||
extern void MainFrameRepaint();
|
||||
extern void AddressBookRepaint();
|
||||
extern void QueueShutdown();
|
||||
extern void InitMessage(const std::string &message);
|
||||
extern std::string _(const char* psz);
|
||||
/** Signals for UI communication. */
|
||||
class CClientUIInterface
|
||||
{
|
||||
public:
|
||||
/** Flags for CClientUIInterface::ThreadSafeMessageBox */
|
||||
enum MessageBoxFlags
|
||||
{
|
||||
YES = 0x00000002,
|
||||
OK = 0x00000004,
|
||||
NO = 0x00000008,
|
||||
YES_NO = (YES|NO),
|
||||
CANCEL = 0x00000010,
|
||||
APPLY = 0x00000020,
|
||||
CLOSE = 0x00000040,
|
||||
OK_DEFAULT = 0x00000000,
|
||||
YES_DEFAULT = 0x00000000,
|
||||
NO_DEFAULT = 0x00000080,
|
||||
CANCEL_DEFAULT = 0x80000000,
|
||||
ICON_EXCLAMATION = 0x00000100,
|
||||
ICON_HAND = 0x00000200,
|
||||
ICON_WARNING = ICON_EXCLAMATION,
|
||||
ICON_ERROR = ICON_HAND,
|
||||
ICON_QUESTION = 0x00000400,
|
||||
ICON_INFORMATION = 0x00000800,
|
||||
ICON_STOP = ICON_HAND,
|
||||
ICON_ASTERISK = ICON_INFORMATION,
|
||||
ICON_MASK = (0x00000100|0x00000200|0x00000400|0x00000800),
|
||||
FORWARD = 0x00001000,
|
||||
BACKWARD = 0x00002000,
|
||||
RESET = 0x00004000,
|
||||
HELP = 0x00008000,
|
||||
MORE = 0x00010000,
|
||||
SETUP = 0x00020000,
|
||||
// Force blocking, modal message box dialog (not just OS notification)
|
||||
MODAL = 0x00040000
|
||||
};
|
||||
|
||||
/** Show message box. */
|
||||
boost::signals2::signal<void (const std::string& message, const std::string& caption, int style)> ThreadSafeMessageBox;
|
||||
|
||||
/** Ask the user whether he want to pay a fee or not. */
|
||||
boost::signals2::signal<bool (int64 nFeeRequired, const std::string& strCaption), boost::signals2::last_value<bool> > ThreadSafeAskFee;
|
||||
|
||||
/** Handle an URL passed on the command line. */
|
||||
boost::signals2::signal<void (const std::string& strURI)> ThreadSafeHandleURI;
|
||||
|
||||
/** Progress message during initialization. */
|
||||
boost::signals2::signal<void (const std::string &message)> InitMessage;
|
||||
|
||||
/** Initiate client shutdown. */
|
||||
boost::signals2::signal<void ()> QueueShutdown;
|
||||
|
||||
/** Translate a message to the native language of the user. */
|
||||
boost::signals2::signal<std::string (const char* psz)> Translate;
|
||||
|
||||
/** Block chain changed. */
|
||||
boost::signals2::signal<void ()> NotifyBlocksChanged;
|
||||
|
||||
/** Number of network connections changed. */
|
||||
boost::signals2::signal<void (int newNumConnections)> NotifyNumConnectionsChanged;
|
||||
|
||||
/**
|
||||
* New, updated or cancelled alert.
|
||||
* @note called with lock cs_mapAlerts held.
|
||||
*/
|
||||
boost::signals2::signal<void (const uint256 &hash, ChangeType status)> NotifyAlertChanged;
|
||||
};
|
||||
|
||||
extern CClientUIInterface uiInterface;
|
||||
|
||||
/**
|
||||
* Translation function: Call Translate signal on UI interface, which returns a boost::optional result.
|
||||
* If no translation slot is registered, nothing is returned, and simply return the input.
|
||||
*/
|
||||
inline std::string _(const char* psz)
|
||||
{
|
||||
boost::optional<std::string> rv = uiInterface.Translate(psz);
|
||||
return rv ? (*rv) : psz;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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.");
|
||||
strMiscWarning = strMessage;
|
||||
printf("*** %s\n", strMessage.c_str());
|
||||
ThreadSafeMessageBox(strMessage+" ", string("Bitcoin"), wxOK | wxICON_EXCLAMATION);
|
||||
uiInterface.ThreadSafeMessageBox(strMessage+" ", string("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -274,7 +274,9 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
|
|||
// Need to completely rewrite the wallet file; if we don't, bdb might keep
|
||||
// bits of the unencrypted private key in slack space in the database file.
|
||||
CDB::Rewrite(strWalletFile);
|
||||
|
||||
}
|
||||
NotifyStatusChanged(this);
|
||||
|
||||
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());
|
||||
wtx.MarkSpent(txin.prevout.n);
|
||||
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
|
||||
// Notify UI
|
||||
vWalletUpdated.push_back(hash);
|
||||
|
||||
// since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
|
||||
WalletUpdateSpent(wtx);
|
||||
}
|
||||
|
||||
// Refresh UI
|
||||
MainFrameRepaint();
|
||||
// Notify UI of new or updated transaction
|
||||
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1183,7 +1182,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
|
|||
coin.BindWallet(this);
|
||||
coin.MarkSpent(txin.prevout.n);
|
||||
coin.WriteToDisk();
|
||||
vWalletUpdated.push_back(coin.GetHash());
|
||||
NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);
|
||||
}
|
||||
|
||||
if (fFileBacked)
|
||||
|
@ -1202,7 +1201,6 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
|
|||
}
|
||||
wtxNew.RelayWalletTransaction();
|
||||
}
|
||||
MainFrameRepaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1231,13 +1229,12 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew,
|
|||
return strError;
|
||||
}
|
||||
|
||||
if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending...")))
|
||||
if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired, _("Sending...")))
|
||||
return "ABORTED";
|
||||
|
||||
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.");
|
||||
|
||||
MainFrameRepaint();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -1290,8 +1287,9 @@ int CWallet::LoadWallet(bool& fFirstRunRet)
|
|||
|
||||
bool CWallet::SetAddressBookName(const CBitcoinAddress& address, const string& strName)
|
||||
{
|
||||
std::map<CBitcoinAddress, std::string>::iterator mi = mapAddressBook.find(address);
|
||||
mapAddressBook[address] = strName;
|
||||
AddressBookRepaint();
|
||||
NotifyAddressBookChanged(this, address.ToString(), strName, HaveKey(address), (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED);
|
||||
if (!fFileBacked)
|
||||
return false;
|
||||
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)
|
||||
{
|
||||
mapAddressBook.erase(address);
|
||||
AddressBookRepaint();
|
||||
NotifyAddressBookChanged(this, address.ToString(), "", HaveKey(address), CT_DELETED);
|
||||
if (!fFileBacked)
|
||||
return false;
|
||||
return CWalletDB(strWalletFile).EraseName(address.ToString());
|
||||
|
@ -1558,3 +1556,14 @@ void CWallet::GetAllReserveAddresses(set<CBitcoinAddress>& setAddress)
|
|||
setAddress.insert(address);
|
||||
}
|
||||
}
|
||||
|
||||
void CWallet::UpdatedTransaction(const uint256 &hashTx)
|
||||
{
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
// Only notify UI if this transaction is in this wallet
|
||||
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);
|
||||
if (mi != mapWallet.end())
|
||||
NotifyTransactionChanged(this, hashTx, CT_UPDATED);
|
||||
}
|
||||
}
|
||||
|
|
21
src/wallet.h
21
src/wallet.h
|
@ -9,6 +9,7 @@
|
|||
#include "key.h"
|
||||
#include "keystore.h"
|
||||
#include "script.h"
|
||||
#include "ui_interface.h"
|
||||
|
||||
class CWalletTx;
|
||||
class CReserveKey;
|
||||
|
@ -102,8 +103,6 @@ public:
|
|||
}
|
||||
|
||||
std::map<uint256, CWalletTx> mapWallet;
|
||||
std::vector<uint256> vWalletUpdated;
|
||||
|
||||
std::map<uint256, int> mapRequestCount;
|
||||
|
||||
std::map<CBitcoinAddress, std::string> mapAddressBook;
|
||||
|
@ -232,13 +231,7 @@ public:
|
|||
|
||||
bool DelAddressBookName(const CBitcoinAddress& address);
|
||||
|
||||
void UpdatedTransaction(const uint256 &hashTx)
|
||||
{
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
vWalletUpdated.push_back(hashTx);
|
||||
}
|
||||
}
|
||||
void UpdatedTransaction(const uint256 &hashTx);
|
||||
|
||||
void PrintWallet(const CBlock& block);
|
||||
|
||||
|
@ -269,6 +262,16 @@ public:
|
|||
|
||||
// get the current wallet format (the oldest client version guaranteed to understand this wallet)
|
||||
int GetVersion() { return nWalletVersion; }
|
||||
|
||||
/** Address book entry changed.
|
||||
* @note called with lock cs_wallet held.
|
||||
*/
|
||||
boost::signals2::signal<void (CWallet *wallet, const std::string &address, const std::string &label, 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. */
|
||||
|
|
Loading…
Add table
Reference in a new issue