Reworked the PasswordGeneratorWidget.

It's loosely based on OS X PasswordAssistant. Generation happens as soon as a
change is made, and on open of the widget. A combobox has been added to allow
one to choose from some randomally-generated alternatives, and the UI is
generally been made a bit more compact.

Written by Michael Curtis <michael@moltenmercury.org> and revised by me.

Closes #119

https://github.com/keepassx/keepassx/pull/38
This commit is contained in:
Felix Geyer 2014-01-12 23:33:36 +01:00
parent 29c997e1bc
commit 3af2307468
8 changed files with 449 additions and 197 deletions

View File

@ -85,6 +85,7 @@ set(keepassx_SOURCES
gui/MessageBox.cpp
gui/PasswordEdit.cpp
gui/PasswordGeneratorWidget.cpp
gui/PasswordComboBox.cpp
gui/SettingsWidget.cpp
gui/SortFilterHideProxyModel.cpp
gui/UnlockDatabaseWidget.cpp
@ -163,6 +164,7 @@ set(keepassx_MOC
gui/MainWindow.h
gui/PasswordEdit.h
gui/PasswordGeneratorWidget.h
gui/PasswordComboBox.h
gui/SettingsWidget.h
gui/SortFilterHideProxyModel.h
gui/UnlockDatabaseWidget.h

View File

@ -19,15 +19,33 @@
#include "crypto/Random.h"
PasswordGenerator* PasswordGenerator::m_instance = Q_NULLPTR;
QString PasswordGenerator::generatePassword(int length,
const PasswordGenerator::CharClasses& classes,
const PasswordGenerator::GeneratorFlags& flags)
PasswordGenerator::PasswordGenerator()
: m_length(0)
, m_classes(0)
, m_flags(0)
{
Q_ASSERT(isValidCombination(length, classes, flags));
}
QVector<PasswordGroup> groups = passwordGroups(classes, flags);
void PasswordGenerator::setLength(int length)
{
m_length = length;
}
void PasswordGenerator::setCharClasses(const CharClasses& classes)
{
m_classes = classes;
}
void PasswordGenerator::setFlags(const GeneratorFlags& flags)
{
m_flags = flags;
}
QString PasswordGenerator::generatePassword() const
{
Q_ASSERT(isValid());
QVector<PasswordGroup> groups = passwordGroups();
QVector<QChar> passwordChars;
Q_FOREACH (const PasswordGroup& group, groups) {
@ -38,14 +56,14 @@ QString PasswordGenerator::generatePassword(int length,
QString password;
if (flags & CharFromEveryGroup) {
if (m_flags & CharFromEveryGroup) {
for (int i = 0; i < groups.size(); i++) {
int pos = randomGen()->randomUInt(groups[i].size());
password.append(groups[i][pos]);
}
for (int i = groups.size(); i < length; i++) {
for (int i = groups.size(); i < m_length; i++) {
int pos = randomGen()->randomUInt(passwordChars.size());
password.append(passwordChars[pos]);
@ -61,7 +79,7 @@ QString PasswordGenerator::generatePassword(int length,
}
}
else {
for (int i = 0; i < length; i++) {
for (int i = 0; i < m_length; i++) {
int pos = randomGen()->randomUInt(passwordChars.size());
password.append(passwordChars[pos]);
@ -71,34 +89,31 @@ QString PasswordGenerator::generatePassword(int length,
return password;
}
bool PasswordGenerator::isValidCombination(int length,
const PasswordGenerator::CharClasses& classes,
const PasswordGenerator::GeneratorFlags& flags)
bool PasswordGenerator::isValid() const
{
if (classes == 0) {
if (m_classes == 0) {
return false;
}
else if (length == 0) {
else if (m_length == 0) {
return false;
}
if ((flags & CharFromEveryGroup) && (length < numCharClasses(classes))) {
if ((m_flags & CharFromEveryGroup) && (m_length < numCharClasses())) {
return false;
}
return true;
}
QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator::CharClasses& classes,
const PasswordGenerator::GeneratorFlags& flags)
QVector<PasswordGroup> PasswordGenerator::passwordGroups() const
{
QVector<PasswordGroup> passwordGroups;
if (classes & LowerLetters) {
if (m_classes & LowerLetters) {
PasswordGroup group;
for (int i = 97; i < (97 + 26); i++) {
if ((flags & ExcludeLookAlike) && (i == 108)) { // "l"
if ((m_flags & ExcludeLookAlike) && (i == 108)) { // "l"
continue;
}
@ -107,11 +122,11 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator
passwordGroups.append(group);
}
if (classes & UpperLetters) {
if (m_classes & UpperLetters) {
PasswordGroup group;
for (int i = 65; i < (65 + 26); i++) {
if ((flags & ExcludeLookAlike) && (i == 73 || i == 79)) { // "I" and "O"
if ((m_flags & ExcludeLookAlike) && (i == 73 || i == 79)) { // "I" and "O"
continue;
}
@ -120,11 +135,11 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator
passwordGroups.append(group);
}
if (classes & Numbers) {
if (m_classes & Numbers) {
PasswordGroup group;
for (int i = 48; i < (48 + 10); i++) {
if ((flags & ExcludeLookAlike) && (i == 48 || i == 49)) { // "0" and "1"
if ((m_flags & ExcludeLookAlike) && (i == 48 || i == 49)) { // "0" and "1"
continue;
}
@ -133,7 +148,7 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator
passwordGroups.append(group);
}
if (classes & SpecialCharacters) {
if (m_classes & SpecialCharacters) {
PasswordGroup group;
for (int i = 33; i <= 47; i++) {
@ -149,7 +164,7 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator
}
for (int i = 123; i <= 126; i++) {
if ((flags & ExcludeLookAlike) && (i == 124)) { // "|"
if ((m_flags & ExcludeLookAlike) && (i == 124)) { // "|"
continue;
}
@ -162,35 +177,23 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator
return passwordGroups;
}
int PasswordGenerator::numCharClasses(const PasswordGenerator::CharClasses& classes)
int PasswordGenerator::numCharClasses() const
{
int numClasses = 0;
if (classes & LowerLetters) {
if (m_classes & LowerLetters) {
numClasses++;
}
if (classes & UpperLetters) {
if (m_classes & UpperLetters) {
numClasses++;
}
if (classes & Numbers) {
if (m_classes & Numbers) {
numClasses++;
}
if (classes & SpecialCharacters) {
if (m_classes & SpecialCharacters) {
numClasses++;
}
return numClasses;
}
PasswordGenerator* PasswordGenerator::instance()
{
if (!m_instance) {
m_instance = new PasswordGenerator();
}
return m_instance;
}
PasswordGenerator::PasswordGenerator()
{
}

View File

@ -45,29 +45,28 @@ public:
};
Q_DECLARE_FLAGS(GeneratorFlags, GeneratorFlag)
QString generatePassword(int length, const PasswordGenerator::CharClasses& classes,
const PasswordGenerator::GeneratorFlags& flags);
bool isValidCombination(int length, const PasswordGenerator::CharClasses& classes,
const PasswordGenerator::GeneratorFlags& flags);
static PasswordGenerator* instance();
private:
public:
PasswordGenerator();
QVector<PasswordGroup> passwordGroups(const PasswordGenerator::CharClasses& classes,
const PasswordGenerator::GeneratorFlags& flags);
int numCharClasses(const PasswordGenerator::CharClasses& classes);
void setLength(int length);
void setCharClasses(const CharClasses& classes);
void setFlags(const GeneratorFlags& flags);
static PasswordGenerator* m_instance;
bool isValid() const;
QString generatePassword() const;
private:
QVector<PasswordGroup> passwordGroups() const;
int numCharClasses() const;
int m_length;
CharClasses m_classes;
GeneratorFlags m_flags;
Q_DISABLE_COPY(PasswordGenerator)
};
inline PasswordGenerator* passwordGenerator() {
return PasswordGenerator::instance();
}
Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordGenerator::CharClasses)
Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordGenerator::GeneratorFlags)

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2013 Michael Curtis <michael@moltenmercury.org>
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PasswordComboBox.h"
#include <QLineEdit>
#include "core/PasswordGenerator.h"
PasswordComboBox::PasswordComboBox(QWidget* parent)
: QComboBox(parent)
, m_generator(Q_NULLPTR)
, m_alternatives(10)
{
setEditable(true);
setEcho(false);
}
PasswordComboBox::~PasswordComboBox()
{
}
void PasswordComboBox::setEcho(bool echo)
{
lineEdit()->setEchoMode(echo ? QLineEdit::Normal : QLineEdit::Password);
QString current = currentText();
if (echo) {
// add fake item to show visual indication that a popup is available
addItem("");
}
else {
// clear items so the combobox indicates that no popup menu is available
clear();
}
setEditText(current);
}
void PasswordComboBox::setGenerator(PasswordGenerator* generator)
{
m_generator = generator;
}
void PasswordComboBox::setNumberAlternatives(int alternatives)
{
m_alternatives = alternatives;
}
void PasswordComboBox::showPopup()
{
// no point in showing a bunch of hidden passwords
if (lineEdit()->echoMode() == QLineEdit::Password) {
hidePopup();
return;
}
// keep existing password as the first item in the popup
QString current = currentText();
clear();
addItem(current);
if (m_generator && m_generator->isValid()) {
for (int alternative = 0; alternative < m_alternatives; alternative++) {
QString password = m_generator->generatePassword();
addItem(password);
}
}
QComboBox::showPopup();
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2013 Michael Curtis <michael@moltenmercury.org>
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KEEPASSX_PASSWORDCOMBOBOX_H
#define KEEPASSX_PASSWORDCOMBOBOX_H
#include <QComboBox>
#include "core/Global.h"
class PasswordGenerator;
class PasswordComboBox : public QComboBox
{
Q_OBJECT
public:
explicit PasswordComboBox(QWidget* parent = Q_NULLPTR);
~PasswordComboBox();
void setEcho(bool echo);
void setGenerator(PasswordGenerator* generator);
void setNumberAlternatives(int alternatives);
void showPopup();
private:
PasswordGenerator* m_generator;
int m_alternatives;
};
#endif // KEEPASSX_PASSWORDCOMBOBOX_H

View File

@ -18,22 +18,36 @@
#include "PasswordGeneratorWidget.h"
#include "ui_PasswordGeneratorWidget.h"
#include <QLineEdit>
#include "core/Config.h"
#include "core/PasswordGenerator.h"
#include "core/FilePath.h"
PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
: QWidget(parent)
, m_updatingSpinBox(false)
, m_generator(new PasswordGenerator())
, m_ui(new Ui::PasswordGeneratorWidget())
{
m_ui->setupUi(this);
connect(m_ui->editNewPassword, SIGNAL(textChanged(QString)), SLOT(updateApplyEnabled(QString)));
m_ui->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show"));
connect(m_ui->editNewPassword->lineEdit(), SIGNAL(textChanged(QString)), SLOT(updateApplyEnabled(QString)));
connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), SLOT(togglePassword(bool)));
connect(m_ui->buttonGenerate, SIGNAL(clicked()), SLOT(generatePassword()));
connect(m_ui->buttonApply, SIGNAL(clicked()), SLOT(emitNewPassword()));
connect(m_ui->buttonApply, SIGNAL(clicked()), SLOT(saveSettings()));
reset();
connect(m_ui->sliderLength, SIGNAL(valueChanged(int)), SLOT(sliderMoved()));
connect(m_ui->spinBoxLength, SIGNAL(valueChanged(int)), SLOT(spinBoxChanged()));
connect(m_ui->optionButtons, SIGNAL(buttonClicked(int)), SLOT(updateGenerator()));
m_ui->editNewPassword->setGenerator(m_generator.data());
loadSettings();
reset();
}
PasswordGeneratorWidget::~PasswordGeneratorWidget()
@ -68,8 +82,10 @@ void PasswordGeneratorWidget::saveSettings()
void PasswordGeneratorWidget::reset()
{
m_ui->editNewPassword->setText("");
m_ui->togglePasswordButton->setChecked(true);
m_ui->editNewPassword->lineEdit()->setText("");
m_ui->togglePasswordButton->setChecked(config()->get("security/passwordscleartext").toBool());
updateGenerator();
}
void PasswordGeneratorWidget::updateApplyEnabled(const QString& password)
@ -79,27 +95,35 @@ void PasswordGeneratorWidget::updateApplyEnabled(const QString& password)
void PasswordGeneratorWidget::togglePassword(bool checked)
{
m_ui->editNewPassword->setEchoMode(checked ? QLineEdit::Password : QLineEdit::Normal);
}
void PasswordGeneratorWidget::generatePassword()
{
int length = m_ui->spinBoxLength->value();
PasswordGenerator::CharClasses classes = charClasses();
PasswordGenerator::GeneratorFlags flags = generatorFlags();
if (!passwordGenerator()->isValidCombination(length, classes, flags)) {
// TODO: display error
return;
}
QString password = passwordGenerator()->generatePassword(length, classes, flags);
m_ui->editNewPassword->setText(password);
m_ui->editNewPassword->setEcho(checked);
}
void PasswordGeneratorWidget::emitNewPassword()
{
Q_EMIT newPassword(m_ui->editNewPassword->text());
Q_EMIT newPassword(m_ui->editNewPassword->lineEdit()->text());
}
void PasswordGeneratorWidget::sliderMoved()
{
if (m_updatingSpinBox) {
return;
}
m_ui->spinBoxLength->setValue(m_ui->sliderLength->value());
updateGenerator();
}
void PasswordGeneratorWidget::spinBoxChanged()
{
// Interlock so that we don't update twice - this causes issues as the spinbox can go higher than slider
m_updatingSpinBox = true;
m_ui->sliderLength->setValue(m_ui->spinBoxLength->value());
m_updatingSpinBox = false;
updateGenerator();
}
PasswordGenerator::CharClasses PasswordGeneratorWidget::charClasses()
@ -139,3 +163,15 @@ PasswordGenerator::GeneratorFlags PasswordGeneratorWidget::generatorFlags()
return flags;
}
void PasswordGeneratorWidget::updateGenerator()
{
m_generator->setLength(m_ui->spinBoxLength->value());
m_generator->setCharClasses(charClasses());
m_generator->setFlags(generatorFlags());
if (m_generator->isValid()) {
QString password = m_generator->generatePassword();
m_ui->editNewPassword->setEditText(password);
}
}

View File

@ -19,6 +19,7 @@
#define KEEPASSX_PASSWORDGENERATORWIDGET_H
#include <QWidget>
#include <QComboBox>
#include "core/Global.h"
#include "core/PasswordGenerator.h"
@ -27,6 +28,8 @@ namespace Ui {
class PasswordGeneratorWidget;
}
class PasswordGenerator;
class PasswordGeneratorWidget : public QWidget
{
Q_OBJECT
@ -43,14 +46,21 @@ Q_SIGNALS:
private Q_SLOTS:
void updateApplyEnabled(const QString& password);
void togglePassword(bool checked);
void generatePassword();
void emitNewPassword();
void saveSettings();
void sliderMoved();
void spinBoxChanged();
void updateGenerator();
private:
bool m_updatingSpinBox;
PasswordGenerator::CharClasses charClasses();
PasswordGenerator::GeneratorFlags generatorFlags();
const QScopedPointer<PasswordGenerator> m_generator;
const QScopedPointer<Ui::PasswordGeneratorWidget> m_ui;
};

View File

@ -6,66 +6,79 @@
<rect>
<x>0</x>
<y>0</y>
<width>468</width>
<height>298</height>
<width>434</width>
<height>250</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="labelGroups">
<property name="text">
<string>Use the following password groups:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelNewPassword">
<property name="text">
<string>Password:</string>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxLower">
<property name="text">
<string>Lower letters</string>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item>
<widget class="PasswordComboBox" name="editNewPassword">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBoxNumbers">
<property name="text">
<string>Numbers</string>
<item>
<widget class="QToolButton" name="togglePasswordButton">
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxUpper">
<property name="text">
<string>Upper letters</string>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelLength">
<property name="text">
<string>Length:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QSlider" name="sliderLength">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>64</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>8</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBoxSpecialChars">
<property name="text">
<string>Special characters</string>
<item>
<widget class="QSpinBox" name="spinBoxLength">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
@ -74,38 +87,117 @@
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkBoxExcludeAlike">
<property name="text">
<string>Exclude look-alike characters</string>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Character Types</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QToolButton" name="checkBoxUpper">
<property name="toolTip">
<string>Upper Case Letters</string>
</property>
<property name="text">
<string>A-Z</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">optionButtons</string>
</attribute>
</widget>
</item>
<item>
<widget class="QToolButton" name="checkBoxLower">
<property name="toolTip">
<string>Lower Case Letters</string>
</property>
<property name="text">
<string>a-z</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">optionButtons</string>
</attribute>
</widget>
</item>
<item>
<widget class="QToolButton" name="checkBoxNumbers">
<property name="toolTip">
<string>Numbers</string>
</property>
<property name="text">
<string>0-9</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">optionButtons</string>
</attribute>
</widget>
</item>
<item>
<widget class="QToolButton" name="checkBoxSpecialChars">
<property name="toolTip">
<string>Special Characters</string>
</property>
<property name="text">
<string>/*_&amp; ...</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">optionButtons</string>
</attribute>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkBoxExcludeAlike">
<property name="text">
<string>Exclude look-alike characters</string>
</property>
<attribute name="buttonGroup">
<string notr="true">optionButtons</string>
</attribute>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxEnsureEvery">
<property name="text">
<string>Ensure that the password contains characters from every group</string>
</property>
<attribute name="buttonGroup">
<string notr="true">optionButtons</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxEnsureEvery">
<property name="text">
<string>Ensure that the password contains characters from every group</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="labelLength">
<property name="text">
<string>Length:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBoxLength">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
@ -119,51 +211,13 @@
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="labelNewPassword">
<property name="text">
<string>New password:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="editNewPassword">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="togglePasswordButton">
<property name="text">
<string>...</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonGenerate">
<property name="text">
<string>Generate</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonApply">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Apply</string>
<string>Accept</string>
</property>
</widget>
</item>
@ -171,19 +225,33 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>PasswordComboBox</class>
<extends>QComboBox</extends>
<header location="global">gui/PasswordComboBox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>editNewPassword</tabstop>
<tabstop>togglePasswordButton</tabstop>
<tabstop>sliderLength</tabstop>
<tabstop>spinBoxLength</tabstop>
<tabstop>checkBoxUpper</tabstop>
<tabstop>checkBoxLower</tabstop>
<tabstop>checkBoxNumbers</tabstop>
<tabstop>checkBoxUpper</tabstop>
<tabstop>checkBoxSpecialChars</tabstop>
<tabstop>checkBoxExcludeAlike</tabstop>
<tabstop>checkBoxEnsureEvery</tabstop>
<tabstop>spinBoxLength</tabstop>
<tabstop>editNewPassword</tabstop>
<tabstop>togglePasswordButton</tabstop>
<tabstop>buttonGenerate</tabstop>
<tabstop>buttonApply</tabstop>
</tabstops>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="optionButtons">
<property name="exclusive">
<bool>false</bool>
</property>
</buttongroup>
</buttongroups>
</ui>