Merge #12079: Improve prioritisetransaction test coverage
[bitcoinplatinum.git] / src / qt / transactiontablemodel.cpp
blob626d4c0bdc93302894f00e9474d9f2f27a5c06c4
1 // Copyright (c) 2011-2017 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.
5 #include <qt/transactiontablemodel.h>
7 #include <qt/addresstablemodel.h>
8 #include <qt/guiconstants.h>
9 #include <qt/guiutil.h>
10 #include <qt/optionsmodel.h>
11 #include <qt/platformstyle.h>
12 #include <qt/transactiondesc.h>
13 #include <qt/transactionrecord.h>
14 #include <qt/walletmodel.h>
16 #include <core_io.h>
17 #include <validation.h>
18 #include <sync.h>
19 #include <uint256.h>
20 #include <util.h>
21 #include <wallet/wallet.h>
23 #include <QColor>
24 #include <QDateTime>
25 #include <QDebug>
26 #include <QIcon>
27 #include <QList>
29 // Amount column is right-aligned it contains numbers
30 static int column_alignments[] = {
31 Qt::AlignLeft|Qt::AlignVCenter, /* status */
32 Qt::AlignLeft|Qt::AlignVCenter, /* watchonly */
33 Qt::AlignLeft|Qt::AlignVCenter, /* date */
34 Qt::AlignLeft|Qt::AlignVCenter, /* type */
35 Qt::AlignLeft|Qt::AlignVCenter, /* address */
36 Qt::AlignRight|Qt::AlignVCenter /* amount */
39 // Comparison operator for sort/binary search of model tx list
40 struct TxLessThan
42 bool operator()(const TransactionRecord &a, const TransactionRecord &b) const
44 return a.hash < b.hash;
46 bool operator()(const TransactionRecord &a, const uint256 &b) const
48 return a.hash < b;
50 bool operator()(const uint256 &a, const TransactionRecord &b) const
52 return a < b.hash;
56 // Private implementation
57 class TransactionTablePriv
59 public:
60 TransactionTablePriv(CWallet *_wallet, TransactionTableModel *_parent) :
61 wallet(_wallet),
62 parent(_parent)
66 CWallet *wallet;
67 TransactionTableModel *parent;
69 /* Local cache of wallet.
70 * As it is in the same order as the CWallet, by definition
71 * this is sorted by sha256.
73 QList<TransactionRecord> cachedWallet;
75 /* Query entire wallet anew from core.
77 void refreshWallet()
79 qDebug() << "TransactionTablePriv::refreshWallet";
80 cachedWallet.clear();
82 LOCK2(cs_main, wallet->cs_wallet);
83 for (const auto& entry : wallet->mapWallet)
85 if (TransactionRecord::showTransaction(entry.second))
86 cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, entry.second));
91 /* Update our model of the wallet incrementally, to synchronize our model of the wallet
92 with that of the core.
94 Call with transaction that was added, removed or changed.
96 void updateWallet(const uint256 &hash, int status, bool showTransaction)
98 qDebug() << "TransactionTablePriv::updateWallet: " + QString::fromStdString(hash.ToString()) + " " + QString::number(status);
100 // Find bounds of this transaction in model
101 QList<TransactionRecord>::iterator lower = qLowerBound(
102 cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
103 QList<TransactionRecord>::iterator upper = qUpperBound(
104 cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
105 int lowerIndex = (lower - cachedWallet.begin());
106 int upperIndex = (upper - cachedWallet.begin());
107 bool inModel = (lower != upper);
109 if(status == CT_UPDATED)
111 if(showTransaction && !inModel)
112 status = CT_NEW; /* Not in model, but want to show, treat as new */
113 if(!showTransaction && inModel)
114 status = CT_DELETED; /* In model, but want to hide, treat as deleted */
117 qDebug() << " inModel=" + QString::number(inModel) +
118 " Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) +
119 " showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status);
121 switch(status)
123 case CT_NEW:
124 if(inModel)
126 qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is already in model";
127 break;
129 if(showTransaction)
131 LOCK2(cs_main, wallet->cs_wallet);
132 // Find transaction in wallet
133 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
134 if(mi == wallet->mapWallet.end())
136 qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is not in wallet";
137 break;
139 // Added -- insert at the right position
140 QList<TransactionRecord> toInsert =
141 TransactionRecord::decomposeTransaction(wallet, mi->second);
142 if(!toInsert.isEmpty()) /* only if something to insert */
144 parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
145 int insert_idx = lowerIndex;
146 for (const TransactionRecord &rec : toInsert)
148 cachedWallet.insert(insert_idx, rec);
149 insert_idx += 1;
151 parent->endInsertRows();
154 break;
155 case CT_DELETED:
156 if(!inModel)
158 qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_DELETED, but transaction is not in model";
159 break;
161 // Removed -- remove entire transaction from table
162 parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
163 cachedWallet.erase(lower, upper);
164 parent->endRemoveRows();
165 break;
166 case CT_UPDATED:
167 // Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for
168 // visible transactions.
169 for (int i = lowerIndex; i < upperIndex; i++) {
170 TransactionRecord *rec = &cachedWallet[i];
171 rec->status.needsUpdate = true;
173 break;
177 int size()
179 return cachedWallet.size();
182 TransactionRecord *index(int idx)
184 if(idx >= 0 && idx < cachedWallet.size())
186 TransactionRecord *rec = &cachedWallet[idx];
188 // Get required locks upfront. This avoids the GUI from getting
189 // stuck if the core is holding the locks for a longer time - for
190 // example, during a wallet rescan.
192 // If a status update is needed (blocks came in since last check),
193 // update the status of this transaction from the wallet. Otherwise,
194 // simply re-use the cached status.
195 TRY_LOCK(cs_main, lockMain);
196 if(lockMain)
198 TRY_LOCK(wallet->cs_wallet, lockWallet);
199 if(lockWallet && rec->statusUpdateNeeded())
201 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
203 if(mi != wallet->mapWallet.end())
205 rec->updateStatus(mi->second);
209 return rec;
211 return 0;
214 QString describe(TransactionRecord *rec, int unit)
217 LOCK2(cs_main, wallet->cs_wallet);
218 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
219 if(mi != wallet->mapWallet.end())
221 return TransactionDesc::toHTML(wallet, mi->second, rec, unit);
224 return QString();
227 QString getTxHex(TransactionRecord *rec)
229 LOCK2(cs_main, wallet->cs_wallet);
230 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
231 if(mi != wallet->mapWallet.end())
233 std::string strHex = EncodeHexTx(*mi->second.tx);
234 return QString::fromStdString(strHex);
236 return QString();
240 TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle, CWallet* _wallet, WalletModel *parent):
241 QAbstractTableModel(parent),
242 wallet(_wallet),
243 walletModel(parent),
244 priv(new TransactionTablePriv(_wallet, this)),
245 fProcessingQueuedTransactions(false),
246 platformStyle(_platformStyle)
248 columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
249 priv->refreshWallet();
251 connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
253 subscribeToCoreSignals();
256 TransactionTableModel::~TransactionTableModel()
258 unsubscribeFromCoreSignals();
259 delete priv;
262 /** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
263 void TransactionTableModel::updateAmountColumnTitle()
265 columns[Amount] = BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
266 Q_EMIT headerDataChanged(Qt::Horizontal,Amount,Amount);
269 void TransactionTableModel::updateTransaction(const QString &hash, int status, bool showTransaction)
271 uint256 updated;
272 updated.SetHex(hash.toStdString());
274 priv->updateWallet(updated, status, showTransaction);
277 void TransactionTableModel::updateConfirmations()
279 // Blocks came in since last poll.
280 // Invalidate status (number of confirmations) and (possibly) description
281 // for all rows. Qt is smart enough to only actually request the data for the
282 // visible rows.
283 Q_EMIT dataChanged(index(0, Status), index(priv->size()-1, Status));
284 Q_EMIT dataChanged(index(0, ToAddress), index(priv->size()-1, ToAddress));
287 int TransactionTableModel::rowCount(const QModelIndex &parent) const
289 Q_UNUSED(parent);
290 return priv->size();
293 int TransactionTableModel::columnCount(const QModelIndex &parent) const
295 Q_UNUSED(parent);
296 return columns.length();
299 QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const
301 QString status;
303 switch(wtx->status.status)
305 case TransactionStatus::OpenUntilBlock:
306 status = tr("Open for %n more block(s)","",wtx->status.open_for);
307 break;
308 case TransactionStatus::OpenUntilDate:
309 status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for));
310 break;
311 case TransactionStatus::Offline:
312 status = tr("Offline");
313 break;
314 case TransactionStatus::Unconfirmed:
315 status = tr("Unconfirmed");
316 break;
317 case TransactionStatus::Abandoned:
318 status = tr("Abandoned");
319 break;
320 case TransactionStatus::Confirming:
321 status = tr("Confirming (%1 of %2 recommended confirmations)").arg(wtx->status.depth).arg(TransactionRecord::RecommendedNumConfirmations);
322 break;
323 case TransactionStatus::Confirmed:
324 status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth);
325 break;
326 case TransactionStatus::Conflicted:
327 status = tr("Conflicted");
328 break;
329 case TransactionStatus::Immature:
330 status = tr("Immature (%1 confirmations, will be available after %2)").arg(wtx->status.depth).arg(wtx->status.depth + wtx->status.matures_in);
331 break;
332 case TransactionStatus::MaturesWarning:
333 status = tr("This block was not received by any other nodes and will probably not be accepted!");
334 break;
335 case TransactionStatus::NotAccepted:
336 status = tr("Generated but not accepted");
337 break;
340 return status;
343 QString TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const
345 if(wtx->time)
347 return GUIUtil::dateTimeStr(wtx->time);
349 return QString();
352 /* Look up address in address book, if found return label (address)
353 otherwise just return (address)
355 QString TransactionTableModel::lookupAddress(const std::string &address, bool tooltip) const
357 QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
358 QString description;
359 if(!label.isEmpty())
361 description += label;
363 if(label.isEmpty() || tooltip)
365 description += QString(" (") + QString::fromStdString(address) + QString(")");
367 return description;
370 QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
372 switch(wtx->type)
374 case TransactionRecord::RecvWithAddress:
375 return tr("Received with");
376 case TransactionRecord::RecvFromOther:
377 return tr("Received from");
378 case TransactionRecord::SendToAddress:
379 case TransactionRecord::SendToOther:
380 return tr("Sent to");
381 case TransactionRecord::SendToSelf:
382 return tr("Payment to yourself");
383 case TransactionRecord::Generated:
384 return tr("Mined");
385 default:
386 return QString();
390 QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx) const
392 switch(wtx->type)
394 case TransactionRecord::Generated:
395 return QIcon(":/icons/tx_mined");
396 case TransactionRecord::RecvWithAddress:
397 case TransactionRecord::RecvFromOther:
398 return QIcon(":/icons/tx_input");
399 case TransactionRecord::SendToAddress:
400 case TransactionRecord::SendToOther:
401 return QIcon(":/icons/tx_output");
402 default:
403 return QIcon(":/icons/tx_inout");
407 QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const
409 QString watchAddress;
410 if (tooltip) {
411 // Mark transactions involving watch-only addresses by adding " (watch-only)"
412 watchAddress = wtx->involvesWatchAddress ? QString(" (") + tr("watch-only") + QString(")") : "";
415 switch(wtx->type)
417 case TransactionRecord::RecvFromOther:
418 return QString::fromStdString(wtx->address) + watchAddress;
419 case TransactionRecord::RecvWithAddress:
420 case TransactionRecord::SendToAddress:
421 case TransactionRecord::Generated:
422 return lookupAddress(wtx->address, tooltip) + watchAddress;
423 case TransactionRecord::SendToOther:
424 return QString::fromStdString(wtx->address) + watchAddress;
425 case TransactionRecord::SendToSelf:
426 default:
427 return tr("(n/a)") + watchAddress;
431 QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
433 // Show addresses without label in a less visible color
434 switch(wtx->type)
436 case TransactionRecord::RecvWithAddress:
437 case TransactionRecord::SendToAddress:
438 case TransactionRecord::Generated:
440 QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(wtx->address));
441 if(label.isEmpty())
442 return COLOR_BAREADDRESS;
443 } break;
444 case TransactionRecord::SendToSelf:
445 return COLOR_BAREADDRESS;
446 default:
447 break;
449 return QVariant();
452 QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed, BitcoinUnits::SeparatorStyle separators) const
454 QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit, false, separators);
455 if(showUnconfirmed)
457 if(!wtx->status.countsForBalance)
459 str = QString("[") + str + QString("]");
462 return QString(str);
465 QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const
467 switch(wtx->status.status)
469 case TransactionStatus::OpenUntilBlock:
470 case TransactionStatus::OpenUntilDate:
471 return COLOR_TX_STATUS_OPENUNTILDATE;
472 case TransactionStatus::Offline:
473 return COLOR_TX_STATUS_OFFLINE;
474 case TransactionStatus::Unconfirmed:
475 return QIcon(":/icons/transaction_0");
476 case TransactionStatus::Abandoned:
477 return QIcon(":/icons/transaction_abandoned");
478 case TransactionStatus::Confirming:
479 switch(wtx->status.depth)
481 case 1: return QIcon(":/icons/transaction_1");
482 case 2: return QIcon(":/icons/transaction_2");
483 case 3: return QIcon(":/icons/transaction_3");
484 case 4: return QIcon(":/icons/transaction_4");
485 default: return QIcon(":/icons/transaction_5");
487 case TransactionStatus::Confirmed:
488 return QIcon(":/icons/transaction_confirmed");
489 case TransactionStatus::Conflicted:
490 return QIcon(":/icons/transaction_conflicted");
491 case TransactionStatus::Immature: {
492 int total = wtx->status.depth + wtx->status.matures_in;
493 int part = (wtx->status.depth * 4 / total) + 1;
494 return QIcon(QString(":/icons/transaction_%1").arg(part));
496 case TransactionStatus::MaturesWarning:
497 case TransactionStatus::NotAccepted:
498 return QIcon(":/icons/transaction_0");
499 default:
500 return COLOR_BLACK;
504 QVariant TransactionTableModel::txWatchonlyDecoration(const TransactionRecord *wtx) const
506 if (wtx->involvesWatchAddress)
507 return QIcon(":/icons/eye");
508 else
509 return QVariant();
512 QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const
514 QString tooltip = formatTxStatus(rec) + QString("\n") + formatTxType(rec);
515 if(rec->type==TransactionRecord::RecvFromOther || rec->type==TransactionRecord::SendToOther ||
516 rec->type==TransactionRecord::SendToAddress || rec->type==TransactionRecord::RecvWithAddress)
518 tooltip += QString(" ") + formatTxToAddress(rec, true);
520 return tooltip;
523 QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
525 if(!index.isValid())
526 return QVariant();
527 TransactionRecord *rec = static_cast<TransactionRecord*>(index.internalPointer());
529 switch(role)
531 case RawDecorationRole:
532 switch(index.column())
534 case Status:
535 return txStatusDecoration(rec);
536 case Watchonly:
537 return txWatchonlyDecoration(rec);
538 case ToAddress:
539 return txAddressDecoration(rec);
541 break;
542 case Qt::DecorationRole:
544 QIcon icon = qvariant_cast<QIcon>(index.data(RawDecorationRole));
545 return platformStyle->TextColorIcon(icon);
547 case Qt::DisplayRole:
548 switch(index.column())
550 case Date:
551 return formatTxDate(rec);
552 case Type:
553 return formatTxType(rec);
554 case ToAddress:
555 return formatTxToAddress(rec, false);
556 case Amount:
557 return formatTxAmount(rec, true, BitcoinUnits::separatorAlways);
559 break;
560 case Qt::EditRole:
561 // Edit role is used for sorting, so return the unformatted values
562 switch(index.column())
564 case Status:
565 return QString::fromStdString(rec->status.sortKey);
566 case Date:
567 return rec->time;
568 case Type:
569 return formatTxType(rec);
570 case Watchonly:
571 return (rec->involvesWatchAddress ? 1 : 0);
572 case ToAddress:
573 return formatTxToAddress(rec, true);
574 case Amount:
575 return qint64(rec->credit + rec->debit);
577 break;
578 case Qt::ToolTipRole:
579 return formatTooltip(rec);
580 case Qt::TextAlignmentRole:
581 return column_alignments[index.column()];
582 case Qt::ForegroundRole:
583 // Use the "danger" color for abandoned transactions
584 if(rec->status.status == TransactionStatus::Abandoned)
586 return COLOR_TX_STATUS_DANGER;
588 // Non-confirmed (but not immature) as transactions are grey
589 if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature)
591 return COLOR_UNCONFIRMED;
593 if(index.column() == Amount && (rec->credit+rec->debit) < 0)
595 return COLOR_NEGATIVE;
597 if(index.column() == ToAddress)
599 return addressColor(rec);
601 break;
602 case TypeRole:
603 return rec->type;
604 case DateRole:
605 return QDateTime::fromTime_t(static_cast<uint>(rec->time));
606 case WatchonlyRole:
607 return rec->involvesWatchAddress;
608 case WatchonlyDecorationRole:
609 return txWatchonlyDecoration(rec);
610 case LongDescriptionRole:
611 return priv->describe(rec, walletModel->getOptionsModel()->getDisplayUnit());
612 case AddressRole:
613 return QString::fromStdString(rec->address);
614 case LabelRole:
615 return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address));
616 case AmountRole:
617 return qint64(rec->credit + rec->debit);
618 case TxIDRole:
619 return rec->getTxID();
620 case TxHashRole:
621 return QString::fromStdString(rec->hash.ToString());
622 case TxHexRole:
623 return priv->getTxHex(rec);
624 case TxPlainTextRole:
626 QString details;
627 QDateTime date = QDateTime::fromTime_t(static_cast<uint>(rec->time));
628 QString txLabel = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address));
630 details.append(date.toString("M/d/yy HH:mm"));
631 details.append(" ");
632 details.append(formatTxStatus(rec));
633 details.append(". ");
634 if(!formatTxType(rec).isEmpty()) {
635 details.append(formatTxType(rec));
636 details.append(" ");
638 if(!rec->address.empty()) {
639 if(txLabel.isEmpty())
640 details.append(tr("(no label)") + " ");
641 else {
642 details.append("(");
643 details.append(txLabel);
644 details.append(") ");
646 details.append(QString::fromStdString(rec->address));
647 details.append(" ");
649 details.append(formatTxAmount(rec, false, BitcoinUnits::separatorNever));
650 return details;
652 case ConfirmedRole:
653 return rec->status.countsForBalance;
654 case FormattedAmountRole:
655 // Used for copy/export, so don't include separators
656 return formatTxAmount(rec, false, BitcoinUnits::separatorNever);
657 case StatusRole:
658 return rec->status.status;
660 return QVariant();
663 QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const
665 if(orientation == Qt::Horizontal)
667 if(role == Qt::DisplayRole)
669 return columns[section];
671 else if (role == Qt::TextAlignmentRole)
673 return column_alignments[section];
674 } else if (role == Qt::ToolTipRole)
676 switch(section)
678 case Status:
679 return tr("Transaction status. Hover over this field to show number of confirmations.");
680 case Date:
681 return tr("Date and time that the transaction was received.");
682 case Type:
683 return tr("Type of transaction.");
684 case Watchonly:
685 return tr("Whether or not a watch-only address is involved in this transaction.");
686 case ToAddress:
687 return tr("User-defined intent/purpose of the transaction.");
688 case Amount:
689 return tr("Amount removed from or added to balance.");
693 return QVariant();
696 QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const
698 Q_UNUSED(parent);
699 TransactionRecord *data = priv->index(row);
700 if(data)
702 return createIndex(row, column, priv->index(row));
704 return QModelIndex();
707 void TransactionTableModel::updateDisplayUnit()
709 // emit dataChanged to update Amount column with the current unit
710 updateAmountColumnTitle();
711 Q_EMIT dataChanged(index(0, Amount), index(priv->size()-1, Amount));
714 // queue notifications to show a non freezing progress dialog e.g. for rescan
715 struct TransactionNotification
717 public:
718 TransactionNotification() {}
719 TransactionNotification(uint256 _hash, ChangeType _status, bool _showTransaction):
720 hash(_hash), status(_status), showTransaction(_showTransaction) {}
722 void invoke(QObject *ttm)
724 QString strHash = QString::fromStdString(hash.GetHex());
725 qDebug() << "NotifyTransactionChanged: " + strHash + " status= " + QString::number(status);
726 QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection,
727 Q_ARG(QString, strHash),
728 Q_ARG(int, status),
729 Q_ARG(bool, showTransaction));
731 private:
732 uint256 hash;
733 ChangeType status;
734 bool showTransaction;
737 static bool fQueueNotifications = false;
738 static std::vector< TransactionNotification > vQueueNotifications;
740 static void NotifyTransactionChanged(TransactionTableModel *ttm, CWallet *wallet, const uint256 &hash, ChangeType status)
742 // Find transaction in wallet
743 std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
744 // Determine whether to show transaction or not (determine this here so that no relocking is needed in GUI thread)
745 bool inWallet = mi != wallet->mapWallet.end();
746 bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second));
748 TransactionNotification notification(hash, status, showTransaction);
750 if (fQueueNotifications)
752 vQueueNotifications.push_back(notification);
753 return;
755 notification.invoke(ttm);
758 static void ShowProgress(TransactionTableModel *ttm, const std::string &title, int nProgress)
760 if (nProgress == 0)
761 fQueueNotifications = true;
763 if (nProgress == 100)
765 fQueueNotifications = false;
766 if (vQueueNotifications.size() > 10) // prevent balloon spam, show maximum 10 balloons
767 QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
768 for (unsigned int i = 0; i < vQueueNotifications.size(); ++i)
770 if (vQueueNotifications.size() - i <= 10)
771 QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
773 vQueueNotifications[i].invoke(ttm);
775 std::vector<TransactionNotification >().swap(vQueueNotifications); // clear
779 void TransactionTableModel::subscribeToCoreSignals()
781 // Connect signals to wallet
782 wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
783 wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
786 void TransactionTableModel::unsubscribeFromCoreSignals()
788 // Disconnect signals from wallet
789 wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
790 wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));