update to 0.4.1
[novacoin.git] / src / qt / transactiontablemodel.cpp
index a84b45a..48c03d3 100644 (file)
@@ -2,22 +2,33 @@
 #include "guiutil.h"
 #include "transactionrecord.h"
 #include "guiconstants.h"
-#include "main.h"
 #include "transactiondesc.h"
+#include "walletmodel.h"
+#include "optionsmodel.h"
+#include "addresstablemodel.h"
+#include "bitcoinunits.h"
+
+#include "wallet.h"
+#include "ui_interface.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";
+// Amount column is right-aligned it contains 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 */
+// Comparison operator for sort/binary search of model tx list
 struct TxLessThan
 {
     bool operator()(const TransactionRecord &a, const TransactionRecord &b) const
@@ -34,14 +45,16 @@ struct TxLessThan
     }
 };
 
-/* Private implementation */
-struct TransactionTablePriv
+// Private implementation
+class TransactionTablePriv
 {
-    TransactionTablePriv(TransactionTableModel *parent):
+public:
+    TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent):
+            wallet(wallet),
             parent(parent)
     {
     }
-
+    CWallet *wallet;
     TransactionTableModel *parent;
 
     /* Local cache of wallet.
@@ -50,72 +63,78 @@ struct TransactionTablePriv
      */
     QList<TransactionRecord> cachedWallet;
 
+    /* Query entire wallet anew from core.
+     */
     void refreshWallet()
     {
-#ifdef WALLET_UPDATE_DEBUG
-        qDebug() << "refreshWallet";
-#endif
-        /* Query entire wallet from core.
-         */
+        OutputDebugStringF("refreshWallet\n");
         cachedWallet.clear();
-        CRITICAL_BLOCK(cs_mapWallet)
         {
-            for(std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+            LOCK(wallet->cs_wallet);
+            for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
             {
-                cachedWallet.append(TransactionRecord::decomposeTransaction(it->second));
+                if(TransactionRecord::showTransaction(it->second))
+                    cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second));
             }
         }
     }
 
-    /* Update our model of the wallet incrementally.
-       Call with list of hashes of transactions that were added, removed or changed.
+    /* Update our model of the wallet incrementally, to synchronize our model of the wallet
+       with that of the core.
+
+       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);
-
-        CRITICAL_BLOCK(cs_mapWallet)
+        OutputDebugStringF("updateWallet %s %i\n", hash.ToString().c_str(), status);
         {
-            for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx)
+            LOCK(wallet->cs_wallet);
+
+            // 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 = mapWallet.find(hash);
-                bool inWallet = mi != 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 = 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 */
+                    // 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);
@@ -128,17 +147,22 @@ struct TransactionTablePriv
                         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;
             }
         }
     }
@@ -154,17 +178,16 @@ struct TransactionTablePriv
         {
             TransactionRecord *rec = &cachedWallet[idx];
 
-            /* If a status update is needed (blocks came in since last check),
-               update the status of this transaction from the wallet. Otherwise,
-               simply re-use the cached status.
-             */
+            // If a status update is needed (blocks came in since last check),
+            //  update the status of this transaction from the wallet. Otherwise,
+            // simply re-use the cached status.
             if(rec->statusUpdateNeeded())
             {
-                CRITICAL_BLOCK(cs_mapWallet)
                 {
-                    std::map<uint256, CWalletTx>::iterator mi = mapWallet.find(rec->hash);
+                    LOCK(wallet->cs_wallet);
+                    std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
 
-                    if(mi != mapWallet.end())
+                    if(mi != wallet->mapWallet.end())
                     {
                         rec->updateStatus(mi->second);
                     }
@@ -180,12 +203,12 @@ struct TransactionTablePriv
 
     QString describe(TransactionRecord *rec)
     {
-        CRITICAL_BLOCK(cs_mapWallet)
         {
-            std::map<uint256, CWalletTx>::iterator mi = mapWallet.find(rec->hash);
-            if(mi != mapWallet.end())
+            LOCK(wallet->cs_wallet);
+            std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
+            if(mi != wallet->mapWallet.end())
             {
-                return QString::fromStdString(TransactionDesc::toHTML(mi->second));
+                return TransactionDesc::toHTML(wallet, mi->second);
             }
         }
         return QString("");
@@ -193,27 +216,22 @@ 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, WalletModel *parent):
         QAbstractTableModel(parent),
-        priv(new TransactionTablePriv(this))
+        wallet(wallet),
+        walletModel(parent),
+        priv(new TransactionTablePriv(wallet, this)),
+        cachedNumBlocks(0)
 {
-    columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit");
+    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);
+
+    connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
 }
 
 TransactionTableModel::~TransactionTableModel()
@@ -221,32 +239,25 @@ 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_CRITICAL_BLOCK(cs_mapWallet)
-    {
-        if(!vWalletUpdated.empty())
-        {
-            BOOST_FOREACH(uint256 hash, vWalletUpdated)
-            {
-                updated.append(hash);
-            }
-            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, Description), index(priv->size()-1, Description));
+        emit dataChanged(index(0, ToAddress), index(priv->size()-1, ToAddress));
     }
 }
 
@@ -262,7 +273,7 @@ int TransactionTableModel::columnCount(const QModelIndex &parent) const
     return columns.length();
 }
 
-QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const
+QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const
 {
     QString status;
 
@@ -272,158 +283,215 @@ 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);
+        status = tr("Offline (%1 confirmations)").arg(wtx->status.depth);
         break;
     case TransactionStatus::Unconfirmed:
-        status = tr("Unconfirmed (%1)").arg(wtx->status.depth);
+        status = tr("Unconfirmed (%1 of %2 confirmations)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations);
         break;
     case TransactionStatus::HaveConfirmations:
-        status = tr("Confirmed (%1)").arg(wtx->status.depth);
+        status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth);
         break;
     }
+    if(wtx->type == TransactionRecord::Generated  || wtx->type == TransactionRecord::StakeMint)
+    {
+        switch(wtx->status.maturity)
+        {
+        case TransactionStatus::Immature:
+            status += "\n" + tr("Mined balance will be available when it matures in %n more block(s)", "", wtx->status.matures_in);
+            break;
+        case TransactionStatus::Mature:
+            break;
+        case TransactionStatus::MaturesWarning:
+            status += "\n" + tr("This block was not received by any other nodes and will probably not be accepted!");
+            break;
+        case TransactionStatus::NotAccepted:
+            status += "\n" + tr("Generated but not accepted");
+            break;
+        }
+    }
 
-    return QVariant(status);
+    return status;
 }
 
-QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const
+QString TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const
 {
     if(wtx->time)
     {
-        return QVariant(GUIUtil::DateTimeStr(wtx->time));
+        return GUIUtil::dateTimeStr(wtx->time);
     }
     else
     {
-        return QVariant();
+        return QString();
     }
 }
 
-/* Look up address in address book, if found return
-     address[0:12]... (label)
-   otherwise just return address
+/* Look up address in address book, if found return label (address)
+   otherwise just return (address)
  */
-std::string lookupAddress(const std::string &address)
+QString TransactionTableModel::lookupAddress(const std::string &address, bool tooltip) const
 {
-    std::string description;
-    CRITICAL_BLOCK(cs_mapAddressBook)
+    QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(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 += label + QString(" ");
+    }
+    if(label.isEmpty() || walletModel->getOptionsModel()->getDisplayAddresses() || tooltip)
+    {
+        description += QString("(") + QString::fromStdString(address) + QString(")");
     }
     return description;
 }
 
-QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx) const
+QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
 {
-    QString description;
+    switch(wtx->type)
+    {
+    case TransactionRecord::RecvWithAddress:
+        return tr("Received with");
+    case TransactionRecord::RecvFromOther:
+        return tr("Received from");
+    case TransactionRecord::SendToAddress:
+    case TransactionRecord::SendToOther:
+        return tr("Sent to");
+    case TransactionRecord::SendToSelf:
+        return tr("Payment to yourself");
+    case TransactionRecord::StakeMint:
+    case TransactionRecord::Generated:
+        return tr("Mined");
+    default:
+        return QString();
+    }
+}
 
+QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx) const
+{
     switch(wtx->type)
     {
+    case TransactionRecord::Generated:
+    case TransactionRecord::StakeMint:
+        return QIcon(":/icons/tx_mined");
     case TransactionRecord::RecvWithAddress:
-        description = tr("Received with: ") + QString::fromStdString(lookupAddress(wtx->address));
-        break;
-    case TransactionRecord::RecvFromIP:
-        description = tr("Received from IP: ") + QString::fromStdString(wtx->address);
-        break;
+    case TransactionRecord::RecvFromOther:
+        return QIcon(":/icons/tx_input");
     case TransactionRecord::SendToAddress:
-        description = tr("Sent to: ") + QString::fromStdString(lookupAddress(wtx->address));
-        break;
-    case TransactionRecord::SendToIP:
-        description = tr("Sent to IP: ") + QString::fromStdString(wtx->address);
-        break;
+    case TransactionRecord::SendToOther:
+        return QIcon(":/icons/tx_output");
+    default:
+        return QIcon(":/icons/tx_inout");
+    }
+    return QVariant();
+}
+
+QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const
+{
+    switch(wtx->type)
+    {
+    case TransactionRecord::RecvFromOther:
+        return QString::fromStdString(wtx->address);
+    case TransactionRecord::RecvWithAddress:
+    case TransactionRecord::SendToAddress:
+    case TransactionRecord::Generated:
+        return lookupAddress(wtx->address, tooltip);
+    case TransactionRecord::SendToOther:
+        return QString::fromStdString(wtx->address);
     case TransactionRecord::SendToSelf:
-        description = tr("Payment to yourself");
-        break;
+    default:
+        return tr("(n/a)");
+    }
+}
+
+QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
+{
+    // Show addresses without label in a less visible color
+    switch(wtx->type)
+    {
+    case TransactionRecord::RecvWithAddress:
+    case TransactionRecord::SendToAddress:
     case TransactionRecord::Generated:
-        switch(wtx->status.maturity)
         {
-        case TransactionStatus::Immature:
-            description = tr("Generated (matures in %n more blocks)", "",
-                           wtx->status.matures_in);
-            break;
-        case TransactionStatus::Mature:
-            description = tr("Generated");
-            break;
-        case TransactionStatus::MaturesWarning:
-            description = tr("Generated - 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)");
-            break;
-        }
+        QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(wtx->address));
+        if(label.isEmpty())
+            return COLOR_BAREADDRESS;
+        } break;
+    case TransactionRecord::SendToSelf:
+        return COLOR_BAREADDRESS;
+    default:
         break;
     }
-    return QVariant(description);
+    return QVariant();
 }
 
-QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) const
+QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const
 {
-    if(wtx->debit)
+    QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit);
+    if(showUnconfirmed)
     {
-        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();
     }
+    return QString(str);
 }
 
-QVariant TransactionTableModel::formatTxCredit(const TransactionRecord *wtx) const
+QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const
 {
-    if(wtx->credit)
+    if(wtx->type == TransactionRecord::Generated || wtx->type == TransactionRecord::StakeMint)
     {
-        QString str = QString::fromStdString(FormatMoney(wtx->credit));
-        if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature)
+        switch(wtx->status.maturity)
         {
-            str = QString("[") + str + QString("]");
+        case TransactionStatus::Immature: {
+            int total = wtx->status.depth + wtx->status.matures_in;
+            int part = (wtx->status.depth * 4 / total) + 1;
+            return QIcon(QString(":/icons/transaction_%1").arg(part));
+            }
+        case TransactionStatus::Mature:
+            return QIcon(":/icons/transaction_confirmed");
+        case TransactionStatus::MaturesWarning:
+        case TransactionStatus::NotAccepted:
+            return QIcon(":/icons/transaction_0");
         }
-        return QVariant(str);
     }
     else
     {
-        return QVariant();
+        switch(wtx->status.status)
+        {
+        case TransactionStatus::OpenUntilBlock:
+        case TransactionStatus::OpenUntilDate:
+            return QColor(64,64,255);
+            break;
+        case TransactionStatus::Offline:
+            return QColor(192,192,192);
+        case TransactionStatus::Unconfirmed:
+            switch(wtx->status.depth)
+            {
+            case 0: return QIcon(":/icons/transaction_0");
+            case 1: return QIcon(":/icons/transaction_1");
+            case 2: return QIcon(":/icons/transaction_2");
+            case 3: return QIcon(":/icons/transaction_3");
+            case 4: return QIcon(":/icons/transaction_4");
+            default: return QIcon(":/icons/transaction_5");
+            };
+        case TransactionStatus::HaveConfirmations:
+            return QIcon(":/icons/transaction_confirmed");
+        }
     }
+    return QColor(0,0,0);
 }
 
-QVariant TransactionTableModel::formatTxDecoration(const TransactionRecord *wtx) const
+QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const
 {
-    switch(wtx->status.status)
+    QString tooltip = formatTxStatus(rec) + QString("\n") + formatTxType(rec);
+    if(rec->type==TransactionRecord::RecvFromOther || rec->type==TransactionRecord::SendToOther ||
+       rec->type==TransactionRecord::SendToAddress || rec->type==TransactionRecord::RecvWithAddress)
     {
-    case TransactionStatus::OpenUntilBlock:
-    case TransactionStatus::OpenUntilDate:
-        return QColor(64,64,255);
-        break;
-    case TransactionStatus::Offline:
-        return QColor(192,192,192);
-    case TransactionStatus::Unconfirmed:
-        if(wtx->status.depth)
-        {
-            return QIcon(":/icons/transaction1");
-        }
-        else
-        {
-            return QIcon(":/icons/transaction0");
-        }
-    case TransactionStatus::HaveConfirmations:
-        return QIcon(":/icons/transaction2");
+        tooltip += QString(" ") + formatTxToAddress(rec, true);
     }
-    return QColor(0,0,0);
+    return tooltip;
 }
 
 QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
@@ -432,89 +500,85 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
         return QVariant();
     TransactionRecord *rec = static_cast<TransactionRecord*>(index.internalPointer());
 
-    if(role == Qt::DecorationRole)
+    switch(role)
     {
-        if(index.column() == Status)
+    case Qt::DecorationRole:
+        switch(index.column())
         {
-            return formatTxDecoration(rec);
+        case Status:
+            return txStatusDecoration(rec);
+        case ToAddress:
+            return txAddressDecoration(rec);
         }
-    }
-    else if(role == Qt::DisplayRole)
-    {
-        /* Delegate to specific column handlers */
+        break;
+    case Qt::DisplayRole:
         switch(index.column())
         {
-        //case Status:
-        //    return formatTxStatus(rec);
         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, false);
+        case Amount:
+            return formatTxAmount(rec);
         }
-    }
-    else if(role == Qt::EditRole)
-    {
-        /* Edit role is used for sorting so return the real values */
+        break;
+    case Qt::EditRole:
+        // Edit role is used for sorting, so return the unformatted values
         switch(index.column())
         {
         case Status:
             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, true);
+        case Amount:
+            return rec->credit + rec->debit;
         }
-    }
-    else if (role == Qt::ToolTipRole)
-    {
-        if(index.column() == Status)
-        {
-            return formatTxStatus(rec);
-        }
-    }
-    else if (role == Qt::TextAlignmentRole)
-    {
+        break;
+    case Qt::ToolTipRole:
+        return formatTooltip(rec);
+    case Qt::TextAlignmentRole:
         return column_alignments[index.column()];
-    }
-    else if (role == Qt::ForegroundRole)
-    {
-        /* Non-confirmed transactions are grey */
-        if(rec->status.confirmed)
+    case Qt::ForegroundRole:
+        // Non-confirmed transactions are grey
+        if(!rec->status.confirmed)
         {
-            return QColor(0, 0, 0);
+            return COLOR_UNCONFIRMED;
         }
-        else
+        if(index.column() == Amount && (rec->credit+rec->debit) < 0)
         {
-            return QColor(128, 128, 128);
+            return COLOR_NEGATIVE;
         }
-    }
-    else if (role == TypeRole)
-    {
-        /* Role for filtering tabs by type */
-        switch(rec->type)
+        if(index.column() == ToAddress)
         {
-        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 addressColor(rec);
         }
-    }
-    else if (role == LongDescriptionRole)
-    {
+        break;
+    case TypeRole:
+        return rec->type;
+    case DateRole:
+        return QDateTime::fromTime_t(static_cast<uint>(rec->time));
+    case LongDescriptionRole:
         return priv->describe(rec);
+    case AddressRole:
+        return QString::fromStdString(rec->address);
+    case LabelRole:
+        return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address));
+    case AmountRole:
+        return rec->credit + rec->debit;
+    case TxIDRole:
+        return QString::fromStdString(rec->getTxID());
+    case ConfirmedRole:
+        // Return True if transaction counts for balance
+        return rec->status.confirmed && !((rec->type == TransactionRecord::Generated || rec->type == TransactionRecord::StakeMint) &&
+                                          rec->status.maturity != TransactionStatus::Mature);
+    case FormattedAmountRole:
+        return formatTxAmount(rec, false);
     }
     return QVariant();
 }
@@ -535,26 +599,21 @@ 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.");
             }
         }
     }
     return QVariant();
 }
 
-Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const
-{
-    return QAbstractTableModel::flags(index);
-}
-
 QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const
 {
     Q_UNUSED(parent);
@@ -569,3 +628,8 @@ QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex
     }
 }
 
+void TransactionTableModel::updateDisplayUnit()
+{
+    // emit dataChanged to update Amount column with the current unit
+    emit dataChanged(index(0, Amount), index(priv->size()-1, Amount));
+}