LCOV - code coverage report
Current view: top level - src/qt - transactiontablemodel.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 1 379 0.3 %
Date: 2020-09-26 01:30:44 Functions: 1 45 2.2 %

          Line data    Source code
       1             : // Copyright (c) 2011-2019 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <qt/transactiontablemodel.h>
       6             : 
       7             : #include <qt/addresstablemodel.h>
       8             : #include <qt/clientmodel.h>
       9             : #include <qt/guiconstants.h>
      10             : #include <qt/guiutil.h>
      11             : #include <qt/optionsmodel.h>
      12             : #include <qt/platformstyle.h>
      13             : #include <qt/transactiondesc.h>
      14             : #include <qt/transactionrecord.h>
      15             : #include <qt/walletmodel.h>
      16             : 
      17             : #include <core_io.h>
      18             : #include <interfaces/handler.h>
      19             : #include <uint256.h>
      20             : 
      21             : #include <algorithm>
      22             : 
      23             : #include <QColor>
      24             : #include <QDateTime>
      25             : #include <QDebug>
      26             : #include <QIcon>
      27             : #include <QList>
      28             : 
      29             : 
      30             : // Amount column is right-aligned it contains numbers
      31             : static int column_alignments[] = {
      32             :         Qt::AlignLeft|Qt::AlignVCenter, /* status */
      33             :         Qt::AlignLeft|Qt::AlignVCenter, /* watchonly */
      34             :         Qt::AlignLeft|Qt::AlignVCenter, /* date */
      35             :         Qt::AlignLeft|Qt::AlignVCenter, /* type */
      36             :         Qt::AlignLeft|Qt::AlignVCenter, /* address */
      37             :         Qt::AlignRight|Qt::AlignVCenter /* amount */
      38             :     };
      39             : 
      40             : // Comparison operator for sort/binary search of model tx list
      41             : struct TxLessThan
      42             : {
      43             :     bool operator()(const TransactionRecord &a, const TransactionRecord &b) const
      44             :     {
      45             :         return a.hash < b.hash;
      46             :     }
      47           0 :     bool operator()(const TransactionRecord &a, const uint256 &b) const
      48             :     {
      49           0 :         return a.hash < b;
      50             :     }
      51           0 :     bool operator()(const uint256 &a, const TransactionRecord &b) const
      52             :     {
      53           0 :         return a < b.hash;
      54             :     }
      55             : };
      56             : 
      57             : // Private implementation
      58           0 : class TransactionTablePriv
      59             : {
      60             : public:
      61           0 :     explicit TransactionTablePriv(TransactionTableModel *_parent) :
      62           0 :         parent(_parent)
      63           0 :     {
      64           0 :     }
      65             : 
      66             :     TransactionTableModel *parent;
      67             : 
      68             :     /* Local cache of wallet.
      69             :      * As it is in the same order as the CWallet, by definition
      70             :      * this is sorted by sha256.
      71             :      */
      72             :     QList<TransactionRecord> cachedWallet;
      73             : 
      74             :     /* Query entire wallet anew from core.
      75             :      */
      76           0 :     void refreshWallet(interfaces::Wallet& wallet)
      77             :     {
      78           0 :         qDebug() << "TransactionTablePriv::refreshWallet";
      79           0 :         cachedWallet.clear();
      80             :         {
      81           0 :             for (const auto& wtx : wallet.getWalletTxs()) {
      82           0 :                 if (TransactionRecord::showTransaction()) {
      83           0 :                     cachedWallet.append(TransactionRecord::decomposeTransaction(wtx));
      84           0 :                 }
      85             :             }
      86             :         }
      87           0 :     }
      88             : 
      89             :     /* Update our model of the wallet incrementally, to synchronize our model of the wallet
      90             :        with that of the core.
      91             : 
      92             :        Call with transaction that was added, removed or changed.
      93             :      */
      94           0 :     void updateWallet(interfaces::Wallet& wallet, const uint256 &hash, int status, bool showTransaction)
      95             :     {
      96           0 :         qDebug() << "TransactionTablePriv::updateWallet: " + QString::fromStdString(hash.ToString()) + " " + QString::number(status);
      97             : 
      98             :         // Find bounds of this transaction in model
      99           0 :         QList<TransactionRecord>::iterator lower = std::lower_bound(
     100           0 :             cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
     101           0 :         QList<TransactionRecord>::iterator upper = std::upper_bound(
     102           0 :             cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
     103           0 :         int lowerIndex = (lower - cachedWallet.begin());
     104           0 :         int upperIndex = (upper - cachedWallet.begin());
     105           0 :         bool inModel = (lower != upper);
     106             : 
     107           0 :         if(status == CT_UPDATED)
     108             :         {
     109           0 :             if(showTransaction && !inModel)
     110           0 :                 status = CT_NEW; /* Not in model, but want to show, treat as new */
     111           0 :             if(!showTransaction && inModel)
     112           0 :                 status = CT_DELETED; /* In model, but want to hide, treat as deleted */
     113             :         }
     114             : 
     115           0 :         qDebug() << "    inModel=" + QString::number(inModel) +
     116           0 :                     " Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) +
     117           0 :                     " showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status);
     118             : 
     119           0 :         switch(status)
     120             :         {
     121             :         case CT_NEW:
     122           0 :             if(inModel)
     123             :             {
     124           0 :                 qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is already in model";
     125           0 :                 break;
     126             :             }
     127           0 :             if(showTransaction)
     128             :             {
     129             :                 // Find transaction in wallet
     130           0 :                 interfaces::WalletTx wtx = wallet.getWalletTx(hash);
     131           0 :                 if(!wtx.tx)
     132             :                 {
     133           0 :                     qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is not in wallet";
     134           0 :                     break;
     135             :                 }
     136             :                 // Added -- insert at the right position
     137           0 :                 QList<TransactionRecord> toInsert =
     138           0 :                         TransactionRecord::decomposeTransaction(wtx);
     139           0 :                 if(!toInsert.isEmpty()) /* only if something to insert */
     140             :                 {
     141           0 :                     parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
     142             :                     int insert_idx = lowerIndex;
     143           0 :                     for (const TransactionRecord &rec : toInsert)
     144             :                     {
     145           0 :                         cachedWallet.insert(insert_idx, rec);
     146           0 :                         insert_idx += 1;
     147           0 :                     }
     148           0 :                     parent->endInsertRows();
     149           0 :                 }
     150           0 :             }
     151             :             break;
     152             :         case CT_DELETED:
     153           0 :             if(!inModel)
     154             :             {
     155           0 :                 qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_DELETED, but transaction is not in model";
     156           0 :                 break;
     157             :             }
     158             :             // Removed -- remove entire transaction from table
     159           0 :             parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
     160           0 :             cachedWallet.erase(lower, upper);
     161           0 :             parent->endRemoveRows();
     162           0 :             break;
     163             :         case CT_UPDATED:
     164             :             // Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for
     165             :             // visible transactions.
     166           0 :             for (int i = lowerIndex; i < upperIndex; i++) {
     167           0 :                 TransactionRecord *rec = &cachedWallet[i];
     168           0 :                 rec->status.needsUpdate = true;
     169             :             }
     170           0 :             break;
     171             :         }
     172           0 :     }
     173             : 
     174           0 :     int size()
     175             :     {
     176           0 :         return cachedWallet.size();
     177             :     }
     178             : 
     179           0 :     TransactionRecord* index(interfaces::Wallet& wallet, const uint256& cur_block_hash, const int idx)
     180             :     {
     181           0 :         if (idx >= 0 && idx < cachedWallet.size()) {
     182           0 :             TransactionRecord *rec = &cachedWallet[idx];
     183             : 
     184             :             // If a status update is needed (blocks came in since last check),
     185             :             // try to update the status of this transaction from the wallet.
     186             :             // Otherwise, simply re-use the cached status.
     187           0 :             interfaces::WalletTxStatus wtx;
     188           0 :             int numBlocks;
     189           0 :             int64_t block_time;
     190           0 :             if (!cur_block_hash.IsNull() && rec->statusUpdateNeeded(cur_block_hash) && wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, block_time)) {
     191           0 :                 rec->updateStatus(wtx, cur_block_hash, numBlocks, block_time);
     192           0 :             }
     193             :             return rec;
     194           0 :         }
     195           0 :         return nullptr;
     196           0 :     }
     197             : 
     198           0 :     QString describe(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit)
     199             :     {
     200           0 :         return TransactionDesc::toHTML(node, wallet, rec, unit);
     201             :     }
     202             : 
     203           0 :     QString getTxHex(interfaces::Wallet& wallet, TransactionRecord *rec)
     204             :     {
     205           0 :         auto tx = wallet.getTx(rec->hash);
     206           0 :         if (tx) {
     207           0 :             std::string strHex = EncodeHexTx(*tx);
     208           0 :             return QString::fromStdString(strHex);
     209           0 :         }
     210           0 :         return QString();
     211           0 :     }
     212             : };
     213             : 
     214           0 : TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle, WalletModel *parent):
     215           0 :         QAbstractTableModel(parent),
     216           0 :         walletModel(parent),
     217           0 :         priv(new TransactionTablePriv(this)),
     218           0 :         fProcessingQueuedTransactions(false),
     219           0 :         platformStyle(_platformStyle)
     220           0 : {
     221           0 :     columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
     222           0 :     priv->refreshWallet(walletModel->wallet());
     223             : 
     224           0 :     connect(walletModel->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &TransactionTableModel::updateDisplayUnit);
     225             : 
     226           0 :     subscribeToCoreSignals();
     227           0 : }
     228             : 
     229           0 : TransactionTableModel::~TransactionTableModel()
     230           0 : {
     231           0 :     unsubscribeFromCoreSignals();
     232           0 :     delete priv;
     233           0 : }
     234             : 
     235             : /** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
     236           0 : void TransactionTableModel::updateAmountColumnTitle()
     237             : {
     238           0 :     columns[Amount] = BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
     239           0 :     Q_EMIT headerDataChanged(Qt::Horizontal,Amount,Amount);
     240           0 : }
     241             : 
     242           0 : void TransactionTableModel::updateTransaction(const QString &hash, int status, bool showTransaction)
     243             : {
     244           0 :     uint256 updated;
     245           0 :     updated.SetHex(hash.toStdString());
     246             : 
     247           0 :     priv->updateWallet(walletModel->wallet(), updated, status, showTransaction);
     248           0 : }
     249             : 
     250           0 : void TransactionTableModel::updateConfirmations()
     251             : {
     252             :     // Blocks came in since last poll.
     253             :     // Invalidate status (number of confirmations) and (possibly) description
     254             :     //  for all rows. Qt is smart enough to only actually request the data for the
     255             :     //  visible rows.
     256           0 :     Q_EMIT dataChanged(index(0, Status), index(priv->size()-1, Status));
     257           0 :     Q_EMIT dataChanged(index(0, ToAddress), index(priv->size()-1, ToAddress));
     258           0 : }
     259             : 
     260           0 : int TransactionTableModel::rowCount(const QModelIndex &parent) const
     261             : {
     262             :     Q_UNUSED(parent);
     263           0 :     return priv->size();
     264             : }
     265             : 
     266           0 : int TransactionTableModel::columnCount(const QModelIndex &parent) const
     267             : {
     268             :     Q_UNUSED(parent);
     269           0 :     return columns.length();
     270             : }
     271             : 
     272           0 : QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const
     273             : {
     274           0 :     QString status;
     275             : 
     276           0 :     switch(wtx->status.status)
     277             :     {
     278             :     case TransactionStatus::OpenUntilBlock:
     279           0 :         status = tr("Open for %n more block(s)","",wtx->status.open_for);
     280           0 :         break;
     281             :     case TransactionStatus::OpenUntilDate:
     282           0 :         status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for));
     283           0 :         break;
     284             :     case TransactionStatus::Unconfirmed:
     285           0 :         status = tr("Unconfirmed");
     286           0 :         break;
     287             :     case TransactionStatus::Abandoned:
     288           0 :         status = tr("Abandoned");
     289           0 :         break;
     290             :     case TransactionStatus::Confirming:
     291           0 :         status = tr("Confirming (%1 of %2 recommended confirmations)").arg(wtx->status.depth).arg(TransactionRecord::RecommendedNumConfirmations);
     292           0 :         break;
     293             :     case TransactionStatus::Confirmed:
     294           0 :         status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth);
     295           0 :         break;
     296             :     case TransactionStatus::Conflicted:
     297           0 :         status = tr("Conflicted");
     298           0 :         break;
     299             :     case TransactionStatus::Immature:
     300           0 :         status = tr("Immature (%1 confirmations, will be available after %2)").arg(wtx->status.depth).arg(wtx->status.depth + wtx->status.matures_in);
     301           0 :         break;
     302             :     case TransactionStatus::NotAccepted:
     303           0 :         status = tr("Generated but not accepted");
     304           0 :         break;
     305             :     }
     306             : 
     307             :     return status;
     308           0 : }
     309             : 
     310           0 : QString TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const
     311             : {
     312           0 :     if(wtx->time)
     313             :     {
     314           0 :         return GUIUtil::dateTimeStr(wtx->time);
     315             :     }
     316           0 :     return QString();
     317           0 : }
     318             : 
     319             : /* Look up address in address book, if found return label (address)
     320             :    otherwise just return (address)
     321             :  */
     322           0 : QString TransactionTableModel::lookupAddress(const std::string &address, bool tooltip) const
     323             : {
     324           0 :     QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
     325           0 :     QString description;
     326           0 :     if(!label.isEmpty())
     327             :     {
     328           0 :         description += label;
     329             :     }
     330           0 :     if(label.isEmpty() || tooltip)
     331             :     {
     332           0 :         description += QString(" (") + QString::fromStdString(address) + QString(")");
     333           0 :     }
     334             :     return description;
     335           0 : }
     336             : 
     337           0 : QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
     338             : {
     339           0 :     switch(wtx->type)
     340             :     {
     341             :     case TransactionRecord::RecvWithAddress:
     342           0 :         return tr("Received with");
     343             :     case TransactionRecord::RecvFromOther:
     344           0 :         return tr("Received from");
     345             :     case TransactionRecord::SendToAddress:
     346             :     case TransactionRecord::SendToOther:
     347           0 :         return tr("Sent to");
     348             :     case TransactionRecord::SendToSelf:
     349           0 :         return tr("Payment to yourself");
     350             :     case TransactionRecord::Generated:
     351           0 :         return tr("Mined");
     352             :     default:
     353           0 :         return QString();
     354             :     }
     355           0 : }
     356             : 
     357           0 : QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx) const
     358             : {
     359           0 :     switch(wtx->type)
     360             :     {
     361             :     case TransactionRecord::Generated:
     362           0 :         return QIcon(":/icons/tx_mined");
     363             :     case TransactionRecord::RecvWithAddress:
     364             :     case TransactionRecord::RecvFromOther:
     365           0 :         return QIcon(":/icons/tx_input");
     366             :     case TransactionRecord::SendToAddress:
     367             :     case TransactionRecord::SendToOther:
     368           0 :         return QIcon(":/icons/tx_output");
     369             :     default:
     370           0 :         return QIcon(":/icons/tx_inout");
     371             :     }
     372           0 : }
     373             : 
     374           0 : QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const
     375             : {
     376           0 :     QString watchAddress;
     377           0 :     if (tooltip) {
     378             :         // Mark transactions involving watch-only addresses by adding " (watch-only)"
     379           0 :         watchAddress = wtx->involvesWatchAddress ? QString(" (") + tr("watch-only") + QString(")") : "";
     380           0 :     }
     381             : 
     382           0 :     switch(wtx->type)
     383             :     {
     384             :     case TransactionRecord::RecvFromOther:
     385           0 :         return QString::fromStdString(wtx->address) + watchAddress;
     386             :     case TransactionRecord::RecvWithAddress:
     387             :     case TransactionRecord::SendToAddress:
     388             :     case TransactionRecord::Generated:
     389           0 :         return lookupAddress(wtx->address, tooltip) + watchAddress;
     390             :     case TransactionRecord::SendToOther:
     391           0 :         return QString::fromStdString(wtx->address) + watchAddress;
     392             :     case TransactionRecord::SendToSelf:
     393           0 :         return lookupAddress(wtx->address, tooltip) + watchAddress;
     394             :     default:
     395           0 :         return tr("(n/a)") + watchAddress;
     396             :     }
     397           0 : }
     398             : 
     399           0 : QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
     400             : {
     401             :     // Show addresses without label in a less visible color
     402           0 :     switch(wtx->type)
     403             :     {
     404             :     case TransactionRecord::RecvWithAddress:
     405             :     case TransactionRecord::SendToAddress:
     406             :     case TransactionRecord::Generated:
     407             :         {
     408           0 :         QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(wtx->address));
     409           0 :         if(label.isEmpty())
     410           0 :             return COLOR_BAREADDRESS;
     411           0 :         } break;
     412             :     case TransactionRecord::SendToSelf:
     413           0 :         return COLOR_BAREADDRESS;
     414             :     default:
     415             :         break;
     416             :     }
     417           0 :     return QVariant();
     418           0 : }
     419             : 
     420           0 : QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed, BitcoinUnits::SeparatorStyle separators) const
     421             : {
     422           0 :     QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit, false, separators);
     423           0 :     if(showUnconfirmed)
     424             :     {
     425           0 :         if(!wtx->status.countsForBalance)
     426             :         {
     427           0 :             str = QString("[") + str + QString("]");
     428           0 :         }
     429             :     }
     430           0 :     return QString(str);
     431           0 : }
     432             : 
     433           0 : QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const
     434             : {
     435           0 :     switch(wtx->status.status)
     436             :     {
     437             :     case TransactionStatus::OpenUntilBlock:
     438             :     case TransactionStatus::OpenUntilDate:
     439           0 :         return COLOR_TX_STATUS_OPENUNTILDATE;
     440             :     case TransactionStatus::Unconfirmed:
     441           0 :         return QIcon(":/icons/transaction_0");
     442             :     case TransactionStatus::Abandoned:
     443           0 :         return QIcon(":/icons/transaction_abandoned");
     444             :     case TransactionStatus::Confirming:
     445           0 :         switch(wtx->status.depth)
     446             :         {
     447           0 :         case 1: return QIcon(":/icons/transaction_1");
     448           0 :         case 2: return QIcon(":/icons/transaction_2");
     449           0 :         case 3: return QIcon(":/icons/transaction_3");
     450           0 :         case 4: return QIcon(":/icons/transaction_4");
     451           0 :         default: return QIcon(":/icons/transaction_5");
     452             :         };
     453             :     case TransactionStatus::Confirmed:
     454           0 :         return QIcon(":/icons/transaction_confirmed");
     455             :     case TransactionStatus::Conflicted:
     456           0 :         return QIcon(":/icons/transaction_conflicted");
     457             :     case TransactionStatus::Immature: {
     458           0 :         int total = wtx->status.depth + wtx->status.matures_in;
     459           0 :         int part = (wtx->status.depth * 4 / total) + 1;
     460           0 :         return QIcon(QString(":/icons/transaction_%1").arg(part));
     461           0 :         }
     462             :     case TransactionStatus::NotAccepted:
     463           0 :         return QIcon(":/icons/transaction_0");
     464             :     default:
     465           0 :         return COLOR_BLACK;
     466             :     }
     467           0 : }
     468             : 
     469           0 : QVariant TransactionTableModel::txWatchonlyDecoration(const TransactionRecord *wtx) const
     470             : {
     471           0 :     if (wtx->involvesWatchAddress)
     472           0 :         return QIcon(":/icons/eye");
     473             :     else
     474           0 :         return QVariant();
     475           0 : }
     476             : 
     477           0 : QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const
     478             : {
     479           0 :     QString tooltip = formatTxStatus(rec) + QString("\n") + formatTxType(rec);
     480           0 :     if(rec->type==TransactionRecord::RecvFromOther || rec->type==TransactionRecord::SendToOther ||
     481           0 :        rec->type==TransactionRecord::SendToAddress || rec->type==TransactionRecord::RecvWithAddress)
     482             :     {
     483           0 :         tooltip += QString(" ") + formatTxToAddress(rec, true);
     484           0 :     }
     485             :     return tooltip;
     486           0 : }
     487             : 
     488           0 : QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
     489             : {
     490           0 :     if(!index.isValid())
     491           0 :         return QVariant();
     492           0 :     TransactionRecord *rec = static_cast<TransactionRecord*>(index.internalPointer());
     493             : 
     494           0 :     switch(role)
     495             :     {
     496             :     case RawDecorationRole:
     497           0 :         switch(index.column())
     498             :         {
     499             :         case Status:
     500           0 :             return txStatusDecoration(rec);
     501             :         case Watchonly:
     502           0 :             return txWatchonlyDecoration(rec);
     503             :         case ToAddress:
     504           0 :             return txAddressDecoration(rec);
     505             :         }
     506             :         break;
     507             :     case Qt::DecorationRole:
     508             :     {
     509           0 :         QIcon icon = qvariant_cast<QIcon>(index.data(RawDecorationRole));
     510           0 :         return platformStyle->TextColorIcon(icon);
     511           0 :     }
     512             :     case Qt::DisplayRole:
     513           0 :         switch(index.column())
     514             :         {
     515             :         case Date:
     516           0 :             return formatTxDate(rec);
     517             :         case Type:
     518           0 :             return formatTxType(rec);
     519             :         case ToAddress:
     520           0 :             return formatTxToAddress(rec, false);
     521             :         case Amount:
     522           0 :             return formatTxAmount(rec, true, BitcoinUnits::SeparatorStyle::ALWAYS);
     523             :         }
     524             :         break;
     525             :     case Qt::EditRole:
     526             :         // Edit role is used for sorting, so return the unformatted values
     527           0 :         switch(index.column())
     528             :         {
     529             :         case Status:
     530           0 :             return QString::fromStdString(rec->status.sortKey);
     531             :         case Date:
     532           0 :             return rec->time;
     533             :         case Type:
     534           0 :             return formatTxType(rec);
     535             :         case Watchonly:
     536           0 :             return (rec->involvesWatchAddress ? 1 : 0);
     537             :         case ToAddress:
     538           0 :             return formatTxToAddress(rec, true);
     539             :         case Amount:
     540           0 :             return qint64(rec->credit + rec->debit);
     541             :         }
     542             :         break;
     543             :     case Qt::ToolTipRole:
     544           0 :         return formatTooltip(rec);
     545             :     case Qt::TextAlignmentRole:
     546           0 :         return column_alignments[index.column()];
     547             :     case Qt::ForegroundRole:
     548             :         // Use the "danger" color for abandoned transactions
     549           0 :         if(rec->status.status == TransactionStatus::Abandoned)
     550             :         {
     551           0 :             return COLOR_TX_STATUS_DANGER;
     552             :         }
     553             :         // Non-confirmed (but not immature) as transactions are grey
     554           0 :         if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature)
     555             :         {
     556           0 :             return COLOR_UNCONFIRMED;
     557             :         }
     558           0 :         if(index.column() == Amount && (rec->credit+rec->debit) < 0)
     559             :         {
     560           0 :             return COLOR_NEGATIVE;
     561             :         }
     562           0 :         if(index.column() == ToAddress)
     563             :         {
     564           0 :             return addressColor(rec);
     565             :         }
     566             :         break;
     567             :     case TypeRole:
     568           0 :         return rec->type;
     569             :     case DateRole:
     570           0 :         return QDateTime::fromTime_t(static_cast<uint>(rec->time));
     571             :     case WatchonlyRole:
     572           0 :         return rec->involvesWatchAddress;
     573             :     case WatchonlyDecorationRole:
     574           0 :         return txWatchonlyDecoration(rec);
     575             :     case LongDescriptionRole:
     576           0 :         return priv->describe(walletModel->node(), walletModel->wallet(), rec, walletModel->getOptionsModel()->getDisplayUnit());
     577             :     case AddressRole:
     578           0 :         return QString::fromStdString(rec->address);
     579             :     case LabelRole:
     580           0 :         return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address));
     581             :     case AmountRole:
     582           0 :         return qint64(rec->credit + rec->debit);
     583             :     case TxHashRole:
     584           0 :         return rec->getTxHash();
     585             :     case TxHexRole:
     586           0 :         return priv->getTxHex(walletModel->wallet(), rec);
     587             :     case TxPlainTextRole:
     588             :         {
     589           0 :             QString details;
     590           0 :             QDateTime date = QDateTime::fromTime_t(static_cast<uint>(rec->time));
     591           0 :             QString txLabel = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address));
     592             : 
     593           0 :             details.append(date.toString("M/d/yy HH:mm"));
     594           0 :             details.append(" ");
     595           0 :             details.append(formatTxStatus(rec));
     596           0 :             details.append(". ");
     597           0 :             if(!formatTxType(rec).isEmpty()) {
     598           0 :                 details.append(formatTxType(rec));
     599           0 :                 details.append(" ");
     600             :             }
     601           0 :             if(!rec->address.empty()) {
     602           0 :                 if(txLabel.isEmpty())
     603           0 :                     details.append(tr("(no label)") + " ");
     604             :                 else {
     605           0 :                     details.append("(");
     606           0 :                     details.append(txLabel);
     607           0 :                     details.append(") ");
     608             :                 }
     609           0 :                 details.append(QString::fromStdString(rec->address));
     610           0 :                 details.append(" ");
     611             :             }
     612           0 :             details.append(formatTxAmount(rec, false, BitcoinUnits::SeparatorStyle::NEVER));
     613           0 :             return details;
     614           0 :         }
     615             :     case ConfirmedRole:
     616           0 :         return rec->status.status == TransactionStatus::Status::Confirming || rec->status.status == TransactionStatus::Status::Confirmed;
     617             :     case FormattedAmountRole:
     618             :         // Used for copy/export, so don't include separators
     619           0 :         return formatTxAmount(rec, false, BitcoinUnits::SeparatorStyle::NEVER);
     620             :     case StatusRole:
     621           0 :         return rec->status.status;
     622             :     }
     623           0 :     return QVariant();
     624           0 : }
     625             : 
     626           0 : QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const
     627             : {
     628           0 :     if(orientation == Qt::Horizontal)
     629             :     {
     630           0 :         if(role == Qt::DisplayRole)
     631             :         {
     632           0 :             return columns[section];
     633             :         }
     634           0 :         else if (role == Qt::TextAlignmentRole)
     635             :         {
     636           0 :             return column_alignments[section];
     637           0 :         } else if (role == Qt::ToolTipRole)
     638             :         {
     639           0 :             switch(section)
     640             :             {
     641             :             case Status:
     642           0 :                 return tr("Transaction status. Hover over this field to show number of confirmations.");
     643             :             case Date:
     644           0 :                 return tr("Date and time that the transaction was received.");
     645             :             case Type:
     646           0 :                 return tr("Type of transaction.");
     647             :             case Watchonly:
     648           0 :                 return tr("Whether or not a watch-only address is involved in this transaction.");
     649             :             case ToAddress:
     650           0 :                 return tr("User-defined intent/purpose of the transaction.");
     651             :             case Amount:
     652           0 :                 return tr("Amount removed from or added to balance.");
     653             :             }
     654             :         }
     655             :     }
     656           0 :     return QVariant();
     657           0 : }
     658             : 
     659           0 : QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const
     660             : {
     661             :     Q_UNUSED(parent);
     662           0 :     TransactionRecord* data = priv->index(walletModel->wallet(), walletModel->getLastBlockProcessed(), row);
     663           0 :     if(data)
     664             :     {
     665           0 :         return createIndex(row, column, data);
     666             :     }
     667           0 :     return QModelIndex();
     668           0 : }
     669             : 
     670           0 : void TransactionTableModel::updateDisplayUnit()
     671             : {
     672             :     // emit dataChanged to update Amount column with the current unit
     673           0 :     updateAmountColumnTitle();
     674           0 :     Q_EMIT dataChanged(index(0, Amount), index(priv->size()-1, Amount));
     675           0 : }
     676             : 
     677             : // queue notifications to show a non freezing progress dialog e.g. for rescan
     678             : struct TransactionNotification
     679             : {
     680             : public:
     681             :     TransactionNotification() {}
     682           0 :     TransactionNotification(uint256 _hash, ChangeType _status, bool _showTransaction):
     683           0 :         hash(_hash), status(_status), showTransaction(_showTransaction) {}
     684             : 
     685           0 :     void invoke(QObject *ttm)
     686             :     {
     687           0 :         QString strHash = QString::fromStdString(hash.GetHex());
     688           0 :         qDebug() << "NotifyTransactionChanged: " + strHash + " status= " + QString::number(status);
     689           0 :         bool invoked = QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection,
     690           0 :                                   Q_ARG(QString, strHash),
     691           0 :                                   Q_ARG(int, status),
     692           0 :                                   Q_ARG(bool, showTransaction));
     693           0 :         assert(invoked);
     694           0 :     }
     695             : private:
     696             :     uint256 hash;
     697             :     ChangeType status;
     698             :     bool showTransaction;
     699             : };
     700             : 
     701             : static bool fQueueNotifications = false;
     702           1 : static std::vector< TransactionNotification > vQueueNotifications;
     703             : 
     704           0 : static void NotifyTransactionChanged(TransactionTableModel *ttm, const uint256 &hash, ChangeType status)
     705             : {
     706             :     // Find transaction in wallet
     707             :     // Determine whether to show transaction or not (determine this here so that no relocking is needed in GUI thread)
     708           0 :     bool showTransaction = TransactionRecord::showTransaction();
     709             : 
     710           0 :     TransactionNotification notification(hash, status, showTransaction);
     711             : 
     712           0 :     if (fQueueNotifications)
     713             :     {
     714           0 :         vQueueNotifications.push_back(notification);
     715           0 :         return;
     716             :     }
     717           0 :     notification.invoke(ttm);
     718           0 : }
     719             : 
     720           0 : static void ShowProgress(TransactionTableModel *ttm, const std::string &title, int nProgress)
     721             : {
     722           0 :     if (nProgress == 0)
     723           0 :         fQueueNotifications = true;
     724             : 
     725           0 :     if (nProgress == 100)
     726             :     {
     727           0 :         fQueueNotifications = false;
     728           0 :         if (vQueueNotifications.size() > 10) { // prevent balloon spam, show maximum 10 balloons
     729           0 :             bool invoked = QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
     730           0 :             assert(invoked);
     731           0 :         }
     732           0 :         for (unsigned int i = 0; i < vQueueNotifications.size(); ++i)
     733             :         {
     734           0 :             if (vQueueNotifications.size() - i <= 10) {
     735           0 :                 bool invoked = QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
     736           0 :                 assert(invoked);
     737           0 :             }
     738             : 
     739           0 :             vQueueNotifications[i].invoke(ttm);
     740             :         }
     741           0 :         std::vector<TransactionNotification >().swap(vQueueNotifications); // clear
     742           0 :     }
     743           0 : }
     744             : 
     745           0 : void TransactionTableModel::subscribeToCoreSignals()
     746             : {
     747             :     // Connect signals to wallet
     748           0 :     m_handler_transaction_changed = walletModel->wallet().handleTransactionChanged(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2));
     749           0 :     m_handler_show_progress = walletModel->wallet().handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
     750           0 : }
     751             : 
     752           0 : void TransactionTableModel::unsubscribeFromCoreSignals()
     753             : {
     754             :     // Disconnect signals from wallet
     755           0 :     m_handler_transaction_changed->disconnect();
     756           0 :     m_handler_show_progress->disconnect();
     757           0 : }

Generated by: LCOV version 1.15