#include "./enterpassworddialog.h" #include "../misc/dialogutils.h" #include "ui_enterpassworddialog.h" #include #include #include #include #include #include #include #include #if defined(QT_UTILITIES_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION) && defined(Q_OS_WIN32) #include #elif defined(X_AVAILABLE) #include #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(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(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(sender)->grabKeyboard(); } break; case QEvent::FocusOut: if (sender == m_ui->userNameLineEdit || sender == m_ui->password1LineEdit || sender == m_ui->password2LineEdit) { qobject_cast(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