diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 2052264dc..eef0c317c 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -233,6 +233,9 @@ RES_ICONS = \ qt/res/icons/editcopy.png \ qt/res/icons/editpaste.png \ qt/res/icons/export.png \ + qt/res/icons/eye.png \ + qt/res/icons/eye_minus.png \ + qt/res/icons/eye_plus.png \ qt/res/icons/filesave.png \ qt/res/icons/history.png \ qt/res/icons/key.png \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 357c6470d..9a7003938 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -18,6 +18,9 @@ res/icons/clock3.png res/icons/clock4.png res/icons/clock5.png + res/icons/eye.png + res/icons/eye_minus.png + res/icons/eye_plus.png res/icons/configure.png res/icons/receive.png res/icons/editpaste.png diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 8eed0856e..15501b8a8 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -57,7 +57,15 @@ public: } painter->setPen(foreground); - painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address); + QRect boundingRect; + painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address, &boundingRect); + + if (index.data(TransactionTableModel::WatchonlyRole).toBool()) + { + QIcon iconWatchonly = qvariant_cast(index.data(TransactionTableModel::WatchonlyDecorationRole)); + QRect watchonlyRect(boundingRect.right() + 5, mainRect.top()+ypad+halfheight, 16, halfheight); + iconWatchonly.paint(painter, watchonlyRect); + } if(amount < 0) { diff --git a/src/qt/res/icons/eye.png b/src/qt/res/icons/eye.png new file mode 100644 index 000000000..c4d182adb Binary files /dev/null and b/src/qt/res/icons/eye.png differ diff --git a/src/qt/res/icons/eye_minus.png b/src/qt/res/icons/eye_minus.png new file mode 100644 index 000000000..08b048eae Binary files /dev/null and b/src/qt/res/icons/eye_minus.png differ diff --git a/src/qt/res/icons/eye_plus.png b/src/qt/res/icons/eye_plus.png new file mode 100644 index 000000000..4ad653156 Binary files /dev/null and b/src/qt/res/icons/eye_plus.png differ diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index f9546fddb..6ab029173 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -22,6 +22,7 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : dateTo(MAX_DATE), addrPrefix(), typeFilter(ALL_TYPES), + watchOnlyFilter(WatchOnlyFilter_All), minAmount(0), limitRows(-1), showInactive(true) @@ -34,6 +35,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & int type = index.data(TransactionTableModel::TypeRole).toInt(); QDateTime datetime = index.data(TransactionTableModel::DateRole).toDateTime(); + bool involvesWatchAddress = index.data(TransactionTableModel::WatchonlyRole).toBool(); QString address = index.data(TransactionTableModel::AddressRole).toString(); QString label = index.data(TransactionTableModel::LabelRole).toString(); qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); @@ -43,6 +45,10 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & return false; if(!(TYPE(type) & typeFilter)) return false; + if (involvesWatchAddress && watchOnlyFilter == WatchOnlyFilter_No) + return false; + if (!involvesWatchAddress && watchOnlyFilter == WatchOnlyFilter_Yes) + return false; if(datetime < dateFrom || datetime > dateTo) return false; if (!address.contains(addrPrefix, Qt::CaseInsensitive) && !label.contains(addrPrefix, Qt::CaseInsensitive)) @@ -78,6 +84,12 @@ void TransactionFilterProxy::setMinAmount(qint64 minimum) invalidateFilter(); } +void TransactionFilterProxy::setWatchOnlyFilter(WatchOnlyFilter filter) +{ + this->watchOnlyFilter = filter; + invalidateFilter(); +} + void TransactionFilterProxy::setLimit(int limit) { this->limitRows = limit; diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h index 9919bc3fd..f408317b5 100644 --- a/src/qt/transactionfilterproxy.h +++ b/src/qt/transactionfilterproxy.h @@ -25,6 +25,13 @@ public: static quint32 TYPE(int type) { return 1<getOptionsModel()->getDisplayUnit()); + 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())); @@ -393,22 +394,19 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const { - // mark transactions involving watch-only addresses: - QString watchAddress = wtx->involvesWatchAddress ? " (w) " : ""; - switch(wtx->type) { case TransactionRecord::RecvFromOther: - return QString::fromStdString(wtx->address) + watchAddress; + return QString::fromStdString(wtx->address); case TransactionRecord::RecvWithAddress: case TransactionRecord::SendToAddress: case TransactionRecord::Generated: - return lookupAddress(wtx->address, tooltip) + watchAddress; + return lookupAddress(wtx->address, tooltip); case TransactionRecord::SendToOther: - return QString::fromStdString(wtx->address) + watchAddress; + return QString::fromStdString(wtx->address); case TransactionRecord::SendToSelf: default: - return tr("(n/a)") + watchAddress; + return tr("(n/a)"); } } @@ -482,6 +480,14 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) return QColor(0,0,0); } +QVariant TransactionTableModel::txWatchonlyDecoration(const TransactionRecord *wtx) const +{ + if (wtx->involvesWatchAddress) + return QIcon(":/icons/eye"); + else + return QVariant(); +} + QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const { QString tooltip = formatTxStatus(rec) + QString("\n") + formatTxType(rec); @@ -506,6 +512,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { case Status: return txStatusDecoration(rec); + case Watchonly: + return txWatchonlyDecoration(rec); case ToAddress: return txAddressDecoration(rec); } @@ -533,6 +541,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return rec->time; case Type: return formatTxType(rec); + case Watchonly: + return (rec->involvesWatchAddress ? 1 : 0); case ToAddress: return formatTxToAddress(rec, true); case Amount: @@ -562,6 +572,10 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return rec->type; case DateRole: return QDateTime::fromTime_t(static_cast(rec->time)); + case WatchonlyRole: + return rec->involvesWatchAddress; + case WatchonlyDecorationRole: + return txWatchonlyDecoration(rec); case LongDescriptionRole: return priv->describe(rec, walletModel->getOptionsModel()->getDisplayUnit()); case AddressRole: @@ -606,6 +620,8 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat return tr("Date and time that the transaction was received."); case Type: return tr("Type of transaction."); + case Watchonly: + return tr("Whether or not a watch-only address is involved in this transaction."); case ToAddress: return tr("Destination address of transaction."); case Amount: diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 2124d3dd1..413f3f9bf 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -28,10 +28,11 @@ public: enum ColumnIndex { Status = 0, - Date = 1, - Type = 2, - ToAddress = 3, - Amount = 4 + Watchonly = 1, + Date = 2, + Type = 3, + ToAddress = 4, + Amount = 5 }; /** Roles to get specific information from a transaction row. @@ -42,6 +43,10 @@ public: TypeRole = Qt::UserRole, /** Date and time this transaction was created */ DateRole, + /** Watch-only boolean */ + WatchonlyRole, + /** Watch-only icon */ + WatchonlyDecorationRole, /** Long description (HTML format) */ LongDescriptionRole, /** Address of transaction */ @@ -83,6 +88,7 @@ private: QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::separatorStandard) const; QString formatTooltip(const TransactionRecord *rec) const; QVariant txStatusDecoration(const TransactionRecord *wtx) const; + QVariant txWatchonlyDecoration(const TransactionRecord *wtx) const; QVariant txAddressDecoration(const TransactionRecord *wtx) const; public slots: diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 7e8b71d8e..2d34d5812 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -51,6 +51,13 @@ TransactionView::TransactionView(QWidget *parent) : hlayout->addSpacing(23); #endif + watchOnlyWidget = new QComboBox(this); + watchOnlyWidget->setFixedWidth(24); + watchOnlyWidget->addItem("", TransactionFilterProxy::WatchOnlyFilter_All); + watchOnlyWidget->addItem(QIcon(":/icons/eye_plus"), "", TransactionFilterProxy::WatchOnlyFilter_Yes); + watchOnlyWidget->addItem(QIcon(":/icons/eye_minus"), "", TransactionFilterProxy::WatchOnlyFilter_No); + hlayout->addWidget(watchOnlyWidget); + dateWidget = new QComboBox(this); #ifdef Q_OS_MAC dateWidget->setFixedWidth(121); @@ -150,6 +157,7 @@ TransactionView::TransactionView(QWidget *parent) : connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); + connect(watchOnlyWidget, SIGNAL(activated(int)), this, SLOT(chooseWatchonly(int))); connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); @@ -187,6 +195,7 @@ void TransactionView::setModel(WalletModel *model) transactionView->verticalHeader()->hide(); transactionView->setColumnWidth(TransactionTableModel::Status, STATUS_COLUMN_WIDTH); + transactionView->setColumnWidth(TransactionTableModel::Watchonly, WATCHONLY_COLUMN_WIDTH); transactionView->setColumnWidth(TransactionTableModel::Date, DATE_COLUMN_WIDTH); transactionView->setColumnWidth(TransactionTableModel::Type, TYPE_COLUMN_WIDTH); transactionView->setColumnWidth(TransactionTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH); @@ -211,6 +220,12 @@ void TransactionView::setModel(WalletModel *model) } } } + + // show/hide column Watch-only + updateWatchOnlyColumn(model->haveWatchOnly()); + + // Watch-only signal + connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyColumn(bool))); } } @@ -270,6 +285,14 @@ void TransactionView::chooseType(int idx) typeWidget->itemData(idx).toInt()); } +void TransactionView::chooseWatchonly(int idx) +{ + if(!transactionProxyModel) + return; + transactionProxyModel->setWatchOnlyFilter( + (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt()); +} + void TransactionView::changedPrefix(const QString &prefix) { if(!transactionProxyModel) @@ -307,6 +330,8 @@ void TransactionView::exportClicked() // name, column, role writer.setModel(transactionProxyModel); writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole); + if (model && model->haveWatchOnly()) + writer.addColumn(tr("Watchonly"), TransactionTableModel::Watchonly); writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole); writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole); writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole); @@ -501,3 +526,10 @@ bool TransactionView::eventFilter(QObject *obj, QEvent *event) } return QWidget::eventFilter(obj, event); } + +// show/hide column Watch-only +void TransactionView::updateWatchOnlyColumn(bool fHaveWatchOnly) +{ + watchOnlyWidget->setVisible(fHaveWatchOnly); + transactionView->setColumnHidden(TransactionTableModel::Watchonly, !fHaveWatchOnly); +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 618efbc56..b249e0041 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -50,6 +50,7 @@ public: enum ColumnWidths { STATUS_COLUMN_WIDTH = 23, + WATCHONLY_COLUMN_WIDTH = 23, DATE_COLUMN_WIDTH = 120, TYPE_COLUMN_WIDTH = 120, AMOUNT_MINIMUM_COLUMN_WIDTH = 120, @@ -63,6 +64,7 @@ private: QComboBox *dateWidget; QComboBox *typeWidget; + QComboBox *watchOnlyWidget; QLineEdit *addressWidget; QLineEdit *amountWidget; @@ -91,6 +93,7 @@ private slots: void copyAmount(); void copyTxID(); void openThirdPartyTxUrl(QString url); + void updateWatchOnlyColumn(bool fHaveWatchOnly); signals: void doubleClicked(const QModelIndex&); @@ -101,6 +104,7 @@ signals: public slots: void chooseDate(int idx); void chooseType(int idx); + void chooseWatchonly(int idx); void changedPrefix(const QString &prefix); void changedAmount(const QString &amount); void exportClicked();