Add traffic monitor
[novacoin.git] / src / qt / clientmodel.cpp
index 822c03d..c100188 100644 (file)
@@ -1,43 +1,51 @@
 #include "clientmodel.h"
-#include "main.h"
 #include "guiconstants.h"
 #include "optionsmodel.h"
 #include "addresstablemodel.h"
 #include "transactiontablemodel.h"
 
+#include "alert.h"
+#include "main.h"
+#include "ui_interface.h"
+
+#include <QDateTime>
 #include <QTimer>
 
-ClientModel::ClientModel(QObject *parent) :
-    QObject(parent), optionsModel(0), addressTableModel(0),
-    transactionTableModel(0)
+extern double GetPoSKernelPS();
+extern double GetDifficulty(const CBlockIndex* blockindex);
+
+static const int64 nClientStartupTime = GetTime();
+
+ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
+    QObject(parent), optionsModel(optionsModel),
+    cachedNumBlocks(0), cachedNumBlocksOfPeers(0), pollTimer(0)
 {
-    // Until signal notifications is built into the bitcoin core,
-    //  simply update everything after polling using a timer.
-    QTimer *timer = new QTimer(this);
-    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
-    timer->start(MODEL_UPDATE_DELAY);
+    numBlocksAtStartup = -1;
+
+    pollTimer = new QTimer(this);
+    pollTimer->setInterval(MODEL_UPDATE_DELAY);
+    pollTimer->start();
+    connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
 
-    optionsModel = new OptionsModel(this);
-    addressTableModel = new AddressTableModel(this);
-    transactionTableModel = new TransactionTableModel(this);
+    subscribeToCoreSignals();
 }
 
-qint64 ClientModel::getBalance() const
+ClientModel::~ClientModel()
 {
-    return GetBalance();
+    unsubscribeFromCoreSignals();
 }
 
-QString ClientModel::getAddress() const
+double ClientModel::getPoSKernelPS()
 {
-    std::vector<unsigned char> vchPubKey;
-    if (CWalletDB("r").ReadDefaultKey(vchPubKey))
-    {
-        return QString::fromStdString(PubKeyToAddress(vchPubKey));
-    }
+    return GetPoSKernelPS();
+}
+
+double ClientModel::getDifficulty(bool fProofofStake)
+{
+    if (fProofofStake)
+       return GetDifficulty(GetLastBlockIndex(pindexBest,true));
     else
-    {
-        return QString();
-    }
+       return GetDifficulty(GetLastBlockIndex(pindexBest,false));
 }
 
 int ClientModel::getNumConnections() const
@@ -50,92 +58,75 @@ int ClientModel::getNumBlocks() const
     return nBestHeight;
 }
 
-int ClientModel::getNumTransactions() const
+int ClientModel::getNumBlocksAtStartup()
 {
-    int numTransactions = 0;
-    CRITICAL_BLOCK(cs_mapWallet)
-    {
-        numTransactions = mapWallet.size();
-    }
-    return numTransactions;
+    if (numBlocksAtStartup == -1) numBlocksAtStartup = getNumBlocks();
+    return numBlocksAtStartup;
 }
 
-void ClientModel::update()
+quint64 ClientModel::getTotalBytesRecv() const
 {
-    // Plainly emit all signals for now. To be more efficient this should check
-    //   whether the values actually changed first.
-    emit balanceChanged(getBalance());
-    emit addressChanged(getAddress());
-    emit numConnectionsChanged(getNumConnections());
-    emit numBlocksChanged(getNumBlocks());
-    emit numTransactionsChanged(getNumTransactions());
+    return CNode::GetTotalBytesRecv();
 }
 
-void ClientModel::setAddress(const QString &defaultAddress)
+quint64 ClientModel::getTotalBytesSent() const
 {
-    uint160 hash160;
-    std::string strAddress = defaultAddress.toStdString();
-    if (!AddressToHash160(strAddress, hash160))
-        return;
-    if (!mapPubKeys.count(hash160))
-        return;
-    CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
+    return CNode::GetTotalBytesSent();
 }
 
-ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payAmount)
+QDateTime ClientModel::getLastBlockDate() const
 {
-    uint160 hash160 = 0;
-    bool valid = false;
+    if (pindexBest)
+        return QDateTime::fromTime_t(pindexBest->GetBlockTime());
+    else
+        return QDateTime::fromTime_t(1360105017); // Genesis block's time
+}
 
-    if(!AddressToHash160(payTo.toUtf8().constData(), hash160))
-    {
-        return InvalidAddress;
-    }
+void ClientModel::updateTimer()
+{
+    // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change.
+    // Periodically check and update with a timer.
+    int newNumBlocks = getNumBlocks();
+    int newNumBlocksOfPeers = getNumBlocksOfPeers();
 
-    if(payAmount <= 0)
+    if(cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers)
     {
-        return InvalidAmount;
-    }
+        cachedNumBlocks = newNumBlocks;
+        cachedNumBlocksOfPeers = newNumBlocksOfPeers;
 
-    if(payAmount > getBalance())
-    {
-        return AmountExceedsBalance;
+        emit numBlocksChanged(newNumBlocks, newNumBlocksOfPeers);
     }
 
-    if((payAmount + nTransactionFee) > getBalance())
-    {
-        return AmountWithFeeExceedsBalance;
-    }
+    emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent());
+}
 
-    CRITICAL_BLOCK(cs_main)
-    {
-        // Send to bitcoin address
-        CWalletTx wtx;
-        CScript scriptPubKey;
-        scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
+void ClientModel::updateNumConnections(int numConnections)
+{
+    emit numConnectionsChanged(numConnections);
+}
 
-        std::string strError = SendMoney(scriptPubKey, payAmount, wtx, true);
-        if (strError == "")
-        {
-            return OK;
-        }
-        else if (strError == "ABORTED")
-        {
-            return Aborted;
-        }
-        else
+void ClientModel::updateAlert(const QString &hash, int status)
+{
+    // Show error message notification for new alert
+    if(status == CT_NEW)
+    {
+        uint256 hash_256;
+        hash_256.SetHex(hash.toStdString());
+        CAlert alert = CAlert::getAlertByHash(hash_256);
+        if(!alert.IsNull())
         {
-            emit error(tr("Sending..."), QString::fromStdString(strError));
-            return MiscError;
+            emit error(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), false);
         }
     }
-    // Add addresses that we've sent to to the address book
-    std::string strAddress = payTo.toStdString();
-    CRITICAL_BLOCK(cs_mapAddressBook)
-        if (!mapAddressBook.count(strAddress))
-            SetAddressBookName(strAddress, "");
 
-    return OK;
+    // 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
+{
+    return fTestNet;
 }
 
 bool ClientModel::inInitialBlockDownload() const
@@ -143,17 +134,75 @@ bool ClientModel::inInitialBlockDownload() const
     return IsInitialBlockDownload();
 }
 
+int ClientModel::getNumBlocksOfPeers() const
+{
+    return GetNumBlocksOfPeers();
+}
+
+QString ClientModel::getStatusBarWarnings() const
+{
+    return QString::fromStdString(GetWarnings("statusbar"));
+}
+
 OptionsModel *ClientModel::getOptionsModel()
 {
     return optionsModel;
 }
 
-AddressTableModel *ClientModel::getAddressTableModel()
+QString ClientModel::formatFullVersion() const
+{
+    return QString::fromStdString(FormatFullVersion());
+}
+
+QString ClientModel::formatBuildDate() const
+{
+    return QString::fromStdString(CLIENT_DATE);
+}
+
+QString ClientModel::clientName() const
+{
+    return QString::fromStdString(CLIENT_NAME);
+}
+
+QString ClientModel::formatClientStartupTime() const
+{
+    return QDateTime::fromTime_t(nClientStartupTime).toString();
+}
+
+// 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()
 {
-    return addressTableModel;
+    // 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));
 }
 
-TransactionTableModel *ClientModel::getTransactionTableModel()
+void ClientModel::unsubscribeFromCoreSignals()
 {
-    return transactionTableModel;
+    // 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));
 }