Remove headers.h
[novacoin.git] / src / qt / transactiontablemodel.cpp
index 0d0d97b..5f505f4 100644 (file)
@@ -4,11 +4,13 @@
 #include "guiconstants.h"
 #include "transactiondesc.h"
 #include "walletmodel.h"
+#include "optionsmodel.h"
+#include "addresstablemodel.h"
+#include "bitcoinunits.h"
 
-#include "headers.h"
+#include "wallet.h"
 
 #include <QLocale>
-#include <QDebug>
 #include <QList>
 #include <QColor>
 #include <QTimer>
@@ -16,7 +18,7 @@
 #include <QDateTime>
 #include <QtAlgorithms>
 
-// Credit and Debit columns are right-aligned as they contain numbers
+// Amount column is right-aligned it contains numbers
 static int column_alignments[] = {
         Qt::AlignLeft|Qt::AlignVCenter,
         Qt::AlignLeft|Qt::AlignVCenter,
@@ -43,8 +45,9 @@ struct TxLessThan
 };
 
 // Private implementation
-struct TransactionTablePriv
+class TransactionTablePriv
 {
+public:
     TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent):
             wallet(wallet),
             parent(parent)
@@ -67,8 +70,8 @@ struct TransactionTablePriv
         qDebug() << "refreshWallet";
 #endif
         cachedWallet.clear();
-        CRITICAL_BLOCK(wallet->cs_mapWallet)
         {
+            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));
@@ -93,15 +96,15 @@ struct TransactionTablePriv
         QList<uint256> updated_sorted = updated;
         qSort(updated_sorted);
 
-        CRITICAL_BLOCK(wallet->cs_mapWallet)
         {
+            LOCK(wallet->cs_wallet);
             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 */
+                // 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 */
+                // Find bounds of this transaction in model
                 QList<TransactionRecord>::iterator lower = qLowerBound(
                     cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
                 QList<TransactionRecord>::iterator upper = qUpperBound(
@@ -169,8 +172,8 @@ struct TransactionTablePriv
             // simply re-use the cached status.
             if(rec->statusUpdateNeeded())
             {
-                CRITICAL_BLOCK(wallet->cs_mapWallet)
                 {
+                    LOCK(wallet->cs_wallet);
                     std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
 
                     if(mi != wallet->mapWallet.end())
@@ -189,12 +192,12 @@ struct TransactionTablePriv
 
     QString describe(TransactionRecord *rec)
     {
-        CRITICAL_BLOCK(wallet->cs_mapWallet)
         {
+            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(wallet, mi->second));
+                return TransactionDesc::toHTML(wallet, mi->second);
             }
         }
         return QString("");
@@ -208,7 +211,7 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren
         walletModel(parent),
         priv(new TransactionTablePriv(wallet, this))
 {
-    columns << tr("Status") << tr("Date") << tr("Type") << tr("Address") << tr("Amount");
+    columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount");
 
     priv->refreshWallet();
 
@@ -227,9 +230,9 @@ void TransactionTableModel::update()
     QList<uint256> updated;
 
     // Check if there are changes to wallet map
-    TRY_CRITICAL_BLOCK(wallet->cs_mapWallet)
     {
-        if(!wallet->vWalletUpdated.empty())
+        TRY_LOCK(wallet->cs_wallet, lockWallet);
+        if (lockWallet && !wallet->vWalletUpdated.empty())
         {
             BOOST_FOREACH(uint256 hash, wallet->vWalletUpdated)
             {
@@ -262,7 +265,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,176 +275,234 @@ 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 %1").arg(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/%2)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations);
+        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)
+    {
+        switch(wtx->status.maturity)
+        {
+        case TransactionStatus::Immature:
+            status += "\n" + tr("Mined balance will be available in %n more blocks", "",
+                           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)
  */
-QString TransactionTableModel::lookupAddress(const std::string &address) const
+QString TransactionTableModel::lookupAddress(const std::string &address, bool tooltip) const
 {
-    QString label = walletModel->labelForAddress(QString::fromStdString(address));
+    QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
     QString description;
-    if(label.isEmpty())
+    if(!label.isEmpty())
     {
-        description = QString::fromStdString(address);
+        description += label + QString(" ");
     }
-    else
+    if(label.isEmpty() || walletModel->getOptionsModel()->getDisplayAddresses() || tooltip)
     {
-        description = label + QString(" (") + QString::fromStdString(address.substr(0,12)) + QString("...)");
+        description += QString("(") + QString::fromStdString(address) + QString(")");
     }
     return description;
 }
 
-QVariant TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
+QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
 {
-    QString description;
-
     switch(wtx->type)
     {
     case TransactionRecord::RecvWithAddress:
-        description = tr("Received with");
-        break;
-    case TransactionRecord::RecvFromIP:
-        description = tr("Received from IP");
-        break;
+        return tr("Received with");
+    case TransactionRecord::RecvFromOther:
+        return tr("Received from");
     case TransactionRecord::SendToAddress:
-        description = tr("Sent to");
-        break;
-    case TransactionRecord::SendToIP:
-        description = tr("Sent to IP");
-        break;
+    case TransactionRecord::SendToOther:
+        return tr("Sent to");
     case TransactionRecord::SendToSelf:
-        description = tr("Payment to yourself");
-        break;
+        return tr("Payment to yourself");
     case TransactionRecord::Generated:
-        description = tr("Generated");
-        break;
+        return tr("Mined");
+    default:
+        return QString();
     }
-    return QVariant(description);
 }
 
-QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx) const
+QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx) const
 {
-    QString description;
+    switch(wtx->type)
+    {
+    case TransactionRecord::Generated:
+        return QIcon(":/icons/tx_mined");
+    case TransactionRecord::RecvWithAddress:
+    case TransactionRecord::RecvFromOther:
+        return QIcon(":/icons/tx_input");
+    case TransactionRecord::SendToAddress:
+    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:
-        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;
+        return lookupAddress(wtx->address, tooltip);
+    case TransactionRecord::SendToOther:
+        return QString::fromStdString(wtx->address);
     case TransactionRecord::SendToSelf:
-        description = QString();
-        break;
     case TransactionRecord::Generated:
-        switch(wtx->status.maturity)
+    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 TransactionStatus::Immature:
-            description = tr("(matures in %n more blocks)", "",
-                           wtx->status.matures_in);
-            break;
-        case TransactionStatus::Mature:
-            description = QString();
-            break;
-        case TransactionStatus::MaturesWarning:
-            description = tr("(Warning: This block was not received by any other nodes and will probably not be accepted!)");
-            break;
-        case TransactionStatus::NotAccepted:
-            description = tr("(not accepted)");
-            break;
-        }
+        QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(wtx->address));
+        if(label.isEmpty())
+            return COLOR_BAREADDRESS;
+        } break;
+    case TransactionRecord::SendToSelf:
+    case TransactionRecord::Generated:
+        return COLOR_BAREADDRESS;
+    default:
         break;
     }
-    return QVariant(description);
+    return QVariant();
 }
 
-QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx) const
+QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const
 {
-    QString str = QString::fromStdString(FormatMoney(wtx->credit + wtx->debit));
-    if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature)
+    QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit);
+    if(showUnconfirmed)
     {
-        str = QString("[") + str + QString("]");
+        if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature)
+        {
+            str = QString("[") + str + QString("]");
+        }
     }
-    return QVariant(str);
+    return QString(str);
 }
 
-QVariant TransactionTableModel::formatTxDecoration(const TransactionRecord *wtx) const
+QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const
 {
-    switch(wtx->status.status)
+    if(wtx->type == TransactionRecord::Generated)
     {
-    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)
+        switch(wtx->status.maturity)
         {
-        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");
+        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");
+        }
+    }
+    else
+    {
+        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);
 }
 
+QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const
+{
+    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)
+    {
+        tooltip += QString(" ") + formatTxToAddress(rec, true);
+    }
+    return tooltip;
+}
+
 QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
 {
     if(!index.isValid())
         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 Date:
@@ -449,14 +510,13 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
         case Type:
             return formatTxType(rec);
         case ToAddress:
-            return formatTxToAddress(rec);
+            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:
@@ -466,57 +526,50 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
         case Type:
             return formatTxType(rec);
         case ToAddress:
-            return formatTxToAddress(rec);
+            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 */
+    case Qt::ForegroundRole:
+        // Non-confirmed transactions are grey
         if(!rec->status.confirmed)
         {
-            return QColor(128, 128, 128);
+            return COLOR_UNCONFIRMED;
         }
         if(index.column() == Amount && (rec->credit+rec->debit) < 0)
         {
-            return QColor(255, 0, 0);
+            return COLOR_NEGATIVE;
         }
-    }
-    else if (role == TypeRole)
-    {
+        if(index.column() == ToAddress)
+        {
+            return addressColor(rec);
+        }
+        break;
+    case TypeRole:
         return rec->type;
-    }
-    else if (role == DateRole)
-    {
+    case DateRole:
         return QDateTime::fromTime_t(static_cast<uint>(rec->time));
-    }
-    else if (role == LongDescriptionRole)
-    {
+    case LongDescriptionRole:
         return priv->describe(rec);
-    }
-    else if (role == AddressRole)
-    {
+    case AddressRole:
         return QString::fromStdString(rec->address);
-    }
-    else if (role == LabelRole)
-    {
-        return walletModel->labelForAddress(QString::fromStdString(rec->address));
-    }
-    else if (role == AbsoluteAmountRole)
-    {
-        return llabs(rec->credit + rec->debit);
+    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->status.maturity != TransactionStatus::Mature);
+    case FormattedAmountRole:
+        return formatTxAmount(rec, false);
     }
     return QVariant();
 }
@@ -552,11 +605,6 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat
     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);