mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-09-21 21:34:48 -04:00
Allow configuring keyboard shortcuts (#9643)
Closes #2689 The design of the respective code is loosely based on KDE's KActionCollection. The ActionCollection manages all actions that can be shortcut configured. These actions are then exposed in the config and a user can assign a different shortcut. Actions inside the MainWindow have been added to the ActionCollection. --------- Co-authored-by: Jonathan White <support@dmapps.us>
This commit is contained in:
parent
d03f5e4977
commit
a472ef8a93
17 changed files with 1238 additions and 233 deletions
|
@ -98,6 +98,7 @@ set(keepassx_SOURCES
|
|||
gui/styles/dark/DarkStyle.cpp
|
||||
gui/styles/light/LightStyle.cpp
|
||||
gui/AboutDialog.cpp
|
||||
gui/ActionCollection.cpp
|
||||
gui/Application.cpp
|
||||
gui/CategoryListWidget.cpp
|
||||
gui/Clipboard.cpp
|
||||
|
@ -130,6 +131,7 @@ set(keepassx_SOURCES
|
|||
gui/SearchWidget.cpp
|
||||
gui/SortFilterHideProxyModel.cpp
|
||||
gui/SquareSvgWidget.cpp
|
||||
gui/ShortcutSettingsPage.cpp
|
||||
gui/TotpSetupDialog.cpp
|
||||
gui/TotpDialog.cpp
|
||||
gui/TotpExportSettingsDialog.cpp
|
||||
|
@ -180,6 +182,7 @@ set(keepassx_SOURCES
|
|||
gui/widgets/ElidedLabel.cpp
|
||||
gui/widgets/KPToolBar.cpp
|
||||
gui/widgets/PopupHelpWidget.cpp
|
||||
gui/widgets/ShortcutWidget.cpp
|
||||
gui/wizard/NewDatabaseWizard.cpp
|
||||
gui/wizard/NewDatabaseWizardPage.cpp
|
||||
gui/wizard/NewDatabaseWizardPageMetaData.cpp
|
||||
|
@ -319,7 +322,6 @@ set(autotype_SOURCES
|
|||
autotype/AutoTypeMatchView.cpp
|
||||
autotype/AutoTypeSelectDialog.cpp
|
||||
autotype/PickcharsDialog.cpp
|
||||
autotype/ShortcutWidget.cpp
|
||||
autotype/WindowSelectComboBox.cpp)
|
||||
|
||||
if(WIN32)
|
||||
|
|
|
@ -597,4 +597,28 @@ void Config::createTempFileInstance()
|
|||
tmpFile->setParent(m_instance);
|
||||
}
|
||||
|
||||
QList<Config::ShortcutEntry> Config::getShortcuts() const
|
||||
{
|
||||
m_settings->beginGroup("Shortcuts");
|
||||
const auto keys = m_settings->childKeys();
|
||||
QList<ShortcutEntry> ret;
|
||||
ret.reserve(keys.size());
|
||||
for (const auto& key : keys) {
|
||||
const auto shortcut = m_settings->value(key).toString();
|
||||
ret.push_back(ShortcutEntry{key, shortcut});
|
||||
}
|
||||
m_settings->endGroup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Config::setShortcuts(const QList<ShortcutEntry>& shortcuts)
|
||||
{
|
||||
m_settings->beginGroup("Shortcuts");
|
||||
m_settings->remove(""); // clear previous
|
||||
for (const auto& shortcutEntry : shortcuts) {
|
||||
m_settings->setValue(shortcutEntry.name, shortcutEntry.shortcut);
|
||||
}
|
||||
m_settings->endGroup();
|
||||
}
|
||||
|
||||
#undef QS
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <QPointer>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
class QSettings;
|
||||
|
||||
|
@ -198,6 +199,12 @@ public:
|
|||
Deleted
|
||||
};
|
||||
|
||||
struct ShortcutEntry
|
||||
{
|
||||
QString name;
|
||||
QString shortcut;
|
||||
};
|
||||
|
||||
~Config() override;
|
||||
QVariant get(ConfigKey key);
|
||||
QVariant getDefault(ConfigKey key);
|
||||
|
@ -208,6 +215,9 @@ public:
|
|||
void sync();
|
||||
void resetToDefaults();
|
||||
|
||||
QList<ShortcutEntry> getShortcuts() const;
|
||||
void setShortcuts(const QList<ShortcutEntry>& shortcuts);
|
||||
|
||||
static Config* instance();
|
||||
static void createConfigFromFile(const QString& configFileName, const QString& localConfigFileName = {});
|
||||
static void createTempFileInstance();
|
||||
|
|
123
src/gui/ActionCollection.cpp
Normal file
123
src/gui/ActionCollection.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ActionCollection.h"
|
||||
#include "core/Config.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
ActionCollection* ActionCollection::instance()
|
||||
{
|
||||
static ActionCollection ac;
|
||||
return ∾
|
||||
}
|
||||
|
||||
QList<QAction*> ActionCollection::actions() const
|
||||
{
|
||||
return m_actions;
|
||||
}
|
||||
|
||||
void ActionCollection::addAction(QAction* action)
|
||||
{
|
||||
if (!m_actions.contains(action)) {
|
||||
m_actions << action;
|
||||
}
|
||||
}
|
||||
|
||||
void ActionCollection::addActions(const QList<QAction*>& actions)
|
||||
{
|
||||
for (auto a : actions) {
|
||||
addAction(a);
|
||||
}
|
||||
}
|
||||
|
||||
QKeySequence ActionCollection::defaultShortcut(const QAction* action) const
|
||||
{
|
||||
auto shortcuts = defaultShortcuts(action);
|
||||
return shortcuts.isEmpty() ? QKeySequence() : shortcuts.first();
|
||||
}
|
||||
|
||||
QList<QKeySequence> ActionCollection::defaultShortcuts(const QAction* action) const
|
||||
{
|
||||
return action->property("defaultShortcuts").value<QList<QKeySequence>>();
|
||||
}
|
||||
|
||||
void ActionCollection::setDefaultShortcut(QAction* action, const QKeySequence& shortcut)
|
||||
{
|
||||
setDefaultShortcuts(action, {shortcut});
|
||||
}
|
||||
|
||||
void ActionCollection::setDefaultShortcut(QAction* action,
|
||||
QKeySequence::StandardKey standard,
|
||||
const QKeySequence& fallback)
|
||||
{
|
||||
if (!QKeySequence::keyBindings(standard).isEmpty()) {
|
||||
setDefaultShortcuts(action, QKeySequence::keyBindings(standard));
|
||||
} else if (fallback != 0) {
|
||||
setDefaultShortcut(action, QKeySequence(fallback));
|
||||
}
|
||||
}
|
||||
|
||||
void ActionCollection::setDefaultShortcuts(QAction* action, const QList<QKeySequence>& shortcuts)
|
||||
{
|
||||
action->setShortcuts(shortcuts);
|
||||
action->setProperty("defaultShortcuts", QVariant::fromValue(shortcuts));
|
||||
}
|
||||
|
||||
void ActionCollection::restoreShortcuts()
|
||||
{
|
||||
const auto shortcuts = Config::instance()->getShortcuts();
|
||||
QHash<QString, QAction*> actionsByName;
|
||||
for (auto action : m_actions) {
|
||||
actionsByName.insert(action->objectName(), action);
|
||||
}
|
||||
for (const auto& shortcut : shortcuts) {
|
||||
if (actionsByName.contains(shortcut.name)) {
|
||||
const auto key = QKeySequence::fromString(shortcut.shortcut);
|
||||
actionsByName.value(shortcut.name)->setShortcut(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionCollection::saveShortcuts()
|
||||
{
|
||||
QList<Config::ShortcutEntry> shortcuts;
|
||||
shortcuts.reserve(m_actions.size());
|
||||
for (auto a : m_actions) {
|
||||
// Only store non-default shortcut assignments
|
||||
if (a->shortcut() != defaultShortcut(a)) {
|
||||
shortcuts << Config::ShortcutEntry{a->objectName(), a->shortcut().toString()};
|
||||
}
|
||||
}
|
||||
Config::instance()->setShortcuts(shortcuts);
|
||||
}
|
||||
|
||||
QAction* ActionCollection::isConflictingShortcut(const QAction* action, const QKeySequence& seq) const
|
||||
{
|
||||
// Empty sequences don't conflict with anything
|
||||
if (seq.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto a : m_actions) {
|
||||
if (a != action && a->shortcut() == seq) {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
61
src/gui/ActionCollection.h
Normal file
61
src/gui/ActionCollection.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_ACTION_COLLECTION_H
|
||||
#define KEEPASSXC_ACTION_COLLECTION_H
|
||||
|
||||
#include <QAction>
|
||||
#include <QKeySequence>
|
||||
#include <QObject>
|
||||
|
||||
/**
|
||||
* This class manages all actions that are shortcut configurable.
|
||||
* It also allows you to access the actions inside it from anywhere
|
||||
* in the gui code.
|
||||
*/
|
||||
class ActionCollection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
ActionCollection() = default;
|
||||
|
||||
public:
|
||||
static ActionCollection* instance();
|
||||
|
||||
QList<QAction*> actions() const;
|
||||
|
||||
void addAction(QAction* action);
|
||||
void addActions(const QList<QAction*>& actions);
|
||||
|
||||
QKeySequence defaultShortcut(const QAction* a) const;
|
||||
QList<QKeySequence> defaultShortcuts(const QAction* a) const;
|
||||
|
||||
void setDefaultShortcut(QAction* a, const QKeySequence& shortcut);
|
||||
void setDefaultShortcut(QAction* a, QKeySequence::StandardKey standard, const QKeySequence& fallback);
|
||||
void setDefaultShortcuts(QAction* a, const QList<QKeySequence>& shortcut);
|
||||
|
||||
// Check if any action conflicts with @p seq and return the conflicting action
|
||||
QAction* isConflictingShortcut(const QAction* action, const QKeySequence& seq) const;
|
||||
|
||||
public slots:
|
||||
void restoreShortcuts();
|
||||
void saveShortcuts();
|
||||
|
||||
private:
|
||||
QList<QAction*> m_actions;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -21,6 +21,7 @@
|
|||
#include "ui_ApplicationSettingsWidgetSecurity.h"
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QToolTip>
|
||||
|
||||
#include "config-keepassx.h"
|
||||
|
||||
|
@ -138,6 +139,22 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
|
|||
m_secUi->lockDatabaseMinimizeCheckBox->setEnabled(!state);
|
||||
});
|
||||
|
||||
// Set Auto-Type shortcut when changed
|
||||
connect(
|
||||
m_generalUi->autoTypeShortcutWidget, &ShortcutWidget::shortcutChanged, this, [this](auto key, auto modifiers) {
|
||||
QString error;
|
||||
if (autoType()->registerGlobalShortcut(key, modifiers, &error)) {
|
||||
m_generalUi->autoTypeShortcutWidget->setStyleSheet("");
|
||||
} else {
|
||||
QToolTip::showText(mapToGlobal(rect().bottomLeft()), error);
|
||||
m_generalUi->autoTypeShortcutWidget->setStyleSheet("background-color: #FF9696;");
|
||||
}
|
||||
});
|
||||
connect(m_generalUi->autoTypeShortcutWidget, &ShortcutWidget::shortcutReset, this, [this] {
|
||||
autoType()->unregisterGlobalShortcut();
|
||||
m_generalUi->autoTypeShortcutWidget->setStyleSheet("");
|
||||
});
|
||||
|
||||
// Disable mouse wheel grab when scrolling
|
||||
// This prevents combo box and spinner values from changing without explicit focus
|
||||
auto mouseWheelFilter = new MouseWheelEventFilter(this);
|
||||
|
|
|
@ -58,8 +58,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>564</width>
|
||||
<height>930</height>
|
||||
<width>566</width>
|
||||
<height>975</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
|
@ -1260,7 +1260,7 @@
|
|||
<customwidget>
|
||||
<class>ShortcutWidget</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>autotype/ShortcutWidget.h</header>
|
||||
<header>gui/widgets/ShortcutWidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
|
|
|
@ -40,9 +40,11 @@
|
|||
#include "core/Resources.h"
|
||||
#include "core/Tools.h"
|
||||
#include "gui/AboutDialog.h"
|
||||
#include "gui/ActionCollection.h"
|
||||
#include "gui/Icons.h"
|
||||
#include "gui/MessageBox.h"
|
||||
#include "gui/SearchWidget.h"
|
||||
#include "gui/ShortcutSettingsPage.h"
|
||||
#include "gui/entry/EntryView.h"
|
||||
#include "gui/osutils/OSUtils.h"
|
||||
|
||||
|
@ -189,6 +191,11 @@ MainWindow::MainWindow()
|
|||
connect(m_ui->tabWidget, &DatabaseTabWidget::databaseUnlocked, this, &MainWindow::databaseUnlocked);
|
||||
connect(m_ui->tabWidget, &DatabaseTabWidget::activeDatabaseChanged, this, &MainWindow::activeDatabaseChanged);
|
||||
|
||||
initViewMenu();
|
||||
initActionCollection();
|
||||
|
||||
m_ui->settingsWidget->addSettingsPage(new ShortcutSettingsPage());
|
||||
|
||||
#ifdef WITH_XC_BROWSER
|
||||
m_ui->settingsWidget->addSettingsPage(new BrowserSettingsPage());
|
||||
connect(
|
||||
|
@ -201,8 +208,6 @@ MainWindow::MainWindow()
|
|||
m_ui->settingsWidget->addSettingsPage(new AgentSettingsPage());
|
||||
#endif
|
||||
|
||||
initViewMenu();
|
||||
|
||||
#if defined(WITH_XC_KEESHARE)
|
||||
KeeShare::init(this);
|
||||
m_ui->settingsWidget->addSettingsPage(new SettingsPageKeeShare(m_ui->tabWidget));
|
||||
|
@ -262,45 +267,6 @@ MainWindow::MainWindow()
|
|||
connect(m_inactivityTimer, SIGNAL(inactivityDetected()), this, SLOT(lockDatabasesAfterInactivity()));
|
||||
applySettingsChanges();
|
||||
|
||||
m_ui->actionDatabaseNew->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_N);
|
||||
setShortcut(m_ui->actionDatabaseOpen, QKeySequence::Open, Qt::CTRL + Qt::Key_O);
|
||||
setShortcut(m_ui->actionDatabaseSave, QKeySequence::Save, Qt::CTRL + Qt::Key_S);
|
||||
setShortcut(m_ui->actionDatabaseSaveAs, QKeySequence::SaveAs, Qt::CTRL + Qt::SHIFT + Qt::Key_S);
|
||||
setShortcut(m_ui->actionDatabaseClose, QKeySequence::Close, Qt::CTRL + Qt::Key_W);
|
||||
m_ui->actionDatabaseSettings->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_Comma);
|
||||
m_ui->actionReports->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_R);
|
||||
setShortcut(m_ui->actionSettings, QKeySequence::Preferences, Qt::CTRL + Qt::Key_Comma);
|
||||
m_ui->actionLockDatabase->setShortcut(Qt::CTRL + Qt::Key_L);
|
||||
m_ui->actionLockAllDatabases->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_L);
|
||||
setShortcut(m_ui->actionQuit, QKeySequence::Quit, Qt::CTRL + Qt::Key_Q);
|
||||
setShortcut(m_ui->actionEntryNew, QKeySequence::New, Qt::CTRL + Qt::Key_N);
|
||||
m_ui->actionEntryEdit->setShortcut(Qt::CTRL + Qt::Key_E);
|
||||
m_ui->actionEntryDelete->setShortcut(Qt::CTRL + Qt::Key_D);
|
||||
m_ui->actionEntryDelete->setShortcut(Qt::Key_Delete);
|
||||
m_ui->actionEntryClone->setShortcut(Qt::CTRL + Qt::Key_K);
|
||||
m_ui->actionEntryTotp->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_T);
|
||||
m_ui->actionEntryDownloadIcon->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D);
|
||||
m_ui->actionEntryCopyTotp->setShortcut(Qt::CTRL + Qt::Key_T);
|
||||
m_ui->actionEntryCopyPasswordTotp->setShortcut(Qt::CTRL + Qt::Key_Y);
|
||||
m_ui->actionEntryMoveUp->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_Up);
|
||||
m_ui->actionEntryMoveDown->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_Down);
|
||||
m_ui->actionEntryCopyUsername->setShortcut(Qt::CTRL + Qt::Key_B);
|
||||
m_ui->actionEntryCopyPassword->setShortcut(Qt::CTRL + Qt::Key_C);
|
||||
m_ui->actionEntryCopyTitle->setShortcut(Qt::CTRL + Qt::Key_I);
|
||||
m_ui->actionEntryAutoTypeSequence->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_V);
|
||||
m_ui->actionEntryOpenUrl->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_U);
|
||||
m_ui->actionEntryCopyURL->setShortcut(Qt::CTRL + Qt::Key_U);
|
||||
m_ui->actionEntryRestore->setShortcut(Qt::CTRL + Qt::Key_R);
|
||||
|
||||
// Prevent conflicts with global Mac shortcuts (force Control on all platforms)
|
||||
#ifdef Q_OS_MAC
|
||||
auto modifier = Qt::META;
|
||||
#else
|
||||
auto modifier = Qt::CTRL;
|
||||
#endif
|
||||
m_ui->actionEntryAddToAgent->setShortcut(modifier + Qt::Key_H);
|
||||
m_ui->actionEntryRemoveFromAgent->setShortcut(modifier + Qt::SHIFT + Qt::Key_H);
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
// Qt 5.10 introduced a new "feature" to hide shortcuts in context menus
|
||||
// Unfortunately, Qt::AA_DontShowShortcutsInContextMenus is broken, have to manually enable them
|
||||
|
@ -1675,15 +1641,6 @@ void MainWindow::showGroupContextMenu(const QPoint& globalPos)
|
|||
m_ui->menuGroups->popup(globalPos);
|
||||
}
|
||||
|
||||
void MainWindow::setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback)
|
||||
{
|
||||
if (!QKeySequence::keyBindings(standard).isEmpty()) {
|
||||
action->setShortcuts(standard);
|
||||
} else if (fallback != 0) {
|
||||
action->setShortcut(QKeySequence(fallback));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::applySettingsChanges()
|
||||
{
|
||||
int timeout = config()->get(Config::Security_LockDatabaseIdleSeconds).toInt() * 1000;
|
||||
|
@ -2094,6 +2051,145 @@ void MainWindow::initViewMenu()
|
|||
});
|
||||
}
|
||||
|
||||
void MainWindow::initActionCollection()
|
||||
{
|
||||
auto ac = ActionCollection::instance();
|
||||
ac->addActions({// Database Menu
|
||||
m_ui->actionDatabaseNew,
|
||||
m_ui->actionDatabaseOpen,
|
||||
m_ui->actionDatabaseSave,
|
||||
m_ui->actionDatabaseSaveAs,
|
||||
m_ui->actionDatabaseSaveBackup,
|
||||
m_ui->actionDatabaseClose,
|
||||
m_ui->actionLockDatabase,
|
||||
m_ui->actionLockAllDatabases,
|
||||
m_ui->actionDatabaseSettings,
|
||||
m_ui->actionDatabaseSecurity,
|
||||
m_ui->actionReports,
|
||||
m_ui->actionPasskeys,
|
||||
m_ui->actionDatabaseMerge,
|
||||
m_ui->actionImportPasskey,
|
||||
m_ui->actionImportCsv,
|
||||
m_ui->actionImportOpVault,
|
||||
m_ui->actionImportKeePass1,
|
||||
m_ui->actionExportCsv,
|
||||
m_ui->actionExportHtml,
|
||||
m_ui->actionExportXML,
|
||||
m_ui->actionQuit,
|
||||
// Entry Menu
|
||||
m_ui->actionEntryNew,
|
||||
m_ui->actionEntryEdit,
|
||||
m_ui->actionEntryClone,
|
||||
m_ui->actionEntryDelete,
|
||||
m_ui->actionEntryCopyUsername,
|
||||
m_ui->actionEntryCopyPassword,
|
||||
m_ui->actionEntryCopyURL,
|
||||
m_ui->actionEntryCopyTitle,
|
||||
m_ui->actionEntryCopyNotes,
|
||||
m_ui->actionEntryTotp,
|
||||
m_ui->actionEntryTotpQRCode,
|
||||
m_ui->actionEntrySetupTotp,
|
||||
m_ui->actionEntryCopyTotp,
|
||||
m_ui->actionEntryCopyPasswordTotp,
|
||||
m_ui->actionEntryAutoTypeSequence,
|
||||
m_ui->actionEntryAutoTypeUsername,
|
||||
m_ui->actionEntryAutoTypeUsernameEnter,
|
||||
m_ui->actionEntryAutoTypePassword,
|
||||
m_ui->actionEntryAutoTypePasswordEnter,
|
||||
m_ui->actionEntryAutoTypeTOTP,
|
||||
m_ui->actionEntryDownloadIcon,
|
||||
m_ui->actionEntryOpenUrl,
|
||||
m_ui->actionEntryMoveUp,
|
||||
m_ui->actionEntryMoveDown,
|
||||
m_ui->actionEntryAddToAgent,
|
||||
m_ui->actionEntryRemoveFromAgent,
|
||||
m_ui->actionEntryRestore,
|
||||
// Group Menu
|
||||
m_ui->actionGroupNew,
|
||||
m_ui->actionGroupEdit,
|
||||
m_ui->actionGroupClone,
|
||||
m_ui->actionGroupDelete,
|
||||
m_ui->actionGroupDownloadFavicons,
|
||||
m_ui->actionGroupSortAsc,
|
||||
m_ui->actionGroupSortDesc,
|
||||
m_ui->actionGroupEmptyRecycleBin,
|
||||
// Tools Menu
|
||||
m_ui->actionPasswordGenerator,
|
||||
m_ui->actionSettings,
|
||||
// View Menu
|
||||
m_ui->actionThemeAuto,
|
||||
m_ui->actionThemeLight,
|
||||
m_ui->actionThemeDark,
|
||||
m_ui->actionThemeClassic,
|
||||
m_ui->actionCompactMode,
|
||||
m_ui->actionShowToolbar,
|
||||
m_ui->actionShowPreviewPanel,
|
||||
m_ui->actionAllowScreenCapture,
|
||||
m_ui->actionAlwaysOnTop,
|
||||
m_ui->actionHideUsernames,
|
||||
m_ui->actionHidePasswords,
|
||||
// Help Menu
|
||||
m_ui->actionGettingStarted,
|
||||
m_ui->actionUserGuide,
|
||||
m_ui->actionKeyboardShortcuts,
|
||||
m_ui->actionOnlineHelp,
|
||||
m_ui->actionCheckForUpdates,
|
||||
m_ui->actionDonate,
|
||||
m_ui->actionBugReport,
|
||||
m_ui->actionAbout});
|
||||
|
||||
// Add actions whose shortcuts were set in the .ui file
|
||||
for (const auto action : ac->actions()) {
|
||||
if (!action->shortcut().isEmpty()) {
|
||||
ac->setDefaultShortcut(action, action->shortcut());
|
||||
}
|
||||
}
|
||||
|
||||
// Actions with standard shortcuts
|
||||
ac->setDefaultShortcut(m_ui->actionDatabaseOpen, QKeySequence::Open, Qt::CTRL + Qt::Key_O);
|
||||
ac->setDefaultShortcut(m_ui->actionDatabaseSave, QKeySequence::Save, Qt::CTRL + Qt::Key_S);
|
||||
ac->setDefaultShortcut(m_ui->actionDatabaseSaveAs, QKeySequence::SaveAs, Qt::CTRL + Qt::SHIFT + Qt::Key_S);
|
||||
ac->setDefaultShortcut(m_ui->actionDatabaseClose, QKeySequence::Close, Qt::CTRL + Qt::Key_W);
|
||||
ac->setDefaultShortcut(m_ui->actionSettings, QKeySequence::Preferences, Qt::CTRL + Qt::Key_Comma);
|
||||
ac->setDefaultShortcut(m_ui->actionQuit, QKeySequence::Quit, Qt::CTRL + Qt::Key_Q);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryNew, QKeySequence::New, Qt::CTRL + Qt::Key_N);
|
||||
|
||||
// Prevent conflicts with global Mac shortcuts (force Control on all platforms)
|
||||
#ifdef Q_OS_MAC
|
||||
auto modifier = Qt::META;
|
||||
#else
|
||||
auto modifier = Qt::CTRL;
|
||||
#endif
|
||||
|
||||
// All other actions with default shortcuts
|
||||
ac->setDefaultShortcut(m_ui->actionDatabaseNew, Qt::CTRL + Qt::SHIFT + Qt::Key_N);
|
||||
ac->setDefaultShortcut(m_ui->actionDatabaseSettings, Qt::CTRL + Qt::SHIFT + Qt::Key_Comma);
|
||||
ac->setDefaultShortcut(m_ui->actionReports, Qt::CTRL + Qt::SHIFT + Qt::Key_R);
|
||||
ac->setDefaultShortcut(m_ui->actionLockDatabase, Qt::CTRL + Qt::Key_L);
|
||||
ac->setDefaultShortcut(m_ui->actionLockAllDatabases, Qt::CTRL + Qt::SHIFT + Qt::Key_L);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryEdit, Qt::CTRL + Qt::Key_E);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryDelete, Qt::CTRL + Qt::Key_D);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryDelete, Qt::Key_Delete);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryClone, Qt::CTRL + Qt::Key_K);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryTotp, Qt::CTRL + Qt::SHIFT + Qt::Key_T);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryDownloadIcon, Qt::CTRL + Qt::SHIFT + Qt::Key_D);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryCopyTotp, Qt::CTRL + Qt::Key_T);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryCopyPasswordTotp, Qt::CTRL + Qt::Key_Y);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryMoveUp, Qt::CTRL + Qt::ALT + Qt::Key_Up);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryMoveDown, Qt::CTRL + Qt::ALT + Qt::Key_Down);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryCopyUsername, Qt::CTRL + Qt::Key_B);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryCopyPassword, Qt::CTRL + Qt::Key_C);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryCopyTitle, Qt::CTRL + Qt::Key_I);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryAutoTypeSequence, Qt::CTRL + Qt::SHIFT + Qt::Key_V);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryOpenUrl, Qt::CTRL + Qt::SHIFT + Qt::Key_U);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryCopyURL, Qt::CTRL + Qt::Key_U);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryRestore, Qt::CTRL + Qt::Key_R);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryAddToAgent, modifier + Qt::Key_H);
|
||||
ac->setDefaultShortcut(m_ui->actionEntryRemoveFromAgent, modifier + Qt::SHIFT + Qt::Key_H);
|
||||
|
||||
QTimer::singleShot(1, ac, &ActionCollection::restoreShortcuts);
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
|
||||
MainWindowEventFilter::MainWindowEventFilter(QObject* parent)
|
||||
|
|
|
@ -154,8 +154,6 @@ private slots:
|
|||
void focusSearchWidget();
|
||||
|
||||
private:
|
||||
static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0);
|
||||
|
||||
static const QString BaseWindowTitle;
|
||||
|
||||
void saveWindowInformation();
|
||||
|
@ -168,6 +166,7 @@ private:
|
|||
void dropEvent(QDropEvent* event) override;
|
||||
|
||||
void initViewMenu();
|
||||
void initActionCollection();
|
||||
|
||||
const QScopedPointer<Ui::MainWindow> m_ui;
|
||||
SignalMultiplexer m_actionMultiplexer;
|
||||
|
|
|
@ -443,6 +443,9 @@
|
|||
<property name="text">
|
||||
<string>&Quit</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Quit Application</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::QuitRole</enum>
|
||||
</property>
|
||||
|
@ -451,6 +454,9 @@
|
|||
<property name="text">
|
||||
<string>&About</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open About Dialog</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::AboutRole</enum>
|
||||
</property>
|
||||
|
@ -467,6 +473,9 @@
|
|||
<property name="text">
|
||||
<string>&Open Database…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open Database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseSave">
|
||||
<property name="enabled">
|
||||
|
@ -489,7 +498,7 @@
|
|||
<string>&New Database…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Create a new database</string>
|
||||
<string>Create Database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseMerge">
|
||||
|
@ -497,7 +506,7 @@
|
|||
<string>&Merge From Database…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Merge from another KDBX database</string>
|
||||
<string>Merge From Database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryNew">
|
||||
|
@ -508,7 +517,7 @@
|
|||
<string>&New Entry…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add a new entry</string>
|
||||
<string>Create Entry</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryEdit">
|
||||
|
@ -519,7 +528,7 @@
|
|||
<string>&Edit Entry…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>View or edit entry</string>
|
||||
<string>Edit Entry</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryDelete">
|
||||
|
@ -529,6 +538,9 @@
|
|||
<property name="text">
|
||||
<string>&Delete Entry…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete Entry</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupNew">
|
||||
<property name="enabled">
|
||||
|
@ -538,7 +550,7 @@
|
|||
<string>&New Group…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add a new group</string>
|
||||
<string>Create Group</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupEdit">
|
||||
|
@ -548,6 +560,9 @@
|
|||
<property name="text">
|
||||
<string>&Edit Group…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Edit Group</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupDelete">
|
||||
<property name="enabled">
|
||||
|
@ -556,6 +571,9 @@
|
|||
<property name="text">
|
||||
<string>&Delete Group…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete Group</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupDownloadFavicons">
|
||||
<property name="enabled">
|
||||
|
@ -564,6 +582,9 @@
|
|||
<property name="text">
|
||||
<string>Download All &Favicons…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Download All Favicons</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupSortAsc">
|
||||
<property name="enabled">
|
||||
|
@ -572,6 +593,9 @@
|
|||
<property name="text">
|
||||
<string>Sort &A-Z</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Sort Groups A-Z</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupSortDesc">
|
||||
<property name="enabled">
|
||||
|
@ -580,6 +604,9 @@
|
|||
<property name="text">
|
||||
<string>Sort &Z-A</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Sort Groups Z-A</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseSaveAs">
|
||||
<property name="enabled">
|
||||
|
@ -588,6 +615,9 @@
|
|||
<property name="text">
|
||||
<string>Sa&ve Database As…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Save Database As</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseSecurity">
|
||||
<property name="enabled">
|
||||
|
@ -596,6 +626,9 @@
|
|||
<property name="text">
|
||||
<string>Database &Security…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show Database Security</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionReports">
|
||||
<property name="enabled">
|
||||
|
@ -605,7 +638,7 @@
|
|||
<string>Database &Reports…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Statistics, health check, etc.</string>
|
||||
<string>Show Database Reports</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
|
@ -619,7 +652,7 @@
|
|||
<string>&Database Settings…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Database settings</string>
|
||||
<string>Show Database Settings</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
|
@ -633,7 +666,7 @@
|
|||
<string>Passkeys…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Passkeys</string>
|
||||
<string>Show Passkeys</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
|
@ -660,6 +693,9 @@
|
|||
<property name="text">
|
||||
<string>&Clone Entry…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Clone Entry</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryMoveUp">
|
||||
<property name="enabled">
|
||||
|
@ -669,7 +705,7 @@
|
|||
<string>Move u&p</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move entry one step up</string>
|
||||
<string>Move Entry Up</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryMoveDown">
|
||||
|
@ -680,7 +716,7 @@
|
|||
<string>Move do&wn</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move entry one step down</string>
|
||||
<string>Move Entry Down</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyUsername">
|
||||
|
@ -691,7 +727,7 @@
|
|||
<string>Copy &Username</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy username to clipboard</string>
|
||||
<string>Copy Username</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyPassword">
|
||||
|
@ -702,7 +738,7 @@
|
|||
<string>Copy &Password</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy password to clipboard</string>
|
||||
<string>Copy Password</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSettings">
|
||||
|
@ -712,6 +748,9 @@
|
|||
<property name="text">
|
||||
<string>&Settings</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show Application Settings</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::PreferencesRole</enum>
|
||||
</property>
|
||||
|
@ -723,6 +762,9 @@
|
|||
<property name="text">
|
||||
<string>&Password Generator</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show Password Generator</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAutoType">
|
||||
<property name="enabled">
|
||||
|
@ -751,7 +793,7 @@
|
|||
<string notr="true">{USERNAME}</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string notr="true">{USERNAME}</string>
|
||||
<string>Perform Auto-Type: {USERNAME}</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAutoTypeUsernameEnter">
|
||||
|
@ -765,7 +807,7 @@
|
|||
<string notr="true">{USERNAME}{ENTER}</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string notr="true">{USERNAME}{ENTER}</string>
|
||||
<string>Perform Auto-Type: {USERNAME}{ENTER}</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAutoTypePassword">
|
||||
|
@ -779,7 +821,7 @@
|
|||
<string notr="true">{PASSWORD}</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string notr="true">{PASSWORD}</string>
|
||||
<string>Perform Auto-Type: {PASSWORD}</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAutoTypePasswordEnter">
|
||||
|
@ -793,7 +835,7 @@
|
|||
<string notr="true">{PASSWORD}{ENTER}</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string notr="true">{PASSWORD}{ENTER}</string>
|
||||
<string>Perform Auto-Type: {PASSWORD}{ENTER}</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAutoTypeTOTP">
|
||||
|
@ -807,7 +849,7 @@
|
|||
<string notr="true">{TOTP}</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string notr="true">{TOTP}</string>
|
||||
<string>Perform Auto-Type: {TOTP}</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryDownloadIcon">
|
||||
|
@ -847,7 +889,7 @@
|
|||
<string>&Title</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy title to clipboard</string>
|
||||
<string>Copy Title</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyURL">
|
||||
|
@ -858,7 +900,7 @@
|
|||
<string>Copy &URL</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy URL to clipboard</string>
|
||||
<string>Copy URL</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyNotes">
|
||||
|
@ -869,7 +911,7 @@
|
|||
<string>&Notes</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy notes to clipboard</string>
|
||||
<string>Copy Notes</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExportCsv">
|
||||
|
@ -879,6 +921,9 @@
|
|||
<property name="text">
|
||||
<string>&CSV File…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Export to CSV</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExportHtml">
|
||||
<property name="enabled">
|
||||
|
@ -887,13 +932,16 @@
|
|||
<property name="text">
|
||||
<string>&HTML File…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Export to HTML</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImportKeePass1">
|
||||
<property name="text">
|
||||
<string>KeePass 1 Database…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Import a KeePass 1 database</string>
|
||||
<string>Import KeePass1 Database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImportOpVault">
|
||||
|
@ -901,7 +949,7 @@
|
|||
<string>1Password Vault…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Import a 1Password Vault</string>
|
||||
<string>Import 1Password Vault</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImportCsv">
|
||||
|
@ -909,7 +957,7 @@
|
|||
<string>CSV File…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Import a CSV file</string>
|
||||
<string>Import CSV File</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryTotp">
|
||||
|
@ -921,11 +969,17 @@
|
|||
<property name="text">
|
||||
<string>Show QR Code</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show TOTP QR Code</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntrySetupTotp">
|
||||
<property name="text">
|
||||
<string>Set up TOTP…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Set up TOTP</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyTotp">
|
||||
<property name="text">
|
||||
|
@ -941,6 +995,9 @@
|
|||
<property name="text">
|
||||
<string>E&mpty recycle bin</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Empty Recycle Bin</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
|
@ -949,11 +1006,17 @@
|
|||
<property name="text">
|
||||
<string>&Donate</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open Donation Website</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionBugReport">
|
||||
<property name="text">
|
||||
<string>Report a &Bug</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open Bug Report</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGettingStarted">
|
||||
<property name="text">
|
||||
|
@ -968,7 +1031,7 @@
|
|||
<string>&Online Help</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Go to online documentation</string>
|
||||
<string>Open Online Documentation</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUserGuide">
|
||||
|
@ -983,6 +1046,9 @@
|
|||
<property name="text">
|
||||
<string>&Keyboard Shortcuts</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open Keyboard Shortcuts Guide</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+/</string>
|
||||
</property>
|
||||
|
@ -994,16 +1060,25 @@
|
|||
<property name="text">
|
||||
<string>Save Database Backup…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Save Database Backup</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAddToAgent">
|
||||
<property name="text">
|
||||
<string>Add key to SSH Agent</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>SSH Agent: Add Key</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryRemoveFromAgent">
|
||||
<property name="text">
|
||||
<string>Remove key from SSH Agent</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>SSH Agent: Remove Key</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCompactMode">
|
||||
<property name="checkable">
|
||||
|
@ -1012,6 +1087,9 @@
|
|||
<property name="text">
|
||||
<string>Compact Mode</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Toggle Compact Mode</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionThemeAuto">
|
||||
<property name="checkable">
|
||||
|
@ -1023,6 +1101,9 @@
|
|||
<property name="text">
|
||||
<string>Automatic</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Set Theme: Automatic</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionThemeLight">
|
||||
<property name="checkable">
|
||||
|
@ -1031,6 +1112,9 @@
|
|||
<property name="text">
|
||||
<string>Light</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Set Theme: Light</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionThemeDark">
|
||||
<property name="checkable">
|
||||
|
@ -1039,6 +1123,9 @@
|
|||
<property name="text">
|
||||
<string>Dark</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Set Theme: Dark</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionThemeClassic">
|
||||
<property name="checkable">
|
||||
|
@ -1047,6 +1134,9 @@
|
|||
<property name="text">
|
||||
<string>Classic (Platform-native)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Set Theme: Classic</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionShowToolbar">
|
||||
<property name="checkable">
|
||||
|
@ -1058,6 +1148,9 @@
|
|||
<property name="text">
|
||||
<string>Show Toolbar</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Toggle Show Toolbar</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionShowPreviewPanel">
|
||||
<property name="checkable">
|
||||
|
@ -1069,6 +1162,9 @@
|
|||
<property name="text">
|
||||
<string>Show Preview Panel</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Toggle Show Preview Panel</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAlwaysOnTop">
|
||||
<property name="checkable">
|
||||
|
@ -1077,6 +1173,9 @@
|
|||
<property name="text">
|
||||
<string>Always on Top</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Toggle Always on Top</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Shift+A</string>
|
||||
</property>
|
||||
|
@ -1088,6 +1187,9 @@
|
|||
<property name="text">
|
||||
<string>Hide Usernames</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Toggle Hide Usernames</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Shift+B</string>
|
||||
</property>
|
||||
|
@ -1102,6 +1204,9 @@
|
|||
<property name="text">
|
||||
<string>Hide Passwords</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Toggle Hide Passwords</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Shift+C</string>
|
||||
</property>
|
||||
|
@ -1114,7 +1219,7 @@
|
|||
<string notr="true">{USERNAME}{TAB}{PASSWORD}{ENTER}</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string notr="true">{USERNAME}{TAB}{PASSWORD}{ENTER}</string>
|
||||
<string notr="true">Perform Auto-Type: Entry Default</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupClone">
|
||||
|
@ -1130,7 +1235,7 @@
|
|||
<string notr="true" extracomment="Translatable string with plural form set in CPP file">Restore Entry(s)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string notr="true" extracomment="Translatable string with plural form set in CPP file">Restore Entry(s)</string>
|
||||
<string notr="true" extracomment="Translatable string with plural form set in CPP file">Restore Entry</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+R</string>
|
||||
|
@ -1152,7 +1257,7 @@
|
|||
<string>&XML File…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>XML File…</string>
|
||||
<string>Export to XML</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAllowScreenCapture">
|
||||
|
@ -1162,6 +1267,9 @@
|
|||
<property name="text">
|
||||
<string>Allow Screen Capture</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Toggle Allow Screen Capture</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
|
282
src/gui/ShortcutSettingsPage.cpp
Normal file
282
src/gui/ShortcutSettingsPage.cpp
Normal file
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ShortcutSettingsPage.h"
|
||||
|
||||
#include "core/Config.h"
|
||||
#include "gui/ActionCollection.h"
|
||||
#include "gui/Icons.h"
|
||||
#include "gui/MessageBox.h"
|
||||
#include "gui/widgets/ShortcutWidget.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QDebug>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStandardItemModel>
|
||||
#include <QTableView>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class KeySequenceDialog final : public QDialog
|
||||
{
|
||||
public:
|
||||
explicit KeySequenceDialog(QWidget* parent = nullptr)
|
||||
: QDialog(parent)
|
||||
, m_keySeqEdit(new ShortcutWidget(this))
|
||||
, m_btnBox(new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel
|
||||
| QDialogButtonBox::RestoreDefaults,
|
||||
this))
|
||||
{
|
||||
auto* l = new QVBoxLayout(this);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
connect(m_btnBox, &QDialogButtonBox::clicked, this, &KeySequenceDialog::restoreDefault);
|
||||
|
||||
auto hLayout = new QHBoxLayout();
|
||||
l->addLayout(hLayout);
|
||||
hLayout->addWidget(new QLabel(QObject::tr("Enter Shortcut")));
|
||||
hLayout->addWidget(m_keySeqEdit);
|
||||
|
||||
l->addStretch();
|
||||
l->addWidget(m_btnBox);
|
||||
|
||||
setFocusProxy(m_keySeqEdit);
|
||||
}
|
||||
|
||||
QKeySequence keySequence() const
|
||||
{
|
||||
return m_keySeqEdit->sequence();
|
||||
}
|
||||
|
||||
bool shouldRestoreDefault() const
|
||||
{
|
||||
return m_restoreDefault;
|
||||
}
|
||||
|
||||
private:
|
||||
void restoreDefault(QAbstractButton* btn)
|
||||
{
|
||||
if (m_btnBox->standardButton(btn) == QDialogButtonBox::RestoreDefaults) {
|
||||
m_restoreDefault = true;
|
||||
reject();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_restoreDefault = false;
|
||||
ShortcutWidget* const m_keySeqEdit;
|
||||
QDialogButtonBox* const m_btnBox;
|
||||
};
|
||||
|
||||
class ShortcutSettingsWidget final : public QWidget
|
||||
{
|
||||
public:
|
||||
explicit ShortcutSettingsWidget(QWidget* parent = nullptr)
|
||||
: QWidget(parent)
|
||||
, m_tableView(new QTableView(this))
|
||||
, m_filterLineEdit(new QLineEdit(this))
|
||||
, m_resetShortcutsButton(new QPushButton(tr("Reset Shortcuts"), this))
|
||||
{
|
||||
auto h = new QHBoxLayout();
|
||||
h->addWidget(m_filterLineEdit);
|
||||
h->addWidget(m_resetShortcutsButton);
|
||||
h->setStretch(0, 1);
|
||||
|
||||
auto l = new QVBoxLayout(this);
|
||||
l->addWidget(new QLabel(tr("Double click an action to change its shortcut")));
|
||||
l->addLayout(h);
|
||||
l->addWidget(m_tableView);
|
||||
|
||||
m_model.setColumnCount(2);
|
||||
m_model.setHorizontalHeaderLabels({QObject::tr("Action"), QObject::tr("Shortcuts")});
|
||||
|
||||
m_proxy.setFilterKeyColumn(-1);
|
||||
m_proxy.setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
m_proxy.setSourceModel(&m_model);
|
||||
|
||||
m_filterLineEdit->setPlaceholderText(tr("Filter..."));
|
||||
connect(m_filterLineEdit, &QLineEdit::textChanged, &m_proxy, &QSortFilterProxyModel::setFilterFixedString);
|
||||
|
||||
connect(m_resetShortcutsButton, &QPushButton::clicked, this, [this]() {
|
||||
auto ac = ActionCollection::instance();
|
||||
for (auto action : ac->actions()) {
|
||||
action->setShortcut(ac->defaultShortcut(action));
|
||||
}
|
||||
loadSettings();
|
||||
});
|
||||
|
||||
m_tableView->setModel(&m_proxy);
|
||||
m_tableView->setSortingEnabled(true);
|
||||
m_tableView->sortByColumn(0, Qt::AscendingOrder);
|
||||
m_tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||
m_tableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
m_tableView->verticalHeader()->hide();
|
||||
m_tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
m_tableView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
|
||||
connect(m_tableView, &QTableView::doubleClicked, this, &ShortcutSettingsWidget::onDoubleClicked);
|
||||
}
|
||||
|
||||
void loadSettings()
|
||||
{
|
||||
m_changedActions.clear();
|
||||
m_filterLineEdit->clear();
|
||||
m_model.setRowCount(0);
|
||||
const auto& actions = ActionCollection::instance()->actions();
|
||||
for (auto a : actions) {
|
||||
auto name = a->toolTip().isEmpty() ? acceleratorsStrippedText(a->text()) : a->toolTip();
|
||||
auto col1 = new QStandardItem(name);
|
||||
col1->setData(QVariant::fromValue(a), Qt::UserRole);
|
||||
auto col2 = new QStandardItem(a->shortcut().toString());
|
||||
m_model.appendRow({col1, col2});
|
||||
}
|
||||
}
|
||||
|
||||
void saveSettings()
|
||||
{
|
||||
if (m_changedActions.count()) {
|
||||
for (const auto& action : m_changedActions.keys()) {
|
||||
action->setShortcut(m_changedActions.value(action));
|
||||
}
|
||||
ActionCollection::instance()->saveShortcuts();
|
||||
}
|
||||
m_changedActions.clear();
|
||||
m_filterLineEdit->clear();
|
||||
}
|
||||
|
||||
private:
|
||||
static QString acceleratorsStrippedText(QString text)
|
||||
{
|
||||
for (int i = 0; i < text.size(); ++i) {
|
||||
if (text.at(i) == QLatin1Char('&') && i + 1 < text.size() && text.at(i + 1) != QLatin1Char('&')) {
|
||||
text.remove(i, 1);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
void onDoubleClicked(QModelIndex index)
|
||||
{
|
||||
if (index.column() != 0) {
|
||||
index = index.sibling(index.row(), 0);
|
||||
}
|
||||
index = m_proxy.mapToSource(index);
|
||||
auto action = index.data(Qt::UserRole).value<QAction*>();
|
||||
|
||||
KeySequenceDialog dialog(this);
|
||||
int ret = dialog.exec();
|
||||
|
||||
QKeySequence change;
|
||||
if (ret == QDialog::Accepted) {
|
||||
change = dialog.keySequence();
|
||||
} else if (dialog.shouldRestoreDefault()) {
|
||||
change = ActionCollection::instance()->defaultShortcut(action);
|
||||
} else {
|
||||
// Rejected
|
||||
return;
|
||||
}
|
||||
|
||||
auto conflict = ActionCollection::instance()->isConflictingShortcut(action, change);
|
||||
bool hasConflict = false;
|
||||
if (conflict) {
|
||||
// we conflicted with an action inside action collection
|
||||
// check if the conflicted action is updated here
|
||||
if (!m_changedActions.contains(conflict)) {
|
||||
hasConflict = true;
|
||||
} else {
|
||||
if (m_changedActions.value(conflict) == change) {
|
||||
hasConflict = true;
|
||||
}
|
||||
}
|
||||
} else if (!change.isEmpty()) {
|
||||
// we did not conflict with any shortcut inside action collection
|
||||
// check if we conflict with any locally modified action
|
||||
for (auto chAction : m_changedActions.keys()) {
|
||||
if (m_changedActions.value(chAction) == change) {
|
||||
hasConflict = true;
|
||||
conflict = chAction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasConflict) {
|
||||
auto conflictName =
|
||||
conflict->toolTip().isEmpty() ? acceleratorsStrippedText(conflict->text()) : conflict->toolTip();
|
||||
auto conflictSeq = change.toString();
|
||||
|
||||
auto ans = MessageBox::question(
|
||||
this,
|
||||
tr("Shortcut Conflict"),
|
||||
tr("Shortcut %1 conflicts with '%2'. Overwrite shortcut?").arg(conflictSeq, conflictName),
|
||||
MessageBox::Overwrite | MessageBox::Discard,
|
||||
MessageBox::Discard);
|
||||
if (ans == MessageBox::Discard) {
|
||||
// Bail out before making any changes
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the conflict shortcut
|
||||
m_changedActions[conflict] = {};
|
||||
for (auto item : m_model.findItems(conflictSeq, Qt::MatchExactly, 1)) {
|
||||
item->setText("");
|
||||
}
|
||||
}
|
||||
|
||||
m_changedActions[action] = change;
|
||||
auto item = m_model.itemFromIndex(index.sibling(index.row(), 1));
|
||||
item->setText(change.toString());
|
||||
}
|
||||
|
||||
QTableView* m_tableView;
|
||||
QLineEdit* m_filterLineEdit;
|
||||
QPushButton* m_resetShortcutsButton;
|
||||
QStandardItemModel m_model;
|
||||
QSortFilterProxyModel m_proxy;
|
||||
QHash<QAction*, QKeySequence> m_changedActions;
|
||||
};
|
||||
|
||||
QString ShortcutSettingsPage::name()
|
||||
{
|
||||
return QObject::tr("Shortcuts");
|
||||
}
|
||||
|
||||
QIcon ShortcutSettingsPage::icon()
|
||||
{
|
||||
return icons()->icon("auto-type");
|
||||
}
|
||||
|
||||
QWidget* ShortcutSettingsPage::createWidget()
|
||||
{
|
||||
return new ShortcutSettingsWidget();
|
||||
}
|
||||
|
||||
void ShortcutSettingsPage::loadSettings(QWidget* widget)
|
||||
{
|
||||
static_cast<ShortcutSettingsWidget*>(widget)->loadSettings();
|
||||
}
|
||||
|
||||
void ShortcutSettingsPage::saveSettings(QWidget* widget)
|
||||
{
|
||||
static_cast<ShortcutSettingsWidget*>(widget)->saveSettings();
|
||||
}
|
36
src/gui/ShortcutSettingsPage.h
Normal file
36
src/gui/ShortcutSettingsPage.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_SHORTCUT_SETTINGSPAGE_H
|
||||
#define KEEPASSXC_SHORTCUT_SETTINGSPAGE_H
|
||||
|
||||
#include "gui/ApplicationSettingsWidget.h"
|
||||
|
||||
class ShortcutSettingsPage : public ISettingsPage
|
||||
{
|
||||
public:
|
||||
explicit ShortcutSettingsPage() = default;
|
||||
~ShortcutSettingsPage() override = default;
|
||||
|
||||
QString name() override;
|
||||
QIcon icon() override;
|
||||
QWidget* createWidget() override;
|
||||
void loadSettings(QWidget* widget) override;
|
||||
void saveSettings(QWidget* widget) override;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_BROWSERSETTINGSPAGE_H
|
|
@ -20,13 +20,8 @@
|
|||
#include <QKeyEvent>
|
||||
#include <QToolTip>
|
||||
|
||||
#include "autotype/AutoType.h"
|
||||
|
||||
ShortcutWidget::ShortcutWidget(QWidget* parent)
|
||||
: QLineEdit(parent)
|
||||
, m_key(static_cast<Qt::Key>(0))
|
||||
, m_modifiers(nullptr)
|
||||
, m_locked(false)
|
||||
{
|
||||
setReadOnly(true);
|
||||
}
|
||||
|
@ -41,6 +36,11 @@ Qt::KeyboardModifiers ShortcutWidget::modifiers() const
|
|||
return m_modifiers;
|
||||
}
|
||||
|
||||
QKeySequence ShortcutWidget::sequence() const
|
||||
{
|
||||
return (m_key == Qt::Key_unknown) ? QKeySequence() : QKeySequence(m_key | m_modifiers);
|
||||
}
|
||||
|
||||
void ShortcutWidget::setShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
m_key = key;
|
||||
|
@ -48,22 +48,15 @@ void ShortcutWidget::setShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
|||
m_locked = true;
|
||||
|
||||
displayShortcut(m_key, m_modifiers);
|
||||
|
||||
QString error;
|
||||
if (autoType()->registerGlobalShortcut(m_key, m_modifiers, &error)) {
|
||||
setStyleSheet("");
|
||||
} else {
|
||||
QToolTip::showText(mapToGlobal(rect().bottomLeft()), error);
|
||||
setStyleSheet("background-color: #FF9696;");
|
||||
}
|
||||
emit shortcutChanged(m_key, m_modifiers);
|
||||
}
|
||||
|
||||
void ShortcutWidget::resetShortcut()
|
||||
{
|
||||
m_key = static_cast<Qt::Key>(0);
|
||||
m_modifiers = nullptr;
|
||||
m_key = Qt::Key_unknown;
|
||||
m_modifiers = Qt::NoModifier;
|
||||
m_locked = false;
|
||||
autoType()->unregisterGlobalShortcut();
|
||||
emit shortcutReset();
|
||||
}
|
||||
|
||||
void ShortcutWidget::keyPressEvent(QKeyEvent* event)
|
||||
|
@ -116,13 +109,11 @@ void ShortcutWidget::keyEvent(QKeyEvent* event)
|
|||
setShortcut(key, modifiers);
|
||||
} else {
|
||||
resetShortcut();
|
||||
setStyleSheet("");
|
||||
displayShortcut(key, modifiers);
|
||||
}
|
||||
} else {
|
||||
if (m_locked) {
|
||||
resetShortcut();
|
||||
setStyleSheet("");
|
||||
}
|
||||
|
||||
displayShortcut(static_cast<Qt::Key>(0), modifiers);
|
|
@ -18,6 +18,7 @@
|
|||
#ifndef KEEPASSX_SHORTCUTWIDGET_H
|
||||
#define KEEPASSX_SHORTCUTWIDGET_H
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QLineEdit>
|
||||
|
||||
class ShortcutWidget : public QLineEdit
|
||||
|
@ -26,10 +27,17 @@ class ShortcutWidget : public QLineEdit
|
|||
|
||||
public:
|
||||
explicit ShortcutWidget(QWidget* parent = nullptr);
|
||||
|
||||
Qt::Key key() const;
|
||||
Qt::KeyboardModifiers modifiers() const;
|
||||
QKeySequence sequence() const;
|
||||
|
||||
void setShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
||||
|
||||
signals:
|
||||
void shortcutChanged(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
||||
void shortcutReset();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void keyReleaseEvent(QKeyEvent* event) override;
|
||||
|
@ -39,9 +47,9 @@ private:
|
|||
void displayShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
||||
void resetShortcut();
|
||||
|
||||
Qt::Key m_key;
|
||||
Qt::KeyboardModifiers m_modifiers;
|
||||
bool m_locked;
|
||||
Qt::Key m_key = Qt::Key_unknown;
|
||||
Qt::KeyboardModifiers m_modifiers = Qt::NoModifier;
|
||||
bool m_locked = false;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_SHORTCUTWIDGET_H
|
Loading…
Add table
Add a link
Reference in a new issue