Implement filter row instead of tabs, allows for more expressive filters
[novacoin.git] / src / qt / transactiontablemodel.cpp
index e25ee1d..28d22b8 100644 (file)
@@ -2,21 +2,32 @@
 #include "guiutil.h"
 #include "transactionrecord.h"
 #include "guiconstants.h"
-#include "main.h"
 #include "transactiondesc.h"
 
+#include "headers.h"
+
 #include <QLocale>
 #include <QDebug>
 #include <QList>
 #include <QColor>
 #include <QTimer>
 #include <QIcon>
+#include <QDateTime>
 #include <QtAlgorithms>
 
 const QString TransactionTableModel::Sent = "s";
 const QString TransactionTableModel::Received = "r";
 const QString TransactionTableModel::Other = "o";
 
+// Credit and Debit columns are right-aligned as they contain numbers
+static int column_alignments[] = {
+        Qt::AlignLeft|Qt::AlignVCenter,
+        Qt::AlignLeft|Qt::AlignVCenter,
+        Qt::AlignLeft|Qt::AlignVCenter,
+        Qt::AlignLeft|Qt::AlignVCenter,
+        Qt::AlignRight|Qt::AlignVCenter
+    };
+
 // Comparison operator for sort/binary search of model tx list
 struct TxLessThan
 {
@@ -37,11 +48,12 @@ struct TxLessThan
 // Private implementation
 struct TransactionTablePriv
 {
-    TransactionTablePriv(TransactionTableModel *parent):
+    TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent):
+            wallet(wallet),
             parent(parent)
     {
     }
-
+    CWallet *wallet;
     TransactionTableModel *parent;
 
     /* Local cache of wallet.
@@ -58,11 +70,11 @@ struct TransactionTablePriv
         qDebug() << "refreshWallet";
 #endif
         cachedWallet.clear();
-        CRITICAL_BLOCK(cs_mapWallet)
+        CRITICAL_BLOCK(wallet->cs_mapWallet)
         {
-            for(std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+            for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
             {
-                cachedWallet.append(TransactionRecord::decomposeTransaction(it->second));
+                cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second));
             }
         }
     }
@@ -84,14 +96,14 @@ struct TransactionTablePriv
         QList<uint256> updated_sorted = updated;
         qSort(updated_sorted);
 
-        CRITICAL_BLOCK(cs_mapWallet)
+        CRITICAL_BLOCK(wallet->cs_mapWallet)
         {
             for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx)
             {
                 const uint256 &hash = updated_sorted.at(update_idx);
                 /* Find transaction in wallet */
-                std::map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
-                bool inWallet = mi != mapWallet.end();
+                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());
@@ -100,6 +112,7 @@ struct TransactionTablePriv
                 int lowerIndex = (lower - cachedWallet.begin());
                 int upperIndex = (upper - cachedWallet.begin());
 
+                // Determine if transaction is in model already
                 bool inModel = false;
                 if(lower != upper)
                 {
@@ -115,7 +128,7 @@ struct TransactionTablePriv
                 {
                     // Added -- insert at the right position
                     QList<TransactionRecord> toInsert =
-                            TransactionRecord::decomposeTransaction(mi->second);
+                            TransactionRecord::decomposeTransaction(wallet, mi->second);
                     if(!toInsert.isEmpty()) /* only if something to insert */
                     {
                         parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
@@ -159,11 +172,11 @@ struct TransactionTablePriv
             // simply re-use the cached status.
             if(rec->statusUpdateNeeded())
             {
-                CRITICAL_BLOCK(cs_mapWallet)
+                CRITICAL_BLOCK(wallet->cs_mapWallet)
                 {
-                    std::map<uint256, CWalletTx>::iterator mi = mapWallet.find(rec->hash);
+                    std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
 
-                    if(mi != mapWallet.end())
+                    if(mi != wallet->mapWallet.end())
                     {
                         rec->updateStatus(mi->second);
                     }
@@ -179,12 +192,12 @@ struct TransactionTablePriv
 
     QString describe(TransactionRecord *rec)
     {
-        CRITICAL_BLOCK(cs_mapWallet)
+        CRITICAL_BLOCK(wallet->cs_mapWallet)
         {
-            std::map<uint256, CWalletTx>::iterator mi = mapWallet.find(rec->hash);
-            if(mi != mapWallet.end())
+            std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
+            if(mi != wallet->mapWallet.end())
             {
-                return QString::fromStdString(TransactionDesc::toHTML(mi->second));
+                return QString::fromStdString(TransactionDesc::toHTML(wallet, mi->second));
             }
         }
         return QString("");
@@ -192,21 +205,12 @@ struct TransactionTablePriv
 
 };
 
-// Credit and Debit columns are right-aligned as they contain numbers
-static int column_alignments[] = {
-        Qt::AlignLeft|Qt::AlignVCenter,
-        Qt::AlignLeft|Qt::AlignVCenter,
-        Qt::AlignLeft|Qt::AlignVCenter,
-        Qt::AlignRight|Qt::AlignVCenter,
-        Qt::AlignRight|Qt::AlignVCenter,
-        Qt::AlignLeft|Qt::AlignVCenter
-    };
-
-TransactionTableModel::TransactionTableModel(QObject *parent):
+TransactionTableModel::TransactionTableModel(CWallet* wallet, QObject *parent):
         QAbstractTableModel(parent),
-        priv(new TransactionTablePriv(this))
+        wallet(wallet),
+        priv(new TransactionTablePriv(wallet, this))
 {
-    columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit");
+    columns << tr("Status") << tr("Date") << tr("Type") << tr("Address") << tr("Amount");
 
     priv->refreshWallet();
 
@@ -225,15 +229,15 @@ void TransactionTableModel::update()
     QList<uint256> updated;
 
     // Check if there are changes to wallet map
-    TRY_CRITICAL_BLOCK(cs_mapWallet)
+    TRY_CRITICAL_BLOCK(wallet->cs_mapWallet)
     {
-        if(!vWalletUpdated.empty())
+        if(!wallet->vWalletUpdated.empty())
         {
-            BOOST_FOREACH(uint256 hash, vWalletUpdated)
+            BOOST_FOREACH(uint256 hash, wallet->vWalletUpdated)
             {
                 updated.append(hash);
             }
-            vWalletUpdated.clear();
+            wallet->vWalletUpdated.clear();
         }
     }
 
@@ -244,7 +248,7 @@ void TransactionTableModel::update()
         // Status (number of confirmations) and (possibly) description
         //  columns changed for all rows.
         emit dataChanged(index(0, Status), index(priv->size()-1, Status));
-        emit dataChanged(index(0, Description), index(priv->size()-1, Description));
+        emit dataChanged(index(0, ToAddress), index(priv->size()-1, ToAddress));
     }
 }
 
@@ -270,7 +274,7 @@ QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) con
         status = tr("Open for %n block(s)","",wtx->status.open_for);
         break;
     case TransactionStatus::OpenUntilDate:
-        status = tr("Open until ") + GUIUtil::DateTimeStr(wtx->status.open_for);
+        status = tr("Open until %1").arg(GUIUtil::DateTimeStr(wtx->status.open_for));
         break;
     case TransactionStatus::Offline:
         status = tr("Offline (%1)").arg(wtx->status.depth);
@@ -298,66 +302,105 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const
     }
 }
 
+/* Look up label for address in address book, if not found return empty string.
+   This should really move to the wallet class.
+ */
+QString TransactionTableModel::labelForAddress(const std::string &address) const
+{
+    CRITICAL_BLOCK(wallet->cs_mapAddressBook)
+    {
+        std::map<std::string, std::string>::iterator mi = wallet->mapAddressBook.find(address);
+        if (mi != wallet->mapAddressBook.end())
+        {
+            return QString::fromStdString(mi->second);
+        }
+    }
+    return QString();
+}
+
 /* Look up address in address book, if found return
      address[0:12]... (label)
    otherwise just return address
  */
-std::string lookupAddress(const std::string &address)
+QString TransactionTableModel::lookupAddress(const std::string &address) const
 {
-    std::string description;
-    CRITICAL_BLOCK(cs_mapAddressBook)
+    QString label = labelForAddress(address);
+    QString description;
+    if(label.isEmpty())
     {
-        std::map<std::string, std::string>::iterator mi = mapAddressBook.find(address);
-        if (mi != mapAddressBook.end() && !(*mi).second.empty())
-        {
-            std::string label = (*mi).second;
-            description += address.substr(0,12) + "... ";
-            description += "(" + label + ")";
-        }
-        else
-        {
-            description += address;
-        }
+        description = QString::fromStdString(address);
+    }
+    else
+    {
+        description = QString::fromStdString(address.substr(0,12)) + QString("... (") + label + QString(")");
     }
     return description;
 }
 
-QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx) const
+QVariant TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
 {
     QString description;
 
     switch(wtx->type)
     {
     case TransactionRecord::RecvWithAddress:
-        description = tr("Received with: ") + QString::fromStdString(lookupAddress(wtx->address));
+        description = tr("Received with");
         break;
     case TransactionRecord::RecvFromIP:
-        description = tr("Received from IP: ") + QString::fromStdString(wtx->address);
+        description = tr("Received from IP");
         break;
     case TransactionRecord::SendToAddress:
-        description = tr("Sent to: ") + QString::fromStdString(lookupAddress(wtx->address));
+        description = tr("Sent to");
         break;
     case TransactionRecord::SendToIP:
-        description = tr("Sent to IP: ") + QString::fromStdString(wtx->address);
+        description = tr("Sent to IP");
         break;
     case TransactionRecord::SendToSelf:
         description = tr("Payment to yourself");
         break;
     case TransactionRecord::Generated:
+        description = tr("Generated");
+        break;
+    }
+    return QVariant(description);
+}
+
+QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx) const
+{
+    QString description;
+
+    switch(wtx->type)
+    {
+    case TransactionRecord::RecvWithAddress:
+        description = lookupAddress(wtx->address);
+        break;
+    case TransactionRecord::RecvFromIP:
+        description = QString::fromStdString(wtx->address);
+        break;
+    case TransactionRecord::SendToAddress:
+        description = lookupAddress(wtx->address);
+        break;
+    case TransactionRecord::SendToIP:
+        description = QString::fromStdString(wtx->address);
+        break;
+    case TransactionRecord::SendToSelf:
+        description = QString();
+        break;
+    case TransactionRecord::Generated:
         switch(wtx->status.maturity)
         {
         case TransactionStatus::Immature:
-            description = tr("Generated (matures in %n more blocks)", "",
+            description = tr("(matures in %n more blocks)", "",
                            wtx->status.matures_in);
             break;
         case TransactionStatus::Mature:
-            description = tr("Generated");
+            description = QString();
             break;
         case TransactionStatus::MaturesWarning:
-            description = tr("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
+            description = tr("(Warning: This block was not received by any other nodes and will probably not be accepted!)");
             break;
         case TransactionStatus::NotAccepted:
-            description = tr("Generated (not accepted)");
+            description = tr("(not accepted)");
             break;
         }
         break;
@@ -365,38 +408,14 @@ QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx
     return QVariant(description);
 }
 
-QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) const
-{
-    if(wtx->debit)
-    {
-        QString str = QString::fromStdString(FormatMoney(wtx->debit));
-        if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature)
-        {
-            str = QString("[") + str + QString("]");
-        }
-        return QVariant(str);
-    }
-    else
-    {
-        return QVariant();
-    }
-}
-
-QVariant TransactionTableModel::formatTxCredit(const TransactionRecord *wtx) const
+QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx) const
 {
-    if(wtx->credit)
+    QString str = QString::fromStdString(FormatMoney(wtx->credit + wtx->debit));
+    if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature)
     {
-        QString str = QString::fromStdString(FormatMoney(wtx->credit));
-        if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature)
-        {
-            str = QString("[") + str + QString("]");
-        }
-        return QVariant(str);
-    }
-    else
-    {
-        return QVariant();
+        str = QString("[") + str + QString("]");
     }
+    return QVariant(str);
 }
 
 QVariant TransactionTableModel::formatTxDecoration(const TransactionRecord *wtx) const
@@ -445,12 +464,12 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
         {
         case Date:
             return formatTxDate(rec);
-        case Description:
-            return formatTxDescription(rec);
-        case Debit:
-            return formatTxDebit(rec);
-        case Credit:
-            return formatTxCredit(rec);
+        case Type:
+            return formatTxType(rec);
+        case ToAddress:
+            return formatTxToAddress(rec);
+        case Amount:
+            return formatTxAmount(rec);
         }
     }
     else if(role == Qt::EditRole)
@@ -462,12 +481,12 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
             return QString::fromStdString(rec->status.sortKey);
         case Date:
             return rec->time;
-        case Description:
-            return formatTxDescription(rec);
-        case Debit:
-            return rec->debit;
-        case Credit:
-            return rec->credit;
+        case Type:
+            return formatTxType(rec);
+        case ToAddress:
+            return formatTxToAddress(rec);
+        case Amount:
+            return rec->credit + rec->debit;
         }
     }
     else if (role == Qt::ToolTipRole)
@@ -488,27 +507,35 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
         {
             return QColor(128, 128, 128);
         }
+        if(index.column() == Amount && (rec->credit+rec->debit) < 0)
+        {
+            return QColor(255, 0, 0);
+        }
     }
     else if (role == TypeRole)
     {
-        /* Role for filtering tabs by type */
-        switch(rec->type)
-        {
-        case TransactionRecord::RecvWithAddress:
-        case TransactionRecord::RecvFromIP:
-            return TransactionTableModel::Received;
-        case TransactionRecord::SendToAddress:
-        case TransactionRecord::SendToIP:
-        case TransactionRecord::SendToSelf:
-            return TransactionTableModel::Sent;
-        default:
-            return TransactionTableModel::Other;
-        }
+        return rec->type;
+    }
+    else if (role == DateRole)
+    {
+        return QDateTime::fromTime_t(static_cast<uint>(rec->time));
     }
     else if (role == LongDescriptionRole)
     {
         return priv->describe(rec);
     }
+    else if (role == AddressRole)
+    {
+        return QString::fromStdString(rec->address);
+    }
+    else if (role == LabelRole)
+    {
+        return labelForAddress(rec->address);
+    }
+    else if (role == AbsoluteAmountRole)
+    {
+        return llabs(rec->credit + rec->debit);
+    }
     return QVariant();
 }
 
@@ -528,15 +555,15 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat
             switch(section)
             {
             case Status:
-                return tr("Transaction status. Hover over this field to show number of transactions.");
+                return tr("Transaction status. Hover over this field to show number of confirmations.");
             case Date:
                 return tr("Date and time that the transaction was received.");
-            case Description:
-                return tr("Short description of the transaction.");
-            case Debit:
-                return tr("Amount removed from balance.");
-            case Credit:
-                return tr("Amount added to balance.");
+            case Type:
+                return tr("Type of transaction.");
+            case ToAddress:
+                return tr("Destination address of transaction.");
+            case Amount:
+                return tr("Amount removed from or added to balance.");
             }
         }
     }