From 69d22c9b0b01953f7483a1cddf00a4a764251d4f Mon Sep 17 00:00:00 2001 From: Samuel Dobson Date: Tue, 25 Jan 2022 00:43:16 +1300 Subject: [PATCH] Add address error location to GUI --- src/qt/bitcoinaddressvalidator.cpp | 5 +++ src/qt/bitcoinaddressvalidator.h | 16 ++++++++ src/qt/guiutil.cpp | 1 + src/qt/qvalidatedlineedit.cpp | 61 +++++++++++++++++++++++++++++- src/qt/qvalidatedlineedit.h | 7 ++++ 5 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index a2adca6eac7..a3eac48cfac 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -95,3 +95,8 @@ QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &po return QValidator::Invalid; } + +void BitcoinAddressErrorLocator::locateErrors(const QString& str, std::string& error_message, std::vector* error_locations) const +{ + DecodeDestination(str.toStdString(), error_message, error_locations); +} diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h index ae698c72a6b..a4b5cbf6f77 100644 --- a/src/qt/bitcoinaddressvalidator.h +++ b/src/qt/bitcoinaddressvalidator.h @@ -32,4 +32,20 @@ class BitcoinAddressCheckValidator : public QValidator State validate(QString &input, int &pos) const override; }; +class ErrorLocator +{ +public: + virtual void locateErrors(const QString& str, std::string& error_message, std::vector* error_locations) const = 0; + virtual ~ErrorLocator() {}; +}; + +/** Bitcoin address error locator. + */ +class BitcoinAddressErrorLocator : public ErrorLocator +{ +public: + void locateErrors(const QString& str, std::string& error_message, std::vector* error_locations) const override; + ~BitcoinAddressErrorLocator() override {}; +}; + #endif // BITCOIN_QT_BITCOINADDRESSVALIDATOR_H diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index c6c8f7b7a6a..7e1e13102c0 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -123,6 +123,7 @@ void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent) QString::fromStdString(DummyAddress(Params())))); widget->setValidator(new BitcoinAddressEntryValidator(parent)); widget->setCheckValidator(new BitcoinAddressCheckValidator(parent)); + widget->setErrorLocator(new BitcoinAddressErrorLocator()); } void AddButtonShortcut(QAbstractButton* button, const QKeySequence& shortcut) diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp index aa936d6b7c7..ef66274e2a0 100644 --- a/src/qt/qvalidatedlineedit.cpp +++ b/src/qt/qvalidatedlineedit.cpp @@ -7,10 +7,15 @@ #include #include +#include +#include +#include + QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) : QLineEdit(parent), valid(true), - checkValidator(nullptr) + checkValidator(nullptr), + errorLocator(nullptr) { connect(this, &QValidatedLineEdit::textChanged, this, &QValidatedLineEdit::markValid); } @@ -108,6 +113,11 @@ void QValidatedLineEdit::setCheckValidator(const QValidator *v) checkValidator = v; } +void QValidatedLineEdit::setErrorLocator(const ErrorLocator *e) +{ + errorLocator = e; +} + bool QValidatedLineEdit::isValid() { // use checkValidator in case the QValidatedLineEdit is disabled @@ -121,3 +131,52 @@ bool QValidatedLineEdit::isValid() return valid; } + +void QValidatedLineEdit::locateErrors() +{ + if (!errorLocator) return; + + // Perform error location on the input + QString input = text(); + std::vector error_locations{}; + std::string error_message; + errorLocator->locateErrors(input, error_message, &error_locations); + + if (error_message.empty()) { + QMessageBox::information(this, "Error locator", "No errors detected.", QMessageBox::Ok); + } else { + QString error_text = "Error: " + QString::fromStdString(error_message) + "
"; + if (!error_locations.empty()) { + error_text += "
";
+            int prev_loc = 0;
+            for (int loc : error_locations) {
+                error_text += input.mid(prev_loc, loc - prev_loc);
+                error_text +=  QString("%1").arg(input[loc]);
+                prev_loc = loc + 1;
+            }
+            if (prev_loc < input.size()) {
+                error_text += input.right(input.size() - prev_loc);
+            }
+            error_text += "
"; + } + QMessageBox msg = QMessageBox(QMessageBox::Information, "Error locator", error_text, QMessageBox::Ok, parentWidget()); + msg.exec(); + } +} + +void QValidatedLineEdit::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *contextMenu = createStandardContextMenu(); + if (errorLocator) { + contextMenu->addAction(tr("Attempt error location"), this, &QValidatedLineEdit::locateErrors); + } + contextMenu->exec(event->globalPos()); + delete contextMenu; +} + +QValidatedLineEdit::~QValidatedLineEdit() +{ + if (errorLocator) { + delete errorLocator; + } +} diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h index b32305f5e19..42354781e7f 100644 --- a/src/qt/qvalidatedlineedit.h +++ b/src/qt/qvalidatedlineedit.h @@ -7,6 +7,8 @@ #include +class ErrorLocator; + /** Line edit that can be marked as "invalid" to show input validation feedback. When marked as invalid, it will get a red background until it is focused. */ @@ -16,17 +18,21 @@ class QValidatedLineEdit : public QLineEdit public: explicit QValidatedLineEdit(QWidget *parent); + ~QValidatedLineEdit(); void clear(); void setCheckValidator(const QValidator *v); + void setErrorLocator(const ErrorLocator *e); bool isValid(); protected: void focusInEvent(QFocusEvent *evt) override; void focusOutEvent(QFocusEvent *evt) override; + void contextMenuEvent(QContextMenuEvent *event) override; private: bool valid; const QValidator *checkValidator; + const ErrorLocator *errorLocator; public Q_SLOTS: void setValid(bool valid); @@ -38,6 +44,7 @@ public Q_SLOTS: private Q_SLOTS: void markValid(); void checkValidity(); + void locateErrors(); }; #endif // BITCOIN_QT_QVALIDATEDLINEEDIT_H