From a6d3f973fa8449f0f7dac864b3bd3928c29c649f Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Mon, 27 Jun 2022 23:21:40 -0400 Subject: [PATCH] Better handling of "Lock on Minimize" setting * Fix #1090 - delay locking databases after minimize to allow for clipboard use, Auto-Type use, and browser integration use. * Fix #6757 - prevent setting both minimize on unlock and lock on minimize settings at the same time. --- share/translations/keepassxc_en.ts | 4 ++++ src/gui/ApplicationSettingsWidget.cpp | 12 +++++++++++- src/gui/Clipboard.cpp | 26 ++++++++++++++++++-------- src/gui/Clipboard.h | 5 ++++- src/gui/DatabaseTabWidget.cpp | 15 +++++++++++++++ src/gui/DatabaseTabWidget.h | 3 +++ src/gui/MainWindow.cpp | 4 ++-- 7 files changed, 57 insertions(+), 12 deletions(-) diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index f4b63fd3f..06cfd3ecb 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -227,6 +227,10 @@ Select backup storage directory + + This setting cannot be enabled when minimize on unlock is enabled. + + ApplicationSettingsWidgetGeneral diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index 12578d8f9..be925c770 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -134,6 +134,15 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent) m_secUi->lockDatabaseIdleSpinBox, SLOT(setEnabled(bool))); // clang-format on + connect(m_generalUi->minimizeAfterUnlockCheckBox, &QCheckBox::toggled, this, [this](bool state) { + if (state) { + m_secUi->lockDatabaseMinimizeCheckBox->setChecked(false); + } + m_secUi->lockDatabaseMinimizeCheckBox->setToolTip( + state ? tr("This setting cannot be enabled when minimize on unlock is enabled.") : ""); + m_secUi->lockDatabaseMinimizeCheckBox->setEnabled(!state); + }); + // Disable mouse wheel grab when scrolling // This prevents combo box and spinner values from changing without explicit focus auto mouseWheelFilter = new MouseWheelEventFilter(this); @@ -296,7 +305,8 @@ void ApplicationSettingsWidget::loadSettings() m_secUi->lockDatabaseIdleCheckBox->setChecked(config()->get(Config::Security_LockDatabaseIdle).toBool()); m_secUi->lockDatabaseIdleSpinBox->setValue(config()->get(Config::Security_LockDatabaseIdleSeconds).toInt()); - m_secUi->lockDatabaseMinimizeCheckBox->setChecked(config()->get(Config::Security_LockDatabaseMinimize).toBool()); + m_secUi->lockDatabaseMinimizeCheckBox->setChecked(m_secUi->lockDatabaseMinimizeCheckBox->isEnabled() + && config()->get(Config::Security_LockDatabaseMinimize).toBool()); m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked( config()->get(Config::Security_LockDatabaseScreenLock).toBool()); m_secUi->fallbackToSearch->setChecked(config()->get(Config::Security_IconDownloadFallback).toBool()); diff --git a/src/gui/Clipboard.cpp b/src/gui/Clipboard.cpp index 804a53143..2def33c81 100644 --- a/src/gui/Clipboard.cpp +++ b/src/gui/Clipboard.cpp @@ -76,14 +76,21 @@ void Clipboard::setText(const QString& text, bool clear) if (config()->get(Config::Security_ClearClipboard).toBool()) { int timeout = config()->get(Config::Security_ClearClipboardTimeout).toInt(); if (timeout > 0) { - m_secondsElapsed = -1; - countdownTick(); + m_secondsToClear = timeout; + sendCountdownStatus(); m_timer->start(1000); + } else { + clearCopiedText(); } } } } +int Clipboard::secondsToClear() +{ + return m_secondsToClear; +} + void Clipboard::clearCopiedText() { m_timer->stop(); @@ -105,17 +112,20 @@ void Clipboard::clearCopiedText() void Clipboard::countdownTick() { - m_secondsElapsed++; - int timeout = config()->get(Config::Security_ClearClipboardTimeout).toInt(); - int timeLeft = timeout - m_secondsElapsed; - if (timeLeft <= 0) { + if (--m_secondsToClear <= 0) { clearCopiedText(); } else { - emit updateCountdown(100 * timeLeft / timeout, - QObject::tr("Clearing the clipboard in %1 second(s)…", "", timeLeft).arg(timeLeft)); + sendCountdownStatus(); } } +void Clipboard::sendCountdownStatus() +{ + emit updateCountdown( + 100 * m_secondsToClear / config()->get(Config::Security_ClearClipboardTimeout).toInt(), + QObject::tr("Clearing the clipboard in %1 second(s)…", "", m_secondsToClear).arg(m_secondsToClear)); +} + Clipboard* Clipboard::instance() { if (!m_instance) { diff --git a/src/gui/Clipboard.h b/src/gui/Clipboard.h index c97d91881..4dc2f3127 100644 --- a/src/gui/Clipboard.h +++ b/src/gui/Clipboard.h @@ -34,6 +34,7 @@ class Clipboard : public QObject public: void setText(const QString& text, bool clear = true); + int secondsToClear(); static Clipboard* instance(); @@ -51,8 +52,10 @@ private: static Clipboard* m_instance; + void sendCountdownStatus(); + QTimer* m_timer; - int m_secondsElapsed = 0; + int m_secondsToClear = 0; #ifdef Q_OS_MACOS // This object lives for the whole program lifetime and we cannot delete it on exit, diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 66715fc4c..c3afcf06f 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -63,6 +63,9 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) #ifdef Q_OS_MACOS connect(macUtils(), SIGNAL(lockDatabases()), SLOT(lockDatabases())); #endif + + m_lockDelayTimer.setSingleShot(true); + connect(&m_lockDelayTimer, &QTimer::timeout, this, [this] { lockDatabases(); }); } DatabaseTabWidget::~DatabaseTabWidget() @@ -649,6 +652,18 @@ bool DatabaseTabWidget::lockDatabases() return numLocked == c; } +void DatabaseTabWidget::lockDatabasesDelayed() +{ + // Delay at least 1 second and up to 20 seconds depending on clipboard state. + // This allows for Auto-Type, Browser Extension, and clipboard to function + // even with "Lock on Minimize" setting enabled. + int lockDelay = qBound(1, clipboard()->secondsToClear(), 20); + m_lockDelayTimer.setInterval(lockDelay * 1000); + if (!m_lockDelayTimer.isActive()) { + m_lockDelayTimer.start(); + } +} + /** * Unlock a database with an unlock popup dialog. * diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index e823043a6..dfd0cff83 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -22,6 +22,7 @@ #include "gui/MessageWidget.h" #include +#include class Database; class DatabaseWidget; @@ -72,6 +73,7 @@ public slots: void exportToHtml(); bool lockDatabases(); + void lockDatabasesDelayed(); void closeDatabaseFromSender(); void unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent); void unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent, const QString& filePath); @@ -112,6 +114,7 @@ private: QPointer m_dbWidgetStateSync; QPointer m_dbWidgetPendingLock; QPointer m_databaseOpenDialog; + QTimer m_lockDelayTimer; }; #endif // KEEPASSX_DATABASETABWIDGET_H diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 2f0c62faa..cab6a5a4c 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1326,7 +1326,7 @@ void MainWindow::changeEvent(QEvent* event) } if (config()->get(Config::Security_LockDatabaseMinimize).toBool()) { - m_ui->tabWidget->lockDatabases(); + m_ui->tabWidget->lockDatabasesDelayed(); } } else { QMainWindow::changeEvent(event); @@ -1674,7 +1674,7 @@ void MainWindow::hideWindow() } if (config()->get(Config::Security_LockDatabaseMinimize).toBool()) { - m_ui->tabWidget->lockDatabases(); + m_ui->tabWidget->lockDatabasesDelayed(); } }