aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bitcoinrpc.cpp16
-rw-r--r--src/init.cpp44
-rw-r--r--src/init.h1
-rw-r--r--src/keystore.cpp15
-rw-r--r--src/keystore.h19
-rw-r--r--src/main.cpp26
-rw-r--r--src/main.h5
-rw-r--r--src/net.cpp4
-rw-r--r--src/noui.cpp33
-rw-r--r--src/qt/addressbookpage.cpp29
-rw-r--r--src/qt/addressbookpage.h5
-rw-r--r--src/qt/addresstablemodel.cpp85
-rw-r--r--src/qt/addresstablemodel.h9
-rw-r--r--src/qt/bitcoin.cpp53
-rw-r--r--src/qt/bitcoingui.cpp9
-rw-r--r--src/qt/bitcoingui.h2
-rw-r--r--src/qt/clientmodel.cpp102
-rw-r--r--src/qt/clientmodel.h17
-rw-r--r--src/qt/forms/messagepage.ui2
-rw-r--r--src/qt/optionsdialog.cpp8
-rw-r--r--src/qt/qtipcserver.cpp4
-rw-r--r--src/qt/rpcconsole.cpp17
-rw-r--r--src/qt/rpcconsole.h3
-rw-r--r--src/qt/transactionrecord.cpp181
-rw-r--r--src/qt/transactiontablemodel.cpp146
-rw-r--r--src/qt/transactiontablemodel.h6
-rw-r--r--src/qt/verifymessagedialog.cpp2
-rw-r--r--src/qt/walletmodel.cpp74
-rw-r--r--src/qt/walletmodel.h11
-rw-r--r--src/rpcdump.cpp2
-rw-r--r--src/test/test_bitcoin.cpp4
-rw-r--r--src/ui_interface.h132
-rw-r--r--src/util.cpp114
-rw-r--r--src/util.h6
-rw-r--r--src/wallet.cpp35
-rw-r--r--src/wallet.h21
36 files changed, 751 insertions, 491 deletions
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index 334425a9a9..6c51bad6db 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -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;
}
diff --git a/src/init.cpp b/src/init.cpp
index 524c68138c..0ddcd558a1 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -23,6 +23,7 @@ using namespace std;
using namespace boost;
CWallet* pwalletMain;
+CClientUIInterface uiInterface;
//////////////////////////////////////////////////////////////////////////////
//
@@ -91,17 +92,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;
@@ -157,17 +147,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;
}
@@ -368,7 +374,7 @@ bool AppInit2()
fprintf(stdout, "Bitcoin server starting\n");
int64 nStart;
- InitMessage(_("Loading addresses..."));
+ uiInterface.InitMessage(_("Loading addresses..."));
printf("Loading addresses...\n");
nStart = GetTimeMillis();
@@ -381,7 +387,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())
@@ -407,7 +413,7 @@ bool AppInit2()
}
}
- InitMessage(_("Loading wallet..."));
+ uiInterface.InitMessage(_("Loading wallet..."));
printf("Loading wallet...\n");
nStart = GetTimeMillis();
bool fFirstRun;
@@ -475,14 +481,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
@@ -651,7 +657,7 @@ bool AppInit2()
if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee))
return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s'"), mapArgs["-paytxfee"].c_str()));
if (nTransactionFee > 0.25 * COIN)
- InitWarning(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction."));
+ InitWarning(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction."));
}
//
diff --git a/src/init.h b/src/init.h
index 9a8f98cce9..970527c7ce 100644
--- a/src/init.h
+++ b/src/init.h
@@ -10,7 +10,6 @@
extern CWallet* pwalletMain;
void Shutdown(void* parg);
-bool AppInit(int argc, char* argv[]);
bool AppInit2();
std::string HelpMessage();
diff --git a/src/keystore.cpp b/src/keystore.cpp
index 313518711b..bdeae30135 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -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;
}
diff --git a/src/keystore.h b/src/keystore.h
index 52889b184e..6b5d01cfa5 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -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
diff --git a/src/main.cpp b/src/main.cpp
index 8ff6d6fec0..e62331debf 100644
--- a/src/main.cpp
+++ b/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;
}
diff --git a/src/main.h b/src/main.h
index 9700909f8b..f4a9759d76 100644
--- a/src/main.h
+++ b/src/main.h
@@ -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
diff --git a/src/net.cpp b/src/net.cpp
index 65b78bf6fa..8eff9cbe78 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -705,7 +705,7 @@ void ThreadSocketHandler2(void* parg)
if (vNodes.size() != nPrevNodeCount)
{
nPrevNodeCount = vNodes.size();
- MainFrameRepaint();
+ uiInterface.NotifyNumConnectionsChanged(vNodes.size());
}
@@ -1760,7 +1760,7 @@ bool BindListenPort(const CService &addrBind, string& strError)
{
int nErr = WSAGetLastError();
if (nErr == WSAEADDRINUSE)
- strError = strprintf(_("Unable to bind to %s on this computer. Bitcoin is probably already running."), addrBind.ToString().c_str());
+ strError = strprintf(_("Unable to bind to %s on this computer. Bitcoin is probably already running."), addrBind.ToString().c_str());
else
strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %d, %s)"), addrBind.ToString().c_str(), nErr, strerror(nErr));
printf("%s\n", strError.c_str());
diff --git a/src/noui.cpp b/src/noui.cpp
index 08a08b439a..57f53b1c0d 100644
--- a/src/noui.cpp
+++ b/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);
+}
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index d314e62b5a..c207987561 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -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();
+ }
+}
diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h
index b2cf2db979..b2e91c7cb2 100644
--- a/src/qt/addressbookpage.h
+++ b/src/qt/addressbookpage.h
@@ -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
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 7b95f51c04..75ea2c12c5 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -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()));
+}
diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h
index 7fd07cfb81..42974e3e1f 100644
--- a/src/qt/addresstablemodel.h
+++ b/src/qt/addresstablemodel.h
@@ -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
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 479638714f..bdc6ea6ffd 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -37,15 +37,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,
@@ -58,10 +56,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;
@@ -76,7 +73,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;
@@ -85,21 +82,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)
{
@@ -108,7 +91,7 @@ void InitMessage(const std::string &message)
}
}
-void QueueShutdown()
+static void QueueShutdown()
{
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
}
@@ -116,7 +99,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();
}
@@ -148,14 +131,14 @@ HelpMessageBox::HelpMessageBox(QWidget *parent):
QMessageBox(parent)
{
header = tr("Bitcoin-Qt") + " " + tr("version") + " " +
- QString::fromStdString(FormatFullVersion()) + "\n\n" +
+ QString::fromStdString(FormatFullVersion()) + "\n\n" +
tr("Usage:") + "\n" +
- " bitcoin-qt [options] " + "\n";
+ " bitcoin-qt [" + tr("options") + "] " + "\n";
coreOptions = QString::fromStdString(HelpMessage());
uiOptions = tr("UI options") + ":\n" +
- " -lang=<lang> " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
- " -min " + tr("Start minimized") + "\n" +
- " -splash " + tr("Show splash screen on startup (default: 1)") + "\n";
+ " -lang=<lang> " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
+ " -min " + tr("Start minimized") + "\n" +
+ " -splash " + tr("Show splash screen on startup (default: 1)") + "\n";
setWindowTitle(tr("Bitcoin-Qt"));
setTextFormat(Qt::PlainText);
@@ -264,6 +247,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"))
@@ -305,9 +296,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);
@@ -348,8 +337,6 @@ int main(int argc, char *argv[])
window.setClientModel(0);
window.setWalletModel(0);
guiref = 0;
- clientmodel = 0;
- walletmodel = 0;
}
Shutdown(NULL);
}
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 7070e59444..9deaa4b6d3 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -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)
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 88e6d064d7..8a7f6e541b 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -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
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 85ab03612d..64fd2a9450 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -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));
+}
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 67835db727..0349c389c5 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -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
diff --git a/src/qt/forms/messagepage.ui b/src/qt/forms/messagepage.ui
index 7c8f3b5ad6..0b685aae1d 100644
--- a/src/qt/forms/messagepage.ui
+++ b/src/qt/forms/messagepage.ui
@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
- <string>Sign Message Dialog</string>
+ <string>Sign Message</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index c3260217bb..7c6ad087cb 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -249,7 +249,7 @@ DisplayOptionsPage::DisplayOptionsPage(QWidget *parent):
QHBoxLayout *lang_hbox = new QHBoxLayout();
lang_hbox->addSpacing(18);
- QLabel *lang_label = new QLabel(tr("User Interface &Language: "));
+ QLabel *lang_label = new QLabel(tr("User Interface &Language:"));
lang_hbox->addWidget(lang_label);
lang = new QValueComboBox(this);
// Make list of languages
@@ -270,7 +270,7 @@ DisplayOptionsPage::DisplayOptionsPage(QWidget *parent):
QHBoxLayout *unit_hbox = new QHBoxLayout();
unit_hbox->addSpacing(18);
- QLabel *unit_label = new QLabel(tr("&Unit to show amounts in: "));
+ QLabel *unit_label = new QLabel(tr("&Unit to show amounts in:"));
unit_hbox->addWidget(unit_label);
unit = new QValueComboBox(this);
unit->setModel(new BitcoinUnits(this));
@@ -354,7 +354,7 @@ NetworkOptionsPage::NetworkOptionsPage(QWidget *parent):
QHBoxLayout *proxy_hbox = new QHBoxLayout();
proxy_hbox->addSpacing(18);
- QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: "));
+ QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP:"));
proxy_hbox->addWidget(proxy_ip_label);
proxy_ip = new QLineEdit();
proxy_ip->setMaximumWidth(140);
@@ -363,7 +363,7 @@ NetworkOptionsPage::NetworkOptionsPage(QWidget *parent):
proxy_ip->setToolTip(tr("IP address of the proxy (e.g. 127.0.0.1)"));
proxy_ip_label->setBuddy(proxy_ip);
proxy_hbox->addWidget(proxy_ip);
- QLabel *proxy_port_label = new QLabel(tr("&Port: "));
+ QLabel *proxy_port_label = new QLabel(tr("&Port:"));
proxy_hbox->addWidget(proxy_port_label);
proxy_port = new QLineEdit();
proxy_port->setMaximumWidth(55);
diff --git a/src/qt/qtipcserver.cpp b/src/qt/qtipcserver.cpp
index 06ada5aaca..3d7d90e902 100644
--- a/src/qt/qtipcserver.cpp
+++ b/src/qt/qtipcserver.cpp
@@ -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;
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 78dbcd7daa..7029ee33bc 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -109,7 +109,9 @@ RPCConsole::RPCConsole(QWidget *parent) :
{
ui->setupUi(this);
-#ifndef WIN32
+#ifdef WIN32
+ ui->openDebugLogfileButton->setIcon(QIcon(":/icons/export"));
+#else
// Show Debug logfile label and Open button only for Windows
ui->labelDebugLogfile->setVisible(false);
ui->openDebugLogfileButton->setVisible(false);
@@ -155,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());
@@ -166,7 +168,7 @@ void RPCConsole::setClientModel(ClientModel *model)
setNumConnections(model->getNumConnections());
ui->isTestNet->setChecked(model->isTestNet());
- setNumBlocks(model->getNumBlocks());
+ setNumBlocks(model->getNumBlocks(), model->getNumBlocksOfPeers());
}
}
@@ -207,9 +209,9 @@ void RPCConsole::clear()
"b { color: #006060; } "
);
- message(CMD_REPLY, tr("Welcome to the Bitcoin RPC console.<br>"
- "Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.<br>"
- "Type <b>help</b> for an overview of available commands."), true);
+ message(CMD_REPLY, (tr("Welcome to the Bitcoin RPC console.") + "<br>" +
+ tr("Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.") + "<br>" +
+ tr("Type <b>help</b> for an overview of available commands.")), true);
}
void RPCConsole::message(int category, const QString &message, bool html)
@@ -233,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
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index c64ea5710c..4b71cdb988 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -33,6 +33,7 @@ protected:
private slots:
void on_lineEdit_returnPressed();
void on_tabWidget_currentChanged(int index);
+ /** open the debug.log from the current datadir */
void on_openDebugLogfileButton_clicked();
public slots:
@@ -41,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 */
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 53cfb409ff..017244ffd0 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -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));
}
}
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 5f505f444e..d36bb495a0 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -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());
-
- // Determine if transaction is in model already
- bool inModel = false;
- if(lower != upper)
- {
- inModel = true;
- }
+ if(showTransaction && !inModel)
+ status = CT_NEW; /* Not in model, but want to show, treat as new */
+ if(!showTransaction && inModel)
+ status = CT_DELETED; /* In model, but want to hide, treat as deleted */
+ }
-#ifdef WALLET_UPDATE_DEBUG
- qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel
- << lowerIndex << "-" << upperIndex;
-#endif
+ OutputDebugStringF(" inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n",
+ inWallet, inModel, lowerIndex, upperIndex, showTransaction, status);
- if(inWallet && !inModel)
+ switch(status)
+ {
+ case CT_NEW:
+ if(inModel)
+ {
+ OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is already in model\n");
+ break;
+ }
+ if(!inWallet)
+ {
+ OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is not in wallet\n");
+ break;
+ }
+ if(showTransaction)
{
// Added -- insert at the right position
QList<TransactionRecord> toInsert =
@@ -141,17 +147,22 @@ public:
parent->endInsertRows();
}
}
- else if(!inWallet && inModel)
- {
- // Removed -- remove entire transaction from table
- parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
- cachedWallet.erase(lower, upper);
- parent->endRemoveRows();
- }
- else if(inWallet && inModel)
+ break;
+ case CT_DELETED:
+ if(!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
- {
- TRY_LOCK(wallet->cs_wallet, lockWallet);
- if (lockWallet && !wallet->vWalletUpdated.empty())
- {
- BOOST_FOREACH(uint256 hash, wallet->vWalletUpdated)
- {
- updated.append(hash);
- }
- wallet->vWalletUpdated.clear();
- }
- }
+ priv->updateWallet(updated, status);
+}
- if(!updated.empty())
+void TransactionTableModel::updateConfirmations()
+{
+ if(nBestHeight != cachedNumBlocks)
{
- 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));
}
diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h
index db88a0604f..0aafa70915 100644
--- a/src/qt/transactiontablemodel.h
+++ b/src/qt/transactiontablemodel.h
@@ -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;
};
diff --git a/src/qt/verifymessagedialog.cpp b/src/qt/verifymessagedialog.cpp
index 1f82e2ac31..0bac24820c 100644
--- a/src/qt/verifymessagedialog.cpp
+++ b/src/qt/verifymessagedialog.cpp
@@ -25,7 +25,7 @@ VerifyMessageDialog::VerifyMessageDialog(AddressTableModel *addressModel, QWidge
#if (QT_VERSION >= 0x040700)
/* Do not move this to the XML file, Qt before 4.7 will choke on it */
ui->lnSig->setPlaceholderText(tr("Enter Bitcoin signature"));
- ui->lnAddress->setPlaceholderText(tr("Click \"Apply\" to obtain address"));
+ ui->lnAddress->setPlaceholderText(tr("Click \"Verify Message\" to obtain address"));
#endif
GUIUtil::setupAddressWidget(ui->lnAddress, this);
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index b9ccb06c09..b89c3dba33 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -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()
+{
+ EncryptionStatus newEncryptionStatus = getEncryptionStatus();
+
+ if(cachedEncryptionStatus != newEncryptionStatus)
+ emit encryptionStatusChanged(newEncryptionStatus);
+}
+
+void WalletModel::updateTransaction(const QString &hash, int status)
{
+ if(transactionTableModel)
+ transactionTableModel->updateTransaction(hash, status);
+
+ // Balance and number of transactions might have changed
qint64 newBalance = getBalance();
qint64 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);
-
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()
{
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 6c47f61bef..8b615ffe8e 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -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);
};
diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp
index 1119aeaf75..9f5693a8bd 100644
--- a/src/rpcdump.cpp
+++ b/src/rpcdump.cpp
@@ -73,8 +73,6 @@ Value importprivkey(const Array& params, bool fHelp)
pwalletMain->ReacceptWalletTransactions();
}
- MainFrameRepaint();
-
return Value::null;
}
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 7ff7545ab4..bf597c9b73 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -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);
}
diff --git a/src/ui_interface.h b/src/ui_interface.h
index 514768086d..711a56d5ec 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -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
-
-/* These UI communication functions are implemented in bitcoin.cpp (for ui) and noui.cpp (no ui) */
-
-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);
+class CBasicKeyStore;
+class CWallet;
+class uint256;
+
+/** General change type (added, updated, removed). */
+enum ChangeType
+{
+ CT_NEW,
+ CT_UPDATED,
+ CT_DELETED
+};
+
+/** 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
diff --git a/src/util.cpp b/src/util.cpp
index b0c80f6dfc..108f33b78e 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -182,8 +182,6 @@ int GetRandInt(int nMax)
-
-
inline int OutputDebugStringF(const char* pszFormat, ...)
{
int ret = 0;
@@ -230,68 +228,30 @@ inline int OutputDebugStringF(const char* pszFormat, ...)
{
static CCriticalSection cs_OutputDebugStringF;
- // accumulate a line at a time
+ // accumulate and output a line at a time
{
LOCK(cs_OutputDebugStringF);
- static char pszBuffer[50000];
- static char* pend;
- if (pend == NULL)
- pend = pszBuffer;
+ static std::string buffer;
+
va_list arg_ptr;
va_start(arg_ptr, pszFormat);
- int limit = END(pszBuffer) - pend - 2;
- int ret = _vsnprintf(pend, limit, pszFormat, arg_ptr);
+ buffer += vstrprintf(pszFormat, arg_ptr);
va_end(arg_ptr);
- if (ret < 0 || ret >= limit)
- {
- pend = END(pszBuffer) - 2;
- *pend++ = '\n';
- }
- else
- pend += ret;
- *pend = '\0';
- char* p1 = pszBuffer;
- char* p2;
- while ((p2 = strchr(p1, '\n')))
+
+ int line_start = 0, line_end;
+ while((line_end = buffer.find('\n', line_start)) != -1)
{
- p2++;
- char c = *p2;
- *p2 = '\0';
- OutputDebugStringA(p1);
- *p2 = c;
- p1 = p2;
+ OutputDebugStringA(buffer.substr(line_start, line_end - line_start).c_str());
+ line_start = line_end + 1;
}
- if (p1 != pszBuffer)
- memmove(pszBuffer, p1, pend - p1 + 1);
- pend -= (p1 - pszBuffer);
+ buffer.erase(0, line_start);
}
}
#endif
return ret;
}
-
-// Safer snprintf
-// - prints up to limit-1 characters
-// - output string is always null terminated even if limit reached
-// - return value is the number of characters actually printed
-int my_snprintf(char* buffer, size_t limit, const char* format, ...)
-{
- if (limit == 0)
- return 0;
- va_list arg_ptr;
- va_start(arg_ptr, format);
- int ret = _vsnprintf(buffer, limit, format, arg_ptr);
- va_end(arg_ptr);
- if (ret < 0 || ret >= (int)limit)
- {
- ret = limit - 1;
- buffer[limit-1] = 0;
- }
- return ret;
-}
-
-string real_strprintf(const std::string &format, int dummy, ...)
+string vstrprintf(const std::string &format, va_list ap)
{
char buffer[50000];
char* p = buffer;
@@ -300,7 +260,7 @@ string real_strprintf(const std::string &format, int dummy, ...)
loop
{
va_list arg_ptr;
- va_start(arg_ptr, dummy);
+ va_copy(arg_ptr, ap);
ret = _vsnprintf(p, limit, format.c_str(), arg_ptr);
va_end(arg_ptr);
if (ret >= 0 && ret < limit)
@@ -318,19 +278,22 @@ string real_strprintf(const std::string &format, int dummy, ...)
return str;
}
+string real_strprintf(const std::string &format, int dummy, ...)
+{
+ va_list arg_ptr;
+ va_start(arg_ptr, dummy);
+ string str = vstrprintf(format, arg_ptr);
+ va_end(arg_ptr);
+ return str;
+}
+
bool error(const char *format, ...)
{
- char buffer[50000];
- int limit = sizeof(buffer);
va_list arg_ptr;
va_start(arg_ptr, format);
- int ret = _vsnprintf(buffer, limit, format, arg_ptr);
+ std::string str = vstrprintf(format, arg_ptr);
va_end(arg_ptr);
- if (ret < 0 || ret >= limit)
- {
- buffer[limit - 1] = 0;
- }
- printf("ERROR: %s\n", buffer);
+ printf("ERROR: %s\n", str.c_str());
return false;
}
@@ -758,7 +721,7 @@ bool WildcardMatch(const string& str, const string& mask)
-void FormatException(char* pszMessage, std::exception* pex, const char* pszThread)
+static std::string FormatException(std::exception* pex, const char* pszThread)
{
#ifdef WIN32
char pszModule[MAX_PATH] = "";
@@ -767,37 +730,34 @@ void FormatException(char* pszMessage, std::exception* pex, const char* pszThrea
const char* pszModule = "bitcoin";
#endif
if (pex)
- snprintf(pszMessage, 1000,
+ return strprintf(
"EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread);
else
- snprintf(pszMessage, 1000,
+ return strprintf(
"UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread);
}
void LogException(std::exception* pex, const char* pszThread)
{
- char pszMessage[10000];
- FormatException(pszMessage, pex, pszThread);
- printf("\n%s", pszMessage);
+ std::string message = FormatException(pex, pszThread);
+ printf("\n%s", message.c_str());
}
void PrintException(std::exception* pex, const char* pszThread)
{
- char pszMessage[10000];
- FormatException(pszMessage, pex, pszThread);
- printf("\n\n************************\n%s\n", pszMessage);
- fprintf(stderr, "\n\n************************\n%s\n", pszMessage);
- strMiscWarning = pszMessage;
+ std::string message = FormatException(pex, pszThread);
+ printf("\n\n************************\n%s\n", message.c_str());
+ fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
+ strMiscWarning = message;
throw;
}
void PrintExceptionContinue(std::exception* pex, const char* pszThread)
{
- char pszMessage[10000];
- FormatException(pszMessage, pex, pszThread);
- printf("\n\n************************\n%s\n", pszMessage);
- fprintf(stderr, "\n\n************************\n%s\n", pszMessage);
- strMiscWarning = pszMessage;
+ std::string message = FormatException(pex, pszThread);
+ printf("\n\n************************\n%s\n", message.c_str());
+ fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
+ strMiscWarning = message;
}
boost::filesystem::path GetDefaultDataDir()
@@ -1040,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);
}
}
}
diff --git a/src/util.h b/src/util.h
index f7bdaf5332..b90cb43eec 100644
--- a/src/util.h
+++ b/src/util.h
@@ -43,11 +43,6 @@ static const int64 CENT = 1000000;
#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0]))
#define printf OutputDebugStringF
-#ifdef snprintf
-#undef snprintf
-#endif
-#define snprintf my_snprintf
-
#ifndef PRI64d
#if defined(_MSC_VER) || defined(__MSVCRT__)
#define PRI64d "I64d"
@@ -133,6 +128,7 @@ int my_snprintf(char* buffer, size_t limit, const char* format, ...);
*/
std::string real_strprintf(const std::string &format, int dummy, ...);
#define strprintf(format, ...) real_strprintf(format, 0, __VA_ARGS__)
+std::string vstrprintf(const std::string &format, va_list ap);
bool error(const char *format, ...);
void LogException(std::exception* pex, const char* pszThread);
diff --git a/src/wallet.cpp b/src/wallet.cpp
index 768f9f85ea..d67cdf4b71 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -274,7 +274,9 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// Need to completely rewrite the wallet file; if we don't, bdb might keep
// 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);
+ }
+}
diff --git a/src/wallet.h b/src/wallet.h
index 44c11e2ec4..55deffdde2 100644
--- a/src/wallet.h
+++ b/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. */