mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-25 14:07:44 -05:00
Support for wordlists in user configuration directory (#6799)
This commit allows users to put alternative wordlists in a `wordlists` subdirectory below their KeePassXC directory (e.g., under Linux, `~/.config/keepassxc/wordlists`). These wordlists will then appear in the dropdown menu in the *Password Generator* widget. In order to differentiate between lists shipped with KeePassXC and user-provided lists, the former appears with a (SYSTEM) prefix.
This commit is contained in:
parent
bb88ad6e8c
commit
7811f10dba
1
COPYING
1
COPYING
@ -189,6 +189,7 @@ Files: share/icons/application/scalable/actions/chevron-double-down.svg
|
|||||||
share/icons/application/scalable/actions/statistics.svg
|
share/icons/application/scalable/actions/statistics.svg
|
||||||
share/icons/application/scalable/actions/system-help.svg
|
share/icons/application/scalable/actions/system-help.svg
|
||||||
share/icons/application/scalable/actions/system-search.svg
|
share/icons/application/scalable/actions/system-search.svg
|
||||||
|
share/icons/application/scalable/actions/trash.svg
|
||||||
share/icons/application/scalable/actions/url-copy.svg
|
share/icons/application/scalable/actions/url-copy.svg
|
||||||
share/icons/application/scalable/actions/username-copy.svg
|
share/icons/application/scalable/actions/username-copy.svg
|
||||||
share/icons/application/scalable/actions/view-history.svg
|
share/icons/application/scalable/actions/view-history.svg
|
||||||
|
1
share/icons/application/scalable/actions/trash.svg
Normal file
1
share/icons/application/scalable/actions/trash.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19M8,9H16V19H8V9M15.5,4L14.5,3H9.5L8.5,4H5V6H19V4H15.5Z" /></svg>
|
After Width: | Height: | Size: 394 B |
@ -70,6 +70,7 @@
|
|||||||
<file>application/scalable/actions/system-help.svg</file>
|
<file>application/scalable/actions/system-help.svg</file>
|
||||||
<file>application/scalable/actions/system-search.svg</file>
|
<file>application/scalable/actions/system-search.svg</file>
|
||||||
<file>application/scalable/actions/system-software-update.svg</file>
|
<file>application/scalable/actions/system-software-update.svg</file>
|
||||||
|
<file>application/scalable/actions/trash.svg</file>
|
||||||
<file>application/scalable/actions/url-copy.svg</file>
|
<file>application/scalable/actions/url-copy.svg</file>
|
||||||
<file>application/scalable/actions/user-guide.svg</file>
|
<file>application/scalable/actions/user-guide.svg</file>
|
||||||
<file>application/scalable/actions/username-copy.svg</file>
|
<file>application/scalable/actions/username-copy.svg</file>
|
||||||
|
@ -5690,6 +5690,34 @@ We recommend you use the AppImage available on our downloads page.</source>
|
|||||||
<source>Wordlist:</source>
|
<source>Wordlist:</source>
|
||||||
<translation>Wordlist:</translation>
|
<translation>Wordlist:</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Delete selected wordlist</source>
|
||||||
|
<translation>Delete selected wordlist</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Do you really want to delete the wordlist "%1"?</source>
|
||||||
|
<translation>Do you really want to delete the wordlist "%1"?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to delete wordlist</source>
|
||||||
|
<translation>Failed to delete wordlist</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Add custom wordlist</source>
|
||||||
|
<translation>Add custom wordlist</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Wordlists</source>
|
||||||
|
<translation>Wordlists</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All files</source>
|
||||||
|
<translation>All files</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to add wordlist</source>
|
||||||
|
<translation>Failed to add wordlist</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Word Separator:</source>
|
<source>Word Separator:</source>
|
||||||
<translation>Word Separator:</translation>
|
<translation>Word Separator:</translation>
|
||||||
@ -5874,6 +5902,27 @@ We recommend you use the AppImage available on our downloads page.</source>
|
|||||||
<source>character</source>
|
<source>character</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>(SYSTEM)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Confirm Delete Wordlist</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select Custom Wordlist</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Overwrite Wordlist?</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Wordlist "%1" already exists as a custom wordlist.
|
||||||
|
Do you want to overwrite it?</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PickcharsDialog</name>
|
<name>PickcharsDialog</name>
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <QLibrary>
|
#include <QLibrary>
|
||||||
|
|
||||||
#include "config-keepassx.h"
|
#include "config-keepassx.h"
|
||||||
|
#include "core/Config.h"
|
||||||
#include "core/Global.h"
|
#include "core/Global.h"
|
||||||
|
|
||||||
Resources* Resources::m_instance(nullptr);
|
Resources* Resources::m_instance(nullptr);
|
||||||
@ -91,6 +92,12 @@ QString Resources::wordlistPath(const QString& name) const
|
|||||||
return dataPath(QStringLiteral("wordlists/%1").arg(name));
|
return dataPath(QStringLiteral("wordlists/%1").arg(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Resources::userWordlistPath(const QString& name) const
|
||||||
|
{
|
||||||
|
QString configPath = QFileInfo(config()->getFileName()).absolutePath();
|
||||||
|
return configPath + QStringLiteral("/wordlists/%1").arg(name);
|
||||||
|
}
|
||||||
|
|
||||||
Resources::Resources()
|
Resources::Resources()
|
||||||
{
|
{
|
||||||
const QString appDirPath = QCoreApplication::applicationDirPath();
|
const QString appDirPath = QCoreApplication::applicationDirPath();
|
||||||
|
@ -27,6 +27,7 @@ public:
|
|||||||
QString dataPath(const QString& name) const;
|
QString dataPath(const QString& name) const;
|
||||||
QString pluginPath(const QString& name) const;
|
QString pluginPath(const QString& name) const;
|
||||||
QString wordlistPath(const QString& name) const;
|
QString wordlistPath(const QString& name) const;
|
||||||
|
QString userWordlistPath(const QString& name) const;
|
||||||
|
|
||||||
static Resources* instance();
|
static Resources* instance();
|
||||||
|
|
||||||
|
@ -28,7 +28,9 @@
|
|||||||
#include "core/PasswordHealth.h"
|
#include "core/PasswordHealth.h"
|
||||||
#include "core/Resources.h"
|
#include "core/Resources.h"
|
||||||
#include "gui/Clipboard.h"
|
#include "gui/Clipboard.h"
|
||||||
|
#include "gui/FileDialog.h"
|
||||||
#include "gui/Icons.h"
|
#include "gui/Icons.h"
|
||||||
|
#include "gui/MessageBox.h"
|
||||||
#include "gui/styles/StateColorPalette.h"
|
#include "gui/styles/StateColorPalette.h"
|
||||||
|
|
||||||
PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
||||||
@ -43,6 +45,8 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
|||||||
m_ui->buttonGenerate->setToolTip(
|
m_ui->buttonGenerate->setToolTip(
|
||||||
tr("Regenerate password (%1)").arg(m_ui->buttonGenerate->shortcut().toString(QKeySequence::NativeText)));
|
tr("Regenerate password (%1)").arg(m_ui->buttonGenerate->shortcut().toString(QKeySequence::NativeText)));
|
||||||
m_ui->buttonCopy->setIcon(icons()->icon("clipboard-text"));
|
m_ui->buttonCopy->setIcon(icons()->icon("clipboard-text"));
|
||||||
|
m_ui->buttonDeleteWordList->setIcon(icons()->icon("trash"));
|
||||||
|
m_ui->buttonAddWordList->setIcon(icons()->icon("document-new"));
|
||||||
m_ui->buttonClose->setShortcut(Qt::Key_Escape);
|
m_ui->buttonClose->setShortcut(Qt::Key_Escape);
|
||||||
|
|
||||||
// Add two shortcuts to save the form CTRL+Enter and CTRL+S
|
// Add two shortcuts to save the form CTRL+Enter and CTRL+S
|
||||||
@ -60,6 +64,8 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
|||||||
connect(m_ui->buttonApply, SIGNAL(clicked()), SLOT(applyPassword()));
|
connect(m_ui->buttonApply, SIGNAL(clicked()), SLOT(applyPassword()));
|
||||||
connect(m_ui->buttonCopy, SIGNAL(clicked()), SLOT(copyPassword()));
|
connect(m_ui->buttonCopy, SIGNAL(clicked()), SLOT(copyPassword()));
|
||||||
connect(m_ui->buttonGenerate, SIGNAL(clicked()), SLOT(regeneratePassword()));
|
connect(m_ui->buttonGenerate, SIGNAL(clicked()), SLOT(regeneratePassword()));
|
||||||
|
connect(m_ui->buttonDeleteWordList, SIGNAL(clicked()), SLOT(deleteWordList()));
|
||||||
|
connect(m_ui->buttonAddWordList, SIGNAL(clicked()), SLOT(addWordList()));
|
||||||
connect(m_ui->buttonClose, SIGNAL(clicked()), SIGNAL(closed()));
|
connect(m_ui->buttonClose, SIGNAL(clicked()), SIGNAL(closed()));
|
||||||
|
|
||||||
connect(m_ui->sliderLength, SIGNAL(valueChanged(int)), SLOT(passwordLengthChanged(int)));
|
connect(m_ui->sliderLength, SIGNAL(valueChanged(int)), SLOT(passwordLengthChanged(int)));
|
||||||
@ -92,15 +98,18 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
|||||||
m_ui->wordCaseComboBox->addItem(tr("UPPER CASE"), PassphraseGenerator::UPPERCASE);
|
m_ui->wordCaseComboBox->addItem(tr("UPPER CASE"), PassphraseGenerator::UPPERCASE);
|
||||||
m_ui->wordCaseComboBox->addItem(tr("Title Case"), PassphraseGenerator::TITLECASE);
|
m_ui->wordCaseComboBox->addItem(tr("Title Case"), PassphraseGenerator::TITLECASE);
|
||||||
|
|
||||||
|
// load system-wide wordlists
|
||||||
QDir path(resources()->wordlistPath(""));
|
QDir path(resources()->wordlistPath(""));
|
||||||
QStringList files = path.entryList(QDir::Files);
|
for (const auto& fileName : path.entryList(QDir::Files)) {
|
||||||
m_ui->comboBoxWordList->addItems(files);
|
m_ui->comboBoxWordList->addItem(tr("(SYSTEM)") + " " + fileName, fileName);
|
||||||
if (files.size() > 1) {
|
}
|
||||||
m_ui->comboBoxWordList->setVisible(true);
|
|
||||||
m_ui->labelWordList->setVisible(true);
|
m_firstCustomWordlistIndex = m_ui->comboBoxWordList->count();
|
||||||
} else {
|
|
||||||
m_ui->comboBoxWordList->setVisible(false);
|
// load user-provided wordlists
|
||||||
m_ui->labelWordList->setVisible(false);
|
path = QDir(resources()->userWordlistPath(""));
|
||||||
|
for (const auto& fileName : path.entryList(QDir::Files)) {
|
||||||
|
m_ui->comboBoxWordList->addItem(fileName, path.absolutePath() + QDir::separator() + fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
@ -164,7 +173,10 @@ void PasswordGeneratorWidget::loadSettings()
|
|||||||
// Diceware config
|
// Diceware config
|
||||||
m_ui->spinBoxWordCount->setValue(config()->get(Config::PasswordGenerator_WordCount).toInt());
|
m_ui->spinBoxWordCount->setValue(config()->get(Config::PasswordGenerator_WordCount).toInt());
|
||||||
m_ui->editWordSeparator->setText(config()->get(Config::PasswordGenerator_WordSeparator).toString());
|
m_ui->editWordSeparator->setText(config()->get(Config::PasswordGenerator_WordSeparator).toString());
|
||||||
m_ui->comboBoxWordList->setCurrentText(config()->get(Config::PasswordGenerator_WordList).toString());
|
int i = m_ui->comboBoxWordList->findData(config()->get(Config::PasswordGenerator_WordList).toString());
|
||||||
|
if (i > -1) {
|
||||||
|
m_ui->comboBoxWordList->setCurrentIndex(i);
|
||||||
|
}
|
||||||
m_ui->wordCaseComboBox->setCurrentIndex(config()->get(Config::PasswordGenerator_WordCase).toInt());
|
m_ui->wordCaseComboBox->setCurrentIndex(config()->get(Config::PasswordGenerator_WordCase).toInt());
|
||||||
|
|
||||||
// Password or diceware?
|
// Password or diceware?
|
||||||
@ -205,7 +217,7 @@ void PasswordGeneratorWidget::saveSettings()
|
|||||||
// Diceware config
|
// Diceware config
|
||||||
config()->set(Config::PasswordGenerator_WordCount, m_ui->spinBoxWordCount->value());
|
config()->set(Config::PasswordGenerator_WordCount, m_ui->spinBoxWordCount->value());
|
||||||
config()->set(Config::PasswordGenerator_WordSeparator, m_ui->editWordSeparator->text());
|
config()->set(Config::PasswordGenerator_WordSeparator, m_ui->editWordSeparator->text());
|
||||||
config()->set(Config::PasswordGenerator_WordList, m_ui->comboBoxWordList->currentText());
|
config()->set(Config::PasswordGenerator_WordList, m_ui->comboBoxWordList->currentData());
|
||||||
config()->set(Config::PasswordGenerator_WordCase, m_ui->wordCaseComboBox->currentIndex());
|
config()->set(Config::PasswordGenerator_WordCase, m_ui->wordCaseComboBox->currentIndex());
|
||||||
|
|
||||||
// Password or diceware?
|
// Password or diceware?
|
||||||
@ -329,6 +341,86 @@ bool PasswordGeneratorWidget::isPasswordVisible() const
|
|||||||
return m_ui->editNewPassword->isPasswordVisible();
|
return m_ui->editNewPassword->isPasswordVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PasswordGeneratorWidget::deleteWordList()
|
||||||
|
{
|
||||||
|
if (m_ui->comboBoxWordList->currentIndex() < m_firstCustomWordlistIndex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file(m_ui->comboBoxWordList->currentData().toString());
|
||||||
|
if (!file.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = MessageBox::question(this,
|
||||||
|
tr("Confirm Delete Wordlist"),
|
||||||
|
tr("Do you really want to delete the wordlist \"%1\"?").arg(file.fileName()),
|
||||||
|
MessageBox::Delete | MessageBox::Cancel,
|
||||||
|
MessageBox::Cancel);
|
||||||
|
if (result != MessageBox::Delete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.remove()) {
|
||||||
|
MessageBox::critical(this, tr("Failed to delete wordlist"), file.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui->comboBoxWordList->removeItem(m_ui->comboBoxWordList->currentIndex());
|
||||||
|
updateGenerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasswordGeneratorWidget::addWordList()
|
||||||
|
{
|
||||||
|
auto filter = QString("%1 (*.txt *.asc *.wordlist);;%2 (*)").arg(tr("Wordlists"), tr("All files"));
|
||||||
|
auto filePath = fileDialog()->getOpenFileName(this, tr("Select Custom Wordlist"), "", filter);
|
||||||
|
if (filePath.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create directory for user-specified wordlists, if necessary
|
||||||
|
QDir destDir(resources()->userWordlistPath(""));
|
||||||
|
destDir.mkpath(".");
|
||||||
|
|
||||||
|
// check if destination wordlist already exists
|
||||||
|
QString fileName = QFileInfo(filePath).fileName();
|
||||||
|
QString destPath = destDir.absolutePath() + QDir::separator() + fileName;
|
||||||
|
QFile dest(destPath);
|
||||||
|
if (dest.exists()) {
|
||||||
|
auto response = MessageBox::warning(this,
|
||||||
|
tr("Overwrite Wordlist?"),
|
||||||
|
tr("Wordlist \"%1\" already exists as a custom wordlist.\n"
|
||||||
|
"Do you want to overwrite it?")
|
||||||
|
.arg(fileName),
|
||||||
|
MessageBox::Overwrite | MessageBox::Cancel,
|
||||||
|
MessageBox::Cancel);
|
||||||
|
if (response != MessageBox::Overwrite) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!dest.remove()) {
|
||||||
|
MessageBox::critical(this, tr("Failed to delete wordlist"), dest.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy wordlist to destination path and add corresponding item to the combo box
|
||||||
|
QFile file(filePath);
|
||||||
|
if (!file.copy(destPath)) {
|
||||||
|
MessageBox::critical(this, tr("Failed to add wordlist"), file.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto index = m_ui->comboBoxWordList->findData(destPath);
|
||||||
|
if (index == -1) {
|
||||||
|
m_ui->comboBoxWordList->addItem(fileName, destPath);
|
||||||
|
index = m_ui->comboBoxWordList->count() - 1;
|
||||||
|
}
|
||||||
|
m_ui->comboBoxWordList->setCurrentIndex(index);
|
||||||
|
|
||||||
|
// update the password generator
|
||||||
|
updateGenerator();
|
||||||
|
}
|
||||||
|
|
||||||
void PasswordGeneratorWidget::setAdvancedMode(bool advanced)
|
void PasswordGeneratorWidget::setAdvancedMode(bool advanced)
|
||||||
{
|
{
|
||||||
saveSettings();
|
saveSettings();
|
||||||
@ -540,10 +632,15 @@ void PasswordGeneratorWidget::updateGenerator()
|
|||||||
static_cast<PassphraseGenerator::PassphraseWordCase>(m_ui->wordCaseComboBox->currentData().toInt()));
|
static_cast<PassphraseGenerator::PassphraseWordCase>(m_ui->wordCaseComboBox->currentData().toInt()));
|
||||||
|
|
||||||
m_dicewareGenerator->setWordCount(m_ui->spinBoxWordCount->value());
|
m_dicewareGenerator->setWordCount(m_ui->spinBoxWordCount->value());
|
||||||
if (!m_ui->comboBoxWordList->currentText().isEmpty()) {
|
auto path = m_ui->comboBoxWordList->currentData().toString();
|
||||||
QString path = resources()->wordlistPath(m_ui->comboBoxWordList->currentText());
|
if (m_ui->comboBoxWordList->currentIndex() < m_firstCustomWordlistIndex) {
|
||||||
m_dicewareGenerator->setWordList(path);
|
path = resources()->wordlistPath(path);
|
||||||
|
m_ui->buttonDeleteWordList->setEnabled(false);
|
||||||
|
} else {
|
||||||
|
m_ui->buttonDeleteWordList->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
m_dicewareGenerator->setWordList(path);
|
||||||
|
|
||||||
m_dicewareGenerator->setWordSeparator(m_ui->editWordSeparator->text());
|
m_dicewareGenerator->setWordSeparator(m_ui->editWordSeparator->text());
|
||||||
|
|
||||||
if (m_dicewareGenerator->isValid()) {
|
if (m_dicewareGenerator->isValid()) {
|
||||||
|
@ -61,6 +61,8 @@ public slots:
|
|||||||
void applyPassword();
|
void applyPassword();
|
||||||
void copyPassword();
|
void copyPassword();
|
||||||
void setPasswordVisible(bool visible);
|
void setPasswordVisible(bool visible);
|
||||||
|
void deleteWordList();
|
||||||
|
void addWordList();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void appliedPassword(const QString& password);
|
void appliedPassword(const QString& password);
|
||||||
@ -80,6 +82,7 @@ private slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_standalone = false;
|
bool m_standalone = false;
|
||||||
|
int m_firstCustomWordlistIndex;
|
||||||
|
|
||||||
void closeEvent(QCloseEvent* event);
|
void closeEvent(QCloseEvent* event);
|
||||||
PasswordGenerator::CharClasses charClasses();
|
PasswordGenerator::CharClasses charClasses();
|
||||||
|
@ -862,14 +862,44 @@ QProgressBar::chunk {
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="2">
|
<item row="0" column="2">
|
||||||
<widget class="QComboBox" name="comboBoxWordList">
|
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
||||||
<property name="sizePolicy">
|
<item>
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<widget class="QComboBox" name="comboBoxWordList">
|
||||||
<horstretch>0</horstretch>
|
<property name="sizePolicy">
|
||||||
<verstretch>0</verstretch>
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
</sizepolicy>
|
<horstretch>0</horstretch>
|
||||||
</property>
|
<verstretch>0</verstretch>
|
||||||
</widget>
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonDeleteWordList">
|
||||||
|
<property name="focusPolicy">
|
||||||
|
<enum>Qt::TabFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Delete selected wordlist</string>
|
||||||
|
</property>
|
||||||
|
<property name="accessibleDescription">
|
||||||
|
<string>Delete selected wordlist</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonAddWordList">
|
||||||
|
<property name="focusPolicy">
|
||||||
|
<enum>Qt::TabFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Add custom wordlist</string>
|
||||||
|
</property>
|
||||||
|
<property name="accessibleDescription">
|
||||||
|
<string>Add custom wordlist</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="2" alignment="Qt::AlignLeft">
|
<item row="4" column="2" alignment="Qt::AlignLeft">
|
||||||
<widget class="QLabel" name="charactersInPassphraseLabel">
|
<widget class="QLabel" name="charactersInPassphraseLabel">
|
||||||
@ -990,6 +1020,8 @@ QProgressBar::chunk {
|
|||||||
<tabstop>checkBoxExcludeAlike</tabstop>
|
<tabstop>checkBoxExcludeAlike</tabstop>
|
||||||
<tabstop>checkBoxEnsureEvery</tabstop>
|
<tabstop>checkBoxEnsureEvery</tabstop>
|
||||||
<tabstop>comboBoxWordList</tabstop>
|
<tabstop>comboBoxWordList</tabstop>
|
||||||
|
<tabstop>buttonDeleteWordList</tabstop>
|
||||||
|
<tabstop>buttonAddWordList</tabstop>
|
||||||
<tabstop>sliderWordCount</tabstop>
|
<tabstop>sliderWordCount</tabstop>
|
||||||
<tabstop>spinBoxWordCount</tabstop>
|
<tabstop>spinBoxWordCount</tabstop>
|
||||||
<tabstop>editWordSeparator</tabstop>
|
<tabstop>editWordSeparator</tabstop>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user