From 51d7cc07f10209ac12bd5286391e3c8b095abd34 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 8 Jul 2011 22:27:36 +0200 Subject: [PATCH] Add context menu on transaction list: copy label, copy address, edit label, show details --- src/qt/addresstablemodel.cpp | 30 +++++++++ src/qt/addresstablemodel.h | 9 +++ src/qt/bitcoingui.cpp | 13 +--- src/qt/bitcoingui.h | 1 - src/qt/sendcoinsdialog.cpp | 3 +- src/qt/transactiontablemodel.cpp | 5 +- src/qt/transactionview.cpp | 110 ++++++++++++++++++++++++++++++- src/qt/transactionview.h | 16 ++++- src/qt/walletmodel.cpp | 14 ---- src/qt/walletmodel.h | 4 -- 10 files changed, 168 insertions(+), 37 deletions(-) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index d04989ea2..9ca754201 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -296,3 +296,33 @@ bool AddressTableModel::validateAddress(const QString &address) return AddressToHash160(address.toStdString(), hash160); } + +/* Look up label for address in address book, if not found return empty string. + */ +QString AddressTableModel::labelForAddress(const QString &address) const +{ + CRITICAL_BLOCK(wallet->cs_mapAddressBook) + { + std::map::iterator mi = wallet->mapAddressBook.find(address.toStdString()); + if (mi != wallet->mapAddressBook.end()) + { + return QString::fromStdString(mi->second); + } + } + return QString(); +} + +int AddressTableModel::lookupAddress(const QString &address) const +{ + QModelIndexList lst = match(index(0, Address, QModelIndex()), + Qt::EditRole, address, 1, Qt::MatchExactly); + if(lst.isEmpty()) + { + return -1; + } + else + { + return lst.at(0).row(); + } +} + diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 6f34a6006..d48e78662 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -49,6 +49,15 @@ public: */ bool validateAddress(const QString &address); + /* Look up label for address in address book, if not found return empty string. + */ + QString labelForAddress(const QString &address) const; + + /* Look up row index of an address in the model. + Return -1 if not found. + */ + int lookupAddress(const QString &address) const; + private: CWallet *wallet; AddressTablePriv *priv; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index add8abc99..529195133 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -84,7 +84,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QVBoxLayout *vbox = new QVBoxLayout(); transactionView = new TransactionView(this); - connect(transactionView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&))); + connect(transactionView, SIGNAL(doubleClicked(const QModelIndex&)), transactionView, SLOT(transactionDetails())); vbox->addWidget(transactionView); transactionsPage = new QWidget(this); @@ -229,7 +229,7 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) connect(walletModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); // Put transaction list in tabs - transactionView->setModel(walletModel->getTransactionTableModel()); + transactionView->setModel(walletModel); addressBookPage->setModel(walletModel->getAddressTableModel()); receiveCoinsPage->setModel(walletModel->getAddressTableModel()); @@ -298,7 +298,7 @@ void BitcoinGUI::setNumConnections(int count) default: icon = ":/icons/connect_4"; break; } labelConnections->setTextFormat(Qt::RichText); - labelConnections->setText("" + tr("%n connection(s)", "", count)); + labelConnections->setText(" " + tr("%n connection(s)", "", count)); } void BitcoinGUI::setNumBlocks(int count) @@ -411,13 +411,6 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) *payFee = (retval == QMessageBox::Yes); } -void BitcoinGUI::transactionDetails(const QModelIndex& idx) -{ - // A transaction is doubleclicked - TransactionDescDialog dlg(idx); - dlg.exec(); -} - void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) { TransactionTableModel *ttm = walletModel->getTransactionTableModel(); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 8c3632a3a..b82818aa3 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -102,7 +102,6 @@ private slots: void optionsClicked(); void aboutClicked(); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); - void transactionDetails(const QModelIndex& idx); void incomingTransaction(const QModelIndex & parent, int start, int end); void exportClicked(); }; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 9b7cc632e..4adda7e8f 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,6 +1,7 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" #include "walletmodel.h" +#include "addresstablemodel.h" #include "guiutil.h" #include "addressbookpage.h" @@ -131,7 +132,7 @@ void SendCoinsDialog::on_buttonBox_rejected() void SendCoinsDialog::on_payTo_textChanged(const QString &address) { - ui->addAsLabel->setText(model->labelForAddress(address)); + ui->addAsLabel->setText(model->getAddressTableModel()->labelForAddress(address)); } void SendCoinsDialog::clear() diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 2477a1e8c..17622e07f 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -4,6 +4,7 @@ #include "guiconstants.h" #include "transactiondesc.h" #include "walletmodel.h" +#include "addresstablemodel.h" #include "headers.h" @@ -325,7 +326,7 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const */ QString TransactionTableModel::lookupAddress(const std::string &address) const { - QString label = walletModel->labelForAddress(QString::fromStdString(address)); + QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address)); QString description; if(label.isEmpty()) { @@ -538,7 +539,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const } else if (role == LabelRole) { - return walletModel->labelForAddress(QString::fromStdString(rec->address)); + return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address)); } else if (role == AbsoluteAmountRole) { diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 5a383da2b..53c33c7c4 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -2,9 +2,13 @@ #include "transactionfilterproxy.h" #include "transactionrecord.h" +#include "walletmodel.h" +#include "addresstablemodel.h" #include "transactiontablemodel.h" #include "guiutil.h" #include "csvmodelwriter.h" +#include "transactiondescdialog.h" +#include "editaddressdialog.h" #include #include @@ -17,6 +21,10 @@ #include #include #include +#include +#include +#include +#include #include @@ -90,23 +98,47 @@ TransactionView::TransactionView(QWidget *parent) : // Always show scroll bar view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); view->setTabKeyNavigation(false); + view->setContextMenuPolicy(Qt::CustomContextMenu); transactionView = view; + // Actions + QAction *copyAddressAction = new QAction("Copy address", this); + QAction *copyLabelAction = new QAction("Copy label", this); + QAction *editLabelAction = new QAction("Edit label", this); + QAction *showDetailsAction = new QAction("Show details...", this); + + contextMenu = new QMenu(); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyLabelAction); + contextMenu->addAction(editLabelAction); + contextMenu->addAction(showDetailsAction); + + // Connect actions connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); connect(addressWidget, SIGNAL(textChanged(const QString&)), this, SLOT(changedPrefix(const QString&))); connect(amountWidget, SIGNAL(textChanged(const QString&)), this, SLOT(changedAmount(const QString&))); connect(view, SIGNAL(doubleClicked(const QModelIndex&)), this, SIGNAL(doubleClicked(const QModelIndex&))); + + connect(view, + SIGNAL(customContextMenuRequested(const QPoint &)), + this, + SLOT(contextualMenu(const QPoint &))); + + connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); + connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); + connect(editLabelAction, SIGNAL(triggered()), this, SLOT(editLabel())); + connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails())); } -void TransactionView::setModel(TransactionTableModel *model) +void TransactionView::setModel(WalletModel *model) { this->model = model; transactionProxyModel = new TransactionFilterProxy(this); - transactionProxyModel->setSourceModel(model); + transactionProxyModel->setSourceModel(model->getTransactionTableModel()); transactionProxyModel->setDynamicSortFilter(true); transactionProxyModel->setSortRole(Qt::EditRole); @@ -233,3 +265,77 @@ void TransactionView::exportClicked() } } +void TransactionView::contextualMenu(const QPoint &point) +{ + QModelIndex index = transactionView->indexAt(point); + if(index.isValid()) + { + contextMenu->exec(QCursor::pos()); + } +} + +void TransactionView::copyAddress() +{ + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + QApplication::clipboard()->setText(selection.at(0).data(TransactionTableModel::AddressRole).toString()); + } +} + +void TransactionView::copyLabel() +{ + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + QApplication::clipboard()->setText(selection.at(0).data(TransactionTableModel::LabelRole).toString()); + } +} + +void TransactionView::editLabel() +{ + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + AddressTableModel *addressBook = model->getAddressTableModel(); + QString address = selection.at(0).data(TransactionTableModel::AddressRole).toString(); + if(address.isEmpty()) + { + // If this transaction has no associated address, exit + return; + } + int idx = addressBook->lookupAddress(address); + if(idx != -1) + { + // Edit sending / receiving address + QModelIndex modelIdx = addressBook->index(idx, 0, QModelIndex()); + // Determine type of address, launch appropriate editor dialog type + QString type = modelIdx.data(AddressTableModel::TypeRole).toString(); + + EditAddressDialog dlg(type==AddressTableModel::Receive + ? EditAddressDialog::EditReceivingAddress + : EditAddressDialog::EditSendingAddress, + this); + dlg.setModel(addressBook); + dlg.loadRow(idx); + dlg.exec(); + } + else + { + // Add sending address + EditAddressDialog dlg(EditAddressDialog::NewSendingAddress, + this); + dlg.exec(); + } + } +} + +void TransactionView::showDetails() +{ + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + TransactionDescDialog dlg(selection.at(0)); + dlg.exec(); + } +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 25212c976..f02751a07 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -3,7 +3,7 @@ #include -class TransactionTableModel; +class WalletModel; class TransactionFilterProxy; QT_BEGIN_NAMESPACE @@ -11,6 +11,7 @@ class QTableView; class QComboBox; class QLineEdit; class QModelIndex; +class QMenu; QT_END_NAMESPACE class TransactionView : public QWidget @@ -19,7 +20,7 @@ class TransactionView : public QWidget public: explicit TransactionView(QWidget *parent = 0); - void setModel(TransactionTableModel *model); + void setModel(WalletModel *model); enum DateEnum { @@ -33,7 +34,7 @@ public: }; private: - TransactionTableModel *model; + WalletModel *model; TransactionFilterProxy *transactionProxyModel; QTableView *transactionView; @@ -42,6 +43,11 @@ private: QLineEdit *addressWidget; QLineEdit *amountWidget; + QMenu *contextMenu; + +private slots: + void contextualMenu(const QPoint &); + signals: void doubleClicked(const QModelIndex&); @@ -51,6 +57,10 @@ public slots: void changedPrefix(const QString &prefix); void changedAmount(const QString &amount); void exportClicked(); + void showDetails(); + void copyAddress(); + void editLabel(); + void copyLabel(); }; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index f962b9a7c..052bf37e3 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -123,18 +123,4 @@ TransactionTableModel *WalletModel::getTransactionTableModel() return transactionTableModel; } -/* Look up label for address in address book, if not found return empty string. - */ -QString WalletModel::labelForAddress(const QString &address) const -{ - CRITICAL_BLOCK(wallet->cs_mapAddressBook) - { - std::map::iterator mi = wallet->mapAddressBook.find(address.toStdString()); - if (mi != wallet->mapAddressBook.end()) - { - return QString::fromStdString(mi->second); - } - } - return QString(); -} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 9c7d16fce..5b46dfb69 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -33,10 +33,6 @@ public: qint64 getBalance() const; int getNumTransactions() const; - /* Look up label for address in address book, if not found return empty string. - */ - QString labelForAddress(const QString &address) const; - /* Send coins */ StatusCode sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs=QString()); private: