From 6e10afb2fcf747e14029c3379f1012b06e927aa8 Mon Sep 17 00:00:00 2001 From: Yunqi Ouyang Date: Wed, 30 Oct 2019 11:30:46 +0800 Subject: [PATCH 1/3] Update build config for out-of-source build --- src/Makefile.test.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 586ed3a112..1295742d04 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -163,7 +163,7 @@ BITCOIN_TEST_SUITE += \ endif test_test_qtum_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) -test_test_qtum_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS) +test_test_qtum_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(srcdir)/test/ $(TESTDEFS) $(EVENT_CFLAGS) test_test_qtum_LDADD = if ENABLE_WALLET test_test_qtum_LDADD += $(LIBBITCOIN_WALLET) From 6aad1a189e8af82693d11f2aa1ecd0e189961907 Mon Sep 17 00:00:00 2001 From: Yunqi Ouyang Date: Wed, 30 Oct 2019 14:46:10 +0800 Subject: [PATCH 2/3] Convert CRLF to LF --- src/qt/abifunctionfield.cpp | 382 +++---- src/qt/abiparam.cpp | 324 +++--- src/qt/abiparamitem.cpp | 196 ++-- src/qt/abiparamsfield.cpp | 114 +- src/qt/addtokenpage.cpp | 326 +++--- src/qt/contractabi.cpp | 1622 ++++++++++++++--------------- src/qt/contractbookpage.cpp | 536 +++++----- src/qt/contractresult.cpp | 506 ++++----- src/qt/createcontract.cpp | 610 +++++------ src/qt/editcontractinfodialog.cpp | 382 +++---- src/qt/eventlog.cpp | 214 ++-- src/qt/execrpccommand.cpp | 220 ++-- src/qt/navigationbar.cpp | 604 +++++------ src/qt/qrctoken.cpp | 650 ++++++------ src/qt/qtumversionchecker.cpp | 130 +-- src/qt/qvalidatedtextedit.cpp | 270 ++--- src/qt/receivetokenpage.cpp | 138 +-- src/qt/sendtokenpage.cpp | 522 +++++----- src/qt/styleSheet.cpp | 278 ++--- src/qt/tabbarinfo.cpp | 448 ++++---- src/qt/tokenamountfield.cpp | 548 +++++----- src/qt/tokendescdialog.cpp | 52 +- src/qt/tokenfilterproxy.cpp | 234 ++--- src/qt/tokenitemmodel.cpp | 1056 +++++++++---------- src/qt/tokentransactionview.cpp | 848 +++++++-------- 25 files changed, 5605 insertions(+), 5605 deletions(-) diff --git a/src/qt/abifunctionfield.cpp b/src/qt/abifunctionfield.cpp index 0f7625f8d6..457b2f3783 100644 --- a/src/qt/abifunctionfield.cpp +++ b/src/qt/abifunctionfield.cpp @@ -1,191 +1,191 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -ABIFunctionField::ABIFunctionField(const PlatformStyle *platformStyle, FunctionType type, QWidget *parent) : - QWidget(parent), - m_contractABI(0), - m_func(new QWidget(this)), - m_comboBoxFunc(new QComboBox(this)), - m_paramsField(new QStackedWidget(this)), - m_functionType(type) -{ - m_platformStyle = platformStyle; - // Setup layouts - m_comboBoxFunc->setFixedWidth(370); - QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setSpacing(10); - mainLayout->setContentsMargins(0, 0, 0, 0); - - QHBoxLayout *topLayout = new QHBoxLayout(m_func); - topLayout->setSpacing(10); - topLayout->setContentsMargins(0, 0, 0, 0); - - m_labelFunction = new QLabel(tr("Function")); - m_labelFunction->setFixedWidth(160); - topLayout->addWidget(m_labelFunction); - - topLayout->addWidget(m_comboBoxFunc); - topLayout->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Fixed)); - - m_func->setLayout(topLayout); - mainLayout->addWidget(m_func); - mainLayout->addWidget(m_paramsField); - mainLayout->addSpacerItem(new QSpacerItem(20, 40, QSizePolicy::Fixed, QSizePolicy::Expanding)); - connect(m_comboBoxFunc, SIGNAL(currentIndexChanged(int)), m_paramsField, SLOT(setCurrentIndex(int))); - connect(m_paramsField, SIGNAL(currentChanged(int)), this, SLOT(on_currentIndexChanged())); - - m_func->setVisible(false); -} - -void ABIFunctionField::updateABIFunctionField() -{ - // Clear the content - clear(); - - if(m_contractABI != NULL) - { - // Populate the control with functions - std::vector functions = m_contractABI->functions; - QStringList functionList; - QStringListModel *functionModel = new QStringListModel(this); - bool bFieldCreate = m_functionType == Create; - bool bFieldCall = m_functionType == Call; - bool bFieldSendTo = m_functionType == SendTo; - bool bFieldFunc = bFieldCall || bFieldSendTo; - for (int func = 0; func < (int)functions.size(); ++func) - { - const FunctionABI &function = functions[func]; - bool bTypeConstructor = function.type == "constructor"; - bool bTypeEvent = function.type == "event"; - bool bTypeDefault = function.type == "default"; - bool bIsConstant = function.constant; - if((bFieldCreate && !bTypeConstructor) || - (bFieldFunc && bTypeConstructor) || - (bFieldFunc && bTypeEvent) || - (bFieldCall && !bIsConstant && !bTypeDefault) || - (bFieldSendTo && bIsConstant && !bTypeDefault)) - { - continue; - } - - ABIParamsField *abiParamsField = new ABIParamsField(m_platformStyle, this); - abiParamsField->updateParamsField(function); - - m_paramsField->addWidget(abiParamsField); - QString funcName = QString::fromStdString(function.name); - QString funcSelector = QString::fromStdString(function.selector()); - functionList.append(QString(funcName + "(" + funcSelector + ")")); - - m_abiFunctionList.append(func); - } - functionModel->setStringList(functionList); - m_comboBoxFunc->setModel(functionModel); - - if(bFieldFunc) - { - bool visible = m_abiFunctionList.size() > 0; - m_func->setVisible(visible); - } - on_currentIndexChanged(); - } -} - -void ABIFunctionField::clear() -{ - m_comboBoxFunc->clear(); - m_abiFunctionList.clear(); - for(int i = m_paramsField->count() - 1; i >= 0; i--) - { - QWidget* widget = m_paramsField->widget(i); - m_paramsField->removeWidget(widget); - widget->deleteLater(); - } -} - -void ABIFunctionField::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -void ABIFunctionField::setContractABI(ContractABI *contractABI) -{ - m_contractABI = contractABI; - updateABIFunctionField(); -} - -QStringList ABIFunctionField::getParamValue(int paramID) -{ - if(m_paramsField->currentWidget() == 0) - return QStringList(); - - return ((ABIParamsField*)m_paramsField->currentWidget())->getParamValue(paramID); -} - -QList ABIFunctionField::getParamsValues() -{ - if(m_paramsField->currentWidget() == 0) - return QList(); - - return ((ABIParamsField*)m_paramsField->currentWidget())->getParamsValues(); -} - -std::vector> ABIFunctionField::getValuesVector() -{ - QList qlist = getParamsValues(); - std::vector> result; - for (int i=0; i itemParam; - QStringList qlistVlaues = qlist[i]; - for(int j=0; jcurrentIndex(); - if(currentFunc == -1) - return -1; - - return m_abiFunctionList[currentFunc]; -} - -bool ABIFunctionField::isValid() -{ - if(m_paramsField->currentWidget() == 0) - return true; - - return ((ABIParamsField*)m_paramsField->currentWidget())->isValid(); -} - -void ABIFunctionField::on_currentIndexChanged() -{ - for (int i = 0; i < m_paramsField->count (); ++i) - { - QSizePolicy::Policy policy = QSizePolicy::Ignored; - if (i == m_paramsField->currentIndex()) - policy = QSizePolicy::Expanding; - - QWidget* pPage = m_paramsField->widget(i); - pPage->setSizePolicy(policy, policy); - } - Q_EMIT(functionChanged()); -} - +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +ABIFunctionField::ABIFunctionField(const PlatformStyle *platformStyle, FunctionType type, QWidget *parent) : + QWidget(parent), + m_contractABI(0), + m_func(new QWidget(this)), + m_comboBoxFunc(new QComboBox(this)), + m_paramsField(new QStackedWidget(this)), + m_functionType(type) +{ + m_platformStyle = platformStyle; + // Setup layouts + m_comboBoxFunc->setFixedWidth(370); + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->setSpacing(10); + mainLayout->setContentsMargins(0, 0, 0, 0); + + QHBoxLayout *topLayout = new QHBoxLayout(m_func); + topLayout->setSpacing(10); + topLayout->setContentsMargins(0, 0, 0, 0); + + m_labelFunction = new QLabel(tr("Function")); + m_labelFunction->setFixedWidth(160); + topLayout->addWidget(m_labelFunction); + + topLayout->addWidget(m_comboBoxFunc); + topLayout->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Fixed)); + + m_func->setLayout(topLayout); + mainLayout->addWidget(m_func); + mainLayout->addWidget(m_paramsField); + mainLayout->addSpacerItem(new QSpacerItem(20, 40, QSizePolicy::Fixed, QSizePolicy::Expanding)); + connect(m_comboBoxFunc, SIGNAL(currentIndexChanged(int)), m_paramsField, SLOT(setCurrentIndex(int))); + connect(m_paramsField, SIGNAL(currentChanged(int)), this, SLOT(on_currentIndexChanged())); + + m_func->setVisible(false); +} + +void ABIFunctionField::updateABIFunctionField() +{ + // Clear the content + clear(); + + if(m_contractABI != NULL) + { + // Populate the control with functions + std::vector functions = m_contractABI->functions; + QStringList functionList; + QStringListModel *functionModel = new QStringListModel(this); + bool bFieldCreate = m_functionType == Create; + bool bFieldCall = m_functionType == Call; + bool bFieldSendTo = m_functionType == SendTo; + bool bFieldFunc = bFieldCall || bFieldSendTo; + for (int func = 0; func < (int)functions.size(); ++func) + { + const FunctionABI &function = functions[func]; + bool bTypeConstructor = function.type == "constructor"; + bool bTypeEvent = function.type == "event"; + bool bTypeDefault = function.type == "default"; + bool bIsConstant = function.constant; + if((bFieldCreate && !bTypeConstructor) || + (bFieldFunc && bTypeConstructor) || + (bFieldFunc && bTypeEvent) || + (bFieldCall && !bIsConstant && !bTypeDefault) || + (bFieldSendTo && bIsConstant && !bTypeDefault)) + { + continue; + } + + ABIParamsField *abiParamsField = new ABIParamsField(m_platformStyle, this); + abiParamsField->updateParamsField(function); + + m_paramsField->addWidget(abiParamsField); + QString funcName = QString::fromStdString(function.name); + QString funcSelector = QString::fromStdString(function.selector()); + functionList.append(QString(funcName + "(" + funcSelector + ")")); + + m_abiFunctionList.append(func); + } + functionModel->setStringList(functionList); + m_comboBoxFunc->setModel(functionModel); + + if(bFieldFunc) + { + bool visible = m_abiFunctionList.size() > 0; + m_func->setVisible(visible); + } + on_currentIndexChanged(); + } +} + +void ABIFunctionField::clear() +{ + m_comboBoxFunc->clear(); + m_abiFunctionList.clear(); + for(int i = m_paramsField->count() - 1; i >= 0; i--) + { + QWidget* widget = m_paramsField->widget(i); + m_paramsField->removeWidget(widget); + widget->deleteLater(); + } +} + +void ABIFunctionField::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +void ABIFunctionField::setContractABI(ContractABI *contractABI) +{ + m_contractABI = contractABI; + updateABIFunctionField(); +} + +QStringList ABIFunctionField::getParamValue(int paramID) +{ + if(m_paramsField->currentWidget() == 0) + return QStringList(); + + return ((ABIParamsField*)m_paramsField->currentWidget())->getParamValue(paramID); +} + +QList ABIFunctionField::getParamsValues() +{ + if(m_paramsField->currentWidget() == 0) + return QList(); + + return ((ABIParamsField*)m_paramsField->currentWidget())->getParamsValues(); +} + +std::vector> ABIFunctionField::getValuesVector() +{ + QList qlist = getParamsValues(); + std::vector> result; + for (int i=0; i itemParam; + QStringList qlistVlaues = qlist[i]; + for(int j=0; jcurrentIndex(); + if(currentFunc == -1) + return -1; + + return m_abiFunctionList[currentFunc]; +} + +bool ABIFunctionField::isValid() +{ + if(m_paramsField->currentWidget() == 0) + return true; + + return ((ABIParamsField*)m_paramsField->currentWidget())->isValid(); +} + +void ABIFunctionField::on_currentIndexChanged() +{ + for (int i = 0; i < m_paramsField->count (); ++i) + { + QSizePolicy::Policy policy = QSizePolicy::Ignored; + if (i == m_paramsField->currentIndex()) + policy = QSizePolicy::Expanding; + + QWidget* pPage = m_paramsField->widget(i); + pPage->setSizePolicy(policy, policy); + } + Q_EMIT(functionChanged()); +} + diff --git a/src/qt/abiparam.cpp b/src/qt/abiparam.cpp index 9bfe5966eb..77d1cfa1d6 100644 --- a/src/qt/abiparam.cpp +++ b/src/qt/abiparam.cpp @@ -1,162 +1,162 @@ -#include -#include -#include -#include - -#include -#include - -ABIParam::ABIParam(const PlatformStyle *platformStyle, int ID, const ParameterABI ¶m, QWidget *parent) : - QWidget(parent), - m_ParamID(ID), - m_paramName(0), - m_mainLayout(0), - m_paramItemsLayout(0), - m_param(param), - m_platformStyle(platformStyle), - m_vSpacer(0), - m_hSpacer(0) -{ - m_paramName = new QLabel(this); - m_mainLayout = new QHBoxLayout(this); - m_paramItemsLayout = new QVBoxLayout(); - m_vSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); - m_hSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); - - m_mainLayout->setSpacing(10); - m_mainLayout->setContentsMargins(0,0,0,0); - - m_paramItemsLayout->setSpacing(3); - m_paramItemsLayout->setContentsMargins(0,0,0,0); - - m_paramName->setToolTip(tr("%1 %2").arg(QString::fromStdString(param.type)).arg(QString::fromStdString(param.name))); - m_paramName->setFixedWidth(160); - m_paramName->setFixedHeight(27); - - QFontMetrics metrix(m_paramName->font()); - int width = m_paramName->width() + 10; - QString text(QString("%2 %1").arg(QString::fromStdString(param.name)).arg(QString::fromStdString(param.type))); - QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); - m_paramName->setText(clippedText); - - QVBoxLayout *vLayout = new QVBoxLayout(); - vLayout->addWidget(m_paramName); - vLayout->addSpacerItem(m_vSpacer); - m_mainLayout->addLayout(vLayout); - - if(param.decodeType().isList()) - { - if(param.decodeType().isDynamic()) - { - addNewParamItem(0); - } - else - { - for(size_t i = 0; i < param.decodeType().length(); i++) - { - ABIParamItem *m_paramValue = new ABIParamItem(m_platformStyle, m_param, this); - m_paramValue->setFixed(true); - m_paramItemsLayout->addWidget(m_paramValue); - m_listParamItems.append(m_paramValue); - } - if(param.decodeType().length() > 1) - { - m_vSpacer->changeSize(20, 40, QSizePolicy::Fixed, QSizePolicy::Expanding); - } - } - } - else - { - ABIParamItem *m_paramValue = new ABIParamItem(m_platformStyle, m_param, this); - m_paramValue->setFixed(true); - m_paramItemsLayout->addWidget(m_paramValue); - m_listParamItems.append(m_paramValue); - } - m_mainLayout->addLayout(m_paramItemsLayout); - m_mainLayout->addSpacerItem(m_hSpacer); - setLayout(m_mainLayout); -} - -QStringList ABIParam::getValue() -{ - QStringList valuesList; - for(int i = 0; i < m_listParamItems.count(); i++) - { - if(!m_listParamItems[i]->getIsDeleted()) - valuesList.append(m_listParamItems[i]->getValue()); - } - return valuesList; -} - -bool ABIParam::isValid() -{ - bool isValid = true; - for(int i = 0; i < m_listParamItems.count(); i++) - { - if(!m_listParamItems[i]->getIsDeleted() && !m_listParamItems[i]->isValid()) - isValid = false; - } - return isValid; -} - -void ABIParam::updateParamItemsPosition() -{ - for(int i = 0; i < m_paramItemsLayout->count(); i++) - { - m_listParamItems[i]->setPosition(i); - } -} - -void ABIParam::addNewParamItem(int position) -{ - if(m_listParamItems.count() == 1 && m_listParamItems[0]->getIsDeleted()) - { - m_hSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); - m_listParamItems[0]->setIsDeleted(false); - } - else - { - ABIParamItem *item = new ABIParamItem(m_platformStyle, m_param, this); - m_listParamItems.insert(position, item); - m_paramItemsLayout->insertWidget(position, item); - - if(m_paramItemsLayout->count() > 1) - { - m_vSpacer->changeSize(20, 40, QSizePolicy::Fixed, QSizePolicy::Expanding); - } - - updateParamItemsPosition(); - - connect(item, SIGNAL(on_addItemClicked(int)), this, SLOT(addNewParamItem(int))); - connect(item, SIGNAL(on_removeItemClicked(int)), this, SLOT(removeParamItem(int))); - } -} - -void ABIParam::removeParamItem(int position) -{ - if(m_listParamItems.count() == 1) - { - m_listParamItems[0]->setIsDeleted(true); - m_hSpacer->changeSize(40, 20, QSizePolicy::Expanding, QSizePolicy::Fixed); - } - else - { - QLayoutItem *item = m_paramItemsLayout->takeAt(position); - QWidget * widget = item->widget(); - if(widget != NULL) - { - m_paramItemsLayout->removeWidget(widget); - disconnect(widget, 0, 0, 0); - widget->setParent(NULL); - delete widget; - m_listParamItems.removeAt(position); - } - - if(m_paramItemsLayout->count() < 2) - { - m_vSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); - } - - updateParamItemsPosition(); - } -} +#include +#include +#include +#include + +#include +#include + +ABIParam::ABIParam(const PlatformStyle *platformStyle, int ID, const ParameterABI ¶m, QWidget *parent) : + QWidget(parent), + m_ParamID(ID), + m_paramName(0), + m_mainLayout(0), + m_paramItemsLayout(0), + m_param(param), + m_platformStyle(platformStyle), + m_vSpacer(0), + m_hSpacer(0) +{ + m_paramName = new QLabel(this); + m_mainLayout = new QHBoxLayout(this); + m_paramItemsLayout = new QVBoxLayout(); + m_vSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); + m_hSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); + + m_mainLayout->setSpacing(10); + m_mainLayout->setContentsMargins(0,0,0,0); + + m_paramItemsLayout->setSpacing(3); + m_paramItemsLayout->setContentsMargins(0,0,0,0); + + m_paramName->setToolTip(tr("%1 %2").arg(QString::fromStdString(param.type)).arg(QString::fromStdString(param.name))); + m_paramName->setFixedWidth(160); + m_paramName->setFixedHeight(27); + + QFontMetrics metrix(m_paramName->font()); + int width = m_paramName->width() + 10; + QString text(QString("%2 %1").arg(QString::fromStdString(param.name)).arg(QString::fromStdString(param.type))); + QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); + m_paramName->setText(clippedText); + + QVBoxLayout *vLayout = new QVBoxLayout(); + vLayout->addWidget(m_paramName); + vLayout->addSpacerItem(m_vSpacer); + m_mainLayout->addLayout(vLayout); + + if(param.decodeType().isList()) + { + if(param.decodeType().isDynamic()) + { + addNewParamItem(0); + } + else + { + for(size_t i = 0; i < param.decodeType().length(); i++) + { + ABIParamItem *m_paramValue = new ABIParamItem(m_platformStyle, m_param, this); + m_paramValue->setFixed(true); + m_paramItemsLayout->addWidget(m_paramValue); + m_listParamItems.append(m_paramValue); + } + if(param.decodeType().length() > 1) + { + m_vSpacer->changeSize(20, 40, QSizePolicy::Fixed, QSizePolicy::Expanding); + } + } + } + else + { + ABIParamItem *m_paramValue = new ABIParamItem(m_platformStyle, m_param, this); + m_paramValue->setFixed(true); + m_paramItemsLayout->addWidget(m_paramValue); + m_listParamItems.append(m_paramValue); + } + m_mainLayout->addLayout(m_paramItemsLayout); + m_mainLayout->addSpacerItem(m_hSpacer); + setLayout(m_mainLayout); +} + +QStringList ABIParam::getValue() +{ + QStringList valuesList; + for(int i = 0; i < m_listParamItems.count(); i++) + { + if(!m_listParamItems[i]->getIsDeleted()) + valuesList.append(m_listParamItems[i]->getValue()); + } + return valuesList; +} + +bool ABIParam::isValid() +{ + bool isValid = true; + for(int i = 0; i < m_listParamItems.count(); i++) + { + if(!m_listParamItems[i]->getIsDeleted() && !m_listParamItems[i]->isValid()) + isValid = false; + } + return isValid; +} + +void ABIParam::updateParamItemsPosition() +{ + for(int i = 0; i < m_paramItemsLayout->count(); i++) + { + m_listParamItems[i]->setPosition(i); + } +} + +void ABIParam::addNewParamItem(int position) +{ + if(m_listParamItems.count() == 1 && m_listParamItems[0]->getIsDeleted()) + { + m_hSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); + m_listParamItems[0]->setIsDeleted(false); + } + else + { + ABIParamItem *item = new ABIParamItem(m_platformStyle, m_param, this); + m_listParamItems.insert(position, item); + m_paramItemsLayout->insertWidget(position, item); + + if(m_paramItemsLayout->count() > 1) + { + m_vSpacer->changeSize(20, 40, QSizePolicy::Fixed, QSizePolicy::Expanding); + } + + updateParamItemsPosition(); + + connect(item, SIGNAL(on_addItemClicked(int)), this, SLOT(addNewParamItem(int))); + connect(item, SIGNAL(on_removeItemClicked(int)), this, SLOT(removeParamItem(int))); + } +} + +void ABIParam::removeParamItem(int position) +{ + if(m_listParamItems.count() == 1) + { + m_listParamItems[0]->setIsDeleted(true); + m_hSpacer->changeSize(40, 20, QSizePolicy::Expanding, QSizePolicy::Fixed); + } + else + { + QLayoutItem *item = m_paramItemsLayout->takeAt(position); + QWidget * widget = item->widget(); + if(widget != NULL) + { + m_paramItemsLayout->removeWidget(widget); + disconnect(widget, 0, 0, 0); + widget->setParent(NULL); + delete widget; + m_listParamItems.removeAt(position); + } + + if(m_paramItemsLayout->count() < 2) + { + m_vSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); + } + + updateParamItemsPosition(); + } +} diff --git a/src/qt/abiparamitem.cpp b/src/qt/abiparamitem.cpp index 59b7883cf4..1e201cb56f 100644 --- a/src/qt/abiparamitem.cpp +++ b/src/qt/abiparamitem.cpp @@ -1,98 +1,98 @@ -#include -#include -#include - -#include -#include - -ABIParamItem::ABIParamItem(const PlatformStyle *platformStyle, const ParameterABI ¶m, QWidget *parent) : - QWidget(parent), - m_buttonAdd(new QToolButton(this)), - m_buttonRemove(new QToolButton(this)), - m_itemValue(new QValidatedLineEdit(this)), - m_isDeleted(false) -{ - QHBoxLayout *mainLayout = new QHBoxLayout(this); - mainLayout->setSpacing(2); - mainLayout->setContentsMargins(0,0,0,0); - - m_buttonAdd->setIcon(platformStyle->MultiStatesIcon(":/icons/add", PlatformStyle::PushButton)); - m_buttonRemove->setIcon(platformStyle->MultiStatesIcon(":/icons/remove", PlatformStyle::PushButton)); - - m_buttonAdd->setFixedSize(30,30); - m_buttonRemove->setFixedSize(30,30); - - m_buttonAdd->setFocusPolicy(Qt::NoFocus); - m_buttonRemove->setFocusPolicy(Qt::NoFocus); - - QRegularExpression regEx; - if(ParameterABI::getRegularExpession(param.decodeType(), regEx)) - { - QRegularExpressionValidator *validator = new QRegularExpressionValidator(m_itemValue); - validator->setRegularExpression(regEx); - m_itemValue->setEmptyIsValid(false); - m_itemValue->setCheckValidator(validator); - } - - mainLayout->addWidget(m_buttonAdd); - mainLayout->addWidget(m_buttonRemove); - mainLayout->addWidget(m_itemValue); - setLayout(mainLayout); - - connect(m_buttonAdd, SIGNAL(clicked(bool)), this, SLOT(on_addItemClicked())); - connect(m_buttonRemove, SIGNAL(clicked(bool)), this, SLOT(on_removeItemClicked())); -} - -QString ABIParamItem::getValue() -{ - return m_itemValue->text(); -} - -void ABIParamItem::setFixed(bool isFixed) -{ - m_buttonAdd->setVisible(!isFixed); - m_buttonRemove->setVisible(!isFixed); -} - -int ABIParamItem::getPosition() const -{ - return m_position; -} - -void ABIParamItem::setPosition(int position) -{ - m_position = position; -} - -void ABIParamItem::on_addItemClicked() -{ - Q_EMIT on_addItemClicked(m_position + 1); -} - -void ABIParamItem::on_removeItemClicked() -{ - Q_EMIT on_removeItemClicked(m_position); -} - -bool ABIParamItem::getIsDeleted() const -{ - return m_isDeleted; -} - -void ABIParamItem::setIsDeleted(bool isDeleted) -{ - m_isDeleted= isDeleted; - if(isDeleted) - { - m_itemValue->setText(""); - m_itemValue->setValid(true); - } - m_itemValue->setVisible(!isDeleted); - m_buttonRemove->setEnabled(!isDeleted); -} - -bool ABIParamItem::isValid() -{ - m_itemValue->checkValidity(); - return m_itemValue->isValid(); -} +#include +#include +#include + +#include +#include + +ABIParamItem::ABIParamItem(const PlatformStyle *platformStyle, const ParameterABI ¶m, QWidget *parent) : + QWidget(parent), + m_buttonAdd(new QToolButton(this)), + m_buttonRemove(new QToolButton(this)), + m_itemValue(new QValidatedLineEdit(this)), + m_isDeleted(false) +{ + QHBoxLayout *mainLayout = new QHBoxLayout(this); + mainLayout->setSpacing(2); + mainLayout->setContentsMargins(0,0,0,0); + + m_buttonAdd->setIcon(platformStyle->MultiStatesIcon(":/icons/add", PlatformStyle::PushButton)); + m_buttonRemove->setIcon(platformStyle->MultiStatesIcon(":/icons/remove", PlatformStyle::PushButton)); + + m_buttonAdd->setFixedSize(30,30); + m_buttonRemove->setFixedSize(30,30); + + m_buttonAdd->setFocusPolicy(Qt::NoFocus); + m_buttonRemove->setFocusPolicy(Qt::NoFocus); + + QRegularExpression regEx; + if(ParameterABI::getRegularExpession(param.decodeType(), regEx)) + { + QRegularExpressionValidator *validator = new QRegularExpressionValidator(m_itemValue); + validator->setRegularExpression(regEx); + m_itemValue->setEmptyIsValid(false); + m_itemValue->setCheckValidator(validator); + } + + mainLayout->addWidget(m_buttonAdd); + mainLayout->addWidget(m_buttonRemove); + mainLayout->addWidget(m_itemValue); + setLayout(mainLayout); + + connect(m_buttonAdd, SIGNAL(clicked(bool)), this, SLOT(on_addItemClicked())); + connect(m_buttonRemove, SIGNAL(clicked(bool)), this, SLOT(on_removeItemClicked())); +} + +QString ABIParamItem::getValue() +{ + return m_itemValue->text(); +} + +void ABIParamItem::setFixed(bool isFixed) +{ + m_buttonAdd->setVisible(!isFixed); + m_buttonRemove->setVisible(!isFixed); +} + +int ABIParamItem::getPosition() const +{ + return m_position; +} + +void ABIParamItem::setPosition(int position) +{ + m_position = position; +} + +void ABIParamItem::on_addItemClicked() +{ + Q_EMIT on_addItemClicked(m_position + 1); +} + +void ABIParamItem::on_removeItemClicked() +{ + Q_EMIT on_removeItemClicked(m_position); +} + +bool ABIParamItem::getIsDeleted() const +{ + return m_isDeleted; +} + +void ABIParamItem::setIsDeleted(bool isDeleted) +{ + m_isDeleted= isDeleted; + if(isDeleted) + { + m_itemValue->setText(""); + m_itemValue->setValid(true); + } + m_itemValue->setVisible(!isDeleted); + m_buttonRemove->setEnabled(!isDeleted); +} + +bool ABIParamItem::isValid() +{ + m_itemValue->checkValidity(); + return m_itemValue->isValid(); +} diff --git a/src/qt/abiparamsfield.cpp b/src/qt/abiparamsfield.cpp index 4e02717954..5b0824b2fe 100644 --- a/src/qt/abiparamsfield.cpp +++ b/src/qt/abiparamsfield.cpp @@ -1,57 +1,57 @@ -#include -#include -#include - -#include - -ABIParamsField::ABIParamsField(const PlatformStyle *platformStyle, QWidget *parent) : - QWidget(parent), - m_mainLayout(new QVBoxLayout(this)) -{ - m_platfromStyle = platformStyle; - m_mainLayout->setSpacing(10); - m_mainLayout->setContentsMargins(0,0,30,0); - this->setLayout(m_mainLayout); -} - -void ABIParamsField::updateParamsField(const FunctionABI &function) -{ - // Add function parameters - m_listParams.clear(); - int paramId = 0; - for(std::vector::const_iterator param = function.inputs.begin(); param != function.inputs.end(); ++param) - { - ABIParam *paramFiled = new ABIParam(m_platfromStyle, paramId, *param); - m_listParams.append(paramFiled); - m_mainLayout->addWidget(paramFiled); - - paramId++; - } - m_mainLayout->addSpacerItem(new QSpacerItem(20, 40, QSizePolicy::Fixed, QSizePolicy::Expanding)); -} - -QStringList ABIParamsField::getParamValue(int paramID) -{ - // Get parameter value - return m_listParams[paramID]->getValue(); -} - -QList ABIParamsField::getParamsValues() -{ - // Get parameters values - QList resultList; - for(int i = 0; i < m_listParams.count(); ++i){ - resultList.append(m_listParams[i]->getValue()); - } - return resultList; -} - -bool ABIParamsField::isValid() -{ - bool isValid = true; - for(int i = 0; i < m_listParams.count(); ++i){ - if(!m_listParams[i]->isValid()) - isValid = false; - } - return isValid; -} +#include +#include +#include + +#include + +ABIParamsField::ABIParamsField(const PlatformStyle *platformStyle, QWidget *parent) : + QWidget(parent), + m_mainLayout(new QVBoxLayout(this)) +{ + m_platfromStyle = platformStyle; + m_mainLayout->setSpacing(10); + m_mainLayout->setContentsMargins(0,0,30,0); + this->setLayout(m_mainLayout); +} + +void ABIParamsField::updateParamsField(const FunctionABI &function) +{ + // Add function parameters + m_listParams.clear(); + int paramId = 0; + for(std::vector::const_iterator param = function.inputs.begin(); param != function.inputs.end(); ++param) + { + ABIParam *paramFiled = new ABIParam(m_platfromStyle, paramId, *param); + m_listParams.append(paramFiled); + m_mainLayout->addWidget(paramFiled); + + paramId++; + } + m_mainLayout->addSpacerItem(new QSpacerItem(20, 40, QSizePolicy::Fixed, QSizePolicy::Expanding)); +} + +QStringList ABIParamsField::getParamValue(int paramID) +{ + // Get parameter value + return m_listParams[paramID]->getValue(); +} + +QList ABIParamsField::getParamsValues() +{ + // Get parameters values + QList resultList; + for(int i = 0; i < m_listParams.count(); ++i){ + resultList.append(m_listParams[i]->getValue()); + } + return resultList; +} + +bool ABIParamsField::isValid() +{ + bool isValid = true; + for(int i = 0; i < m_listParams.count(); ++i){ + if(!m_listParams[i]->isValid()) + isValid = false; + } + return isValid; +} diff --git a/src/qt/addtokenpage.cpp b/src/qt/addtokenpage.cpp index d5ed9e50e5..e1ae9e013f 100644 --- a/src/qt/addtokenpage.cpp +++ b/src/qt/addtokenpage.cpp @@ -1,163 +1,163 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -AddTokenPage::AddTokenPage(QWidget *parent) : - QWidget(parent), - ui(new Ui::AddTokenPage), - m_tokenABI(0), - m_model(0), - m_clientModel(0) -{ - ui->setupUi(this); - - // Set stylesheet - SetObjectStyleSheet(ui->clearButton, StyleSheetNames::ButtonBlack); - - ui->labelDescription->setText(tr("(This is your wallet address which will be tied to the token for send/receive operations)")); - QFont font = QApplication::font(); - font.setPointSizeF(font.pointSizeF() * 0.8); - ui->labelDescription->setFont(font); - ui->labelSpacer->setFont(font); - - m_tokenABI = new Token(); - - connect(ui->lineEditContractAddress, &QLineEdit::textChanged, this, &AddTokenPage::on_addressChanged); - connect(ui->lineEditTokenName, &QLineEdit::textChanged, this, &AddTokenPage::on_updateConfirmButton); - connect(ui->lineEditTokenSymbol, &QLineEdit::textChanged, this, &AddTokenPage::on_updateConfirmButton); - - ui->lineEditSenderAddress->setAddressColumn(AddressTableModel::Address); - ui->lineEditSenderAddress->setTypeRole(AddressTableModel::TypeRole); - ui->lineEditSenderAddress->setSenderAddress(true); - if(ui->lineEditSenderAddress->isEditable()) - ((QValidatedLineEdit*)ui->lineEditSenderAddress->lineEdit())->setEmptyIsValid(false); - m_validTokenAddress = false; -} - -AddTokenPage::~AddTokenPage() -{ - delete ui; - - if(m_tokenABI) - delete m_tokenABI; - m_tokenABI = 0; -} - -void AddTokenPage::setClientModel(ClientModel *clientModel) -{ - m_clientModel = clientModel; -} - -void AddTokenPage::clearAll() -{ - ui->lineEditContractAddress->setText(""); - ui->lineEditTokenName->setText(""); - ui->lineEditTokenSymbol->setText(""); - ui->lineEditDecimals->setText(""); - ui->lineEditSenderAddress->setCurrentIndex(-1); -} - -void AddTokenPage::setModel(WalletModel *_model) -{ - m_model = _model; - on_zeroBalanceAddressToken(m_model->getOptionsModel()->getZeroBalanceAddressToken()); - connect(m_model->getOptionsModel(), SIGNAL(zeroBalanceAddressTokenChanged(bool)), this, SLOT(on_zeroBalanceAddressToken(bool))); - - ui->lineEditSenderAddress->setWalletModel(m_model); - m_tokenABI->setModel(m_model); -} - -void AddTokenPage::on_clearButton_clicked() -{ - clearAll(); -} - -void AddTokenPage::on_confirmButton_clicked() -{ - if(ui->lineEditSenderAddress->isValidAddress()) - { - interfaces::TokenInfo tokenInfo; - tokenInfo.contract_address = ui->lineEditContractAddress->text().toStdString(); - tokenInfo.token_name = ui->lineEditTokenName->text().toStdString(); - tokenInfo.token_symbol = ui->lineEditTokenSymbol->text().toStdString(); - tokenInfo.decimals = ui->lineEditDecimals->text().toInt(); - tokenInfo.sender_address = ui->lineEditSenderAddress->currentText().toStdString(); - - if(m_model) - { - if(!m_model->wallet().isMineAddress(tokenInfo.sender_address)) - { - QString symbol = QString::fromStdString(tokenInfo.token_symbol); - QString address = QString::fromStdString(tokenInfo.sender_address); - QString message = tr("The %1 address \"%2\" is not yours, please change it to new one.\n").arg(symbol, address); - QMessageBox::warning(this, tr("Invalid token address"), message); - } - else if(m_model->wallet().existTokenEntry(tokenInfo)) - { - QMessageBox::information(this, tr("Token exist"), tr("The token already exist with the specified contract and sender addresses.")); - } - else - { - m_model->wallet().addTokenEntry(tokenInfo); - - clearAll(); - - if(!fLogEvents) - { - QMessageBox::information(this, tr("Log events"), tr("Enable log events from the option menu in order to receive token transactions.")); - } - } - } - } -} - -void AddTokenPage::on_addressChanged() -{ - QString tokenAddress = ui->lineEditContractAddress->text(); - if(m_tokenABI) - { - m_tokenABI->setAddress(tokenAddress.toStdString()); - std::string name, symbol, decimals; - bool ret = m_tokenABI->name(name); - ret &= m_tokenABI->symbol(symbol); - ret &= m_tokenABI->decimals(decimals); - ui->lineEditTokenName->setText(QString::fromStdString(name)); - ui->lineEditTokenSymbol->setText(QString::fromStdString(symbol)); - ui->lineEditDecimals->setText(QString::fromStdString(decimals)); - m_validTokenAddress = ret; - } - ui->confirmButton->setEnabled(m_validTokenAddress); -} - -void AddTokenPage::on_updateConfirmButton() -{ - bool enabled = true; - if(ui->lineEditTokenName->text().isEmpty()) - { - enabled = false; - } - if(ui->lineEditTokenSymbol->text().isEmpty()) - { - enabled = false; - } - enabled &= m_validTokenAddress; - ui->confirmButton->setEnabled(enabled); -} - -void AddTokenPage::on_zeroBalanceAddressToken(bool enable) -{ - ui->lineEditSenderAddress->setIncludeZeroValue(enable); -} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +AddTokenPage::AddTokenPage(QWidget *parent) : + QWidget(parent), + ui(new Ui::AddTokenPage), + m_tokenABI(0), + m_model(0), + m_clientModel(0) +{ + ui->setupUi(this); + + // Set stylesheet + SetObjectStyleSheet(ui->clearButton, StyleSheetNames::ButtonBlack); + + ui->labelDescription->setText(tr("(This is your wallet address which will be tied to the token for send/receive operations)")); + QFont font = QApplication::font(); + font.setPointSizeF(font.pointSizeF() * 0.8); + ui->labelDescription->setFont(font); + ui->labelSpacer->setFont(font); + + m_tokenABI = new Token(); + + connect(ui->lineEditContractAddress, &QLineEdit::textChanged, this, &AddTokenPage::on_addressChanged); + connect(ui->lineEditTokenName, &QLineEdit::textChanged, this, &AddTokenPage::on_updateConfirmButton); + connect(ui->lineEditTokenSymbol, &QLineEdit::textChanged, this, &AddTokenPage::on_updateConfirmButton); + + ui->lineEditSenderAddress->setAddressColumn(AddressTableModel::Address); + ui->lineEditSenderAddress->setTypeRole(AddressTableModel::TypeRole); + ui->lineEditSenderAddress->setSenderAddress(true); + if(ui->lineEditSenderAddress->isEditable()) + ((QValidatedLineEdit*)ui->lineEditSenderAddress->lineEdit())->setEmptyIsValid(false); + m_validTokenAddress = false; +} + +AddTokenPage::~AddTokenPage() +{ + delete ui; + + if(m_tokenABI) + delete m_tokenABI; + m_tokenABI = 0; +} + +void AddTokenPage::setClientModel(ClientModel *clientModel) +{ + m_clientModel = clientModel; +} + +void AddTokenPage::clearAll() +{ + ui->lineEditContractAddress->setText(""); + ui->lineEditTokenName->setText(""); + ui->lineEditTokenSymbol->setText(""); + ui->lineEditDecimals->setText(""); + ui->lineEditSenderAddress->setCurrentIndex(-1); +} + +void AddTokenPage::setModel(WalletModel *_model) +{ + m_model = _model; + on_zeroBalanceAddressToken(m_model->getOptionsModel()->getZeroBalanceAddressToken()); + connect(m_model->getOptionsModel(), SIGNAL(zeroBalanceAddressTokenChanged(bool)), this, SLOT(on_zeroBalanceAddressToken(bool))); + + ui->lineEditSenderAddress->setWalletModel(m_model); + m_tokenABI->setModel(m_model); +} + +void AddTokenPage::on_clearButton_clicked() +{ + clearAll(); +} + +void AddTokenPage::on_confirmButton_clicked() +{ + if(ui->lineEditSenderAddress->isValidAddress()) + { + interfaces::TokenInfo tokenInfo; + tokenInfo.contract_address = ui->lineEditContractAddress->text().toStdString(); + tokenInfo.token_name = ui->lineEditTokenName->text().toStdString(); + tokenInfo.token_symbol = ui->lineEditTokenSymbol->text().toStdString(); + tokenInfo.decimals = ui->lineEditDecimals->text().toInt(); + tokenInfo.sender_address = ui->lineEditSenderAddress->currentText().toStdString(); + + if(m_model) + { + if(!m_model->wallet().isMineAddress(tokenInfo.sender_address)) + { + QString symbol = QString::fromStdString(tokenInfo.token_symbol); + QString address = QString::fromStdString(tokenInfo.sender_address); + QString message = tr("The %1 address \"%2\" is not yours, please change it to new one.\n").arg(symbol, address); + QMessageBox::warning(this, tr("Invalid token address"), message); + } + else if(m_model->wallet().existTokenEntry(tokenInfo)) + { + QMessageBox::information(this, tr("Token exist"), tr("The token already exist with the specified contract and sender addresses.")); + } + else + { + m_model->wallet().addTokenEntry(tokenInfo); + + clearAll(); + + if(!fLogEvents) + { + QMessageBox::information(this, tr("Log events"), tr("Enable log events from the option menu in order to receive token transactions.")); + } + } + } + } +} + +void AddTokenPage::on_addressChanged() +{ + QString tokenAddress = ui->lineEditContractAddress->text(); + if(m_tokenABI) + { + m_tokenABI->setAddress(tokenAddress.toStdString()); + std::string name, symbol, decimals; + bool ret = m_tokenABI->name(name); + ret &= m_tokenABI->symbol(symbol); + ret &= m_tokenABI->decimals(decimals); + ui->lineEditTokenName->setText(QString::fromStdString(name)); + ui->lineEditTokenSymbol->setText(QString::fromStdString(symbol)); + ui->lineEditDecimals->setText(QString::fromStdString(decimals)); + m_validTokenAddress = ret; + } + ui->confirmButton->setEnabled(m_validTokenAddress); +} + +void AddTokenPage::on_updateConfirmButton() +{ + bool enabled = true; + if(ui->lineEditTokenName->text().isEmpty()) + { + enabled = false; + } + if(ui->lineEditTokenSymbol->text().isEmpty()) + { + enabled = false; + } + enabled &= m_validTokenAddress; + ui->confirmButton->setEnabled(enabled); +} + +void AddTokenPage::on_zeroBalanceAddressToken(bool enable) +{ + ui->lineEditSenderAddress->setIncludeZeroValue(enable); +} diff --git a/src/qt/contractabi.cpp b/src/qt/contractabi.cpp index e95dbb413b..d8cf819676 100755 --- a/src/qt/contractabi.cpp +++ b/src/qt/contractabi.cpp @@ -1,811 +1,811 @@ -#include -#include -#include -#include - -namespace ContractABI_NS -{ -// Defining json preprocessor functions in order to avoid repetitive code with slight difference -#define ReadJsonString(json, param, result) if(json.exists(#param) && json[#param].isStr())\ - result.param = json[#param].get_str(); -#define ReadJsonBool(json, param, result) if(json.exists(#param) && json[#param].isBool())\ - result.param = json[#param].get_bool(); -#define ReadJsonArray(json, param, result) if(json.exists(#param) && json[#param].isArray())\ - result = json[#param].get_array(); - -// String parsing functions -inline bool startsWithString(const std::string& str, const std::string& s, size_t& pos) -{ - if(pos >= str.length()) return false; - - size_t length = s.length(); - bool ret = (str.substr(pos, length) == s); - if(ret) pos += length; - return ret; -} - -inline std::string startsWithNumber(const std::string& str, size_t& pos) -{ - if(pos >= str.length()) return ""; - - std::stringstream ss; - for(size_t i = pos; i < str.size(); i++) - { - char c = str[i]; - if(c >= '0' && c <= '9') - ss << c; - else - break; - } - - std::string s = ss.str(); - pos += s.length(); - return s; -} - -// define constransts -const static int HEX_INSTRUCTION_SIZE = 64; -} -using namespace ContractABI_NS; - -ContractABI::ContractABI() -{} - -bool ContractABI::loads(const std::string &json_data) -{ - clean(); - - UniValue json_contract; - bool ret = json_contract.read(json_data); - if(ret && json_contract.isArray()) - { - // Read all functions from the contract - size_t size = json_contract.size(); - for(size_t i = 0; i < size; i++) - { - const UniValue& json_function = json_contract[i]; - FunctionABI function; - ReadJsonString(json_function, name, function); - ReadJsonString(json_function, type, function); - ReadJsonBool(json_function, payable, function); - ReadJsonBool(json_function, constant, function); - ReadJsonBool(json_function, anonymous, function); - - UniValue json_inputs; - ReadJsonArray(json_function, inputs, json_inputs); - for(size_t j = 0; j < json_inputs.size(); j++) - { - const UniValue& json_param = json_inputs[j]; - ParameterABI param; - ReadJsonString(json_param, name, param); - ReadJsonString(json_param, type, param); - function.inputs.push_back(param); - } - - UniValue json_outputs; - ReadJsonArray(json_function, outputs, json_outputs); - for(size_t j = 0; j < json_outputs.size(); j++) - { - const UniValue& json_param = json_outputs[j]; - ParameterABI param; - ReadJsonString(json_param, name, param); - ReadJsonString(json_param, type, param); - ReadJsonBool(json_param, indexed, param); - function.outputs.push_back(param); - } - - functions.push_back(function); - } - } - - FunctionABI function; - function.type = "default"; - function.payable = true; - functions.push_back(function); - - return ret; -} - -void ContractABI::clean() -{ - functions.clear(); -} - -FunctionABI::FunctionABI(const std::string &_name, - const std::string &_type, - const std::vector &_inputs, - const std::vector &_outputs, - bool _payable, bool _constant, bool _anonymous): - name(_name), - type(_type), - inputs(_inputs), - outputs(_outputs), - payable(_payable), - constant(_constant), - anonymous(_anonymous) -{} - -bool FunctionABI::abiIn(const std::vector> &values, std::string &data, std::vector& errors) const -{ - bool ret = inputs.size() == values.size(); - std::string params; - std::map mapDynamic; - for(size_t i = 0; i < inputs.size(); i++) - { - ret &= inputs[i].abiIn(values[i], params, mapDynamic); - errors.push_back(inputs[i].lastError()); - } - if(ret) - { - processDynamicParams(mapDynamic, params); - data = selector() + params; - } - return ret; -} - -bool FunctionABI::abiOut(const std::string &data, std::vector> &values, std::vector& errors) const -{ - size_t pos = 0; - bool ret = true; - for(size_t i = 0; i < outputs.size(); i++) - { - std::vector value; - ret &= outputs[i].abiOut(data, pos, value); - values.push_back(value); - errors.push_back(outputs[i].lastError()); - } - return ret; -} - -std::string FunctionABI::selector() const -{ - if(type == "default") - { - return defaultSelector(); - } - - if(type == "constructor" || (type == "event" && anonymous)) - { - return ""; - } - - std::stringstream id; - id << name; - id << "("; - if(inputs.size() > 0) - { - id << inputs[0].type; - } - for(size_t i = 1; i < inputs.size(); i++) - { - id << "," << inputs[i].type; - } - id << ")"; - std::string sig = id.str(); - - dev::bytes hash; - if(type == "event") - { - hash = dev::sha3(sig).ref().toBytes(); - } - else - { - hash = dev::sha3(sig).ref().cropped(0, 4).toBytes(); - } - - return dev::toHex(hash); -} - -std::string FunctionABI::defaultSelector() -{ - return "00"; -} - -QString FunctionABI::errorMessage(std::vector &errors, bool in) const -{ - if(in && errors.size() != inputs.size()) - return ""; - if(!in && errors.size() != outputs.size()) - return ""; - const std::vector& params = in ? inputs : outputs; - - QStringList messages; - messages.append(QObject::tr("ABI parsing error:")); - for(size_t i = 0; i < errors.size(); i++) - { - ParameterABI::ErrorType err = errors[i]; - if(err == ParameterABI::Ok) continue; - const ParameterABI& param = params[i]; - QString _type = QString::fromStdString(param.type); - QString _name = QString::fromStdString(param.name); - - switch (err) { - case ParameterABI::UnsupportedABI: - messages.append(QObject::tr("Unsupported type %1 %2.").arg(_type, _name)); - break; - case ParameterABI::EncodingError: - messages.append(QObject::tr("Error encoding parameter %1 %2.").arg(_type, _name)); - break; - case ParameterABI::DecodingError: - messages.append(QObject::tr("Error decoding parameter %1 %2.").arg(_type, _name)); - break; - default: - break; - } - } - return messages.join('\n'); -} - -void FunctionABI::processDynamicParams(const std::map &mapDynamic, std::string &data) const -{ - for(auto i = mapDynamic.begin(); i != mapDynamic.end(); i++) - { - int pos = i->first; - std::string value = i->second; - dev::u256 inRef = data.size() / 2; - dev::bytes rawRef = dev::eth::ABISerialiser::serialise(inRef); - std::string strRef = dev::toHex(rawRef); - data.replace(pos, strRef.size(), strRef); - data += value; - } -} - -ParameterABI::ParameterABI(const std::string &_name, const std::string &_type, bool _indexed): - name(_name), - type(_type), - indexed(_indexed), - m_lastError(ParameterABI::Ok) -{} - -ParameterABI::~ParameterABI() -{} - -bool ParameterABI::abiInBasic(ParameterType::Type abiType, std::string value, std::string &data) const -{ - switch (abiType) { - case ParameterType::abi_bytes: - { - value = dev::asString(dev::fromHex(value)); - dev::string32 inData = dev::eth::toString32(value); - data += dev::toHex(inData); - } - break; - case ParameterType::abi_bool: - value = value == "false" ? "0" : "1"; - case ParameterType::abi_int: - case ParameterType::abi_uint: - { - dev::u256 inData(value.c_str()); - dev::bytes rawData = dev::eth::ABISerialiser::serialise(inData); - data += dev::toHex(rawData); - } - break; - case ParameterType::abi_address: - { - dev::u160 inData = dev::fromBigEndian(dev::fromHex(value)); - dev::bytes rawData = dev::eth::ABISerialiser::serialise(inData); - data += dev::toHex(rawData); - } - break; - default: - m_lastError = UnsupportedABI; - return false; - } - return true; -} - -bool ParameterABI::abiOutBasic(ParameterType::Type abiType, const std::string &data, size_t &pos, std::string &value) const -{ - switch (abiType) { - case ParameterType::abi_bytes: - { - dev::bytes rawData = dev::fromHex(data.substr(pos, HEX_INSTRUCTION_SIZE)); - dev::bytesConstRef o(&rawData); - std::string outData = dev::toString(dev::eth::ABIDeserialiser::deserialise(o)); - value = dev::toHex(outData); - } - break; - case ParameterType::abi_uint: - { - dev::bytes rawData = dev::fromHex(data.substr(pos, HEX_INSTRUCTION_SIZE)); - dev::bytesConstRef o(&rawData); - dev::u256 outData = dev::eth::ABIDeserialiser::deserialise(o); - value = outData.str(); - } - break; - case ParameterType::abi_int: - { - dev::bytes rawData = dev::fromHex(data.substr(pos, HEX_INSTRUCTION_SIZE)); - dev::bytesConstRef o(&rawData); - dev::s256 outData = dev::u2s(dev::eth::ABIDeserialiser::deserialise(o)); - value = outData.str(); - } - break; - case ParameterType::abi_address: - { - dev::bytes rawData = dev::fromHex(data.substr(pos, HEX_INSTRUCTION_SIZE)); - dev::bytesConstRef o(&rawData); - dev::u160 outData = dev::eth::ABIDeserialiser::deserialise(o); - dev::bytes rawAddress(20); - dev::toBigEndian(outData, rawAddress); - value = dev::toHex(rawAddress); - } - break; - case ParameterType::abi_bool: - { - dev::bytes rawData = dev::fromHex(data.substr(pos, HEX_INSTRUCTION_SIZE)); - dev::bytesConstRef o(&rawData); - dev::u256 outData = dev::eth::ABIDeserialiser::deserialise(o); - value = outData == 0 ? "false" : "true"; - } - break; - default: - m_lastError = UnsupportedABI; - return false; - } - - pos += HEX_INSTRUCTION_SIZE; - - return true; -} - -void ParameterABI::addDynamic(const std::string ¶mData, std::string &data, std::map &mapDynamic) const -{ - int key = data.size(); - data += paramData.substr(0, HEX_INSTRUCTION_SIZE); - mapDynamic[key] = paramData.substr(HEX_INSTRUCTION_SIZE); -} - -bool ParameterABI::abiIn(const std::vector &value, std::string &data, std::map& mapDynamic) const -{ - try - { - m_lastError = Ok; - ParameterType::Type abiType = decodeType().type(); - if(decodeType().isDynamic() && !decodeType().isList()) - { - // Dynamic basic type (list of bytes or chars) - std::string _value = value[0]; - switch (abiType) { - case ParameterType::abi_bytes: - _value = dev::asString(dev::fromHex(_value)); - case ParameterType::abi_string: - { - std::string paramData = dev::toHex(dev::eth::ABISerialiser::serialise(_value)); - addDynamic(paramData, data, mapDynamic); - } - break; - default: - m_lastError = UnsupportedABI; - return false; - } - } - else if(!decodeType().isDynamic() && !decodeType().isList()) - { - // Static basic type - abiInBasic(abiType, value[0], data); - } - else if(decodeType().isDynamic() && decodeType().isList()) - { - // Dynamic list type - std::string paramData; - abiInBasic(ParameterType::abi_uint, "32", paramData); - size_t length = value.size(); - abiInBasic(ParameterType::abi_uint, std::to_string(length), paramData); - for(size_t i = 0; i < length; i++) - { - abiInBasic(abiType, value[i], paramData); - } - addDynamic(paramData, data, mapDynamic); - } - else if(!decodeType().isDynamic() && decodeType().isList()) - { - // Static list type - size_t length = decodeType().length(); - for(size_t i = 0; i < length; i++) - { - abiInBasic(abiType, value[i], data); - } - } - else - { - // Unknown type - m_lastError = UnsupportedABI; - return false; - } - } - catch(...) - { - m_lastError = EncodingError; - return false; - } - - return true; -} - -bool ParameterABI::abiOut(const std::string &data, size_t &pos, std::vector &value) const -{ - try - { - m_lastError = Ok; - ParameterType::Type abiType = decodeType().type(); - if(decodeType().isDynamic() && !decodeType().isList()) - { - // Dynamic basic type - switch (abiType) { - case ParameterType::abi_bytes: - { - dev::bytes rawData = dev::fromHex(data.substr(pos)); - dev::bytesConstRef o(&rawData); - std::string outData = dev::eth::ABIDeserialiser::deserialise(o); - value.push_back(dev::toHex(outData)); - } - break; - case ParameterType::abi_string: - { - dev::bytes rawData = dev::fromHex(data.substr(pos)); - dev::bytesConstRef o(&rawData); - value.push_back(dev::eth::ABIDeserialiser::deserialise(o)); - } - break; - default: - m_lastError = UnsupportedABI; - return false; - } - - pos += HEX_INSTRUCTION_SIZE; - } - else if(!decodeType().isDynamic() && !decodeType().isList()) - { - // Static basic type - std::string paramValue; - if(abiOutBasic(abiType, data, pos, paramValue)) - { - value.push_back(paramValue); - } - else - { - return false; - } - } - else if(decodeType().isDynamic() && decodeType().isList()) - { - // Dynamic list type - - // Get position - std::string paramValue; - if(!abiOutBasic(ParameterType::abi_uint, data, pos, paramValue)) - return false; - size_t oldPos = pos; - pos = std::atoi(paramValue.c_str()) * 2; - - // Get length - if(!abiOutBasic(ParameterType::abi_uint, data, pos, paramValue)) - return false; - size_t length = std::atoi(paramValue.c_str()); - - // Read list - for(size_t i = 0; i < length; i++) - { - if(!abiOutBasic(abiType, data, pos, paramValue)) - return false; - value.push_back(paramValue); - } - - // Restore position - pos = oldPos; - } - else if(!decodeType().isDynamic() && decodeType().isList()) - { - // Static list type - std::string paramValue; - size_t length = decodeType().length(); - - // Read list - for(size_t i = 0; i < length; i++) - { - if(!abiOutBasic(abiType, data, pos, paramValue)) - return false; - value.push_back(paramValue); - } - } - else - { - // Unknown type - m_lastError = UnsupportedABI; - return false; - } - } - catch(...) - { - m_lastError = DecodingError; - return false; - } - - return true; -} - -bool ParameterABI::getRegularExpession(const ParameterType ¶mType, QRegularExpression ®Ex) -{ - bool ret = false; - switch (paramType.type()) { - case ParameterType::abi_bytes: - { - if(paramType.isDynamic()) - { - regEx.setPattern(paternBytes); - } - else - { - // Expression to check the number of bytes encoded in hex (1-32) - regEx.setPattern(QString(paternBytes32).arg(paramType.totalBytes()*2)); - } - ret = true; - break; - } - case ParameterType::abi_uint: - { - regEx.setPattern(paternUint); - ret = true; - break; - } - case ParameterType::abi_int: - { - regEx.setPattern(paternInt); - ret = true; - break; - } - case ParameterType::abi_address: - { - regEx.setPattern(paternAddress); - ret = true; - break; - } - case ParameterType::abi_bool: - { - regEx.setPattern(paternBool); - ret = true; - break; - } - default: - { - ret = false; - break; - } - } - return ret; -} - -ParameterABI::ErrorType ParameterABI::lastError() const -{ - return m_lastError; -} - -const ParameterType &ParameterABI::decodeType() const -{ - if(m_decodeType.canonical() != type) - { - m_decodeType = ParameterType(type); - } - - return m_decodeType; -} - -ParameterType::ParameterType(const std::string& _type): - m_type(ParameterType::abi_none), - m_whole(0), - m_decimal(0), - m_length(0), - m_isList(false), - m_valid(false) -{ - determine(_type); -} - -bool ParameterType::determine(const std::string &_type) -{ - clean(); - - // Initialize variables - bool ret = true; - size_t pos = 0; - - // Set string representation - m_canonical = _type; - - // Determine the basic type - if(startsWithString(m_canonical, "uint", pos)) - { - m_type = abi_uint; - } - else if(startsWithString(m_canonical, "int", pos)) - { - m_type = abi_int; - } - else if(startsWithString(m_canonical, "address", pos)) - { - m_type = abi_address; - } - else if(startsWithString(m_canonical, "bool", pos)) - { - m_type = abi_bool; - } - else if(startsWithString(m_canonical, "fixed", pos)) - { - m_type = abi_fixed; - } - else if(startsWithString(m_canonical, "ufixed", pos)) - { - m_type = abi_ufixed; - } - else if(startsWithString(m_canonical, "bytes", pos)) - { - m_type = abi_bytes; - } - else if(startsWithString(m_canonical, "string", pos)) - { - m_type = abi_string; - } - - // Provide more informations about the type - if(m_type != abi_none) - { - // Get the whole number part size - std::string strWhole = startsWithNumber(m_canonical, pos); - if(!strWhole.empty()) - { - m_whole = atoi(strWhole.c_str()); - } - - // Get the decimal number part size - if(startsWithString(m_canonical, "x", pos)) - { - std::string strDecimal = startsWithNumber(m_canonical, pos); - if(!strDecimal.empty()) - { - m_decimal = atoi(strDecimal.c_str()); - } - else - { - ret = false; - } - } - - // Get information for list type - if(startsWithString(m_canonical, "[", pos)) - { - std::string strLength = startsWithNumber(m_canonical, pos); - if(!strLength.empty()) - { - m_length = atoi(strLength.c_str()); - } - if(startsWithString(m_canonical, "]", pos)) - { - m_isList = true; - } - else - { - ret = false; - } - } - } - - // Return if not parsed correctly - if(m_canonical.length() != pos || ret == false) - ret = false; - - // Check the validity of the types - if(ret && (m_type == abi_int || m_type == abi_uint)) - { - ret &= m_whole > 0; - ret &= m_whole <= 256; - ret &= m_whole % 8 == 0; - ret &= m_decimal == 0; - } - - if(ret && m_type == abi_address) - { - ret &= m_whole == 0; - ret &= m_decimal == 0; - if(ret) m_whole = 160; - } - - if(ret && (m_type == abi_fixed || m_type == abi_ufixed)) - { - ret &= m_whole > 0; - ret &= m_decimal > 0; - ret &= (m_whole + m_decimal) <= 256; - } - - if(ret && m_type == abi_bytes) - { - m_whole *= 8; - ret &= m_whole > 0; - ret &= m_whole <= 256; - ret &= m_decimal == 0; - } - - if(ret && m_type == abi_function) - { - ret &= m_whole == 0; - ret &= m_decimal == 0; - if(ret) m_whole = 196; - } - - if(ret && m_type == abi_string) - { - ret &= m_whole == 0; - ret &= m_decimal == 0; - } - - m_valid = ret; - - return m_valid; -} - -size_t ParameterType::wholeBits() const -{ - return m_whole; -} - -size_t ParameterType::decimalBits() const -{ - return m_decimal; -} - -size_t ParameterType::totalBytes() const -{ - return ceil((m_whole + m_decimal) / 8.0); -} - -size_t ParameterType::length() const -{ - return m_length; -} - -bool ParameterType::isList() const -{ - return m_isList; -} - -bool ParameterType::isDynamic() const -{ - // Type bytes is dynamic when the count of bytes is not known. - // Type string is always dynamic. - if((m_type == abi_bytes && totalBytes() == 0) || m_type == abi_string) - { - return true; - } - - // Type list is dynamic when the count of elements is not known. - if(m_isList) - return m_length == 0; - - return false; -} - -bool ParameterType::isValid() const -{ - return m_valid; -} - -const std::string& ParameterType::canonical() const -{ - return m_canonical; -} - -void ParameterType::clean() -{ - m_type = ParameterType::abi_none; - m_whole = 0; - m_decimal = 0; - m_length = 0; - m_isList = false; - m_valid = false; - m_canonical = ""; -} - -ParameterType::Type ParameterType::type() const -{ - return m_type; -} +#include +#include +#include +#include + +namespace ContractABI_NS +{ +// Defining json preprocessor functions in order to avoid repetitive code with slight difference +#define ReadJsonString(json, param, result) if(json.exists(#param) && json[#param].isStr())\ + result.param = json[#param].get_str(); +#define ReadJsonBool(json, param, result) if(json.exists(#param) && json[#param].isBool())\ + result.param = json[#param].get_bool(); +#define ReadJsonArray(json, param, result) if(json.exists(#param) && json[#param].isArray())\ + result = json[#param].get_array(); + +// String parsing functions +inline bool startsWithString(const std::string& str, const std::string& s, size_t& pos) +{ + if(pos >= str.length()) return false; + + size_t length = s.length(); + bool ret = (str.substr(pos, length) == s); + if(ret) pos += length; + return ret; +} + +inline std::string startsWithNumber(const std::string& str, size_t& pos) +{ + if(pos >= str.length()) return ""; + + std::stringstream ss; + for(size_t i = pos; i < str.size(); i++) + { + char c = str[i]; + if(c >= '0' && c <= '9') + ss << c; + else + break; + } + + std::string s = ss.str(); + pos += s.length(); + return s; +} + +// define constransts +const static int HEX_INSTRUCTION_SIZE = 64; +} +using namespace ContractABI_NS; + +ContractABI::ContractABI() +{} + +bool ContractABI::loads(const std::string &json_data) +{ + clean(); + + UniValue json_contract; + bool ret = json_contract.read(json_data); + if(ret && json_contract.isArray()) + { + // Read all functions from the contract + size_t size = json_contract.size(); + for(size_t i = 0; i < size; i++) + { + const UniValue& json_function = json_contract[i]; + FunctionABI function; + ReadJsonString(json_function, name, function); + ReadJsonString(json_function, type, function); + ReadJsonBool(json_function, payable, function); + ReadJsonBool(json_function, constant, function); + ReadJsonBool(json_function, anonymous, function); + + UniValue json_inputs; + ReadJsonArray(json_function, inputs, json_inputs); + for(size_t j = 0; j < json_inputs.size(); j++) + { + const UniValue& json_param = json_inputs[j]; + ParameterABI param; + ReadJsonString(json_param, name, param); + ReadJsonString(json_param, type, param); + function.inputs.push_back(param); + } + + UniValue json_outputs; + ReadJsonArray(json_function, outputs, json_outputs); + for(size_t j = 0; j < json_outputs.size(); j++) + { + const UniValue& json_param = json_outputs[j]; + ParameterABI param; + ReadJsonString(json_param, name, param); + ReadJsonString(json_param, type, param); + ReadJsonBool(json_param, indexed, param); + function.outputs.push_back(param); + } + + functions.push_back(function); + } + } + + FunctionABI function; + function.type = "default"; + function.payable = true; + functions.push_back(function); + + return ret; +} + +void ContractABI::clean() +{ + functions.clear(); +} + +FunctionABI::FunctionABI(const std::string &_name, + const std::string &_type, + const std::vector &_inputs, + const std::vector &_outputs, + bool _payable, bool _constant, bool _anonymous): + name(_name), + type(_type), + inputs(_inputs), + outputs(_outputs), + payable(_payable), + constant(_constant), + anonymous(_anonymous) +{} + +bool FunctionABI::abiIn(const std::vector> &values, std::string &data, std::vector& errors) const +{ + bool ret = inputs.size() == values.size(); + std::string params; + std::map mapDynamic; + for(size_t i = 0; i < inputs.size(); i++) + { + ret &= inputs[i].abiIn(values[i], params, mapDynamic); + errors.push_back(inputs[i].lastError()); + } + if(ret) + { + processDynamicParams(mapDynamic, params); + data = selector() + params; + } + return ret; +} + +bool FunctionABI::abiOut(const std::string &data, std::vector> &values, std::vector& errors) const +{ + size_t pos = 0; + bool ret = true; + for(size_t i = 0; i < outputs.size(); i++) + { + std::vector value; + ret &= outputs[i].abiOut(data, pos, value); + values.push_back(value); + errors.push_back(outputs[i].lastError()); + } + return ret; +} + +std::string FunctionABI::selector() const +{ + if(type == "default") + { + return defaultSelector(); + } + + if(type == "constructor" || (type == "event" && anonymous)) + { + return ""; + } + + std::stringstream id; + id << name; + id << "("; + if(inputs.size() > 0) + { + id << inputs[0].type; + } + for(size_t i = 1; i < inputs.size(); i++) + { + id << "," << inputs[i].type; + } + id << ")"; + std::string sig = id.str(); + + dev::bytes hash; + if(type == "event") + { + hash = dev::sha3(sig).ref().toBytes(); + } + else + { + hash = dev::sha3(sig).ref().cropped(0, 4).toBytes(); + } + + return dev::toHex(hash); +} + +std::string FunctionABI::defaultSelector() +{ + return "00"; +} + +QString FunctionABI::errorMessage(std::vector &errors, bool in) const +{ + if(in && errors.size() != inputs.size()) + return ""; + if(!in && errors.size() != outputs.size()) + return ""; + const std::vector& params = in ? inputs : outputs; + + QStringList messages; + messages.append(QObject::tr("ABI parsing error:")); + for(size_t i = 0; i < errors.size(); i++) + { + ParameterABI::ErrorType err = errors[i]; + if(err == ParameterABI::Ok) continue; + const ParameterABI& param = params[i]; + QString _type = QString::fromStdString(param.type); + QString _name = QString::fromStdString(param.name); + + switch (err) { + case ParameterABI::UnsupportedABI: + messages.append(QObject::tr("Unsupported type %1 %2.").arg(_type, _name)); + break; + case ParameterABI::EncodingError: + messages.append(QObject::tr("Error encoding parameter %1 %2.").arg(_type, _name)); + break; + case ParameterABI::DecodingError: + messages.append(QObject::tr("Error decoding parameter %1 %2.").arg(_type, _name)); + break; + default: + break; + } + } + return messages.join('\n'); +} + +void FunctionABI::processDynamicParams(const std::map &mapDynamic, std::string &data) const +{ + for(auto i = mapDynamic.begin(); i != mapDynamic.end(); i++) + { + int pos = i->first; + std::string value = i->second; + dev::u256 inRef = data.size() / 2; + dev::bytes rawRef = dev::eth::ABISerialiser::serialise(inRef); + std::string strRef = dev::toHex(rawRef); + data.replace(pos, strRef.size(), strRef); + data += value; + } +} + +ParameterABI::ParameterABI(const std::string &_name, const std::string &_type, bool _indexed): + name(_name), + type(_type), + indexed(_indexed), + m_lastError(ParameterABI::Ok) +{} + +ParameterABI::~ParameterABI() +{} + +bool ParameterABI::abiInBasic(ParameterType::Type abiType, std::string value, std::string &data) const +{ + switch (abiType) { + case ParameterType::abi_bytes: + { + value = dev::asString(dev::fromHex(value)); + dev::string32 inData = dev::eth::toString32(value); + data += dev::toHex(inData); + } + break; + case ParameterType::abi_bool: + value = value == "false" ? "0" : "1"; + case ParameterType::abi_int: + case ParameterType::abi_uint: + { + dev::u256 inData(value.c_str()); + dev::bytes rawData = dev::eth::ABISerialiser::serialise(inData); + data += dev::toHex(rawData); + } + break; + case ParameterType::abi_address: + { + dev::u160 inData = dev::fromBigEndian(dev::fromHex(value)); + dev::bytes rawData = dev::eth::ABISerialiser::serialise(inData); + data += dev::toHex(rawData); + } + break; + default: + m_lastError = UnsupportedABI; + return false; + } + return true; +} + +bool ParameterABI::abiOutBasic(ParameterType::Type abiType, const std::string &data, size_t &pos, std::string &value) const +{ + switch (abiType) { + case ParameterType::abi_bytes: + { + dev::bytes rawData = dev::fromHex(data.substr(pos, HEX_INSTRUCTION_SIZE)); + dev::bytesConstRef o(&rawData); + std::string outData = dev::toString(dev::eth::ABIDeserialiser::deserialise(o)); + value = dev::toHex(outData); + } + break; + case ParameterType::abi_uint: + { + dev::bytes rawData = dev::fromHex(data.substr(pos, HEX_INSTRUCTION_SIZE)); + dev::bytesConstRef o(&rawData); + dev::u256 outData = dev::eth::ABIDeserialiser::deserialise(o); + value = outData.str(); + } + break; + case ParameterType::abi_int: + { + dev::bytes rawData = dev::fromHex(data.substr(pos, HEX_INSTRUCTION_SIZE)); + dev::bytesConstRef o(&rawData); + dev::s256 outData = dev::u2s(dev::eth::ABIDeserialiser::deserialise(o)); + value = outData.str(); + } + break; + case ParameterType::abi_address: + { + dev::bytes rawData = dev::fromHex(data.substr(pos, HEX_INSTRUCTION_SIZE)); + dev::bytesConstRef o(&rawData); + dev::u160 outData = dev::eth::ABIDeserialiser::deserialise(o); + dev::bytes rawAddress(20); + dev::toBigEndian(outData, rawAddress); + value = dev::toHex(rawAddress); + } + break; + case ParameterType::abi_bool: + { + dev::bytes rawData = dev::fromHex(data.substr(pos, HEX_INSTRUCTION_SIZE)); + dev::bytesConstRef o(&rawData); + dev::u256 outData = dev::eth::ABIDeserialiser::deserialise(o); + value = outData == 0 ? "false" : "true"; + } + break; + default: + m_lastError = UnsupportedABI; + return false; + } + + pos += HEX_INSTRUCTION_SIZE; + + return true; +} + +void ParameterABI::addDynamic(const std::string ¶mData, std::string &data, std::map &mapDynamic) const +{ + int key = data.size(); + data += paramData.substr(0, HEX_INSTRUCTION_SIZE); + mapDynamic[key] = paramData.substr(HEX_INSTRUCTION_SIZE); +} + +bool ParameterABI::abiIn(const std::vector &value, std::string &data, std::map& mapDynamic) const +{ + try + { + m_lastError = Ok; + ParameterType::Type abiType = decodeType().type(); + if(decodeType().isDynamic() && !decodeType().isList()) + { + // Dynamic basic type (list of bytes or chars) + std::string _value = value[0]; + switch (abiType) { + case ParameterType::abi_bytes: + _value = dev::asString(dev::fromHex(_value)); + case ParameterType::abi_string: + { + std::string paramData = dev::toHex(dev::eth::ABISerialiser::serialise(_value)); + addDynamic(paramData, data, mapDynamic); + } + break; + default: + m_lastError = UnsupportedABI; + return false; + } + } + else if(!decodeType().isDynamic() && !decodeType().isList()) + { + // Static basic type + abiInBasic(abiType, value[0], data); + } + else if(decodeType().isDynamic() && decodeType().isList()) + { + // Dynamic list type + std::string paramData; + abiInBasic(ParameterType::abi_uint, "32", paramData); + size_t length = value.size(); + abiInBasic(ParameterType::abi_uint, std::to_string(length), paramData); + for(size_t i = 0; i < length; i++) + { + abiInBasic(abiType, value[i], paramData); + } + addDynamic(paramData, data, mapDynamic); + } + else if(!decodeType().isDynamic() && decodeType().isList()) + { + // Static list type + size_t length = decodeType().length(); + for(size_t i = 0; i < length; i++) + { + abiInBasic(abiType, value[i], data); + } + } + else + { + // Unknown type + m_lastError = UnsupportedABI; + return false; + } + } + catch(...) + { + m_lastError = EncodingError; + return false; + } + + return true; +} + +bool ParameterABI::abiOut(const std::string &data, size_t &pos, std::vector &value) const +{ + try + { + m_lastError = Ok; + ParameterType::Type abiType = decodeType().type(); + if(decodeType().isDynamic() && !decodeType().isList()) + { + // Dynamic basic type + switch (abiType) { + case ParameterType::abi_bytes: + { + dev::bytes rawData = dev::fromHex(data.substr(pos)); + dev::bytesConstRef o(&rawData); + std::string outData = dev::eth::ABIDeserialiser::deserialise(o); + value.push_back(dev::toHex(outData)); + } + break; + case ParameterType::abi_string: + { + dev::bytes rawData = dev::fromHex(data.substr(pos)); + dev::bytesConstRef o(&rawData); + value.push_back(dev::eth::ABIDeserialiser::deserialise(o)); + } + break; + default: + m_lastError = UnsupportedABI; + return false; + } + + pos += HEX_INSTRUCTION_SIZE; + } + else if(!decodeType().isDynamic() && !decodeType().isList()) + { + // Static basic type + std::string paramValue; + if(abiOutBasic(abiType, data, pos, paramValue)) + { + value.push_back(paramValue); + } + else + { + return false; + } + } + else if(decodeType().isDynamic() && decodeType().isList()) + { + // Dynamic list type + + // Get position + std::string paramValue; + if(!abiOutBasic(ParameterType::abi_uint, data, pos, paramValue)) + return false; + size_t oldPos = pos; + pos = std::atoi(paramValue.c_str()) * 2; + + // Get length + if(!abiOutBasic(ParameterType::abi_uint, data, pos, paramValue)) + return false; + size_t length = std::atoi(paramValue.c_str()); + + // Read list + for(size_t i = 0; i < length; i++) + { + if(!abiOutBasic(abiType, data, pos, paramValue)) + return false; + value.push_back(paramValue); + } + + // Restore position + pos = oldPos; + } + else if(!decodeType().isDynamic() && decodeType().isList()) + { + // Static list type + std::string paramValue; + size_t length = decodeType().length(); + + // Read list + for(size_t i = 0; i < length; i++) + { + if(!abiOutBasic(abiType, data, pos, paramValue)) + return false; + value.push_back(paramValue); + } + } + else + { + // Unknown type + m_lastError = UnsupportedABI; + return false; + } + } + catch(...) + { + m_lastError = DecodingError; + return false; + } + + return true; +} + +bool ParameterABI::getRegularExpession(const ParameterType ¶mType, QRegularExpression ®Ex) +{ + bool ret = false; + switch (paramType.type()) { + case ParameterType::abi_bytes: + { + if(paramType.isDynamic()) + { + regEx.setPattern(paternBytes); + } + else + { + // Expression to check the number of bytes encoded in hex (1-32) + regEx.setPattern(QString(paternBytes32).arg(paramType.totalBytes()*2)); + } + ret = true; + break; + } + case ParameterType::abi_uint: + { + regEx.setPattern(paternUint); + ret = true; + break; + } + case ParameterType::abi_int: + { + regEx.setPattern(paternInt); + ret = true; + break; + } + case ParameterType::abi_address: + { + regEx.setPattern(paternAddress); + ret = true; + break; + } + case ParameterType::abi_bool: + { + regEx.setPattern(paternBool); + ret = true; + break; + } + default: + { + ret = false; + break; + } + } + return ret; +} + +ParameterABI::ErrorType ParameterABI::lastError() const +{ + return m_lastError; +} + +const ParameterType &ParameterABI::decodeType() const +{ + if(m_decodeType.canonical() != type) + { + m_decodeType = ParameterType(type); + } + + return m_decodeType; +} + +ParameterType::ParameterType(const std::string& _type): + m_type(ParameterType::abi_none), + m_whole(0), + m_decimal(0), + m_length(0), + m_isList(false), + m_valid(false) +{ + determine(_type); +} + +bool ParameterType::determine(const std::string &_type) +{ + clean(); + + // Initialize variables + bool ret = true; + size_t pos = 0; + + // Set string representation + m_canonical = _type; + + // Determine the basic type + if(startsWithString(m_canonical, "uint", pos)) + { + m_type = abi_uint; + } + else if(startsWithString(m_canonical, "int", pos)) + { + m_type = abi_int; + } + else if(startsWithString(m_canonical, "address", pos)) + { + m_type = abi_address; + } + else if(startsWithString(m_canonical, "bool", pos)) + { + m_type = abi_bool; + } + else if(startsWithString(m_canonical, "fixed", pos)) + { + m_type = abi_fixed; + } + else if(startsWithString(m_canonical, "ufixed", pos)) + { + m_type = abi_ufixed; + } + else if(startsWithString(m_canonical, "bytes", pos)) + { + m_type = abi_bytes; + } + else if(startsWithString(m_canonical, "string", pos)) + { + m_type = abi_string; + } + + // Provide more informations about the type + if(m_type != abi_none) + { + // Get the whole number part size + std::string strWhole = startsWithNumber(m_canonical, pos); + if(!strWhole.empty()) + { + m_whole = atoi(strWhole.c_str()); + } + + // Get the decimal number part size + if(startsWithString(m_canonical, "x", pos)) + { + std::string strDecimal = startsWithNumber(m_canonical, pos); + if(!strDecimal.empty()) + { + m_decimal = atoi(strDecimal.c_str()); + } + else + { + ret = false; + } + } + + // Get information for list type + if(startsWithString(m_canonical, "[", pos)) + { + std::string strLength = startsWithNumber(m_canonical, pos); + if(!strLength.empty()) + { + m_length = atoi(strLength.c_str()); + } + if(startsWithString(m_canonical, "]", pos)) + { + m_isList = true; + } + else + { + ret = false; + } + } + } + + // Return if not parsed correctly + if(m_canonical.length() != pos || ret == false) + ret = false; + + // Check the validity of the types + if(ret && (m_type == abi_int || m_type == abi_uint)) + { + ret &= m_whole > 0; + ret &= m_whole <= 256; + ret &= m_whole % 8 == 0; + ret &= m_decimal == 0; + } + + if(ret && m_type == abi_address) + { + ret &= m_whole == 0; + ret &= m_decimal == 0; + if(ret) m_whole = 160; + } + + if(ret && (m_type == abi_fixed || m_type == abi_ufixed)) + { + ret &= m_whole > 0; + ret &= m_decimal > 0; + ret &= (m_whole + m_decimal) <= 256; + } + + if(ret && m_type == abi_bytes) + { + m_whole *= 8; + ret &= m_whole > 0; + ret &= m_whole <= 256; + ret &= m_decimal == 0; + } + + if(ret && m_type == abi_function) + { + ret &= m_whole == 0; + ret &= m_decimal == 0; + if(ret) m_whole = 196; + } + + if(ret && m_type == abi_string) + { + ret &= m_whole == 0; + ret &= m_decimal == 0; + } + + m_valid = ret; + + return m_valid; +} + +size_t ParameterType::wholeBits() const +{ + return m_whole; +} + +size_t ParameterType::decimalBits() const +{ + return m_decimal; +} + +size_t ParameterType::totalBytes() const +{ + return ceil((m_whole + m_decimal) / 8.0); +} + +size_t ParameterType::length() const +{ + return m_length; +} + +bool ParameterType::isList() const +{ + return m_isList; +} + +bool ParameterType::isDynamic() const +{ + // Type bytes is dynamic when the count of bytes is not known. + // Type string is always dynamic. + if((m_type == abi_bytes && totalBytes() == 0) || m_type == abi_string) + { + return true; + } + + // Type list is dynamic when the count of elements is not known. + if(m_isList) + return m_length == 0; + + return false; +} + +bool ParameterType::isValid() const +{ + return m_valid; +} + +const std::string& ParameterType::canonical() const +{ + return m_canonical; +} + +void ParameterType::clean() +{ + m_type = ParameterType::abi_none; + m_whole = 0; + m_decimal = 0; + m_length = 0; + m_isList = false; + m_valid = false; + m_canonical = ""; +} + +ParameterType::Type ParameterType::type() const +{ + return m_type; +} diff --git a/src/qt/contractbookpage.cpp b/src/qt/contractbookpage.cpp index 2cff2347f4..fbc6858498 100644 --- a/src/qt/contractbookpage.cpp +++ b/src/qt/contractbookpage.cpp @@ -1,268 +1,268 @@ -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -ContractBookPage::ContractBookPage(const PlatformStyle *platformStyle, QWidget *parent) : - QDialog(parent), - ui(new Ui::ContractBookPage), - model(0) -{ - ui->setupUi(this); - - SetObjectStyleSheet(ui->tableView, StyleSheetNames::TableViewLight); - - if (!platformStyle->getImagesOnButtons()) { - ui->newContractInfo->setIcon(QIcon()); - ui->copyAddress->setIcon(QIcon()); - ui->deleteContractInfo->setIcon(QIcon()); - ui->exportButton->setIcon(QIcon()); - } else { - ui->newContractInfo->setIcon(platformStyle->MultiStatesIcon(":/icons/add", PlatformStyle::PushButton, 0x5a5a5d)); - ui->copyAddress->setIcon(platformStyle->MultiStatesIcon(":/icons/editcopy", PlatformStyle::PushButton, 0x5a5a5d)); - ui->deleteContractInfo->setIcon(platformStyle->MultiStatesIcon(":/icons/remove", PlatformStyle::PushButton, 0x5a5a5d)); - ui->exportButton->setIcon(platformStyle->MultiStatesIcon(":/icons/export")); - } - - SetObjectStyleSheet(ui->newContractInfo, StyleSheetNames::ButtonWhite); - SetObjectStyleSheet(ui->copyAddress, StyleSheetNames::ButtonWhite); - SetObjectStyleSheet(ui->deleteContractInfo, StyleSheetNames::ButtonWhite); - SetObjectStyleSheet(ui->exportButton, StyleSheetNames::ButtonBlue); - SetObjectStyleSheet(ui->chooseContractInfo, StyleSheetNames::ButtonBlue); - - setWindowTitle(tr("Choose the contract for send/call")); - ui->labelExplanation->setText(tr("These are your saved contracts. Always check the contract address and the ABI before sending/calling.")); - - // Context menu actions - QAction *copyAddressAction = new QAction(tr("Copy &Address"), this); - QAction *copyNameAction = new QAction(tr("Copy &Name"), this); - QAction *copyABIAction = new QAction(tr("Copy &Interface"), this); - QAction *editAction = new QAction(tr("&Edit"), this); - QAction *deleteAction = new QAction(tr("&Delete"), this); - - // Build context menu - contextMenu = new QMenu(this); - contextMenu->addAction(copyAddressAction); - contextMenu->addAction(copyNameAction); - contextMenu->addAction(copyABIAction); - contextMenu->addAction(editAction); - contextMenu->addAction(deleteAction); - contextMenu->addSeparator(); - - // Connect signals for context menu actions - connect(copyAddressAction, &QAction::triggered, this, &ContractBookPage::on_copyAddress_clicked); - connect(copyNameAction, &QAction::triggered, this, &ContractBookPage::onCopyNameAction); - connect(copyABIAction, &QAction::triggered, this, &ContractBookPage::onCopyABIAction); - connect(editAction, &QAction::triggered, this, &ContractBookPage::onEditAction); - connect(deleteAction, &QAction::triggered, this, &ContractBookPage::on_deleteContractInfo_clicked); - - connect(ui->tableView, &QTableView::doubleClicked, this, &ContractBookPage::accept); - connect(ui->tableView, &QTableView::customContextMenuRequested, this, &ContractBookPage::contextualMenu); - - connect(ui->chooseContractInfo, &QPushButton::clicked, this, &ContractBookPage::accept); -} - -ContractBookPage::~ContractBookPage() -{ - delete ui; -} - -void ContractBookPage::setModel(ContractTableModel *_model) -{ - this->model = _model; - if(!_model) - return; - - proxyModel = new QSortFilterProxyModel(this); - proxyModel->setSourceModel(_model); - proxyModel->setDynamicSortFilter(true); - proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); - - ui->tableView->setModel(proxyModel); - ui->tableView->sortByColumn(0, Qt::AscendingOrder); - - // Set column widths - ui->tableView->setColumnWidth(ContractTableModel::Label, ColumnWidths::LABEL_COLUMN_WIDTH); - ui->tableView->setColumnWidth(ContractTableModel::Address, ColumnWidths::ADDRESS_COLUMN_WIDTH); -#if QT_VERSION < 0x050000 - ui->tableView->horizontalHeader()->setResizeMode(ContractTableModel::Label, QHeaderView::Fixed); - ui->tableView->horizontalHeader()->setResizeMode(ContractTableModel::Address, QHeaderView::Fixed); - ui->tableView->horizontalHeader()->setResizeMode(ContractTableModel::ABI, QHeaderView::Stretch); -#else - ui->tableView->horizontalHeader()->setSectionResizeMode(ContractTableModel::Label, QHeaderView::Fixed); - ui->tableView->horizontalHeader()->setSectionResizeMode(ContractTableModel::Address, QHeaderView::Fixed); - ui->tableView->horizontalHeader()->setSectionResizeMode(ContractTableModel::ABI, QHeaderView::Stretch); -#endif - - connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ContractBookPage::selectionChanged); - - // Select row for newly created contract info - connect(_model, &ContractTableModel::rowsInserted, this, &ContractBookPage::selectNewContractInfo); - - selectionChanged(); -} - -void ContractBookPage::onCopyNameAction() -{ - GUIUtil::copyEntryData(ui->tableView, ContractTableModel::Label); -} - -void ContractBookPage::onCopyABIAction() -{ - GUIUtil::copyEntryData(ui->tableView, ContractTableModel::ABI); -} - -void ContractBookPage::on_copyAddress_clicked() -{ - GUIUtil::copyEntryData(ui->tableView, ContractTableModel::Address); -} - -void ContractBookPage::onEditAction() -{ - if(!model) - return; - - if(!ui->tableView->selectionModel()) - return; - QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows(); - if(indexes.isEmpty()) - return; - - EditContractInfoDialog dlg(EditContractInfoDialog::EditContractInfo, this); - dlg.setModel(model); - QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0)); - dlg.loadRow(origIndex.row()); - dlg.exec(); -} - -void ContractBookPage::on_newContractInfo_clicked() -{ - if(!model) - return; - - EditContractInfoDialog dlg(EditContractInfoDialog::NewContractInfo, this); - dlg.setModel(model); - if(dlg.exec()) - { - newContractInfoToSelect = dlg.getAddress(); - } -} - -void ContractBookPage::on_deleteContractInfo_clicked() -{ - QTableView *table = ui->tableView; - if(!table->selectionModel()) - return; - - QModelIndexList indexes = table->selectionModel()->selectedRows(); - if(!indexes.isEmpty()) - { - int row = indexes.at(0).row(); - QModelIndex index = table->model()->index(row, ContractTableModel::Address); - QString contractAddress = table->model()->data(index).toString(); - QString message = tr("Are you sure you want to delete the address \"%1\" from your contract address list?"); - if(QMessageBox::Yes == QMessageBox::question(this, tr("Delete contact address"), message.arg(contractAddress), - QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) - { - table->model()->removeRow(row); - } - } -} - -void ContractBookPage::selectionChanged() -{ - // Set button states based on selection - QTableView *table = ui->tableView; - if(!table->selectionModel()) - return; - - if(table->selectionModel()->hasSelection()) - { - ui->deleteContractInfo->setEnabled(true); - ui->copyAddress->setEnabled(true); - } - else - { - ui->deleteContractInfo->setEnabled(false); - ui->copyAddress->setEnabled(false); - } -} - -void ContractBookPage::done(int retval) -{ - QTableView *table = ui->tableView; - if(!table->selectionModel() || !table->model()) - return; - - // Figure out which contract info was selected, and return it - QModelIndexList indexes = table->selectionModel()->selectedRows(ContractTableModel::Address); - - Q_FOREACH (const QModelIndex& index, indexes) { - QVariant address = table->model()->data(index); - QVariant ABI = table->model()->index(index.row(), ContractTableModel::ABI).data(); - addressValue = address.toString(); - ABIValue = ABI.toString(); - } - - if(addressValue.isEmpty()) - { - // If no contract info entry selected, return rejected - retval = Rejected; - } - - QDialog::done(retval); -} - -void ContractBookPage::on_exportButton_clicked() -{ - // CSV is currently the only supported format - QString filename = GUIUtil::getSaveFileName(this, - tr("Export Contract List"), QString(), - tr("Comma separated file (*.csv)"), NULL); - - if (filename.isNull()) - return; - - CSVModelWriter writer(filename); - - // name, column, role - writer.setModel(proxyModel); - writer.addColumn("Label", ContractTableModel::Label, Qt::EditRole); - writer.addColumn("Address", ContractTableModel::Address, Qt::EditRole); - writer.addColumn("ABI", ContractTableModel::ABI, Qt::EditRole); - - if(!writer.write()) { - QMessageBox::critical(this, tr("Exporting Failed"), - tr("There was an error trying to save the address list to %1. Please try again.").arg(filename)); - } -} - -void ContractBookPage::contextualMenu(const QPoint &point) -{ - QModelIndex index = ui->tableView->indexAt(point); - if(index.isValid()) - { - contextMenu->exec(QCursor::pos()); - } -} - -void ContractBookPage::selectNewContractInfo(const QModelIndex &parent, int begin, int) -{ - QModelIndex idx = proxyModel->mapFromSource(model->index(begin, ContractTableModel::Address, parent)); - if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newContractInfoToSelect)) - { - // Select row of newly created address, once - ui->tableView->setFocus(); - ui->tableView->selectRow(idx.row()); - newContractInfoToSelect.clear(); - } -} +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +ContractBookPage::ContractBookPage(const PlatformStyle *platformStyle, QWidget *parent) : + QDialog(parent), + ui(new Ui::ContractBookPage), + model(0) +{ + ui->setupUi(this); + + SetObjectStyleSheet(ui->tableView, StyleSheetNames::TableViewLight); + + if (!platformStyle->getImagesOnButtons()) { + ui->newContractInfo->setIcon(QIcon()); + ui->copyAddress->setIcon(QIcon()); + ui->deleteContractInfo->setIcon(QIcon()); + ui->exportButton->setIcon(QIcon()); + } else { + ui->newContractInfo->setIcon(platformStyle->MultiStatesIcon(":/icons/add", PlatformStyle::PushButton, 0x5a5a5d)); + ui->copyAddress->setIcon(platformStyle->MultiStatesIcon(":/icons/editcopy", PlatformStyle::PushButton, 0x5a5a5d)); + ui->deleteContractInfo->setIcon(platformStyle->MultiStatesIcon(":/icons/remove", PlatformStyle::PushButton, 0x5a5a5d)); + ui->exportButton->setIcon(platformStyle->MultiStatesIcon(":/icons/export")); + } + + SetObjectStyleSheet(ui->newContractInfo, StyleSheetNames::ButtonWhite); + SetObjectStyleSheet(ui->copyAddress, StyleSheetNames::ButtonWhite); + SetObjectStyleSheet(ui->deleteContractInfo, StyleSheetNames::ButtonWhite); + SetObjectStyleSheet(ui->exportButton, StyleSheetNames::ButtonBlue); + SetObjectStyleSheet(ui->chooseContractInfo, StyleSheetNames::ButtonBlue); + + setWindowTitle(tr("Choose the contract for send/call")); + ui->labelExplanation->setText(tr("These are your saved contracts. Always check the contract address and the ABI before sending/calling.")); + + // Context menu actions + QAction *copyAddressAction = new QAction(tr("Copy &Address"), this); + QAction *copyNameAction = new QAction(tr("Copy &Name"), this); + QAction *copyABIAction = new QAction(tr("Copy &Interface"), this); + QAction *editAction = new QAction(tr("&Edit"), this); + QAction *deleteAction = new QAction(tr("&Delete"), this); + + // Build context menu + contextMenu = new QMenu(this); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyNameAction); + contextMenu->addAction(copyABIAction); + contextMenu->addAction(editAction); + contextMenu->addAction(deleteAction); + contextMenu->addSeparator(); + + // Connect signals for context menu actions + connect(copyAddressAction, &QAction::triggered, this, &ContractBookPage::on_copyAddress_clicked); + connect(copyNameAction, &QAction::triggered, this, &ContractBookPage::onCopyNameAction); + connect(copyABIAction, &QAction::triggered, this, &ContractBookPage::onCopyABIAction); + connect(editAction, &QAction::triggered, this, &ContractBookPage::onEditAction); + connect(deleteAction, &QAction::triggered, this, &ContractBookPage::on_deleteContractInfo_clicked); + + connect(ui->tableView, &QTableView::doubleClicked, this, &ContractBookPage::accept); + connect(ui->tableView, &QTableView::customContextMenuRequested, this, &ContractBookPage::contextualMenu); + + connect(ui->chooseContractInfo, &QPushButton::clicked, this, &ContractBookPage::accept); +} + +ContractBookPage::~ContractBookPage() +{ + delete ui; +} + +void ContractBookPage::setModel(ContractTableModel *_model) +{ + this->model = _model; + if(!_model) + return; + + proxyModel = new QSortFilterProxyModel(this); + proxyModel->setSourceModel(_model); + proxyModel->setDynamicSortFilter(true); + proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + + ui->tableView->setModel(proxyModel); + ui->tableView->sortByColumn(0, Qt::AscendingOrder); + + // Set column widths + ui->tableView->setColumnWidth(ContractTableModel::Label, ColumnWidths::LABEL_COLUMN_WIDTH); + ui->tableView->setColumnWidth(ContractTableModel::Address, ColumnWidths::ADDRESS_COLUMN_WIDTH); +#if QT_VERSION < 0x050000 + ui->tableView->horizontalHeader()->setResizeMode(ContractTableModel::Label, QHeaderView::Fixed); + ui->tableView->horizontalHeader()->setResizeMode(ContractTableModel::Address, QHeaderView::Fixed); + ui->tableView->horizontalHeader()->setResizeMode(ContractTableModel::ABI, QHeaderView::Stretch); +#else + ui->tableView->horizontalHeader()->setSectionResizeMode(ContractTableModel::Label, QHeaderView::Fixed); + ui->tableView->horizontalHeader()->setSectionResizeMode(ContractTableModel::Address, QHeaderView::Fixed); + ui->tableView->horizontalHeader()->setSectionResizeMode(ContractTableModel::ABI, QHeaderView::Stretch); +#endif + + connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ContractBookPage::selectionChanged); + + // Select row for newly created contract info + connect(_model, &ContractTableModel::rowsInserted, this, &ContractBookPage::selectNewContractInfo); + + selectionChanged(); +} + +void ContractBookPage::onCopyNameAction() +{ + GUIUtil::copyEntryData(ui->tableView, ContractTableModel::Label); +} + +void ContractBookPage::onCopyABIAction() +{ + GUIUtil::copyEntryData(ui->tableView, ContractTableModel::ABI); +} + +void ContractBookPage::on_copyAddress_clicked() +{ + GUIUtil::copyEntryData(ui->tableView, ContractTableModel::Address); +} + +void ContractBookPage::onEditAction() +{ + if(!model) + return; + + if(!ui->tableView->selectionModel()) + return; + QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows(); + if(indexes.isEmpty()) + return; + + EditContractInfoDialog dlg(EditContractInfoDialog::EditContractInfo, this); + dlg.setModel(model); + QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0)); + dlg.loadRow(origIndex.row()); + dlg.exec(); +} + +void ContractBookPage::on_newContractInfo_clicked() +{ + if(!model) + return; + + EditContractInfoDialog dlg(EditContractInfoDialog::NewContractInfo, this); + dlg.setModel(model); + if(dlg.exec()) + { + newContractInfoToSelect = dlg.getAddress(); + } +} + +void ContractBookPage::on_deleteContractInfo_clicked() +{ + QTableView *table = ui->tableView; + if(!table->selectionModel()) + return; + + QModelIndexList indexes = table->selectionModel()->selectedRows(); + if(!indexes.isEmpty()) + { + int row = indexes.at(0).row(); + QModelIndex index = table->model()->index(row, ContractTableModel::Address); + QString contractAddress = table->model()->data(index).toString(); + QString message = tr("Are you sure you want to delete the address \"%1\" from your contract address list?"); + if(QMessageBox::Yes == QMessageBox::question(this, tr("Delete contact address"), message.arg(contractAddress), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) + { + table->model()->removeRow(row); + } + } +} + +void ContractBookPage::selectionChanged() +{ + // Set button states based on selection + QTableView *table = ui->tableView; + if(!table->selectionModel()) + return; + + if(table->selectionModel()->hasSelection()) + { + ui->deleteContractInfo->setEnabled(true); + ui->copyAddress->setEnabled(true); + } + else + { + ui->deleteContractInfo->setEnabled(false); + ui->copyAddress->setEnabled(false); + } +} + +void ContractBookPage::done(int retval) +{ + QTableView *table = ui->tableView; + if(!table->selectionModel() || !table->model()) + return; + + // Figure out which contract info was selected, and return it + QModelIndexList indexes = table->selectionModel()->selectedRows(ContractTableModel::Address); + + Q_FOREACH (const QModelIndex& index, indexes) { + QVariant address = table->model()->data(index); + QVariant ABI = table->model()->index(index.row(), ContractTableModel::ABI).data(); + addressValue = address.toString(); + ABIValue = ABI.toString(); + } + + if(addressValue.isEmpty()) + { + // If no contract info entry selected, return rejected + retval = Rejected; + } + + QDialog::done(retval); +} + +void ContractBookPage::on_exportButton_clicked() +{ + // CSV is currently the only supported format + QString filename = GUIUtil::getSaveFileName(this, + tr("Export Contract List"), QString(), + tr("Comma separated file (*.csv)"), NULL); + + if (filename.isNull()) + return; + + CSVModelWriter writer(filename); + + // name, column, role + writer.setModel(proxyModel); + writer.addColumn("Label", ContractTableModel::Label, Qt::EditRole); + writer.addColumn("Address", ContractTableModel::Address, Qt::EditRole); + writer.addColumn("ABI", ContractTableModel::ABI, Qt::EditRole); + + if(!writer.write()) { + QMessageBox::critical(this, tr("Exporting Failed"), + tr("There was an error trying to save the address list to %1. Please try again.").arg(filename)); + } +} + +void ContractBookPage::contextualMenu(const QPoint &point) +{ + QModelIndex index = ui->tableView->indexAt(point); + if(index.isValid()) + { + contextMenu->exec(QCursor::pos()); + } +} + +void ContractBookPage::selectNewContractInfo(const QModelIndex &parent, int begin, int) +{ + QModelIndex idx = proxyModel->mapFromSource(model->index(begin, ContractTableModel::Address, parent)); + if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newContractInfoToSelect)) + { + // Select row of newly created address, once + ui->tableView->setFocus(); + ui->tableView->selectRow(idx.row()); + newContractInfoToSelect.clear(); + } +} diff --git a/src/qt/contractresult.cpp b/src/qt/contractresult.cpp index f3d5fcaa80..10d40d58c7 100644 --- a/src/qt/contractresult.cpp +++ b/src/qt/contractresult.cpp @@ -1,253 +1,253 @@ -#include -#include -#include -#include -#include - -#include - -ContractResult::ContractResult(QWidget *parent) : - QStackedWidget(parent), - ui(new Ui::ContractResult) -{ - ui->setupUi(this); -} - -ContractResult::~ContractResult() -{ - delete ui; -} - -void ContractResult::setResultData(QVariant result, FunctionABI function, QList paramValues, ContractTxType type) -{ - switch (type) { - case CreateResult: - updateCreateResult(result); - setCurrentWidget(ui->pageCreateOrSendToResult); - break; - - case SendToResult: - updateSendToResult(result); - setCurrentWidget(ui->pageCreateOrSendToResult); - break; - - case CallResult: - updateCallResult(result, function, paramValues); - setCurrentWidget(ui->pageCallResult); - break; - - default: - break; - } -} - -void ContractResult::setParamsData(FunctionABI function, QList paramValues) -{ - // Remove previous widget from scroll area - QWidget *scrollWidget = ui->scrollAreaParams->widget(); - if(scrollWidget) - scrollWidget->deleteLater(); - - // Don't show empty list - if(function.inputs.size() == 0) - { - ui->scrollAreaParams->setVisible(false); - return; - } - - QWidget *widgetParams = new QWidget(this); - widgetParams->setObjectName("scrollAreaWidgetContents"); - - QVBoxLayout *mainLayout = new QVBoxLayout(widgetParams); - mainLayout->setSpacing(6); - mainLayout->setContentsMargins(0,0,30,0); - - // Add rows with params and values sent - int i = 0; - for(std::vector::const_iterator param = function.inputs.begin(); param != function.inputs.end(); ++param) - { - QHBoxLayout *hLayout = new QHBoxLayout(); - hLayout->setSpacing(10); - hLayout->setContentsMargins(0,0,0,0); - QVBoxLayout *vNameLayout = new QVBoxLayout(); - vNameLayout->setSpacing(3); - vNameLayout->setContentsMargins(0,0,0,0); - QVBoxLayout *paramValuesLayout = new QVBoxLayout(); - paramValuesLayout->setSpacing(3); - paramValuesLayout->setContentsMargins(0,0,0,0); - - QLabel *paramName = new QLabel(this); - paramName->setFixedWidth(160); - paramName->setFixedHeight(19); - QFontMetrics metrix(paramName->font()); - int width = paramName->width() + 10; - QString text(QString("%2 %1").arg(QString::fromStdString(param->name)).arg(QString::fromStdString(param->type))); - QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); - paramName->setText(clippedText); - paramName->setToolTip(QString("%2 %1").arg(QString::fromStdString(param->name)).arg(QString::fromStdString(param->type))); - - vNameLayout->addWidget(paramName); - hLayout->addLayout(vNameLayout); - QStringList listValues = paramValues[i]; - if(listValues.size() > 0) - { - int spacerSize = 0; - for(int j = 0; j < listValues.count(); j++) - { - QLineEdit *paramValue = new QLineEdit(this); - paramValue->setReadOnly(true); - paramValue->setText(listValues[j]); - paramValuesLayout->addWidget(paramValue); - if(j > 0) - spacerSize += 30; // Line edit height + spacing - } - if(spacerSize > 0) - vNameLayout->addSpacerItem(new QSpacerItem(20, spacerSize, QSizePolicy::Fixed, QSizePolicy::Fixed)); - - hLayout->addLayout(paramValuesLayout); - } - else - { - hLayout->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Fixed)); - } - - mainLayout->addLayout(hLayout); - i++; - } - widgetParams->setLayout(mainLayout); - widgetParams->adjustSize(); - if(widgetParams->sizeHint().height() < 70) - ui->scrollAreaParams->setMaximumHeight(widgetParams->sizeHint().height() + 2); - else - ui->scrollAreaParams->setMaximumHeight(140); - ui->scrollAreaParams->setWidget(widgetParams); - ui->scrollAreaParams->setVisible(true); -} - -void ContractResult::updateCreateResult(QVariant result) -{ - ui->lineEditContractAddress->setVisible(true); - ui->labelContractAddress->setVisible(true); - - QVariantMap variantMap = result.toMap(); - - ui->lineEditTxID->setText(variantMap.value("txid").toString()); - ui->lineEditSenderAddress->setText(variantMap.value("sender").toString()); - ui->lineEditHash160->setText(variantMap.value("hash160").toString()); - ui->lineEditContractAddress->setText(variantMap.value("address").toString()); -} - -void ContractResult::updateSendToResult(QVariant result) -{ - ui->lineEditContractAddress->setVisible(false); - ui->labelContractAddress->setVisible(false); - - QVariantMap variantMap = result.toMap(); - - ui->lineEditTxID->setText(variantMap.value("txid").toString()); - ui->lineEditSenderAddress->setText(variantMap.value("sender").toString()); - ui->lineEditHash160->setText(variantMap.value("hash160").toString()); -} - -void ContractResult::updateCallResult(QVariant result, FunctionABI function, QList paramValues) -{ - QVariantMap variantMap = result.toMap(); - QVariantMap executionResultMap = variantMap.value("executionResult").toMap(); - - ui->lineEditCallContractAddress->setText(variantMap.value("address").toString()); - ui->lineEditFunction->setText(QString::fromStdString(function.name) + "()"); - - setParamsData(function, paramValues); - - ui->lineEditCallSenderAddress->setText(variantMap.value("sender").toString()); - std::string rawData = executionResultMap.value("output").toString().toStdString(); - std::vector> values; - std::vector errors; - if(function.abiOut(rawData, values, errors)) - { - // Remove previous widget from scroll area - QWidget *scrollWidget = ui->scrollAreaResult->widget(); - if(scrollWidget) - scrollWidget->deleteLater(); - - if(values.size() > 0) - { - QWidget *widgetResults = new QWidget(this); - widgetResults->setObjectName("scrollAreaWidgetContents"); - - QVBoxLayout *mainLayout = new QVBoxLayout(widgetResults); - mainLayout->setSpacing(6); - mainLayout->setContentsMargins(0,6,0,6); - widgetResults->setLayout(mainLayout); - - for(size_t i = 0; i < values.size(); i++) - { - QHBoxLayout *hLayout = new QHBoxLayout(); - hLayout->setSpacing(10); - hLayout->setContentsMargins(0,0,0,0); - QVBoxLayout *vNameLayout = new QVBoxLayout(); - vNameLayout->setSpacing(3); - vNameLayout->setContentsMargins(0,0,0,0); - QVBoxLayout *paramValuesLayout = new QVBoxLayout(); - paramValuesLayout->setSpacing(3); - paramValuesLayout->setContentsMargins(0,0,0,0); - - QLabel *resultName = new QLabel(this); - resultName->setFixedWidth(160); - resultName->setFixedHeight(19); - QFontMetrics metrix(resultName->font()); - int width = resultName->width() + 10; - QString text(QString("%2 %1").arg(QString::fromStdString(function.outputs[i].name)).arg(QString::fromStdString(function.outputs[i].type))); - QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); - resultName->setText(clippedText); - resultName->setToolTip(QString("%2 %1").arg(QString::fromStdString(function.outputs[i].name)).arg(QString::fromStdString(function.outputs[i].type))); - - vNameLayout->addWidget(resultName); - std::vector listValues = values[i]; - hLayout->addLayout(vNameLayout); - if(listValues.size() > 0) - { - int spacerSize = 0; - for(size_t j = 0; j < listValues.size(); j++) - { - QLineEdit *resultValue = new QLineEdit(this); - resultValue->setReadOnly(true); - resultValue->setText(QString::fromStdString(listValues[j])); - paramValuesLayout->addWidget(resultValue); - if(j > 0) - spacerSize += 30; // Line edit height + spacing - } - if(spacerSize > 0) - vNameLayout->addSpacerItem(new QSpacerItem(20, spacerSize, QSizePolicy::Fixed, QSizePolicy::Fixed)); - hLayout->addLayout(paramValuesLayout); - } - else - { - hLayout->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Fixed)); - } - mainLayout->addLayout(hLayout); - } - widgetResults->adjustSize(); - if(widgetResults->sizeHint().height() < 70) - { - ui->scrollAreaResult->setMaximumHeight(widgetResults->sizeHint().height() + 2); - } - else - { - ui->scrollAreaResult->setMaximumHeight(140); - } - ui->scrollAreaResult->setWidget(widgetResults); - ui->groupBoxResult->setVisible(true); - } - else - { - ui->groupBoxResult->setVisible(false); - } - } - else - { - QString errorMessage; - errorMessage = function.errorMessage(errors, false); - QMessageBox::warning(this, tr("Create contract"), errorMessage); - } -} +#include +#include +#include +#include +#include + +#include + +ContractResult::ContractResult(QWidget *parent) : + QStackedWidget(parent), + ui(new Ui::ContractResult) +{ + ui->setupUi(this); +} + +ContractResult::~ContractResult() +{ + delete ui; +} + +void ContractResult::setResultData(QVariant result, FunctionABI function, QList paramValues, ContractTxType type) +{ + switch (type) { + case CreateResult: + updateCreateResult(result); + setCurrentWidget(ui->pageCreateOrSendToResult); + break; + + case SendToResult: + updateSendToResult(result); + setCurrentWidget(ui->pageCreateOrSendToResult); + break; + + case CallResult: + updateCallResult(result, function, paramValues); + setCurrentWidget(ui->pageCallResult); + break; + + default: + break; + } +} + +void ContractResult::setParamsData(FunctionABI function, QList paramValues) +{ + // Remove previous widget from scroll area + QWidget *scrollWidget = ui->scrollAreaParams->widget(); + if(scrollWidget) + scrollWidget->deleteLater(); + + // Don't show empty list + if(function.inputs.size() == 0) + { + ui->scrollAreaParams->setVisible(false); + return; + } + + QWidget *widgetParams = new QWidget(this); + widgetParams->setObjectName("scrollAreaWidgetContents"); + + QVBoxLayout *mainLayout = new QVBoxLayout(widgetParams); + mainLayout->setSpacing(6); + mainLayout->setContentsMargins(0,0,30,0); + + // Add rows with params and values sent + int i = 0; + for(std::vector::const_iterator param = function.inputs.begin(); param != function.inputs.end(); ++param) + { + QHBoxLayout *hLayout = new QHBoxLayout(); + hLayout->setSpacing(10); + hLayout->setContentsMargins(0,0,0,0); + QVBoxLayout *vNameLayout = new QVBoxLayout(); + vNameLayout->setSpacing(3); + vNameLayout->setContentsMargins(0,0,0,0); + QVBoxLayout *paramValuesLayout = new QVBoxLayout(); + paramValuesLayout->setSpacing(3); + paramValuesLayout->setContentsMargins(0,0,0,0); + + QLabel *paramName = new QLabel(this); + paramName->setFixedWidth(160); + paramName->setFixedHeight(19); + QFontMetrics metrix(paramName->font()); + int width = paramName->width() + 10; + QString text(QString("%2 %1").arg(QString::fromStdString(param->name)).arg(QString::fromStdString(param->type))); + QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); + paramName->setText(clippedText); + paramName->setToolTip(QString("%2 %1").arg(QString::fromStdString(param->name)).arg(QString::fromStdString(param->type))); + + vNameLayout->addWidget(paramName); + hLayout->addLayout(vNameLayout); + QStringList listValues = paramValues[i]; + if(listValues.size() > 0) + { + int spacerSize = 0; + for(int j = 0; j < listValues.count(); j++) + { + QLineEdit *paramValue = new QLineEdit(this); + paramValue->setReadOnly(true); + paramValue->setText(listValues[j]); + paramValuesLayout->addWidget(paramValue); + if(j > 0) + spacerSize += 30; // Line edit height + spacing + } + if(spacerSize > 0) + vNameLayout->addSpacerItem(new QSpacerItem(20, spacerSize, QSizePolicy::Fixed, QSizePolicy::Fixed)); + + hLayout->addLayout(paramValuesLayout); + } + else + { + hLayout->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Fixed)); + } + + mainLayout->addLayout(hLayout); + i++; + } + widgetParams->setLayout(mainLayout); + widgetParams->adjustSize(); + if(widgetParams->sizeHint().height() < 70) + ui->scrollAreaParams->setMaximumHeight(widgetParams->sizeHint().height() + 2); + else + ui->scrollAreaParams->setMaximumHeight(140); + ui->scrollAreaParams->setWidget(widgetParams); + ui->scrollAreaParams->setVisible(true); +} + +void ContractResult::updateCreateResult(QVariant result) +{ + ui->lineEditContractAddress->setVisible(true); + ui->labelContractAddress->setVisible(true); + + QVariantMap variantMap = result.toMap(); + + ui->lineEditTxID->setText(variantMap.value("txid").toString()); + ui->lineEditSenderAddress->setText(variantMap.value("sender").toString()); + ui->lineEditHash160->setText(variantMap.value("hash160").toString()); + ui->lineEditContractAddress->setText(variantMap.value("address").toString()); +} + +void ContractResult::updateSendToResult(QVariant result) +{ + ui->lineEditContractAddress->setVisible(false); + ui->labelContractAddress->setVisible(false); + + QVariantMap variantMap = result.toMap(); + + ui->lineEditTxID->setText(variantMap.value("txid").toString()); + ui->lineEditSenderAddress->setText(variantMap.value("sender").toString()); + ui->lineEditHash160->setText(variantMap.value("hash160").toString()); +} + +void ContractResult::updateCallResult(QVariant result, FunctionABI function, QList paramValues) +{ + QVariantMap variantMap = result.toMap(); + QVariantMap executionResultMap = variantMap.value("executionResult").toMap(); + + ui->lineEditCallContractAddress->setText(variantMap.value("address").toString()); + ui->lineEditFunction->setText(QString::fromStdString(function.name) + "()"); + + setParamsData(function, paramValues); + + ui->lineEditCallSenderAddress->setText(variantMap.value("sender").toString()); + std::string rawData = executionResultMap.value("output").toString().toStdString(); + std::vector> values; + std::vector errors; + if(function.abiOut(rawData, values, errors)) + { + // Remove previous widget from scroll area + QWidget *scrollWidget = ui->scrollAreaResult->widget(); + if(scrollWidget) + scrollWidget->deleteLater(); + + if(values.size() > 0) + { + QWidget *widgetResults = new QWidget(this); + widgetResults->setObjectName("scrollAreaWidgetContents"); + + QVBoxLayout *mainLayout = new QVBoxLayout(widgetResults); + mainLayout->setSpacing(6); + mainLayout->setContentsMargins(0,6,0,6); + widgetResults->setLayout(mainLayout); + + for(size_t i = 0; i < values.size(); i++) + { + QHBoxLayout *hLayout = new QHBoxLayout(); + hLayout->setSpacing(10); + hLayout->setContentsMargins(0,0,0,0); + QVBoxLayout *vNameLayout = new QVBoxLayout(); + vNameLayout->setSpacing(3); + vNameLayout->setContentsMargins(0,0,0,0); + QVBoxLayout *paramValuesLayout = new QVBoxLayout(); + paramValuesLayout->setSpacing(3); + paramValuesLayout->setContentsMargins(0,0,0,0); + + QLabel *resultName = new QLabel(this); + resultName->setFixedWidth(160); + resultName->setFixedHeight(19); + QFontMetrics metrix(resultName->font()); + int width = resultName->width() + 10; + QString text(QString("%2 %1").arg(QString::fromStdString(function.outputs[i].name)).arg(QString::fromStdString(function.outputs[i].type))); + QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); + resultName->setText(clippedText); + resultName->setToolTip(QString("%2 %1").arg(QString::fromStdString(function.outputs[i].name)).arg(QString::fromStdString(function.outputs[i].type))); + + vNameLayout->addWidget(resultName); + std::vector listValues = values[i]; + hLayout->addLayout(vNameLayout); + if(listValues.size() > 0) + { + int spacerSize = 0; + for(size_t j = 0; j < listValues.size(); j++) + { + QLineEdit *resultValue = new QLineEdit(this); + resultValue->setReadOnly(true); + resultValue->setText(QString::fromStdString(listValues[j])); + paramValuesLayout->addWidget(resultValue); + if(j > 0) + spacerSize += 30; // Line edit height + spacing + } + if(spacerSize > 0) + vNameLayout->addSpacerItem(new QSpacerItem(20, spacerSize, QSizePolicy::Fixed, QSizePolicy::Fixed)); + hLayout->addLayout(paramValuesLayout); + } + else + { + hLayout->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Fixed)); + } + mainLayout->addLayout(hLayout); + } + widgetResults->adjustSize(); + if(widgetResults->sizeHint().height() < 70) + { + ui->scrollAreaResult->setMaximumHeight(widgetResults->sizeHint().height() + 2); + } + else + { + ui->scrollAreaResult->setMaximumHeight(140); + } + ui->scrollAreaResult->setWidget(widgetResults); + ui->groupBoxResult->setVisible(true); + } + else + { + ui->groupBoxResult->setVisible(false); + } + } + else + { + QString errorMessage; + errorMessage = function.errorMessage(errors, false); + QMessageBox::warning(this, tr("Create contract"), errorMessage); + } +} diff --git a/src/qt/createcontract.cpp b/src/qt/createcontract.cpp index 5caff0e005..60d9538352 100644 --- a/src/qt/createcontract.cpp +++ b/src/qt/createcontract.cpp @@ -1,305 +1,305 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace CreateContract_NS -{ -// Contract data names -static const QString PRC_COMMAND = "createcontract"; -static const QString PARAM_BYTECODE = "bytecode"; -static const QString PARAM_GASLIMIT = "gaslimit"; -static const QString PARAM_GASPRICE = "gasprice"; -static const QString PARAM_SENDER = "sender"; - -static const CAmount SINGLE_STEP = 0.00000001*COIN; -static const CAmount HIGH_GASPRICE = 0.001*COIN; -} -using namespace CreateContract_NS; - -CreateContract::CreateContract(const PlatformStyle *platformStyle, QWidget *parent) : - QWidget(parent), - ui(new Ui::CreateContract), - m_model(0), - m_clientModel(0), - m_execRPCCommand(0), - m_ABIFunctionField(0), - m_contractABI(0), - m_tabInfo(0), - m_results(1) -{ - // Setup ui components - Q_UNUSED(platformStyle); - ui->setupUi(this); - - // Set stylesheet - SetObjectStyleSheet(ui->pushButtonClearAll, StyleSheetNames::ButtonBlack); - - setLinkLabels(); - m_ABIFunctionField = new ABIFunctionField(platformStyle, ABIFunctionField::Create, ui->scrollAreaConstructor); - ui->scrollAreaConstructor->setWidget(m_ABIFunctionField); - ui->labelBytecode->setToolTip(tr("The bytecode of the contract")); - ui->labelSenderAddress->setToolTip(tr("The quantum address that will be used to create the contract.")); - - m_tabInfo = new TabBarInfo(ui->stackedWidget); - m_tabInfo->addTab(0, tr("Create Contract")); - - // Set defaults - ui->lineEditGasPrice->setValue(DEFAULT_GAS_PRICE); - ui->lineEditGasPrice->setSingleStep(SINGLE_STEP); - ui->lineEditGasLimit->setMinimum(MINIMUM_GAS_LIMIT); - ui->lineEditGasLimit->setMaximum(DEFAULT_GAS_LIMIT_OP_CREATE); - ui->lineEditGasLimit->setValue(DEFAULT_GAS_LIMIT_OP_CREATE); - ui->pushButtonCreateContract->setEnabled(false); - ui->lineEditSenderAddress->setSenderAddress(true); - - // Create new PRC command line interface - QStringList lstMandatory; - lstMandatory.append(PARAM_BYTECODE); - QStringList lstOptional; - lstOptional.append(PARAM_GASLIMIT); - lstOptional.append(PARAM_GASPRICE); - lstOptional.append(PARAM_SENDER); - QMap lstTranslations; - lstTranslations[PARAM_BYTECODE] = ui->labelBytecode->text(); - lstTranslations[PARAM_GASLIMIT] = ui->labelGasLimit->text(); - lstTranslations[PARAM_GASPRICE] = ui->labelGasPrice->text(); - lstTranslations[PARAM_SENDER] = ui->labelSenderAddress->text(); - m_execRPCCommand = new ExecRPCCommand(PRC_COMMAND, lstMandatory, lstOptional, lstTranslations, this); - m_contractABI = new ContractABI(); - - // Connect signals with slots - connect(ui->pushButtonClearAll, &QPushButton::clicked, this, &CreateContract::on_clearAllClicked); - connect(ui->pushButtonCreateContract, &QPushButton::clicked, this, &CreateContract::on_createContractClicked); - connect(ui->textEditBytecode, &QValidatedTextEdit::textChanged, this, &CreateContract::on_updateCreateButton); - connect(ui->textEditInterface, &QValidatedTextEdit::textChanged, this, &CreateContract::on_newContractABI); - connect(ui->stackedWidget, &QStackedWidget::currentChanged, this, &CreateContract::on_updateCreateButton); - - // Set bytecode validator - QRegularExpression regEx; - regEx.setPattern(paternHex); - QRegularExpressionValidator *bytecodeValidator = new QRegularExpressionValidator(ui->textEditBytecode); - bytecodeValidator->setRegularExpression(regEx); - ui->textEditBytecode->setCheckValidator(bytecodeValidator); -} - -CreateContract::~CreateContract() -{ - delete m_contractABI; - delete ui; -} - -void CreateContract::setLinkLabels() -{ - ui->labelSolidity->setOpenExternalLinks(true); - ui->labelSolidity->setText("Solidity compiler"); -} - -void CreateContract::setModel(WalletModel *_model) -{ - m_model = _model; - ui->lineEditSenderAddress->setWalletModel(m_model); - - if (m_model && m_model->getOptionsModel()) - connect(m_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &CreateContract::updateDisplayUnit); - - // update the display unit, to not use the default ("QTUM") - updateDisplayUnit(); -} - -bool CreateContract::isValidBytecode() -{ - ui->textEditBytecode->checkValidity(); - return ui->textEditBytecode->isValid(); -} - -bool CreateContract::isValidInterfaceABI() -{ - ui->textEditInterface->checkValidity(); - return ui->textEditInterface->isValid(); -} - -bool CreateContract::isDataValid() -{ - bool dataValid = true; - int func = m_ABIFunctionField->getSelectedFunction(); - bool funcValid = func == -1 ? true : m_ABIFunctionField->isValid(); - - if(!isValidBytecode()) - dataValid = false; - if(!isValidInterfaceABI()) - dataValid = false; - if(!funcValid) - dataValid = false; - - return dataValid; -} - -void CreateContract::setClientModel(ClientModel *_clientModel) -{ - m_clientModel = _clientModel; - - if (m_clientModel) - { - connect(m_clientModel, SIGNAL(gasInfoChanged(quint64, quint64, quint64)), this, SLOT(on_gasInfoChanged(quint64, quint64, quint64))); - } -} - -void CreateContract::on_clearAllClicked() -{ - ui->textEditBytecode->clear(); - ui->lineEditGasLimit->setValue(DEFAULT_GAS_LIMIT_OP_CREATE); - ui->lineEditGasPrice->setValue(DEFAULT_GAS_PRICE); - ui->lineEditSenderAddress->setCurrentIndex(-1); - ui->textEditInterface->clear(); - m_tabInfo->clear(); -} - -void CreateContract::on_createContractClicked() -{ - if(isDataValid()) - { - WalletModel::UnlockContext ctx(m_model->requestUnlock()); - if(!ctx.isValid()) - { - return; - } - - // Initialize variables - QMap lstParams; - QVariant result; - QString errorMessage; - QString resultJson; - int unit = BitcoinUnits::BTC; - uint64_t gasLimit = ui->lineEditGasLimit->value(); - CAmount gasPrice = ui->lineEditGasPrice->value(); - int func = m_ABIFunctionField->getSelectedFunction(); - - // Check for high gas price - if(gasPrice > HIGH_GASPRICE) - { - QString message = tr("The Gas Price is too high, are you sure you want to possibly spend a max of %1 for this transaction?"); - if(QMessageBox::question(this, tr("High Gas price"), message.arg(BitcoinUnits::formatWithUnit(unit, gasLimit * gasPrice))) == QMessageBox::No) - return; - } - - // Append params to the list - QString bytecode = ui->textEditBytecode->toPlainText() + toDataHex(func, errorMessage); - ExecRPCCommand::appendParam(lstParams, PARAM_BYTECODE, bytecode); - ExecRPCCommand::appendParam(lstParams, PARAM_GASLIMIT, QString::number(gasLimit)); - ExecRPCCommand::appendParam(lstParams, PARAM_GASPRICE, BitcoinUnits::format(unit, gasPrice, false, BitcoinUnits::separatorNever)); - ExecRPCCommand::appendParam(lstParams, PARAM_SENDER, ui->lineEditSenderAddress->currentText()); - - QString questionString = tr("Are you sure you want to create contract?
"); - - SendConfirmationDialog confirmationDialog(tr("Confirm contract creation."), questionString, 3, this); - confirmationDialog.exec(); - QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result(); - if(retval == QMessageBox::Yes) - { - // Execute RPC command line - if(errorMessage.isEmpty() && m_execRPCCommand->exec(m_model->node(), m_model, lstParams, result, resultJson, errorMessage)) - { - ContractResult *widgetResult = new ContractResult(ui->stackedWidget); - widgetResult->setResultData(result, FunctionABI(), QList(), ContractResult::CreateResult); - ui->stackedWidget->addWidget(widgetResult); - int position = ui->stackedWidget->count() - 1; - m_results = position == 1 ? 1 : m_results + 1; - - m_tabInfo->addTab(position, tr("Result %1").arg(m_results)); - m_tabInfo->setCurrent(position); - } - else - { - QMessageBox::warning(this, tr("Create contract"), errorMessage); - } - } - } -} - -void CreateContract::on_gasInfoChanged(quint64 blockGasLimit, quint64 minGasPrice, quint64 nGasPrice) -{ - Q_UNUSED(nGasPrice); - ui->labelGasLimit->setToolTip(tr("Gas limit. Default = %1, Max = %2").arg(DEFAULT_GAS_LIMIT_OP_CREATE).arg(blockGasLimit)); - ui->labelGasPrice->setToolTip(tr("Gas price: QTUM price per gas unit. Default = %1, Min = %2").arg(QString::fromStdString(FormatMoney(DEFAULT_GAS_PRICE))).arg(QString::fromStdString(FormatMoney(minGasPrice)))); - ui->lineEditGasPrice->SetMinValue(minGasPrice); - ui->lineEditGasLimit->setMaximum(blockGasLimit); -} - -void CreateContract::on_updateCreateButton() -{ - bool enabled = true; - if(ui->textEditBytecode->toPlainText().isEmpty()) - { - enabled = false; - } - enabled &= ui->stackedWidget->currentIndex() == 0; - - ui->pushButtonCreateContract->setEnabled(enabled); -} - -void CreateContract::on_newContractABI() -{ - std::string json_data = ui->textEditInterface->toPlainText().toStdString(); - if(!m_contractABI->loads(json_data)) - { - m_contractABI->clean(); - ui->textEditInterface->setIsValidManually(false); - } - else - { - ui->textEditInterface->setIsValidManually(true); - } - m_ABIFunctionField->setContractABI(m_contractABI); - - on_updateCreateButton(); -} - -void CreateContract::updateDisplayUnit() -{ - if(m_model && m_model->getOptionsModel()) - { - // Update gasPriceAmount with the current unit - ui->lineEditGasPrice->setDisplayUnit(m_model->getOptionsModel()->getDisplayUnit()); - } -} - -QString CreateContract::toDataHex(int func, QString& errorMessage) -{ - if(func == -1 || m_ABIFunctionField == NULL || m_contractABI == NULL) - { - return ""; - } - - std::string strData; - std::vector> values = m_ABIFunctionField->getValuesVector(); - FunctionABI function = m_contractABI->functions[func]; - std::vector errors; - if(function.abiIn(values, strData, errors)) - { - return QString::fromStdString(strData); - } - else - { - errorMessage = function.errorMessage(errors, true); - } - return ""; -} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace CreateContract_NS +{ +// Contract data names +static const QString PRC_COMMAND = "createcontract"; +static const QString PARAM_BYTECODE = "bytecode"; +static const QString PARAM_GASLIMIT = "gaslimit"; +static const QString PARAM_GASPRICE = "gasprice"; +static const QString PARAM_SENDER = "sender"; + +static const CAmount SINGLE_STEP = 0.00000001*COIN; +static const CAmount HIGH_GASPRICE = 0.001*COIN; +} +using namespace CreateContract_NS; + +CreateContract::CreateContract(const PlatformStyle *platformStyle, QWidget *parent) : + QWidget(parent), + ui(new Ui::CreateContract), + m_model(0), + m_clientModel(0), + m_execRPCCommand(0), + m_ABIFunctionField(0), + m_contractABI(0), + m_tabInfo(0), + m_results(1) +{ + // Setup ui components + Q_UNUSED(platformStyle); + ui->setupUi(this); + + // Set stylesheet + SetObjectStyleSheet(ui->pushButtonClearAll, StyleSheetNames::ButtonBlack); + + setLinkLabels(); + m_ABIFunctionField = new ABIFunctionField(platformStyle, ABIFunctionField::Create, ui->scrollAreaConstructor); + ui->scrollAreaConstructor->setWidget(m_ABIFunctionField); + ui->labelBytecode->setToolTip(tr("The bytecode of the contract")); + ui->labelSenderAddress->setToolTip(tr("The quantum address that will be used to create the contract.")); + + m_tabInfo = new TabBarInfo(ui->stackedWidget); + m_tabInfo->addTab(0, tr("Create Contract")); + + // Set defaults + ui->lineEditGasPrice->setValue(DEFAULT_GAS_PRICE); + ui->lineEditGasPrice->setSingleStep(SINGLE_STEP); + ui->lineEditGasLimit->setMinimum(MINIMUM_GAS_LIMIT); + ui->lineEditGasLimit->setMaximum(DEFAULT_GAS_LIMIT_OP_CREATE); + ui->lineEditGasLimit->setValue(DEFAULT_GAS_LIMIT_OP_CREATE); + ui->pushButtonCreateContract->setEnabled(false); + ui->lineEditSenderAddress->setSenderAddress(true); + + // Create new PRC command line interface + QStringList lstMandatory; + lstMandatory.append(PARAM_BYTECODE); + QStringList lstOptional; + lstOptional.append(PARAM_GASLIMIT); + lstOptional.append(PARAM_GASPRICE); + lstOptional.append(PARAM_SENDER); + QMap lstTranslations; + lstTranslations[PARAM_BYTECODE] = ui->labelBytecode->text(); + lstTranslations[PARAM_GASLIMIT] = ui->labelGasLimit->text(); + lstTranslations[PARAM_GASPRICE] = ui->labelGasPrice->text(); + lstTranslations[PARAM_SENDER] = ui->labelSenderAddress->text(); + m_execRPCCommand = new ExecRPCCommand(PRC_COMMAND, lstMandatory, lstOptional, lstTranslations, this); + m_contractABI = new ContractABI(); + + // Connect signals with slots + connect(ui->pushButtonClearAll, &QPushButton::clicked, this, &CreateContract::on_clearAllClicked); + connect(ui->pushButtonCreateContract, &QPushButton::clicked, this, &CreateContract::on_createContractClicked); + connect(ui->textEditBytecode, &QValidatedTextEdit::textChanged, this, &CreateContract::on_updateCreateButton); + connect(ui->textEditInterface, &QValidatedTextEdit::textChanged, this, &CreateContract::on_newContractABI); + connect(ui->stackedWidget, &QStackedWidget::currentChanged, this, &CreateContract::on_updateCreateButton); + + // Set bytecode validator + QRegularExpression regEx; + regEx.setPattern(paternHex); + QRegularExpressionValidator *bytecodeValidator = new QRegularExpressionValidator(ui->textEditBytecode); + bytecodeValidator->setRegularExpression(regEx); + ui->textEditBytecode->setCheckValidator(bytecodeValidator); +} + +CreateContract::~CreateContract() +{ + delete m_contractABI; + delete ui; +} + +void CreateContract::setLinkLabels() +{ + ui->labelSolidity->setOpenExternalLinks(true); + ui->labelSolidity->setText("Solidity compiler"); +} + +void CreateContract::setModel(WalletModel *_model) +{ + m_model = _model; + ui->lineEditSenderAddress->setWalletModel(m_model); + + if (m_model && m_model->getOptionsModel()) + connect(m_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &CreateContract::updateDisplayUnit); + + // update the display unit, to not use the default ("QTUM") + updateDisplayUnit(); +} + +bool CreateContract::isValidBytecode() +{ + ui->textEditBytecode->checkValidity(); + return ui->textEditBytecode->isValid(); +} + +bool CreateContract::isValidInterfaceABI() +{ + ui->textEditInterface->checkValidity(); + return ui->textEditInterface->isValid(); +} + +bool CreateContract::isDataValid() +{ + bool dataValid = true; + int func = m_ABIFunctionField->getSelectedFunction(); + bool funcValid = func == -1 ? true : m_ABIFunctionField->isValid(); + + if(!isValidBytecode()) + dataValid = false; + if(!isValidInterfaceABI()) + dataValid = false; + if(!funcValid) + dataValid = false; + + return dataValid; +} + +void CreateContract::setClientModel(ClientModel *_clientModel) +{ + m_clientModel = _clientModel; + + if (m_clientModel) + { + connect(m_clientModel, SIGNAL(gasInfoChanged(quint64, quint64, quint64)), this, SLOT(on_gasInfoChanged(quint64, quint64, quint64))); + } +} + +void CreateContract::on_clearAllClicked() +{ + ui->textEditBytecode->clear(); + ui->lineEditGasLimit->setValue(DEFAULT_GAS_LIMIT_OP_CREATE); + ui->lineEditGasPrice->setValue(DEFAULT_GAS_PRICE); + ui->lineEditSenderAddress->setCurrentIndex(-1); + ui->textEditInterface->clear(); + m_tabInfo->clear(); +} + +void CreateContract::on_createContractClicked() +{ + if(isDataValid()) + { + WalletModel::UnlockContext ctx(m_model->requestUnlock()); + if(!ctx.isValid()) + { + return; + } + + // Initialize variables + QMap lstParams; + QVariant result; + QString errorMessage; + QString resultJson; + int unit = BitcoinUnits::BTC; + uint64_t gasLimit = ui->lineEditGasLimit->value(); + CAmount gasPrice = ui->lineEditGasPrice->value(); + int func = m_ABIFunctionField->getSelectedFunction(); + + // Check for high gas price + if(gasPrice > HIGH_GASPRICE) + { + QString message = tr("The Gas Price is too high, are you sure you want to possibly spend a max of %1 for this transaction?"); + if(QMessageBox::question(this, tr("High Gas price"), message.arg(BitcoinUnits::formatWithUnit(unit, gasLimit * gasPrice))) == QMessageBox::No) + return; + } + + // Append params to the list + QString bytecode = ui->textEditBytecode->toPlainText() + toDataHex(func, errorMessage); + ExecRPCCommand::appendParam(lstParams, PARAM_BYTECODE, bytecode); + ExecRPCCommand::appendParam(lstParams, PARAM_GASLIMIT, QString::number(gasLimit)); + ExecRPCCommand::appendParam(lstParams, PARAM_GASPRICE, BitcoinUnits::format(unit, gasPrice, false, BitcoinUnits::separatorNever)); + ExecRPCCommand::appendParam(lstParams, PARAM_SENDER, ui->lineEditSenderAddress->currentText()); + + QString questionString = tr("Are you sure you want to create contract?
"); + + SendConfirmationDialog confirmationDialog(tr("Confirm contract creation."), questionString, 3, this); + confirmationDialog.exec(); + QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result(); + if(retval == QMessageBox::Yes) + { + // Execute RPC command line + if(errorMessage.isEmpty() && m_execRPCCommand->exec(m_model->node(), m_model, lstParams, result, resultJson, errorMessage)) + { + ContractResult *widgetResult = new ContractResult(ui->stackedWidget); + widgetResult->setResultData(result, FunctionABI(), QList(), ContractResult::CreateResult); + ui->stackedWidget->addWidget(widgetResult); + int position = ui->stackedWidget->count() - 1; + m_results = position == 1 ? 1 : m_results + 1; + + m_tabInfo->addTab(position, tr("Result %1").arg(m_results)); + m_tabInfo->setCurrent(position); + } + else + { + QMessageBox::warning(this, tr("Create contract"), errorMessage); + } + } + } +} + +void CreateContract::on_gasInfoChanged(quint64 blockGasLimit, quint64 minGasPrice, quint64 nGasPrice) +{ + Q_UNUSED(nGasPrice); + ui->labelGasLimit->setToolTip(tr("Gas limit. Default = %1, Max = %2").arg(DEFAULT_GAS_LIMIT_OP_CREATE).arg(blockGasLimit)); + ui->labelGasPrice->setToolTip(tr("Gas price: QTUM price per gas unit. Default = %1, Min = %2").arg(QString::fromStdString(FormatMoney(DEFAULT_GAS_PRICE))).arg(QString::fromStdString(FormatMoney(minGasPrice)))); + ui->lineEditGasPrice->SetMinValue(minGasPrice); + ui->lineEditGasLimit->setMaximum(blockGasLimit); +} + +void CreateContract::on_updateCreateButton() +{ + bool enabled = true; + if(ui->textEditBytecode->toPlainText().isEmpty()) + { + enabled = false; + } + enabled &= ui->stackedWidget->currentIndex() == 0; + + ui->pushButtonCreateContract->setEnabled(enabled); +} + +void CreateContract::on_newContractABI() +{ + std::string json_data = ui->textEditInterface->toPlainText().toStdString(); + if(!m_contractABI->loads(json_data)) + { + m_contractABI->clean(); + ui->textEditInterface->setIsValidManually(false); + } + else + { + ui->textEditInterface->setIsValidManually(true); + } + m_ABIFunctionField->setContractABI(m_contractABI); + + on_updateCreateButton(); +} + +void CreateContract::updateDisplayUnit() +{ + if(m_model && m_model->getOptionsModel()) + { + // Update gasPriceAmount with the current unit + ui->lineEditGasPrice->setDisplayUnit(m_model->getOptionsModel()->getDisplayUnit()); + } +} + +QString CreateContract::toDataHex(int func, QString& errorMessage) +{ + if(func == -1 || m_ABIFunctionField == NULL || m_contractABI == NULL) + { + return ""; + } + + std::string strData; + std::vector> values = m_ABIFunctionField->getValuesVector(); + FunctionABI function = m_contractABI->functions[func]; + std::vector errors; + if(function.abiIn(values, strData, errors)) + { + return QString::fromStdString(strData); + } + else + { + errorMessage = function.errorMessage(errors, true); + } + return ""; +} diff --git a/src/qt/editcontractinfodialog.cpp b/src/qt/editcontractinfodialog.cpp index 6852205138..e4229421b2 100644 --- a/src/qt/editcontractinfodialog.cpp +++ b/src/qt/editcontractinfodialog.cpp @@ -1,191 +1,191 @@ -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -EditContractInfoDialog::EditContractInfoDialog(Mode _mode, QWidget *parent) : - QDialog(parent), - ui(new Ui::EditContractInfoDialog), - mapper(0), - mode(_mode), - model(0), - m_contractABI(0) -{ - m_contractABI = new ContractABI(); - - ui->setupUi(this); - - SetObjectStyleSheet(ui->buttonBox->button(QDialogButtonBox::Cancel), StyleSheetNames::ButtonWhite); - SetObjectStyleSheet(ui->buttonBox->button(QDialogButtonBox::Ok), StyleSheetNames::ButtonBlue); - - switch(mode) - { - case NewContractInfo: - setWindowTitle(tr("New contract info")); - break; - case EditContractInfo: - setWindowTitle(tr("Edit contract info")); - break; - } - - mapper = new QDataWidgetMapper(this); - mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); - - connect(ui->ABIEdit, &QValidatedTextEdit::textChanged, this, &EditContractInfoDialog::on_newContractABI); - - // Set contract address validator - QRegularExpression regEx; - regEx.setPattern(paternAddress); - QRegularExpressionValidator *addressValidator = new QRegularExpressionValidator(ui->addressEdit); - addressValidator->setRegularExpression(regEx); - ui->addressEdit->setCheckValidator(addressValidator); - ui->addressEdit->setEmptyIsValid(false); -} - -EditContractInfoDialog::~EditContractInfoDialog() -{ - delete m_contractABI; - delete ui; -} - -bool EditContractInfoDialog::isValidContractAddress() -{ - ui->addressEdit->checkValidity(); - return ui->addressEdit->isValid(); -} - -bool EditContractInfoDialog::isValidInterfaceABI() -{ - ui->ABIEdit->checkValidity(); - return ui->ABIEdit->isValid(); -} - -bool EditContractInfoDialog::isDataValid() -{ - bool dataValid = true; - - if(!isValidContractAddress()) - dataValid = false; - if(!isValidInterfaceABI()) - dataValid = false; - - return dataValid; -} - -void EditContractInfoDialog::setModel(ContractTableModel *_model) -{ - this->model = _model; - if(!_model) - return; - - mapper->setModel(_model); - mapper->addMapping(ui->labelEdit, ContractTableModel::Label); - mapper->addMapping(ui->addressEdit, ContractTableModel::Address); - mapper->addMapping(ui->ABIEdit, ContractTableModel::ABI, "plainText"); -} - -void EditContractInfoDialog::loadRow(int row) -{ - mapper->setCurrentIndex(row); -} - -bool EditContractInfoDialog::saveCurrentRow() -{ - if(!model) - return false; - - model->resetEditStatus(); - switch(mode) - { - case NewContractInfo: - address = model->addRow( - ui->labelEdit->text(), - ui->addressEdit->text(), - ui->ABIEdit->toPlainText()); - if(!address.isEmpty()) - this->ABI = ui->ABIEdit->toPlainText(); - break; - case EditContractInfo: - if(mapper->submit()) - { - this->address = ui->addressEdit->text(); - this->ABI = ui->ABIEdit->toPlainText(); - } - break; - } - bool editError = model->getEditStatus() >= ContractTableModel::DUPLICATE_ADDRESS; - return !address.isEmpty() && !editError; -} - -void EditContractInfoDialog::accept() -{ - if(isDataValid()) - { - if(!model) - return; - - if(!saveCurrentRow()) - { - switch(model->getEditStatus()) - { - case ContractTableModel::OK: - // Failed with unknown reason. Just reject. - break; - case ContractTableModel::NO_CHANGES: - // No changes were made during edit operation. Just reject. - break; - case ContractTableModel::DUPLICATE_ADDRESS: - QMessageBox::warning(this, windowTitle(), - tr("The entered address \"%1\" is already in the contract book.").arg(ui->addressEdit->text()), - QMessageBox::Ok, QMessageBox::Ok); - break; - - } - return; - } - QDialog::accept(); - } -} - -void EditContractInfoDialog::on_newContractABI() -{ - std::string json_data = ui->ABIEdit->toPlainText().toStdString(); - if(!m_contractABI->loads(json_data)) - { - ui->ABIEdit->setIsValidManually(false); - } - else - { - ui->ABIEdit->setIsValidManually(true); - } - m_contractABI->clean(); -} - -QString EditContractInfoDialog::getAddress() const -{ - return address; -} - -void EditContractInfoDialog::setAddress(const QString &_address) -{ - this->address = _address; - ui->addressEdit->setText(_address); -} - -QString EditContractInfoDialog::getABI() const -{ - return ABI; -} - -void EditContractInfoDialog::setABI(const QString &_ABI) -{ - this->ABI = _ABI; - ui->ABIEdit->setText(_ABI); -} +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +EditContractInfoDialog::EditContractInfoDialog(Mode _mode, QWidget *parent) : + QDialog(parent), + ui(new Ui::EditContractInfoDialog), + mapper(0), + mode(_mode), + model(0), + m_contractABI(0) +{ + m_contractABI = new ContractABI(); + + ui->setupUi(this); + + SetObjectStyleSheet(ui->buttonBox->button(QDialogButtonBox::Cancel), StyleSheetNames::ButtonWhite); + SetObjectStyleSheet(ui->buttonBox->button(QDialogButtonBox::Ok), StyleSheetNames::ButtonBlue); + + switch(mode) + { + case NewContractInfo: + setWindowTitle(tr("New contract info")); + break; + case EditContractInfo: + setWindowTitle(tr("Edit contract info")); + break; + } + + mapper = new QDataWidgetMapper(this); + mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); + + connect(ui->ABIEdit, &QValidatedTextEdit::textChanged, this, &EditContractInfoDialog::on_newContractABI); + + // Set contract address validator + QRegularExpression regEx; + regEx.setPattern(paternAddress); + QRegularExpressionValidator *addressValidator = new QRegularExpressionValidator(ui->addressEdit); + addressValidator->setRegularExpression(regEx); + ui->addressEdit->setCheckValidator(addressValidator); + ui->addressEdit->setEmptyIsValid(false); +} + +EditContractInfoDialog::~EditContractInfoDialog() +{ + delete m_contractABI; + delete ui; +} + +bool EditContractInfoDialog::isValidContractAddress() +{ + ui->addressEdit->checkValidity(); + return ui->addressEdit->isValid(); +} + +bool EditContractInfoDialog::isValidInterfaceABI() +{ + ui->ABIEdit->checkValidity(); + return ui->ABIEdit->isValid(); +} + +bool EditContractInfoDialog::isDataValid() +{ + bool dataValid = true; + + if(!isValidContractAddress()) + dataValid = false; + if(!isValidInterfaceABI()) + dataValid = false; + + return dataValid; +} + +void EditContractInfoDialog::setModel(ContractTableModel *_model) +{ + this->model = _model; + if(!_model) + return; + + mapper->setModel(_model); + mapper->addMapping(ui->labelEdit, ContractTableModel::Label); + mapper->addMapping(ui->addressEdit, ContractTableModel::Address); + mapper->addMapping(ui->ABIEdit, ContractTableModel::ABI, "plainText"); +} + +void EditContractInfoDialog::loadRow(int row) +{ + mapper->setCurrentIndex(row); +} + +bool EditContractInfoDialog::saveCurrentRow() +{ + if(!model) + return false; + + model->resetEditStatus(); + switch(mode) + { + case NewContractInfo: + address = model->addRow( + ui->labelEdit->text(), + ui->addressEdit->text(), + ui->ABIEdit->toPlainText()); + if(!address.isEmpty()) + this->ABI = ui->ABIEdit->toPlainText(); + break; + case EditContractInfo: + if(mapper->submit()) + { + this->address = ui->addressEdit->text(); + this->ABI = ui->ABIEdit->toPlainText(); + } + break; + } + bool editError = model->getEditStatus() >= ContractTableModel::DUPLICATE_ADDRESS; + return !address.isEmpty() && !editError; +} + +void EditContractInfoDialog::accept() +{ + if(isDataValid()) + { + if(!model) + return; + + if(!saveCurrentRow()) + { + switch(model->getEditStatus()) + { + case ContractTableModel::OK: + // Failed with unknown reason. Just reject. + break; + case ContractTableModel::NO_CHANGES: + // No changes were made during edit operation. Just reject. + break; + case ContractTableModel::DUPLICATE_ADDRESS: + QMessageBox::warning(this, windowTitle(), + tr("The entered address \"%1\" is already in the contract book.").arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + break; + + } + return; + } + QDialog::accept(); + } +} + +void EditContractInfoDialog::on_newContractABI() +{ + std::string json_data = ui->ABIEdit->toPlainText().toStdString(); + if(!m_contractABI->loads(json_data)) + { + ui->ABIEdit->setIsValidManually(false); + } + else + { + ui->ABIEdit->setIsValidManually(true); + } + m_contractABI->clean(); +} + +QString EditContractInfoDialog::getAddress() const +{ + return address; +} + +void EditContractInfoDialog::setAddress(const QString &_address) +{ + this->address = _address; + ui->addressEdit->setText(_address); +} + +QString EditContractInfoDialog::getABI() const +{ + return ABI; +} + +void EditContractInfoDialog::setABI(const QString &_ABI) +{ + this->ABI = _ABI; + ui->ABIEdit->setText(_ABI); +} diff --git a/src/qt/eventlog.cpp b/src/qt/eventlog.cpp index fde34a3e46..bf5849c15f 100755 --- a/src/qt/eventlog.cpp +++ b/src/qt/eventlog.cpp @@ -1,107 +1,107 @@ -#include -#include -#include -#include -#include -#include - -namespace EventLog_NS -{ -static const QString RPC_SERACH_LOGS = "searchlogs"; -static const QString PARAM_FROM_BLOCK = "fromBlock"; -static const QString PARAM_TO_BLOCK = "toBlock"; -static const QString PARAM_ADDRESSES = "address"; -static const QString PARAM_TOPICS = "topics"; -} -using namespace EventLog_NS; - -QString createJsonString(std::string key, const std::vector value) -{ - QJsonObject json; - QJsonArray array; - for(size_t i = 0; i < value.size(); i++) - { - array.append(QJsonValue(QString::fromStdString(value[i]))); - } - json.insert(QString::fromStdString(key), array); - - QJsonDocument doc(json); - QString retString(doc.toJson(QJsonDocument::Compact)); - retString.insert(0, "'"); - retString.append("'"); - - return retString; -} - -EventLog::EventLog(): - m_RPCCommand(0) -{ - // Create new searchlogs command line interface - QStringList lstMandatory; - lstMandatory.append(PARAM_FROM_BLOCK); - lstMandatory.append(PARAM_TO_BLOCK); - QStringList lstOptional; - lstOptional.append(PARAM_ADDRESSES); - lstOptional.append(PARAM_TOPICS); - m_RPCCommand = new ExecRPCCommand(RPC_SERACH_LOGS, lstMandatory, lstOptional, QMap()); -} - -EventLog::~EventLog() -{ - if(m_RPCCommand) - { - delete m_RPCCommand; - m_RPCCommand = 0; - } -} - -bool EventLog::searchTokenTx(interfaces::Node& node, const WalletModel* wallet_model, int64_t fromBlock, int64_t toBlock, std::string strContractAddress, std::string strSenderAddress, QVariant &result) -{ - std::vector addresses; - addresses.push_back(strContractAddress); - - std::vector topics; - // Skip the event type check - static std::string nullRecord = uint256().ToString(); - topics.push_back(nullRecord); - // Match the log with sender address - topics.push_back(strSenderAddress); - // Match the log with receiver address - topics.push_back(strSenderAddress); - - return search(node, wallet_model, fromBlock, toBlock, addresses, topics, result); -} - -bool EventLog::search(interfaces::Node& node, const WalletModel* wallet_model, int64_t fromBlock, int64_t toBlock, const std::vector addresses, const std::vector topics, QVariant &result) -{ - setStartBlock(fromBlock); - setEndBlock(toBlock); - setAddresses(addresses); - setTopics(topics); - - QString resultJson; - QString errorMessage; - if(!m_RPCCommand->exec(node, wallet_model, m_lstParams, result, resultJson, errorMessage)) - return false; - return true; -} - -void EventLog::setStartBlock(int64_t fromBlock) -{ - m_lstParams[PARAM_FROM_BLOCK] = QString::number(fromBlock); -} - -void EventLog::setEndBlock(int64_t toBlock) -{ - m_lstParams[PARAM_TO_BLOCK] = QString::number(toBlock); -} - -void EventLog::setAddresses(const std::vector addresses) -{ - m_lstParams[PARAM_ADDRESSES] = createJsonString("addresses", addresses); -} - -void EventLog::setTopics(const std::vector topics) -{ - m_lstParams[PARAM_TOPICS] = createJsonString("topics", topics); -} +#include +#include +#include +#include +#include +#include + +namespace EventLog_NS +{ +static const QString RPC_SERACH_LOGS = "searchlogs"; +static const QString PARAM_FROM_BLOCK = "fromBlock"; +static const QString PARAM_TO_BLOCK = "toBlock"; +static const QString PARAM_ADDRESSES = "address"; +static const QString PARAM_TOPICS = "topics"; +} +using namespace EventLog_NS; + +QString createJsonString(std::string key, const std::vector value) +{ + QJsonObject json; + QJsonArray array; + for(size_t i = 0; i < value.size(); i++) + { + array.append(QJsonValue(QString::fromStdString(value[i]))); + } + json.insert(QString::fromStdString(key), array); + + QJsonDocument doc(json); + QString retString(doc.toJson(QJsonDocument::Compact)); + retString.insert(0, "'"); + retString.append("'"); + + return retString; +} + +EventLog::EventLog(): + m_RPCCommand(0) +{ + // Create new searchlogs command line interface + QStringList lstMandatory; + lstMandatory.append(PARAM_FROM_BLOCK); + lstMandatory.append(PARAM_TO_BLOCK); + QStringList lstOptional; + lstOptional.append(PARAM_ADDRESSES); + lstOptional.append(PARAM_TOPICS); + m_RPCCommand = new ExecRPCCommand(RPC_SERACH_LOGS, lstMandatory, lstOptional, QMap()); +} + +EventLog::~EventLog() +{ + if(m_RPCCommand) + { + delete m_RPCCommand; + m_RPCCommand = 0; + } +} + +bool EventLog::searchTokenTx(interfaces::Node& node, const WalletModel* wallet_model, int64_t fromBlock, int64_t toBlock, std::string strContractAddress, std::string strSenderAddress, QVariant &result) +{ + std::vector addresses; + addresses.push_back(strContractAddress); + + std::vector topics; + // Skip the event type check + static std::string nullRecord = uint256().ToString(); + topics.push_back(nullRecord); + // Match the log with sender address + topics.push_back(strSenderAddress); + // Match the log with receiver address + topics.push_back(strSenderAddress); + + return search(node, wallet_model, fromBlock, toBlock, addresses, topics, result); +} + +bool EventLog::search(interfaces::Node& node, const WalletModel* wallet_model, int64_t fromBlock, int64_t toBlock, const std::vector addresses, const std::vector topics, QVariant &result) +{ + setStartBlock(fromBlock); + setEndBlock(toBlock); + setAddresses(addresses); + setTopics(topics); + + QString resultJson; + QString errorMessage; + if(!m_RPCCommand->exec(node, wallet_model, m_lstParams, result, resultJson, errorMessage)) + return false; + return true; +} + +void EventLog::setStartBlock(int64_t fromBlock) +{ + m_lstParams[PARAM_FROM_BLOCK] = QString::number(fromBlock); +} + +void EventLog::setEndBlock(int64_t toBlock) +{ + m_lstParams[PARAM_TO_BLOCK] = QString::number(toBlock); +} + +void EventLog::setAddresses(const std::vector addresses) +{ + m_lstParams[PARAM_ADDRESSES] = createJsonString("addresses", addresses); +} + +void EventLog::setTopics(const std::vector topics) +{ + m_lstParams[PARAM_TOPICS] = createJsonString("topics", topics); +} diff --git a/src/qt/execrpccommand.cpp b/src/qt/execrpccommand.cpp index 4e5b07aea2..3cb6bc443e 100644 --- a/src/qt/execrpccommand.cpp +++ b/src/qt/execrpccommand.cpp @@ -1,110 +1,110 @@ -#include -#include -#include -#include - -ExecRPCCommand::ExecRPCCommand(const QString &command, const QStringList &mandatory, const QStringList &optional, const QMap& translations, QObject *parent) - : QObject(parent) -{ - m_command = command; - m_mandatoryParams = mandatory; - m_optionalParams = optional; - m_translations = translations; -} - -bool ExecRPCCommand::exec(interfaces::Node &node, const WalletModel* wallet_model, const QMap ¶ms, QVariant &result, QString &resultJson, QString &errorMessage) -{ - QStringList commandLine; - commandLine.append(m_command); - - // Check the list of mandatory parameters - QStringList mandatoryNotPresent; - for(int i = 0; i < m_mandatoryParams.count(); i++) - { - QString key = m_mandatoryParams[i]; - if(!params.contains(key)) - { - mandatoryNotPresent.append(m_translations[key]); - } - else - { - commandLine.append(params[key]); - } - } - if(mandatoryNotPresent.count() > 0) - { - errorMessage = tr("Mandatory fields are not present:\n%1").arg(mandatoryNotPresent.join(", ")); - return false; - } - - // Check the list of optional parameters - bool haveOptional = false; - int optionalParamsAt = commandLine.size(); - QStringList optionalNotPresent; - for(int i = m_optionalParams.count() - 1; i > -1; i--) - { - QString key = m_optionalParams[i]; - if(params.contains(key)) - { - if(!haveOptional) haveOptional = true; - commandLine.insert(optionalParamsAt, params[key]); - } - else - { - if(haveOptional) - { - optionalNotPresent.prepend(m_translations[key]); - } - } - } - if(optionalNotPresent.count() > 0) - { - errorMessage = tr("Optional fields are not present:\n%1").arg(optionalNotPresent.join(", ")); - return false; - } - - // Execute the RPC command - try - { - std::string strResult; - std::string strCommand = commandLine.join(' ').toStdString(); - if(RPCConsole::RPCExecuteCommandLine(node, strResult, strCommand, nullptr, wallet_model)) - { - resultJson = strResult.c_str(); - QJsonDocument doc = QJsonDocument::fromJson(strResult.c_str()); - result = doc.toVariant(); - return true; - } - else - { - errorMessage = tr("Parse error: unbalanced ' or \""); - } - } - catch (UniValue& objError) - { - try // Nice formatting for standard-format error - { - int code = find_value(objError, "code").get_int(); - std::string message = find_value(objError, "message").get_str(); - errorMessage = QString::fromStdString(message) + " (code " + QString::number(code) + ")"; - } - catch (const std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message - { // Show raw JSON object - errorMessage = QString::fromStdString(objError.write()); - } - } - catch (const std::exception& e) - { - errorMessage = QString("Error: ") + QString::fromStdString(e.what()); - } - - return false; -} - -void ExecRPCCommand::appendParam(QMap ¶ms, const QString ¶mName, const QString ¶mValue) -{ - QString _paramValue = paramValue.trimmed(); - if(!(_paramValue.isNull() || _paramValue.isEmpty())) params[paramName] = _paramValue; -} - - +#include +#include +#include +#include + +ExecRPCCommand::ExecRPCCommand(const QString &command, const QStringList &mandatory, const QStringList &optional, const QMap& translations, QObject *parent) + : QObject(parent) +{ + m_command = command; + m_mandatoryParams = mandatory; + m_optionalParams = optional; + m_translations = translations; +} + +bool ExecRPCCommand::exec(interfaces::Node &node, const WalletModel* wallet_model, const QMap ¶ms, QVariant &result, QString &resultJson, QString &errorMessage) +{ + QStringList commandLine; + commandLine.append(m_command); + + // Check the list of mandatory parameters + QStringList mandatoryNotPresent; + for(int i = 0; i < m_mandatoryParams.count(); i++) + { + QString key = m_mandatoryParams[i]; + if(!params.contains(key)) + { + mandatoryNotPresent.append(m_translations[key]); + } + else + { + commandLine.append(params[key]); + } + } + if(mandatoryNotPresent.count() > 0) + { + errorMessage = tr("Mandatory fields are not present:\n%1").arg(mandatoryNotPresent.join(", ")); + return false; + } + + // Check the list of optional parameters + bool haveOptional = false; + int optionalParamsAt = commandLine.size(); + QStringList optionalNotPresent; + for(int i = m_optionalParams.count() - 1; i > -1; i--) + { + QString key = m_optionalParams[i]; + if(params.contains(key)) + { + if(!haveOptional) haveOptional = true; + commandLine.insert(optionalParamsAt, params[key]); + } + else + { + if(haveOptional) + { + optionalNotPresent.prepend(m_translations[key]); + } + } + } + if(optionalNotPresent.count() > 0) + { + errorMessage = tr("Optional fields are not present:\n%1").arg(optionalNotPresent.join(", ")); + return false; + } + + // Execute the RPC command + try + { + std::string strResult; + std::string strCommand = commandLine.join(' ').toStdString(); + if(RPCConsole::RPCExecuteCommandLine(node, strResult, strCommand, nullptr, wallet_model)) + { + resultJson = strResult.c_str(); + QJsonDocument doc = QJsonDocument::fromJson(strResult.c_str()); + result = doc.toVariant(); + return true; + } + else + { + errorMessage = tr("Parse error: unbalanced ' or \""); + } + } + catch (UniValue& objError) + { + try // Nice formatting for standard-format error + { + int code = find_value(objError, "code").get_int(); + std::string message = find_value(objError, "message").get_str(); + errorMessage = QString::fromStdString(message) + " (code " + QString::number(code) + ")"; + } + catch (const std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message + { // Show raw JSON object + errorMessage = QString::fromStdString(objError.write()); + } + } + catch (const std::exception& e) + { + errorMessage = QString("Error: ") + QString::fromStdString(e.what()); + } + + return false; +} + +void ExecRPCCommand::appendParam(QMap ¶ms, const QString ¶mName, const QString ¶mValue) +{ + QString _paramValue = paramValue.trimmed(); + if(!(_paramValue.isNull() || _paramValue.isEmpty())) params[paramName] = _paramValue; +} + + diff --git a/src/qt/navigationbar.cpp b/src/qt/navigationbar.cpp index fc028fd940..6d7958b86d 100644 --- a/src/qt/navigationbar.cpp +++ b/src/qt/navigationbar.cpp @@ -1,302 +1,302 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -namespace NavigationBar_NS -{ -static const int ToolButtonWidth = 220; -static const int ToolButtonHeight = 54; -static const int ToolButtonIconSize = 32; -static const int MarginLeft = 0; -static const int MarginRight = 0; -static const int MarginTop = 0; -static const int MarginBottom = 8; -static const int ButtonSpacing = 2; -static const int SubNavPaddingRight = 40; -} -using namespace NavigationBar_NS; - -class NavToolButton : public QToolButton -{ -public: - explicit NavToolButton(QWidget * parent, bool subBar): - QToolButton(parent), - m_subBar(subBar), - m_iconCached(false) - {} - -protected: - void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE - { - QStylePainter sp( this ); - QStyleOptionToolButton opt; - initStyleOption( &opt ); - - if(m_subBar) - { - const QString strText = opt.text; - - //draw background - opt.text.clear(); - opt.icon = QIcon(); - sp.drawComplexControl( QStyle::CC_ToolButton, opt ); - opt.text = strText; - - //draw label - drawLabel(&opt, &sp); - } - else - { - //update icon - updateIcon(opt); - - //draw control - sp.drawComplexControl( QStyle::CC_ToolButton, opt ); - } - } - - void drawLabel(const QStyleOption *opt, QPainter *p) - { - if (const QStyleOptionToolButton *toolbutton - = qstyleoption_cast(opt)) { - - // Choose color - QColor color; - if(!(toolbutton->state & QStyle::State_Enabled)) - { - color = 0x1a96ce; - } - else if(toolbutton->state & (QStyle::State_Sunken | QStyle::State_On)) - { - color = 0xe5f3f9; - } - else if(toolbutton->state & QStyle::State_MouseOver) - { - color = 0xb3dcef; - } - else - { - color = 0x7fc4e3; - } - - // Determine area - QRect rect = toolbutton->rect; - int w = rect.width() - SubNavPaddingRight; - w = qMax(w, 0); - rect.setWidth(w); - int shiftX = 0; - int shiftY = 0; - if (toolbutton->state & (QStyle::State_Sunken | QStyle::State_On)) { - shiftX = style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, toolbutton, this); - shiftY = style()->pixelMetric(QStyle::PM_ButtonShiftVertical, toolbutton, this); - } - - // Draw text - if (!toolbutton->text.isEmpty()) { - int alignment = Qt::AlignRight | Qt::AlignVCenter | Qt::TextShowMnemonic; - rect.translate(shiftX, shiftY); - p->setFont(toolbutton->font); - p->setPen(color); - p->drawText(rect, alignment, toolbutton->text); - } - } - } - - void updateIcon(QStyleOptionToolButton &toolbutton) - { - // Update mouse over icon - if((toolbutton.state & QStyle::State_Enabled) && - !(toolbutton.state & QStyle::State_On) && - (toolbutton.state & QStyle::State_MouseOver)) - { - if(!m_iconCached) - { - QIcon icon = toolbutton.icon; - QPixmap pixmap = icon.pixmap(toolbutton.iconSize, QIcon::Selected, QIcon::On); - m_hoverIcon = QIcon(pixmap); - m_iconCached = true; - } - toolbutton.icon = m_hoverIcon; - } - } - -private: - bool m_subBar; - bool m_iconCached; - QIcon m_hoverIcon; -}; - -NavigationBar::NavigationBar(QWidget *parent) : - QWidget(parent), - m_toolStyle(Qt::ToolButtonTextBesideIcon), - m_subBar(false), - m_built(false) -{ -} - -void NavigationBar::addAction(QAction *action) -{ - // Add action to the list - m_actions.append(action); -} - -QAction *NavigationBar::addGroup(QList list, const QIcon &icon, const QString &text) -{ - // Add new group - QAction* action = new QAction(icon, text, this); - mapGroup(action, list); - return action; -} - -QAction *NavigationBar::addGroup(QList list, const QString &text) -{ - // Add new group - QAction* action = new QAction(text, this); - mapGroup(action, list); - return action; -} - -void NavigationBar::buildUi() -{ - // Build the layout of the complex GUI component - if(!m_built) - { - // Set it visible if main component - setVisible(!m_subBar); - - // Create new layout for the bar - QActionGroup* actionGroup = new QActionGroup(this); - actionGroup->setExclusive(true); - QVBoxLayout* vboxLayout = new QVBoxLayout(this); - int defButtonWidth = ToolButtonWidth; - vboxLayout->setContentsMargins(m_subBar ? 0 : MarginLeft, - m_subBar ? 0 : MarginTop, - m_subBar ? 0 : MarginRight, - m_subBar ? 0 : MarginBottom); - vboxLayout->setSpacing(m_subBar ? 0 : ButtonSpacing); - - // List all actions - for(int i = 0; i < m_actions.count(); i++) - { - // Add an action to the layout - QAction* action = m_actions[i]; - action->setActionGroup(actionGroup); - action->setCheckable(true); - QToolButton* toolButton = new NavToolButton(this, m_subBar); - toolButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - toolButton->setToolButtonStyle(m_toolStyle); - toolButton->setDefaultAction(action); - toolButton->setIconSize(QSize(ToolButtonIconSize, ToolButtonIconSize)); - if(m_subBar) - { - SetObjectStyleSheet(toolButton, StyleSheetNames::ToolSubBlack); - } - else - { - SetObjectStyleSheet(toolButton, StyleSheetNames::ToolBlack); - } - - if(m_groups.contains(action)) - { - // Add the tool button - QVBoxLayout* vboxLayout2 = new QVBoxLayout(); - vboxLayout->addLayout(vboxLayout2); - vboxLayout2->addWidget(toolButton); - vboxLayout2->setSpacing(0); - if(!m_subBar) - { - SetObjectStyleSheet(toolButton, StyleSheetNames::ToolGroupBlack); - } - - // Add sub-navigation bar for the group of actions - QList group = m_groups[action]; - NavigationBar* subNavBar = new NavigationBar(this); - subNavBar->setSubBar(true); - for(int j = 0; j < group.count(); j++) - { - subNavBar->addAction(group[j]); - } - vboxLayout2->addWidget(subNavBar); - subNavBar->buildUi(); - connect(action, &QAction::toggled, subNavBar, &NavigationBar::onSubBarClick); - } - else - { - - vboxLayout->addWidget(toolButton); - } - } - - if(!m_subBar) - { - // Set specific parameters for for the main component - if(m_actions.count()) - { - m_actions[0]->setChecked(true); - } - setMinimumWidth(defButtonWidth + MarginLeft + MarginRight); - vboxLayout->addStretch(1); - } - - // The component is built - m_built = true; - } -} - -void NavigationBar::onSubBarClick(bool clicked) -{ - // Expand/collapse the sub-navigation bar - setVisible(clicked); - - - if(clicked && m_actions.count() > 0) - { - // Activate the checked action - bool haveChecked = false; - for(int i = 0; i < m_actions.count(); i++) - { - QAction* action = m_actions[i]; - if(action->isChecked()) - { - action->trigger(); - haveChecked = true; - break; - } - } - if(!haveChecked) - { - m_actions[0]->trigger(); - } - } -} - -void NavigationBar::setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle) -{ - // Set the tool button style - m_toolStyle = toolButtonStyle; -} - -void NavigationBar::resizeEvent(QResizeEvent *evt) -{ - QWidget::resizeEvent(evt); - Q_EMIT resized(size()); -} - -void NavigationBar::mapGroup(QAction *action, QList list) -{ - // Map the group with the actions - addAction(action); - m_groups[action] = list; -} - -void NavigationBar::setSubBar(bool subBar) -{ - // Set the component be sub-navigation bar - m_subBar = subBar; -} - +#include +#include +#include +#include +#include +#include +#include +#include + +namespace NavigationBar_NS +{ +static const int ToolButtonWidth = 220; +static const int ToolButtonHeight = 54; +static const int ToolButtonIconSize = 32; +static const int MarginLeft = 0; +static const int MarginRight = 0; +static const int MarginTop = 0; +static const int MarginBottom = 8; +static const int ButtonSpacing = 2; +static const int SubNavPaddingRight = 40; +} +using namespace NavigationBar_NS; + +class NavToolButton : public QToolButton +{ +public: + explicit NavToolButton(QWidget * parent, bool subBar): + QToolButton(parent), + m_subBar(subBar), + m_iconCached(false) + {} + +protected: + void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE + { + QStylePainter sp( this ); + QStyleOptionToolButton opt; + initStyleOption( &opt ); + + if(m_subBar) + { + const QString strText = opt.text; + + //draw background + opt.text.clear(); + opt.icon = QIcon(); + sp.drawComplexControl( QStyle::CC_ToolButton, opt ); + opt.text = strText; + + //draw label + drawLabel(&opt, &sp); + } + else + { + //update icon + updateIcon(opt); + + //draw control + sp.drawComplexControl( QStyle::CC_ToolButton, opt ); + } + } + + void drawLabel(const QStyleOption *opt, QPainter *p) + { + if (const QStyleOptionToolButton *toolbutton + = qstyleoption_cast(opt)) { + + // Choose color + QColor color; + if(!(toolbutton->state & QStyle::State_Enabled)) + { + color = 0x1a96ce; + } + else if(toolbutton->state & (QStyle::State_Sunken | QStyle::State_On)) + { + color = 0xe5f3f9; + } + else if(toolbutton->state & QStyle::State_MouseOver) + { + color = 0xb3dcef; + } + else + { + color = 0x7fc4e3; + } + + // Determine area + QRect rect = toolbutton->rect; + int w = rect.width() - SubNavPaddingRight; + w = qMax(w, 0); + rect.setWidth(w); + int shiftX = 0; + int shiftY = 0; + if (toolbutton->state & (QStyle::State_Sunken | QStyle::State_On)) { + shiftX = style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, toolbutton, this); + shiftY = style()->pixelMetric(QStyle::PM_ButtonShiftVertical, toolbutton, this); + } + + // Draw text + if (!toolbutton->text.isEmpty()) { + int alignment = Qt::AlignRight | Qt::AlignVCenter | Qt::TextShowMnemonic; + rect.translate(shiftX, shiftY); + p->setFont(toolbutton->font); + p->setPen(color); + p->drawText(rect, alignment, toolbutton->text); + } + } + } + + void updateIcon(QStyleOptionToolButton &toolbutton) + { + // Update mouse over icon + if((toolbutton.state & QStyle::State_Enabled) && + !(toolbutton.state & QStyle::State_On) && + (toolbutton.state & QStyle::State_MouseOver)) + { + if(!m_iconCached) + { + QIcon icon = toolbutton.icon; + QPixmap pixmap = icon.pixmap(toolbutton.iconSize, QIcon::Selected, QIcon::On); + m_hoverIcon = QIcon(pixmap); + m_iconCached = true; + } + toolbutton.icon = m_hoverIcon; + } + } + +private: + bool m_subBar; + bool m_iconCached; + QIcon m_hoverIcon; +}; + +NavigationBar::NavigationBar(QWidget *parent) : + QWidget(parent), + m_toolStyle(Qt::ToolButtonTextBesideIcon), + m_subBar(false), + m_built(false) +{ +} + +void NavigationBar::addAction(QAction *action) +{ + // Add action to the list + m_actions.append(action); +} + +QAction *NavigationBar::addGroup(QList list, const QIcon &icon, const QString &text) +{ + // Add new group + QAction* action = new QAction(icon, text, this); + mapGroup(action, list); + return action; +} + +QAction *NavigationBar::addGroup(QList list, const QString &text) +{ + // Add new group + QAction* action = new QAction(text, this); + mapGroup(action, list); + return action; +} + +void NavigationBar::buildUi() +{ + // Build the layout of the complex GUI component + if(!m_built) + { + // Set it visible if main component + setVisible(!m_subBar); + + // Create new layout for the bar + QActionGroup* actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); + QVBoxLayout* vboxLayout = new QVBoxLayout(this); + int defButtonWidth = ToolButtonWidth; + vboxLayout->setContentsMargins(m_subBar ? 0 : MarginLeft, + m_subBar ? 0 : MarginTop, + m_subBar ? 0 : MarginRight, + m_subBar ? 0 : MarginBottom); + vboxLayout->setSpacing(m_subBar ? 0 : ButtonSpacing); + + // List all actions + for(int i = 0; i < m_actions.count(); i++) + { + // Add an action to the layout + QAction* action = m_actions[i]; + action->setActionGroup(actionGroup); + action->setCheckable(true); + QToolButton* toolButton = new NavToolButton(this, m_subBar); + toolButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + toolButton->setToolButtonStyle(m_toolStyle); + toolButton->setDefaultAction(action); + toolButton->setIconSize(QSize(ToolButtonIconSize, ToolButtonIconSize)); + if(m_subBar) + { + SetObjectStyleSheet(toolButton, StyleSheetNames::ToolSubBlack); + } + else + { + SetObjectStyleSheet(toolButton, StyleSheetNames::ToolBlack); + } + + if(m_groups.contains(action)) + { + // Add the tool button + QVBoxLayout* vboxLayout2 = new QVBoxLayout(); + vboxLayout->addLayout(vboxLayout2); + vboxLayout2->addWidget(toolButton); + vboxLayout2->setSpacing(0); + if(!m_subBar) + { + SetObjectStyleSheet(toolButton, StyleSheetNames::ToolGroupBlack); + } + + // Add sub-navigation bar for the group of actions + QList group = m_groups[action]; + NavigationBar* subNavBar = new NavigationBar(this); + subNavBar->setSubBar(true); + for(int j = 0; j < group.count(); j++) + { + subNavBar->addAction(group[j]); + } + vboxLayout2->addWidget(subNavBar); + subNavBar->buildUi(); + connect(action, &QAction::toggled, subNavBar, &NavigationBar::onSubBarClick); + } + else + { + + vboxLayout->addWidget(toolButton); + } + } + + if(!m_subBar) + { + // Set specific parameters for for the main component + if(m_actions.count()) + { + m_actions[0]->setChecked(true); + } + setMinimumWidth(defButtonWidth + MarginLeft + MarginRight); + vboxLayout->addStretch(1); + } + + // The component is built + m_built = true; + } +} + +void NavigationBar::onSubBarClick(bool clicked) +{ + // Expand/collapse the sub-navigation bar + setVisible(clicked); + + + if(clicked && m_actions.count() > 0) + { + // Activate the checked action + bool haveChecked = false; + for(int i = 0; i < m_actions.count(); i++) + { + QAction* action = m_actions[i]; + if(action->isChecked()) + { + action->trigger(); + haveChecked = true; + break; + } + } + if(!haveChecked) + { + m_actions[0]->trigger(); + } + } +} + +void NavigationBar::setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle) +{ + // Set the tool button style + m_toolStyle = toolButtonStyle; +} + +void NavigationBar::resizeEvent(QResizeEvent *evt) +{ + QWidget::resizeEvent(evt); + Q_EMIT resized(size()); +} + +void NavigationBar::mapGroup(QAction *action, QList list) +{ + // Map the group with the actions + addAction(action); + m_groups[action] = list; +} + +void NavigationBar::setSubBar(bool subBar) +{ + // Set the component be sub-navigation bar + m_subBar = subBar; +} + diff --git a/src/qt/qrctoken.cpp b/src/qt/qrctoken.cpp index f483c0d178..697528f8d1 100644 --- a/src/qt/qrctoken.cpp +++ b/src/qt/qrctoken.cpp @@ -1,325 +1,325 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define TOKEN_SIZE 54 -#define SYMBOL_WIDTH 60 -#define MARGIN 5 - -class TokenViewDelegate : public QAbstractItemDelegate -{ -public: - - TokenViewDelegate(const PlatformStyle *_platformStyle, QObject *parent) : - QAbstractItemDelegate(parent), - platformStyle(_platformStyle) - {} - - void paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const - { - painter->save(); - - QString tokenSymbol = index.data(TokenItemModel::SymbolRole).toString(); - QString tokenBalance = index.data(TokenItemModel::BalanceRole).toString(); - QString receiveAddress = index.data(TokenItemModel::SenderRole).toString(); - - QRect mainRect = option.rect; - - bool selected = option.state & QStyle::State_Selected; - if(selected) - { - painter->fillRect(mainRect,QColor("#009ee5")); - } - else - { - painter->fillRect(mainRect,QColor("#383938")); - } - - QRect hLineRect(mainRect.left(), mainRect.bottom(), mainRect.width(), 1); - painter->fillRect(hLineRect, QColor("#2e2e2e")); - - QColor foreground("#dddddd"); - painter->setPen(foreground); - - QFont font = option.font; - font.setPointSizeF(option.font.pointSizeF() * 1.1); - font.setBold(true); - painter->setFont(font); - QColor amountColor("#ffffff"); - painter->setPen(amountColor); - - QFontMetrics fmName(option.font); - QString clippedSymbol = fmName.elidedText(tokenSymbol, Qt::ElideRight, SYMBOL_WIDTH); - QRect tokenSymbolRect(mainRect.left() + MARGIN, mainRect.top() + MARGIN, SYMBOL_WIDTH, mainRect.height() / 2 - MARGIN); - painter->drawText(tokenSymbolRect, Qt::AlignLeft|Qt::AlignVCenter, clippedSymbol); - - int amountWidth = (mainRect.width() - 4 * MARGIN - tokenSymbolRect.width()); - QFontMetrics fmAmount(font); - QString clippedAmount = fmAmount.elidedText(tokenBalance, Qt::ElideRight, amountWidth); - QRect tokenBalanceRect(tokenSymbolRect.right() + 2 * MARGIN, tokenSymbolRect.top(), amountWidth, tokenSymbolRect.height()); - painter->drawText(tokenBalanceRect, Qt::AlignLeft|Qt::AlignVCenter, clippedAmount); - - QFont addressFont = option.font; - addressFont.setPointSizeF(option.font.pointSizeF() * 0.8); - painter->setFont(addressFont); - painter->setPen(foreground); - QRect receiveAddressRect(mainRect.left() + MARGIN, tokenSymbolRect.bottom(), mainRect.width() - 2 * MARGIN, mainRect.height() / 2 - 2 * MARGIN); - painter->drawText(receiveAddressRect, Qt::AlignLeft|Qt::AlignVCenter, receiveAddress); - - painter->restore(); - } - - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const - { - return QSize(TOKEN_SIZE, TOKEN_SIZE); - } - - const PlatformStyle *platformStyle; -}; - -QRCToken::QRCToken(const PlatformStyle *platformStyle, QWidget *parent) : - QWidget(parent), - ui(new Ui::QRCToken), - m_model(0), - m_clientModel(0), - m_tokenModel(0), - m_tokenDelegate(0), - m_tokenTransactionView(0) -{ - ui->setupUi(this); - - m_platformStyle = platformStyle; - - m_sendTokenPage = new SendTokenPage(this); - m_receiveTokenPage = new ReceiveTokenPage(platformStyle, this); - m_addTokenPage = new AddTokenPage(this); - m_tokenDelegate = new TokenViewDelegate(platformStyle, this); - - m_sendTokenPage->setEnabled(false); - m_receiveTokenPage->setEnabled(false); - - ui->stackedWidgetToken->addWidget(m_sendTokenPage); - ui->stackedWidgetToken->addWidget(m_receiveTokenPage); - ui->stackedWidgetToken->addWidget(m_addTokenPage); - - m_tokenTransactionView = new TokenTransactionView(m_platformStyle, this); - m_tokenTransactionView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - ui->tokenViewLayout->addWidget(m_tokenTransactionView); - - ui->tokensList->setItemDelegate(m_tokenDelegate); - ui->tokensList->setContextMenuPolicy(Qt::CustomContextMenu); - ui->tokensList->setAttribute(Qt::WA_MacShowFocusRect, false); - - QAction *copySenderAction = new QAction(tr("Copy receive address"), this); - QAction *copyTokenBalanceAction = new QAction(tr("Copy token balance"), this); - QAction *copyTokenNameAction = new QAction(tr("Copy token name"), this); - QAction *copyTokenAddressAction = new QAction(tr("Copy contract address"), this); - QAction *removeTokenAction = new QAction(tr("Remove token"), this); - - contextMenu = new QMenu(ui->tokensList); - contextMenu->addAction(copySenderAction); - contextMenu->addAction(copyTokenBalanceAction); - contextMenu->addAction(copyTokenNameAction); - contextMenu->addAction(copyTokenAddressAction); - contextMenu->addAction(removeTokenAction); - - connect(copyTokenAddressAction, &QAction::triggered, this, &QRCToken::copyTokenAddress); - connect(copyTokenBalanceAction, &QAction::triggered, this, &QRCToken::copyTokenBalance); - connect(copyTokenNameAction, &QAction::triggered, this, &QRCToken::copyTokenName); - connect(copySenderAction, &QAction::triggered, this, &QRCToken::copySenderAddress); - connect(removeTokenAction, &QAction::triggered, this, &QRCToken::removeToken); - - connect(ui->tokensList, &QListView::clicked, this, &QRCToken::on_currentTokenChanged); - connect(ui->tokensList, &QListView::customContextMenuRequested, this, &QRCToken::contextualMenu); - - on_goToSendTokenPage(); -} - -QRCToken::~QRCToken() -{ - delete ui; -} - -void QRCToken::setModel(WalletModel *_model) -{ - m_model = _model; - m_addTokenPage->setModel(m_model); - m_sendTokenPage->setModel(m_model); - m_tokenTransactionView->setModel(_model); - if(m_model && m_model->getTokenItemModel()) - { - // Sort tokens by symbol - QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this); - TokenItemModel* tokenModel = m_model->getTokenItemModel(); - proxyModel->setSourceModel(tokenModel); - proxyModel->sort(1, Qt::AscendingOrder); - m_tokenModel = proxyModel; - - // Set tokens model - ui->tokensList->setModel(m_tokenModel); - connect(ui->tokensList->selectionModel(), &QItemSelectionModel::currentChanged, this, &QRCToken::on_currentChanged); - - // Set current token - connect(m_tokenModel, &QAbstractItemModel::dataChanged, this, &QRCToken::on_dataChanged); - connect(m_tokenModel, &QAbstractItemModel::rowsInserted, this, &QRCToken::on_rowsInserted); - if(m_tokenModel->rowCount() > 0) - { - QModelIndex currentToken(m_tokenModel->index(0, 0)); - ui->tokensList->setCurrentIndex(currentToken); - on_currentTokenChanged(currentToken); - } - } -} - -void QRCToken::setClientModel(ClientModel *_clientModel) -{ - m_clientModel = _clientModel; - m_sendTokenPage->setClientModel(_clientModel); - m_addTokenPage->setClientModel(_clientModel); -} - -void QRCToken::on_goToSendTokenPage() -{ - ui->stackedWidgetToken->setCurrentIndex(0); -} - -void QRCToken::on_goToReceiveTokenPage() -{ - ui->stackedWidgetToken->setCurrentIndex(1); -} - -void QRCToken::on_goToAddTokenPage() -{ - ui->stackedWidgetToken->setCurrentIndex(2); -} - -void QRCToken::on_currentTokenChanged(QModelIndex index) -{ - if(m_tokenModel) - { - if(index.isValid()) - { - m_selectedTokenHash = m_tokenModel->data(index, TokenItemModel::HashRole).toString(); - std::string address = m_tokenModel->data(index, TokenItemModel::AddressRole).toString().toStdString(); - std::string symbol = m_tokenModel->data(index, TokenItemModel::SymbolRole).toString().toStdString(); - std::string sender = m_tokenModel->data(index, TokenItemModel::SenderRole).toString().toStdString(); - int8_t decimals = m_tokenModel->data(index, TokenItemModel::DecimalsRole).toInt(); - std::string balance = m_tokenModel->data(index, TokenItemModel::RawBalanceRole).toString().toStdString(); - m_sendTokenPage->setTokenData(address, sender, symbol, decimals, balance); - m_receiveTokenPage->setAddress(QString::fromStdString(sender)); - m_receiveTokenPage->setSymbol(QString::fromStdString(symbol)); - - if(!m_sendTokenPage->isEnabled()) - m_sendTokenPage->setEnabled(true); - if(!m_receiveTokenPage->isEnabled()) - m_receiveTokenPage->setEnabled(true); - } - else - { - m_sendTokenPage->setEnabled(false); - m_receiveTokenPage->setEnabled(false); - m_receiveTokenPage->setAddress(QString::fromStdString("")); - m_receiveTokenPage->setSymbol(QString::fromStdString("")); - } - } -} - -void QRCToken::on_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) -{ - Q_UNUSED(bottomRight); - Q_UNUSED(roles); - - if(m_tokenModel) - { - QString tokenHash = m_tokenModel->data(topLeft, TokenItemModel::HashRole).toString(); - if(m_selectedTokenHash.isEmpty() || - tokenHash == m_selectedTokenHash) - { - on_currentTokenChanged(topLeft); - } - } -} - -void QRCToken::on_currentChanged(QModelIndex current, QModelIndex previous) -{ - Q_UNUSED(previous); - - on_currentTokenChanged(current); -} - -void QRCToken::on_rowsInserted(QModelIndex index, int first, int last) -{ - Q_UNUSED(index); - Q_UNUSED(first); - Q_UNUSED(last); - - if(m_tokenModel->rowCount() == 1) - { - QModelIndex currentToken(m_tokenModel->index(0, 0)); - ui->tokensList->setCurrentIndex(currentToken); - on_currentTokenChanged(currentToken); - } -} - -void QRCToken::contextualMenu(const QPoint &point) -{ - QModelIndex index = ui->tokensList->indexAt(point); - QModelIndexList selection = ui->tokensList->selectionModel()->selectedIndexes(); - if (selection.empty()) - return; - - if(index.isValid()) - { - contextMenu->exec(QCursor::pos()); - } -} - -void QRCToken::copyTokenAddress() -{ - GUIUtil::copyEntryDataFromList(ui->tokensList, TokenItemModel::AddressRole); -} - -void QRCToken::copyTokenBalance() -{ - GUIUtil::copyEntryDataFromList(ui->tokensList, TokenItemModel::BalanceRole); -} - -void QRCToken::copyTokenName() -{ - GUIUtil::copyEntryDataFromList(ui->tokensList, TokenItemModel::NameRole); -} - -void QRCToken::copySenderAddress() -{ - GUIUtil::copyEntryDataFromList(ui->tokensList, TokenItemModel::SenderRole); -} - -void QRCToken::removeToken() -{ - QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm token remove"), tr("The selected token will be removed from the list. Are you sure?"), - QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); - - if(btnRetVal == QMessageBox::Yes) - { - QModelIndexList selection = ui->tokensList->selectionModel()->selectedIndexes(); - if (selection.empty() && !m_model) - return; - - QModelIndex index = selection[0]; - std::string sHash = index.data(TokenItemModel::HashRole).toString().toStdString(); - m_model->wallet().removeTokenEntry(sHash); - } -} +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define TOKEN_SIZE 54 +#define SYMBOL_WIDTH 60 +#define MARGIN 5 + +class TokenViewDelegate : public QAbstractItemDelegate +{ +public: + + TokenViewDelegate(const PlatformStyle *_platformStyle, QObject *parent) : + QAbstractItemDelegate(parent), + platformStyle(_platformStyle) + {} + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const + { + painter->save(); + + QString tokenSymbol = index.data(TokenItemModel::SymbolRole).toString(); + QString tokenBalance = index.data(TokenItemModel::BalanceRole).toString(); + QString receiveAddress = index.data(TokenItemModel::SenderRole).toString(); + + QRect mainRect = option.rect; + + bool selected = option.state & QStyle::State_Selected; + if(selected) + { + painter->fillRect(mainRect,QColor("#009ee5")); + } + else + { + painter->fillRect(mainRect,QColor("#383938")); + } + + QRect hLineRect(mainRect.left(), mainRect.bottom(), mainRect.width(), 1); + painter->fillRect(hLineRect, QColor("#2e2e2e")); + + QColor foreground("#dddddd"); + painter->setPen(foreground); + + QFont font = option.font; + font.setPointSizeF(option.font.pointSizeF() * 1.1); + font.setBold(true); + painter->setFont(font); + QColor amountColor("#ffffff"); + painter->setPen(amountColor); + + QFontMetrics fmName(option.font); + QString clippedSymbol = fmName.elidedText(tokenSymbol, Qt::ElideRight, SYMBOL_WIDTH); + QRect tokenSymbolRect(mainRect.left() + MARGIN, mainRect.top() + MARGIN, SYMBOL_WIDTH, mainRect.height() / 2 - MARGIN); + painter->drawText(tokenSymbolRect, Qt::AlignLeft|Qt::AlignVCenter, clippedSymbol); + + int amountWidth = (mainRect.width() - 4 * MARGIN - tokenSymbolRect.width()); + QFontMetrics fmAmount(font); + QString clippedAmount = fmAmount.elidedText(tokenBalance, Qt::ElideRight, amountWidth); + QRect tokenBalanceRect(tokenSymbolRect.right() + 2 * MARGIN, tokenSymbolRect.top(), amountWidth, tokenSymbolRect.height()); + painter->drawText(tokenBalanceRect, Qt::AlignLeft|Qt::AlignVCenter, clippedAmount); + + QFont addressFont = option.font; + addressFont.setPointSizeF(option.font.pointSizeF() * 0.8); + painter->setFont(addressFont); + painter->setPen(foreground); + QRect receiveAddressRect(mainRect.left() + MARGIN, tokenSymbolRect.bottom(), mainRect.width() - 2 * MARGIN, mainRect.height() / 2 - 2 * MARGIN); + painter->drawText(receiveAddressRect, Qt::AlignLeft|Qt::AlignVCenter, receiveAddress); + + painter->restore(); + } + + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const + { + return QSize(TOKEN_SIZE, TOKEN_SIZE); + } + + const PlatformStyle *platformStyle; +}; + +QRCToken::QRCToken(const PlatformStyle *platformStyle, QWidget *parent) : + QWidget(parent), + ui(new Ui::QRCToken), + m_model(0), + m_clientModel(0), + m_tokenModel(0), + m_tokenDelegate(0), + m_tokenTransactionView(0) +{ + ui->setupUi(this); + + m_platformStyle = platformStyle; + + m_sendTokenPage = new SendTokenPage(this); + m_receiveTokenPage = new ReceiveTokenPage(platformStyle, this); + m_addTokenPage = new AddTokenPage(this); + m_tokenDelegate = new TokenViewDelegate(platformStyle, this); + + m_sendTokenPage->setEnabled(false); + m_receiveTokenPage->setEnabled(false); + + ui->stackedWidgetToken->addWidget(m_sendTokenPage); + ui->stackedWidgetToken->addWidget(m_receiveTokenPage); + ui->stackedWidgetToken->addWidget(m_addTokenPage); + + m_tokenTransactionView = new TokenTransactionView(m_platformStyle, this); + m_tokenTransactionView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + ui->tokenViewLayout->addWidget(m_tokenTransactionView); + + ui->tokensList->setItemDelegate(m_tokenDelegate); + ui->tokensList->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tokensList->setAttribute(Qt::WA_MacShowFocusRect, false); + + QAction *copySenderAction = new QAction(tr("Copy receive address"), this); + QAction *copyTokenBalanceAction = new QAction(tr("Copy token balance"), this); + QAction *copyTokenNameAction = new QAction(tr("Copy token name"), this); + QAction *copyTokenAddressAction = new QAction(tr("Copy contract address"), this); + QAction *removeTokenAction = new QAction(tr("Remove token"), this); + + contextMenu = new QMenu(ui->tokensList); + contextMenu->addAction(copySenderAction); + contextMenu->addAction(copyTokenBalanceAction); + contextMenu->addAction(copyTokenNameAction); + contextMenu->addAction(copyTokenAddressAction); + contextMenu->addAction(removeTokenAction); + + connect(copyTokenAddressAction, &QAction::triggered, this, &QRCToken::copyTokenAddress); + connect(copyTokenBalanceAction, &QAction::triggered, this, &QRCToken::copyTokenBalance); + connect(copyTokenNameAction, &QAction::triggered, this, &QRCToken::copyTokenName); + connect(copySenderAction, &QAction::triggered, this, &QRCToken::copySenderAddress); + connect(removeTokenAction, &QAction::triggered, this, &QRCToken::removeToken); + + connect(ui->tokensList, &QListView::clicked, this, &QRCToken::on_currentTokenChanged); + connect(ui->tokensList, &QListView::customContextMenuRequested, this, &QRCToken::contextualMenu); + + on_goToSendTokenPage(); +} + +QRCToken::~QRCToken() +{ + delete ui; +} + +void QRCToken::setModel(WalletModel *_model) +{ + m_model = _model; + m_addTokenPage->setModel(m_model); + m_sendTokenPage->setModel(m_model); + m_tokenTransactionView->setModel(_model); + if(m_model && m_model->getTokenItemModel()) + { + // Sort tokens by symbol + QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this); + TokenItemModel* tokenModel = m_model->getTokenItemModel(); + proxyModel->setSourceModel(tokenModel); + proxyModel->sort(1, Qt::AscendingOrder); + m_tokenModel = proxyModel; + + // Set tokens model + ui->tokensList->setModel(m_tokenModel); + connect(ui->tokensList->selectionModel(), &QItemSelectionModel::currentChanged, this, &QRCToken::on_currentChanged); + + // Set current token + connect(m_tokenModel, &QAbstractItemModel::dataChanged, this, &QRCToken::on_dataChanged); + connect(m_tokenModel, &QAbstractItemModel::rowsInserted, this, &QRCToken::on_rowsInserted); + if(m_tokenModel->rowCount() > 0) + { + QModelIndex currentToken(m_tokenModel->index(0, 0)); + ui->tokensList->setCurrentIndex(currentToken); + on_currentTokenChanged(currentToken); + } + } +} + +void QRCToken::setClientModel(ClientModel *_clientModel) +{ + m_clientModel = _clientModel; + m_sendTokenPage->setClientModel(_clientModel); + m_addTokenPage->setClientModel(_clientModel); +} + +void QRCToken::on_goToSendTokenPage() +{ + ui->stackedWidgetToken->setCurrentIndex(0); +} + +void QRCToken::on_goToReceiveTokenPage() +{ + ui->stackedWidgetToken->setCurrentIndex(1); +} + +void QRCToken::on_goToAddTokenPage() +{ + ui->stackedWidgetToken->setCurrentIndex(2); +} + +void QRCToken::on_currentTokenChanged(QModelIndex index) +{ + if(m_tokenModel) + { + if(index.isValid()) + { + m_selectedTokenHash = m_tokenModel->data(index, TokenItemModel::HashRole).toString(); + std::string address = m_tokenModel->data(index, TokenItemModel::AddressRole).toString().toStdString(); + std::string symbol = m_tokenModel->data(index, TokenItemModel::SymbolRole).toString().toStdString(); + std::string sender = m_tokenModel->data(index, TokenItemModel::SenderRole).toString().toStdString(); + int8_t decimals = m_tokenModel->data(index, TokenItemModel::DecimalsRole).toInt(); + std::string balance = m_tokenModel->data(index, TokenItemModel::RawBalanceRole).toString().toStdString(); + m_sendTokenPage->setTokenData(address, sender, symbol, decimals, balance); + m_receiveTokenPage->setAddress(QString::fromStdString(sender)); + m_receiveTokenPage->setSymbol(QString::fromStdString(symbol)); + + if(!m_sendTokenPage->isEnabled()) + m_sendTokenPage->setEnabled(true); + if(!m_receiveTokenPage->isEnabled()) + m_receiveTokenPage->setEnabled(true); + } + else + { + m_sendTokenPage->setEnabled(false); + m_receiveTokenPage->setEnabled(false); + m_receiveTokenPage->setAddress(QString::fromStdString("")); + m_receiveTokenPage->setSymbol(QString::fromStdString("")); + } + } +} + +void QRCToken::on_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) +{ + Q_UNUSED(bottomRight); + Q_UNUSED(roles); + + if(m_tokenModel) + { + QString tokenHash = m_tokenModel->data(topLeft, TokenItemModel::HashRole).toString(); + if(m_selectedTokenHash.isEmpty() || + tokenHash == m_selectedTokenHash) + { + on_currentTokenChanged(topLeft); + } + } +} + +void QRCToken::on_currentChanged(QModelIndex current, QModelIndex previous) +{ + Q_UNUSED(previous); + + on_currentTokenChanged(current); +} + +void QRCToken::on_rowsInserted(QModelIndex index, int first, int last) +{ + Q_UNUSED(index); + Q_UNUSED(first); + Q_UNUSED(last); + + if(m_tokenModel->rowCount() == 1) + { + QModelIndex currentToken(m_tokenModel->index(0, 0)); + ui->tokensList->setCurrentIndex(currentToken); + on_currentTokenChanged(currentToken); + } +} + +void QRCToken::contextualMenu(const QPoint &point) +{ + QModelIndex index = ui->tokensList->indexAt(point); + QModelIndexList selection = ui->tokensList->selectionModel()->selectedIndexes(); + if (selection.empty()) + return; + + if(index.isValid()) + { + contextMenu->exec(QCursor::pos()); + } +} + +void QRCToken::copyTokenAddress() +{ + GUIUtil::copyEntryDataFromList(ui->tokensList, TokenItemModel::AddressRole); +} + +void QRCToken::copyTokenBalance() +{ + GUIUtil::copyEntryDataFromList(ui->tokensList, TokenItemModel::BalanceRole); +} + +void QRCToken::copyTokenName() +{ + GUIUtil::copyEntryDataFromList(ui->tokensList, TokenItemModel::NameRole); +} + +void QRCToken::copySenderAddress() +{ + GUIUtil::copyEntryDataFromList(ui->tokensList, TokenItemModel::SenderRole); +} + +void QRCToken::removeToken() +{ + QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm token remove"), tr("The selected token will be removed from the list. Are you sure?"), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); + + if(btnRetVal == QMessageBox::Yes) + { + QModelIndexList selection = ui->tokensList->selectionModel()->selectedIndexes(); + if (selection.empty() && !m_model) + return; + + QModelIndex index = selection[0]; + std::string sHash = index.data(TokenItemModel::HashRole).toString().toStdString(); + m_model->wallet().removeTokenEntry(sHash); + } +} diff --git a/src/qt/qtumversionchecker.cpp b/src/qt/qtumversionchecker.cpp index f5fbb9d628..f23c771d5f 100755 --- a/src/qt/qtumversionchecker.cpp +++ b/src/qt/qtumversionchecker.cpp @@ -1,65 +1,65 @@ -#include -#include - -#include -#include -#include -#include -#include -#include - -#define paternVersion "qtum-([0-9]+\\.)?([0-9]+\\.)?([0-9]+)-" - -QtumVersionChecker::QtumVersionChecker(QObject *parent) : QObject(parent) -{ - currentVersion = Version(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION); -} - -QtumVersionChecker::~QtumVersionChecker() -{ - -} - -bool QtumVersionChecker::newVersionAvailable() -{ - Version maxReleaseVersion = getMaxReleaseVersion(); - return maxReleaseVersion > currentVersion; -} - -QList QtumVersionChecker::getVersions() -{ - QNetworkAccessManager manager; - QNetworkReply *response = manager.get(QNetworkRequest(QUrl(QTUM_RELEASES))); - QEventLoop event; - connect(response, &QNetworkReply::finished, &event, &QEventLoop::quit); - event.exec(); - QString html = response->readAll(); - - QRegularExpression regEx(paternVersion); - QRegularExpressionMatchIterator regExIt = regEx.globalMatch(html); - - QList versions; - - while (regExIt.hasNext()) { - QRegularExpressionMatch match = regExIt.next(); - QString versionString = match.captured().mid(5, match.captured().length() - 6); // get version string in format XX.XX.XX - Version version(versionString); - if(!versions.contains(version)) - { - versions.append(version); - } - } - return versions; -} - -Version QtumVersionChecker::getMaxReleaseVersion() -{ - QList versions = getVersions(); - Version maxVersion; - - if(!versions.isEmpty()) - { - maxVersion = *std::max_element(versions.begin(), versions.end()); - } - return maxVersion; -} +#include +#include + +#include +#include +#include +#include +#include +#include + +#define paternVersion "qtum-([0-9]+\\.)?([0-9]+\\.)?([0-9]+)-" + +QtumVersionChecker::QtumVersionChecker(QObject *parent) : QObject(parent) +{ + currentVersion = Version(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION); +} + +QtumVersionChecker::~QtumVersionChecker() +{ + +} + +bool QtumVersionChecker::newVersionAvailable() +{ + Version maxReleaseVersion = getMaxReleaseVersion(); + return maxReleaseVersion > currentVersion; +} + +QList QtumVersionChecker::getVersions() +{ + QNetworkAccessManager manager; + QNetworkReply *response = manager.get(QNetworkRequest(QUrl(QTUM_RELEASES))); + QEventLoop event; + connect(response, &QNetworkReply::finished, &event, &QEventLoop::quit); + event.exec(); + QString html = response->readAll(); + + QRegularExpression regEx(paternVersion); + QRegularExpressionMatchIterator regExIt = regEx.globalMatch(html); + + QList versions; + + while (regExIt.hasNext()) { + QRegularExpressionMatch match = regExIt.next(); + QString versionString = match.captured().mid(5, match.captured().length() - 6); // get version string in format XX.XX.XX + Version version(versionString); + if(!versions.contains(version)) + { + versions.append(version); + } + } + return versions; +} + +Version QtumVersionChecker::getMaxReleaseVersion() +{ + QList versions = getVersions(); + Version maxVersion; + + if(!versions.isEmpty()) + { + maxVersion = *std::max_element(versions.begin(), versions.end()); + } + return maxVersion; +} diff --git a/src/qt/qvalidatedtextedit.cpp b/src/qt/qvalidatedtextedit.cpp index 790d3261ea..acb606b607 100644 --- a/src/qt/qvalidatedtextedit.cpp +++ b/src/qt/qvalidatedtextedit.cpp @@ -1,135 +1,135 @@ -#include -#include - -#include - -QValidatedTextEdit::QValidatedTextEdit(QWidget *parent) : - QTextEdit(parent), - valid(true), - checkValidator(0), - emptyIsValid(true), - isValidManually(false) -{ - connect(this, &QValidatedTextEdit::textChanged, this, &QValidatedTextEdit::markValid); - setStyleSheet(""); -} - -void QValidatedTextEdit::clear() -{ - setValid(true); - QTextEdit::clear(); -} - -void QValidatedTextEdit::setCheckValidator(const QValidator *v) -{ - checkValidator = v; -} - -bool QValidatedTextEdit::isValid() -{ - // use checkValidator in case the QValidatedTextEdit is disabled - if (checkValidator) - { - QString address = toPlainText(); - int pos = 0; - if (checkValidator->validate(address, pos) == QValidator::Acceptable) - return true; - } - - return valid; -} - -void QValidatedTextEdit::setValid(bool _valid) -{ - if(_valid == this->valid) - { - return; - } - - if(_valid) - { - setStyleSheet(""); - } - else - { - SetObjectStyleSheet(this, StyleSheetNames::Invalid); - } - this->valid = _valid; -} - -void QValidatedTextEdit::setEnabled(bool enabled) -{ - if (!enabled) - { - // A disabled QValidatedLineEdit should be marked valid - setValid(true); - } - else - { - // Recheck validity when QValidatedLineEdit gets enabled - checkValidity(); - } - - QTextEdit::setEnabled(enabled); -} - -void QValidatedTextEdit::checkValidity() -{ - if (emptyIsValid && toPlainText().isEmpty()) - { - setValid(true); - } - else if(isValidManually) - { - setValid(true); - } - else if (checkValidator) - { - QString address = toPlainText(); - int pos = 0; - if (checkValidator->validate(address, pos) == QValidator::Acceptable) - setValid(true); - else - setValid(false); - } - else - setValid(false); -} - -void QValidatedTextEdit::markValid() -{ - // As long as a user is typing ensure we display state as valid - setValid(true); -} - -void QValidatedTextEdit::focusInEvent(QFocusEvent *event) -{ - setValid(true); - QTextEdit::focusInEvent(event); -} - -void QValidatedTextEdit::focusOutEvent(QFocusEvent *event) -{ - checkValidity(); - QTextEdit::focusOutEvent(event); -} - -bool QValidatedTextEdit::getIsValidManually() const -{ - return isValidManually; -} - -void QValidatedTextEdit::setIsValidManually(bool value) -{ - isValidManually = value; -} - -bool QValidatedTextEdit::getEmptyIsValid() const -{ - return emptyIsValid; -} - -void QValidatedTextEdit::setEmptyIsValid(bool value) -{ - emptyIsValid = value; -} +#include +#include + +#include + +QValidatedTextEdit::QValidatedTextEdit(QWidget *parent) : + QTextEdit(parent), + valid(true), + checkValidator(0), + emptyIsValid(true), + isValidManually(false) +{ + connect(this, &QValidatedTextEdit::textChanged, this, &QValidatedTextEdit::markValid); + setStyleSheet(""); +} + +void QValidatedTextEdit::clear() +{ + setValid(true); + QTextEdit::clear(); +} + +void QValidatedTextEdit::setCheckValidator(const QValidator *v) +{ + checkValidator = v; +} + +bool QValidatedTextEdit::isValid() +{ + // use checkValidator in case the QValidatedTextEdit is disabled + if (checkValidator) + { + QString address = toPlainText(); + int pos = 0; + if (checkValidator->validate(address, pos) == QValidator::Acceptable) + return true; + } + + return valid; +} + +void QValidatedTextEdit::setValid(bool _valid) +{ + if(_valid == this->valid) + { + return; + } + + if(_valid) + { + setStyleSheet(""); + } + else + { + SetObjectStyleSheet(this, StyleSheetNames::Invalid); + } + this->valid = _valid; +} + +void QValidatedTextEdit::setEnabled(bool enabled) +{ + if (!enabled) + { + // A disabled QValidatedLineEdit should be marked valid + setValid(true); + } + else + { + // Recheck validity when QValidatedLineEdit gets enabled + checkValidity(); + } + + QTextEdit::setEnabled(enabled); +} + +void QValidatedTextEdit::checkValidity() +{ + if (emptyIsValid && toPlainText().isEmpty()) + { + setValid(true); + } + else if(isValidManually) + { + setValid(true); + } + else if (checkValidator) + { + QString address = toPlainText(); + int pos = 0; + if (checkValidator->validate(address, pos) == QValidator::Acceptable) + setValid(true); + else + setValid(false); + } + else + setValid(false); +} + +void QValidatedTextEdit::markValid() +{ + // As long as a user is typing ensure we display state as valid + setValid(true); +} + +void QValidatedTextEdit::focusInEvent(QFocusEvent *event) +{ + setValid(true); + QTextEdit::focusInEvent(event); +} + +void QValidatedTextEdit::focusOutEvent(QFocusEvent *event) +{ + checkValidity(); + QTextEdit::focusOutEvent(event); +} + +bool QValidatedTextEdit::getIsValidManually() const +{ + return isValidManually; +} + +void QValidatedTextEdit::setIsValidManually(bool value) +{ + isValidManually = value; +} + +bool QValidatedTextEdit::getEmptyIsValid() const +{ + return emptyIsValid; +} + +void QValidatedTextEdit::setEmptyIsValid(bool value) +{ + emptyIsValid = value; +} diff --git a/src/qt/receivetokenpage.cpp b/src/qt/receivetokenpage.cpp index 78ffee531e..7ad43b4c38 100644 --- a/src/qt/receivetokenpage.cpp +++ b/src/qt/receivetokenpage.cpp @@ -1,69 +1,69 @@ -#include -#include - -#include -#include -#include -#include - -ReceiveTokenPage::ReceiveTokenPage(const PlatformStyle *_platformStyle, QWidget *parent) : - QWidget(parent), - ui(new Ui::ReceiveTokenPage), - platformStyle(_platformStyle) -{ - ui->setupUi(this); - connect(ui->copyAddressButton, &QToolButton::clicked, this, &ReceiveTokenPage::on_copyAddressClicked); - ui->copyAddressButton->setIcon(platformStyle->MultiStatesIcon(":/icons/editcopy", PlatformStyle::PushButton)); - ui->copyAddressButton->setVisible(false); - setAddress(""); -} - -ReceiveTokenPage::~ReceiveTokenPage() -{ - delete ui; -} - -void ReceiveTokenPage::setAddress(QString address) -{ - m_address = address; - createQRCode(); -} - -void ReceiveTokenPage::setSymbol(QString symbol) -{ - QString addressText = symbol.isEmpty() ? "" : (QString("%1 ").arg(symbol) + tr("Address")); - ui->labelTokenAddressText->setText(addressText); -} - -void ReceiveTokenPage::on_copyAddressClicked() -{ - if(!m_address.isEmpty()) - GUIUtil::setClipboard(m_address); -} - -void ReceiveTokenPage::createQRCode() -{ - SendCoinsRecipient info; - if(!m_address.isEmpty()) - { - info.address = m_address; - if(ReceiveRequestDialog::createQRCode(ui->lblQRCode, info)) - { - ui->lblQRCode->setVisible(true); - ui->lblQRCode->setScaledContents(true); - } - else - { - ui->lblQRCode->setVisible(false); - } - ui->labelTokenAddress->setText(m_address); - ui->copyAddressButton->setVisible(true); - } - else - { - ui->lblQRCode->clear(); - ui->labelTokenAddress->setText(""); - ui->labelTokenAddressText->setText(""); - ui->copyAddressButton->setVisible(false); - } -} +#include +#include + +#include +#include +#include +#include + +ReceiveTokenPage::ReceiveTokenPage(const PlatformStyle *_platformStyle, QWidget *parent) : + QWidget(parent), + ui(new Ui::ReceiveTokenPage), + platformStyle(_platformStyle) +{ + ui->setupUi(this); + connect(ui->copyAddressButton, &QToolButton::clicked, this, &ReceiveTokenPage::on_copyAddressClicked); + ui->copyAddressButton->setIcon(platformStyle->MultiStatesIcon(":/icons/editcopy", PlatformStyle::PushButton)); + ui->copyAddressButton->setVisible(false); + setAddress(""); +} + +ReceiveTokenPage::~ReceiveTokenPage() +{ + delete ui; +} + +void ReceiveTokenPage::setAddress(QString address) +{ + m_address = address; + createQRCode(); +} + +void ReceiveTokenPage::setSymbol(QString symbol) +{ + QString addressText = symbol.isEmpty() ? "" : (QString("%1 ").arg(symbol) + tr("Address")); + ui->labelTokenAddressText->setText(addressText); +} + +void ReceiveTokenPage::on_copyAddressClicked() +{ + if(!m_address.isEmpty()) + GUIUtil::setClipboard(m_address); +} + +void ReceiveTokenPage::createQRCode() +{ + SendCoinsRecipient info; + if(!m_address.isEmpty()) + { + info.address = m_address; + if(ReceiveRequestDialog::createQRCode(ui->lblQRCode, info)) + { + ui->lblQRCode->setVisible(true); + ui->lblQRCode->setScaledContents(true); + } + else + { + ui->lblQRCode->setVisible(false); + } + ui->labelTokenAddress->setText(m_address); + ui->copyAddressButton->setVisible(true); + } + else + { + ui->lblQRCode->clear(); + ui->labelTokenAddress->setText(""); + ui->labelTokenAddressText->setText(""); + ui->copyAddressButton->setVisible(false); + } +} diff --git a/src/qt/sendtokenpage.cpp b/src/qt/sendtokenpage.cpp index 932c112ca7..ffc177d3b7 100644 --- a/src/qt/sendtokenpage.cpp +++ b/src/qt/sendtokenpage.cpp @@ -1,261 +1,261 @@ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const CAmount SINGLE_STEP = 0.00000001*COIN; - -struct SelectedToken{ - std::string address; - std::string sender; - std::string symbol; - int8_t decimals; - std::string balance; - SelectedToken(): - decimals(0) - {} -}; - -SendTokenPage::SendTokenPage(QWidget *parent) : - QWidget(parent), - ui(new Ui::SendTokenPage), - m_model(0), - m_clientModel(0), - m_tokenABI(0), - m_selectedToken(0) -{ - // Setup ui components - ui->setupUi(this); - - // Set stylesheet - SetObjectStyleSheet(ui->clearButton, StyleSheetNames::ButtonBlack); - - ui->labelPayTo->setToolTip(tr("The address that will receive the tokens.")); - ui->labelAmount->setToolTip(tr("The amount in Token to send.")); - ui->labelDescription->setToolTip(tr("Optional description for transaction.")); - m_tokenABI = new Token(); - m_selectedToken = new SelectedToken(); - - // Set defaults - ui->lineEditGasPrice->setValue(DEFAULT_GAS_PRICE); - ui->lineEditGasPrice->setSingleStep(SINGLE_STEP); - ui->lineEditGasLimit->setMaximum(DEFAULT_GAS_LIMIT_OP_SEND); - ui->lineEditGasLimit->setValue(DEFAULT_GAS_LIMIT_OP_SEND); - ui->confirmButton->setEnabled(false); - - // Connect signals with slots - connect(ui->lineEditPayTo, &QValidatedLineEdit::textChanged, this, &SendTokenPage::on_updateConfirmButton); - connect(ui->lineEditAmount, &TokenAmountField::valueChanged,this, &SendTokenPage::on_updateConfirmButton); - connect(ui->confirmButton, &QPushButton::clicked, this, &SendTokenPage::on_confirmClicked); - - ui->lineEditPayTo->setCheckValidator(new BitcoinAddressCheckValidator(parent, true)); -} - -SendTokenPage::~SendTokenPage() -{ - delete ui; - - if(m_tokenABI) - delete m_tokenABI; - m_tokenABI = 0; -} - -void SendTokenPage::setModel(WalletModel *_model) -{ - m_model = _model; - m_tokenABI->setModel(m_model); - - if (m_model && m_model->getOptionsModel()) - connect(m_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &SendTokenPage::updateDisplayUnit); - - // update the display unit, to not use the default ("QTUM") - updateDisplayUnit(); -} - -void SendTokenPage::setClientModel(ClientModel *_clientModel) -{ - m_clientModel = _clientModel; - - if (m_clientModel) - { - connect(m_clientModel, SIGNAL(gasInfoChanged(quint64, quint64, quint64)), this, SLOT(on_gasInfoChanged(quint64, quint64, quint64))); - } -} - -void SendTokenPage::clearAll() -{ - ui->lineEditPayTo->setText(""); - ui->lineEditAmount->clear(); - ui->lineEditDescription->setText(""); - ui->lineEditGasLimit->setValue(DEFAULT_GAS_LIMIT_OP_SEND); - ui->lineEditGasPrice->setValue(DEFAULT_GAS_PRICE); -} - -bool SendTokenPage::isValidAddress() -{ - ui->lineEditPayTo->checkValidity(); - return ui->lineEditPayTo->isValid(); -} - -bool SendTokenPage::isDataValid() -{ - bool dataValid = true; - - if(!isValidAddress()) - dataValid = false; - if(!ui->lineEditAmount->validate()) - dataValid = false; - if(ui->lineEditAmount->value(0) <= 0) - { - ui->lineEditAmount->setValid(false); - dataValid = false; - } - return dataValid; -} - -void SendTokenPage::on_clearButton_clicked() -{ - clearAll(); -} - -void SendTokenPage::on_gasInfoChanged(quint64 blockGasLimit, quint64 minGasPrice, quint64 nGasPrice) -{ - Q_UNUSED(nGasPrice); - ui->labelGasLimit->setToolTip(tr("Gas limit. Default = %1, Max = %2").arg(DEFAULT_GAS_LIMIT_OP_CREATE).arg(blockGasLimit)); - ui->labelGasPrice->setToolTip(tr("Gas price: QTUM price per gas unit. Default = %1, Min = %2").arg(QString::fromStdString(FormatMoney(DEFAULT_GAS_PRICE))).arg(QString::fromStdString(FormatMoney(minGasPrice)))); - ui->lineEditGasPrice->SetMinValue(minGasPrice); - ui->lineEditGasLimit->setMaximum(blockGasLimit); -} - -void SendTokenPage::on_updateConfirmButton() -{ - bool enabled = true; - if(ui->lineEditPayTo->text().isEmpty() || ui->lineEditAmount->text().isEmpty()) - { - enabled = false; - } - - ui->confirmButton->setEnabled(enabled); -} - -void SendTokenPage::on_confirmClicked() -{ - if(!isDataValid()) - return; - - WalletModel::UnlockContext ctx(m_model->requestUnlock()); - if(!ctx.isValid()) - { - return; - } - - if(m_model) - { - int unit = BitcoinUnits::BTC; - uint64_t gasLimit = ui->lineEditGasLimit->value(); - CAmount gasPrice = ui->lineEditGasPrice->value(); - std::string label = ui->lineEditDescription->text().trimmed().toStdString(); - - m_tokenABI->setAddress(m_selectedToken->address); - m_tokenABI->setSender(m_selectedToken->sender); - m_tokenABI->setGasLimit(QString::number(gasLimit).toStdString()); - m_tokenABI->setGasPrice(BitcoinUnits::format(unit, gasPrice, false, BitcoinUnits::separatorNever).toStdString()); - - std::string toAddress = ui->lineEditPayTo->text().toStdString(); - std::string amountToSend = ui->lineEditAmount->text().toStdString(); - QString amountFormated = BitcoinUnits::formatToken(m_selectedToken->decimals, ui->lineEditAmount->value(), false, BitcoinUnits::separatorAlways); - - QString questionString = tr("Are you sure you want to send?

"); - questionString.append(tr("%1 %2 to ") - .arg(amountFormated).arg(QString::fromStdString(m_selectedToken->symbol))); - questionString.append(tr("
%3
") - .arg(QString::fromStdString(toAddress))); - - SendConfirmationDialog confirmationDialog(tr("Confirm send token."), questionString, 3, this); - confirmationDialog.exec(); - QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result(); - if(retval == QMessageBox::Yes) - { - if(m_tokenABI->transfer(toAddress, amountToSend, true)) - { - interfaces::TokenTx tokenTx; - tokenTx.contract_address = m_selectedToken->address; - tokenTx.sender_address = m_selectedToken->sender; - tokenTx.receiver_address = toAddress; - dev::u256 nValue(amountToSend); - tokenTx.value = u256Touint(nValue); - tokenTx.tx_hash = uint256S(m_tokenABI->getTxId()); - tokenTx.label = label; - m_model->wallet().addTokenTxEntry(tokenTx); - } - else - { - QMessageBox::warning(this, tr("Send token"), QString::fromStdString(m_tokenABI->getErrorMessage())); - } - clearAll(); - } - } -} - -void SendTokenPage::updateDisplayUnit() -{ - if(m_model && m_model->getOptionsModel()) - { - // Update gasPriceAmount with the current unit - ui->lineEditGasPrice->setDisplayUnit(m_model->getOptionsModel()->getDisplayUnit()); - } -} - -void SendTokenPage::setTokenData(std::string address, std::string sender, std::string symbol, int8_t decimals, std::string balance) -{ - // Update data with the current token - int decimalDiff = decimals - m_selectedToken->decimals; - m_selectedToken->address = address; - m_selectedToken->sender = sender; - m_selectedToken->symbol = symbol; - m_selectedToken->decimals = decimals; - m_selectedToken->balance = balance; - - // Convert values for different number of decimals - int256_t totalSupply(balance); - int256_t value(ui->lineEditAmount->value()); - if(value != 0) - { - for(int i = 0; i < decimalDiff; i++) - { - value *= 10; - } - for(int i = decimalDiff; i < 0; i++) - { - value /= 10; - } - } - if(value > totalSupply) - { - value = totalSupply; - } - - // Update the amount field with the current token data - ui->lineEditAmount->clear(); - ui->lineEditAmount->setDecimalUnits(decimals); - ui->lineEditAmount->setTotalSupply(totalSupply); - if(value != 0) - { - ui->lineEditAmount->setValue(value); - } -} +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const CAmount SINGLE_STEP = 0.00000001*COIN; + +struct SelectedToken{ + std::string address; + std::string sender; + std::string symbol; + int8_t decimals; + std::string balance; + SelectedToken(): + decimals(0) + {} +}; + +SendTokenPage::SendTokenPage(QWidget *parent) : + QWidget(parent), + ui(new Ui::SendTokenPage), + m_model(0), + m_clientModel(0), + m_tokenABI(0), + m_selectedToken(0) +{ + // Setup ui components + ui->setupUi(this); + + // Set stylesheet + SetObjectStyleSheet(ui->clearButton, StyleSheetNames::ButtonBlack); + + ui->labelPayTo->setToolTip(tr("The address that will receive the tokens.")); + ui->labelAmount->setToolTip(tr("The amount in Token to send.")); + ui->labelDescription->setToolTip(tr("Optional description for transaction.")); + m_tokenABI = new Token(); + m_selectedToken = new SelectedToken(); + + // Set defaults + ui->lineEditGasPrice->setValue(DEFAULT_GAS_PRICE); + ui->lineEditGasPrice->setSingleStep(SINGLE_STEP); + ui->lineEditGasLimit->setMaximum(DEFAULT_GAS_LIMIT_OP_SEND); + ui->lineEditGasLimit->setValue(DEFAULT_GAS_LIMIT_OP_SEND); + ui->confirmButton->setEnabled(false); + + // Connect signals with slots + connect(ui->lineEditPayTo, &QValidatedLineEdit::textChanged, this, &SendTokenPage::on_updateConfirmButton); + connect(ui->lineEditAmount, &TokenAmountField::valueChanged,this, &SendTokenPage::on_updateConfirmButton); + connect(ui->confirmButton, &QPushButton::clicked, this, &SendTokenPage::on_confirmClicked); + + ui->lineEditPayTo->setCheckValidator(new BitcoinAddressCheckValidator(parent, true)); +} + +SendTokenPage::~SendTokenPage() +{ + delete ui; + + if(m_tokenABI) + delete m_tokenABI; + m_tokenABI = 0; +} + +void SendTokenPage::setModel(WalletModel *_model) +{ + m_model = _model; + m_tokenABI->setModel(m_model); + + if (m_model && m_model->getOptionsModel()) + connect(m_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &SendTokenPage::updateDisplayUnit); + + // update the display unit, to not use the default ("QTUM") + updateDisplayUnit(); +} + +void SendTokenPage::setClientModel(ClientModel *_clientModel) +{ + m_clientModel = _clientModel; + + if (m_clientModel) + { + connect(m_clientModel, SIGNAL(gasInfoChanged(quint64, quint64, quint64)), this, SLOT(on_gasInfoChanged(quint64, quint64, quint64))); + } +} + +void SendTokenPage::clearAll() +{ + ui->lineEditPayTo->setText(""); + ui->lineEditAmount->clear(); + ui->lineEditDescription->setText(""); + ui->lineEditGasLimit->setValue(DEFAULT_GAS_LIMIT_OP_SEND); + ui->lineEditGasPrice->setValue(DEFAULT_GAS_PRICE); +} + +bool SendTokenPage::isValidAddress() +{ + ui->lineEditPayTo->checkValidity(); + return ui->lineEditPayTo->isValid(); +} + +bool SendTokenPage::isDataValid() +{ + bool dataValid = true; + + if(!isValidAddress()) + dataValid = false; + if(!ui->lineEditAmount->validate()) + dataValid = false; + if(ui->lineEditAmount->value(0) <= 0) + { + ui->lineEditAmount->setValid(false); + dataValid = false; + } + return dataValid; +} + +void SendTokenPage::on_clearButton_clicked() +{ + clearAll(); +} + +void SendTokenPage::on_gasInfoChanged(quint64 blockGasLimit, quint64 minGasPrice, quint64 nGasPrice) +{ + Q_UNUSED(nGasPrice); + ui->labelGasLimit->setToolTip(tr("Gas limit. Default = %1, Max = %2").arg(DEFAULT_GAS_LIMIT_OP_CREATE).arg(blockGasLimit)); + ui->labelGasPrice->setToolTip(tr("Gas price: QTUM price per gas unit. Default = %1, Min = %2").arg(QString::fromStdString(FormatMoney(DEFAULT_GAS_PRICE))).arg(QString::fromStdString(FormatMoney(minGasPrice)))); + ui->lineEditGasPrice->SetMinValue(minGasPrice); + ui->lineEditGasLimit->setMaximum(blockGasLimit); +} + +void SendTokenPage::on_updateConfirmButton() +{ + bool enabled = true; + if(ui->lineEditPayTo->text().isEmpty() || ui->lineEditAmount->text().isEmpty()) + { + enabled = false; + } + + ui->confirmButton->setEnabled(enabled); +} + +void SendTokenPage::on_confirmClicked() +{ + if(!isDataValid()) + return; + + WalletModel::UnlockContext ctx(m_model->requestUnlock()); + if(!ctx.isValid()) + { + return; + } + + if(m_model) + { + int unit = BitcoinUnits::BTC; + uint64_t gasLimit = ui->lineEditGasLimit->value(); + CAmount gasPrice = ui->lineEditGasPrice->value(); + std::string label = ui->lineEditDescription->text().trimmed().toStdString(); + + m_tokenABI->setAddress(m_selectedToken->address); + m_tokenABI->setSender(m_selectedToken->sender); + m_tokenABI->setGasLimit(QString::number(gasLimit).toStdString()); + m_tokenABI->setGasPrice(BitcoinUnits::format(unit, gasPrice, false, BitcoinUnits::separatorNever).toStdString()); + + std::string toAddress = ui->lineEditPayTo->text().toStdString(); + std::string amountToSend = ui->lineEditAmount->text().toStdString(); + QString amountFormated = BitcoinUnits::formatToken(m_selectedToken->decimals, ui->lineEditAmount->value(), false, BitcoinUnits::separatorAlways); + + QString questionString = tr("Are you sure you want to send?

"); + questionString.append(tr("%1 %2 to ") + .arg(amountFormated).arg(QString::fromStdString(m_selectedToken->symbol))); + questionString.append(tr("
%3
") + .arg(QString::fromStdString(toAddress))); + + SendConfirmationDialog confirmationDialog(tr("Confirm send token."), questionString, 3, this); + confirmationDialog.exec(); + QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result(); + if(retval == QMessageBox::Yes) + { + if(m_tokenABI->transfer(toAddress, amountToSend, true)) + { + interfaces::TokenTx tokenTx; + tokenTx.contract_address = m_selectedToken->address; + tokenTx.sender_address = m_selectedToken->sender; + tokenTx.receiver_address = toAddress; + dev::u256 nValue(amountToSend); + tokenTx.value = u256Touint(nValue); + tokenTx.tx_hash = uint256S(m_tokenABI->getTxId()); + tokenTx.label = label; + m_model->wallet().addTokenTxEntry(tokenTx); + } + else + { + QMessageBox::warning(this, tr("Send token"), QString::fromStdString(m_tokenABI->getErrorMessage())); + } + clearAll(); + } + } +} + +void SendTokenPage::updateDisplayUnit() +{ + if(m_model && m_model->getOptionsModel()) + { + // Update gasPriceAmount with the current unit + ui->lineEditGasPrice->setDisplayUnit(m_model->getOptionsModel()->getDisplayUnit()); + } +} + +void SendTokenPage::setTokenData(std::string address, std::string sender, std::string symbol, int8_t decimals, std::string balance) +{ + // Update data with the current token + int decimalDiff = decimals - m_selectedToken->decimals; + m_selectedToken->address = address; + m_selectedToken->sender = sender; + m_selectedToken->symbol = symbol; + m_selectedToken->decimals = decimals; + m_selectedToken->balance = balance; + + // Convert values for different number of decimals + int256_t totalSupply(balance); + int256_t value(ui->lineEditAmount->value()); + if(value != 0) + { + for(int i = 0; i < decimalDiff; i++) + { + value *= 10; + } + for(int i = decimalDiff; i < 0; i++) + { + value /= 10; + } + } + if(value > totalSupply) + { + value = totalSupply; + } + + // Update the amount field with the current token data + ui->lineEditAmount->clear(); + ui->lineEditAmount->setDecimalUnits(decimals); + ui->lineEditAmount->setTotalSupply(totalSupply); + if(value != 0) + { + ui->lineEditAmount->setValue(value); + } +} diff --git a/src/qt/styleSheet.cpp b/src/qt/styleSheet.cpp index a44ede3d5b..6a08137de7 100755 --- a/src/qt/styleSheet.cpp +++ b/src/qt/styleSheet.cpp @@ -1,139 +1,139 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const QString STYLE_FORMAT = ":/styles/%1"; -static const QColor LINK_COLOR = "#2d9ad0"; - -class QtumStyle : public QProxyStyle -{ -public: - - void polish(QWidget *widget) - { - if(widget && widget->inherits("QComboBox")) - { - QComboBox* comboBox = (QComboBox*)widget; - if(comboBox->view() && comboBox->view()->inherits("QComboBoxListView")) - { - comboBox->setView(new QListView()); - qApp->processEvents(); - } - - if(comboBox->view() && comboBox->view()->parentWidget()) - { - QWidget* parent = comboBox->view()->parentWidget(); - parent->setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint); - parent->setAttribute(Qt::WA_TranslucentBackground); - } - } - if(widget && widget->inherits("QMessageBox")) - { - QMessageBox* messageBox = (QMessageBox*)widget; - QPixmap iconPixmap; - QMessageBox::Icon icon = messageBox->icon(); - switch (icon) - { - case QMessageBox::Information: - iconPixmap = QPixmap(":/styles/app-icons/message_info"); - break; - case QMessageBox::Warning: - iconPixmap = QPixmap(":/styles/app-icons/message_warning"); - break; - case QMessageBox::Critical: - iconPixmap = QPixmap(":/styles/app-icons/message_critical"); - break; - case QMessageBox::Question: - iconPixmap = QPixmap(":/styles/app-icons/message_question"); - break; - default: - QProxyStyle::polish(widget); - return; - } - messageBox->setIconPixmap(iconPixmap.scaled(45,49)); - } - if(widget && widget->inherits("QPushButton")) - { - QPushButton* button = (QPushButton*)widget; - button->setText(button->text().toUpper()); - } - if(widget && widget->inherits("QLineEdit")) - { - QLineEdit* lineEdit = (QLineEdit*)widget; - if(lineEdit->isReadOnly()) - { - lineEdit->setFocusPolicy(Qt::ClickFocus); - } - } - - QProxyStyle::polish(widget); - } -}; - -StyleSheet &StyleSheet::instance() -{ - static StyleSheet inst; - return inst; -} - -StyleSheet::StyleSheet() -{} - -void StyleSheet::setStyleSheet(QWidget *widget, const QString &style_name) -{ - setObjectStyleSheet(widget, style_name); -} - -void StyleSheet::setStyleSheet(QApplication *app, const QString& style_name) -{ - QStyle* mainStyle = QStyleFactory::create("fusion"); - QtumStyle* qtumStyle = new QtumStyle; - qtumStyle->setBaseStyle(mainStyle); - app->setStyle(qtumStyle); - - QPalette mainPalette(app->palette()); - mainPalette.setColor(QPalette::Link, LINK_COLOR); - app->setPalette(mainPalette); - - // Increase the font size slightly for Windows and MAC - QFont font = app->font(); - qreal fontSize = font.pointSizeF(); - qreal multiplier = 1; -#if defined(Q_OS_WIN) || defined(Q_OS_MAC) - multiplier = 1.1; -#endif - font.setPointSizeF(fontSize * multiplier); - app->setFont(font); - - setObjectStyleSheet(app, style_name); -} - -QString StyleSheet::getStyleSheet(const QString &style_name) -{ - QString style; - QFile file(STYLE_FORMAT.arg(style_name)); - if(file.open(QIODevice::ReadOnly)) - { - style = file.readAll(); - m_cacheStyles[style_name] = style; - } - return style; -} - -template -void StyleSheet::setObjectStyleSheet(T *object, const QString &style_name) -{ - QString style_value = m_cacheStyles.contains(style_name) ? m_cacheStyles[style_name] : getStyleSheet(style_name); - object->setStyleSheet(style_value); -} +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const QString STYLE_FORMAT = ":/styles/%1"; +static const QColor LINK_COLOR = "#2d9ad0"; + +class QtumStyle : public QProxyStyle +{ +public: + + void polish(QWidget *widget) + { + if(widget && widget->inherits("QComboBox")) + { + QComboBox* comboBox = (QComboBox*)widget; + if(comboBox->view() && comboBox->view()->inherits("QComboBoxListView")) + { + comboBox->setView(new QListView()); + qApp->processEvents(); + } + + if(comboBox->view() && comboBox->view()->parentWidget()) + { + QWidget* parent = comboBox->view()->parentWidget(); + parent->setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint); + parent->setAttribute(Qt::WA_TranslucentBackground); + } + } + if(widget && widget->inherits("QMessageBox")) + { + QMessageBox* messageBox = (QMessageBox*)widget; + QPixmap iconPixmap; + QMessageBox::Icon icon = messageBox->icon(); + switch (icon) + { + case QMessageBox::Information: + iconPixmap = QPixmap(":/styles/app-icons/message_info"); + break; + case QMessageBox::Warning: + iconPixmap = QPixmap(":/styles/app-icons/message_warning"); + break; + case QMessageBox::Critical: + iconPixmap = QPixmap(":/styles/app-icons/message_critical"); + break; + case QMessageBox::Question: + iconPixmap = QPixmap(":/styles/app-icons/message_question"); + break; + default: + QProxyStyle::polish(widget); + return; + } + messageBox->setIconPixmap(iconPixmap.scaled(45,49)); + } + if(widget && widget->inherits("QPushButton")) + { + QPushButton* button = (QPushButton*)widget; + button->setText(button->text().toUpper()); + } + if(widget && widget->inherits("QLineEdit")) + { + QLineEdit* lineEdit = (QLineEdit*)widget; + if(lineEdit->isReadOnly()) + { + lineEdit->setFocusPolicy(Qt::ClickFocus); + } + } + + QProxyStyle::polish(widget); + } +}; + +StyleSheet &StyleSheet::instance() +{ + static StyleSheet inst; + return inst; +} + +StyleSheet::StyleSheet() +{} + +void StyleSheet::setStyleSheet(QWidget *widget, const QString &style_name) +{ + setObjectStyleSheet(widget, style_name); +} + +void StyleSheet::setStyleSheet(QApplication *app, const QString& style_name) +{ + QStyle* mainStyle = QStyleFactory::create("fusion"); + QtumStyle* qtumStyle = new QtumStyle; + qtumStyle->setBaseStyle(mainStyle); + app->setStyle(qtumStyle); + + QPalette mainPalette(app->palette()); + mainPalette.setColor(QPalette::Link, LINK_COLOR); + app->setPalette(mainPalette); + + // Increase the font size slightly for Windows and MAC + QFont font = app->font(); + qreal fontSize = font.pointSizeF(); + qreal multiplier = 1; +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) + multiplier = 1.1; +#endif + font.setPointSizeF(fontSize * multiplier); + app->setFont(font); + + setObjectStyleSheet(app, style_name); +} + +QString StyleSheet::getStyleSheet(const QString &style_name) +{ + QString style; + QFile file(STYLE_FORMAT.arg(style_name)); + if(file.open(QIODevice::ReadOnly)) + { + style = file.readAll(); + m_cacheStyles[style_name] = style; + } + return style; +} + +template +void StyleSheet::setObjectStyleSheet(T *object, const QString &style_name) +{ + QString style_value = m_cacheStyles.contains(style_name) ? m_cacheStyles[style_name] : getStyleSheet(style_name); + object->setStyleSheet(style_value); +} diff --git a/src/qt/tabbarinfo.cpp b/src/qt/tabbarinfo.cpp index 678f9bb10d..ced697fba4 100644 --- a/src/qt/tabbarinfo.cpp +++ b/src/qt/tabbarinfo.cpp @@ -1,224 +1,224 @@ -#include -#include -#include - -TabBarInfo::TabBarInfo(QStackedWidget *parent) : - QObject(parent), - m_current(0), - m_stack(parent), - m_tabBar(0), - m_attached(false), - m_iconCloseTab(0) -{} - -bool TabBarInfo::addTab(int index, const QString &name) -{ - if(m_stack->count() <= index) - { - return false; - } - m_mapName[index] = name; - m_mapVisible[index] = true; - update(); - return true; -} - -void TabBarInfo::removeTab(int index) -{ - int count = m_stack->count(); - if(count <= index) - { - return; - } - - QMap mapName; - QMap mapVisible; - for(int i = 0; i < count; i++) - { - if(i < index) - { - mapName[i] = m_mapName[i]; - mapVisible[i] = m_mapVisible[i]; - } - else if(i > index) - { - mapName[i-1] = m_mapName[i]; - mapVisible[i-1] = m_mapVisible[i]; - } - } - m_mapName = mapName; - m_mapVisible = mapVisible; - - QWidget* widget = m_stack->widget(index); - m_stack->removeWidget(widget); - widget->deleteLater(); - - update(); -} - -void TabBarInfo::setTabVisible(int index, bool visible) -{ - if(m_stack->count() > index) - { - m_mapVisible[index] = visible; - } - update(); -} - -void TabBarInfo::attach(QTabBar *tabBar, QIcon* iconCloseTab) -{ - m_tabBar = tabBar; - m_iconCloseTab = iconCloseTab; - if(m_tabBar) - { - connect(m_tabBar, &QTabBar::currentChanged, this, &TabBarInfo::on_currentChanged); - } - update(); - m_attached = true; -} - -void TabBarInfo::detach() -{ - m_attached = false; - if(m_tabBar) - { - disconnect(m_tabBar, 0, 0, 0); - int count = m_tabBar->count(); - for(int i = count - 1; i >= 0; i--) - { - m_tabBar->removeTab(i); - } - m_tabBar = 0; - } -} - -void TabBarInfo::setCurrent(int index) -{ - m_current = index; - update(); -} - -void TabBarInfo::clear() -{ - for(int i = m_stack->count() - 1; i > 0; i--) - { - QWidget* widget = m_stack->widget(i); - m_stack->removeWidget(widget); - widget->deleteLater(); - } - - QMap mapName; - QMap mapVisible; - mapName[0] = m_mapName[0]; - mapVisible[0] = m_mapVisible[0]; - m_mapName = mapName; - m_mapVisible = mapVisible; - m_current = 0; - - update(); -} - -void TabBarInfo::on_currentChanged(int index) -{ - if(m_attached && index < m_mapTabInfo.keys().size()) - { - int tab = m_mapTabInfo[index]; - if(tab < m_stack->count()) - { - m_stack->setCurrentIndex(tab); - } - m_current = tab; - } -} - -void TabBarInfo::on_closeButtonClick() -{ - if(m_attached && m_tabBar) - { - QObject* obj = sender(); - if(obj) - { - for(int index = 0; index < m_tabBar->count(); index++) - { - if(obj == m_tabBar->tabButton(index, QTabBar::RightSide)) - { - if(index < m_mapTabInfo.keys().size()) - { - int tab = m_mapTabInfo[index]; - removeTab(tab); - } - break; - } - } - } - } -} - -void TabBarInfo::update() -{ - if(m_tabBar) - { - // Populate the tab bar - QMap mapTabInfo; - int currentTab = 0; - int numberTabs = m_mapName.keys().size(); - for(int i = 0; i < numberTabs; i++) - { - bool visible = m_mapVisible[i]; - if(visible) - { - if(m_tabBar->count() > currentTab) - { - m_tabBar->setTabText(currentTab, m_mapName[i]); - } - else - { - m_tabBar->addTab(m_mapName[i]); - int count = m_tabBar->count(); - m_tabBar->setTabButton(count - 1, QTabBar::LeftSide, 0); - if(count == 1) - { - m_tabBar->setTabButton(0, QTabBar::RightSide, 0); - } - else - { - if(m_iconCloseTab) - { - QToolButton* tool = new QToolButton(m_tabBar); - tool->setIcon(*m_iconCloseTab); - tool->setObjectName("tabBarTool"); - tool->setFixedSize(16, 16); - m_tabBar->setTabButton(count - 1, QTabBar::RightSide, tool); - connect(tool, &QToolButton::clicked, this, &TabBarInfo::on_closeButtonClick); - } - } - } - mapTabInfo[currentTab] = i; - currentTab++; - } - } - int count = m_tabBar->count(); - if(currentTab < count) - { - for(int i = count - 1; i >= currentTab; i--) - { - m_tabBar->removeTab(i); - } - } - m_mapTabInfo = mapTabInfo; - - // Set the current tab - int tabCurrent = m_mapTabInfo[m_tabBar->currentIndex()]; - if(tabCurrent != m_current) - { - for(int i = 0; i < m_tabBar->count(); i++) - { - tabCurrent = m_mapTabInfo[i]; - if(tabCurrent == m_current) - { - m_tabBar->setCurrentIndex(tabCurrent); - } - } - } - } -} +#include +#include +#include + +TabBarInfo::TabBarInfo(QStackedWidget *parent) : + QObject(parent), + m_current(0), + m_stack(parent), + m_tabBar(0), + m_attached(false), + m_iconCloseTab(0) +{} + +bool TabBarInfo::addTab(int index, const QString &name) +{ + if(m_stack->count() <= index) + { + return false; + } + m_mapName[index] = name; + m_mapVisible[index] = true; + update(); + return true; +} + +void TabBarInfo::removeTab(int index) +{ + int count = m_stack->count(); + if(count <= index) + { + return; + } + + QMap mapName; + QMap mapVisible; + for(int i = 0; i < count; i++) + { + if(i < index) + { + mapName[i] = m_mapName[i]; + mapVisible[i] = m_mapVisible[i]; + } + else if(i > index) + { + mapName[i-1] = m_mapName[i]; + mapVisible[i-1] = m_mapVisible[i]; + } + } + m_mapName = mapName; + m_mapVisible = mapVisible; + + QWidget* widget = m_stack->widget(index); + m_stack->removeWidget(widget); + widget->deleteLater(); + + update(); +} + +void TabBarInfo::setTabVisible(int index, bool visible) +{ + if(m_stack->count() > index) + { + m_mapVisible[index] = visible; + } + update(); +} + +void TabBarInfo::attach(QTabBar *tabBar, QIcon* iconCloseTab) +{ + m_tabBar = tabBar; + m_iconCloseTab = iconCloseTab; + if(m_tabBar) + { + connect(m_tabBar, &QTabBar::currentChanged, this, &TabBarInfo::on_currentChanged); + } + update(); + m_attached = true; +} + +void TabBarInfo::detach() +{ + m_attached = false; + if(m_tabBar) + { + disconnect(m_tabBar, 0, 0, 0); + int count = m_tabBar->count(); + for(int i = count - 1; i >= 0; i--) + { + m_tabBar->removeTab(i); + } + m_tabBar = 0; + } +} + +void TabBarInfo::setCurrent(int index) +{ + m_current = index; + update(); +} + +void TabBarInfo::clear() +{ + for(int i = m_stack->count() - 1; i > 0; i--) + { + QWidget* widget = m_stack->widget(i); + m_stack->removeWidget(widget); + widget->deleteLater(); + } + + QMap mapName; + QMap mapVisible; + mapName[0] = m_mapName[0]; + mapVisible[0] = m_mapVisible[0]; + m_mapName = mapName; + m_mapVisible = mapVisible; + m_current = 0; + + update(); +} + +void TabBarInfo::on_currentChanged(int index) +{ + if(m_attached && index < m_mapTabInfo.keys().size()) + { + int tab = m_mapTabInfo[index]; + if(tab < m_stack->count()) + { + m_stack->setCurrentIndex(tab); + } + m_current = tab; + } +} + +void TabBarInfo::on_closeButtonClick() +{ + if(m_attached && m_tabBar) + { + QObject* obj = sender(); + if(obj) + { + for(int index = 0; index < m_tabBar->count(); index++) + { + if(obj == m_tabBar->tabButton(index, QTabBar::RightSide)) + { + if(index < m_mapTabInfo.keys().size()) + { + int tab = m_mapTabInfo[index]; + removeTab(tab); + } + break; + } + } + } + } +} + +void TabBarInfo::update() +{ + if(m_tabBar) + { + // Populate the tab bar + QMap mapTabInfo; + int currentTab = 0; + int numberTabs = m_mapName.keys().size(); + for(int i = 0; i < numberTabs; i++) + { + bool visible = m_mapVisible[i]; + if(visible) + { + if(m_tabBar->count() > currentTab) + { + m_tabBar->setTabText(currentTab, m_mapName[i]); + } + else + { + m_tabBar->addTab(m_mapName[i]); + int count = m_tabBar->count(); + m_tabBar->setTabButton(count - 1, QTabBar::LeftSide, 0); + if(count == 1) + { + m_tabBar->setTabButton(0, QTabBar::RightSide, 0); + } + else + { + if(m_iconCloseTab) + { + QToolButton* tool = new QToolButton(m_tabBar); + tool->setIcon(*m_iconCloseTab); + tool->setObjectName("tabBarTool"); + tool->setFixedSize(16, 16); + m_tabBar->setTabButton(count - 1, QTabBar::RightSide, tool); + connect(tool, &QToolButton::clicked, this, &TabBarInfo::on_closeButtonClick); + } + } + } + mapTabInfo[currentTab] = i; + currentTab++; + } + } + int count = m_tabBar->count(); + if(currentTab < count) + { + for(int i = count - 1; i >= currentTab; i--) + { + m_tabBar->removeTab(i); + } + } + m_mapTabInfo = mapTabInfo; + + // Set the current tab + int tabCurrent = m_mapTabInfo[m_tabBar->currentIndex()]; + if(tabCurrent != m_current) + { + for(int i = 0; i < m_tabBar->count(); i++) + { + tabCurrent = m_mapTabInfo[i]; + if(tabCurrent == m_current) + { + m_tabBar->setCurrentIndex(tabCurrent); + } + } + } + } +} diff --git a/src/qt/tokenamountfield.cpp b/src/qt/tokenamountfield.cpp index c7a48af093..5febbf938f 100644 --- a/src/qt/tokenamountfield.cpp +++ b/src/qt/tokenamountfield.cpp @@ -1,274 +1,274 @@ -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -/** QSpinBox that uses fixed-point numbers internally and uses our own - * formatting/parsing functions. - */ -class TokenAmountSpinBox: public QAbstractSpinBox -{ - Q_OBJECT - -public: - explicit TokenAmountSpinBox(QWidget *parent): - QAbstractSpinBox(parent), - decimalUnits(0), - totalSupply(0), - singleStep(0), - minAmount(0) - { - setAlignment(Qt::AlignRight); - - connect(lineEdit(), &QLineEdit::textEdited, this, &TokenAmountSpinBox::valueChanged); - } - - QValidator::State validate(QString &text, int &pos) const - { - if(text.isEmpty()) - return QValidator::Intermediate; - bool valid = false; - parse(text, &valid); - /* Make sure we return Intermediate so that fixup() is called on defocus */ - return valid ? QValidator::Intermediate : QValidator::Invalid; - } - - void fixup(QString &input) const - { - bool valid = false; - int256_t val = parse(input, &valid); - val = getMax(val, minAmount); - if(valid) - { - input = BitcoinUnits::formatToken(decimalUnits, val, false, BitcoinUnits::separatorAlways); - lineEdit()->setText(input); - } - } - - int256_t value(bool *valid_out=0) const - { - return parse(text(), valid_out); - } - - void setValue(const int256_t& value) - { - int256_t val = getMax(value, minAmount); - lineEdit()->setText(BitcoinUnits::formatToken(decimalUnits, val, false, BitcoinUnits::separatorAlways)); - Q_EMIT valueChanged(); - } - - void stepBy(int steps) - { - bool valid = false; - int256_t val = value(&valid); - val = val + steps * singleStep; - val = getMin(getMax(val, minAmount), totalSupply); - setValue(val); - } - - int256_t minimum() const - { - return minAmount; - } - - void setMinimum(const int256_t& min) - { - minAmount = min; - Q_EMIT valueChanged(); - } - - void setTotalSupply(const int256_t &value) - { - totalSupply = value; - } - - void setDecimalUnits(int value) - { - decimalUnits = value; - setSingleStep(); - } - -private: - int decimalUnits; // Token decimal units - int256_t totalSupply; // Token total supply - int256_t singleStep; - int256_t minAmount; - - /** - * Parse a string into a number of base monetary units and - * return validity. - * @note Must return 0 if !valid. - */ - int256_t parse(const QString &text, bool *valid_out=0) const - { - int256_t val = 0; - bool valid = BitcoinUnits::parseToken(decimalUnits, text, &val); - if(valid) - { - if(val < 0 || val > totalSupply) - valid = false; - } - if(valid_out) - *valid_out = valid; - return valid ? val : 0; - } - - void setSingleStep() - { - int256_t step = 1; - for(int i = 1; i < decimalUnits; i++) - { - step *= 10; - } - singleStep = step; - } - - int256_t getMax(int256_t a, int256_t b) const{ - return a > b ? a : b; - } - - int256_t getMin(int256_t a, int256_t b) const{ - return a > b ? b : a; - } - -protected: - bool event(QEvent *event) - { - if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) - { - QKeyEvent *keyEvent = static_cast(event); - if (keyEvent->key() == Qt::Key_Comma) - { - // Translate a comma into a period - QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count()); - return QAbstractSpinBox::event(&periodKeyEvent); - } - } - return QAbstractSpinBox::event(event); - } - - StepEnabled stepEnabled() const - { - if (isReadOnly()) // Disable steps when TokenAmountSpinBox is read-only - return StepNone; - if (text().isEmpty()) // Allow step-up with empty field - return StepUpEnabled; - - StepEnabled rv = 0; - bool valid = false; - int256_t val = value(&valid); - if(valid) - { - if(val > minAmount) - rv |= StepDownEnabled; - if(val < totalSupply) - rv |= StepUpEnabled; - } - return rv; - } - -Q_SIGNALS: - void valueChanged(); -}; - -#include "tokenamountfield.moc" - -TokenAmountField::TokenAmountField(QWidget *parent) : - QWidget(parent), - amount(0) -{ - amount = new TokenAmountSpinBox(this); - amount->setLocale(QLocale::c()); - amount->installEventFilter(this); - - QHBoxLayout *layout = new QHBoxLayout(this); - layout->addWidget(amount); - - layout->setContentsMargins(0,0,0,0); - - setLayout(layout); - connect(amount, &TokenAmountSpinBox::valueChanged, this, &TokenAmountField::valueChanged); -} - -void TokenAmountField::clear() -{ - amount->clear(); -} - -void TokenAmountField::setEnabled(bool fEnabled) -{ - amount->setEnabled(fEnabled); -} - -bool TokenAmountField::validate() -{ - bool valid = false; - value(&valid); - setValid(valid); - return valid; -} - -void TokenAmountField::setValid(bool valid) -{ - if (valid) - amount->setStyleSheet(""); - else - SetObjectStyleSheet(amount, StyleSheetNames::Invalid); -} - -bool TokenAmountField::eventFilter(QObject *object, QEvent *event) -{ - if (event->type() == QEvent::FocusIn) - { - // Clear invalid flag on focus - setValid(true); - } - return QWidget::eventFilter(object, event); -} - -int256_t TokenAmountField::value(bool *valid_out) const -{ - return amount->value(valid_out); -} - -void TokenAmountField::setValue(const int256_t& value) -{ - amount->setValue(value); -} - -void TokenAmountField::setReadOnly(bool fReadOnly) -{ - amount->setReadOnly(fReadOnly); -} - -int256_t TokenAmountField::minimum() const -{ - return amount->minimum(); -} - -void TokenAmountField::setMinimum(const int256_t& min) -{ - amount->setMinimum(min); -} - -void TokenAmountField::setTotalSupply(const int256_t &value) -{ - amount->setTotalSupply(value); -} - -void TokenAmountField::setDecimalUnits(int value) -{ - amount->setDecimalUnits(value); -} - -QString TokenAmountField::text() const -{ - return QString::fromStdString(value().str()); -} +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +/** QSpinBox that uses fixed-point numbers internally and uses our own + * formatting/parsing functions. + */ +class TokenAmountSpinBox: public QAbstractSpinBox +{ + Q_OBJECT + +public: + explicit TokenAmountSpinBox(QWidget *parent): + QAbstractSpinBox(parent), + decimalUnits(0), + totalSupply(0), + singleStep(0), + minAmount(0) + { + setAlignment(Qt::AlignRight); + + connect(lineEdit(), &QLineEdit::textEdited, this, &TokenAmountSpinBox::valueChanged); + } + + QValidator::State validate(QString &text, int &pos) const + { + if(text.isEmpty()) + return QValidator::Intermediate; + bool valid = false; + parse(text, &valid); + /* Make sure we return Intermediate so that fixup() is called on defocus */ + return valid ? QValidator::Intermediate : QValidator::Invalid; + } + + void fixup(QString &input) const + { + bool valid = false; + int256_t val = parse(input, &valid); + val = getMax(val, minAmount); + if(valid) + { + input = BitcoinUnits::formatToken(decimalUnits, val, false, BitcoinUnits::separatorAlways); + lineEdit()->setText(input); + } + } + + int256_t value(bool *valid_out=0) const + { + return parse(text(), valid_out); + } + + void setValue(const int256_t& value) + { + int256_t val = getMax(value, minAmount); + lineEdit()->setText(BitcoinUnits::formatToken(decimalUnits, val, false, BitcoinUnits::separatorAlways)); + Q_EMIT valueChanged(); + } + + void stepBy(int steps) + { + bool valid = false; + int256_t val = value(&valid); + val = val + steps * singleStep; + val = getMin(getMax(val, minAmount), totalSupply); + setValue(val); + } + + int256_t minimum() const + { + return minAmount; + } + + void setMinimum(const int256_t& min) + { + minAmount = min; + Q_EMIT valueChanged(); + } + + void setTotalSupply(const int256_t &value) + { + totalSupply = value; + } + + void setDecimalUnits(int value) + { + decimalUnits = value; + setSingleStep(); + } + +private: + int decimalUnits; // Token decimal units + int256_t totalSupply; // Token total supply + int256_t singleStep; + int256_t minAmount; + + /** + * Parse a string into a number of base monetary units and + * return validity. + * @note Must return 0 if !valid. + */ + int256_t parse(const QString &text, bool *valid_out=0) const + { + int256_t val = 0; + bool valid = BitcoinUnits::parseToken(decimalUnits, text, &val); + if(valid) + { + if(val < 0 || val > totalSupply) + valid = false; + } + if(valid_out) + *valid_out = valid; + return valid ? val : 0; + } + + void setSingleStep() + { + int256_t step = 1; + for(int i = 1; i < decimalUnits; i++) + { + step *= 10; + } + singleStep = step; + } + + int256_t getMax(int256_t a, int256_t b) const{ + return a > b ? a : b; + } + + int256_t getMin(int256_t a, int256_t b) const{ + return a > b ? b : a; + } + +protected: + bool event(QEvent *event) + { + if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) + { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Comma) + { + // Translate a comma into a period + QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count()); + return QAbstractSpinBox::event(&periodKeyEvent); + } + } + return QAbstractSpinBox::event(event); + } + + StepEnabled stepEnabled() const + { + if (isReadOnly()) // Disable steps when TokenAmountSpinBox is read-only + return StepNone; + if (text().isEmpty()) // Allow step-up with empty field + return StepUpEnabled; + + StepEnabled rv = 0; + bool valid = false; + int256_t val = value(&valid); + if(valid) + { + if(val > minAmount) + rv |= StepDownEnabled; + if(val < totalSupply) + rv |= StepUpEnabled; + } + return rv; + } + +Q_SIGNALS: + void valueChanged(); +}; + +#include "tokenamountfield.moc" + +TokenAmountField::TokenAmountField(QWidget *parent) : + QWidget(parent), + amount(0) +{ + amount = new TokenAmountSpinBox(this); + amount->setLocale(QLocale::c()); + amount->installEventFilter(this); + + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(amount); + + layout->setContentsMargins(0,0,0,0); + + setLayout(layout); + connect(amount, &TokenAmountSpinBox::valueChanged, this, &TokenAmountField::valueChanged); +} + +void TokenAmountField::clear() +{ + amount->clear(); +} + +void TokenAmountField::setEnabled(bool fEnabled) +{ + amount->setEnabled(fEnabled); +} + +bool TokenAmountField::validate() +{ + bool valid = false; + value(&valid); + setValid(valid); + return valid; +} + +void TokenAmountField::setValid(bool valid) +{ + if (valid) + amount->setStyleSheet(""); + else + SetObjectStyleSheet(amount, StyleSheetNames::Invalid); +} + +bool TokenAmountField::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::FocusIn) + { + // Clear invalid flag on focus + setValid(true); + } + return QWidget::eventFilter(object, event); +} + +int256_t TokenAmountField::value(bool *valid_out) const +{ + return amount->value(valid_out); +} + +void TokenAmountField::setValue(const int256_t& value) +{ + amount->setValue(value); +} + +void TokenAmountField::setReadOnly(bool fReadOnly) +{ + amount->setReadOnly(fReadOnly); +} + +int256_t TokenAmountField::minimum() const +{ + return amount->minimum(); +} + +void TokenAmountField::setMinimum(const int256_t& min) +{ + amount->setMinimum(min); +} + +void TokenAmountField::setTotalSupply(const int256_t &value) +{ + amount->setTotalSupply(value); +} + +void TokenAmountField::setDecimalUnits(int value) +{ + amount->setDecimalUnits(value); +} + +QString TokenAmountField::text() const +{ + return QString::fromStdString(value().str()); +} diff --git a/src/qt/tokendescdialog.cpp b/src/qt/tokendescdialog.cpp index 3b39a8b84e..ec5b486fed 100644 --- a/src/qt/tokendescdialog.cpp +++ b/src/qt/tokendescdialog.cpp @@ -1,26 +1,26 @@ -#include -#include - -#include -#include - -#include - -TokenDescDialog::TokenDescDialog(const QModelIndex &idx, QWidget *parent) : - QDialog(parent), - ui(new Ui::TokenDescDialog) -{ - ui->setupUi(this); - - // Set stylesheet - SetObjectStyleSheet(this, StyleSheetNames::ScrollBarDark); - - setWindowTitle(tr("Details for %1").arg(idx.data(TokenTransactionTableModel::TxHashRole).toString())); - QString desc = idx.data(TokenTransactionTableModel::LongDescriptionRole).toString(); - ui->detailText->setHtml(desc); -} - -TokenDescDialog::~TokenDescDialog() -{ - delete ui; -} +#include +#include + +#include +#include + +#include + +TokenDescDialog::TokenDescDialog(const QModelIndex &idx, QWidget *parent) : + QDialog(parent), + ui(new Ui::TokenDescDialog) +{ + ui->setupUi(this); + + // Set stylesheet + SetObjectStyleSheet(this, StyleSheetNames::ScrollBarDark); + + setWindowTitle(tr("Details for %1").arg(idx.data(TokenTransactionTableModel::TxHashRole).toString())); + QString desc = idx.data(TokenTransactionTableModel::LongDescriptionRole).toString(); + ui->detailText->setHtml(desc); +} + +TokenDescDialog::~TokenDescDialog() +{ + delete ui; +} diff --git a/src/qt/tokenfilterproxy.cpp b/src/qt/tokenfilterproxy.cpp index fc36d95d13..b236632aa9 100644 --- a/src/qt/tokenfilterproxy.cpp +++ b/src/qt/tokenfilterproxy.cpp @@ -1,117 +1,117 @@ -#include - -#include - -#include - -// Earliest date that can be represented (far in the past) -const QDateTime TokenFilterProxy::MIN_DATE = QDateTime::fromTime_t(0); -// Last date that can be represented (far in the future) -const QDateTime TokenFilterProxy::MAX_DATE = QDateTime::fromTime_t(0xFFFFFFFF); - -int256_t abs_int256(const int256_t& value) -{ - return value > 0 ? value : -value; -} - -TokenFilterProxy::TokenFilterProxy(QObject *parent) : - QSortFilterProxyModel(parent), - dateFrom(MIN_DATE), - dateTo(MAX_DATE), - addrPrefix(), - name(), - typeFilter(ALL_TYPES), - minAmount(0), - limitRows(-1) -{ - -} - -void TokenFilterProxy::setDateRange(const QDateTime &from, const QDateTime &to) -{ - this->dateFrom = from; - this->dateTo = to; - invalidateFilter(); -} - -void TokenFilterProxy::setAddressPrefix(const QString &_addrPrefix) -{ - this->addrPrefix = _addrPrefix; - invalidateFilter(); -} - -void TokenFilterProxy::setTypeFilter(quint32 modes) -{ - this->typeFilter = modes; - invalidateFilter(); -} - -void TokenFilterProxy::setMinAmount(const int256_t &minimum) -{ - this->minAmount = minimum; - invalidateFilter(); -} - -void TokenFilterProxy::setName(const QString _name) -{ - this->name = _name; - invalidateFilter(); -} - -void TokenFilterProxy::setLimit(int limit) -{ - this->limitRows = limit; -} - -int TokenFilterProxy::rowCount(const QModelIndex &parent) const -{ - if(limitRows != -1) - { - return std::min(QSortFilterProxyModel::rowCount(parent), limitRows); - } - else - { - return QSortFilterProxyModel::rowCount(parent); - } -} - -bool TokenFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); - - int type = index.data(TokenTransactionTableModel::TypeRole).toInt(); - QDateTime datetime = index.data(TokenTransactionTableModel::DateRole).toDateTime(); - QString address = index.data(TokenTransactionTableModel::AddressRole).toString(); - int256_t amount(index.data(TokenTransactionTableModel::AmountRole).toString().toStdString()); - amount = abs_int256(amount); - QString tokenName = index.data(TokenTransactionTableModel::NameRole).toString(); - - if(!(TYPE(type) & typeFilter)) - return false; - if(datetime < dateFrom || datetime > dateTo) - return false; - if (!address.contains(addrPrefix, Qt::CaseInsensitive)) - return false; - if(amount < minAmount) - return false; - if(!name.isEmpty() && name != tokenName) - return false; - - return true; -} - -bool TokenFilterProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - if(left.column() == TokenTransactionTableModel::Amount && - right.column() == TokenTransactionTableModel::Amount) - { - int256_t amountLeft(left.data(TokenTransactionTableModel::AmountRole).toString().toStdString()); - amountLeft = abs_int256(amountLeft); - - int256_t amountRight(right.data(TokenTransactionTableModel::AmountRole).toString().toStdString()); - amountRight = abs_int256(amountRight); - - return amountLeft < amountRight; - } - return QSortFilterProxyModel::lessThan(left, right); -} +#include + +#include + +#include + +// Earliest date that can be represented (far in the past) +const QDateTime TokenFilterProxy::MIN_DATE = QDateTime::fromTime_t(0); +// Last date that can be represented (far in the future) +const QDateTime TokenFilterProxy::MAX_DATE = QDateTime::fromTime_t(0xFFFFFFFF); + +int256_t abs_int256(const int256_t& value) +{ + return value > 0 ? value : -value; +} + +TokenFilterProxy::TokenFilterProxy(QObject *parent) : + QSortFilterProxyModel(parent), + dateFrom(MIN_DATE), + dateTo(MAX_DATE), + addrPrefix(), + name(), + typeFilter(ALL_TYPES), + minAmount(0), + limitRows(-1) +{ + +} + +void TokenFilterProxy::setDateRange(const QDateTime &from, const QDateTime &to) +{ + this->dateFrom = from; + this->dateTo = to; + invalidateFilter(); +} + +void TokenFilterProxy::setAddressPrefix(const QString &_addrPrefix) +{ + this->addrPrefix = _addrPrefix; + invalidateFilter(); +} + +void TokenFilterProxy::setTypeFilter(quint32 modes) +{ + this->typeFilter = modes; + invalidateFilter(); +} + +void TokenFilterProxy::setMinAmount(const int256_t &minimum) +{ + this->minAmount = minimum; + invalidateFilter(); +} + +void TokenFilterProxy::setName(const QString _name) +{ + this->name = _name; + invalidateFilter(); +} + +void TokenFilterProxy::setLimit(int limit) +{ + this->limitRows = limit; +} + +int TokenFilterProxy::rowCount(const QModelIndex &parent) const +{ + if(limitRows != -1) + { + return std::min(QSortFilterProxyModel::rowCount(parent), limitRows); + } + else + { + return QSortFilterProxyModel::rowCount(parent); + } +} + +bool TokenFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + + int type = index.data(TokenTransactionTableModel::TypeRole).toInt(); + QDateTime datetime = index.data(TokenTransactionTableModel::DateRole).toDateTime(); + QString address = index.data(TokenTransactionTableModel::AddressRole).toString(); + int256_t amount(index.data(TokenTransactionTableModel::AmountRole).toString().toStdString()); + amount = abs_int256(amount); + QString tokenName = index.data(TokenTransactionTableModel::NameRole).toString(); + + if(!(TYPE(type) & typeFilter)) + return false; + if(datetime < dateFrom || datetime > dateTo) + return false; + if (!address.contains(addrPrefix, Qt::CaseInsensitive)) + return false; + if(amount < minAmount) + return false; + if(!name.isEmpty() && name != tokenName) + return false; + + return true; +} + +bool TokenFilterProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + if(left.column() == TokenTransactionTableModel::Amount && + right.column() == TokenTransactionTableModel::Amount) + { + int256_t amountLeft(left.data(TokenTransactionTableModel::AmountRole).toString().toStdString()); + amountLeft = abs_int256(amountLeft); + + int256_t amountRight(right.data(TokenTransactionTableModel::AmountRole).toString().toStdString()); + amountRight = abs_int256(amountRight); + + return amountLeft < amountRight; + } + return QSortFilterProxyModel::lessThan(left, right); +} diff --git a/src/qt/tokenitemmodel.cpp b/src/qt/tokenitemmodel.cpp index efed093744..36b2e13921 100755 --- a/src/qt/tokenitemmodel.cpp +++ b/src/qt/tokenitemmodel.cpp @@ -1,528 +1,528 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -class TokenItemEntry -{ -public: - TokenItemEntry() - {} - - TokenItemEntry(const interfaces::TokenInfo &tokenInfo) - { - hash = tokenInfo.hash; - createTime.setTime_t(tokenInfo.time); - contractAddress = QString::fromStdString(tokenInfo.contract_address); - tokenName = QString::fromStdString(tokenInfo.token_name); - tokenSymbol = QString::fromStdString(tokenInfo.token_symbol); - decimals = tokenInfo.decimals; - senderAddress = QString::fromStdString(tokenInfo.sender_address); - } - - TokenItemEntry( const TokenItemEntry &obj) - { - hash = obj.hash; - createTime = obj.createTime; - contractAddress = obj.contractAddress; - tokenName = obj.tokenName; - tokenSymbol = obj.tokenSymbol; - decimals = obj.decimals; - senderAddress = obj.senderAddress; - balance = obj.balance; - } - - ~TokenItemEntry() - {} - - uint256 hash; - QDateTime createTime; - QString contractAddress; - QString tokenName; - QString tokenSymbol; - quint8 decimals; - QString senderAddress; - int256_t balance; -}; - -class TokenTxWorker : public QObject -{ - Q_OBJECT -public: - WalletModel *walletModel; - bool first; - Token tokenAbi; - TokenTxWorker(WalletModel *_walletModel): - walletModel(_walletModel), first(true) {} - -private Q_SLOTS: - void updateTokenTx(const QString &hash) - { - // Initialize variables - uint256 tokenHash = uint256S(hash.toStdString()); - int64_t fromBlock = 0; - int64_t toBlock = -1; - interfaces::TokenInfo tokenInfo; - uint256 blockHash; - bool found = false; - - int64_t backInPast = first ? COINBASE_MATURITY : 10; - first = false; - - // Get current height and block hash - toBlock = walletModel->node().getNumBlocks(); - blockHash = walletModel->node().getBlockHash(toBlock); - - if(toBlock > -1) - { - // Find the token tx in the wallet - tokenInfo = walletModel->wallet().getToken(tokenHash); - found = tokenInfo.hash == tokenHash; - if(found) - { - // Get the start location for search the event log - if(tokenInfo.block_number < toBlock) - { - if(walletModel->node().getBlockHash(tokenInfo.block_number) == tokenInfo.block_hash) - { - fromBlock = tokenInfo.block_number; - } - else - { - fromBlock = tokenInfo.block_number - backInPast; - } - } - else - { - fromBlock = toBlock - backInPast; - } - if(fromBlock < 0) - fromBlock = 0; - - tokenInfo.block_hash = blockHash; - tokenInfo.block_number = toBlock; - } - } - - if(found) - { - // List the events and update the token tx - std::vector tokenEvents; - tokenAbi.setAddress(tokenInfo.contract_address); - tokenAbi.setSender(tokenInfo.sender_address); - tokenAbi.transferEvents(tokenEvents, fromBlock, toBlock); - for(size_t i = 0; i < tokenEvents.size(); i++) - { - TokenEvent event = tokenEvents[i]; - interfaces::TokenTx tokenTx; - tokenTx.contract_address = event.address; - tokenTx.sender_address = event.sender; - tokenTx.receiver_address = event.receiver; - tokenTx.value = event.value; - tokenTx.tx_hash = event.transactionHash; - tokenTx.block_hash = event.blockHash; - tokenTx.block_number = event.blockNumber; - walletModel->wallet().addTokenTxEntry(tokenTx, false); - } - - walletModel->wallet().addTokenEntry(tokenInfo); - } - } - - void cleanTokenTxEntries() - { - if(walletModel) walletModel->wallet().cleanTokenTxEntries(); - } - - void updateBalance(QString hash, QString contractAddress, QString senderAddress) - { - tokenAbi.setAddress(contractAddress.toStdString()); - tokenAbi.setSender(senderAddress.toStdString()); - std::string strBalance; - if(tokenAbi.balanceOf(strBalance)) - { - QString balance = QString::fromStdString(strBalance); - Q_EMIT balanceChanged(hash, balance); - } - } - -Q_SIGNALS: - // Signal that balance in token changed - void balanceChanged(QString hash, QString balance); -}; - -#include "tokenitemmodel.moc" - -struct TokenItemEntryLessThan -{ - bool operator()(const TokenItemEntry &a, const TokenItemEntry &b) const - { - return a.hash < b.hash; - } - bool operator()(const TokenItemEntry &a, const uint256 &b) const - { - return a.hash < b; - } - bool operator()(const uint256 &a, const TokenItemEntry &b) const - { - return a < b.hash; - } -}; - -class TokenItemPriv -{ -public: - QList cachedTokenItem; - TokenItemModel *parent; - - TokenItemPriv(TokenItemModel *_parent): - parent(_parent) {} - - void refreshTokenItem(interfaces::Wallet& wallet) - { - cachedTokenItem.clear(); - { - for(interfaces::TokenInfo token : wallet.getTokens()) - { - TokenItemEntry tokenItem(token); - if(parent) - { - parent->updateBalance(tokenItem); - } - cachedTokenItem.append(tokenItem); - } - } - std::sort(cachedTokenItem.begin(), cachedTokenItem.end(), TokenItemEntryLessThan()); - } - - void updateEntry(const TokenItemEntry &_item, int status) - { - // Find address / label in model - TokenItemEntry item; - QList::iterator lower = qLowerBound( - cachedTokenItem.begin(), cachedTokenItem.end(), _item, TokenItemEntryLessThan()); - QList::iterator upper = qUpperBound( - cachedTokenItem.begin(), cachedTokenItem.end(), _item, TokenItemEntryLessThan()); - int lowerIndex = (lower - cachedTokenItem.begin()); - int upperIndex = (upper - cachedTokenItem.begin()); - bool inModel = (lower != upper); - item = _item; - if(inModel) - { - item.balance = cachedTokenItem[lowerIndex].balance; - } - - switch(status) - { - case CT_NEW: - if(inModel) - { - qWarning() << "TokenItemPriv::updateEntry: Warning: Got CT_NEW, but entry is already in model"; - break; - } - parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex); - cachedTokenItem.insert(lowerIndex, item); - parent->endInsertRows(); - break; - case CT_UPDATED: - if(!inModel) - { - qWarning() << "TokenItemPriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model"; - break; - } - cachedTokenItem[lowerIndex] = item; - parent->emitDataChanged(lowerIndex); - break; - case CT_DELETED: - if(!inModel) - { - qWarning() << "TokenItemPriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model"; - break; - } - parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); - cachedTokenItem.erase(lower, upper); - parent->endRemoveRows(); - break; - } - } - - int updateBalance(QString hash, QString balance) - { - uint256 updated; - updated.SetHex(hash.toStdString()); - int256_t val(balance.toStdString()); - - for(int i = 0; i < cachedTokenItem.size(); i++) - { - TokenItemEntry item = cachedTokenItem[i]; - if(item.hash == updated && item.balance != val) - { - item.balance = val; - cachedTokenItem[i] = item; - return i; - } - } - - return -1; - } - - int size() - { - return cachedTokenItem.size(); - } - - TokenItemEntry *index(int idx) - { - if(idx >= 0 && idx < cachedTokenItem.size()) - { - return &cachedTokenItem[idx]; - } - else - { - return 0; - } - } -}; - -TokenItemModel::TokenItemModel(WalletModel *parent): - QAbstractItemModel(parent), - walletModel(parent), - priv(0), - worker(0), - tokenTxCleaned(false) -{ - columns << tr("Token Name") << tr("Token Symbol") << tr("Balance"); - - priv = new TokenItemPriv(this); - priv->refreshTokenItem(walletModel->wallet()); - - worker = new TokenTxWorker(walletModel); - worker->tokenAbi.setModel(walletModel); - worker->moveToThread(&(t)); - connect(worker, &TokenTxWorker::balanceChanged, this, &TokenItemModel::balanceChanged); - - t.start(); - - subscribeToCoreSignals(); -} - -TokenItemModel::~TokenItemModel() -{ - unsubscribeFromCoreSignals(); - - t.quit(); - t.wait(); - - if(priv) - { - delete priv; - priv = 0; - } -} - -QModelIndex TokenItemModel::index(int row, int column, const QModelIndex &parent) const -{ - Q_UNUSED(parent); - TokenItemEntry *data = priv->index(row); - if(data) - { - return createIndex(row, column, priv->index(row)); - } - return QModelIndex(); -} - -QModelIndex TokenItemModel::parent(const QModelIndex &child) const -{ - Q_UNUSED(child); - return QModelIndex(); -} - -int TokenItemModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return priv->size(); -} - -int TokenItemModel::columnCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return columns.length(); -} - -QVariant TokenItemModel::data(const QModelIndex &index, int role) const -{ - if(!index.isValid()) - return QVariant(); - - TokenItemEntry *rec = static_cast(index.internalPointer()); - - switch (role) { - case Qt::DisplayRole: - switch(index.column()) - { - case Name: - return rec->tokenName; - case Symbol: - return rec->tokenSymbol; - case Balance: - return BitcoinUnits::formatToken(rec->decimals, rec->balance, false, BitcoinUnits::separatorAlways); - default: - break; - } - break; - case TokenItemModel::HashRole: - return QString::fromStdString(rec->hash.ToString()); - break; - case TokenItemModel::AddressRole: - return rec->contractAddress; - break; - case TokenItemModel::NameRole: - return rec->tokenName; - break; - case TokenItemModel::SymbolRole: - return rec->tokenSymbol; - break; - case TokenItemModel::DecimalsRole: - return rec->decimals; - break; - case TokenItemModel::SenderRole: - return rec->senderAddress; - break; - case TokenItemModel::BalanceRole: - return BitcoinUnits::formatToken(rec->decimals, rec->balance, false, BitcoinUnits::separatorAlways); - break; - case TokenItemModel::RawBalanceRole: - return QString::fromStdString(rec->balance.str()); - break; - default: - break; - } - - return QVariant(); -} - -void TokenItemModel::updateToken(const QString &hash, int status, bool showToken) -{ - // Find token in wallet - uint256 updated; - updated.SetHex(hash.toStdString()); - interfaces::TokenInfo token =walletModel->wallet().getToken(updated); - showToken &= token.hash == updated; - - TokenItemEntry tokenEntry; - if(showToken) - { - tokenEntry = TokenItemEntry(token); - updateBalance(tokenEntry); - } - else - { - tokenEntry.hash = updated; - } - priv->updateEntry(tokenEntry, status); -} - -void TokenItemModel::checkTokenBalanceChanged() -{ - if(!priv) - return; - - // Update token balance - for(int i = 0; i < priv->cachedTokenItem.size(); i++) - { - TokenItemEntry tokenEntry = priv->cachedTokenItem[i]; - updateBalance(tokenEntry); - } - - // Update token transactions - if(fLogEvents) - { - // Search for token transactions - for(int i = 0; i < priv->cachedTokenItem.size(); i++) - { - TokenItemEntry tokenEntry = priv->cachedTokenItem[i]; - QString hash = QString::fromStdString(tokenEntry.hash.ToString()); - QMetaObject::invokeMethod(worker, "updateTokenTx", Qt::QueuedConnection, - Q_ARG(QString, hash)); - } - - // Clean token transactions - if(!tokenTxCleaned) - { - tokenTxCleaned = true; - QMetaObject::invokeMethod(worker, "cleanTokenTxEntries", Qt::QueuedConnection); - } - } -} - -void TokenItemModel::emitDataChanged(int idx) -{ - Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex())); -} - -struct TokenNotification -{ -public: - TokenNotification() {} - TokenNotification(uint256 _hash, ChangeType _status, bool _showToken): - hash(_hash), status(_status), showToken(_showToken) {} - - void invoke(QObject *tim) - { - QString strHash = QString::fromStdString(hash.GetHex()); - qDebug() << "NotifyTokenChanged: " + strHash + " status= " + QString::number(status); - - QMetaObject::invokeMethod(tim, "updateToken", Qt::QueuedConnection, - Q_ARG(QString, strHash), - Q_ARG(int, status), - Q_ARG(bool, showToken)); - } -private: - uint256 hash; - ChangeType status; - bool showToken; -}; - -static void NotifyTokenChanged(TokenItemModel *tim, const uint256 &hash, ChangeType status) -{ - TokenNotification notification(hash, status, true); - notification.invoke(tim); -} - -void TokenItemModel::subscribeToCoreSignals() -{ - // Connect signals to wallet - m_handler_token_changed = walletModel->wallet().handleTokenChanged(boost::bind(NotifyTokenChanged, this, _1, _2)); -} - -void TokenItemModel::unsubscribeFromCoreSignals() -{ - // Disconnect signals from wallet - m_handler_token_changed->disconnect(); -} - -void TokenItemModel::balanceChanged(QString hash, QString balance) -{ - int index = priv->updateBalance(hash, balance); - if(index > -1) - { - emitDataChanged(index); - } -} - -void TokenItemModel::updateBalance(const TokenItemEntry &entry) -{ - QString hash = QString::fromStdString(entry.hash.ToString()); - QMetaObject::invokeMethod(worker, "updateBalance", Qt::QueuedConnection, - Q_ARG(QString, hash), Q_ARG(QString, entry.contractAddress), Q_ARG(QString, entry.senderAddress)); -} +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +class TokenItemEntry +{ +public: + TokenItemEntry() + {} + + TokenItemEntry(const interfaces::TokenInfo &tokenInfo) + { + hash = tokenInfo.hash; + createTime.setTime_t(tokenInfo.time); + contractAddress = QString::fromStdString(tokenInfo.contract_address); + tokenName = QString::fromStdString(tokenInfo.token_name); + tokenSymbol = QString::fromStdString(tokenInfo.token_symbol); + decimals = tokenInfo.decimals; + senderAddress = QString::fromStdString(tokenInfo.sender_address); + } + + TokenItemEntry( const TokenItemEntry &obj) + { + hash = obj.hash; + createTime = obj.createTime; + contractAddress = obj.contractAddress; + tokenName = obj.tokenName; + tokenSymbol = obj.tokenSymbol; + decimals = obj.decimals; + senderAddress = obj.senderAddress; + balance = obj.balance; + } + + ~TokenItemEntry() + {} + + uint256 hash; + QDateTime createTime; + QString contractAddress; + QString tokenName; + QString tokenSymbol; + quint8 decimals; + QString senderAddress; + int256_t balance; +}; + +class TokenTxWorker : public QObject +{ + Q_OBJECT +public: + WalletModel *walletModel; + bool first; + Token tokenAbi; + TokenTxWorker(WalletModel *_walletModel): + walletModel(_walletModel), first(true) {} + +private Q_SLOTS: + void updateTokenTx(const QString &hash) + { + // Initialize variables + uint256 tokenHash = uint256S(hash.toStdString()); + int64_t fromBlock = 0; + int64_t toBlock = -1; + interfaces::TokenInfo tokenInfo; + uint256 blockHash; + bool found = false; + + int64_t backInPast = first ? COINBASE_MATURITY : 10; + first = false; + + // Get current height and block hash + toBlock = walletModel->node().getNumBlocks(); + blockHash = walletModel->node().getBlockHash(toBlock); + + if(toBlock > -1) + { + // Find the token tx in the wallet + tokenInfo = walletModel->wallet().getToken(tokenHash); + found = tokenInfo.hash == tokenHash; + if(found) + { + // Get the start location for search the event log + if(tokenInfo.block_number < toBlock) + { + if(walletModel->node().getBlockHash(tokenInfo.block_number) == tokenInfo.block_hash) + { + fromBlock = tokenInfo.block_number; + } + else + { + fromBlock = tokenInfo.block_number - backInPast; + } + } + else + { + fromBlock = toBlock - backInPast; + } + if(fromBlock < 0) + fromBlock = 0; + + tokenInfo.block_hash = blockHash; + tokenInfo.block_number = toBlock; + } + } + + if(found) + { + // List the events and update the token tx + std::vector tokenEvents; + tokenAbi.setAddress(tokenInfo.contract_address); + tokenAbi.setSender(tokenInfo.sender_address); + tokenAbi.transferEvents(tokenEvents, fromBlock, toBlock); + for(size_t i = 0; i < tokenEvents.size(); i++) + { + TokenEvent event = tokenEvents[i]; + interfaces::TokenTx tokenTx; + tokenTx.contract_address = event.address; + tokenTx.sender_address = event.sender; + tokenTx.receiver_address = event.receiver; + tokenTx.value = event.value; + tokenTx.tx_hash = event.transactionHash; + tokenTx.block_hash = event.blockHash; + tokenTx.block_number = event.blockNumber; + walletModel->wallet().addTokenTxEntry(tokenTx, false); + } + + walletModel->wallet().addTokenEntry(tokenInfo); + } + } + + void cleanTokenTxEntries() + { + if(walletModel) walletModel->wallet().cleanTokenTxEntries(); + } + + void updateBalance(QString hash, QString contractAddress, QString senderAddress) + { + tokenAbi.setAddress(contractAddress.toStdString()); + tokenAbi.setSender(senderAddress.toStdString()); + std::string strBalance; + if(tokenAbi.balanceOf(strBalance)) + { + QString balance = QString::fromStdString(strBalance); + Q_EMIT balanceChanged(hash, balance); + } + } + +Q_SIGNALS: + // Signal that balance in token changed + void balanceChanged(QString hash, QString balance); +}; + +#include "tokenitemmodel.moc" + +struct TokenItemEntryLessThan +{ + bool operator()(const TokenItemEntry &a, const TokenItemEntry &b) const + { + return a.hash < b.hash; + } + bool operator()(const TokenItemEntry &a, const uint256 &b) const + { + return a.hash < b; + } + bool operator()(const uint256 &a, const TokenItemEntry &b) const + { + return a < b.hash; + } +}; + +class TokenItemPriv +{ +public: + QList cachedTokenItem; + TokenItemModel *parent; + + TokenItemPriv(TokenItemModel *_parent): + parent(_parent) {} + + void refreshTokenItem(interfaces::Wallet& wallet) + { + cachedTokenItem.clear(); + { + for(interfaces::TokenInfo token : wallet.getTokens()) + { + TokenItemEntry tokenItem(token); + if(parent) + { + parent->updateBalance(tokenItem); + } + cachedTokenItem.append(tokenItem); + } + } + std::sort(cachedTokenItem.begin(), cachedTokenItem.end(), TokenItemEntryLessThan()); + } + + void updateEntry(const TokenItemEntry &_item, int status) + { + // Find address / label in model + TokenItemEntry item; + QList::iterator lower = qLowerBound( + cachedTokenItem.begin(), cachedTokenItem.end(), _item, TokenItemEntryLessThan()); + QList::iterator upper = qUpperBound( + cachedTokenItem.begin(), cachedTokenItem.end(), _item, TokenItemEntryLessThan()); + int lowerIndex = (lower - cachedTokenItem.begin()); + int upperIndex = (upper - cachedTokenItem.begin()); + bool inModel = (lower != upper); + item = _item; + if(inModel) + { + item.balance = cachedTokenItem[lowerIndex].balance; + } + + switch(status) + { + case CT_NEW: + if(inModel) + { + qWarning() << "TokenItemPriv::updateEntry: Warning: Got CT_NEW, but entry is already in model"; + break; + } + parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex); + cachedTokenItem.insert(lowerIndex, item); + parent->endInsertRows(); + break; + case CT_UPDATED: + if(!inModel) + { + qWarning() << "TokenItemPriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model"; + break; + } + cachedTokenItem[lowerIndex] = item; + parent->emitDataChanged(lowerIndex); + break; + case CT_DELETED: + if(!inModel) + { + qWarning() << "TokenItemPriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model"; + break; + } + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedTokenItem.erase(lower, upper); + parent->endRemoveRows(); + break; + } + } + + int updateBalance(QString hash, QString balance) + { + uint256 updated; + updated.SetHex(hash.toStdString()); + int256_t val(balance.toStdString()); + + for(int i = 0; i < cachedTokenItem.size(); i++) + { + TokenItemEntry item = cachedTokenItem[i]; + if(item.hash == updated && item.balance != val) + { + item.balance = val; + cachedTokenItem[i] = item; + return i; + } + } + + return -1; + } + + int size() + { + return cachedTokenItem.size(); + } + + TokenItemEntry *index(int idx) + { + if(idx >= 0 && idx < cachedTokenItem.size()) + { + return &cachedTokenItem[idx]; + } + else + { + return 0; + } + } +}; + +TokenItemModel::TokenItemModel(WalletModel *parent): + QAbstractItemModel(parent), + walletModel(parent), + priv(0), + worker(0), + tokenTxCleaned(false) +{ + columns << tr("Token Name") << tr("Token Symbol") << tr("Balance"); + + priv = new TokenItemPriv(this); + priv->refreshTokenItem(walletModel->wallet()); + + worker = new TokenTxWorker(walletModel); + worker->tokenAbi.setModel(walletModel); + worker->moveToThread(&(t)); + connect(worker, &TokenTxWorker::balanceChanged, this, &TokenItemModel::balanceChanged); + + t.start(); + + subscribeToCoreSignals(); +} + +TokenItemModel::~TokenItemModel() +{ + unsubscribeFromCoreSignals(); + + t.quit(); + t.wait(); + + if(priv) + { + delete priv; + priv = 0; + } +} + +QModelIndex TokenItemModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + TokenItemEntry *data = priv->index(row); + if(data) + { + return createIndex(row, column, priv->index(row)); + } + return QModelIndex(); +} + +QModelIndex TokenItemModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +int TokenItemModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return priv->size(); +} + +int TokenItemModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QVariant TokenItemModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + TokenItemEntry *rec = static_cast(index.internalPointer()); + + switch (role) { + case Qt::DisplayRole: + switch(index.column()) + { + case Name: + return rec->tokenName; + case Symbol: + return rec->tokenSymbol; + case Balance: + return BitcoinUnits::formatToken(rec->decimals, rec->balance, false, BitcoinUnits::separatorAlways); + default: + break; + } + break; + case TokenItemModel::HashRole: + return QString::fromStdString(rec->hash.ToString()); + break; + case TokenItemModel::AddressRole: + return rec->contractAddress; + break; + case TokenItemModel::NameRole: + return rec->tokenName; + break; + case TokenItemModel::SymbolRole: + return rec->tokenSymbol; + break; + case TokenItemModel::DecimalsRole: + return rec->decimals; + break; + case TokenItemModel::SenderRole: + return rec->senderAddress; + break; + case TokenItemModel::BalanceRole: + return BitcoinUnits::formatToken(rec->decimals, rec->balance, false, BitcoinUnits::separatorAlways); + break; + case TokenItemModel::RawBalanceRole: + return QString::fromStdString(rec->balance.str()); + break; + default: + break; + } + + return QVariant(); +} + +void TokenItemModel::updateToken(const QString &hash, int status, bool showToken) +{ + // Find token in wallet + uint256 updated; + updated.SetHex(hash.toStdString()); + interfaces::TokenInfo token =walletModel->wallet().getToken(updated); + showToken &= token.hash == updated; + + TokenItemEntry tokenEntry; + if(showToken) + { + tokenEntry = TokenItemEntry(token); + updateBalance(tokenEntry); + } + else + { + tokenEntry.hash = updated; + } + priv->updateEntry(tokenEntry, status); +} + +void TokenItemModel::checkTokenBalanceChanged() +{ + if(!priv) + return; + + // Update token balance + for(int i = 0; i < priv->cachedTokenItem.size(); i++) + { + TokenItemEntry tokenEntry = priv->cachedTokenItem[i]; + updateBalance(tokenEntry); + } + + // Update token transactions + if(fLogEvents) + { + // Search for token transactions + for(int i = 0; i < priv->cachedTokenItem.size(); i++) + { + TokenItemEntry tokenEntry = priv->cachedTokenItem[i]; + QString hash = QString::fromStdString(tokenEntry.hash.ToString()); + QMetaObject::invokeMethod(worker, "updateTokenTx", Qt::QueuedConnection, + Q_ARG(QString, hash)); + } + + // Clean token transactions + if(!tokenTxCleaned) + { + tokenTxCleaned = true; + QMetaObject::invokeMethod(worker, "cleanTokenTxEntries", Qt::QueuedConnection); + } + } +} + +void TokenItemModel::emitDataChanged(int idx) +{ + Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex())); +} + +struct TokenNotification +{ +public: + TokenNotification() {} + TokenNotification(uint256 _hash, ChangeType _status, bool _showToken): + hash(_hash), status(_status), showToken(_showToken) {} + + void invoke(QObject *tim) + { + QString strHash = QString::fromStdString(hash.GetHex()); + qDebug() << "NotifyTokenChanged: " + strHash + " status= " + QString::number(status); + + QMetaObject::invokeMethod(tim, "updateToken", Qt::QueuedConnection, + Q_ARG(QString, strHash), + Q_ARG(int, status), + Q_ARG(bool, showToken)); + } +private: + uint256 hash; + ChangeType status; + bool showToken; +}; + +static void NotifyTokenChanged(TokenItemModel *tim, const uint256 &hash, ChangeType status) +{ + TokenNotification notification(hash, status, true); + notification.invoke(tim); +} + +void TokenItemModel::subscribeToCoreSignals() +{ + // Connect signals to wallet + m_handler_token_changed = walletModel->wallet().handleTokenChanged(boost::bind(NotifyTokenChanged, this, _1, _2)); +} + +void TokenItemModel::unsubscribeFromCoreSignals() +{ + // Disconnect signals from wallet + m_handler_token_changed->disconnect(); +} + +void TokenItemModel::balanceChanged(QString hash, QString balance) +{ + int index = priv->updateBalance(hash, balance); + if(index > -1) + { + emitDataChanged(index); + } +} + +void TokenItemModel::updateBalance(const TokenItemEntry &entry) +{ + QString hash = QString::fromStdString(entry.hash.ToString()); + QMetaObject::invokeMethod(worker, "updateBalance", Qt::QueuedConnection, + Q_ARG(QString, hash), Q_ARG(QString, entry.contractAddress), Q_ARG(QString, entry.senderAddress)); +} diff --git a/src/qt/tokentransactionview.cpp b/src/qt/tokentransactionview.cpp index b3c0cc2c26..680706b569 100644 --- a/src/qt/tokentransactionview.cpp +++ b/src/qt/tokentransactionview.cpp @@ -1,424 +1,424 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define paternTokenAmount "^[0-9]{1,59}\\.{1,1}[0-9]{0,18}" - -TokenTransactionView::TokenTransactionView(const PlatformStyle *platformStyle, QWidget *parent) : - QWidget(parent), - model(0), - tokenProxyModel(0), - tokenView(0), - columnResizingFixer(0) -{ - // Build filter row - setContentsMargins(0,0,0,0); - - QHBoxLayout *hlayout = new QHBoxLayout(); - hlayout->setContentsMargins(6,6,6,6); - hlayout->setSpacing(10); - hlayout->addSpacing(STATUS_COLUMN_WIDTH); - - dateWidget = new QComboBox(this); - dateWidget->setFixedWidth(DATE_COLUMN_WIDTH -10); - - 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); - typeWidget->setFixedWidth(TYPE_COLUMN_WIDTH -10); - - typeWidget->addItem(tr("All"), TokenFilterProxy::ALL_TYPES); - typeWidget->addItem(tr("Received with"), TokenFilterProxy::TYPE(TokenTransactionRecord::RecvWithAddress)); - typeWidget->addItem(tr("Sent to"), TokenFilterProxy::TYPE(TokenTransactionRecord::SendToAddress)); - typeWidget->addItem(tr("To yourself"), TokenFilterProxy::TYPE(TokenTransactionRecord::SendToSelf)); - hlayout->addWidget(typeWidget); - - addressWidget = new QLineEdit(this); -#if QT_VERSION >= 0x040700 - addressWidget->setPlaceholderText(tr("Enter address to search")); -#endif - hlayout->addWidget(addressWidget); - - nameWidget = new QComboBox(this); - nameWidget->setFixedWidth(NAME_COLUMN_WIDTH -10); - nameWidget->addItem(tr("All"), ""); - - hlayout->addWidget(nameWidget); - - amountWidget = new QLineEdit(this); -#if QT_VERSION >= 0x040700 - amountWidget->setPlaceholderText(tr("Min amount")); -#endif - amountWidget->setFixedWidth(AMOUNT_COLUMN_WIDTH - 10); - - QRegularExpression regEx; - regEx.setPattern(paternTokenAmount); - QRegularExpressionValidator *validator = new QRegularExpressionValidator(amountWidget); - validator->setRegularExpression(regEx); - amountWidget->setValidator(validator); - hlayout->addWidget(amountWidget); - - 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); - tokenView = view; - - // Actions - QAction *copyAddressAction = new QAction(tr("Copy address"), this); - QAction *copyAmountAction = new QAction(tr("Copy amount"), this); - QAction *copyTxIDAction = new QAction(tr("Copy transaction ID"), this); - QAction *copyTxPlainText = new QAction(tr("Copy full transaction details"), this); - QAction *showDetailsAction = new QAction(tr("Show transaction details"), this); - - contextMenu = new QMenu(tokenView); - contextMenu->addAction(copyAddressAction); - contextMenu->addAction(copyAmountAction); - contextMenu->addAction(copyTxIDAction); - contextMenu->addAction(copyTxPlainText); - contextMenu->addAction(showDetailsAction); - - connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); - connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); - connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); - connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); - connect(nameWidget, SIGNAL(activated(int)), this, SLOT(chooseName(int))); - - connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); - - connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); - connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount())); - connect(copyTxIDAction, SIGNAL(triggered()), this, SLOT(copyTxID())); - connect(copyTxPlainText, SIGNAL(triggered()), this, SLOT(copyTxPlainText())); - connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails())); - connect(tokenView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(showDetails())); -} - -void TokenTransactionView::setModel(WalletModel *_model) -{ - this->model = _model; - if(_model) - { - refreshNameWidget(); - connect(model->getTokenItemModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),this, SLOT(addToNameWidget(QModelIndex,int,int))); - connect(model->getTokenItemModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),this, SLOT(removeFromNameWidget(QModelIndex,int,int))); - - tokenProxyModel = new TokenFilterProxy(this); - tokenProxyModel->setSourceModel(_model->getTokenTransactionTableModel()); - tokenProxyModel->setDynamicSortFilter(true); - tokenProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); - tokenProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - - tokenProxyModel->setSortRole(Qt::EditRole); - - tokenView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - tokenView->setModel(tokenProxyModel); - tokenView->setAlternatingRowColors(true); - tokenView->setSelectionBehavior(QAbstractItemView::SelectRows); - tokenView->setSelectionMode(QAbstractItemView::ExtendedSelection); - tokenView->setSortingEnabled(true); - tokenView->sortByColumn(TokenTransactionTableModel::Date, Qt::DescendingOrder); - tokenView->verticalHeader()->hide(); - - tokenView->setColumnWidth(TokenTransactionTableModel::Status, STATUS_COLUMN_WIDTH); - tokenView->setColumnWidth(TokenTransactionTableModel::Date, DATE_COLUMN_WIDTH); - tokenView->setColumnWidth(TokenTransactionTableModel::Type, TYPE_COLUMN_WIDTH); - tokenView->setColumnWidth(TokenTransactionTableModel::Name, NAME_COLUMN_WIDTH); - tokenView->setColumnWidth(TokenTransactionTableModel::Amount, AMOUNT_COLUMN_WIDTH); - - columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tokenView, AMOUNT_MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH, this, 3); - } -} - -QWidget *TokenTransactionView::createDateRangeWidget() -{ - dateRangeWidget = new QFrame(); - dateRangeWidget->setFrameStyle(QFrame::Panel | QFrame::Raised); - dateRangeWidget->setContentsMargins(1,1,1,8); - 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 TokenTransactionView::refreshNameWidget() -{ - if(model) - { - TokenItemModel *tim = model->getTokenItemModel(); - for(int i = 0; i < tim->rowCount(); i++) - { - QString name = tim->data(tim->index(i, 0), TokenItemModel::SymbolRole).toString(); - if(nameWidget->findText(name) == -1) - nameWidget->addItem(name, name); - } - } -} - -void TokenTransactionView::addToNameWidget(const QModelIndex &parent, int start, int /*end*/) -{ - if(model) - { - TokenItemModel *tim = model->getTokenItemModel(); - QString name = tim->index(start, TokenItemModel::Symbol, parent).data().toString(); - if(nameWidget->findText(name) == -1) - nameWidget->addItem(name, name); - } -} - -void TokenTransactionView::removeFromNameWidget(const QModelIndex &parent, int start, int /*end*/) -{ - if(model) - { - TokenItemModel *tim = model->getTokenItemModel(); - QString name = tim->index(start, TokenItemModel::Symbol, parent).data().toString(); - int nameCount = 0; - - for(int i = 0; i < tim->rowCount(); i++) - { - QString checkName = tim->index(i, TokenItemModel::Symbol, parent).data().toString(); - if(name == checkName) - { - nameCount++; - } - } - - int nameIndex = nameWidget->findText(name); - if(nameCount == 1 && nameIndex != -1) - nameWidget->removeItem(nameIndex); - } -} - -void TokenTransactionView::resizeEvent(QResizeEvent *event) -{ - QWidget::resizeEvent(event); - columnResizingFixer->stretchColumnWidth(TokenTransactionTableModel::ToAddress); -} - -bool TokenTransactionView::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(tokenView, 0, TokenTransactionTableModel::TxPlainTextRole); - return true; - } - } - return QWidget::eventFilter(obj, event); -} - -void TokenTransactionView::contextualMenu(const QPoint &point) -{ - QModelIndex index = tokenView->indexAt(point); - QModelIndexList selection = tokenView->selectionModel()->selectedRows(0); - if (selection.empty()) - return; - - if(index.isValid()) - { - contextMenu->exec(QCursor::pos()); - } -} - -void TokenTransactionView::dateRangeChanged() -{ - if(!tokenProxyModel) - return; - tokenProxyModel->setDateRange( - QDateTime(dateFrom->date()), - QDateTime(dateTo->date()).addDays(1)); -} - -void TokenTransactionView::showDetails() -{ - if(!tokenView->selectionModel()) - return; - QModelIndexList selection = tokenView->selectionModel()->selectedRows(); - if(!selection.isEmpty()) - { - TokenDescDialog *dlg = new TokenDescDialog(selection.at(0)); - dlg->setAttribute(Qt::WA_DeleteOnClose); - dlg->show(); - } -} - -void TokenTransactionView::copyAddress() -{ - GUIUtil::copyEntryData(tokenView, 0, TokenTransactionTableModel::AddressRole); -} - -void TokenTransactionView::copyAmount() -{ - GUIUtil::copyEntryData(tokenView, 0, TokenTransactionTableModel::AmountRole); -} - -void TokenTransactionView::copyTxID() -{ - GUIUtil::copyEntryData(tokenView, 0, TokenTransactionTableModel::TxHashRole); -} - -void TokenTransactionView::copyTxPlainText() -{ - GUIUtil::copyEntryData(tokenView, 0, TokenTransactionTableModel::TxPlainTextRole); -} - -void TokenTransactionView::chooseDate(int idx) -{ - if(!tokenProxyModel) - return; - QDate current = QDate::currentDate(); - dateRangeWidget->setVisible(false); - switch(dateWidget->itemData(idx).toInt()) - { - case All: - tokenProxyModel->setDateRange( - TokenFilterProxy::MIN_DATE, - TokenFilterProxy::MAX_DATE); - break; - case Today: - tokenProxyModel->setDateRange( - QDateTime(current), - TokenFilterProxy::MAX_DATE); - break; - case ThisWeek: { - // Find last Monday - QDate startOfWeek = current.addDays(-(current.dayOfWeek()-1)); - tokenProxyModel->setDateRange( - QDateTime(startOfWeek), - TokenFilterProxy::MAX_DATE); - - } break; - case ThisMonth: - tokenProxyModel->setDateRange( - QDateTime(QDate(current.year(), current.month(), 1)), - TokenFilterProxy::MAX_DATE); - break; - case LastMonth: - tokenProxyModel->setDateRange( - QDateTime(QDate(current.year(), current.month(), 1).addMonths(-1)), - QDateTime(QDate(current.year(), current.month(), 1))); - break; - case ThisYear: - tokenProxyModel->setDateRange( - QDateTime(QDate(current.year(), 1, 1)), - TokenFilterProxy::MAX_DATE); - break; - case Range: - dateRangeWidget->setVisible(true); - dateRangeChanged(); - break; - } -} - -void TokenTransactionView::chooseType(int idx) -{ - if(!tokenProxyModel) - return; - tokenProxyModel->setTypeFilter( - typeWidget->itemData(idx).toInt()); -} - -void TokenTransactionView::chooseName(int idx) -{ - if(!tokenProxyModel) - return; - tokenProxyModel->setName( - nameWidget->itemData(idx).toString()); -} - -void TokenTransactionView::changedPrefix(const QString &prefix) -{ - if(!tokenProxyModel) - return; - tokenProxyModel->setAddressPrefix(prefix); -} - -void TokenTransactionView::changedAmount(const QString &amount) -{ - if(!tokenProxyModel) - return; - int256_t amount_parsed = 0; - if(BitcoinUnits::parseToken(18, amount, &amount_parsed)) - { - tokenProxyModel->setMinAmount(amount_parsed); - } - else - { - tokenProxyModel->setMinAmount(0); - } -} +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define paternTokenAmount "^[0-9]{1,59}\\.{1,1}[0-9]{0,18}" + +TokenTransactionView::TokenTransactionView(const PlatformStyle *platformStyle, QWidget *parent) : + QWidget(parent), + model(0), + tokenProxyModel(0), + tokenView(0), + columnResizingFixer(0) +{ + // Build filter row + setContentsMargins(0,0,0,0); + + QHBoxLayout *hlayout = new QHBoxLayout(); + hlayout->setContentsMargins(6,6,6,6); + hlayout->setSpacing(10); + hlayout->addSpacing(STATUS_COLUMN_WIDTH); + + dateWidget = new QComboBox(this); + dateWidget->setFixedWidth(DATE_COLUMN_WIDTH -10); + + 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); + typeWidget->setFixedWidth(TYPE_COLUMN_WIDTH -10); + + typeWidget->addItem(tr("All"), TokenFilterProxy::ALL_TYPES); + typeWidget->addItem(tr("Received with"), TokenFilterProxy::TYPE(TokenTransactionRecord::RecvWithAddress)); + typeWidget->addItem(tr("Sent to"), TokenFilterProxy::TYPE(TokenTransactionRecord::SendToAddress)); + typeWidget->addItem(tr("To yourself"), TokenFilterProxy::TYPE(TokenTransactionRecord::SendToSelf)); + hlayout->addWidget(typeWidget); + + addressWidget = new QLineEdit(this); +#if QT_VERSION >= 0x040700 + addressWidget->setPlaceholderText(tr("Enter address to search")); +#endif + hlayout->addWidget(addressWidget); + + nameWidget = new QComboBox(this); + nameWidget->setFixedWidth(NAME_COLUMN_WIDTH -10); + nameWidget->addItem(tr("All"), ""); + + hlayout->addWidget(nameWidget); + + amountWidget = new QLineEdit(this); +#if QT_VERSION >= 0x040700 + amountWidget->setPlaceholderText(tr("Min amount")); +#endif + amountWidget->setFixedWidth(AMOUNT_COLUMN_WIDTH - 10); + + QRegularExpression regEx; + regEx.setPattern(paternTokenAmount); + QRegularExpressionValidator *validator = new QRegularExpressionValidator(amountWidget); + validator->setRegularExpression(regEx); + amountWidget->setValidator(validator); + hlayout->addWidget(amountWidget); + + 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); + tokenView = view; + + // Actions + QAction *copyAddressAction = new QAction(tr("Copy address"), this); + QAction *copyAmountAction = new QAction(tr("Copy amount"), this); + QAction *copyTxIDAction = new QAction(tr("Copy transaction ID"), this); + QAction *copyTxPlainText = new QAction(tr("Copy full transaction details"), this); + QAction *showDetailsAction = new QAction(tr("Show transaction details"), this); + + contextMenu = new QMenu(tokenView); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyAmountAction); + contextMenu->addAction(copyTxIDAction); + contextMenu->addAction(copyTxPlainText); + contextMenu->addAction(showDetailsAction); + + connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); + connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); + connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); + connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); + connect(nameWidget, SIGNAL(activated(int)), this, SLOT(chooseName(int))); + + connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); + + connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); + connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount())); + connect(copyTxIDAction, SIGNAL(triggered()), this, SLOT(copyTxID())); + connect(copyTxPlainText, SIGNAL(triggered()), this, SLOT(copyTxPlainText())); + connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails())); + connect(tokenView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(showDetails())); +} + +void TokenTransactionView::setModel(WalletModel *_model) +{ + this->model = _model; + if(_model) + { + refreshNameWidget(); + connect(model->getTokenItemModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),this, SLOT(addToNameWidget(QModelIndex,int,int))); + connect(model->getTokenItemModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),this, SLOT(removeFromNameWidget(QModelIndex,int,int))); + + tokenProxyModel = new TokenFilterProxy(this); + tokenProxyModel->setSourceModel(_model->getTokenTransactionTableModel()); + tokenProxyModel->setDynamicSortFilter(true); + tokenProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + tokenProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + + tokenProxyModel->setSortRole(Qt::EditRole); + + tokenView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + tokenView->setModel(tokenProxyModel); + tokenView->setAlternatingRowColors(true); + tokenView->setSelectionBehavior(QAbstractItemView::SelectRows); + tokenView->setSelectionMode(QAbstractItemView::ExtendedSelection); + tokenView->setSortingEnabled(true); + tokenView->sortByColumn(TokenTransactionTableModel::Date, Qt::DescendingOrder); + tokenView->verticalHeader()->hide(); + + tokenView->setColumnWidth(TokenTransactionTableModel::Status, STATUS_COLUMN_WIDTH); + tokenView->setColumnWidth(TokenTransactionTableModel::Date, DATE_COLUMN_WIDTH); + tokenView->setColumnWidth(TokenTransactionTableModel::Type, TYPE_COLUMN_WIDTH); + tokenView->setColumnWidth(TokenTransactionTableModel::Name, NAME_COLUMN_WIDTH); + tokenView->setColumnWidth(TokenTransactionTableModel::Amount, AMOUNT_COLUMN_WIDTH); + + columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tokenView, AMOUNT_MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH, this, 3); + } +} + +QWidget *TokenTransactionView::createDateRangeWidget() +{ + dateRangeWidget = new QFrame(); + dateRangeWidget->setFrameStyle(QFrame::Panel | QFrame::Raised); + dateRangeWidget->setContentsMargins(1,1,1,8); + 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 TokenTransactionView::refreshNameWidget() +{ + if(model) + { + TokenItemModel *tim = model->getTokenItemModel(); + for(int i = 0; i < tim->rowCount(); i++) + { + QString name = tim->data(tim->index(i, 0), TokenItemModel::SymbolRole).toString(); + if(nameWidget->findText(name) == -1) + nameWidget->addItem(name, name); + } + } +} + +void TokenTransactionView::addToNameWidget(const QModelIndex &parent, int start, int /*end*/) +{ + if(model) + { + TokenItemModel *tim = model->getTokenItemModel(); + QString name = tim->index(start, TokenItemModel::Symbol, parent).data().toString(); + if(nameWidget->findText(name) == -1) + nameWidget->addItem(name, name); + } +} + +void TokenTransactionView::removeFromNameWidget(const QModelIndex &parent, int start, int /*end*/) +{ + if(model) + { + TokenItemModel *tim = model->getTokenItemModel(); + QString name = tim->index(start, TokenItemModel::Symbol, parent).data().toString(); + int nameCount = 0; + + for(int i = 0; i < tim->rowCount(); i++) + { + QString checkName = tim->index(i, TokenItemModel::Symbol, parent).data().toString(); + if(name == checkName) + { + nameCount++; + } + } + + int nameIndex = nameWidget->findText(name); + if(nameCount == 1 && nameIndex != -1) + nameWidget->removeItem(nameIndex); + } +} + +void TokenTransactionView::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + columnResizingFixer->stretchColumnWidth(TokenTransactionTableModel::ToAddress); +} + +bool TokenTransactionView::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(tokenView, 0, TokenTransactionTableModel::TxPlainTextRole); + return true; + } + } + return QWidget::eventFilter(obj, event); +} + +void TokenTransactionView::contextualMenu(const QPoint &point) +{ + QModelIndex index = tokenView->indexAt(point); + QModelIndexList selection = tokenView->selectionModel()->selectedRows(0); + if (selection.empty()) + return; + + if(index.isValid()) + { + contextMenu->exec(QCursor::pos()); + } +} + +void TokenTransactionView::dateRangeChanged() +{ + if(!tokenProxyModel) + return; + tokenProxyModel->setDateRange( + QDateTime(dateFrom->date()), + QDateTime(dateTo->date()).addDays(1)); +} + +void TokenTransactionView::showDetails() +{ + if(!tokenView->selectionModel()) + return; + QModelIndexList selection = tokenView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + TokenDescDialog *dlg = new TokenDescDialog(selection.at(0)); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->show(); + } +} + +void TokenTransactionView::copyAddress() +{ + GUIUtil::copyEntryData(tokenView, 0, TokenTransactionTableModel::AddressRole); +} + +void TokenTransactionView::copyAmount() +{ + GUIUtil::copyEntryData(tokenView, 0, TokenTransactionTableModel::AmountRole); +} + +void TokenTransactionView::copyTxID() +{ + GUIUtil::copyEntryData(tokenView, 0, TokenTransactionTableModel::TxHashRole); +} + +void TokenTransactionView::copyTxPlainText() +{ + GUIUtil::copyEntryData(tokenView, 0, TokenTransactionTableModel::TxPlainTextRole); +} + +void TokenTransactionView::chooseDate(int idx) +{ + if(!tokenProxyModel) + return; + QDate current = QDate::currentDate(); + dateRangeWidget->setVisible(false); + switch(dateWidget->itemData(idx).toInt()) + { + case All: + tokenProxyModel->setDateRange( + TokenFilterProxy::MIN_DATE, + TokenFilterProxy::MAX_DATE); + break; + case Today: + tokenProxyModel->setDateRange( + QDateTime(current), + TokenFilterProxy::MAX_DATE); + break; + case ThisWeek: { + // Find last Monday + QDate startOfWeek = current.addDays(-(current.dayOfWeek()-1)); + tokenProxyModel->setDateRange( + QDateTime(startOfWeek), + TokenFilterProxy::MAX_DATE); + + } break; + case ThisMonth: + tokenProxyModel->setDateRange( + QDateTime(QDate(current.year(), current.month(), 1)), + TokenFilterProxy::MAX_DATE); + break; + case LastMonth: + tokenProxyModel->setDateRange( + QDateTime(QDate(current.year(), current.month(), 1).addMonths(-1)), + QDateTime(QDate(current.year(), current.month(), 1))); + break; + case ThisYear: + tokenProxyModel->setDateRange( + QDateTime(QDate(current.year(), 1, 1)), + TokenFilterProxy::MAX_DATE); + break; + case Range: + dateRangeWidget->setVisible(true); + dateRangeChanged(); + break; + } +} + +void TokenTransactionView::chooseType(int idx) +{ + if(!tokenProxyModel) + return; + tokenProxyModel->setTypeFilter( + typeWidget->itemData(idx).toInt()); +} + +void TokenTransactionView::chooseName(int idx) +{ + if(!tokenProxyModel) + return; + tokenProxyModel->setName( + nameWidget->itemData(idx).toString()); +} + +void TokenTransactionView::changedPrefix(const QString &prefix) +{ + if(!tokenProxyModel) + return; + tokenProxyModel->setAddressPrefix(prefix); +} + +void TokenTransactionView::changedAmount(const QString &amount) +{ + if(!tokenProxyModel) + return; + int256_t amount_parsed = 0; + if(BitcoinUnits::parseToken(18, amount, &amount_parsed)) + { + tokenProxyModel->setMinAmount(amount_parsed); + } + else + { + tokenProxyModel->setMinAmount(0); + } +} From a2eec6de8c3ec7e939fbdcce4535fc2887904817 Mon Sep 17 00:00:00 2001 From: Yunqi Ouyang Date: Wed, 30 Oct 2019 14:48:53 +0800 Subject: [PATCH 3/3] Update include statements in QT to support out-of-source build --- src/qt/tokenamountfield.cpp | 2 +- src/qt/tokenitemmodel.cpp | 4 ++-- src/qt/walletmodel.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qt/tokenamountfield.cpp b/src/qt/tokenamountfield.cpp index 5febbf938f..eb434d50c4 100644 --- a/src/qt/tokenamountfield.cpp +++ b/src/qt/tokenamountfield.cpp @@ -178,7 +178,7 @@ class TokenAmountSpinBox: public QAbstractSpinBox void valueChanged(); }; -#include "tokenamountfield.moc" +#include TokenAmountField::TokenAmountField(QWidget *parent) : QWidget(parent), diff --git a/src/qt/tokenitemmodel.cpp b/src/qt/tokenitemmodel.cpp index 36b2e13921..9254b9577e 100755 --- a/src/qt/tokenitemmodel.cpp +++ b/src/qt/tokenitemmodel.cpp @@ -64,7 +64,7 @@ class TokenTxWorker : public QObject Token tokenAbi; TokenTxWorker(WalletModel *_walletModel): walletModel(_walletModel), first(true) {} - + private Q_SLOTS: void updateTokenTx(const QString &hash) { @@ -161,7 +161,7 @@ private Q_SLOTS: void balanceChanged(QString hash, QString balance); }; -#include "tokenitemmodel.moc" +#include struct TokenItemEntryLessThan { diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 352367ecf3..34caeae2cc 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -52,7 +52,7 @@ private Q_SLOTS: } }; -#include "walletmodel.moc" +#include WalletModel::WalletModel(std::unique_ptr wallet, interfaces::Node& node, const PlatformStyle *platformStyle, OptionsModel *_optionsModel, QObject *parent) : QObject(parent), m_wallet(std::move(wallet)), m_node(node), optionsModel(_optionsModel), addressTableModel(nullptr),