diff options
author | Wladimir J. van der Laan <laanwj@gmail.com> | 2014-10-28 19:52:21 +0100 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2014-10-28 21:20:55 +0100 |
commit | 023e63df78b847812040bf6958c97476606dfbfd (patch) | |
tree | 047396bfc6c1490db1a3bcfc95e8f4db03c26780 /src/qt/transactiontablemodel.cpp | |
parent | cd9114e5136ecc1f60baa43fffeeb632782f2353 (diff) |
qt: Move transaction notification to transaction table model
Move transaction new/update notification to TransactionTableModel.
This moves the concerns to where they're actually handled.
No need to bounce this through wallet model.
- Do wallet transaction preprocessing on signal handler side;
avoids locking cs_main/cs_wallet on notification in GUI thread
(except for new transactions)
Diffstat (limited to 'src/qt/transactiontablemodel.cpp')
-rw-r--r-- | src/qt/transactiontablemodel.cpp | 206 |
1 files changed, 141 insertions, 65 deletions
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index e34d776818..79cb4a6296 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -91,87 +91,80 @@ public: Call with transaction that was added, removed or changed. */ - void updateWallet(const uint256 &hash, int status) + void updateWallet(const uint256 &hash, int status, bool showTransaction) { qDebug() << "TransactionTablePriv::updateWallet : " + QString::fromStdString(hash.ToString()) + " " + QString::number(status); - { - LOCK2(cs_main, 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); - // 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); + if(status == CT_UPDATED) + { + 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 */ + } - // Determine whether to show transaction or not - bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second)); + qDebug() << " inModel=" + QString::number(inModel) + + " Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) + + " showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status); - if(status == CT_UPDATED) + switch(status) + { + case CT_NEW: + if(inModel) { - 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 */ + qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is already in model"; + break; } - - qDebug() << " inWallet=" + QString::number(inWallet) + " inModel=" + QString::number(inModel) + - " Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) + - " showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status); - - switch(status) + if(showTransaction) { - case CT_NEW: - if(inModel) - { - qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is already in model"; - break; - } - if(!inWallet) + LOCK2(cs_main, wallet->cs_wallet); + // Find transaction in wallet + std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash); + if(mi == wallet->mapWallet.end()) { qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is not in wallet"; break; } - if(showTransaction) + // Added -- insert at the right position + QList<TransactionRecord> toInsert = + TransactionRecord::decomposeTransaction(wallet, mi->second); + if(!toInsert.isEmpty()) /* only if something to insert */ { - // Added -- insert at the right position - QList<TransactionRecord> toInsert = - TransactionRecord::decomposeTransaction(wallet, mi->second); - if(!toInsert.isEmpty()) /* only if something to insert */ + parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); + int insert_idx = lowerIndex; + foreach(const TransactionRecord &rec, toInsert) { - parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); - int insert_idx = lowerIndex; - foreach(const TransactionRecord &rec, toInsert) - { - cachedWallet.insert(insert_idx, rec); - insert_idx += 1; - } - parent->endInsertRows(); + cachedWallet.insert(insert_idx, rec); + insert_idx += 1; } + parent->endInsertRows(); } - break; - case CT_DELETED: - if(!inModel) - { - qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_DELETED, but transaction is not in model"; - 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; + case CT_DELETED: + if(!inModel) + { + qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_DELETED, but transaction is not in model"; 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; } } @@ -230,16 +223,20 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren QAbstractTableModel(parent), wallet(wallet), walletModel(parent), - priv(new TransactionTablePriv(wallet, this)) + priv(new TransactionTablePriv(wallet, this)), + fProcessingQueuedTransactions(false) { columns << QString() << QString() << tr("Date") << tr("Type") << tr("Address") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); priv->refreshWallet(); connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + subscribeToCoreSignals(); } TransactionTableModel::~TransactionTableModel() { + unsubscribeFromCoreSignals(); delete priv; } @@ -250,12 +247,12 @@ void TransactionTableModel::updateAmountColumnTitle() emit headerDataChanged(Qt::Horizontal,Amount,Amount); } -void TransactionTableModel::updateTransaction(const QString &hash, int status) +void TransactionTableModel::updateTransaction(const QString &hash, int status, bool showTransaction) { uint256 updated; updated.SetHex(hash.toStdString()); - priv->updateWallet(updated, status); + priv->updateWallet(updated, status, showTransaction); } void TransactionTableModel::updateConfirmations() @@ -649,3 +646,82 @@ void TransactionTableModel::updateDisplayUnit() updateAmountColumnTitle(); emit dataChanged(index(0, Amount), index(priv->size()-1, Amount)); } + +// queue notifications to show a non freezing progress dialog e.g. for rescan +struct TransactionNotification +{ +public: + TransactionNotification() {} + TransactionNotification(uint256 hash, ChangeType status, bool showTransaction): + hash(hash), status(status), showTransaction(showTransaction) {} + + void invoke(QObject *ttm) + { + QString strHash = QString::fromStdString(hash.GetHex()); + qDebug() << "NotifyTransactionChanged : " + strHash + " status= " + QString::number(status); + QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection, + Q_ARG(QString, strHash), + Q_ARG(int, status), + Q_ARG(bool, showTransaction)); + } +private: + uint256 hash; + ChangeType status; + bool showTransaction; +}; + +static bool fQueueNotifications = false; +static std::vector< TransactionNotification > vQueueNotifications; + +static void NotifyTransactionChanged(TransactionTableModel *ttm, CWallet *wallet, const uint256 &hash, ChangeType status) +{ + // Find transaction in wallet + std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash); + // Determine whether to show transaction or not (determine this here so that no relocking is needed in GUI thread) + bool inWallet = mi != wallet->mapWallet.end(); + bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second)); + + TransactionNotification notification(hash, status, showTransaction); + + if (fQueueNotifications) + { + vQueueNotifications.push_back(notification); + return; + } + notification.invoke(ttm); +} + +static void ShowProgress(TransactionTableModel *ttm, const std::string &title, int nProgress) +{ + if (nProgress == 0) + fQueueNotifications = true; + + if (nProgress == 100) + { + fQueueNotifications = false; + if (vQueueNotifications.size() > 10) // prevent balloon spam, show maximum 10 balloons + QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true)); + for (unsigned int i = 0; i < vQueueNotifications.size(); ++i) + { + if (vQueueNotifications.size() - i <= 10) + QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false)); + + vQueueNotifications[i].invoke(ttm); + } + std::vector<TransactionNotification >().swap(vQueueNotifications); // clear + } +} + +void TransactionTableModel::subscribeToCoreSignals() +{ + // Connect signals to wallet + wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); + wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); +} + +void TransactionTableModel::unsubscribeFromCoreSignals() +{ + // Disconnect signals from wallet + wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); + wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); +} |