[Qt] extend validate line edit and btc address validator

- remove btc address length from address validator
- add an optional btc address check in validated line edit that defaults
  to off and is used in GUIUtil::setupAddressWidget()
- an isAcceptable() check is added to validated line edit on focus out
  which only kicks in, when a validator is used with that widget
- remove an isAcceptable() check from sendcoinsentry.cpp
- remove obsolete attributes from ui files, which are set by calling
  GUIUtil::setupAddressWidget()
- move some more things to GUIUtil::setupAddressWidget() and remove them
  from normal code e.g. placeholder text
This commit is contained in:
Philip Kaufmann 2013-11-20 15:56:51 +01:00 committed by Wladimir J. van der Laan
parent aab8fc58c6
commit c78bd93701
No known key found for this signature in database
GPG key ID: 74810B012346C9A6
11 changed files with 140 additions and 55 deletions

View file

@ -1,9 +1,11 @@
// Copyright (c) 2011-2013 The Bitcoin developers // Copyright (c) 2011-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying // Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "bitcoinaddressvalidator.h" #include "bitcoinaddressvalidator.h"
#include "base58.h"
/* Base58 characters are: /* Base58 characters are:
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
@ -11,21 +13,23 @@
- All numbers except for '0' - All numbers except for '0'
- All upper-case letters except for 'I' and 'O' - All upper-case letters except for 'I' and 'O'
- All lower-case letters except for 'l' - All lower-case letters except for 'l'
User friendly Base58 input can map
- 'l' and 'I' to '1'
- '0' and 'O' to 'o'
*/ */
BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : BitcoinAddressEntryValidator::BitcoinAddressEntryValidator(QObject *parent) :
QValidator(parent) QValidator(parent)
{ {
} }
QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) const QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &pos) const
{ {
Q_UNUSED(pos);
// Empty address is "intermediate" input
if (input.isEmpty())
return QValidator::Intermediate;
// Correction // Correction
for(int idx=0; idx<input.size();) for (int idx = 0; idx < input.size();)
{ {
bool removeChar = false; bool removeChar = false;
QChar ch = input.at(idx); QChar ch = input.at(idx);
@ -42,11 +46,13 @@ QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) co
default: default:
break; break;
} }
// Remove whitespace // Remove whitespace
if(ch.isSpace()) if (ch.isSpace())
removeChar = true; removeChar = true;
// To next character // To next character
if(removeChar) if (removeChar)
input.remove(idx, 1); input.remove(idx, 1);
else else
++idx; ++idx;
@ -54,11 +60,11 @@ QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) co
// Validation // Validation
QValidator::State state = QValidator::Acceptable; QValidator::State state = QValidator::Acceptable;
for(int idx=0; idx<input.size(); ++idx) for (int idx = 0; idx < input.size(); ++idx)
{ {
int ch = input.at(idx).unicode(); int ch = input.at(idx).unicode();
if(((ch >= '0' && ch<='9') || if (((ch >= '0' && ch<='9') ||
(ch >= 'a' && ch<='z') || (ch >= 'a' && ch<='z') ||
(ch >= 'A' && ch<='Z')) && (ch >= 'A' && ch<='Z')) &&
ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') ch != 'l' && ch != 'I' && ch != '0' && ch != 'O')
@ -71,11 +77,21 @@ QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) co
} }
} }
// Empty address is "intermediate" input
if(input.isEmpty())
{
state = QValidator::Intermediate;
}
return state; return state;
} }
BitcoinAddressCheckValidator::BitcoinAddressCheckValidator(QObject *parent) :
QValidator(parent)
{
}
QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &pos) const
{
Q_UNUSED(pos);
// Validate the passed Bitcoin address
CBitcoinAddress addr(input.toStdString());
if (addr.IsValid())
return QValidator::Acceptable;
return QValidator::Invalid;
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2011-2013 The Bitcoin developers // Copyright (c) 2011-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying // Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -7,19 +7,29 @@
#include <QValidator> #include <QValidator>
/** Base58 entry widget validator. /** Base58 entry widget validator, checks for valid characters and
Corrects near-miss characters and refuses characters that are not part of base58. * removes some whitespace.
*/ */
class BitcoinAddressValidator : public QValidator class BitcoinAddressEntryValidator : public QValidator
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit BitcoinAddressValidator(QObject *parent = 0); explicit BitcoinAddressEntryValidator(QObject *parent);
State validate(QString &input, int &pos) const; State validate(QString &input, int &pos) const;
};
static const int MaxAddressLength = 35; /** Bitcoin address widget validator, checks for a valid bitcoin address.
*/
class BitcoinAddressCheckValidator : public QValidator
{
Q_OBJECT
public:
explicit BitcoinAddressCheckValidator(QObject *parent);
State validate(QString &input, int &pos) const;
}; };
#endif // BITCOINADDRESSVALIDATOR_H #endif // BITCOINADDRESSVALIDATOR_H

View file

@ -47,7 +47,7 @@
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QLineEdit" name="addressEdit"> <widget class="QValidatedLineEdit" name="addressEdit">
<property name="toolTip"> <property name="toolTip">
<string>The address associated with this address list entry. This can only be modified for sending addresses.</string> <string>The address associated with this address list entry. This can only be modified for sending addresses.</string>
</property> </property>
@ -67,6 +67,13 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
<header>qvalidatedlineedit.h</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>

View file

@ -47,9 +47,6 @@
<property name="toolTip"> <property name="toolTip">
<string>The address to sign the message with (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> <string>The address to sign the message with (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string>
</property> </property>
<property name="maxLength">
<number>34</number>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@ -260,9 +257,6 @@
<property name="toolTip"> <property name="toolTip">
<string>The address the message was signed with (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> <string>The address the message was signed with (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string>
</property> </property>
<property name="maxLength">
<number>34</number>
</property>
</widget> </widget>
</item> </item>
<item> <item>

View file

@ -6,6 +6,7 @@
#include "bitcoinaddressvalidator.h" #include "bitcoinaddressvalidator.h"
#include "bitcoinunits.h" #include "bitcoinunits.h"
#include "qvalidatedlineedit.h"
#include "walletmodel.h" #include "walletmodel.h"
#include "core.h" #include "core.h"
@ -72,11 +73,16 @@ QFont bitcoinAddressFont()
return font; return font;
} }
void setupAddressWidget(QLineEdit *widget, QWidget *parent) void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
{ {
widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength); parent->setFocusProxy(widget);
widget->setValidator(new BitcoinAddressValidator(parent));
widget->setFont(bitcoinAddressFont()); widget->setFont(bitcoinAddressFont());
#if QT_VERSION >= 0x040700
widget->setPlaceholderText(QObject::tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)"));
#endif
widget->setValidator(new BitcoinAddressEntryValidator(parent));
widget->setCheckValidator(new BitcoinAddressCheckValidator(parent));
} }
void setupAmountWidget(QLineEdit *widget, QWidget *parent) void setupAmountWidget(QLineEdit *widget, QWidget *parent)

View file

@ -9,6 +9,7 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
class QValidatedLineEdit;
class SendCoinsRecipient; class SendCoinsRecipient;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -32,7 +33,7 @@ namespace GUIUtil
QFont bitcoinAddressFont(); QFont bitcoinAddressFont();
// Set up widgets for address and amounts // Set up widgets for address and amounts
void setupAddressWidget(QLineEdit *widget, QWidget *parent); void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent);
void setupAmountWidget(QLineEdit *widget, QWidget *parent); void setupAmountWidget(QLineEdit *widget, QWidget *parent);
// Parse "bitcoin:" URI into recipient object, return true on successful parsing // Parse "bitcoin:" URI into recipient object, return true on successful parsing

View file

@ -4,10 +4,13 @@
#include "qvalidatedlineedit.h" #include "qvalidatedlineedit.h"
#include "bitcoinaddressvalidator.h"
#include "guiconstants.h" #include "guiconstants.h"
QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) : QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) :
QLineEdit(parent), valid(true) QLineEdit(parent),
valid(true),
checkValidator(0)
{ {
connect(this, SIGNAL(textChanged(QString)), this, SLOT(markValid())); connect(this, SIGNAL(textChanged(QString)), this, SLOT(markValid()));
} }
@ -34,11 +37,20 @@ void QValidatedLineEdit::focusInEvent(QFocusEvent *evt)
{ {
// Clear invalid flag on focus // Clear invalid flag on focus
setValid(true); setValid(true);
QLineEdit::focusInEvent(evt); QLineEdit::focusInEvent(evt);
} }
void QValidatedLineEdit::focusOutEvent(QFocusEvent *evt)
{
checkValidity();
QLineEdit::focusOutEvent(evt);
}
void QValidatedLineEdit::markValid() void QValidatedLineEdit::markValid()
{ {
// As long as a user is typing ensure we display state as valid
setValid(true); setValid(true);
} }
@ -47,3 +59,49 @@ void QValidatedLineEdit::clear()
setValid(true); setValid(true);
QLineEdit::clear(); QLineEdit::clear();
} }
void QValidatedLineEdit::setEnabled(bool enabled)
{
if (!enabled)
{
// A disabled QValidatedLineEdit should be marked valid
setValid(true);
}
else
{
// Recheck validity when QValidatedLineEdit gets enabled
checkValidity();
}
QLineEdit::setEnabled(enabled);
}
void QValidatedLineEdit::checkValidity()
{
if (text().isEmpty())
{
setValid(true);
}
else if (hasAcceptableInput())
{
setValid(true);
// Check contents on focus out
if (checkValidator)
{
QString address = text();
int pos = 0;
if (checkValidator->validate(address, pos) == QValidator::Acceptable)
setValid(true);
else
setValid(false);
}
}
else
setValid(false);
}
void QValidatedLineEdit::setCheckValidator(const QValidator *v)
{
checkValidator = v;
}

View file

@ -15,20 +15,25 @@ class QValidatedLineEdit : public QLineEdit
Q_OBJECT Q_OBJECT
public: public:
explicit QValidatedLineEdit(QWidget *parent = 0); explicit QValidatedLineEdit(QWidget *parent);
void clear(); void clear();
void setCheckValidator(const QValidator *v);
protected: protected:
void focusInEvent(QFocusEvent *evt); void focusInEvent(QFocusEvent *evt);
void focusOutEvent(QFocusEvent *evt);
private: private:
bool valid; bool valid;
const QValidator *checkValidator;
public slots: public slots:
void setValid(bool valid); void setValid(bool valid);
void setEnabled(bool enabled);
private slots: private slots:
void markValid(); void markValid();
void checkValidity();
}; };
#endif // QVALIDATEDLINEEDIT_H #endif // QVALIDATEDLINEEDIT_H

View file

@ -33,9 +33,8 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
ui->clearButton->setIcon(QIcon()); ui->clearButton->setIcon(QIcon());
ui->sendButton->setIcon(QIcon()); ui->sendButton->setIcon(QIcon());
#endif #endif
#if QT_VERSION >= 0x040700
ui->lineEditCoinControlChange->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
#endif
addEntry(); addEntry();
@ -43,7 +42,6 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
// Coin Control // Coin Control
ui->lineEditCoinControlChange->setFont(GUIUtil::bitcoinAddressFont());
connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked())); connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int))); connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &))); connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &)));
@ -536,7 +534,6 @@ void SendCoinsDialog::coinControlChangeChecked(int state)
if (state == Qt::Unchecked) if (state == Qt::Unchecked)
{ {
CoinControlDialog::coinControl->destChange = CNoDestination(); CoinControlDialog::coinControl->destChange = CNoDestination();
ui->lineEditCoinControlChange->setValid(true);
ui->labelCoinControlChangeLabel->clear(); ui->labelCoinControlChangeLabel->clear();
} }
else else
@ -563,7 +560,6 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
} }
else if (!addr.IsValid()) // Invalid address else if (!addr.IsValid()) // Invalid address
{ {
ui->lineEditCoinControlChange->setValid(false);
ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address")); ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
} }
else // Valid address else // Valid address
@ -573,7 +569,6 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
addr.GetKeyID(keyid); addr.GetKeyID(keyid);
if (!model->getPubKey(keyid, pubkey)) // Unknown change address if (!model->getPubKey(keyid, pubkey)) // Unknown change address
{ {
ui->lineEditCoinControlChange->setValid(false);
ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
} }
else // Known change address else // Known change address

View file

@ -28,9 +28,7 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) :
#endif #endif
#if QT_VERSION >= 0x040700 #if QT_VERSION >= 0x040700
ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book"));
ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)"));
#endif #endif
setFocusProxy(ui->payTo);
// normal bitcoin address field // normal bitcoin address field
GUIUtil::setupAddressWidget(ui->payTo, this); GUIUtil::setupAddressWidget(ui->payTo, this);
@ -121,7 +119,7 @@ bool SendCoinsEntry::validate()
if (recipient.paymentRequest.IsInitialized()) if (recipient.paymentRequest.IsInitialized())
return retval; return retval;
if (!ui->payTo->hasAcceptableInput() || !model->validateAddress(ui->payTo->text())) if (!model->validateAddress(ui->payTo->text()))
{ {
ui->payTo->setValid(false); ui->payTo->setValid(false);
retval = false; retval = false;

View file

@ -26,11 +26,8 @@ SignVerifyMessageDialog::SignVerifyMessageDialog(QWidget *parent) :
ui->setupUi(this); ui->setupUi(this);
#if QT_VERSION >= 0x040700 #if QT_VERSION >= 0x040700
ui->addressIn_SM->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)"));
ui->signatureOut_SM->setPlaceholderText(tr("Click \"Sign Message\" to generate signature")); ui->signatureOut_SM->setPlaceholderText(tr("Click \"Sign Message\" to generate signature"));
ui->addressIn_VM->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); ui->addressIn_VM->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)"));
ui->signatureIn_VM->setPlaceholderText(tr("Enter Bitcoin signature"));
#endif #endif
GUIUtil::setupAddressWidget(ui->addressIn_SM, this); GUIUtil::setupAddressWidget(ui->addressIn_SM, this);
@ -112,7 +109,6 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
CBitcoinAddress addr(ui->addressIn_SM->text().toStdString()); CBitcoinAddress addr(ui->addressIn_SM->text().toStdString());
if (!addr.IsValid()) if (!addr.IsValid())
{ {
ui->addressIn_SM->setValid(false);
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return; return;
@ -193,7 +189,6 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
CBitcoinAddress addr(ui->addressIn_VM->text().toStdString()); CBitcoinAddress addr(ui->addressIn_VM->text().toStdString());
if (!addr.IsValid()) if (!addr.IsValid())
{ {
ui->addressIn_VM->setValid(false);
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return; return;