mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-12-04 15:39:34 +01:00
Compare commits
11 Commits
f927c4c41a
...
592d553ff8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
592d553ff8 | ||
|
|
a709f14cf3 | ||
|
|
9031cb530e | ||
|
|
ebf0676661 | ||
|
|
6130a64be5 | ||
|
|
9814037fd3 | ||
|
|
8c8ae49240 | ||
|
|
1e370b8ab8 | ||
|
|
cd9bb483fe | ||
|
|
2cc2c905b5 | ||
|
|
d9ccf767d0 |
@@ -32,7 +32,9 @@ To configure Auto-Type sequences for your entries, perform the following steps:
|
|||||||
|
|
||||||
1. Navigate to the entries list and open the desired entry for editing. Click the _Auto-Type_ item from the left-hand menu bar *(1)*. Press the kbd:[+] button *(2)* to add a new sequence entry. Select the desired window using the drop-down menu, or simply type a window title in the box *(3)*.
|
1. Navigate to the entries list and open the desired entry for editing. Click the _Auto-Type_ item from the left-hand menu bar *(1)*. Press the kbd:[+] button *(2)* to add a new sequence entry. Select the desired window using the drop-down menu, or simply type a window title in the box *(3)*.
|
||||||
+
|
+
|
||||||
TIP: You can use an asterisk (`\*`) to match any value (e.g., when a window title contains a dynamic filename or website name). Set the window title to `*` to match all windows. Leave the window title blank to offer additional default Auto-Type sequences, such as custom attributes.
|
TIP: You can use an asterisk (`\*`) as a wildcard (e.g., when a window title contains a dynamic file or website name). Set the window title to `*` to match all windows. Leave the window title blank to offer additional sequences for every matching window. This is useful for typing individual custom attributes, for example.
|
||||||
|
+
|
||||||
|
TIP: To use a standard regular expression for window title matching, the window title must start and end with two forward slashes (e.g., `//^Secure Login - .*$//`).
|
||||||
+
|
+
|
||||||
.Auto-Type entry sequences
|
.Auto-Type entry sequences
|
||||||
image::autotype_entry_sequences.png[]
|
image::autotype_entry_sequences.png[]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,7 @@ Exec=keepassxc %f
|
|||||||
TryExec=keepassxc
|
TryExec=keepassxc
|
||||||
Icon=@APP_ICON_NAME@
|
Icon=@APP_ICON_NAME@
|
||||||
StartupWMClass=keepassxc
|
StartupWMClass=keepassxc
|
||||||
StartupNotify=true
|
StartupNotify=false
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
Version=1.5
|
Version=1.5
|
||||||
|
|||||||
@@ -157,6 +157,25 @@
|
|||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>AppKit</name>
|
||||||
|
<message>
|
||||||
|
<source>Window</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Minimize</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Zoom</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Bring All to Front</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ApplicationSettingsWidget</name>
|
<name>ApplicationSettingsWidget</name>
|
||||||
<message>
|
<message>
|
||||||
@@ -561,10 +580,6 @@
|
|||||||
<source>Export settings…</source>
|
<source>Export settings…</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Open browser on double clicking URL field in entry view</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Font size:</source>
|
<source>Font size:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
@@ -577,6 +592,26 @@
|
|||||||
<source>Skip confirmation for main window Auto-Type actions</source>
|
<source>Skip confirmation for main window Auto-Type actions</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Double-click action for URL:</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Double-click action for URL field</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit entry</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open entry URL in browser</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy entry URL to clipboard</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Auto-generate password for new entries</source>
|
<source>Auto-generate password for new entries</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
|
|||||||
@@ -372,16 +372,30 @@ bool NativeMessageInstaller::createNativeMessageFile(SupportedBrowsers browser)
|
|||||||
|
|
||||||
QFile scriptFile(path);
|
QFile scriptFile(path);
|
||||||
if (!scriptFile.open(QIODevice::WriteOnly)) {
|
if (!scriptFile.open(QIODevice::WriteOnly)) {
|
||||||
qWarning() << "Browser Plugin: Failed to open native message file for writing at " << scriptFile.fileName();
|
if (!scriptFile.open(QIODevice::ReadOnly)) {
|
||||||
qWarning() << scriptFile.errorString();
|
qWarning() << "Browser Plugin: Failed to open native message file at " << scriptFile.fileName();
|
||||||
return false;
|
qWarning() << scriptFile.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We failed to write to `scriptFile`, but we can read it, so we assume that it's a read-only file.
|
||||||
|
// Consider success if the read-only file already contains the content we would have written.
|
||||||
|
QJsonDocument expectedDoc(constructFile(browser));
|
||||||
|
QJsonDocument actualDoc = QJsonDocument::fromJson(scriptFile.readAll());
|
||||||
|
|
||||||
|
if (expectedDoc != actualDoc) {
|
||||||
|
qWarning() << "Browser Plugin: Unexpected (read-only) native message file at " << scriptFile.fileName();
|
||||||
|
qWarning() << "Expected contents: " << expectedDoc;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QJsonDocument doc(constructFile(browser));
|
||||||
|
if (scriptFile.write(doc.toJson()) < 0) {
|
||||||
|
qWarning() << "Browser Plugin: Failed to write native message file at " << scriptFile.fileName();
|
||||||
|
qWarning() << scriptFile.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonDocument doc(constructFile(browser));
|
|
||||||
if (scriptFile.write(doc.toJson()) < 0) {
|
|
||||||
qWarning() << "Browser Plugin: Failed to write native message file at " << scriptFile.fileName();
|
|
||||||
qWarning() << scriptFile.errorString();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,9 @@ namespace Utils
|
|||||||
SetConsoleMode(hIn, mode);
|
SetConsoleMode(hIn, mode);
|
||||||
#else
|
#else
|
||||||
struct termios t;
|
struct termios t;
|
||||||
tcgetattr(STDIN_FILENO, &t);
|
if (tcgetattr(STDIN_FILENO, &t) < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
t.c_lflag |= ECHO;
|
t.c_lflag |= ECHO;
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
|||||||
{Config::SearchLimitGroup,{QS("SearchLimitGroup"), Roaming, false}},
|
{Config::SearchLimitGroup,{QS("SearchLimitGroup"), Roaming, false}},
|
||||||
{Config::MinimizeOnOpenUrl,{QS("MinimizeOnOpenUrl"), Roaming, false}},
|
{Config::MinimizeOnOpenUrl,{QS("MinimizeOnOpenUrl"), Roaming, false}},
|
||||||
{Config::OpenURLOnDoubleClick, {QS("OpenURLOnDoubleClick"), Roaming, true}},
|
{Config::OpenURLOnDoubleClick, {QS("OpenURLOnDoubleClick"), Roaming, true}},
|
||||||
|
{Config::URLDoubleClickAction, {QS("URLDoubleClickAction"), Roaming, 0}},
|
||||||
{Config::HideWindowOnCopy,{QS("HideWindowOnCopy"), Roaming, false}},
|
{Config::HideWindowOnCopy,{QS("HideWindowOnCopy"), Roaming, false}},
|
||||||
{Config::MinimizeOnCopy,{QS("MinimizeOnCopy"), Roaming, true}},
|
{Config::MinimizeOnCopy,{QS("MinimizeOnCopy"), Roaming, true}},
|
||||||
{Config::AutoGeneratePasswordForNewEntries,{QS("AutoGeneratePasswordForNewEntries"), Roaming, false}},
|
{Config::AutoGeneratePasswordForNewEntries,{QS("AutoGeneratePasswordForNewEntries"), Roaming, false}},
|
||||||
@@ -118,6 +119,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
|||||||
{Config::GUI_CheckForUpdates, {QS("GUI/CheckForUpdates"), Roaming, true}},
|
{Config::GUI_CheckForUpdates, {QS("GUI/CheckForUpdates"), Roaming, true}},
|
||||||
{Config::GUI_CheckForUpdatesNextCheck, {QS("GUI/CheckForUpdatesNextCheck"), Local, 0}},
|
{Config::GUI_CheckForUpdatesNextCheck, {QS("GUI/CheckForUpdatesNextCheck"), Local, 0}},
|
||||||
{Config::GUI_CheckForUpdatesIncludeBetas, {QS("GUI/CheckForUpdatesIncludeBetas"), Roaming, false}},
|
{Config::GUI_CheckForUpdatesIncludeBetas, {QS("GUI/CheckForUpdatesIncludeBetas"), Roaming, false}},
|
||||||
|
{Config::GUI_SearchWaitForEnter, {QS("GUI/SearchWaitForEnter"), Roaming, false}},
|
||||||
{Config::GUI_ShowExpiredEntriesOnDatabaseUnlock, {QS("GUI/ShowExpiredEntriesOnDatabaseUnlock"), Roaming, true}},
|
{Config::GUI_ShowExpiredEntriesOnDatabaseUnlock, {QS("GUI/ShowExpiredEntriesOnDatabaseUnlock"), Roaming, true}},
|
||||||
{Config::GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays, {QS("GUI/ShowExpiredEntriesOnDatabaseUnlockOffsetDays"), Roaming, 3}},
|
{Config::GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays, {QS("GUI/ShowExpiredEntriesOnDatabaseUnlockOffsetDays"), Roaming, 3}},
|
||||||
{Config::GUI_FontSizeOffset, {QS("GUI/FontSizeOffset"), Local, 0}},
|
{Config::GUI_FontSizeOffset, {QS("GUI/FontSizeOffset"), Local, 0}},
|
||||||
@@ -491,6 +493,15 @@ void Config::migrate()
|
|||||||
remove(GUI_ListViewState);
|
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);
|
m_settings->setValue("ConfigVersion", CONFIG_VERSION);
|
||||||
sync();
|
sync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ public:
|
|||||||
SearchLimitGroup,
|
SearchLimitGroup,
|
||||||
MinimizeOnOpenUrl,
|
MinimizeOnOpenUrl,
|
||||||
OpenURLOnDoubleClick,
|
OpenURLOnDoubleClick,
|
||||||
|
URLDoubleClickAction,
|
||||||
HideWindowOnCopy,
|
HideWindowOnCopy,
|
||||||
MinimizeOnCopy,
|
MinimizeOnCopy,
|
||||||
MinimizeAfterUnlock,
|
MinimizeAfterUnlock,
|
||||||
@@ -99,7 +100,7 @@ public:
|
|||||||
GUI_CompactMode,
|
GUI_CompactMode,
|
||||||
GUI_CheckForUpdates,
|
GUI_CheckForUpdates,
|
||||||
GUI_CheckForUpdatesIncludeBetas,
|
GUI_CheckForUpdatesIncludeBetas,
|
||||||
SearchWaitForEnter,
|
GUI_SearchWaitForEnter,
|
||||||
GUI_ShowExpiredEntriesOnDatabaseUnlock,
|
GUI_ShowExpiredEntriesOnDatabaseUnlock,
|
||||||
GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays,
|
GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays,
|
||||||
GUI_FontSizeOffset,
|
GUI_FontSizeOffset,
|
||||||
|
|||||||
@@ -334,12 +334,15 @@ QList<QString> Entry::autoTypeSequences(const QString& windowTitle) const
|
|||||||
};
|
};
|
||||||
|
|
||||||
QList<QString> sequenceList;
|
QList<QString> sequenceList;
|
||||||
|
QList<QString> emptyWindowSequences;
|
||||||
|
|
||||||
// Add window association matches
|
// Add window association matches
|
||||||
const auto assocList = autoTypeAssociations()->getAll();
|
const auto assocList = autoTypeAssociations()->getAll();
|
||||||
for (const auto& assoc : assocList) {
|
for (const auto& assoc : assocList) {
|
||||||
auto window = resolveMultiplePlaceholders(assoc.window);
|
auto window = resolveMultiplePlaceholders(assoc.window);
|
||||||
if (!assoc.window.isEmpty() && windowMatches(window)) {
|
if (assoc.window.isEmpty()) {
|
||||||
|
emptyWindowSequences << assoc.sequence;
|
||||||
|
} else if (windowMatches(window)) {
|
||||||
if (!assoc.sequence.isEmpty()) {
|
if (!assoc.sequence.isEmpty()) {
|
||||||
sequenceList << assoc.sequence;
|
sequenceList << assoc.sequence;
|
||||||
} else {
|
} else {
|
||||||
@@ -358,6 +361,11 @@ QList<QString> Entry::autoTypeSequences(const QString& windowTitle) const
|
|||||||
sequenceList << effectiveAutoTypeSequence();
|
sequenceList << effectiveAutoTypeSequence();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If any associations were made, include the empty window associations
|
||||||
|
if (!sequenceList.isEmpty()) {
|
||||||
|
sequenceList.append(emptyWindowSequences);
|
||||||
|
}
|
||||||
|
|
||||||
return sequenceList;
|
return sequenceList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -426,6 +426,29 @@ namespace Tools
|
|||||||
return filename.trimmed();
|
return filename.trimmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString cleanUsername()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
QString userName = qgetenv("USERNAME");
|
||||||
|
if (userName.isEmpty()) {
|
||||||
|
userName = qgetenv("USER");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
QString userName = qgetenv("USER");
|
||||||
|
if (userName.isEmpty()) {
|
||||||
|
userName = qgetenv("USERNAME");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Sanitize username for file safety
|
||||||
|
userName = userName.trimmed();
|
||||||
|
// Replace <>:"/\|?* with _
|
||||||
|
userName.replace(QRegularExpression(R"([<>:\"\/\\|?*])"), "_");
|
||||||
|
// Remove trailing dots and spaces
|
||||||
|
userName.replace(QRegularExpression(R"([.\s]+$)"), "");
|
||||||
|
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
QVariantMap qo2qvm(const QObject* object, const QStringList& ignoredProperties)
|
QVariantMap qo2qvm(const QObject* object, const QStringList& ignoredProperties)
|
||||||
{
|
{
|
||||||
QVariantMap result;
|
QVariantMap result;
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ namespace Tools
|
|||||||
QString envSubstitute(const QString& filepath,
|
QString envSubstitute(const QString& filepath,
|
||||||
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment());
|
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment());
|
||||||
QString cleanFilename(QString filename);
|
QString cleanFilename(QString filename);
|
||||||
|
QString cleanUsername();
|
||||||
|
|
||||||
template <class T> QSet<T> asSet(const QList<T>& a)
|
template <class T> QSet<T> asSet(const QList<T>& a)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
#include "core/Bootstrap.h"
|
#include "core/Bootstrap.h"
|
||||||
|
#include "core/Tools.h"
|
||||||
#include "gui/MainWindow.h"
|
#include "gui/MainWindow.h"
|
||||||
#include "gui/MessageBox.h"
|
#include "gui/MessageBox.h"
|
||||||
#include "gui/osutils/OSUtils.h"
|
#include "gui/osutils/OSUtils.h"
|
||||||
@@ -31,6 +32,7 @@
|
|||||||
#include <QLocalSocket>
|
#include <QLocalSocket>
|
||||||
#include <QLockFile>
|
#include <QLockFile>
|
||||||
#include <QPixmapCache>
|
#include <QPixmapCache>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QSocketNotifier>
|
#include <QSocketNotifier>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
@@ -63,20 +65,19 @@ Application::Application(int& argc, char** argv)
|
|||||||
registerUnixSignals();
|
registerUnixSignals();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QString userName = qgetenv("USER");
|
// Build identifier
|
||||||
if (userName.isEmpty()) {
|
auto identifier = QStringLiteral("keepassxc");
|
||||||
userName = qgetenv("USERNAME");
|
auto username = Tools::cleanUsername();
|
||||||
}
|
if (!username.isEmpty()) {
|
||||||
QString identifier = "keepassxc";
|
identifier += QChar('-') + username;
|
||||||
if (!userName.isEmpty()) {
|
|
||||||
identifier += "-" + userName;
|
|
||||||
}
|
}
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
// In DEBUG mode don't interfere with Release instances
|
// In DEBUG mode don’t interfere with Release instances
|
||||||
identifier += "-DEBUG";
|
identifier += QStringLiteral("-DEBUG");
|
||||||
#endif
|
#endif
|
||||||
QString lockName = identifier + ".lock";
|
|
||||||
m_socketName = identifier + ".socket";
|
QString lockName = identifier + QStringLiteral(".lock");
|
||||||
|
m_socketName = identifier + QStringLiteral(".socket");
|
||||||
|
|
||||||
// According to documentation we should use RuntimeLocation on *nixes, but even Qt doesn't respect
|
// According to documentation we should use RuntimeLocation on *nixes, but even Qt doesn't respect
|
||||||
// this and creates sockets in TempLocation, so let's be consistent.
|
// this and creates sockets in TempLocation, so let's be consistent.
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ void ApplicationSettingsWidget::loadSettings()
|
|||||||
m_generalUi->autoReloadOnChangeCheckBox->setChecked(config()->get(Config::AutoReloadOnChange).toBool());
|
m_generalUi->autoReloadOnChangeCheckBox->setChecked(config()->get(Config::AutoReloadOnChange).toBool());
|
||||||
m_generalUi->minimizeAfterUnlockCheckBox->setChecked(config()->get(Config::MinimizeAfterUnlock).toBool());
|
m_generalUi->minimizeAfterUnlockCheckBox->setChecked(config()->get(Config::MinimizeAfterUnlock).toBool());
|
||||||
m_generalUi->minimizeOnOpenUrlCheckBox->setChecked(config()->get(Config::MinimizeOnOpenUrl).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());
|
m_generalUi->hideWindowOnCopyCheckBox->setChecked(config()->get(Config::HideWindowOnCopy).toBool());
|
||||||
hideWindowOnCopyCheckBoxToggled(m_generalUi->hideWindowOnCopyCheckBox->isChecked());
|
hideWindowOnCopyCheckBoxToggled(m_generalUi->hideWindowOnCopyCheckBox->isChecked());
|
||||||
m_generalUi->minimizeOnCopyRadioButton->setChecked(config()->get(Config::MinimizeOnCopy).toBool());
|
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::AutoReloadOnChange, m_generalUi->autoReloadOnChangeCheckBox->isChecked());
|
||||||
config()->set(Config::MinimizeAfterUnlock, m_generalUi->minimizeAfterUnlockCheckBox->isChecked());
|
config()->set(Config::MinimizeAfterUnlock, m_generalUi->minimizeAfterUnlockCheckBox->isChecked());
|
||||||
config()->set(Config::MinimizeOnOpenUrl, m_generalUi->minimizeOnOpenUrlCheckBox->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::HideWindowOnCopy, m_generalUi->hideWindowOnCopyCheckBox->isChecked());
|
||||||
config()->set(Config::MinimizeOnCopy, m_generalUi->minimizeOnCopyRadioButton->isChecked());
|
config()->set(Config::MinimizeOnCopy, m_generalUi->minimizeOnCopyRadioButton->isChecked());
|
||||||
config()->set(Config::DropToBackgroundOnCopy, m_generalUi->dropToBackgroundOnCopyRadioButton->isChecked());
|
config()->set(Config::DropToBackgroundOnCopy, m_generalUi->dropToBackgroundOnCopyRadioButton->isChecked());
|
||||||
|
|||||||
@@ -554,14 +554,59 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="openUrlOnDoubleClick">
|
<layout class="QHBoxLayout" name="urlDoubleClickLayout">
|
||||||
<property name="text">
|
<item>
|
||||||
<string>Open browser on double clicking URL field in entry view</string>
|
<widget class="QLabel" name="urlDoubleClickLabel">
|
||||||
</property>
|
<property name="text">
|
||||||
<property name="checked">
|
<string>Double-click action for URL:</string>
|
||||||
<bool>true</bool>
|
</property>
|
||||||
</property>
|
<property name="buddy">
|
||||||
</widget>
|
<cstring>urlDoubleClickComboBox</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="urlDoubleClickComboBox">
|
||||||
|
<property name="focusPolicy">
|
||||||
|
<enum>Qt::StrongFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="accessibleName">
|
||||||
|
<string>Double-click action for URL field</string>
|
||||||
|
</property>
|
||||||
|
<property name="sizeAdjustPolicy">
|
||||||
|
<enum>QComboBox::AdjustToContents</enum>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Open entry URL in browser</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Copy entry URL to clipboard</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Edit entry</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="urlDoubleClickSpacer">
|
||||||
|
<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>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="useGroupIconOnEntryCreationCheckBox">
|
<widget class="QCheckBox" name="useGroupIconOnEntryCreationCheckBox">
|
||||||
@@ -1439,7 +1484,7 @@
|
|||||||
<tabstop>alternativeSaveComboBox</tabstop>
|
<tabstop>alternativeSaveComboBox</tabstop>
|
||||||
<tabstop>ConfirmMoveEntryToRecycleBinCheckBox</tabstop>
|
<tabstop>ConfirmMoveEntryToRecycleBinCheckBox</tabstop>
|
||||||
<tabstop>EnableCopyOnDoubleClickCheckBox</tabstop>
|
<tabstop>EnableCopyOnDoubleClickCheckBox</tabstop>
|
||||||
<tabstop>openUrlOnDoubleClick</tabstop>
|
<tabstop>urlDoubleClickComboBox</tabstop>
|
||||||
<tabstop>useGroupIconOnEntryCreationCheckBox</tabstop>
|
<tabstop>useGroupIconOnEntryCreationCheckBox</tabstop>
|
||||||
<tabstop>minimizeOnOpenUrlCheckBox</tabstop>
|
<tabstop>minimizeOnOpenUrlCheckBox</tabstop>
|
||||||
<tabstop>hideWindowOnCopyCheckBox</tabstop>
|
<tabstop>hideWindowOnCopyCheckBox</tabstop>
|
||||||
|
|||||||
@@ -1583,12 +1583,21 @@ void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::Mod
|
|||||||
// case EntryModel::Attachments:
|
// case EntryModel::Attachments:
|
||||||
// break;
|
// break;
|
||||||
case EntryModel::Url:
|
case EntryModel::Url:
|
||||||
if (!entry->url().isEmpty() && config()->get(Config::OpenURLOnDoubleClick).toBool()) {
|
if (!entry->url().isEmpty()) {
|
||||||
openUrlForEntry(entry);
|
switch (config()->get(Config::URLDoubleClickAction).toInt()) {
|
||||||
break;
|
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.
|
break;
|
||||||
[[fallthrough]];
|
|
||||||
default:
|
default:
|
||||||
switchToEntryEdit(entry);
|
switchToEntryEdit(entry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ MainWindow::MainWindow()
|
|||||||
|
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
macUtils()->configureWindowAndHelpMenus(this, m_ui->menuHelp);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS)
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS)
|
||||||
new MainWindowAdaptor(this);
|
new MainWindowAdaptor(this);
|
||||||
QDBusConnection dbus = QDBusConnection::sessionBus();
|
QDBusConnection dbus = QDBusConnection::sessionBus();
|
||||||
|
|||||||
@@ -72,10 +72,10 @@ SearchWidget::SearchWidget(QWidget* parent)
|
|||||||
m_actionLimitGroup->setChecked(config()->get(Config::SearchLimitGroup).toBool());
|
m_actionLimitGroup->setChecked(config()->get(Config::SearchLimitGroup).toBool());
|
||||||
|
|
||||||
m_actionWaitForEnter = m_searchMenu->addAction(
|
m_actionWaitForEnter = m_searchMenu->addAction(
|
||||||
tr("Press Enter to search"), this, [](bool state) { config()->set(Config::SearchWaitForEnter, state); });
|
tr("Press Enter to search"), this, [](bool state) { config()->set(Config::GUI_SearchWaitForEnter, state); });
|
||||||
m_actionWaitForEnter->setObjectName("actionSearchWaitForEnter");
|
m_actionWaitForEnter->setObjectName("actionSearchWaitForEnter");
|
||||||
m_actionWaitForEnter->setCheckable(true);
|
m_actionWaitForEnter->setCheckable(true);
|
||||||
m_actionWaitForEnter->setChecked(config()->get(Config::SearchWaitForEnter).toBool());
|
m_actionWaitForEnter->setChecked(config()->get(Config::GUI_SearchWaitForEnter).toBool());
|
||||||
|
|
||||||
m_ui->searchIcon->setIcon(icons()->icon("system-search"));
|
m_ui->searchIcon->setIcon(icons()->icon("system-search"));
|
||||||
m_ui->searchEdit->addAction(m_ui->searchIcon, QLineEdit::LeadingPosition);
|
m_ui->searchEdit->addAction(m_ui->searchIcon, QLineEdit::LeadingPosition);
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMainWindow>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
class QWindow;
|
class QWindow;
|
||||||
@@ -45,6 +47,7 @@ public:
|
|||||||
bool enableScreenRecording();
|
bool enableScreenRecording();
|
||||||
void toggleForegroundApp(bool foreground);
|
void toggleForegroundApp(bool foreground);
|
||||||
void setWindowSecurity(QWindow* window, bool state);
|
void setWindowSecurity(QWindow* window, bool state);
|
||||||
|
void configureWindowAndHelpMenus(QMainWindow* mainWindow, QMenu* helpMenu);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void userSwitched();
|
void userSwitched();
|
||||||
|
|||||||
@@ -42,5 +42,6 @@
|
|||||||
- (bool) enableScreenRecording;
|
- (bool) enableScreenRecording;
|
||||||
- (void) toggleForegroundApp:(bool) foreground;
|
- (void) toggleForegroundApp:(bool) foreground;
|
||||||
- (void) setWindowSecurity:(NSWindow*) window state:(bool) state;
|
- (void) setWindowSecurity:(NSWindow*) window state:(bool) state;
|
||||||
|
- (void) configureWindowAndHelpMenus:(QMainWindow*) mainWindow helpMenu:(QMenu*) helpMenu;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
#import "AppKitImpl.h"
|
#import "AppKitImpl.h"
|
||||||
#import <QWindow>
|
#import <QWindow>
|
||||||
|
#import <QMenu>
|
||||||
|
#import <QMenuBar>
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#if __clang_major__ >= 13 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_3
|
#if __clang_major__ >= 13 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_3
|
||||||
#import <ScreenCaptureKit/ScreenCaptureKit.h>
|
#import <ScreenCaptureKit/ScreenCaptureKit.h>
|
||||||
@@ -184,7 +186,7 @@
|
|||||||
//
|
//
|
||||||
// Check if screen recording is enabled, may show an popup asking for permissions
|
// Check if screen recording is enabled, may show an popup asking for permissions
|
||||||
//
|
//
|
||||||
- (bool) enableScreenRecording
|
- (bool) enableScreenRecording
|
||||||
{
|
{
|
||||||
#if __clang_major__ >= 13 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_3
|
#if __clang_major__ >= 13 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_3
|
||||||
if (@available(macOS 12.3, *)) {
|
if (@available(macOS 12.3, *)) {
|
||||||
@@ -192,7 +194,7 @@
|
|||||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||||
|
|
||||||
// Attempt to use SCShareableContent to check for screen recording permission
|
// Attempt to use SCShareableContent to check for screen recording permission
|
||||||
[SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent * _Nullable content,
|
[SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent * _Nullable content,
|
||||||
NSError * _Nullable error) {
|
NSError * _Nullable error) {
|
||||||
Q_UNUSED(error);
|
Q_UNUSED(error);
|
||||||
if (content) {
|
if (content) {
|
||||||
@@ -231,8 +233,29 @@
|
|||||||
[window setSharingType: state ? NSWindowSharingNone : NSWindowSharingReadOnly];
|
[window setSharingType: state ? NSWindowSharingNone : NSWindowSharingReadOnly];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) configureWindowAndHelpMenus:(QMainWindow*) mainWindow helpMenu:(QMenu*) helpMenu
|
||||||
|
{
|
||||||
|
QMenu *qtWindowMenu = new QMenu(AppKit::tr("Window"));
|
||||||
|
NSMenu *nsWindowMenu = qtWindowMenu->toNSMenu();
|
||||||
|
|
||||||
|
QString minimizeStr = AppKit::tr("Minimize");
|
||||||
|
[nsWindowMenu addItemWithTitle:minimizeStr.toNSString() action:@selector(performMiniaturize:) keyEquivalent:@""];
|
||||||
|
QString zoomStr = AppKit::tr("Zoom");
|
||||||
|
[nsWindowMenu addItemWithTitle:zoomStr.toNSString() action:@selector(performZoom:) keyEquivalent:@""];
|
||||||
|
[nsWindowMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
QString bringAllToFrontStr = AppKit::tr("Bring All to Front");
|
||||||
|
[nsWindowMenu addItemWithTitle:bringAllToFrontStr.toNSString() action:@selector(arrangeInFront:) keyEquivalent:@""];
|
||||||
|
|
||||||
|
NSApp.windowsMenu = nsWindowMenu;
|
||||||
|
|
||||||
|
mainWindow->menuBar()->insertMenu(helpMenu->menuAction(), qtWindowMenu);
|
||||||
|
|
||||||
|
NSApp.helpMenu = helpMenu->toNSMenu();
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// ------------------------- C++ Trampolines -------------------------
|
// ------------------------- C++ Trampolines -------------------------
|
||||||
//
|
//
|
||||||
@@ -312,3 +335,8 @@ void AppKit::setWindowSecurity(QWindow* window, bool state)
|
|||||||
auto view = reinterpret_cast<NSView*>(window->winId());
|
auto view = reinterpret_cast<NSView*>(window->winId());
|
||||||
[static_cast<id>(self) setWindowSecurity:view.window state:state];
|
[static_cast<id>(self) setWindowSecurity:view.window state:state];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppKit::configureWindowAndHelpMenus(QMainWindow* window, QMenu* helpMenu)
|
||||||
|
{
|
||||||
|
[static_cast<id>(self) configureWindowAndHelpMenus:window helpMenu:helpMenu];
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
#include <ApplicationServices/ApplicationServices.h>
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
|
|
||||||
@@ -202,6 +203,11 @@ void MacUtils::registerNativeEventFilter()
|
|||||||
::InstallApplicationEventHandler(MacUtils::hotkeyHandler, 1, &eventSpec, this, nullptr);
|
::InstallApplicationEventHandler(MacUtils::hotkeyHandler, 1, &eventSpec, this, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacUtils::configureWindowAndHelpMenus(QMainWindow* mainWindow, QMenu* helpMenu)
|
||||||
|
{
|
||||||
|
return m_appkit->configureWindowAndHelpMenus(mainWindow, helpMenu);
|
||||||
|
}
|
||||||
|
|
||||||
bool MacUtils::registerGlobalShortcut(const QString& name, Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error)
|
bool MacUtils::registerGlobalShortcut(const QString& name, Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error)
|
||||||
{
|
{
|
||||||
auto keycode = qtToNativeKeyCode(key);
|
auto keycode = qtToNativeKeyCode(key);
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ public:
|
|||||||
|
|
||||||
void registerNativeEventFilter() override;
|
void registerNativeEventFilter() override;
|
||||||
|
|
||||||
|
void configureWindowAndHelpMenus(QMainWindow* mainWindow, QMenu* helpMenu);
|
||||||
|
|
||||||
bool registerGlobalShortcut(const QString& name,
|
bool registerGlobalShortcut(const QString& name,
|
||||||
Qt::Key key,
|
Qt::Key key,
|
||||||
Qt::KeyboardModifiers modifiers,
|
Qt::KeyboardModifiers modifiers,
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ void NixUtils::setLaunchAtStartup(bool enable)
|
|||||||
<< QStringLiteral("TryExec=") << executeablePathOrName << '\n'
|
<< QStringLiteral("TryExec=") << executeablePathOrName << '\n'
|
||||||
<< QStringLiteral("Icon=") << QApplication::applicationName().toLower() << '\n'
|
<< QStringLiteral("Icon=") << QApplication::applicationName().toLower() << '\n'
|
||||||
<< QStringLiteral("StartupWMClass=keepassxc") << '\n'
|
<< QStringLiteral("StartupWMClass=keepassxc") << '\n'
|
||||||
<< QStringLiteral("StartupNotify=true") << '\n'
|
<< QStringLiteral("StartupNotify=false") << '\n'
|
||||||
<< QStringLiteral("Terminal=false") << '\n'
|
<< QStringLiteral("Terminal=false") << '\n'
|
||||||
<< QStringLiteral("Type=Application") << '\n'
|
<< QStringLiteral("Type=Application") << '\n'
|
||||||
<< QStringLiteral("Version=1.0") << '\n'
|
<< QStringLiteral("Version=1.0") << '\n'
|
||||||
|
|||||||
@@ -56,9 +56,9 @@ namespace
|
|||||||
// but those cases with high probability constructed examples and very rare in real usage
|
// but those cases with high probability constructed examples and very rare in real usage
|
||||||
const auto* sourceReference = sourceDb->rootGroup()->findEntryByUuid(targetEntry->uuid());
|
const auto* sourceReference = sourceDb->rootGroup()->findEntryByUuid(targetEntry->uuid());
|
||||||
const auto resolvedValue = sourceReference->resolveMultiplePlaceholders(standardValue);
|
const auto resolvedValue = sourceReference->resolveMultiplePlaceholders(standardValue);
|
||||||
targetEntry->setUpdateTimeinfo(false);
|
targetEntry->beginUpdate();
|
||||||
targetEntry->attributes()->set(attribute, resolvedValue, targetEntry->attributes()->isProtected(attribute));
|
targetEntry->attributes()->set(attribute, resolvedValue, targetEntry->attributes()->isProtected(attribute));
|
||||||
targetEntry->setUpdateTimeinfo(true);
|
targetEntry->endUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -125,6 +125,20 @@ void TestAutoType::init()
|
|||||||
m_entry5->setPassword("example5");
|
m_entry5->setPassword("example5");
|
||||||
m_entry5->setTitle("some title");
|
m_entry5->setTitle("some title");
|
||||||
m_entry5->setUrl("http://example.org");
|
m_entry5->setUrl("http://example.org");
|
||||||
|
|
||||||
|
m_entry6 = new Entry();
|
||||||
|
m_entry6->setGroup(m_group);
|
||||||
|
m_entry6->setPassword("example6");
|
||||||
|
m_entry6->setTitle("empty window test");
|
||||||
|
association.window = "";
|
||||||
|
association.sequence = "{S:Empty Window}";
|
||||||
|
m_entry6->autoTypeAssociations()->add(association);
|
||||||
|
association.window = "non-matching window";
|
||||||
|
association.sequence = "should not match";
|
||||||
|
m_entry6->autoTypeAssociations()->add(association);
|
||||||
|
association.window = "*notepad*";
|
||||||
|
association.sequence = "{USERNAME}";
|
||||||
|
m_entry6->autoTypeAssociations()->add(association);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestAutoType::cleanup()
|
void TestAutoType::cleanup()
|
||||||
@@ -446,3 +460,13 @@ void TestAutoType::testAutoTypeEffectiveSequences()
|
|||||||
QCOMPARE(entry6->defaultAutoTypeSequence(), sequenceOrphan);
|
QCOMPARE(entry6->defaultAutoTypeSequence(), sequenceOrphan);
|
||||||
QCOMPARE(entry6->effectiveAutoTypeSequence(), QString());
|
QCOMPARE(entry6->effectiveAutoTypeSequence(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestAutoType::testAutoTypeEmptyWindowAssociation()
|
||||||
|
{
|
||||||
|
auto assoc = m_entry6->autoTypeSequences("Windows Notepad");
|
||||||
|
QCOMPARE(assoc.size(), 2);
|
||||||
|
QVERIFY(assoc.contains("{S:Empty Window}"));
|
||||||
|
|
||||||
|
assoc = m_entry6->autoTypeSequences("Some Other Window");
|
||||||
|
QVERIFY(assoc.isEmpty());
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ private slots:
|
|||||||
void testAutoTypeResults_data();
|
void testAutoTypeResults_data();
|
||||||
void testAutoTypeSyntaxChecks();
|
void testAutoTypeSyntaxChecks();
|
||||||
void testAutoTypeEffectiveSequences();
|
void testAutoTypeEffectiveSequences();
|
||||||
|
void testAutoTypeEmptyWindowAssociation();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AutoTypePlatformInterface* m_platform;
|
AutoTypePlatformInterface* m_platform;
|
||||||
@@ -64,6 +65,7 @@ private:
|
|||||||
Entry* m_entry3;
|
Entry* m_entry3;
|
||||||
Entry* m_entry4;
|
Entry* m_entry4;
|
||||||
Entry* m_entry5;
|
Entry* m_entry5;
|
||||||
|
Entry* m_entry6;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_TESTAUTOTYPE_H
|
#endif // KEEPASSX_TESTAUTOTYPE_H
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include "TestConfig.h"
|
#include "TestConfig.h"
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
|
||||||
#include "config-keepassx-tests.h"
|
#include "config-keepassx-tests.h"
|
||||||
@@ -40,3 +41,39 @@ void TestConfig::testUpgrade()
|
|||||||
|
|
||||||
tempFile.remove();
|
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();
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class TestConfig : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private slots:
|
private slots:
|
||||||
void testUpgrade();
|
void testUpgrade();
|
||||||
|
void testURLDoubleClickMigration();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_TESTCONFIG_H
|
#endif // KEEPASSX_TESTCONFIG_H
|
||||||
|
|||||||
@@ -428,3 +428,26 @@ void TestTools::testIsTextMimeType()
|
|||||||
QVERIFY(!Tools::isTextMimeType(noText));
|
QVERIFY(!Tools::isTextMimeType(noText));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test sanitization logic for Tools::cleanUsername
|
||||||
|
void TestTools::testCleanUsername()
|
||||||
|
{
|
||||||
|
// Test vars
|
||||||
|
QFETCH(QString, input);
|
||||||
|
QFETCH(QString, expected);
|
||||||
|
|
||||||
|
qputenv("USER", input.toUtf8());
|
||||||
|
qputenv("USERNAME", input.toUtf8());
|
||||||
|
QCOMPARE(Tools::cleanUsername(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestTools::testCleanUsername_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("input");
|
||||||
|
QTest::addColumn<QString>("expected");
|
||||||
|
|
||||||
|
QTest::newRow("Leading and trailing spaces") << " user " << "user";
|
||||||
|
QTest::newRow("Special characters") << R"(user<>:"/\|?*name)" << "user_________name";
|
||||||
|
QTest::newRow("Trailing dots and spaces") << "username... " << "username";
|
||||||
|
QTest::newRow("Combination of issues") << R"( user<>:"/\|?*name... )" << "user_________name";
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ private slots:
|
|||||||
void testGetMimeType();
|
void testGetMimeType();
|
||||||
void testGetMimeTypeByFileInfo();
|
void testGetMimeTypeByFileInfo();
|
||||||
void testIsTextMimeType();
|
void testIsTextMimeType();
|
||||||
|
void testCleanUsername();
|
||||||
|
void testCleanUsername_data();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_TESTTOOLS_H
|
#endif // KEEPASSX_TESTTOOLS_H
|
||||||
|
|||||||
Reference in New Issue
Block a user