diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 02960506a949..1f47a08eaf61 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -36,6 +36,8 @@ QT_FORMS_UI = \ qt/forms/askpassphrasedialog.ui \ qt/forms/coincontroldialog.ui \ qt/forms/editaddressdialog.ui \ + qt/forms/governancelist.ui \ + qt/forms/governancedialog.ui \ qt/forms/helpmessagedialog.ui \ qt/forms/intro.ui \ qt/forms/modaloverlay.ui \ @@ -66,6 +68,8 @@ QT_MOC_CPP = \ qt/moc_coincontroltreewidget.cpp \ qt/moc_csvmodelwriter.cpp \ qt/moc_editaddressdialog.cpp \ + qt/moc_governancelist.cpp \ + qt/moc_governancedialog.cpp \ qt/moc_guiutil.cpp \ qt/moc_intro.cpp \ qt/moc_macdockiconhandler.cpp \ @@ -136,6 +140,8 @@ BITCOIN_QT_H = \ qt/coincontroltreewidget.h \ qt/csvmodelwriter.h \ qt/editaddressdialog.h \ + qt/governancelist.h \ + qt/governancedialog.h \ qt/guiconstants.h \ qt/guiutil.h \ qt/intro.h \ @@ -277,6 +283,8 @@ BITCOIN_QT_WALLET_CPP = \ qt/coincontroldialog.cpp \ qt/coincontroltreewidget.cpp \ qt/editaddressdialog.cpp \ + qt/governancelist.cpp \ + qt/governancedialog.cpp \ qt/masternodelist.cpp \ qt/openuridialog.cpp \ qt/overviewpage.cpp \ diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index aefa22b54886..762696b091f1 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -100,6 +100,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * overviewAction(0), historyAction(0), masternodeAction(0), + governanceAction(0), quitAction(0), sendCoinsAction(0), sendCoinsMenuAction(0), @@ -370,6 +371,19 @@ void BitcoinGUI::createActions() #endif tabGroup->addAction(masternodeAction); + governanceAction = new QAction(QIcon(":/icons/governance"), tr("&Governance"), this); + governanceAction->setStatusTip(tr("Show governance items")); + governanceAction->setToolTip(governanceAction->statusTip()); + governanceAction->setCheckable(true); +#ifdef Q_OS_MAC + governanceAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_5)); +#else + governanceAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); +#endif + tabGroup->addAction(governanceAction); + connect(governanceAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(governanceAction, SIGNAL(triggered()), this, SLOT(gotoGovernancePage())); + // These showNormalIfMinimized are needed because Send Coins and Receive Coins // can be triggered from the tray menu, and need to show the GUI to be useful. connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); @@ -592,6 +606,7 @@ void BitcoinGUI::createToolBars() toolbar->addAction(receiveCoinsAction); toolbar->addAction(historyAction); toolbar->addAction(masternodeAction); + toolbar->addAction(governanceAction); QSettings settings; toolbar->setMovable(false); overviewAction->setChecked(true); @@ -746,6 +761,7 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled) receiveCoinsMenuAction->setEnabled(enabled); historyAction->setEnabled(enabled); masternodeAction->setEnabled(enabled); + governanceAction->setEnabled(enabled); encryptWalletAction->setEnabled(enabled); backupWalletAction->setEnabled(enabled); changePassphraseAction->setEnabled(enabled); @@ -912,6 +928,12 @@ void BitcoinGUI::gotoMasternodePage() if (walletFrame) walletFrame->gotoMasternodePage(); } +void BitcoinGUI::gotoGovernancePage() +{ + governanceAction->setChecked(true); + if (walletFrame) walletFrame->gotoGovernancePage(); +} + void BitcoinGUI::gotoReceiveCoinsPage() { receiveCoinsAction->setChecked(true); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 8a6d230a85d9..fa76cb2bed44 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -102,6 +102,7 @@ class BitcoinGUI : public QMainWindow QAction *overviewAction; QAction *historyAction; QAction *masternodeAction; + QAction *governanceAction; QAction *quitAction; QAction *sendCoinsAction; QAction *sendCoinsMenuAction; @@ -239,6 +240,8 @@ private Q_SLOTS: void gotoHistoryPage(); /** Switch to masternode page */ void gotoMasternodePage(); + /** Switch to governance page */ + void gotoGovernancePage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ diff --git a/src/qt/dash.qrc b/src/qt/dash.qrc index 74a112c35698..6d08897b0d0d 100644 --- a/src/qt/dash.qrc +++ b/src/qt/dash.qrc @@ -30,6 +30,7 @@ res/icons/edit.png res/icons/history.png res/icons/masternodes.png + res/icons/governance.png res/icons/overview.png res/icons/export.png res/icons/synced.png diff --git a/src/qt/forms/governancedialog.ui b/src/qt/forms/governancedialog.ui new file mode 100644 index 000000000000..add988e42ff4 --- /dev/null +++ b/src/qt/forms/governancedialog.ui @@ -0,0 +1,112 @@ + + + GovernanceDialog + + + + 0 + 0 + 670 + 698 + + + + + + + + 75 + true + + + + Governance Object + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 0 + 50 + + + + QFrame::NoFrame + + + QFrame::Plain + + + true + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + + + + QDialogButtonBox::Close + + + + + + + + + + + buttonBox + rejected() + GovernanceDialog + reject() + + + 452 + 573 + + + 243 + 298 + + + + + buttonBox + accepted() + GovernanceDialog + accept() + + + 452 + 573 + + + 243 + 298 + + + + + diff --git a/src/qt/forms/governancelist.ui b/src/qt/forms/governancelist.ui new file mode 100644 index 000000000000..d06643bcecae --- /dev/null +++ b/src/qt/forms/governancelist.ui @@ -0,0 +1,226 @@ + + + GovernanceList + + + + 0 + 0 + 967 + 371 + + + + Form + + + + + + 0 + + + + + <html><head/><body><p>Note: Governance objects in your local wallet can be potentially incorrect. Always wait for wallet sync and additional data before voting on any proposal. </p></body></html> + + + + + + + + + + + Search: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Number of proposals: + + + + + + + 0 + + + + + + + + + + 695 + 0 + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + + ID + + + + + Name + + + + + Url + + + + + Amount + + + + + Yes + + + + + No + + + + + Absolute Yes + + + + + Funding + + + + + + + + + + Vote Yes + + + + + + + Vote No + + + + + + + Vote Abstain + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Show Proposal Information + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Update Governance + + + + + + + + + + diff --git a/src/qt/governancedialog.cpp b/src/qt/governancedialog.cpp new file mode 100644 index 000000000000..83c748257836 --- /dev/null +++ b/src/qt/governancedialog.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2011-2013 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "governancedialog.h" +#include "ui_governancedialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#if QT_VERSION < 0x050000 +#include +#endif + +#if defined(HAVE_CONFIG_H) +#include "config/pacglobal-config.h" /* for USE_QRCODE */ +#endif + +#ifdef USE_QRCODE +#include +#endif + + +GovernanceDialog::GovernanceDialog(QWidget *parent) : + QDialog(parent), + walletModel(0), + ui(new Ui::GovernanceDialog), + model(0) +{ + ui->setupUi(this); +} + +GovernanceDialog::~GovernanceDialog() +{ + delete ui; +} + +void GovernanceDialog::setModel(OptionsModel *model) +{ + this->model = model; + + if (model) + connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(update())); + + // update the display unit if necessary + update(); +} + +void GovernanceDialog::setInfo(QString strWindowtitle, QString strQRCode, QString strTextInfo, QString strQRCodeTitle) +{ + this->strWindowtitle = strWindowtitle; + this->strQRCode = strQRCode; + this->strTextInfo = strTextInfo; + this->strQRCodeTitle = strQRCodeTitle; + update(); +} + + + +void GovernanceDialog::setWalletModel(WalletModel *model) +{ + this->walletModel = model; +} + + + +void GovernanceDialog::update() +{ + if(!model) + return; + + setWindowTitle(strWindowtitle); + ui->outUri->setText(strTextInfo); + +} diff --git a/src/qt/governancedialog.h b/src/qt/governancedialog.h new file mode 100644 index 000000000000..ffe5f6745ec1 --- /dev/null +++ b/src/qt/governancedialog.h @@ -0,0 +1,56 @@ +// Copyright (c) 2011-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef GOVERNANCE_DIALOG_H +#define GOVERNANCE_DIALOG_H + +#include "walletmodel.h" +#include + +#include +#include +#include + +class OptionsModel; +class WalletModel; + +namespace Ui { + class GovernanceDialog; +} + +QT_BEGIN_NAMESPACE +class QMenu; +QT_END_NAMESPACE + + +class GovernanceDialog : public QDialog +{ + Q_OBJECT + +public: + explicit GovernanceDialog(QWidget *parent = 0); + ~GovernanceDialog(); + void setWalletModel(WalletModel *walletModel); + void setModel(OptionsModel *model); + void setInfo(QString strWindowtitle, QString strQRCode, QString strTextInfo, QString strQRCodeTitle); + + +private: + WalletModel *walletModel; + +private Q_SLOTS: + void update(); + + +private: + Ui::GovernanceDialog *ui; + OptionsModel *model; + uint256 gObjHash; + QString strWindowtitle; + QString strQRCode; + QString strTextInfo; + QString strQRCodeTitle; +}; + +#endif // GOVERNANCE_DIALOG_H diff --git a/src/qt/governancelist.cpp b/src/qt/governancelist.cpp new file mode 100644 index 000000000000..211215065f27 --- /dev/null +++ b/src/qt/governancelist.cpp @@ -0,0 +1,421 @@ +#include "governancelist.h" +#include "ui_governancelist.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +GovernanceList::GovernanceList(const PlatformStyle *platformStyle, QWidget *parent) : + QWidget(parent), + ui(new Ui::GovernanceList), + clientModel(0), + walletModel(0) +{ + ui->setupUi(this); + int columnHash = 50; + int columnName = 250; + int columnUrl = 250; + int columnAmount = 120; + int columnvoteYes = 80; + int columnvoteNo = 80; + int columnAbsoluteYes = 150; + int columnFund = 50; + ui->tableWidgetGobjects->setColumnWidth(0, columnHash); + ui->tableWidgetGobjects->setColumnWidth(1, columnName); + ui->tableWidgetGobjects->setColumnWidth(2, columnUrl); + ui->tableWidgetGobjects->setColumnWidth(3, columnAmount); + ui->tableWidgetGobjects->setColumnWidth(4, columnvoteYes); + ui->tableWidgetGobjects->setColumnWidth(5, columnvoteNo); + ui->tableWidgetGobjects->setColumnWidth(6, columnAbsoluteYes); + ui->tableWidgetGobjects->setColumnWidth(7, columnFund); + contextMenu = new QMenu(); + connect(ui->tableWidgetGobjects, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&))); + connect(ui->tableWidgetGobjects, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_GovernanceButton_clicked())); + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(updateGobjects())); + timer->start(1000); + fFilterUpdated = false; + nTimeFilterUpdated = GetTime(); +} + +void GovernanceList::on_voteYesButton_clicked() +{ + std::string gobjectSingle; + { + LOCK(cs_gobjlist); + // Find selected gobject + QItemSelectionModel* selectionModel = ui->tableWidgetGobjects->selectionModel(); + QModelIndexList selected = selectionModel->selectedRows(); + if(selected.count() == 0) return; + QModelIndex index = selected.at(0); + int nSelectedRow = index.row(); + gobjectSingle = ui->tableWidgetGobjects->item(nSelectedRow, 0)->text().toStdString(); + } + uint256 parsedGobjectHash; + parsedGobjectHash.SetHex(gobjectSingle); + // Display message box + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm YES vote"), + tr("Are you sure you want vote YES on this proposal with all your masternodes?"), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); + if(retval != QMessageBox::Yes) return; + WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); + if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForStakingOnly) { + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) return; // Unlock wallet was cancelled + vote_outcome_enum_t VoteValue = vote_outcome_enum_t(1); + Vote(parsedGobjectHash, VoteValue); + return; + } + vote_outcome_enum_t VoteValue = vote_outcome_enum_t(1); + Vote(parsedGobjectHash, VoteValue); +} + +void GovernanceList::on_voteNoButton_clicked() +{ + std::string gobjectSingle; + { + LOCK(cs_gobjlist); + // Find selected gobject + QItemSelectionModel* selectionModel = ui->tableWidgetGobjects->selectionModel(); + QModelIndexList selected = selectionModel->selectedRows(); + if(selected.count() == 0) return; + QModelIndex index = selected.at(0); + int nSelectedRow = index.row(); + gobjectSingle = ui->tableWidgetGobjects->item(nSelectedRow, 0)->text().toStdString(); + } + uint256 parsedGobjectHash; + parsedGobjectHash.SetHex(gobjectSingle); + // Display message box + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm NO vote"), + tr("Are you sure you want vote NO on this proposal with all your masternodes?"), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); + if(retval != QMessageBox::Yes) return; + WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); + if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForStakingOnly) { + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) return; // Unlock wallet was cancelled + vote_outcome_enum_t VoteValue = vote_outcome_enum_t(2); + Vote(parsedGobjectHash, VoteValue); + return; + } + vote_outcome_enum_t VoteValue = vote_outcome_enum_t(2); + Vote(parsedGobjectHash, VoteValue); +} + +void GovernanceList::on_voteAbstainButton_clicked() +{ + std::string gobjectSingle; + { + LOCK(cs_gobjlist); + // Find selected gobject + QItemSelectionModel* selectionModel = ui->tableWidgetGobjects->selectionModel(); + QModelIndexList selected = selectionModel->selectedRows(); + if(selected.count() == 0) return; + QModelIndex index = selected.at(0); + int nSelectedRow = index.row(); + gobjectSingle = ui->tableWidgetGobjects->item(nSelectedRow, 0)->text().toStdString(); + } + uint256 parsedGobjectHash; + parsedGobjectHash.SetHex(gobjectSingle); + + // Display message box + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm ABSTAIN vote"), + tr("Are you sure you want vote ABSTAIN on this proposal with all your masternodes?"), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); + if(retval != QMessageBox::Yes) return; + WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); + if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForStakingOnly) { + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) return; // Unlock wallet was cancelled + vote_outcome_enum_t VoteValue = vote_outcome_enum_t(3); + Vote(parsedGobjectHash, VoteValue); + return; + } + vote_outcome_enum_t VoteValue = vote_outcome_enum_t(3); + Vote(parsedGobjectHash, VoteValue); +} + +void GovernanceList::Vote(uint256 nHash, vote_outcome_enum_t eVoteOutcome) +{ + int nTotalVotes = 0; + int nSuccessful = 0; + int nFailed = 0; + vote_signal_enum_t eVoteSignal = CGovernanceVoting::ConvertVoteSignal("funding"); + if (eVoteSignal == VOTE_SIGNAL_NONE || eVoteOutcome == VOTE_OUTCOME_NONE) { + LogPrint("governance", "We are not going to be voting."); + return; + } + + std::map votingKeys; + auto mnList = deterministicMNManager->GetListAtChainTip(); + mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) { + CKey votingKey; + if (pwalletMain->GetKey(dmn->pdmnState->keyIDVoting, votingKey)) { + nTotalVotes++; + votingKeys.emplace(dmn->proTxHash, votingKey); + } + }); + + LogPrint("governance", "Will be voting using %d deterministic masternodes.", nTotalVotes); + + { + LOCK(governance.cs); + CGovernanceObject* pGovObj = governance.FindGovernanceObject(nHash); + int govObjType = pGovObj->GetObjectType(); + } + + // iterate through the mn keys + for (const auto& p : votingKeys) { + const auto& proTxHash = p.first; + const auto& key = p.second; + auto dmn = mnList.GetValidMN(proTxHash); + if (!dmn) { + nFailed++; + continue; + } + + CGovernanceVote vote(dmn->collateralOutpoint, nHash, eVoteSignal, eVoteOutcome); + if (!vote.Sign(key, key.GetPubKey().GetID())) { + nFailed++; + continue; + } + + CGovernanceException exception; + if (governance.ProcessVoteAndRelay(vote, exception, *g_connman)) { + nSuccessful++; + } else { + nFailed++; + } + } + LogPrint("governance", "Voted successfully %d time(s) and failed %d time(s).", nSuccessful, nFailed); +} + +GovernanceList::~GovernanceList() +{ + delete ui; +} + +void GovernanceList::setWalletModel(WalletModel *model) +{ + this->walletModel = model; +} + +void GovernanceList::showContextMenu(const QPoint &point) +{ + QTableWidgetItem *item = ui->tableWidgetGobjects->itemAt(point); + if(item) contextMenu->exec(QCursor::pos()); +} + +void GovernanceList::updateGobjects() +{ + TRY_LOCK(cs_gobjlist, fLockAcquired); + if(!fLockAcquired) { + return; + } + static int64_t nTimeListUpdated = GetTime(); + // to prevent high cpu usage update only once in GOBJECT_UPDATE_SECONDS seconds + // or GOBJECT_COOLDOWN_SECONDS seconds after filter was last changed + int64_t nSecondsToWait = fFilterUpdated + ? nTimeFilterUpdated - GetTime() + GOBJECT_COOLDOWN_SECONDS + : nTimeListUpdated - GetTime() + GOBJECT_UPDATE_SECONDS; + if(fFilterUpdated) ui->countGobjectLabel->setText(QString::fromStdString(strprintf("Please wait... %d", nSecondsToWait))); + if(!fFilterUpdated) ui->countGobjectLabel->setText(QString::fromStdString(strprintf("Please wait... %d", nSecondsToWait))); + if(nSecondsToWait > 0) return; + nTimeListUpdated = GetTime(); + fFilterUpdated = false; + QString strToFilter; + ui->countGobjectLabel->setText("Updating..."); + ui->tableWidgetGobjects->setSortingEnabled(false); + ui->tableWidgetGobjects->clearContents(); + ui->tableWidgetGobjects->setRowCount(0); + int nStartTime = 0; +for (const auto& pGovObj : governance.GetAllNewerThan(nStartTime)) { + int gobject = pGovObj->GetObjectType(); + if (gobject == 1) { + // populate list + // Address, Protocol, Status, Active Seconds, Last Seen, Pub Key + // Get Object as Hex and convert to std::string + std::string HexStr = pGovObj->GetDataAsHexString(); + std::vector v = ParseHex(HexStr); + std::string s(v.begin(), v.end()); + std::string nameStr = getValue(s, "name", true); + std::string urlStr = getValue(s, "url", true); + std::string amountStr = getNumericValue(s, "payment_amount"); + // Define "Funding" for Vote count + vote_signal_enum_t VoteCountType = vote_signal_enum_t(1); + std::string vFunding; + if (pGovObj->IsSetCachedFunding()) { + vFunding = "Yes"; + } else {vFunding = "No";} + QString name = QString::fromStdString(nameStr); + QString url = QString::fromStdString(urlStr); + QString amount = QString::fromStdString(amountStr); + std::string hash = pGovObj->GetHash().GetHex(); + QTableWidgetItem *Hash = new QTableWidgetItem(QString::fromStdString(hash)); + QTableWidgetItem *nameItem = new QTableWidgetItem(name); + QTableWidgetItem *urlItem = new QTableWidgetItem(url); + QTableWidgetItem *amounItem = new QTableWidgetItem(amount); + QTableWidgetItem *voteYes = new QTableWidgetItem(QString::number(pGovObj->GetYesCount(VoteCountType))); + QTableWidgetItem *voteNo = new QTableWidgetItem(QString::number(pGovObj->GetNoCount(VoteCountType))); + QTableWidgetItem *AbsoluteYes = new QTableWidgetItem(QString::number(pGovObj->GetAbsoluteYesCount(VoteCountType))); + QTableWidgetItem *fundingStatus = new QTableWidgetItem(QString::fromStdString(vFunding)); + if (strCurrentFilter != "") { + strToFilter = Hash->text() + " " + + nameItem->text() + " " + + amounItem->text() + " " + + voteYes->text() + " " + + voteNo->text() + " " + + AbsoluteYes->text() + " " + + fundingStatus->text(); + if (!strToFilter.contains(strCurrentFilter)) continue; + } + ui->tableWidgetGobjects->insertRow(0); + ui->tableWidgetGobjects->setItem(0, 0, Hash); + ui->tableWidgetGobjects->setItem(0, 1, nameItem); + ui->tableWidgetGobjects->setItem(0, 2, urlItem); + ui->tableWidgetGobjects->setItem(0, 3, amounItem); + ui->tableWidgetGobjects->setItem(0, 4, voteYes); + ui->tableWidgetGobjects->setItem(0, 5, voteNo); + ui->tableWidgetGobjects->setItem(0, 6, AbsoluteYes); + ui->tableWidgetGobjects->setItem(0, 7, fundingStatus); + } + } + ui->countGobjectLabel->setText(QString::number(ui->tableWidgetGobjects->rowCount())); + ui->tableWidgetGobjects->setSortingEnabled(true); +} + +void GovernanceList::setClientModel(ClientModel *model) +{ + this->clientModel = model; +} + +void GovernanceList::on_UpdateButton_clicked() +{ + updateGobjects(); +} +void GovernanceList::on_filterLineEdit_textChanged(const QString &strFilterIn) +{ + strCurrentFilter = strFilterIn; + nTimeFilterUpdated = GetTime(); + fFilterUpdated = true; + ui->countGobjectLabel->setText(QString::fromStdString(strprintf("Please wait... %d", GOBJECT_UPDATE_SECONDS))); +} + +void GovernanceList::on_GovernanceButton_clicked() +{ + std::string gobjectSingle; + { + LOCK(cs_gobjlist); + // Find selected gobject + QItemSelectionModel* selectionModel = ui->tableWidgetGobjects->selectionModel(); + QModelIndexList selected = selectionModel->selectedRows(); + if(selected.count() == 0) return; + QModelIndex index = selected.at(0); + int nSelectedRow = index.row(); + gobjectSingle = ui->tableWidgetGobjects->item(nSelectedRow, 0)->text().toStdString(); + } + uint256 parsedGobjectHash; + parsedGobjectHash.SetHex(gobjectSingle); + ShowGovernanceObject(parsedGobjectHash); +} + +void GovernanceList::ShowGovernanceObject(uint256 gobjectSingle) +{ + if(!walletModel || !walletModel->getOptionsModel()) + return; + CGovernanceObject* pGovObj = governance.FindGovernanceObject(gobjectSingle); + // Title of popup window + QString strWindowtitle = tr("Additional information for Governance Object"); + // Title above QR-Code + QString strQRCodeTitle = "Governance Object"; + vote_signal_enum_t VotesFunding = vote_signal_enum_t(1); + vote_signal_enum_t VotesValid = vote_signal_enum_t(2); + vote_signal_enum_t VotesDelete = vote_signal_enum_t(3); + vote_signal_enum_t VotesEndorsed = vote_signal_enum_t(4); + std::string s = pGovObj->GetDataAsHexString(); + std::string dataString = pGovObj->GetDataAsHexString(); + std::string dataHex = pGovObj->GetDataAsHexString(); + std::string name = getValue(s,"name", true); + std::string url = getValue(s,"url", true); + std::string amount = getNumericValue(s,"payment_amount"); + std::string hash = gobjectSingle.ToString(); + // Funding Variables + std::string FundingYes = std::to_string(pGovObj->GetYesCount(VotesFunding)); + std::string FundingNo = std::to_string(pGovObj->GetNoCount(VotesFunding)); + std::string FundingAbstain = std::to_string(pGovObj->GetAbstainCount(VotesFunding)); + std::string FundingAbYes = std::to_string(pGovObj->GetAbsoluteYesCount(VotesFunding)); + // Valid Variables + std::string ValidYes = std::to_string(pGovObj->GetYesCount(VotesValid)); + std::string ValidNo = std::to_string(pGovObj->GetNoCount(VotesValid)); + std::string ValidAbstain = std::to_string(pGovObj->GetAbstainCount(VotesValid)); + std::string ValidAbYes = std::to_string(pGovObj->GetAbsoluteYesCount(VotesValid)); + // Delete Variables + std::string DeleteYes = std::to_string(pGovObj->GetYesCount(VotesDelete)); + std::string DeleteNo = std::to_string(pGovObj->GetNoCount(VotesDelete)); + std::string DeleteAbstain = std::to_string(pGovObj->GetAbstainCount(VotesDelete)); + std::string DeleteAbYes = std::to_string(pGovObj->GetAbsoluteYesCount(VotesDelete)); + // Endorse Variables + std::string EndorseYes = std::to_string(pGovObj->GetYesCount(VotesEndorsed)); + std::string EndorseNo = std::to_string(pGovObj->GetNoCount(VotesEndorsed)); + std::string EndorseAbstain = std::to_string(pGovObj->GetAbstainCount(VotesEndorsed)); + std::string EndorseAbYes = std::to_string(pGovObj->GetAbsoluteYesCount(VotesEndorsed)); + // Create dialog text as HTML + QString strHTML = ""; + strHTML += "" + tr("Hash") + ": " + GUIUtil::HtmlEscape(hash) + "
"; + strHTML += "" + tr("Proposal Name") + ": " + GUIUtil::HtmlEscape(name) + "
"; + strHTML += "" + tr("Proposal Url") + ": " + GUIUtil::HtmlEscape(url) + "
"; + strHTML += "" + tr("Payment Amount") + ": " + GUIUtil::HtmlEscape(amount) + "
"; + strHTML += "

" + tr("Funding Votes") +":" + "
Yes: " + GUIUtil::HtmlEscape(FundingYes) + "" + "
No: " + GUIUtil::HtmlEscape(FundingNo) + "" + "
Abstain: " + GUIUtil::HtmlEscape(FundingAbstain) + "" + "
Absolute Yes: " + GUIUtil::HtmlEscape(FundingAbYes) + "

"; + strHTML += "

" + tr("Valid Votes") +":" + "
Yes: " + GUIUtil::HtmlEscape(ValidYes) + "" + "
No: " + GUIUtil::HtmlEscape(ValidNo) + "" + "
Abstain: " + GUIUtil::HtmlEscape(ValidAbstain) + "" + "
Absolute Yes: " + GUIUtil::HtmlEscape(ValidAbYes) + "

"; + strHTML += "

" + tr("Delete Votes") +":" + "
Yes: " + GUIUtil::HtmlEscape(DeleteYes) + "" + "
No: " + GUIUtil::HtmlEscape(DeleteNo) + "" + "
Abstain: " + GUIUtil::HtmlEscape(DeleteAbstain) + "" + "
Absolute Yes: " + GUIUtil::HtmlEscape(DeleteAbYes) + "

"; + strHTML += "

" + tr("Endorse Votes") +":" + "
Yes: " + GUIUtil::HtmlEscape(EndorseYes) + "" + "
No: " + GUIUtil::HtmlEscape(EndorseNo) + "" + "
Abstain: " + GUIUtil::HtmlEscape(EndorseAbstain) + "" + "
Absolute Yes: " + GUIUtil::HtmlEscape(EndorseAbYes) + "

"; + strHTML += "" + tr("Raw Information (Hex)") + ": " + GUIUtil::HtmlEscape(dataHex) + "
"; + strHTML += "" + tr("Raw Information (String)") + ": " + GUIUtil::HtmlEscape(dataString) + "
"; + //strHTML += "" + tr("Sentinel") + ": " + (mn.lastPing.nSentinelVersion > DEFAULT_SENTINEL_VERSION ? GUIUtil::HtmlEscape(SafeIntVersionToString(mn.lastPing.nSentinelVersion)) : tr("Unknown")) + "
"; + //strHTML += "" + tr("Status") + ": " + GUIUtil::HtmlEscape(CMasternode::StateToString(mn.nActiveState)) + "
"; + //strHTML += "" + tr("Payee") + ": " + GUIUtil::HtmlEscape(CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString()) + "
"; + //strHTML += "" + tr("Active") + ": " + GUIUtil::HtmlEscape(DurationToDHMS(mn.lastPing.sigTime - mn.sigTime)) + "
"; + //strHTML += "" + tr("Last Seen") + ": " + GUIUtil::HtmlEscape(DateTimeStrFormat("%Y-%m-%d %H:%M", mn.lastPing.sigTime + GetOffsetFromUtc())) + "
"; + // Open Governance dialog + GovernanceDialog *dialog = new GovernanceDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->setModel(walletModel->getOptionsModel()); + dialog->setInfo(strWindowtitle, strWindowtitle,strHTML, ""); + dialog->show(); +} + +std::string getValue(std::string str,std::string key, bool format) +{ + std::string s_pattern = "\"" + key + "\""; + int beg = str.find(s_pattern); + int f_comma = str.find("\"", beg+s_pattern.size()); + int s_comma = str.find("\"", f_comma+1); + std::string s2 = str.substr(f_comma+1, s_comma-f_comma-1); + return s2; +} + + + +std::string getNumericValue(std::string str, std::string key) +{ + std::string s_pattern = "\"" + key + "\""; + int beg = str.find(s_pattern); + int f_comma = str.find(":", beg+s_pattern.size()); + int s_comma = str.find(",", f_comma+1); + std::string s2 = str.substr(f_comma+1, s_comma-f_comma-1); + return s2; +} diff --git a/src/qt/governancelist.h b/src/qt/governancelist.h new file mode 100644 index 000000000000..e640eedeaa23 --- /dev/null +++ b/src/qt/governancelist.h @@ -0,0 +1,77 @@ +#ifndef GOVERNANCELIST_H +#define GOVERNANCELIST_H + +#include "primitives/transaction.h" +#include "platformstyle.h" +#include "sync.h" +#include "util.h" +#include + +#include +#include +#include + +#define GOBJECT_UPDATE_SECONDS 15 +#define GOBJECT_COOLDOWN_SECONDS 3 + +namespace Ui { + class GovernanceList; +} +class ClientModel; +class WalletModel; + +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + +std::string getValue(std::string, std::string, bool); +std::string getNumericValue(std::string str, std::string key); + +/** Governance Manager page widget */ +class GovernanceList : public QWidget +{ + Q_OBJECT + +public: + explicit GovernanceList(const PlatformStyle *platformStyle, QWidget *parent = 0); + ~GovernanceList(); + void setClientModel(ClientModel *clientModel); + void setWalletModel(WalletModel *walletModel); + void ShowGovernanceObject(uint256 gobjectSingle); + void Vote(uint256 nHash, vote_outcome_enum_t eVoteOutcome); + + +private: + QMenu *contextMenu; + int64_t nTimeFilterUpdated; + bool fFilterUpdated; + +public Q_SLOTS: + void updateGobjects(); + + +Q_SIGNALS: + void doubleClicked(const QModelIndex&); + +private: + QTimer *timer; + Ui::GovernanceList *ui; + ClientModel *clientModel; + WalletModel *walletModel; + + // Protects tableWidgetMasternodes + CCriticalSection cs_gobjlist; + + QString strCurrentFilter; + +private Q_SLOTS: + void showContextMenu(const QPoint &); + void on_filterLineEdit_textChanged(const QString &strFilterIn); + void on_GovernanceButton_clicked(); + void on_UpdateButton_clicked(); + void on_voteYesButton_clicked(); + void on_voteNoButton_clicked(); + void on_voteAbstainButton_clicked(); + +}; +#endif // GOVERNANCELIST_H diff --git a/src/qt/res/icons/governance.png b/src/qt/res/icons/governance.png new file mode 100755 index 000000000000..d057a9f21933 Binary files /dev/null and b/src/qt/res/icons/governance.png differ diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 0eca28ddebf7..1664d928c341 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -129,6 +129,13 @@ void WalletFrame::gotoMasternodePage() i.value()->gotoMasternodePage(); } +void WalletFrame::gotoGovernancePage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoGovernancePage(); +} + void WalletFrame::gotoReceiveCoinsPage() { QMap::const_iterator i; diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 432c19d94d9c..84eb2b9da9ff 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -68,6 +68,8 @@ public Q_SLOTS: void gotoHistoryPage(); /** Switch to masternode page */ void gotoMasternodePage(); + /** Switch to governance page */ + void gotoGovernancePage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 39178cbc028b..bcff6763d565 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -81,6 +81,8 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent): addWidget(receiveCoinsPage); addWidget(sendCoinsPage); addWidget(masternodeListPage); + governanceListPage = new GovernanceList(platformStyle); + addWidget(governanceListPage); // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); @@ -134,6 +136,7 @@ void WalletView::setClientModel(ClientModel *_clientModel) overviewPage->setClientModel(_clientModel); sendCoinsPage->setClientModel(_clientModel); masternodeListPage->setClientModel(_clientModel); + governanceListPage->setClientModel(_clientModel); } void WalletView::setWalletModel(WalletModel *_walletModel) @@ -144,6 +147,7 @@ void WalletView::setWalletModel(WalletModel *_walletModel) transactionView->setModel(_walletModel); overviewPage->setWalletModel(_walletModel); masternodeListPage->setWalletModel(_walletModel); + governanceListPage->setWalletModel(_walletModel); receiveCoinsPage->setModel(_walletModel); sendCoinsPage->setModel(_walletModel); usedReceivingAddressesPage->setModel(_walletModel->getAddressTableModel()); @@ -222,6 +226,11 @@ void WalletView::gotoReceiveCoinsPage() setCurrentWidget(receiveCoinsPage); } +void WalletView::gotoGovernancePage() +{ + setCurrentWidget(governanceListPage); +} + void WalletView::gotoSendCoinsPage(QString addr) { setCurrentWidget(sendCoinsPage); diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 2fa9435052fa..fe5733f3d4cb 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -7,11 +7,13 @@ #include "amount.h" #include "masternodelist.h" +#include "governancelist.h" #include class BitcoinGUI; class ClientModel; +class GovernancePage; class OverviewPage; class PlatformStyle; class ReceiveCoinsDialog; @@ -67,6 +69,7 @@ class WalletView : public QStackedWidget AddressBookPage *usedSendingAddressesPage; AddressBookPage *usedReceivingAddressesPage; MasternodeList *masternodeListPage; + GovernanceList *governanceListPage; TransactionView *transactionView; @@ -83,6 +86,8 @@ public Q_SLOTS: void gotoMasternodePage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); + /** Switch to governance page */ + void gotoGovernancePage(); /** Switch to send coins page */ void gotoSendCoinsPage(QString addr = "");