diff --git a/src/browser/BrowserAccessControlDialog.ui b/src/browser/BrowserAccessControlDialog.ui index 29715314d..55914bfec 100755 --- a/src/browser/BrowserAccessControlDialog.ui +++ b/src/browser/BrowserAccessControlDialog.ui @@ -48,6 +48,9 @@ + + Allow access + Allow @@ -55,6 +58,9 @@ + + Deny access + Deny diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserOptionDialog.ui index 3466100a3..0e3e446f1 100755 --- a/src/browser/BrowserOptionDialog.ui +++ b/src/browser/BrowserOptionDialog.ui @@ -365,6 +365,9 @@ + + Custom proxy location field + 999 @@ -375,6 +378,9 @@ + + Browser for custom proxy file + Browse... diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index f8e9f0371..eb88b5c1b 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -17,12 +17,12 @@ * along with this program. If not, see . */ +#include #include #include #include #include #include -#include #include "BrowserAccessControlDialog.h" #include "BrowserEntryConfig.h" diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 681f87be4..ce0b5a65a 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -89,6 +89,13 @@ void Config::sync() m_settings->sync(); } +void Config::resetToDefaults() +{ + for (const auto& setting : m_defaults.keys()) { + m_settings->setValue(setting, m_defaults.value(setting)); + } +} + void Config::upgrade() { const auto keys = deprecationMap.keys(); diff --git a/src/core/Config.h b/src/core/Config.h index ca77e0c0a..d65b3256b 100644 --- a/src/core/Config.h +++ b/src/core/Config.h @@ -38,6 +38,7 @@ public: void set(const QString& key, const QVariant& value); bool hasAccessError(); void sync(); + void resetToDefaults(); static Config* instance(); static void createConfigFromFile(const QString& file); diff --git a/src/core/Translator.cpp b/src/core/Translator.cpp index 95de3ce91..4e3f568cb 100644 --- a/src/core/Translator.cpp +++ b/src/core/Translator.cpp @@ -34,16 +34,21 @@ */ void Translator::installTranslators() { - QLocale locale; - QString language = config()->get("GUI/Language").toString(); - if (!language.isEmpty() && language != "system") { - // use actual English translation instead of the English locale source language - if (language == "en") { - language = "en_US"; - } - locale = QLocale(language); + QStringList languages; + QString languageSetting = config()->get("GUI/Language").toString(); + if (languageSetting.isEmpty() || languageSetting == "system") { + // NOTE: this is a workaround for the terrible way Qt loads languages + // using the QLocale::uiLanguages() approach. Instead, we search each + // language and all country variants in order before moving to the next. + QLocale locale; + languages = locale.uiLanguages(); + } else { + languages << languageSetting; } + // Always try to load english last + languages << "en_US"; + const QStringList paths = { #ifdef QT_DEBUG QString("%1/share/translations").arg(KEEPASSX_BINARY_DIR), @@ -52,9 +57,10 @@ void Translator::installTranslators() bool translationsLoaded = false; for (const QString& path : paths) { - translationsLoaded |= installTranslator(locale, path) || installTranslator(QLocale("en_US"), path); - if (!installQtTranslator(language, path)) { - installQtTranslator(QLocale("en"), path); + installQtTranslator(languages, path); + if (installTranslator(languages, path)) { + translationsLoaded = true; + break; } } @@ -64,6 +70,48 @@ void Translator::installTranslators() } } +/** + * Install KeePassXC translator. + * + * @param languages priority-ordered list of languages + * @param path absolute search path + * @return true on success + */ +bool Translator::installTranslator(const QStringList& languages, const QString& path) +{ + for (const auto& language : languages) { + QLocale locale(language); + QScopedPointer translator(new QTranslator(qApp)); + if (translator->load(locale, "keepassx_", "", path)) { + return QCoreApplication::installTranslator(translator.take()); + } + } + + return false; +} + +/** + * Install Qt5 base translator from the specified local search path or the default system path + * if no qtbase_* translations were found at the local path. + * + * @param languages priority-ordered list of languages + * @param path absolute search path + * @return true on success + */ +bool Translator::installQtTranslator(const QStringList& languages, const QString& path) +{ + for (const auto& language : languages) { + QLocale locale(language); + QScopedPointer qtTranslator(new QTranslator(qApp)); + if (qtTranslator->load(locale, "qtbase_", "", path)) { + return QCoreApplication::installTranslator(qtTranslator.take()); + } else if (qtTranslator->load(locale, "qtbase_", "", QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { + return QCoreApplication::installTranslator(qtTranslator.take()); + } + } + return false; +} + /** * @return list of pairs of available language codes and names */ @@ -108,38 +156,3 @@ QList> Translator::availableLanguages() return languages; } - -/** - * Install KeePassXC translator. - * - * @param language translator language - * @param path local search path - * @return true on success - */ -bool Translator::installTranslator(const QLocale& locale, const QString& path) -{ - QScopedPointer translator(new QTranslator(qApp)); - if (translator->load(locale, "keepassx_", "", path)) { - return QCoreApplication::installTranslator(translator.take()); - } - return false; -} - -/** - * Install Qt5 base translator from the specified local search path or the default system path - * if no qtbase_* translations were found at the local path. - * - * @param language translator language - * @param path local search path - * @return true on success - */ -bool Translator::installQtTranslator(const QLocale& locale, const QString& path) -{ - QScopedPointer qtTranslator(new QTranslator(qApp)); - if (qtTranslator->load(locale, "qtbase_", "", path)) { - return QCoreApplication::installTranslator(qtTranslator.take()); - } else if (qtTranslator->load(locale, "qtbase_", "", QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { - return QCoreApplication::installTranslator(qtTranslator.take()); - } - return false; -} diff --git a/src/core/Translator.h b/src/core/Translator.h index 23a18422c..8236ade43 100644 --- a/src/core/Translator.h +++ b/src/core/Translator.h @@ -29,8 +29,8 @@ public: static QList> availableLanguages(); private: - static bool installTranslator(const QLocale& locale, const QString& path); - static bool installQtTranslator(const QLocale& locale, const QString& path); + static bool installTranslator(const QStringList& languages, const QString& path); + static bool installQtTranslator(const QStringList& languages, const QString& path); }; #endif // KEEPASSX_TRANSLATOR_H diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index 22dea6a19..1e7dca651 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -28,6 +28,7 @@ #include "core/Global.h" #include "core/Translator.h" +#include "MessageBox.h" #include "touchid/TouchID.h" class ApplicationSettingsWidget::ExtraPage @@ -54,6 +55,28 @@ private: QWidget* widget; }; +/** + * Helper class to ignore mouse wheel events on non-focused widgets + * NOTE: The widget must NOT have a focus policy of "WHEEL" + */ +class MouseWheelEventFilter : public QObject +{ +public: + explicit MouseWheelEventFilter(QObject* parent) + : QObject(parent){}; + +protected: + bool eventFilter(QObject* obj, QEvent* event) override + { + const auto* widget = qobject_cast(obj); + if (event->type() == QEvent::Wheel && widget && !widget->hasFocus()) { + event->ignore(); + return true; + } + return QObject::eventFilter(obj, event); + } +}; + ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent) : EditWidget(parent) , m_secWidget(new QWidget()) @@ -84,6 +107,7 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent) connect(m_generalUi->systrayShowCheckBox, SIGNAL(toggled(bool)), SLOT(systrayToggled(bool))); connect(m_generalUi->toolbarHideCheckBox, SIGNAL(toggled(bool)), SLOT(toolbarSettingsToggled(bool))); connect(m_generalUi->rememberLastDatabasesCheckBox, SIGNAL(toggled(bool)), SLOT(rememberDatabasesToggled(bool))); + connect(m_generalUi->resetSettingsButton, SIGNAL(clicked()), SLOT(resetSettings())); connect(m_secUi->clearClipboardCheckBox, SIGNAL(toggled(bool)), m_secUi->clearClipboardSpinBox, SLOT(setEnabled(bool))); @@ -95,6 +119,13 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent) m_secUi->touchIDResetSpinBox, SLOT(setEnabled(bool))); // clang-format on + // Disable mouse wheel grab when scrolling + // This prevents combo box and spinner values from changing without explicit focus + auto mouseWheelFilter = new MouseWheelEventFilter(this); + m_generalUi->faviconTimeoutSpinBox->installEventFilter(mouseWheelFilter); + m_generalUi->toolButtonStyleComboBox->installEventFilter(mouseWheelFilter); + m_generalUi->languageComboBox->installEventFilter(mouseWheelFilter); + #ifdef WITH_XC_UPDATECHECK connect(m_generalUi->checkForUpdatesOnStartupCheckBox, SIGNAL(toggled(bool)), SLOT(checkUpdatesToggled(bool))); #else @@ -246,7 +277,6 @@ void ApplicationSettingsWidget::loadSettings() void ApplicationSettingsWidget::saveSettings() { - if (config()->hasAccessError()) { showMessage(tr("Access error for config file %1").arg(config()->getFileName()), MessageWidget::Error); // We prevent closing the settings page if we could not write to @@ -330,6 +360,7 @@ void ApplicationSettingsWidget::saveSettings() config()->set("LastDatabases", {}); config()->set("OpenPreviousDatabasesOnStartup", {}); config()->set("LastActiveDatabase", {}); + config()->set("LastAttachmentDir", {}); } if (!config()->get("RememberLastKeyFiles").toBool()) { @@ -342,6 +373,48 @@ void ApplicationSettingsWidget::saveSettings() } } +void ApplicationSettingsWidget::resetSettings() +{ + // Confirm reset + auto ans = MessageBox::question(this, + tr("Reset Settings?"), + tr("Are you sure you want to reset all general and security settings to default?"), + MessageBox::Reset | MessageBox::Cancel, + MessageBox::Cancel); + if (ans == MessageBox::Cancel) { + return; + } + + if (config()->hasAccessError()) { + showMessage(tr("Access error for config file %1").arg(config()->getFileName()), MessageWidget::Error); + // We prevent closing the settings page if we could not write to + // the config file. + return; + } + + // Reset general and security settings to default + config()->resetToDefaults(); + + // Clear recently used data + config()->set("LastDatabases", {}); + config()->set("OpenPreviousDatabasesOnStartup", {}); + config()->set("LastActiveDatabase", {}); + config()->set("LastAttachmentDir", {}); + config()->set("LastKeyFiles", {}); + config()->set("LastDir", ""); + + // Save the Extra Pages (these are NOT reset) + for (const ExtraPage& page : asConst(m_extraPages)) { + page.saveSettings(); + } + + config()->sync(); + + // Refresh the settings widget and notify listeners + loadSettings(); + emit settingsReset(); +} + void ApplicationSettingsWidget::reject() { // register the old key again as it might have changed diff --git a/src/gui/ApplicationSettingsWidget.h b/src/gui/ApplicationSettingsWidget.h index fe9ac88fc..63487e1b5 100644 --- a/src/gui/ApplicationSettingsWidget.h +++ b/src/gui/ApplicationSettingsWidget.h @@ -50,8 +50,12 @@ public: void addSettingsPage(ISettingsPage* page); void loadSettings(); +signals: + void settingsReset(); + private slots: void saveSettings(); + void resetSettings(); void reject(); void autoSaveToggled(bool checked); void hideWindowOnCopyCheckBoxToggled(bool checked); diff --git a/src/gui/ApplicationSettingsWidgetGeneral.ui b/src/gui/ApplicationSettingsWidgetGeneral.ui index 311b408b8..d2ffd2a1a 100644 --- a/src/gui/ApplicationSettingsWidgetGeneral.ui +++ b/src/gui/ApplicationSettingsWidgetGeneral.ui @@ -7,7 +7,7 @@ 0 0 684 - 860 + 951 @@ -349,6 +349,12 @@ 0 + + Qt::StrongFocus + + + Website icon download timeout in seconds + sec @@ -437,7 +443,7 @@ - + 0 @@ -487,8 +493,30 @@ 0 + + Qt::StrongFocus + + + Toolbar button style + + + QComboBox::AdjustToContents + + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -521,7 +549,7 @@ QLayout::SetMaximumSize - + Qt::Horizontal @@ -609,7 +637,7 @@ - + 8 @@ -634,6 +662,12 @@ 0 + + Qt::StrongFocus + + + Language selection + @@ -643,6 +677,68 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + + + 0 + + + QLayout::SetMaximumSize + + + + + Reset Settings to Default + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 50 + 20 + + + + @@ -715,6 +811,9 @@ 0 + + Global auto-type shortcut + @@ -732,6 +831,9 @@ 0 + + Auto-type character typing delay milliseconds + ms @@ -761,6 +863,9 @@ 0 + + Auto-type start delay milliseconds + ms diff --git a/src/gui/ApplicationSettingsWidgetSecurity.ui b/src/gui/ApplicationSettingsWidgetSecurity.ui index 209c156b3..2310bd07d 100644 --- a/src/gui/ApplicationSettingsWidgetSecurity.ui +++ b/src/gui/ApplicationSettingsWidgetSecurity.ui @@ -28,14 +28,7 @@ Timeouts - - - - - Clear clipboard after - - - + @@ -47,6 +40,9 @@ 0 + + Clipboard clear seconds + sec @@ -61,14 +57,93 @@ - - + + - Clear search query after + Forget TouchID after inactivity of + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + 0 + 0 + + + + Touch ID inactivity reset + + + min + + + 1440 + + + 30 + + + + + + + + 0 + 0 + + + + Lock databases after inactivity of + + + false + + + + 0 + 0 + + + + Database lock timeout seconds + + + sec + + + 10 + + + 43200 + + + 240 + + + + false @@ -96,70 +171,17 @@ + + + + Clear clipboard after + + + - - - - 0 - 0 - - + - Lock databases after inactivity of - - - - - - - false - - - - 0 - 0 - - - - sec - - - 10 - - - 43200 - - - 240 - - - - - - - Forget TouchID after inactivity of - - - - - - - false - - - - 0 - 0 - - - - min - - - 1440 - - - 30 + Clear search query after @@ -272,6 +294,21 @@ + + clearClipboardCheckBox + clearClipboardSpinBox + touchIDResetSpinBox + lockDatabaseOnScreenLockCheckBox + touchIDResetOnScreenLockCheckBox + lockDatabaseMinimizeCheckBox + relockDatabaseAutoTypeCheckBox + passwordRepeatCheckBox + passwordCleartextCheckBox + passwordShowDotsCheckBox + passwordPreviewCleartextCheckBox + hideNotesCheckBox + fallbackToSearch + diff --git a/src/gui/EditWidgetProperties.ui b/src/gui/EditWidgetProperties.ui index fb8ed5030..d80bf1584 100644 --- a/src/gui/EditWidgetProperties.ui +++ b/src/gui/EditWidgetProperties.ui @@ -46,6 +46,9 @@ 0 + + Datetime created + true @@ -66,6 +69,9 @@ 0 + + Datetime modified + true @@ -80,6 +86,9 @@ + + Datetime accessed + true @@ -94,6 +103,9 @@ + + Unique ID + true @@ -109,6 +121,9 @@ + + Plugin data + QAbstractItemView::NoEditTriggers @@ -130,6 +145,9 @@ + + Remove selected plugin data + Remove diff --git a/src/gui/EntryPreviewWidget.ui b/src/gui/EntryPreviewWidget.ui index b2c8c28bf..b620da659 100644 --- a/src/gui/EntryPreviewWidget.ui +++ b/src/gui/EntryPreviewWidget.ui @@ -521,7 +521,7 @@ Advanced - + 0 @@ -976,7 +976,7 @@ - + 6 diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index ad00a6621..bdfefab08 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -343,7 +343,7 @@ MainWindow::MainWindow() connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(setMenuActionState())); connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(updateWindowTitle())); connect(m_ui->settingsWidget, SIGNAL(accepted()), SLOT(applySettingsChanges())); - connect(m_ui->settingsWidget, SIGNAL(apply()), SLOT(applySettingsChanges())); + connect(m_ui->settingsWidget, SIGNAL(settingsReset()), SLOT(applySettingsChanges())); connect(m_ui->settingsWidget, SIGNAL(accepted()), SLOT(switchToDatabases())); connect(m_ui->settingsWidget, SIGNAL(rejected()), SLOT(switchToDatabases())); @@ -802,12 +802,12 @@ void MainWindow::openBugReportUrl() void MainWindow::openGettingStartedGuide() { - customOpenUrl(filePath()->dataPath("docs/KeePassXC_GettingStarted.pdf")); + customOpenUrl(QString("file:///%1").arg(filePath()->dataPath("docs/KeePassXC_GettingStarted.pdf"))); } void MainWindow::openUserGuide() { - customOpenUrl(filePath()->dataPath("docs/KeePassXC_UserGuide.pdf")); + customOpenUrl(QString("file:///%1").arg(filePath()->dataPath("docs/KeePassXC_UserGuide.pdf"))); } void MainWindow::openOnlineHelp() diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index 807cdcf6e..135454f61 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -69,6 +69,9 @@ 0 + + Qt::TabFocus + 2 @@ -116,7 +119,11 @@ 0 - + + + Qt::TabFocus + + @@ -141,7 +148,11 @@ - + + + Qt::TabFocus + + @@ -166,7 +177,11 @@ - + + + Qt::TabFocus + + @@ -183,6 +198,9 @@ 21 + + Qt::NoFocus + Qt::PreventContextMenu @@ -316,6 +334,9 @@ + + Qt::NoFocus + Qt::PreventContextMenu diff --git a/src/gui/MessageBox.cpp b/src/gui/MessageBox.cpp index 7652d6345..7d2b2a516 100644 --- a/src/gui/MessageBox.cpp +++ b/src/gui/MessageBox.cpp @@ -18,8 +18,8 @@ #include "MessageBox.h" -#include #include +#include QWindow* MessageBox::m_overrideParent(nullptr); @@ -63,6 +63,7 @@ void MessageBox::initializeButtonDefs() {Skip, {QMessageBox::tr("Skip"), QMessageBox::ButtonRole::AcceptRole}}, {Disable, {QMessageBox::tr("Disable"), QMessageBox::ButtonRole::AcceptRole}}, {Merge, {QMessageBox::tr("Merge"), QMessageBox::ButtonRole::AcceptRole}}, + {Continue, {QMessageBox::tr("Continue"), QMessageBox::ButtonRole::AcceptRole}}, }; } @@ -146,6 +147,17 @@ MessageBox::Button MessageBox::critical(QWidget* parent, return messageBox(parent, QMessageBox::Critical, title, text, buttons, defaultButton, action, checkbox); } +MessageBox::Button MessageBox::warning(QWidget* parent, + const QString& title, + const QString& text, + MessageBox::Buttons buttons, + MessageBox::Button defaultButton, + MessageBox::Action action, + QCheckBox* checkbox) +{ + return messageBox(parent, QMessageBox::Warning, title, text, buttons, defaultButton, action, checkbox); +} + MessageBox::Button MessageBox::information(QWidget* parent, const QString& title, const QString& text, @@ -168,17 +180,6 @@ MessageBox::Button MessageBox::question(QWidget* parent, return messageBox(parent, QMessageBox::Question, title, text, buttons, defaultButton, action, checkbox); } -MessageBox::Button MessageBox::warning(QWidget* parent, - const QString& title, - const QString& text, - MessageBox::Buttons buttons, - MessageBox::Button defaultButton, - MessageBox::Action action, - QCheckBox* checkbox) -{ - return messageBox(parent, QMessageBox::Warning, title, text, buttons, defaultButton, action, checkbox); -} - void MessageBox::setNextAnswer(MessageBox::Button button) { m_nextAnswer = button; diff --git a/src/gui/MessageBox.h b/src/gui/MessageBox.h index 13deffb62..dc6ed4a40 100644 --- a/src/gui/MessageBox.h +++ b/src/gui/MessageBox.h @@ -59,10 +59,11 @@ public: Skip = 1 << 24, Disable = 1 << 25, Merge = 1 << 26, + Continue = 1 << 27, // Internal loop markers. Update Last when new KeePassXC button is added First = Ok, - Last = Merge, + Last = Continue, }; enum Action @@ -80,30 +81,30 @@ public: const QString& title, const QString& text, Buttons buttons = MessageBox::Ok, - Button defaultButton = MessageBox::NoButton, - Action action = MessageBox::None, - QCheckBox* checkbox = nullptr); - static Button information(QWidget* parent, - const QString& title, - const QString& text, - Buttons buttons = MessageBox::Ok, - Button defaultButton = MessageBox::NoButton, - Action action = MessageBox::None, - QCheckBox* checkbox = nullptr); - static Button question(QWidget* parent, - const QString& title, - const QString& text, - Buttons buttons = MessageBox::Ok, - Button defaultButton = MessageBox::NoButton, + Button defaultButton = MessageBox::Ok, Action action = MessageBox::None, QCheckBox* checkbox = nullptr); static Button warning(QWidget* parent, const QString& title, const QString& text, Buttons buttons = MessageBox::Ok, - Button defaultButton = MessageBox::NoButton, + Button defaultButton = MessageBox::Ok, Action action = MessageBox::None, QCheckBox* checkbox = nullptr); + static Button information(QWidget* parent, + const QString& title, + const QString& text, + Buttons buttons = MessageBox::Ok, + Button defaultButton = MessageBox::Ok, + Action action = MessageBox::None, + QCheckBox* checkbox = nullptr); + static Button question(QWidget* parent, + const QString& title, + const QString& text, + Buttons buttons = MessageBox::Yes | MessageBox::Cancel, + Button defaultButton = MessageBox::Cancel, + Action action = MessageBox::None, + QCheckBox* checkbox = nullptr); class OverrideParent { diff --git a/src/gui/PasswordEdit.cpp b/src/gui/PasswordEdit.cpp index e341eddd4..bc5cfc9fd 100644 --- a/src/gui/PasswordEdit.cpp +++ b/src/gui/PasswordEdit.cpp @@ -19,6 +19,7 @@ #include "PasswordEdit.h" #include "core/Config.h" +#include "core/FilePath.h" #include "gui/Font.h" const QColor PasswordEdit::CorrectSoFarColor = QColor(255, 205, 15); @@ -28,6 +29,16 @@ PasswordEdit::PasswordEdit(QWidget* parent) : QLineEdit(parent) , m_basePasswordEdit(nullptr) { + const QIcon errorIcon = filePath()->icon("status", "dialog-error"); + m_errorAction = addAction(errorIcon, QLineEdit::TrailingPosition); + m_errorAction->setVisible(false); + m_errorAction->setToolTip(tr("Passwords do not match")); + + const QIcon correctIcon = filePath()->icon("actions", "dialog-ok"); + m_correctAction = addAction(correctIcon, QLineEdit::TrailingPosition); + m_correctAction->setVisible(false); + m_correctAction->setToolTip(tr("Passwords match so far")); + setEchoMode(QLineEdit::Password); updateStylesheet(); @@ -83,19 +94,23 @@ bool PasswordEdit::passwordsEqual() const void PasswordEdit::updateStylesheet() { - QString stylesheet("QLineEdit { "); + QString stylesheet("QLineEdit { background: %1; }"); if (m_basePasswordEdit && !passwordsEqual()) { - stylesheet.append("background: %1; "); - + bool isCorrect = true; if (m_basePasswordEdit->text().startsWith(text())) { stylesheet = stylesheet.arg(CorrectSoFarColor.name()); } else { stylesheet = stylesheet.arg(ErrorColor.name()); + isCorrect = false; } + m_correctAction->setVisible(isCorrect); + m_errorAction->setVisible(!isCorrect); + } else { + m_correctAction->setVisible(false); + m_errorAction->setVisible(false); } - stylesheet.append("}"); setStyleSheet(stylesheet); } diff --git a/src/gui/PasswordEdit.h b/src/gui/PasswordEdit.h index 29b33f0bd..b6e74ed00 100644 --- a/src/gui/PasswordEdit.h +++ b/src/gui/PasswordEdit.h @@ -19,6 +19,7 @@ #ifndef KEEPASSX_PASSWORDEDIT_H #define KEEPASSX_PASSWORDEDIT_H +#include #include #include @@ -47,6 +48,8 @@ private slots: private: bool passwordsEqual() const; + QPointer m_errorAction; + QPointer m_correctAction; QPointer m_basePasswordEdit; }; diff --git a/src/gui/PasswordGeneratorWidget.ui b/src/gui/PasswordGeneratorWidget.ui index ca970fbda..ff2d0582f 100644 --- a/src/gui/PasswordGeneratorWidget.ui +++ b/src/gui/PasswordGeneratorWidget.ui @@ -160,6 +160,9 @@ QProgressBar::chunk { + + Generated password + 999 @@ -167,6 +170,12 @@ QProgressBar::chunk { + + Toggle password visibiity + + + + true @@ -253,7 +262,13 @@ QProgressBar::chunk { Qt::StrongFocus - Upper Case Letters + Upper-case letters + + + Upper-case letters + + + A-Z @@ -278,7 +293,13 @@ QProgressBar::chunk { Qt::StrongFocus - Lower Case Letters + Lower-case letters + + + Lower-case letters + + + a-z @@ -305,6 +326,12 @@ QProgressBar::chunk { Numbers + + Numbers + + + + 0-9 @@ -331,7 +358,10 @@ QProgressBar::chunk { Qt::StrongFocus - Special Characters + Special characters + + + Special characters /*_& ... @@ -364,6 +394,9 @@ QProgressBar::chunk { Extended ASCII + + Extended ASCII + ExtendedASCII @@ -447,7 +480,10 @@ QProgressBar::chunk { Qt::StrongFocus - Upper Case Letters A to F + Upper-case letters + + + Upper-case letters A-Z @@ -472,7 +508,10 @@ QProgressBar::chunk { Qt::StrongFocus - Lower Case Letters A to F + Lower-case letters + + + Lower-case letters a-z @@ -531,6 +570,9 @@ QProgressBar::chunk { Braces + + Braces + {[( @@ -563,6 +605,9 @@ QProgressBar::chunk { Punctuation + + Punctuation + .,:; @@ -618,7 +663,10 @@ QProgressBar::chunk { Qt::StrongFocus - Math + Math Symbols + + + Math Symbols <*+!?= @@ -643,7 +691,10 @@ QProgressBar::chunk { Qt::StrongFocus - Dashes + Dashes and Slashes + + + Dashes and Slashes \_|-/ @@ -677,6 +728,9 @@ QProgressBar::chunk { Logograms + + Logograms + #$%&&@^`~ @@ -786,6 +840,9 @@ QProgressBar::chunk { Character set to exclude from generated password + + Excluded characters + true @@ -809,6 +866,9 @@ QProgressBar::chunk { Add non-hex letters to "do not include" list + + Hex Passwords + Hex @@ -881,6 +941,9 @@ QProgressBar::chunk { + + Password length + 1 @@ -903,6 +966,9 @@ QProgressBar::chunk { + + Password length + 1 @@ -1084,6 +1150,12 @@ QProgressBar::chunk { + + Regenerate password + + + + Regenerate @@ -1091,6 +1163,12 @@ QProgressBar::chunk { + + Copy password + + + + Copy @@ -1101,6 +1179,12 @@ QProgressBar::chunk { false + + Accept password + + + + Accept @@ -1136,6 +1220,7 @@ QProgressBar::chunk { editNewPassword togglePasswordButton + tabWidget sliderLength spinBoxLength checkBoxUpper @@ -1149,15 +1234,16 @@ QProgressBar::chunk { checkBoxPunctuation checkBoxMath checkBoxLogograms + checkBoxLowerAdv checkBoxBraces checkBoxQuotes checkBoxDashes checkBoxExtASCIIAdv editExcludedChars - buttonSimpleMode + buttonAddHex checkBoxExcludeAlike checkBoxEnsureEvery - tabWidget + buttonSimpleMode comboBoxWordList sliderWordCount spinBoxWordCount diff --git a/src/gui/TotpSetupDialog.ui b/src/gui/TotpSetupDialog.ui index 6f2af49f1..a12da0fa4 100644 --- a/src/gui/TotpSetupDialog.ui +++ b/src/gui/TotpSetupDialog.ui @@ -7,7 +7,7 @@ 0 0 249 - 248 + 278 @@ -30,7 +30,14 @@ - + + + Secret key in Base32 format + + + Secret key field + + @@ -121,6 +128,9 @@ + + Time step field + sec @@ -181,6 +191,16 @@ + + seedEdit + radioDefault + radioSteam + radioCustom + stepSpinBox + radio6Digits + radio7Digits + radio8Digits + diff --git a/src/gui/WelcomeWidget.ui b/src/gui/WelcomeWidget.ui index 3cc35c666..8b72df840 100644 --- a/src/gui/WelcomeWidget.ui +++ b/src/gui/WelcomeWidget.ui @@ -185,10 +185,21 @@ 110 + + Open a recent database + + + buttonNewDatabase + buttonOpenDatabase + buttonImportKeePass1 + buttonImportOpVault + buttonImportCSV + recentListWidget + diff --git a/src/gui/csvImport/CsvImportWidget.ui b/src/gui/csvImport/CsvImportWidget.ui index df0af79f1..648351021 100644 --- a/src/gui/csvImport/CsvImportWidget.ui +++ b/src/gui/csvImport/CsvImportWidget.ui @@ -143,6 +143,9 @@ false + + Codec + false @@ -184,6 +187,9 @@ false + + Text qualification + false @@ -225,6 +231,9 @@ false + + Field seperation + false @@ -266,6 +275,9 @@ false + + Comments start with + false @@ -308,7 +320,7 @@ - Number of headers line to discard + Number of header lines to discard Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -323,6 +335,9 @@ false + + Number of header lines to discard + @@ -417,6 +432,9 @@ false + + CSV import preview + true diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui index b9bbf433b..463f572d5 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui @@ -112,6 +112,9 @@ + + Stored browser keys + QAbstractItemView::NoEditTriggers @@ -133,6 +136,9 @@ + + Remove selected key + Remove diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.ui b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.ui index 1de060e9a..f8ba579dc 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.ui +++ b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.ui @@ -77,6 +77,9 @@ + + Change existing decryption time + Change @@ -89,6 +92,9 @@ + + Decryption time in seconds + 1 @@ -198,6 +204,9 @@ 0 + + Database format + @@ -240,6 +249,9 @@ 0 + + Encryption algorithm + AES: 256 Bit (default) @@ -267,6 +279,9 @@ 0 + + Key derivation function + @@ -292,6 +307,9 @@ 16777215 + + Transform rounds + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -349,6 +367,9 @@ 16777215 + + Memory usage + 1 @@ -378,6 +399,9 @@ 16777215 + + Parallelism + 1 diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.ui b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.ui index 00c1437bb..02f07952b 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.ui +++ b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.ui @@ -49,7 +49,11 @@ - + + + Database name field + + @@ -59,7 +63,11 @@ - + + + Database description field + + @@ -73,6 +81,9 @@ true + + Default username field + @@ -88,6 +99,9 @@ + + Maximum number of history items per entry + Max. history items: @@ -95,6 +109,9 @@ + + Maximum size of history per entry + Max. history size: @@ -102,6 +119,12 @@ + + Maximum size of history per entry + + + Maximum size of history per entry + MiB @@ -115,6 +138,12 @@ + + Maximum number of history items per entry + + + Maximum number of history items per entry + 2000000000 diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.ui b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.ui index b4ac30b9b..6878a5536 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.ui +++ b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.ui @@ -31,7 +31,11 @@ - + + + Database name field + + @@ -41,7 +45,11 @@ - + + + Database description field + + diff --git a/src/gui/entry/EditEntryWidgetAdvanced.ui b/src/gui/entry/EditEntryWidgetAdvanced.ui index 9556eee19..7b079b676 100644 --- a/src/gui/entry/EditEntryWidgetAdvanced.ui +++ b/src/gui/entry/EditEntryWidgetAdvanced.ui @@ -7,7 +7,7 @@ 0 0 532 - 364 + 374 @@ -32,6 +32,9 @@ 0 + + Attribute selection + QAbstractScrollArea::AdjustToContents @@ -55,6 +58,9 @@ 0 + + Attribute value + @@ -62,6 +68,9 @@ + + Add a new attribute + Add @@ -72,6 +81,9 @@ false + + Remove selected attribute + Remove @@ -82,6 +94,9 @@ false + + Edit attribute name + Edit Name @@ -105,6 +120,9 @@ true + + Toggle attribute protection + margin-left:50%;margin-right:50% @@ -121,6 +139,9 @@ false + + Show a protected attribute + Reveal @@ -177,6 +198,9 @@ 25 + + Foreground color selection + @@ -219,6 +243,9 @@ 25 + + Background color selection + @@ -264,6 +291,12 @@ addAttributeButton removeAttributeButton editAttributeButton + protectAttributeButton + revealAttributeButton + fgColorCheckBox + fgColorButton + bgColorCheckBox + bgColorButton diff --git a/src/gui/entry/EditEntryWidgetAutoType.ui b/src/gui/entry/EditEntryWidgetAutoType.ui index 81261394d..d987e8047 100644 --- a/src/gui/entry/EditEntryWidgetAutoType.ui +++ b/src/gui/entry/EditEntryWidgetAutoType.ui @@ -83,6 +83,9 @@ false + + Custom Auto-Type sequence + @@ -91,10 +94,10 @@ false - Open AutoType help webpage + Open Auto-Type help webpage - AutoType help button + Open Auto-Type help webpage @@ -113,6 +116,9 @@ + + Existing window associations + false @@ -159,6 +165,12 @@ 25 + + Add new window association + + + Add new window association + + @@ -181,6 +193,12 @@ 25 + + Remove selected window association + + + Remove selected window association + - @@ -200,7 +218,17 @@ - + + + You can use an asterisk (*) to match everything + + + Set the window association title + + + You can use an asterisk to match everything + + @@ -248,6 +276,9 @@ false + + Custom Auto-Type sequence for this window + diff --git a/src/gui/entry/EditEntryWidgetHistory.ui b/src/gui/entry/EditEntryWidgetHistory.ui index 8390f22fa..b85d3f0c1 100644 --- a/src/gui/entry/EditEntryWidgetHistory.ui +++ b/src/gui/entry/EditEntryWidgetHistory.ui @@ -25,6 +25,9 @@ + + Entry history selection + true @@ -43,6 +46,12 @@ false + + Show entry at selected history state + + + Show entry at selected history state + Show @@ -53,6 +62,12 @@ false + + Restore entry to selected history state + + + Restore entry to selected history state + Restore @@ -63,6 +78,12 @@ false + + Delete selected history state + + + Delete selected history state + Delete @@ -73,6 +94,12 @@ false + + Delete all history + + + Delete all history + Delete all diff --git a/src/gui/entry/EditEntryWidgetMain.ui b/src/gui/entry/EditEntryWidgetMain.ui index 5ed534dc2..f9078f2d1 100644 --- a/src/gui/entry/EditEntryWidgetMain.ui +++ b/src/gui/entry/EditEntryWidgetMain.ui @@ -2,6 +2,14 @@ EditEntryWidgetMain + + + 0 + 0 + 329 + 381 + + 0 @@ -22,10 +30,20 @@ - + + + Url field + + + + Download favicon for URL + + + Download favicon for URL + @@ -44,6 +62,9 @@ + + Repeat password field + QLineEdit::Password @@ -51,6 +72,9 @@ + + Toggle password generator + true @@ -62,6 +86,9 @@ + + Password field + QLineEdit::Password @@ -69,6 +96,9 @@ + + Toggle password visibility + true @@ -92,6 +122,9 @@ + + Toggle notes visible + Notes @@ -104,6 +137,9 @@ false + + Expiration field + true @@ -117,6 +153,9 @@ 0 + + Expiration presets + Presets @@ -138,6 +177,9 @@ 100 + + Notes field + @@ -161,13 +203,24 @@ - + + + Title field + + - + + + Username field + + + + Toggle expiration + Expires @@ -193,12 +246,15 @@ titleEdit usernameComboBox passwordEdit - passwordRepeatEdit togglePasswordButton + passwordRepeatEdit togglePasswordGeneratorButton urlEdit + fetchFaviconButton + expireCheck expireDatePicker expirePresets + notesEnabled notesEdit diff --git a/src/gui/entry/EditEntryWidgetSSHAgent.ui b/src/gui/entry/EditEntryWidgetSSHAgent.ui index 5957f6572..2e9d94b65 100644 --- a/src/gui/entry/EditEntryWidgetSSHAgent.ui +++ b/src/gui/entry/EditEntryWidgetSSHAgent.ui @@ -37,6 +37,9 @@ + + Remove key from agent after specified seconds + seconds @@ -165,6 +168,9 @@ + + Browser for key file + Browse... @@ -181,7 +187,14 @@ - + + + Qt::ClickFocus + + + External key file + + @@ -209,6 +222,9 @@ 0 + + Select attachment file + false @@ -270,6 +286,22 @@ + + addKeyToAgentCheckBox + removeKeyFromAgentCheckBox + requireUserConfirmationCheckBox + lifetimeCheckBox + lifetimeSpinBox + attachmentRadioButton + attachmentComboBox + externalFileRadioButton + browseButton + addToAgentButton + removeFromAgentButton + decryptButton + publicKeyEdit + copyToClipboardButton + diff --git a/src/gui/entry/EntryAttachmentsWidget.ui b/src/gui/entry/EntryAttachmentsWidget.ui index 60292309f..bd6e15538 100644 --- a/src/gui/entry/EntryAttachmentsWidget.ui +++ b/src/gui/entry/EntryAttachmentsWidget.ui @@ -2,6 +2,14 @@ EntryAttachmentsWidget + + + 0 + 0 + 337 + 289 + + Form @@ -19,7 +27,11 @@ 0 - + + + Attachments + + @@ -41,6 +53,9 @@ false + + Add new attachment + Add @@ -51,6 +66,9 @@ false + + Remove selected attachment + Remove @@ -61,6 +79,9 @@ false + + Open selected attachment + Open @@ -71,6 +92,9 @@ false + + Save selected attachment to disk + Save diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index cd7896b06..bff11e124 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -18,9 +18,11 @@ #include "EntryView.h" +#include #include #include #include +#include #include "core/FilePath.h" #include "gui/SortFilterHideProxyModel.h" @@ -56,6 +58,8 @@ EntryView::EntryView(QWidget* parent) connect(m_model, SIGNAL(passwordsHiddenChanged()), SIGNAL(viewStateChanged())); // clang-format on + new QShortcut(Qt::CTRL + Qt::Key_F10, this, SLOT(contextMenuShortcutPressed()), nullptr, Qt::WidgetShortcut); + m_headerMenu = new QMenu(this); m_headerMenu->setTitle(tr("Customize View")); m_headerMenu->addSection(tr("Customize View")); @@ -128,6 +132,14 @@ EntryView::EntryView(QWidget* parent) m_model->setPaperClipPixmap(filePath()->icon("actions", "paperclip").pixmap(16)); } +void EntryView::contextMenuShortcutPressed() +{ + auto index = currentIndex(); + if (hasFocus() && index.isValid()) { + emit customContextMenuRequested(visualRect(index).bottomLeft()); + } +} + void EntryView::keyPressEvent(QKeyEvent* event) { if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && currentIndex().isValid()) { @@ -140,15 +152,18 @@ void EntryView::keyPressEvent(QKeyEvent* event) int last = m_model->rowCount() - 1; if (last > 0) { + QAccessibleEvent accessibleEvent(this, QAccessible::PageChanged); if (event->key() == Qt::Key_Up && currentIndex().row() == 0) { QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(last, 0)); setCurrentEntry(m_model->entryFromIndex(index)); + QAccessible::updateAccessibility(&accessibleEvent); return; } if (event->key() == Qt::Key_Down && currentIndex().row() == last) { QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0)); setCurrentEntry(m_model->entryFromIndex(index)); + QAccessible::updateAccessibility(&accessibleEvent); return; } } diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index 09dfd8dde..53de7aff5 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -72,6 +72,7 @@ private slots: void fitColumnsToWindow(); void fitColumnsToContents(); void resetViewToDefaults(); + void contextMenuShortcutPressed(); private: void fillRemainingWidth(bool lastColumnOnly); diff --git a/src/gui/group/EditGroupWidgetMain.ui b/src/gui/group/EditGroupWidgetMain.ui index 20ce2f414..486e408b6 100644 --- a/src/gui/group/EditGroupWidgetMain.ui +++ b/src/gui/group/EditGroupWidgetMain.ui @@ -31,7 +31,11 @@ - + + + Name field + + @@ -54,23 +58,36 @@ 120 + + Notes field + + + Toggle expiration + Expires - + + + Auto-Type toggle for this and sub groups + + false + + Expiration field + true @@ -84,7 +101,11 @@ - + + + Search toggle for this and sub groups + + @@ -130,6 +151,12 @@ false + + Default auto-type sequence field + + + + diff --git a/src/gui/group/GroupView.cpp b/src/gui/group/GroupView.cpp index 77b5bff86..33c591696 100644 --- a/src/gui/group/GroupView.cpp +++ b/src/gui/group/GroupView.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "core/Database.h" #include "core/Group.h" @@ -42,6 +43,8 @@ GroupView::GroupView(Database* db, QWidget* parent) connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(emitGroupChanged())); // clang-format on + new QShortcut(Qt::CTRL + Qt::Key_F10, this, SLOT(contextMenuShortcutPressed()), nullptr, Qt::WidgetShortcut); + modelReset(); setDragEnabled(true); @@ -50,6 +53,14 @@ GroupView::GroupView(Database* db, QWidget* parent) setDefaultDropAction(Qt::MoveAction); } +void GroupView::contextMenuShortcutPressed() +{ + auto index = currentIndex(); + if (hasFocus() && index.isValid()) { + emit customContextMenuRequested(visualRect(index).bottomLeft()); + } +} + void GroupView::changeDatabase(const QSharedPointer& newDb) { m_model->changeDatabase(newDb.data()); diff --git a/src/gui/group/GroupView.h b/src/gui/group/GroupView.h index 76425c6c3..00b5a28c0 100644 --- a/src/gui/group/GroupView.h +++ b/src/gui/group/GroupView.h @@ -45,6 +45,7 @@ private slots: void emitGroupChanged(); void syncExpandedState(const QModelIndex& parent, int start, int end); void modelReset(); + void contextMenuShortcutPressed(); protected: void dragMoveEvent(QDragMoveEvent* event) override; diff --git a/src/gui/masterkey/KeyFileEditWidget.ui b/src/gui/masterkey/KeyFileEditWidget.ui index a267935b5..fd52e2e1f 100644 --- a/src/gui/masterkey/KeyFileEditWidget.ui +++ b/src/gui/masterkey/KeyFileEditWidget.ui @@ -31,6 +31,9 @@ 0 + + Key file selection + true @@ -38,13 +41,19 @@ + + Browse for key file + - Browse + Browse... + + Generate a new key file + Generate diff --git a/src/gui/masterkey/PasswordEditWidget.ui b/src/gui/masterkey/PasswordEditWidget.ui index e435e9901..d0a85eb59 100644 --- a/src/gui/masterkey/PasswordEditWidget.ui +++ b/src/gui/masterkey/PasswordEditWidget.ui @@ -34,6 +34,9 @@ + + Password field + QLineEdit::Password @@ -41,6 +44,9 @@ + + Toggle password visibility + true @@ -52,13 +58,20 @@ + + Repeat password field + QLineEdit::Password - + + + Toggle password generator + + diff --git a/src/gui/masterkey/YubiKeyEditWidget.ui b/src/gui/masterkey/YubiKeyEditWidget.ui index 08508739a..fa150084b 100644 --- a/src/gui/masterkey/YubiKeyEditWidget.ui +++ b/src/gui/masterkey/YubiKeyEditWidget.ui @@ -30,6 +30,9 @@ + + Refresh hardware tokens + Refresh @@ -43,6 +46,9 @@ 0 + + Hardware key slot selection + diff --git a/src/keeshare/SettingsWidgetKeeShare.ui b/src/keeshare/SettingsWidgetKeeShare.ui index 77a9e3eba..0840c9747 100644 --- a/src/keeshare/SettingsWidgetKeeShare.ui +++ b/src/keeshare/SettingsWidgetKeeShare.ui @@ -31,6 +31,12 @@ + + Allow KeeShare imports + + + Allow KeeShare imports + Allow import @@ -38,6 +44,12 @@ + + Allow KeeShare exports + + + Allow KeeShare exports + Allow export @@ -77,6 +89,9 @@ + + Key + true @@ -105,16 +120,26 @@ + + Certificate + true - + + + Signer name field + + + + Fingerprint + true @@ -137,6 +162,9 @@ + + Generate new certificate + Generate @@ -144,6 +172,9 @@ + + Import existing certificate + Import @@ -151,6 +182,9 @@ + + Export own certificate + Export @@ -169,6 +203,9 @@ + + Known shares + QAbstractItemView::NoEditTriggers @@ -215,6 +252,9 @@ + + Trust selected certificate + Trust @@ -222,6 +262,9 @@ + + Ask whether to trust the selected certificate every time + Ask @@ -229,6 +272,9 @@ + + Untrust selected certificate + Untrust @@ -236,6 +282,9 @@ + + Remove selected certificate + Remove diff --git a/src/keeshare/group/EditGroupWidgetKeeShare.ui b/src/keeshare/group/EditGroupWidgetKeeShare.ui index 30e34962f..b64195c64 100644 --- a/src/keeshare/group/EditGroupWidgetKeeShare.ui +++ b/src/keeshare/group/EditGroupWidgetKeeShare.ui @@ -39,7 +39,11 @@ - + + + Sharing mode field + + @@ -51,10 +55,17 @@ - + + + Path to share file field + + + + Browser for share file + ... @@ -73,6 +84,9 @@ + + Password field + QLineEdit::Password @@ -80,6 +94,9 @@ + + Toggle password visibility + true @@ -87,6 +104,9 @@ + + Toggle password generator + true @@ -99,6 +119,9 @@ + + Clear fields + Clear