qtutilities/enterpassworddialog/enterpassworddialog.cpp

342 lines
10 KiB
C++

#include "./enterpassworddialog.h"
#include "../misc/dialogutils.h"
#include "ui_enterpassworddialog.h"
#include <QApplication>
#include <QEvent>
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QIcon>
#include <QKeyEvent>
#include <QMessageBox>
#include <QStyle>
#if defined(QT_UTILITIES_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION) && defined(Q_OS_WIN32)
#include <windows.h>
#elif defined(X_AVAILABLE)
#include <X11/XKBlib.h>
#undef KeyPress
#undef KeyRelease
#undef FocusIn
#undef FocusOut
#endif
namespace QtUtilities {
/*!
* \class EnterPasswordDialog
* \brief The EnterPasswordDialog class provides a simple dialog to ask the user
* for a password.
*/
/*!
* \brief Constructs a password dialog.
* \param parent Specifies the parent widget.
*/
EnterPasswordDialog::EnterPasswordDialog(QWidget *parent)
: QDialog(parent)
, m_ui(new Ui::EnterPasswordDialog)
{
// setup ui
m_ui->setupUi(this);
makeHeading(m_ui->instructionLabel);
setStyleSheet(dialogStyleForPalette(palette()));
setDescription();
setPromptForUserName(false);
setVerificationRequired(false);
setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
installEventFilter(this);
m_ui->userNameLineEdit->installEventFilter(this);
m_ui->password1LineEdit->installEventFilter(this);
m_ui->password2LineEdit->installEventFilter(this);
m_capslockPressed = isCapslockPressed();
m_ui->capslockWarningWidget->setVisible(m_capslockPressed);
// draw icon to capslock warning graphics view
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, this);
QGraphicsScene *scene = new QGraphicsScene();
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(icon.pixmap(16, 16));
scene->addItem(item);
m_ui->capslockWarningGraphicsView->setScene(scene);
// connect signals and slots
connect(m_ui->showPasswordCheckBox, &QCheckBox::clicked, this, &EnterPasswordDialog::updateShowPassword);
connect(m_ui->noPwCheckBox, &QCheckBox::clicked, this, &EnterPasswordDialog::updateShowPassword);
connect(m_ui->confirmPushButton, &QPushButton::clicked, this, &EnterPasswordDialog::confirm);
connect(m_ui->abortPushButton, &QPushButton::clicked, this, &EnterPasswordDialog::abort);
// grab the keyboard
grabKeyboard();
}
/*!
* \brief Destroys the password dialog.
*/
EnterPasswordDialog::~EnterPasswordDialog()
{
}
/*!
* \brief Returns the description. The description is shown under the
* instruction text.
* \sa setDescription()
*/
QString EnterPasswordDialog::description() const
{
return m_ui->descLabel->text();
}
/*!
* \brief Sets the description.
* \sa description()
*/
void EnterPasswordDialog::setDescription(const QString &description)
{
m_ui->descLabel->setText(description);
m_ui->descLabel->setHidden(description.isEmpty());
adjustSize();
}
/*!
* \brief Returns whether the dialogs prompts for a user name as well.
*
* The dialog does not prompt for a user name by default.
*
* \sa setPromptForUserName()
*/
bool EnterPasswordDialog::promtForUserName() const
{
return !m_ui->userNameLineEdit->isHidden();
}
/*!
* \brief Sets whethere the dialog prompts for a user name as well.
* \sa promptForUserName()
*/
void EnterPasswordDialog::setPromptForUserName(bool prompt)
{
m_ui->userNameLineEdit->setHidden(!prompt);
adjustSize();
}
/*!
* \brief Returns an indication whether a verification (password has to be
* entered twice) is required.
*
* \sa EnterPasswordDialog::setVerificationRequired()
*/
bool EnterPasswordDialog::isVerificationRequired() const
{
return !m_ui->password2LineEdit->isHidden();
}
/*!
* \brief Returns an indication whether the user is force to enter a password.
*
* If no password is required, the user is allowed to skip the dialog without
* entering
* a password.
*
* \sa EnterPasswordDialog::setPasswordRequired()
*/
bool EnterPasswordDialog::isPasswordRequired() const
{
return m_ui->noPwCheckBox->isHidden();
}
/*!
* \brief Sets whether the user is force to enter a password.
*
* If no password is required, the user is allowed to skip the dialog without
* entering
* a password.
*
* \sa EnterPasswordDialog::isPasswordRequired()
*/
void EnterPasswordDialog::setPasswordRequired(bool value)
{
m_ui->noPwCheckBox->setHidden(value);
m_ui->noPwCheckBox->setChecked(false);
adjustSize();
}
/*!
* \brief Updates the relevant controls to show entered characters or to mask
* them them.
*
* This private slot is called when m_ui->showPasswordCheckBox is clicked.
*/
void EnterPasswordDialog::updateShowPassword()
{
m_ui->password1LineEdit->setEchoMode(m_ui->showPasswordCheckBox->isChecked() ? QLineEdit::Normal : QLineEdit::Password);
m_ui->password1LineEdit->setEnabled(!m_ui->noPwCheckBox->isChecked());
m_ui->password2LineEdit->setEnabled(!(m_ui->showPasswordCheckBox->isChecked() || m_ui->noPwCheckBox->isChecked()));
}
/*!
* \brief Sets whether a verification (password has to be entered twice) is
* required.
*
* \sa EnterPasswordDialog::isVerificationRequired()
*/
void EnterPasswordDialog::setVerificationRequired(bool value)
{
if (m_instruction.isEmpty()) {
m_ui->instructionLabel->setText(value ? tr("Enter the new password") : tr("Enter the password"));
}
m_ui->password2LineEdit->setHidden(!value);
adjustSize();
}
/*!
* \brief Sets the instruction text.
*
* \sa EnterPasswordDialog::instruction()
*/
void EnterPasswordDialog::setInstruction(const QString &value)
{
m_instruction = value;
if (m_instruction.isEmpty()) {
m_ui->instructionLabel->setText(isVerificationRequired() ? tr("Enter the new password") : tr("Enter the password"));
} else {
m_ui->instructionLabel->setText(value);
}
adjustSize();
}
bool EnterPasswordDialog::event(QEvent *event)
{
switch (event->type()) {
case QEvent::PaletteChange:
setStyleSheet(dialogStyleForPalette(palette()));
break;
case QEvent::KeyPress: {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_CapsLock) {
m_capslockPressed = !m_capslockPressed;
}
m_ui->capslockWarningWidget->setVisible(m_capslockPressed);
break;
}
case QEvent::LanguageChange:
m_ui->retranslateUi(this);
break;
default:;
}
return QDialog::event(event);
}
/*!
* \brief Internal method to notice when the capslock key is pressed by the
* user.
*
* Invocation of this method is done by installing the event filter in the
* constructor.
*/
bool EnterPasswordDialog::eventFilter(QObject *sender, QEvent *event)
{
switch (event->type()) {
case QEvent::KeyPress: {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_CapsLock) {
m_capslockPressed = !m_capslockPressed;
} else {
QString text = keyEvent->text();
if (text.length()) {
QChar firstChar = text.at(0);
bool shiftPressed = (keyEvent->modifiers() & Qt::ShiftModifier) != 0;
if ((shiftPressed && firstChar.isLower()) || (!shiftPressed && firstChar.isUpper())) {
m_capslockPressed = true;
} else if (firstChar.isLetter()) {
m_capslockPressed = false;
}
}
}
m_ui->capslockWarningWidget->setVisible(m_capslockPressed);
} break;
case QEvent::FocusIn:
if (sender == m_ui->userNameLineEdit || sender == m_ui->password1LineEdit || sender == m_ui->password2LineEdit) {
releaseKeyboard();
qobject_cast<QWidget *>(sender)->grabKeyboard();
}
break;
case QEvent::FocusOut:
if (sender == m_ui->userNameLineEdit || sender == m_ui->password1LineEdit || sender == m_ui->password2LineEdit) {
qobject_cast<QWidget *>(sender)->releaseKeyboard();
grabKeyboard();
}
break;
default:;
}
return false;
}
/*!
* \brief Sets the dialog status to QDialog::Accepted if a valid password has
* been enterd.
* Displays an error message otherwise.
*
* This private slot is called when m_ui->confirmPushButton is clicked.
*/
void EnterPasswordDialog::confirm()
{
if (!isPasswordRequired() && m_ui->noPwCheckBox->isChecked()) {
m_password.clear();
done(QDialog::Accepted);
} else {
QString userName = m_ui->userNameLineEdit->text();
QString password = m_ui->password1LineEdit->text();
QString repeatedPassword = m_ui->password2LineEdit->text();
if (promtForUserName() && userName.isEmpty()) {
QMessageBox::warning(this, windowTitle(), tr("You didn't enter a user name."));
} else if (password.isEmpty()) {
QMessageBox::warning(this, windowTitle(), tr("You didn't enter a password."));
} else {
if (isVerificationRequired() && (password != repeatedPassword) && !m_ui->showPasswordCheckBox->isChecked()) {
if (repeatedPassword.isEmpty()) {
QMessageBox::warning(this, windowTitle(),
tr("You have to enter the new password twice to "
"ensure you enterd it correct."));
} else {
QMessageBox::warning(this, windowTitle(), tr("You mistyped the password."));
}
} else {
m_userName = userName;
m_password = password;
done(QDialog::Accepted);
}
}
}
}
/*!
* \brief Returns an indication whether the capslock key is pressed using
* platform specific functions.
*
* \remarks
* - Returns always false for unsupported platforms.
* - This method always returns false when the detection is not
* supported. It is supported under X11
* and Windows.
* - The function requires the application to be linked against X11 on
* Linux/Unix.
* - This static function will be used internally to detect whether the
* capslock key is pressed when initializing the dialog.
*/
bool EnterPasswordDialog::isCapslockPressed()
{
#if defined(QT_UTILITIES_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION) && defined(Q_OS_WIN32)
return GetKeyState(VK_CAPITAL) == 1;
#elif defined(X_AVAILABLE)
auto *const d = XOpenDisplay(nullptr);
auto capsState = false;
if (d) {
unsigned n;
XkbGetIndicatorState(d, XkbUseCoreKbd, &n);
capsState = (n & 0x01) == 1;
}
return capsState;
#else
return false;
#endif
}
} // namespace QtUtilities