From 592d553ff82f484949371ce4d718e7a85b4af7df Mon Sep 17 00:00:00 2001 From: Juzu-O Date: Sun, 2 Nov 2025 19:32:11 +0200 Subject: [PATCH] Add URL double-click action option to Settings (#12322) * Closes #4717 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: juzu-o <3142026+juzu-o@users.noreply.github.com> Co-authored-by: Jonathan White --- share/translations/keepassxc_en.ts | 24 ++++++-- src/core/Config.cpp | 10 ++++ src/core/Config.h | 1 + src/gui/ApplicationSettingsWidget.cpp | 4 +- src/gui/ApplicationSettingsWidgetGeneral.ui | 63 ++++++++++++++++++--- src/gui/DatabaseWidget.cpp | 19 +++++-- tests/TestConfig.cpp | 37 ++++++++++++ tests/TestConfig.h | 1 + 8 files changed, 139 insertions(+), 20 deletions(-) diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index caa33e301..ffc840a4f 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -580,10 +580,6 @@ Export settings… - - Open browser on double clicking URL field in entry view - - Font size: @@ -596,6 +592,26 @@ Skip confirmation for main window Auto-Type actions + + Double-click action for URL: + + + + Double-click action for URL field + + + + Edit entry + + + + Open entry URL in browser + + + + Copy entry URL to clipboard + + Auto-generate password for new entries diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 16a54fbcd..3f2636eff 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -67,6 +67,7 @@ static const QHash configStrings = { {Config::SearchLimitGroup,{QS("SearchLimitGroup"), Roaming, false}}, {Config::MinimizeOnOpenUrl,{QS("MinimizeOnOpenUrl"), Roaming, false}}, {Config::OpenURLOnDoubleClick, {QS("OpenURLOnDoubleClick"), Roaming, true}}, + {Config::URLDoubleClickAction, {QS("URLDoubleClickAction"), Roaming, 0}}, {Config::HideWindowOnCopy,{QS("HideWindowOnCopy"), Roaming, false}}, {Config::MinimizeOnCopy,{QS("MinimizeOnCopy"), Roaming, true}}, {Config::AutoGeneratePasswordForNewEntries,{QS("AutoGeneratePasswordForNewEntries"), Roaming, false}}, @@ -492,6 +493,15 @@ void Config::migrate() remove(GUI_ListViewState); } + // Migrate from boolean OpenURLOnDoubleClick to enum URLDoubleClickAction + if (m_settings->contains(configStrings[OpenURLOnDoubleClick].name) + && !m_settings->contains(configStrings[URLDoubleClickAction].name)) { + bool openUrlOnDoubleClick = get(OpenURLOnDoubleClick).toBool(); + // Convert: true (open browser) -> 0, false (edit entry) -> 2 + set(URLDoubleClickAction, openUrlOnDoubleClick ? 0 : 2); + // Keep the old setting for now for compatibility + } + m_settings->setValue("ConfigVersion", CONFIG_VERSION); sync(); } diff --git a/src/core/Config.h b/src/core/Config.h index d1809e415..8f54f9c01 100644 --- a/src/core/Config.h +++ b/src/core/Config.h @@ -50,6 +50,7 @@ public: SearchLimitGroup, MinimizeOnOpenUrl, OpenURLOnDoubleClick, + URLDoubleClickAction, HideWindowOnCopy, MinimizeOnCopy, MinimizeAfterUnlock, diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index cb2ee3af1..9f24bbc85 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -211,7 +211,7 @@ void ApplicationSettingsWidget::loadSettings() m_generalUi->autoReloadOnChangeCheckBox->setChecked(config()->get(Config::AutoReloadOnChange).toBool()); m_generalUi->minimizeAfterUnlockCheckBox->setChecked(config()->get(Config::MinimizeAfterUnlock).toBool()); m_generalUi->minimizeOnOpenUrlCheckBox->setChecked(config()->get(Config::MinimizeOnOpenUrl).toBool()); - m_generalUi->openUrlOnDoubleClick->setChecked(config()->get(Config::OpenURLOnDoubleClick).toBool()); + m_generalUi->urlDoubleClickComboBox->setCurrentIndex(config()->get(Config::URLDoubleClickAction).toInt()); m_generalUi->hideWindowOnCopyCheckBox->setChecked(config()->get(Config::HideWindowOnCopy).toBool()); hideWindowOnCopyCheckBoxToggled(m_generalUi->hideWindowOnCopyCheckBox->isChecked()); m_generalUi->minimizeOnCopyRadioButton->setChecked(config()->get(Config::MinimizeOnCopy).toBool()); @@ -389,7 +389,7 @@ void ApplicationSettingsWidget::saveSettings() config()->set(Config::AutoReloadOnChange, m_generalUi->autoReloadOnChangeCheckBox->isChecked()); config()->set(Config::MinimizeAfterUnlock, m_generalUi->minimizeAfterUnlockCheckBox->isChecked()); config()->set(Config::MinimizeOnOpenUrl, m_generalUi->minimizeOnOpenUrlCheckBox->isChecked()); - config()->set(Config::OpenURLOnDoubleClick, m_generalUi->openUrlOnDoubleClick->isChecked()); + config()->set(Config::URLDoubleClickAction, m_generalUi->urlDoubleClickComboBox->currentIndex()); config()->set(Config::HideWindowOnCopy, m_generalUi->hideWindowOnCopyCheckBox->isChecked()); config()->set(Config::MinimizeOnCopy, m_generalUi->minimizeOnCopyRadioButton->isChecked()); config()->set(Config::DropToBackgroundOnCopy, m_generalUi->dropToBackgroundOnCopyRadioButton->isChecked()); diff --git a/src/gui/ApplicationSettingsWidgetGeneral.ui b/src/gui/ApplicationSettingsWidgetGeneral.ui index 0e297e0ec..42ce4acc1 100644 --- a/src/gui/ApplicationSettingsWidgetGeneral.ui +++ b/src/gui/ApplicationSettingsWidgetGeneral.ui @@ -554,14 +554,59 @@ - - - Open browser on double clicking URL field in entry view - - - true - - + + + + + Double-click action for URL: + + + urlDoubleClickComboBox + + + + + + + Qt::StrongFocus + + + Double-click action for URL field + + + QComboBox::AdjustToContents + + + + Open entry URL in browser + + + + + Copy entry URL to clipboard + + + + + Edit entry + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + @@ -1439,7 +1484,7 @@ alternativeSaveComboBox ConfirmMoveEntryToRecycleBinCheckBox EnableCopyOnDoubleClickCheckBox - openUrlOnDoubleClick + urlDoubleClickComboBox useGroupIconOnEntryCreationCheckBox minimizeOnOpenUrlCheckBox hideWindowOnCopyCheckBox diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 925c890b9..2afde49fe 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1583,12 +1583,21 @@ void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::Mod // case EntryModel::Attachments: // break; case EntryModel::Url: - if (!entry->url().isEmpty() && config()->get(Config::OpenURLOnDoubleClick).toBool()) { - openUrlForEntry(entry); - break; + if (!entry->url().isEmpty()) { + switch (config()->get(Config::URLDoubleClickAction).toInt()) { + case 2: // Edit entry + switchToEntryEdit(entry); + break; + case 1: // Copy entry URL to clipboard + setClipboardTextAndMinimize(entry->resolveMultiplePlaceholders(entry->url())); + break; + case 0: // Open entry URL in browser (default) + default: + openUrlForEntry(entry); + break; + } } - // Note, order matters here. We want to fall into the default case. - [[fallthrough]]; + break; default: switchToEntryEdit(entry); } diff --git a/tests/TestConfig.cpp b/tests/TestConfig.cpp index b83a905d0..718a4d316 100644 --- a/tests/TestConfig.cpp +++ b/tests/TestConfig.cpp @@ -17,6 +17,7 @@ #include "TestConfig.h" +#include #include #include "config-keepassx-tests.h" @@ -40,3 +41,39 @@ void TestConfig::testUpgrade() tempFile.remove(); } + +void TestConfig::testURLDoubleClickMigration() +{ + // Test migration from OpenURLOnDoubleClick to URLDoubleClickAction + TemporaryFile tempFile; + tempFile.open(); + + // Create a config with old setting = true (open browser) + QSettings oldConfig(tempFile.fileName(), QSettings::IniFormat); + oldConfig.setValue("OpenURLOnDoubleClick", true); + oldConfig.sync(); + tempFile.close(); + + Config::createConfigFromFile(tempFile.fileName()); + + // Should migrate to URLDoubleClickAction = 0 (open browser) + QCOMPARE(config()->get(Config::URLDoubleClickAction).toInt(), 0); + + tempFile.remove(); + + // Test migration from OpenURLOnDoubleClick = false (edit entry) + TemporaryFile tempFile2; + tempFile2.open(); + + QSettings oldConfig2(tempFile2.fileName(), QSettings::IniFormat); + oldConfig2.setValue("OpenURLOnDoubleClick", false); + oldConfig2.sync(); + tempFile2.close(); + + Config::createConfigFromFile(tempFile2.fileName()); + + // Should migrate to URLDoubleClickAction = 2 (edit entry) + QCOMPARE(config()->get(Config::URLDoubleClickAction).toInt(), 2); + + tempFile2.remove(); +} diff --git a/tests/TestConfig.h b/tests/TestConfig.h index d5852acdd..2c48f5202 100644 --- a/tests/TestConfig.h +++ b/tests/TestConfig.h @@ -25,6 +25,7 @@ class TestConfig : public QObject Q_OBJECT private slots: void testUpgrade(); + void testURLDoubleClickMigration(); }; #endif // KEEPASSX_TESTCONFIG_H