diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index cbfda0636..e9535f973 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -219,18 +219,30 @@ You must restart the application to set the new language. Would you like to restart now? - - Reset Settings? - - - - Are you sure you want to reset all general and security settings to default? - - Select backup storage directory + + Confirm Reset + + + + Are you sure you want to reset all settings to default? + + + + Import KeePassXC Settings + + + + Failed to import settings from %1, not a valid settings file. + + + + Export KeePassXC Settings + + ApplicationSettingsWidgetGeneral @@ -306,22 +318,10 @@ Backup database file before saving - - Backup destination - - - - Specifies the database backup file location. Occurrences of "{DB_FILENAME}" are replaced with the filename of the saved database without extension. {TIME:<format>} is replaced with the backup time, see https://doc.qt.io/qt-5/qdatetime.html#toString. <format> defaults to format string "dd_MM_yyyy_hh-mm-ss". - - {DB_FILENAME}.old.kdbx - - Choose... - - Use alternative saving method (may solve problems with Dropbox, Google Drive, GVFS, etc.) @@ -501,6 +501,46 @@ number of days warning for password expiration + + Destination format: + + + + <html><head/><body><p><span style=" font-weight:600;">{DB_FILENAME}</span> is replaced with the filename of the saved database without extension</p><p><span style=" font-weight:600;">{TIME:&lt;format&gt;}</span> is replaced with the specified time format (default: dd_MM_yyyy_hh-mm-ss)</p><p>See the User Guide for more details</p></body></html> + + + + Choose folder... + + + + Show confirmation before moving entries to recycle bin + + + + Copy data on double clicking field in entry view + + + + Show toolbar + + + + Show the menu bar by pressing the Alt key + + + + Show menubar + + + + Import settings… + + + + Export settings… + + ApplicationSettingsWidgetSecurity @@ -566,18 +606,6 @@ Hide passwords in the entry preview panel - - Hide entry notes by default - - - - Move entries to recycle bin without confirmation - - - - Enable double click to copy the username/password entry columns - - Privacy @@ -594,6 +622,14 @@ Lock databases when switching user + + Lock Options + + + + Hide notes in the entry preview panel + + AutoType diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 871c26419..a38b307be 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -302,6 +302,45 @@ void Config::resetToDefaults() } } +bool Config::importSettings(const QString& fileName) +{ + // Ensure file is valid ini with values + QSettings settings(fileName, QSettings::IniFormat); + if (settings.status() != QSettings::NoError || settings.allKeys().isEmpty()) { + return false; + } + + // Only import valid roaming settings + auto isValidSetting = [](const QString& key) { + for (const auto& value : configStrings.values()) { + if (value.type == ConfigType::Roaming && value.name == key) { + return true; + } + } + return false; + }; + + // Clear existing settings and set valid items + m_settings->clear(); + for (const auto& key : settings.allKeys()) { + if (isValidSetting(key)) { + m_settings->setValue(key, settings.value(key)); + } + } + + sync(); + + return true; +} + +void Config::exportSettings(const QString& fileName) const +{ + QSettings settings(fileName, QSettings::IniFormat); + for (const auto& key : m_settings->allKeys()) { + settings.setValue(key, m_settings->value(key)); + } +} + /** * Map of configuration file settings that are either deprecated, or have * had their name changed to their new config enum values. diff --git a/src/core/Config.h b/src/core/Config.h index af24427a0..0f39beafb 100644 --- a/src/core/Config.h +++ b/src/core/Config.h @@ -217,6 +217,9 @@ public: void sync(); void resetToDefaults(); + bool importSettings(const QString& fileName); + void exportSettings(const QString& fileName) const; + QList getShortcuts() const; void setShortcuts(const QList& shortcuts); diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index cbe431043..27f27eb6d 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -117,6 +117,8 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent) connect(m_generalUi->systrayShowCheckBox, SIGNAL(toggled(bool)), SLOT(systrayToggled(bool))); connect(m_generalUi->rememberLastDatabasesCheckBox, SIGNAL(toggled(bool)), SLOT(rememberDatabasesToggled(bool))); connect(m_generalUi->resetSettingsButton, SIGNAL(clicked()), SLOT(resetSettings())); + connect(m_generalUi->importSettingsButton, SIGNAL(clicked()), SLOT(importSettings())); + connect(m_generalUi->exportSettingsButton, SIGNAL(clicked()), SLOT(exportSettings())); connect(m_generalUi->useAlternativeSaveCheckBox, SIGNAL(toggled(bool)), m_generalUi->alternativeSaveComboBox, SLOT(setEnabled(bool))); @@ -233,6 +235,10 @@ void ApplicationSettingsWidget::loadSettings() m_generalUi->autoTypeEntryURLMatchCheckBox->setChecked(config()->get(Config::AutoTypeEntryURLMatch).toBool()); m_generalUi->autoTypeHideExpiredEntryCheckBox->setChecked(config()->get(Config::AutoTypeHideExpiredEntry).toBool()); m_generalUi->faviconTimeoutSpinBox->setValue(config()->get(Config::FaviconDownloadTimeout).toInt()); + m_generalUi->ConfirmMoveEntryToRecycleBinCheckBox->setChecked( + !config()->get(Config::Security_NoConfirmMoveEntryToRecycleBin).toBool()); + m_generalUi->EnableCopyOnDoubleClickCheckBox->setChecked( + config()->get(Config::Security_EnableCopyOnDoubleClick).toBool()); m_generalUi->languageComboBox->clear(); QList> languages = Translator::availableLanguages(); @@ -244,6 +250,8 @@ void ApplicationSettingsWidget::loadSettings() m_generalUi->languageComboBox->setCurrentIndex(defaultIndex); } + m_generalUi->menubarShowCheckBox->setChecked(!config()->get(Config::GUI_HideMenubar).toBool()); + m_generalUi->toolbarShowCheckBox->setChecked(!config()->get(Config::GUI_HideToolbar).toBool()); m_generalUi->toolbarMovableCheckBox->setChecked(config()->get(Config::GUI_MovableToolbar).toBool()); m_generalUi->monospaceNotesCheckBox->setChecked(config()->get(Config::GUI_MonospaceNotes).toBool()); m_generalUi->colorPasswordsCheckBox->setChecked(config()->get(Config::GUI_ColorPasswords).toBool()); @@ -332,10 +340,6 @@ void ApplicationSettingsWidget::loadSettings() config()->get(Config::Security_HidePasswordPreviewPanel).toBool()); m_secUi->hideTotpCheckBox->setChecked(config()->get(Config::Security_HideTotpPreviewPanel).toBool()); m_secUi->hideNotesCheckBox->setChecked(config()->get(Config::Security_HideNotes).toBool()); - m_secUi->NoConfirmMoveEntryToRecycleBinCheckBox->setChecked( - config()->get(Config::Security_NoConfirmMoveEntryToRecycleBin).toBool()); - m_secUi->EnableCopyOnDoubleClickCheckBox->setChecked( - config()->get(Config::Security_EnableCopyOnDoubleClick).toBool()); m_secUi->quickUnlockCheckBox->setEnabled(getQuickUnlock()->isAvailable()); m_secUi->quickUnlockCheckBox->setChecked(config()->get(Config::Security_QuickUnlock).toBool()); @@ -386,6 +390,9 @@ void ApplicationSettingsWidget::saveSettings() config()->set(Config::AutoTypeEntryURLMatch, m_generalUi->autoTypeEntryURLMatchCheckBox->isChecked()); config()->set(Config::AutoTypeHideExpiredEntry, m_generalUi->autoTypeHideExpiredEntryCheckBox->isChecked()); config()->set(Config::FaviconDownloadTimeout, m_generalUi->faviconTimeoutSpinBox->value()); + config()->set(Config::Security_NoConfirmMoveEntryToRecycleBin, + !m_generalUi->ConfirmMoveEntryToRecycleBinCheckBox->isChecked()); + config()->set(Config::Security_EnableCopyOnDoubleClick, m_generalUi->EnableCopyOnDoubleClickCheckBox->isChecked()); auto language = m_generalUi->languageComboBox->currentData().toString(); if (config()->get(Config::GUI_Language) != language) { @@ -396,6 +403,8 @@ void ApplicationSettingsWidget::saveSettings() } config()->set(Config::GUI_Language, language); + config()->set(Config::GUI_HideMenubar, !m_generalUi->menubarShowCheckBox->isChecked()); + config()->set(Config::GUI_HideToolbar, !m_generalUi->toolbarShowCheckBox->isChecked()); config()->set(Config::GUI_MovableToolbar, m_generalUi->toolbarMovableCheckBox->isChecked()); config()->set(Config::GUI_MonospaceNotes, m_generalUi->monospaceNotesCheckBox->isChecked()); config()->set(Config::GUI_ColorPasswords, m_generalUi->colorPasswordsCheckBox->isChecked()); @@ -446,9 +455,6 @@ void ApplicationSettingsWidget::saveSettings() config()->set(Config::Security_HidePasswordPreviewPanel, m_secUi->passwordPreviewCleartextCheckBox->isChecked()); config()->set(Config::Security_HideTotpPreviewPanel, m_secUi->hideTotpCheckBox->isChecked()); config()->set(Config::Security_HideNotes, m_secUi->hideNotesCheckBox->isChecked()); - config()->set(Config::Security_NoConfirmMoveEntryToRecycleBin, - m_secUi->NoConfirmMoveEntryToRecycleBinCheckBox->isChecked()); - config()->set(Config::Security_EnableCopyOnDoubleClick, m_secUi->EnableCopyOnDoubleClickCheckBox->isChecked()); if (m_secUi->quickUnlockCheckBox->isEnabled()) { config()->set(Config::Security_QuickUnlock, m_secUi->quickUnlockCheckBox->isChecked()); @@ -476,8 +482,8 @@ 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?"), + tr("Confirm Reset"), + tr("Are you sure you want to reset all settings to default?"), MessageBox::Reset | MessageBox::Cancel, MessageBox::Cancel); if (ans == MessageBox::Cancel) { @@ -512,6 +518,33 @@ void ApplicationSettingsWidget::resetSettings() emit settingsReset(); } +void ApplicationSettingsWidget::importSettings() +{ + auto file = fileDialog()->getOpenFileName(this, tr("Import KeePassXC Settings"), {}, "*.ini"); + if (file.isEmpty()) { + return; + } + + if (!config()->importSettings(file)) { + showMessage(tr("Failed to import settings from %1, not a valid settings file.").arg(file), + MessageWidget::Error); + return; + } + + loadSettings(); + emit settingsReset(); +} + +void ApplicationSettingsWidget::exportSettings() +{ + auto file = fileDialog()->getSaveFileName(this, tr("Export KeePassXC Settings"), {}, "*.ini"); + if (file.isEmpty()) { + return; + } + + config()->exportSettings(file); +} + 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 5c6572173..7e43196b1 100644 --- a/src/gui/ApplicationSettingsWidget.h +++ b/src/gui/ApplicationSettingsWidget.h @@ -54,6 +54,8 @@ signals: private slots: void saveSettings(); void resetSettings(); + void importSettings(); + void exportSettings(); void reject(); void autoSaveToggled(bool checked); void hideWindowOnCopyCheckBoxToggled(bool checked); diff --git a/src/gui/ApplicationSettingsWidgetGeneral.ui b/src/gui/ApplicationSettingsWidgetGeneral.ui index 1af95e5ea..6e74205ff 100644 --- a/src/gui/ApplicationSettingsWidgetGeneral.ui +++ b/src/gui/ApplicationSettingsWidgetGeneral.ui @@ -58,8 +58,8 @@ 0 0 - 566 - 975 + 568 + 1153 @@ -268,31 +268,25 @@ - - - QLayout::SetMaximumSize - + 6 - + + QLayout::SetMaximumSize + + On database unlock, show entries that will expire within - + true - - - 0 - 0 - - Qt::StrongFocus @@ -313,7 +307,7 @@ - + Qt::Horizontal @@ -374,6 +368,25 @@ + + 0 + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 30 + 20 + + + + @@ -383,41 +396,70 @@ - Backup destination + Destination format: backupFilePath + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 6 + 20 + + + + false - Specifies the database backup file location. Occurrences of "{DB_FILENAME}" are replaced with the filename of the saved database without extension. {TIME:<format>} is replaced with the backup time, see https://doc.qt.io/qt-5/qdatetime.html#toString. <format> defaults to format string "dd_MM_yyyy_hh-mm-ss". + <html><head/><body><p><span style=" font-weight:600;">{DB_FILENAME}</span> is replaced with the filename of the saved database without extension</p><p><span style=" font-weight:600;">{TIME:&lt;format&gt;}</span> is replaced with the specified time format (default: dd_MM_yyyy_hh-mm-ss)</p><p>See the User Guide for more details</p></body></html> {DB_FILENAME}.old.kdbx + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 6 + 20 + + + + false - Choose... + Choose folder... - - - @@ -487,6 +529,20 @@ Entry Management + + + + Show confirmation before moving entries to recycle bin + + + + + + + Copy data on double clicking field in entry view + + + @@ -643,6 +699,35 @@ 10 + + + + true + + + + 0 + 0 + + + + Movable toolbar + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -662,19 +747,28 @@ - - + + true - + 0 0 + + margin-right: 5px + - Movable toolbar + Toolbar button style: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + toolButtonStyleComboBox @@ -720,46 +814,25 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - true - - - - 0 - 0 - - - - margin-right: 5px - - - Toolbar button style: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - toolButtonStyleComboBox - - - + + + + Show toolbar + + + + + + + Show the menu bar by pressing the Alt key + + + Show menubar + + + @@ -941,7 +1014,7 @@ - 0 + 6 QLayout::SetMaximumSize @@ -954,21 +1027,32 @@ - + Qt::Horizontal - - QSizePolicy::Expanding - - 50 + 40 20 + + + + Import settings… + + + + + + + Export settings… + + + @@ -1265,6 +1349,8 @@ backupFilePathPicker useAlternativeSaveCheckBox alternativeSaveComboBox + ConfirmMoveEntryToRecycleBinCheckBox + EnableCopyOnDoubleClickCheckBox useGroupIconOnEntryCreationCheckBox minimizeOnOpenUrlCheckBox hideWindowOnCopyCheckBox @@ -1274,6 +1360,8 @@ languageComboBox toolButtonStyleComboBox toolbarMovableCheckBox + toolbarShowCheckBox + menubarShowCheckBox colorPasswordsCheckBox monospaceNotesCheckBox minimizeOnCloseCheckBox @@ -1281,6 +1369,8 @@ trayIconAppearance systrayMinimizeToTrayCheckBox resetSettingsButton + importSettingsButton + exportSettingsButton autoTypeEntryTitleMatchCheckBox autoTypeEntryURLMatchCheckBox autoTypeAskCheckBox diff --git a/src/gui/ApplicationSettingsWidgetSecurity.ui b/src/gui/ApplicationSettingsWidgetSecurity.ui index 84f1083ac..c4cb0e01f 100644 --- a/src/gui/ApplicationSettingsWidgetSecurity.ui +++ b/src/gui/ApplicationSettingsWidgetSecurity.ui @@ -7,7 +7,7 @@ 0 0 364 - 493 + 505 @@ -31,7 +31,7 @@ Timeouts - + @@ -160,11 +160,11 @@ - + - Convenience + Lock Options - + @@ -179,13 +179,6 @@ - - - - Lock databases after minimizing the window - - - @@ -194,12 +187,21 @@ - + - Hide passwords when editing them + Lock databases after minimizing the window + + + + + + + Convenience + + @@ -207,6 +209,13 @@ + + + + Hide passwords when editing them + + + @@ -224,21 +233,7 @@ - Hide entry notes by default - - - - - - - Move entries to recycle bin without confirmation - - - - - - - Enable double click to copy the username/password entry columns + Hide notes in the entry preview panel @@ -284,19 +279,17 @@ clearClipboardSpinBox lockDatabaseIdleCheckBox lockDatabaseIdleSpinBox - lockDatabasesOnUserSwitchCheckBox clearSearchCheckBox clearSearchSpinBox quickUnlockCheckBox lockDatabaseOnScreenLockCheckBox + lockDatabasesOnUserSwitchCheckBox lockDatabaseMinimizeCheckBox - passwordsHiddenCheckBox passwordShowDotsCheckBox + passwordsHiddenCheckBox passwordPreviewCleartextCheckBox hideTotpCheckBox hideNotesCheckBox - NoConfirmMoveEntryToRecycleBinCheckBox - EnableCopyOnDoubleClickCheckBox fallbackToSearch diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 9e38dcda9..7f223a3a4 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1704,6 +1704,8 @@ void MainWindow::applySettingsChanges() m_inactivityTimer->deactivate(); } + m_ui->actionShowToolbar->setChecked(!config()->get(Config::GUI_HideToolbar).toBool()); + m_ui->actionShowMenubar->setChecked(!config()->get(Config::GUI_HideMenubar).toBool()); m_ui->menubar->setHidden(config()->get(Config::GUI_HideMenubar).toBool()); m_ui->toolBar->setHidden(config()->get(Config::GUI_HideToolbar).toBool()); auto movable = config()->get(Config::GUI_MovableToolbar).toBool();