diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 7f8bc1c6d..1a473ac4b 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -184,6 +184,7 @@ QT_MOC_CPP = \
qt/moc_transactionfilterproxy.cpp \
qt/moc_transactiontablemodel.cpp \
qt/moc_transactionview.cpp \
+ qt/moc_kevaview.cpp \
qt/moc_utilitydialog.cpp \
qt/moc_walletframe.cpp \
qt/moc_walletmodel.cpp \
@@ -258,6 +259,7 @@ BITCOIN_QT_H = \
qt/transactionrecord.h \
qt/transactiontablemodel.h \
qt/transactionview.h \
+ qt/kevaview.h \
qt/utilitydialog.h \
qt/walletframe.h \
qt/walletmodel.h \
@@ -372,6 +374,7 @@ BITCOIN_QT_WALLET_CPP = \
qt/transactionrecord.cpp \
qt/transactiontablemodel.cpp \
qt/transactionview.cpp \
+ qt/kevaview.cpp \
qt/walletframe.cpp \
qt/walletmodel.cpp \
qt/walletmodeltransaction.cpp \
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 422c90725..2a07ab6d9 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -313,6 +313,13 @@ void BitcoinGUI::createActions()
historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4));
tabGroup->addAction(historyAction);
+ kevaAction = new QAction(platformStyle->SingleColorIcon(":/icons/key"), tr("&Keva"), this);
+ kevaAction->setStatusTip(tr("Keva related operations"));
+ kevaAction->setToolTip(kevaAction->statusTip());
+ kevaAction->setCheckable(true);
+ kevaAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5));
+ tabGroup->addAction(kevaAction);
+
#ifdef ENABLE_WALLET
// These showNormalIfMinimized are needed because Send Coins and Receive Coins
// can be triggered from the tray menu, and need to show the GUI to be useful.
@@ -328,6 +335,8 @@ void BitcoinGUI::createActions()
connect(receiveCoinsMenuAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage()));
connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
+ connect(kevaAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
+ connect(kevaAction, SIGNAL(triggered()), this, SLOT(gotoKevaPage()));
#endif // ENABLE_WALLET
quitAction = new QAction(platformStyle->TextColorIcon(":/icons/quit"), tr("E&xit"), this);
@@ -462,6 +471,7 @@ void BitcoinGUI::createToolBars()
toolbar->addAction(sendCoinsAction);
toolbar->addAction(receiveCoinsAction);
toolbar->addAction(historyAction);
+ toolbar->addAction(kevaAction);
overviewAction->setChecked(true);
}
}
@@ -498,13 +508,13 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
}
#endif // ENABLE_WALLET
unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel());
-
+
OptionsModel* optionsModel = _clientModel->getOptionsModel();
if(optionsModel)
{
// be aware of the tray icon disable state change reported by the OptionsModel object.
connect(optionsModel,SIGNAL(hideTrayIconChanged(bool)),this,SLOT(setTrayIconVisible(bool)));
-
+
// initialize the disable state of the tray icon with the current value in the model.
setTrayIconVisible(optionsModel->getHideTrayIcon());
}
@@ -691,6 +701,12 @@ void BitcoinGUI::gotoHistoryPage()
if (walletFrame) walletFrame->gotoHistoryPage();
}
+void BitcoinGUI::gotoKevaPage()
+{
+ kevaAction->setChecked(true);
+ if (walletFrame) walletFrame->gotoKevaPage();
+}
+
void BitcoinGUI::gotoReceiveCoinsPage()
{
receiveCoinsAction->setChecked(true);
@@ -1052,7 +1068,7 @@ void BitcoinGUI::setHDStatus(int hdEnabled)
labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
labelWalletHDStatusIcon->setToolTip(hdEnabled ? tr("HD key generation is enabled") : tr("HD key generation is disabled"));
- // eventually disable the QLabel to set its opacity to 50%
+ // eventually disable the QLabel to set its opacity to 50%
labelWalletHDStatusIcon->setEnabled(hdEnabled);
}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index ddb7ecb76..feaa27a8a 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -91,6 +91,7 @@ private:
QMenuBar *appMenuBar;
QAction *overviewAction;
+ QAction *kevaAction;
QAction *historyAction;
QAction *quitAction;
QAction *sendCoinsAction;
@@ -195,6 +196,8 @@ private Q_SLOTS:
void gotoOverviewPage();
/** Switch to history (transactions) page */
void gotoHistoryPage();
+ /** Switch to Keva page */
+ void gotoKevaPage();
/** Switch to receive coins page */
void gotoReceiveCoinsPage();
/** Switch to send coins page */
@@ -233,7 +236,7 @@ private Q_SLOTS:
/** Show progress dialog e.g. for verifychain */
void showProgress(const QString &title, int nProgress);
-
+
/** When hideTrayIcon setting is changed in OptionsModel hide or show the icon accordingly. */
void setTrayIconVisible(bool);
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index d6f4e7100..37169f73d 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -9,7 +9,7 @@
#define UNUSED
#endif
static const char UNUSED *bitcoin_strings[] = {
-QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin Core"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Kevacoin Core"),
QT_TRANSLATE_NOOP("bitcoin-core", "The %s developers"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"(1 = keep tx meta data e.g. account owner and payment request information, 2 "
@@ -156,7 +156,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"and enables automatic pruning of old blocks if a target size in MiB is "
"provided. This mode is incompatible with -txindex and -rescan. Warning: "
"Reverting this setting requires re-downloading the entire blockchain. "
-"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u "
+"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u "
"= automatically prune block files to stay under the specified target size in "
"MiB)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -320,6 +320,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Include IP addresses in debug output (default
QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"),
QT_TRANSLATE_NOOP("bitcoin-core", "Information"),
QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. %s is shutting down."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Input tx is not a keva operation"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Input tx is not mine"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Input tx not found in wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address or hostname: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address or hostname: '%s'"),
diff --git a/src/qt/kevaview.cpp b/src/qt/kevaview.cpp
new file mode 100644
index 000000000..d01eeed49
--- /dev/null
+++ b/src/qt/kevaview.cpp
@@ -0,0 +1,623 @@
+// Copyright (c) 2011-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+KevaView::KevaView(const PlatformStyle *platformStyle, QWidget *parent) :
+ QWidget(parent), model(0), transactionProxyModel(0),
+ kevaView(0), abandonAction(0), bumpFeeAction(0), columnResizingFixer(0)
+{
+ // Build filter row
+ setContentsMargins(0,0,0,0);
+
+ QHBoxLayout *hlayout = new QHBoxLayout();
+ hlayout->setContentsMargins(0,0,0,0);
+
+ if (platformStyle->getUseExtraSpacing()) {
+ hlayout->setSpacing(5);
+ hlayout->addSpacing(26);
+ } else {
+ hlayout->setSpacing(0);
+ hlayout->addSpacing(23);
+ }
+
+ watchOnlyWidget = new QComboBox(this);
+ watchOnlyWidget->setFixedWidth(24);
+ watchOnlyWidget->addItem("", TransactionFilterProxy::WatchOnlyFilter_All);
+ watchOnlyWidget->addItem(platformStyle->SingleColorIcon(":/icons/eye_plus"), "", TransactionFilterProxy::WatchOnlyFilter_Yes);
+ watchOnlyWidget->addItem(platformStyle->SingleColorIcon(":/icons/eye_minus"), "", TransactionFilterProxy::WatchOnlyFilter_No);
+ hlayout->addWidget(watchOnlyWidget);
+
+ dateWidget = new QComboBox(this);
+ if (platformStyle->getUseExtraSpacing()) {
+ dateWidget->setFixedWidth(121);
+ } else {
+ dateWidget->setFixedWidth(120);
+ }
+ dateWidget->addItem(tr("All"), All);
+ dateWidget->addItem(tr("Today"), Today);
+ dateWidget->addItem(tr("This week"), ThisWeek);
+ dateWidget->addItem(tr("This month"), ThisMonth);
+ dateWidget->addItem(tr("Last month"), LastMonth);
+ dateWidget->addItem(tr("This year"), ThisYear);
+ dateWidget->addItem(tr("Range..."), Range);
+ hlayout->addWidget(dateWidget);
+
+ typeWidget = new QComboBox(this);
+ if (platformStyle->getUseExtraSpacing()) {
+ typeWidget->setFixedWidth(121);
+ } else {
+ typeWidget->setFixedWidth(120);
+ }
+
+ typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES);
+ typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) |
+ TransactionFilterProxy::TYPE(TransactionRecord::RecvFromOther));
+ typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) |
+ TransactionFilterProxy::TYPE(TransactionRecord::SendToOther));
+ typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf));
+ typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated));
+ typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other));
+
+ hlayout->addWidget(typeWidget);
+
+ search_widget = new QLineEdit(this);
+#if QT_VERSION >= 0x040700
+ search_widget->setPlaceholderText(tr("Enter address, transaction id, or label to search"));
+#endif
+ hlayout->addWidget(search_widget);
+
+ amountWidget = new QLineEdit(this);
+#if QT_VERSION >= 0x040700
+ amountWidget->setPlaceholderText(tr("Min amount"));
+#endif
+ if (platformStyle->getUseExtraSpacing()) {
+ amountWidget->setFixedWidth(97);
+ } else {
+ amountWidget->setFixedWidth(100);
+ }
+ amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this));
+ hlayout->addWidget(amountWidget);
+
+ // Delay before filtering transactions in ms
+ static const int input_filter_delay = 200;
+
+ QTimer* amount_typing_delay = new QTimer(this);
+ amount_typing_delay->setSingleShot(true);
+ amount_typing_delay->setInterval(input_filter_delay);
+
+ QTimer* prefix_typing_delay = new QTimer(this);
+ prefix_typing_delay->setSingleShot(true);
+ prefix_typing_delay->setInterval(input_filter_delay);
+
+ QVBoxLayout *vlayout = new QVBoxLayout(this);
+ vlayout->setContentsMargins(0,0,0,0);
+ vlayout->setSpacing(0);
+
+ QTableView *view = new QTableView(this);
+ vlayout->addLayout(hlayout);
+ vlayout->addWidget(createDateRangeWidget());
+ vlayout->addWidget(view);
+ vlayout->setSpacing(0);
+ int width = view->verticalScrollBar()->sizeHint().width();
+ // Cover scroll bar width with spacing
+ if (platformStyle->getUseExtraSpacing()) {
+ hlayout->addSpacing(width+2);
+ } else {
+ hlayout->addSpacing(width);
+ }
+ // Always show scroll bar
+ view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ view->setTabKeyNavigation(false);
+ view->setContextMenuPolicy(Qt::CustomContextMenu);
+
+ view->installEventFilter(this);
+
+ kevaView = view;
+ kevaView->setObjectName("KevaView");
+
+ // Actions
+ abandonAction = new QAction(tr("Abandon transaction"), this);
+ bumpFeeAction = new QAction(tr("Increase transaction fee"), this);
+ bumpFeeAction->setObjectName("bumpFeeAction");
+ QAction *copyAddressAction = new QAction(tr("Copy address"), this);
+ QAction *copyLabelAction = new QAction(tr("Copy label"), this);
+ QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
+ QAction *copyTxIDAction = new QAction(tr("Copy transaction ID"), this);
+ QAction *copyTxHexAction = new QAction(tr("Copy raw transaction"), this);
+ QAction *copyTxPlainText = new QAction(tr("Copy full transaction details"), this);
+ QAction *editLabelAction = new QAction(tr("Edit label"), this);
+ QAction *showDetailsAction = new QAction(tr("Show transaction details"), this);
+
+ contextMenu = new QMenu(this);
+ contextMenu->setObjectName("contextMenu");
+ contextMenu->addAction(copyAddressAction);
+ contextMenu->addAction(copyLabelAction);
+ contextMenu->addAction(copyAmountAction);
+ contextMenu->addAction(copyTxIDAction);
+ contextMenu->addAction(copyTxHexAction);
+ contextMenu->addAction(copyTxPlainText);
+ contextMenu->addAction(showDetailsAction);
+ contextMenu->addSeparator();
+ contextMenu->addAction(bumpFeeAction);
+ contextMenu->addAction(abandonAction);
+ contextMenu->addAction(editLabelAction);
+
+ mapperThirdPartyTxUrls = new QSignalMapper(this);
+
+ // Connect actions
+ connect(mapperThirdPartyTxUrls, SIGNAL(mapped(QString)), this, SLOT(openThirdPartyTxUrl(QString)));
+
+ 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(amountWidget, SIGNAL(textChanged(QString)), amount_typing_delay, SLOT(start()));
+ connect(amount_typing_delay, SIGNAL(timeout()), this, SLOT(changedAmount()));
+ connect(search_widget, SIGNAL(textChanged(QString)), prefix_typing_delay, SLOT(start()));
+ connect(prefix_typing_delay, SIGNAL(timeout()), this, SLOT(changedSearch()));
+
+ connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex)));
+ connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
+
+ connect(bumpFeeAction, SIGNAL(triggered()), this, SLOT(bumpFee()));
+ connect(abandonAction, SIGNAL(triggered()), this, SLOT(abandonTx()));
+ connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
+ connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
+ connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
+ connect(copyTxIDAction, SIGNAL(triggered()), this, SLOT(copyTxID()));
+ connect(copyTxHexAction, SIGNAL(triggered()), this, SLOT(copyTxHex()));
+ connect(copyTxPlainText, SIGNAL(triggered()), this, SLOT(copyTxPlainText()));
+ connect(editLabelAction, SIGNAL(triggered()), this, SLOT(editLabel()));
+ connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails()));
+}
+
+void KevaView::setModel(WalletModel *_model)
+{
+ this->model = _model;
+ if(_model)
+ {
+ transactionProxyModel = new TransactionFilterProxy(this);
+ transactionProxyModel->setSourceModel(_model->getTransactionTableModel());
+ transactionProxyModel->setDynamicSortFilter(true);
+ transactionProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
+ transactionProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+
+ transactionProxyModel->setSortRole(Qt::EditRole);
+
+ kevaView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ kevaView->setModel(transactionProxyModel);
+ kevaView->setAlternatingRowColors(true);
+ kevaView->setSelectionBehavior(QAbstractItemView::SelectRows);
+ kevaView->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ kevaView->setSortingEnabled(true);
+ kevaView->sortByColumn(TransactionTableModel::Date, Qt::DescendingOrder);
+ kevaView->verticalHeader()->hide();
+
+ kevaView->setColumnWidth(TransactionTableModel::Status, STATUS_COLUMN_WIDTH);
+ kevaView->setColumnWidth(TransactionTableModel::Watchonly, WATCHONLY_COLUMN_WIDTH);
+ kevaView->setColumnWidth(TransactionTableModel::Date, DATE_COLUMN_WIDTH);
+ kevaView->setColumnWidth(TransactionTableModel::Type, TYPE_COLUMN_WIDTH);
+ kevaView->setColumnWidth(TransactionTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH);
+
+ columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(kevaView, AMOUNT_MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH, this);
+
+ if (_model->getOptionsModel())
+ {
+ // Add third party transaction URLs to context menu
+ QStringList listUrls = _model->getOptionsModel()->getThirdPartyTxUrls().split("|", QString::SkipEmptyParts);
+ for (int i = 0; i < listUrls.size(); ++i)
+ {
+ QString host = QUrl(listUrls[i].trimmed(), QUrl::StrictMode).host();
+ if (!host.isEmpty())
+ {
+ QAction *thirdPartyTxUrlAction = new QAction(host, this); // use host as menu item label
+ if (i == 0)
+ contextMenu->addSeparator();
+ contextMenu->addAction(thirdPartyTxUrlAction);
+ connect(thirdPartyTxUrlAction, SIGNAL(triggered()), mapperThirdPartyTxUrls, SLOT(map()));
+ mapperThirdPartyTxUrls->setMapping(thirdPartyTxUrlAction, listUrls[i].trimmed());
+ }
+ }
+ }
+
+ // show/hide column Watch-only
+ updateWatchOnlyColumn(_model->haveWatchOnly());
+
+ // Watch-only signal
+ connect(_model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyColumn(bool)));
+ }
+}
+
+void KevaView::chooseDate(int idx)
+{
+ if(!transactionProxyModel)
+ return;
+ QDate current = QDate::currentDate();
+ dateRangeWidget->setVisible(false);
+ switch(dateWidget->itemData(idx).toInt())
+ {
+ case All:
+ transactionProxyModel->setDateRange(
+ TransactionFilterProxy::MIN_DATE,
+ TransactionFilterProxy::MAX_DATE);
+ break;
+ case Today:
+ transactionProxyModel->setDateRange(
+ QDateTime(current),
+ TransactionFilterProxy::MAX_DATE);
+ break;
+ case ThisWeek: {
+ // Find last Monday
+ QDate startOfWeek = current.addDays(-(current.dayOfWeek()-1));
+ transactionProxyModel->setDateRange(
+ QDateTime(startOfWeek),
+ TransactionFilterProxy::MAX_DATE);
+
+ } break;
+ case ThisMonth:
+ transactionProxyModel->setDateRange(
+ QDateTime(QDate(current.year(), current.month(), 1)),
+ TransactionFilterProxy::MAX_DATE);
+ break;
+ case LastMonth:
+ transactionProxyModel->setDateRange(
+ QDateTime(QDate(current.year(), current.month(), 1).addMonths(-1)),
+ QDateTime(QDate(current.year(), current.month(), 1)));
+ break;
+ case ThisYear:
+ transactionProxyModel->setDateRange(
+ QDateTime(QDate(current.year(), 1, 1)),
+ TransactionFilterProxy::MAX_DATE);
+ break;
+ case Range:
+ dateRangeWidget->setVisible(true);
+ dateRangeChanged();
+ break;
+ }
+}
+
+void KevaView::chooseType(int idx)
+{
+ if(!transactionProxyModel)
+ return;
+ transactionProxyModel->setTypeFilter(
+ typeWidget->itemData(idx).toInt());
+}
+
+void KevaView::chooseWatchonly(int idx)
+{
+ if(!transactionProxyModel)
+ return;
+ transactionProxyModel->setWatchOnlyFilter(
+ (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt());
+}
+
+void KevaView::changedSearch()
+{
+ if(!transactionProxyModel)
+ return;
+ transactionProxyModel->setSearchString(search_widget->text());
+}
+
+void KevaView::changedAmount()
+{
+ if(!transactionProxyModel)
+ return;
+ CAmount amount_parsed = 0;
+ if (BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amountWidget->text(), &amount_parsed)) {
+ transactionProxyModel->setMinAmount(amount_parsed);
+ }
+ else
+ {
+ transactionProxyModel->setMinAmount(0);
+ }
+}
+
+void KevaView::exportClicked()
+{
+ if (!model || !model->getOptionsModel()) {
+ return;
+ }
+
+ // CSV is currently the only supported format
+ QString filename = GUIUtil::getSaveFileName(this,
+ tr("Export Transaction History"), QString(),
+ tr("Comma separated file (*.csv)"), nullptr);
+
+ if (filename.isNull())
+ return;
+
+ CSVModelWriter writer(filename);
+
+ // name, column, role
+ writer.setModel(transactionProxyModel);
+ writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole);
+ if (model->haveWatchOnly())
+ writer.addColumn(tr("Watch-only"), TransactionTableModel::Watchonly);
+ writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole);
+ writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole);
+ writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole);
+ writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole);
+ writer.addColumn(BitcoinUnits::getAmountColumnTitle(model->getOptionsModel()->getDisplayUnit()), 0, TransactionTableModel::FormattedAmountRole);
+ writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole);
+
+ if(!writer.write()) {
+ Q_EMIT message(tr("Exporting Failed"), tr("There was an error trying to save the transaction history to %1.").arg(filename),
+ CClientUIInterface::MSG_ERROR);
+ }
+ else {
+ Q_EMIT message(tr("Exporting Successful"), tr("The transaction history was successfully saved to %1.").arg(filename),
+ CClientUIInterface::MSG_INFORMATION);
+ }
+}
+
+void KevaView::contextualMenu(const QPoint &point)
+{
+ QModelIndex index = kevaView->indexAt(point);
+ QModelIndexList selection = kevaView->selectionModel()->selectedRows(0);
+ if (selection.empty())
+ return;
+
+ // check if transaction can be abandoned, disable context menu action in case it doesn't
+ uint256 hash;
+ hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString());
+ abandonAction->setEnabled(model->transactionCanBeAbandoned(hash));
+ bumpFeeAction->setEnabled(model->transactionCanBeBumped(hash));
+
+ if(index.isValid())
+ {
+ contextMenu->popup(kevaView->viewport()->mapToGlobal(point));
+ }
+}
+
+void KevaView::abandonTx()
+{
+ if(!kevaView || !kevaView->selectionModel())
+ return;
+ QModelIndexList selection = kevaView->selectionModel()->selectedRows(0);
+
+ // get the hash from the TxHashRole (QVariant / QString)
+ uint256 hash;
+ QString hashQStr = selection.at(0).data(TransactionTableModel::TxHashRole).toString();
+ hash.SetHex(hashQStr.toStdString());
+
+ // Abandon the wallet transaction over the walletModel
+ model->abandonTransaction(hash);
+
+ // Update the table
+ model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false);
+}
+
+void KevaView::bumpFee()
+{
+ if(!kevaView || !kevaView->selectionModel())
+ return;
+ QModelIndexList selection = kevaView->selectionModel()->selectedRows(0);
+
+ // get the hash from the TxHashRole (QVariant / QString)
+ uint256 hash;
+ QString hashQStr = selection.at(0).data(TransactionTableModel::TxHashRole).toString();
+ hash.SetHex(hashQStr.toStdString());
+
+ // Bump tx fee over the walletModel
+ if (model->bumpFee(hash)) {
+ // Update the table
+ model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, true);
+ }
+}
+
+void KevaView::copyAddress()
+{
+ GUIUtil::copyEntryData(kevaView, 0, TransactionTableModel::AddressRole);
+}
+
+void KevaView::copyLabel()
+{
+ GUIUtil::copyEntryData(kevaView, 0, TransactionTableModel::LabelRole);
+}
+
+void KevaView::copyAmount()
+{
+ GUIUtil::copyEntryData(kevaView, 0, TransactionTableModel::FormattedAmountRole);
+}
+
+void KevaView::copyTxID()
+{
+ GUIUtil::copyEntryData(kevaView, 0, TransactionTableModel::TxIDRole);
+}
+
+void KevaView::copyTxHex()
+{
+ GUIUtil::copyEntryData(kevaView, 0, TransactionTableModel::TxHexRole);
+}
+
+void KevaView::copyTxPlainText()
+{
+ GUIUtil::copyEntryData(kevaView, 0, TransactionTableModel::TxPlainTextRole);
+}
+
+void KevaView::editLabel()
+{
+ if(!kevaView->selectionModel() ||!model)
+ return;
+ QModelIndexList selection = kevaView->selectionModel()->selectedRows();
+ if(!selection.isEmpty())
+ {
+ AddressTableModel *addressBook = model->getAddressTableModel();
+ if(!addressBook)
+ return;
+ QString address = selection.at(0).data(TransactionTableModel::AddressRole).toString();
+ if(address.isEmpty())
+ {
+ // If this transaction has no associated address, exit
+ return;
+ }
+ // Is address in address book? Address book can miss address when a transaction is
+ // sent from outside the UI.
+ 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.setModel(addressBook);
+ dlg.setAddress(address);
+ dlg.exec();
+ }
+ }
+}
+
+void KevaView::showDetails()
+{
+ if(!kevaView->selectionModel())
+ return;
+ QModelIndexList selection = kevaView->selectionModel()->selectedRows();
+ if(!selection.isEmpty())
+ {
+ TransactionDescDialog *dlg = new TransactionDescDialog(selection.at(0));
+ dlg->setAttribute(Qt::WA_DeleteOnClose);
+ dlg->show();
+ }
+}
+
+void KevaView::openThirdPartyTxUrl(QString url)
+{
+ if(!kevaView || !kevaView->selectionModel())
+ return;
+ QModelIndexList selection = kevaView->selectionModel()->selectedRows(0);
+ if(!selection.isEmpty())
+ QDesktopServices::openUrl(QUrl::fromUserInput(url.replace("%s", selection.at(0).data(TransactionTableModel::TxHashRole).toString())));
+}
+
+QWidget *KevaView::createDateRangeWidget()
+{
+ dateRangeWidget = new QFrame();
+ dateRangeWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
+ dateRangeWidget->setContentsMargins(1,1,1,1);
+ QHBoxLayout *layout = new QHBoxLayout(dateRangeWidget);
+ layout->setContentsMargins(0,0,0,0);
+ layout->addSpacing(23);
+ layout->addWidget(new QLabel(tr("Range:")));
+
+ dateFrom = new QDateTimeEdit(this);
+ dateFrom->setDisplayFormat("dd/MM/yy");
+ dateFrom->setCalendarPopup(true);
+ dateFrom->setMinimumWidth(100);
+ dateFrom->setDate(QDate::currentDate().addDays(-7));
+ layout->addWidget(dateFrom);
+ layout->addWidget(new QLabel(tr("to")));
+
+ dateTo = new QDateTimeEdit(this);
+ dateTo->setDisplayFormat("dd/MM/yy");
+ dateTo->setCalendarPopup(true);
+ dateTo->setMinimumWidth(100);
+ dateTo->setDate(QDate::currentDate());
+ layout->addWidget(dateTo);
+ layout->addStretch();
+
+ // Hide by default
+ dateRangeWidget->setVisible(false);
+
+ // Notify on change
+ connect(dateFrom, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged()));
+ connect(dateTo, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged()));
+
+ return dateRangeWidget;
+}
+
+void KevaView::dateRangeChanged()
+{
+ if(!transactionProxyModel)
+ return;
+ transactionProxyModel->setDateRange(
+ QDateTime(dateFrom->date()),
+ QDateTime(dateTo->date()).addDays(1));
+}
+
+void KevaView::focusTransaction(const QModelIndex &idx)
+{
+ if(!transactionProxyModel)
+ return;
+ QModelIndex targetIdx = transactionProxyModel->mapFromSource(idx);
+ kevaView->scrollTo(targetIdx);
+ kevaView->setCurrentIndex(targetIdx);
+ kevaView->setFocus();
+}
+
+// We override the virtual resizeEvent of the QWidget to adjust tables column
+// sizes as the tables width is proportional to the dialogs width.
+void KevaView::resizeEvent(QResizeEvent* event)
+{
+ QWidget::resizeEvent(event);
+ columnResizingFixer->stretchColumnWidth(TransactionTableModel::ToAddress);
+}
+
+// Need to override default Ctrl+C action for amount as default behaviour is just to copy DisplayRole text
+bool KevaView::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::KeyPress)
+ {
+ QKeyEvent *ke = static_cast(event);
+ if (ke->key() == Qt::Key_C && ke->modifiers().testFlag(Qt::ControlModifier))
+ {
+ GUIUtil::copyEntryData(kevaView, 0, TransactionTableModel::TxPlainTextRole);
+ return true;
+ }
+ }
+ return QWidget::eventFilter(obj, event);
+}
+
+// show/hide column Watch-only
+void KevaView::updateWatchOnlyColumn(bool fHaveWatchOnly)
+{
+ watchOnlyWidget->setVisible(fHaveWatchOnly);
+ kevaView->setColumnHidden(TransactionTableModel::Watchonly, !fHaveWatchOnly);
+}
diff --git a/src/qt/kevaview.h b/src/qt/kevaview.h
new file mode 100644
index 000000000..dfffeec2a
--- /dev/null
+++ b/src/qt/kevaview.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2011-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_KevaView_H
+#define BITCOIN_QT_KevaView_H
+
+#include
+
+#include
+#include
+
+class PlatformStyle;
+class TransactionFilterProxy;
+class WalletModel;
+
+QT_BEGIN_NAMESPACE
+class QComboBox;
+class QDateTimeEdit;
+class QFrame;
+class QLineEdit;
+class QMenu;
+class QModelIndex;
+class QSignalMapper;
+class QTableView;
+QT_END_NAMESPACE
+
+/** Widget showing the transaction list for a wallet, including a filter row.
+ Using the filter row, the user can view or export a subset of the transactions.
+ */
+class KevaView : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit KevaView(const PlatformStyle *platformStyle, QWidget *parent = 0);
+
+ void setModel(WalletModel *model);
+
+ // Date ranges for filter
+ enum DateEnum
+ {
+ All,
+ Today,
+ ThisWeek,
+ ThisMonth,
+ LastMonth,
+ ThisYear,
+ Range
+ };
+
+ enum ColumnWidths {
+ STATUS_COLUMN_WIDTH = 30,
+ WATCHONLY_COLUMN_WIDTH = 23,
+ DATE_COLUMN_WIDTH = 120,
+ TYPE_COLUMN_WIDTH = 113,
+ AMOUNT_MINIMUM_COLUMN_WIDTH = 120,
+ MINIMUM_COLUMN_WIDTH = 23
+ };
+
+private:
+ WalletModel *model;
+ TransactionFilterProxy *transactionProxyModel;
+ QTableView *kevaView;
+
+ QComboBox *dateWidget;
+ QComboBox *typeWidget;
+ QComboBox *watchOnlyWidget;
+ QLineEdit *search_widget;
+ QLineEdit *amountWidget;
+
+ QMenu *contextMenu;
+ QSignalMapper *mapperThirdPartyTxUrls;
+
+ QFrame *dateRangeWidget;
+ QDateTimeEdit *dateFrom;
+ QDateTimeEdit *dateTo;
+ QAction *abandonAction;
+ QAction *bumpFeeAction;
+
+ QWidget *createDateRangeWidget();
+
+ GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer;
+
+ virtual void resizeEvent(QResizeEvent* event);
+
+ bool eventFilter(QObject *obj, QEvent *event);
+
+private Q_SLOTS:
+ void contextualMenu(const QPoint &);
+ void dateRangeChanged();
+ void showDetails();
+ void copyAddress();
+ void editLabel();
+ void copyLabel();
+ void copyAmount();
+ void copyTxID();
+ void copyTxHex();
+ void copyTxPlainText();
+ void openThirdPartyTxUrl(QString url);
+ void updateWatchOnlyColumn(bool fHaveWatchOnly);
+ void abandonTx();
+ void bumpFee();
+
+Q_SIGNALS:
+ void doubleClicked(const QModelIndex&);
+
+ /** Fired when a message should be reported to the user */
+ void message(const QString &title, const QString &message, unsigned int style);
+
+public Q_SLOTS:
+ void chooseDate(int idx);
+ void chooseType(int idx);
+ void chooseWatchonly(int idx);
+ void changedAmount();
+ void changedSearch();
+ void exportClicked();
+ void focusTransaction(const QModelIndex&);
+
+};
+
+#endif // BITCOIN_QT_KevaView_H
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index 647e8f566..86ae7aa2c 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -85,7 +85,7 @@
-
+
@@ -304,17 +304,17 @@
BitcoinGUI
-
+
Sign &message...
-
+
Synchronizing with network...
-
+
&Overview
@@ -338,6 +338,16 @@
Browse transaction history
+
+
+
+
+
+
+
+
+
+
@@ -409,7 +419,7 @@
-
+
@@ -434,12 +444,12 @@
Reindexing blocks on disk...
-
+
Send coins to a Kevacoin address
-
+
Backup wallet to another location
@@ -464,12 +474,12 @@
&Verify message...
-
+
Kevacoin
-
+
Wallet
@@ -484,7 +494,7 @@
&Receive
-
+
&Show / Hide
@@ -529,12 +539,12 @@
Tabs toolbar
-
+
-
+
@@ -554,7 +564,7 @@
-
+
%n active connection to Kevacoin network
@@ -615,12 +625,12 @@
Up to date
-
+
-
+
@@ -635,7 +645,7 @@
Catching up...
-
+
@@ -1671,7 +1681,7 @@
PaymentServer
-
+
@@ -1950,7 +1960,7 @@
-
+
@@ -2623,7 +2633,7 @@
SendCoinsDialog
-
+
Send Coins
@@ -2754,7 +2764,7 @@
-
+
Send to multiple recipients at once
@@ -2769,7 +2779,7 @@
-
+
@@ -2779,17 +2789,7 @@
-
-
-
-
-
-
-
-
-
-
-
+
Clear &All
@@ -2809,7 +2809,7 @@
S&end
-
+
@@ -2849,7 +2849,7 @@
-
+
@@ -2877,22 +2877,12 @@
-
-
-
-
-
-
-
-
-
-
-
+
-
+
@@ -2942,7 +2932,7 @@
-
+
Estimated to begin confirmation within %n block.
@@ -3329,11 +3319,6 @@
-
-
-
-
-
@@ -3370,20 +3355,7 @@
-
-
-
-
-
-
-
-
- , broadcast through %n node
- , broadcast through %n nodes
-
-
-
-
+
Date
@@ -3593,11 +3565,6 @@
-
-
-
-
-
@@ -3628,11 +3595,6 @@
-
-
-
-
-
@@ -3674,7 +3636,7 @@
-
+
@@ -3927,7 +3889,7 @@
WalletModel
-
+
Send Coins
@@ -4025,7 +3987,7 @@
bitcoin-core
-
+
Options:
@@ -4035,17 +3997,17 @@
Specify data directory
-
+
Connect to a node to retrieve peer addresses, and disconnect
-
+
Specify your own public address
-
+
Accept command line and JSON-RPC commands
@@ -4085,7 +4047,7 @@
-
+
@@ -4100,7 +4062,7 @@
-
+
Kevacoin Core
@@ -4200,12 +4162,7 @@
-
-
-
-
-
-
+
@@ -4449,6 +4406,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4625,7 +4597,7 @@
-
+
@@ -4740,7 +4712,7 @@
Information
-
+
@@ -4925,7 +4897,7 @@
Password for JSON-RPC connections
-
+
Execute command when the best block changes (%s in cmd is replaced by block hash)
@@ -5000,7 +4972,12 @@
-
+
+
+
+
+
+
@@ -5115,7 +5092,7 @@
-
+
@@ -5260,17 +5237,17 @@
Loading wallet...
-
+
Cannot downgrade wallet
-
+
Rescanning...
-
+
Done loading
@@ -5280,4 +5257,4 @@
Error
-
\ No newline at end of file
+
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 7a4ffea03..ae3ac8f71 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index c0b9d0426..ad84801fb 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -124,6 +124,13 @@ void WalletFrame::gotoHistoryPage()
i.value()->gotoHistoryPage();
}
+void WalletFrame::gotoKevaPage()
+{
+ QMap::const_iterator i;
+ for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
+ i.value()->gotoKevaPage();
+}
+
void WalletFrame::gotoReceiveCoinsPage()
{
QMap::const_iterator i;
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index 42ce69fea..fbc648b2f 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -66,6 +66,8 @@ public Q_SLOTS:
void gotoOverviewPage();
/** Switch to history (transactions) page */
void gotoHistoryPage();
+ /** Switch to Keva page */
+ void gotoKevaPage();
/** Switch to receive coins page */
void gotoReceiveCoinsPage();
/** Switch to send coins page */
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 7eced9289..cc30ba29d 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#include
#include
@@ -53,6 +54,25 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
vbox->addLayout(hbox_buttons);
transactionsPage->setLayout(vbox);
+#if 1
+{
+ kevaPage = new QWidget(this);
+ QVBoxLayout *vbox = new QVBoxLayout();
+ QHBoxLayout *hbox_buttons = new QHBoxLayout();
+ kevaView = new KevaView(platformStyle, this);
+ vbox->addWidget(kevaView);
+ QPushButton *exportButton = new QPushButton(tr("&Export"), this);
+ exportButton->setToolTip(tr("Export the data in the current tab to a file"));
+ if (platformStyle->getImagesOnButtons()) {
+ exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
+ }
+ hbox_buttons->addStretch();
+ hbox_buttons->addWidget(exportButton);
+ vbox->addLayout(hbox_buttons);
+ kevaPage->setLayout(vbox);
+}
+#endif
+
receiveCoinsPage = new ReceiveCoinsDialog(platformStyle);
sendCoinsPage = new SendCoinsDialog(platformStyle);
@@ -61,6 +81,7 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
addWidget(overviewPage);
addWidget(transactionsPage);
+ addWidget(kevaPage);
addWidget(receiveCoinsPage);
addWidget(sendCoinsPage);
@@ -100,7 +121,7 @@ void WalletView::setBitcoinGUI(BitcoinGUI *gui)
// Pass through transaction notifications
connect(this, SIGNAL(incomingTransaction(QString,int,CAmount,QString,QString,QString)), gui, SLOT(incomingTransaction(QString,int,CAmount,QString,QString,QString)));
- // Connect HD enabled state signal
+ // Connect HD enabled state signal
connect(this, SIGNAL(hdEnabledStatusChanged(int)), gui, SLOT(setHDStatus(int)));
}
}
@@ -119,6 +140,7 @@ void WalletView::setWalletModel(WalletModel *_walletModel)
// Put transaction list in tabs
transactionView->setModel(_walletModel);
+ kevaView->setModel(_walletModel);
overviewPage->setWalletModel(_walletModel);
receiveCoinsPage->setModel(_walletModel);
sendCoinsPage->setModel(_walletModel);
@@ -179,6 +201,11 @@ void WalletView::gotoHistoryPage()
setCurrentWidget(transactionsPage);
}
+void WalletView::gotoKevaPage()
+{
+ setCurrentWidget(kevaPage);
+}
+
void WalletView::gotoReceiveCoinsPage()
{
setCurrentWidget(receiveCoinsPage);
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index 30d68e4ef..784dbefd6 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -17,6 +17,7 @@ class ReceiveCoinsDialog;
class SendCoinsDialog;
class SendCoinsRecipient;
class TransactionView;
+class KevaView;
class WalletModel;
class AddressBookPage;
@@ -60,6 +61,7 @@ private:
OverviewPage *overviewPage;
QWidget *transactionsPage;
+ QWidget *kevaPage;
ReceiveCoinsDialog *receiveCoinsPage;
SendCoinsDialog *sendCoinsPage;
AddressBookPage *usedSendingAddressesPage;
@@ -67,6 +69,8 @@ private:
TransactionView *transactionView;
+ KevaView *kevaView;
+
QProgressDialog *progressDialog;
const PlatformStyle *platformStyle;
@@ -75,6 +79,8 @@ public Q_SLOTS:
void gotoOverviewPage();
/** Switch to history (transactions) page */
void gotoHistoryPage();
+ /** Switch to Keva page */
+ void gotoKevaPage();
/** Switch to receive coins page */
void gotoReceiveCoinsPage();
/** Switch to send coins page */