diff --git a/src/qt/forms/kevadialog.ui b/src/qt/forms/kevadialog.ui index 3575018d6..ce651c074 100644 --- a/src/qt/forms/kevadialog.ui +++ b/src/qt/forms/kevadialog.ui @@ -227,7 +227,7 @@ - + false @@ -247,7 +247,7 @@ - + false @@ -295,15 +295,12 @@ - reqLabel - reqAmount - useBech32 reqMessage receiveButton clearButton recentRequestsView - showRequestButton - removeRequestButton + showValueButton + removeButton diff --git a/src/qt/kevadialog.cpp b/src/qt/kevadialog.cpp index 9ae587906..563af90a7 100644 --- a/src/qt/kevadialog.cpp +++ b/src/qt/kevadialog.cpp @@ -38,12 +38,12 @@ KevaDialog::KevaDialog(const PlatformStyle *_platformStyle, QWidget *parent) : if (!_platformStyle->getImagesOnButtons()) { ui->receiveButton->setIcon(QIcon()); - ui->showRequestButton->setIcon(QIcon()); - ui->removeRequestButton->setIcon(QIcon()); + ui->showValueButton->setIcon(QIcon()); + ui->removeButton->setIcon(QIcon()); } else { ui->receiveButton->setIcon(_platformStyle->SingleColorIcon(":/icons/address-book")); - ui->showRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/edit")); - ui->removeRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove")); + ui->showValueButton->setIcon(_platformStyle->SingleColorIcon(":/icons/edit")); + ui->removeButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove")); } // context menu actions @@ -65,7 +65,6 @@ KevaDialog::KevaDialog(const PlatformStyle *_platformStyle, QWidget *parent) : connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); connect(copyMessageAction, SIGNAL(triggered()), this, SLOT(copyMessage())); connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount())); - } void KevaDialog::setModel(WalletModel *_model) @@ -87,9 +86,9 @@ void KevaDialog::setModel(WalletModel *_model) tableView->setColumnWidth(KevaTableModel::Key, KEY_COLUMN_WIDTH); tableView->setColumnWidth(KevaTableModel::Block, BLOCK_MINIMUM_COLUMN_WIDTH); - connect(tableView->selectionModel(), - SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, - SLOT(recentRequestsView_selectionChanged(QItemSelection, QItemSelection))); + connect(ui->kevaView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(kevaView_selectionChanged())); + // Last 2 columns are set by the columnResizingFixer, when the table geometry is ready. columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, BLOCK_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this); } @@ -182,15 +181,15 @@ void KevaDialog::on_kevaView_doubleClicked(const QModelIndex &index) dialog->show(); } -void KevaDialog::kevaView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +void KevaDialog::kevaView_selectionChanged() { // Enable Show/Remove buttons only if anything is selected. bool enable = !ui->kevaView->selectionModel()->selectedRows().isEmpty(); - ui->showRequestButton->setEnabled(enable); - ui->removeRequestButton->setEnabled(enable); + ui->showValueButton->setEnabled(enable); + ui->removeButton->setEnabled(enable); } -void KevaDialog::on_showRequestButton_clicked() +void KevaDialog::on_showValueButton_clicked() { if(!model || !model->getKevaTableModel() || !ui->kevaView->selectionModel()) return; @@ -201,16 +200,33 @@ void KevaDialog::on_showRequestButton_clicked() } } -void KevaDialog::on_removeRequestButton_clicked() +void KevaDialog::on_removeButton_clicked() { if(!model || !model->getKevaTableModel() || !ui->kevaView->selectionModel()) return; QModelIndexList selection = ui->kevaView->selectionModel()->selectedRows(); if(selection.empty()) return; - // correct for selection mode ContiguousSelection - QModelIndex firstIndex = selection.at(0); - model->getKevaTableModel()->removeRows(firstIndex.row(), selection.length(), firstIndex.parent()); + + QMessageBox::StandardButton reply; + QModelIndex index = selection.at(0); + QModelIndex keyIdx = index.sibling(index.row(), KevaTableModel::Key); + QString keyStr = keyIdx.data(Qt::DisplayRole).toString(); + reply = QMessageBox::warning(this, tr("Warning"), tr("Delete the key \"%1\"?").arg(keyStr), + QMessageBox::Cancel|QMessageBox::Ok); + + if (reply == QMessageBox::Cancel) { + return; + } + + std::string nameSpace = ui->nameSpace->text().toStdString(); + std::string key = keyStr.toStdString(); + + if (this->model->deleteKevaEntry(nameSpace, key)) { + // correct for selection mode ContiguousSelection + QModelIndex firstIndex = selection.at(0); + model->getKevaTableModel()->removeRows(firstIndex.row(), selection.length(), firstIndex.parent()); + } } // We override the virtual resizeEvent of the QWidget to adjust tables column @@ -300,3 +316,17 @@ void KevaDialog::copyAmount() { copyColumnToClipboard(KevaTableModel::Block); } + + +int KevaDialog::createNamespace(std::string displayName, std::string& namespaceId) +{ + if (!this->model) { + return 0; + } + + if (!this->model->createNamespace(displayName, namespaceId)) { + // TODO: show error message. + return 1; + } + return 1; +} diff --git a/src/qt/kevadialog.h b/src/qt/kevadialog.h index ce5f80da1..b043676ef 100644 --- a/src/qt/kevadialog.h +++ b/src/qt/kevadialog.h @@ -44,6 +44,7 @@ public: void setModel(WalletModel *model); void showNamespace(QString ns); + int createNamespace(std::string displayName, std::string& namespaceId); public Q_SLOTS: void clear(); @@ -68,10 +69,10 @@ private Q_SLOTS: void on_showContent_clicked(); void on_createNamespace_clicked(); void on_listNamespaces_clicked(); - void on_showRequestButton_clicked(); - void on_removeRequestButton_clicked(); + void on_showValueButton_clicked(); + void on_removeButton_clicked(); void on_kevaView_doubleClicked(const QModelIndex &index); - void kevaView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void kevaView_selectionChanged(); void updateDisplayUnit(); void showMenu(const QPoint &point); void copyURI(); diff --git a/src/qt/kevanamespacemodel.cpp b/src/qt/kevanamespacemodel.cpp index b963fa39a..f25294c37 100644 --- a/src/qt/kevanamespacemodel.cpp +++ b/src/qt/kevanamespacemodel.cpp @@ -105,10 +105,14 @@ bool KevaNamespaceModel::removeRows(int row, int count, const QModelIndex &paren Qt::ItemFlags KevaNamespaceModel::flags(const QModelIndex &index) const { - return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + const NamespaceEntry *rec = &list[index.row()]; + if (rec->confirmed) { + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + } else { + return Qt::ItemIsSelectable; + } } - // actually add to table in GUI void KevaNamespaceModel::setNamespace(std::vector vNamespaceEntries) { diff --git a/src/qt/kevanamespacemodel.h b/src/qt/kevanamespacemodel.h index bae625e55..56b99bdd5 100644 --- a/src/qt/kevanamespacemodel.h +++ b/src/qt/kevanamespacemodel.h @@ -16,10 +16,11 @@ class CWallet; class NamespaceEntry { public: - NamespaceEntry() { } + NamespaceEntry():confirmed(true) { } std::string id; std::string name; + bool confirmed; }; class NamespaceEntryLessThan diff --git a/src/qt/kevanewnamespacedialog.cpp b/src/qt/kevanewnamespacedialog.cpp index 4170ee021..2e004697b 100644 --- a/src/qt/kevanewnamespacedialog.cpp +++ b/src/qt/kevanewnamespacedialog.cpp @@ -6,7 +6,9 @@ #include #include +#include +#include #include KevaNewNamespaceDialog::KevaNewNamespaceDialog(QWidget *parent) : @@ -14,14 +16,36 @@ KevaNewNamespaceDialog::KevaNewNamespaceDialog(QWidget *parent) : ui(new Ui::KevaNewNamespaceDialog) { ui->setupUi(this); + connect(ui->buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject())); + connect(ui->buttonBox->button(QDialogButtonBox::Save), SIGNAL(clicked()), this, SLOT(accept())); + connect(ui->namespaceText, SIGNAL(textChanged(const QString &)), this, SLOT(onNamespaceChanged(const QString &))); + ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); +} + +void KevaNewNamespaceDialog::onNamespaceChanged(const QString & ns) +{ + int length = ns.length(); + bool enabled = length > 0; + ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(enabled); } void KevaNewNamespaceDialog::accept() { - // Create the namespace here. + KevaDialog* dialog = (KevaDialog*)this->parentWidget(); + QString nsText = ui->namespaceText->text(); + std::string namespaceId; + if (!dialog->createNamespace(nsText.toStdString(), namespaceId)) { + //TODO: error message. + return; + } + dialog->showNamespace(QString::fromStdString(namespaceId)); QDialog::accept(); } +void KevaNewNamespaceDialog::reject() +{ +} + KevaNewNamespaceDialog::~KevaNewNamespaceDialog() { delete ui; diff --git a/src/qt/kevanewnamespacedialog.h b/src/qt/kevanewnamespacedialog.h index fb0245e04..041b1707b 100644 --- a/src/qt/kevanewnamespacedialog.h +++ b/src/qt/kevanewnamespacedialog.h @@ -26,6 +26,8 @@ public: public Q_SLOTS: void accept(); + void reject(); + void onNamespaceChanged(const QString & ns); private: Ui::KevaNewNamespaceDialog *ui; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 6ed19d576..38fc3827e 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -40,6 +40,8 @@ #include #include +const int NAMESPACE_LENGTH = 21; +const std::string DUMMY_NAMESPACE = "___DUMMY_NAMESPACE___"; WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) : QObject(parent), wallet(_wallet), optionsModel(_optionsModel), addressTableModel(0), @@ -764,9 +766,27 @@ int WalletModel::getDefaultConfirmTarget() const void WalletModel::getKevaEntries(std::vector& vKevaEntries, std::string nameSpace) { + valtype nameSpaceVal = ValtypeFromString(nameSpace); + + { + // Get the unconfirmed namespaces and list them at the beginning. + LOCK (mempool.cs); + + std::vector> unconfirmedKeyValueList; + mempool.getUnconfirmedKeyValueList(unconfirmedKeyValueList, nameSpaceVal); + + for (auto e : unconfirmedKeyValueList) { + KevaEntry entry; + entry.key = ValtypeToString(std::get<1>(e)); + entry.value = ValtypeToString(std::get<2>(e)); + entry.block = -1; // Unconfirmed. + entry.date = QDateTime(); + vKevaEntries.push_back(std::move(entry)); + } + } + LOCK(cs_main); - valtype nameSpaceVal = ValtypeFromString(nameSpace); valtype key; CKevaData data; std::unique_ptr iter(pcoinsTip->IterateKeys(nameSpaceVal)); @@ -783,6 +803,7 @@ void WalletModel::getKevaEntries(std::vector& vKevaEntries, std::stri } vKevaEntries.push_back(std::move(entry)); } + } void WalletModel::getNamespaceEntries(std::vector& vNamespaceEntries) @@ -833,6 +854,23 @@ void WalletModel::getNamespaceEntries(std::vector& vNamespaceEnt } } + { + // Also get the unconfirmed namespaces and list them at the beginning. + LOCK (mempool.cs); + + std::vector> unconfirmedNamespaces; + mempool.getUnconfirmedNamespaceList(unconfirmedNamespaces); + + for (auto ns: unconfirmedNamespaces) { + NamespaceEntry entry; + entry.id = ValtypeToString(std::get<0>(ns)); + entry.name = ValtypeToString(std::get<1>(ns)); + entry.confirmed = false; + vNamespaceEntries.push_back(std::move(entry)); + } + } + + // The confirmed namespaces. std::map::iterator it = mapObjects.begin(); while (it != mapObjects.end()) { NamespaceEntry entry; @@ -842,3 +880,105 @@ void WalletModel::getNamespaceEntries(std::vector& vNamespaceEnt it++; } } + + +int WalletModel::createNamespace(std::string displayNameStr, std::string& namespaceId) +{ + const valtype displayName = ValtypeFromString (displayNameStr); + if (displayName.size() > MAX_NAMESPACE_LENGTH) { + return 0; + } + + CReserveKey keyName(wallet); + CPubKey pubKey; + const bool ok = keyName.GetReservedKey(pubKey, true); + assert(ok); + + CKeyID keyId = pubKey.GetID(); + + // The namespace name is: Hash160("first txin") + // For now we don't know the first txin, so use dummy name here. + // It will be replaced later in CreateTransaction. + valtype namespaceDummy = ToByteVector(std::string(DUMMY_NAMESPACE)); + assert(namespaceDummy.size() == NAMESPACE_LENGTH); + + CScript redeemScript = GetScriptForDestination(WitnessV0KeyHash(keyId)); + CScriptID scriptHash = CScriptID(redeemScript); + CScript addrName = GetScriptForDestination(scriptHash); + const CScript newScript = CKevaScript::buildKevaNamespace(addrName, namespaceDummy, displayName); + + CCoinControl coinControl; + CWalletTx wtx; + valtype kevaNamespace; + SendMoneyToScript(wallet, newScript, nullptr, kevaNamespace, + KEVA_LOCKED_AMOUNT, false, wtx, coinControl); + keyName.KeepKey(); + + namespaceId = EncodeBase58Check(kevaNamespace); + return 1; +} + + +int WalletModel::deleteKevaEntry(std::string namespaceStr, std::string keyStr) +{ + valtype nameSpace; + if (!DecodeKevaNamespace(namespaceStr, Params(), nameSpace)) { + //TODO: show error message. + return 0; + } + + const valtype key = ValtypeFromString(keyStr); + if (key.size() > MAX_KEY_LENGTH) { + //TODO: show error message. + return 0; + } + + bool hasKey = false; + CKevaData data; + { + LOCK2(cs_main, mempool.cs); + std::vector> unconfirmedKeyValueList; + valtype val; + if (mempool.getUnconfirmedKeyValue(nameSpace, key, val)) { + if (val.size() > 0) { + hasKey = true; + } + } else if (pcoinsTip->GetName(nameSpace, key, data)) { + hasKey = true; + } + } + + if (!hasKey) { + //TODO: show error message. + return 0; + } + + COutput output; + std::string kevaNamespce = namespaceStr; + if (!wallet->FindKevaCoin(output, kevaNamespce)) { + // TODO: This namespace can not be updated + return 0; + } + const COutPoint outp(output.tx->GetHash(), output.i); + const CTxIn txIn(outp); + + CReserveKey keyName(wallet); + CPubKey pubKeyReserve; + const bool ok = keyName.GetReservedKey(pubKeyReserve, true); + assert(ok); + + CScript redeemScript = GetScriptForDestination(WitnessV0KeyHash(pubKeyReserve.GetID())); + CScriptID scriptHash = CScriptID(redeemScript); + CScript addrName = GetScriptForDestination(scriptHash); + + const CScript kevaScript = CKevaScript::buildKevaDelete(addrName, nameSpace, key); + + CCoinControl coinControl; + CWalletTx wtx; + valtype empty; + SendMoneyToScript(wallet, kevaScript, &txIn, empty, + KEVA_LOCKED_AMOUNT, false, wtx, coinControl); + + keyName.KeepKey(); + return 1; +} \ No newline at end of file diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index d8a3cca45..cd3948dbd 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -229,6 +229,8 @@ public: // Keva void getKevaEntries(std::vector& vKevaEntries, std::string nameSpace); void getNamespaceEntries(std::vector& vNamespaceEntries); + int createNamespace(std::string displayName, std::string& namespaceId); + int deleteKevaEntry(std::string nameSpace, std::string key); private: CWallet *wallet;