19
#include "MainWindow.h"
20
#include "ui_MainWindow.h"
23
#include <QDesktopServices>
34
#include "config-keepassx.h"
36
#include "Application.h"
38
#include "autotype/AutoType.h"
39
#include "core/InactivityTimer.h"
40
#include "core/Resources.h"
41
#include "core/Tools.h"
42
#include "gui/AboutDialog.h"
43
#include "gui/ActionCollection.h"
45
#include "gui/MessageBox.h"
46
#include "gui/SearchWidget.h"
47
#include "gui/ShortcutSettingsPage.h"
48
#include "gui/entry/EntryView.h"
49
#include "gui/osutils/OSUtils.h"
51
#ifdef WITH_XC_UPDATECHECK
52
#include "gui/UpdateCheckDialog.h"
53
#include "updatecheck/UpdateChecker.h"
56
#ifdef WITH_XC_SSHAGENT
57
#include "sshagent/AgentSettingsPage.h"
58
#include "sshagent/SSHAgent.h"
60
#ifdef WITH_XC_KEESHARE
61
#include "keeshare/KeeShare.h"
62
#include "keeshare/SettingsPageKeeShare.h"
65
#ifdef WITH_XC_FDOSECRETS
66
#include "fdosecrets/FdoSecretsPlugin.h"
70
#include "keys/drivers/YubiKey.h"
74
#include "browser/BrowserService.h"
75
#include "browser/BrowserSettingsPage.h"
78
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS)
79
#include "mainwindowadaptor.h"
82
const QString MainWindow::BaseWindowTitle = "KeePassXC";
84
MainWindow* g_MainWindow = nullptr;
85
MainWindow* getMainWindow()
90
MainWindow::MainWindow()
91
: m_ui(new Ui::MainWindow())
97
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS)
98
new MainWindowAdaptor(this);
99
QDBusConnection dbus = QDBusConnection::sessionBus();
100
dbus.registerObject("/keepassxc", this);
101
dbus.registerService("org.keepassxc.KeePassXC.MainWindow");
104
setAcceptDrops(true);
106
if (config()->get(Config::GUI_CompactMode).toBool()) {
107
m_ui->toolBar->setIconSize({20, 20});
111
m_searchWidget = new SearchWidget();
112
m_searchWidget->connectSignals(m_actionMultiplexer);
113
m_searchWidgetAction = m_ui->toolBar->addWidget(m_searchWidget);
114
m_searchWidgetAction->setEnabled(false);
116
new QShortcut(QKeySequence::Find, this, SLOT(focusSearchWidget()));
118
connect(m_searchWidget, &SearchWidget::searchCanceled, this, [this] {
119
m_ui->toolBar->setExpanded(false);
120
m_ui->toolBar->setVisible(!config()->get(Config::GUI_HideToolbar).toBool());
122
connect(m_searchWidget, &SearchWidget::lostFocus, this, [this] {
123
m_ui->toolBar->setExpanded(false);
124
m_ui->toolBar->setVisible(!config()->get(Config::GUI_HideToolbar).toBool());
127
m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size();
129
m_entryContextMenu = new QMenu(this);
130
m_entryContextMenu->setSeparatorsCollapsible(true);
131
m_entryContextMenu->addAction(m_ui->actionEntryCopyUsername);
132
m_entryContextMenu->addAction(m_ui->actionEntryCopyPassword);
133
m_entryContextMenu->addAction(m_ui->actionEntryCopyURL);
134
m_entryContextMenu->addAction(m_ui->menuEntryCopyAttribute->menuAction());
135
m_entryContextMenu->addAction(m_ui->menuEntryTotp->menuAction());
136
m_entryContextMenu->addAction(m_ui->menuTags->menuAction());
137
m_entryContextMenu->addSeparator();
138
m_entryContextMenu->addAction(m_ui->actionEntryAutoType);
139
m_entryContextMenu->addSeparator();
140
#ifdef WITH_XC_BROWSER_PASSKEYS
141
m_entryContextMenu->addAction(m_ui->actionEntryImportPasskey);
142
m_entryContextMenu->addSeparator();
144
m_entryContextMenu->addAction(m_ui->actionEntryEdit);
145
m_entryContextMenu->addAction(m_ui->actionEntryClone);
146
m_entryContextMenu->addAction(m_ui->actionEntryDelete);
147
m_entryContextMenu->addAction(m_ui->actionEntryNew);
148
m_entryContextMenu->addSeparator();
149
m_entryContextMenu->addAction(m_ui->actionEntryMoveUp);
150
m_entryContextMenu->addAction(m_ui->actionEntryMoveDown);
151
m_entryContextMenu->addSeparator();
152
m_entryContextMenu->addAction(m_ui->actionEntryOpenUrl);
153
m_entryContextMenu->addAction(m_ui->actionEntryDownloadIcon);
154
m_entryContextMenu->addSeparator();
155
m_entryContextMenu->addAction(m_ui->actionEntryAddToAgent);
156
m_entryContextMenu->addAction(m_ui->actionEntryRemoveFromAgent);
157
m_entryContextMenu->addSeparator();
158
m_entryContextMenu->addAction(m_ui->actionEntryRestore);
160
m_entryNewContextMenu = new QMenu(this);
161
m_entryNewContextMenu->addAction(m_ui->actionEntryNew);
164
auto autotypeMenu = new QMenu({}, this);
165
autotypeMenu->addAction(m_ui->actionEntryAutoTypeSequence);
166
autotypeMenu->addSeparator();
167
autotypeMenu->addAction(m_ui->actionEntryAutoTypeUsername);
168
autotypeMenu->addAction(m_ui->actionEntryAutoTypeUsernameEnter);
169
autotypeMenu->addAction(m_ui->actionEntryAutoTypePassword);
170
autotypeMenu->addAction(m_ui->actionEntryAutoTypePasswordEnter);
171
autotypeMenu->addAction(m_ui->actionEntryAutoTypeTOTP);
172
m_ui->actionEntryAutoType->setMenu(autotypeMenu);
173
auto autoTypeButton = qobject_cast<QToolButton*>(m_ui->toolBar->widgetForAction(m_ui->actionEntryAutoType));
174
if (autoTypeButton) {
175
autoTypeButton->setPopupMode(QToolButton::MenuButtonPopup);
178
auto databaseLockMenu = new QMenu({}, this);
179
databaseLockMenu->addAction(m_ui->actionLockAllDatabases);
180
m_ui->actionLockDatabaseToolbar->setMenu(databaseLockMenu);
181
auto databaseLockButton =
182
qobject_cast<QToolButton*>(m_ui->toolBar->widgetForAction(m_ui->actionLockDatabaseToolbar));
183
if (databaseLockButton) {
184
databaseLockButton->setPopupMode(QToolButton::MenuButtonPopup);
187
restoreGeometry(config()->get(Config::GUI_MainWindowGeometry).toByteArray());
188
restoreState(config()->get(Config::GUI_MainWindowState).toByteArray());
190
connect(m_ui->tabWidget, &DatabaseTabWidget::databaseLocked, this, &MainWindow::databaseLocked);
191
connect(m_ui->tabWidget, &DatabaseTabWidget::databaseUnlocked, this, &MainWindow::databaseUnlocked);
192
connect(m_ui->tabWidget, &DatabaseTabWidget::activeDatabaseChanged, this, &MainWindow::activeDatabaseChanged);
195
initActionCollection();
197
m_ui->settingsWidget->addSettingsPage(new ShortcutSettingsPage());
199
#ifdef WITH_XC_BROWSER
200
m_ui->settingsWidget->addSettingsPage(new BrowserSettingsPage());
202
browserService(), &BrowserService::requestUnlock, m_ui->tabWidget, &DatabaseTabWidget::performBrowserUnlock);
205
#ifdef WITH_XC_SSHAGENT
206
connect(sshAgent(), SIGNAL(error(QString)), this, SLOT(showErrorMessage(QString)));
207
connect(sshAgent(), SIGNAL(enabledChanged(bool)), this, SLOT(agentEnabled(bool)));
208
m_ui->settingsWidget->addSettingsPage(new AgentSettingsPage());
211
#if defined(WITH_XC_KEESHARE)
212
KeeShare::init(this);
213
m_ui->settingsWidget->addSettingsPage(new SettingsPageKeeShare(m_ui->tabWidget));
214
connect(KeeShare::instance(),
215
SIGNAL(sharingMessage(QString, MessageWidget::MessageType)),
216
SLOT(displayGlobalMessage(QString, MessageWidget::MessageType)));
219
#ifdef WITH_XC_FDOSECRETS
220
auto fdoSS = new FdoSecretsPlugin(m_ui->tabWidget);
221
connect(fdoSS, &FdoSecretsPlugin::error, this, &MainWindow::showErrorMessage);
222
connect(fdoSS, &FdoSecretsPlugin::requestSwitchToDatabases, this, &MainWindow::switchToDatabases);
223
connect(fdoSS, &FdoSecretsPlugin::requestShowNotification, this, &MainWindow::displayDesktopNotification);
224
fdoSS->updateServiceState();
225
m_ui->settingsWidget->addSettingsPage(fdoSS);
228
#ifdef WITH_XC_YUBIKEY
229
connect(YubiKey::instance(), SIGNAL(userInteractionRequest()), SLOT(showYubiKeyPopup()), Qt::QueuedConnection);
230
connect(YubiKey::instance(), SIGNAL(challengeCompleted()), SLOT(hideYubiKeyPopup()), Qt::QueuedConnection);
233
setWindowIcon(icons()->applicationIcon());
234
m_ui->globalMessageWidget->hideMessage();
235
connect(m_ui->globalMessageWidget, &MessageWidget::linkActivated, &MessageWidget::openHttpUrl);
237
m_clearHistoryAction = new QAction(tr("Clear history"), m_ui->menuFile);
238
m_lastDatabasesActions = new QActionGroup(m_ui->menuRecentDatabases);
239
connect(m_clearHistoryAction, SIGNAL(triggered()), this, SLOT(clearLastDatabases()));
240
connect(m_lastDatabasesActions, SIGNAL(triggered(QAction*)), this, SLOT(openRecentDatabase(QAction*)));
241
connect(m_ui->menuRecentDatabases, SIGNAL(aboutToShow()), this, SLOT(updateLastDatabasesMenu()));
243
m_copyAdditionalAttributeActions = new QActionGroup(m_ui->menuEntryCopyAttribute);
244
m_actionMultiplexer.connect(
245
m_copyAdditionalAttributeActions, SIGNAL(triggered(QAction*)), SLOT(copyAttribute(QAction*)));
246
connect(m_ui->menuEntryCopyAttribute, SIGNAL(aboutToShow()), this, SLOT(updateCopyAttributesMenu()));
248
m_setTagsMenuActions = new QActionGroup(m_ui->menuTags);
249
m_setTagsMenuActions->setExclusive(false);
250
m_actionMultiplexer.connect(m_setTagsMenuActions, SIGNAL(triggered(QAction*)), SLOT(setTag(QAction*)));
251
connect(m_ui->menuTags, &QMenu::aboutToShow, this, &MainWindow::updateSetTagsMenu);
253
Qt::Key globalAutoTypeKey = static_cast<Qt::Key>(config()->get(Config::GlobalAutoTypeKey).toInt());
254
Qt::KeyboardModifiers globalAutoTypeModifiers =
255
static_cast<Qt::KeyboardModifiers>(config()->get(Config::GlobalAutoTypeModifiers).toInt());
256
if (globalAutoTypeKey > 0 && globalAutoTypeModifiers > 0) {
257
autoType()->registerGlobalShortcut(globalAutoTypeKey, globalAutoTypeModifiers);
260
m_ui->toolbarSeparator->setVisible(false);
261
m_showToolbarSeparator = config()->get(Config::GUI_ApplicationTheme).toString() != "classic";
263
m_ui->actionEntryAutoType->setVisible(autoType()->isAvailable());
264
m_ui->actionAllowScreenCapture->setVisible(osUtils->canPreventScreenCapture());
266
m_inactivityTimer = new InactivityTimer(this);
267
connect(m_inactivityTimer, SIGNAL(inactivityDetected()), this, SLOT(lockDatabasesAfterInactivity()));
268
applySettingsChanges();
270
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
273
m_ui->actionEntryNew->setShortcutVisibleInContextMenu(true);
274
m_ui->actionEntryEdit->setShortcutVisibleInContextMenu(true);
275
m_ui->actionEntryDelete->setShortcutVisibleInContextMenu(true);
276
m_ui->actionEntryRestore->setShortcutVisibleInContextMenu(true);
277
m_ui->actionEntryClone->setShortcutVisibleInContextMenu(true);
278
m_ui->actionEntryTotp->setShortcutVisibleInContextMenu(true);
279
m_ui->actionEntryDownloadIcon->setShortcutVisibleInContextMenu(true);
280
m_ui->actionEntryCopyTotp->setShortcutVisibleInContextMenu(true);
281
m_ui->actionEntryCopyPasswordTotp->setShortcutVisibleInContextMenu(true);
282
m_ui->actionEntryMoveUp->setShortcutVisibleInContextMenu(true);
283
m_ui->actionEntryMoveDown->setShortcutVisibleInContextMenu(true);
284
m_ui->actionEntryCopyUsername->setShortcutVisibleInContextMenu(true);
285
m_ui->actionEntryCopyPassword->setShortcutVisibleInContextMenu(true);
286
m_ui->actionEntryAutoTypeSequence->setShortcutVisibleInContextMenu(true);
287
m_ui->actionEntryOpenUrl->setShortcutVisibleInContextMenu(true);
288
m_ui->actionEntryCopyURL->setShortcutVisibleInContextMenu(true);
289
m_ui->actionEntryCopyTitle->setShortcutVisibleInContextMenu(true);
290
m_ui->actionEntryAddToAgent->setShortcutVisibleInContextMenu(true);
291
m_ui->actionEntryRemoveFromAgent->setShortcutVisibleInContextMenu(true);
294
connect(m_ui->menuEntries, SIGNAL(aboutToShow()), SLOT(obtainContextFocusLock()));
295
connect(m_ui->menuEntries, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock()));
296
connect(m_entryContextMenu, SIGNAL(aboutToShow()), SLOT(obtainContextFocusLock()));
297
connect(m_entryContextMenu, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock()));
298
connect(m_entryNewContextMenu, SIGNAL(aboutToShow()), SLOT(obtainContextFocusLock()));
299
connect(m_entryNewContextMenu, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock()));
300
connect(m_ui->menuGroups, SIGNAL(aboutToShow()), SLOT(obtainContextFocusLock()));
301
connect(m_ui->menuGroups, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock()));
304
new QShortcut(Qt::CTRL + Qt::Key_M, this, SLOT(minimizeOrHide()));
305
new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_M, this, SLOT(hideWindow()));
308
auto dbTabModifier2 = Qt::CTRL;
310
dbTabModifier2 = Qt::ALT;
312
new QShortcut(dbTabModifier2 + Qt::Key_Tab, this, SLOT(selectNextDatabaseTab()));
313
new QShortcut(Qt::CTRL + Qt::Key_PageDown, this, SLOT(selectNextDatabaseTab()));
314
new QShortcut(dbTabModifier2 + Qt::SHIFT + Qt::Key_Tab, this, SLOT(selectPreviousDatabaseTab()));
315
new QShortcut(Qt::CTRL + Qt::Key_PageUp, this, SLOT(selectPreviousDatabaseTab()));
319
auto dbTabModifier = Qt::CTRL;
321
dbTabModifier = Qt::ALT;
323
auto shortcut = new QShortcut(dbTabModifier + Qt::Key_1, this);
324
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(0); });
325
shortcut = new QShortcut(dbTabModifier + Qt::Key_2, this);
326
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(1); });
327
shortcut = new QShortcut(dbTabModifier + Qt::Key_3, this);
328
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(2); });
329
shortcut = new QShortcut(dbTabModifier + Qt::Key_4, this);
330
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(3); });
331
shortcut = new QShortcut(dbTabModifier + Qt::Key_5, this);
332
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(4); });
333
shortcut = new QShortcut(dbTabModifier + Qt::Key_6, this);
334
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(5); });
335
shortcut = new QShortcut(dbTabModifier + Qt::Key_7, this);
336
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(6); });
337
shortcut = new QShortcut(dbTabModifier + Qt::Key_8, this);
338
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(7); });
339
shortcut = new QShortcut(dbTabModifier + Qt::Key_9, this);
340
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(m_ui->tabWidget->count() - 1); });
342
m_ui->actionDatabaseNew->setIcon(icons()->icon("document-new"));
343
m_ui->actionDatabaseOpen->setIcon(icons()->icon("document-open"));
344
m_ui->menuRecentDatabases->setIcon(icons()->icon("document-open-recent"));
345
m_ui->actionDatabaseSave->setIcon(icons()->icon("document-save"));
346
m_ui->actionDatabaseSaveAs->setIcon(icons()->icon("document-save-as"));
347
m_ui->actionDatabaseSaveBackup->setIcon(icons()->icon("document-save-copy"));
348
m_ui->actionDatabaseClose->setIcon(icons()->icon("document-close"));
349
m_ui->actionReports->setIcon(icons()->icon("reports"));
350
m_ui->actionDatabaseSettings->setIcon(icons()->icon("document-edit"));
351
m_ui->actionDatabaseSecurity->setIcon(icons()->icon("database-change-key"));
352
m_ui->actionLockDatabase->setIcon(icons()->icon("database-lock"));
353
m_ui->actionLockDatabaseToolbar->setIcon(icons()->icon("database-lock"));
354
m_ui->actionLockAllDatabases->setIcon(icons()->icon("database-lock-all"));
355
m_ui->actionQuit->setIcon(icons()->icon("application-exit"));
356
m_ui->actionDatabaseMerge->setIcon(icons()->icon("database-merge"));
357
m_ui->actionImport->setIcon(icons()->icon("document-import"));
358
m_ui->menuExport->setIcon(icons()->icon("document-export"));
360
m_ui->actionEntryNew->setIcon(icons()->icon("entry-new"));
361
m_ui->actionEntryClone->setIcon(icons()->icon("entry-clone"));
362
m_ui->actionEntryEdit->setIcon(icons()->icon("entry-edit"));
363
m_ui->actionEntryDelete->setIcon(icons()->icon("entry-delete"));
364
m_ui->actionEntryRestore->setIcon(icons()->icon("entry-restore"));
365
m_ui->actionEntryAutoType->setIcon(icons()->icon("auto-type"));
366
m_ui->actionEntryAutoTypeSequence->setIcon(icons()->icon("auto-type"));
367
m_ui->actionEntryAutoTypeUsername->setIcon(icons()->icon("auto-type"));
368
m_ui->actionEntryAutoTypeUsernameEnter->setIcon(icons()->icon("auto-type"));
369
m_ui->actionEntryAutoTypePassword->setIcon(icons()->icon("auto-type"));
370
m_ui->actionEntryAutoTypePasswordEnter->setIcon(icons()->icon("auto-type"));
371
m_ui->actionEntryAutoTypeTOTP->setIcon(icons()->icon("auto-type"));
372
m_ui->actionEntryMoveUp->setIcon(icons()->icon("move-up"));
373
m_ui->actionEntryMoveDown->setIcon(icons()->icon("move-down"));
374
m_ui->actionEntryCopyUsername->setIcon(icons()->icon("username-copy"));
375
m_ui->actionEntryCopyPassword->setIcon(icons()->icon("password-copy"));
376
m_ui->actionEntryCopyURL->setIcon(icons()->icon("url-copy"));
377
m_ui->menuEntryCopyAttribute->setIcon(icons()->icon("attributes-copy"));
378
m_ui->menuEntryTotp->setIcon(icons()->icon("totp"));
379
m_ui->actionEntryTotp->setIcon(icons()->icon("totp"));
380
m_ui->actionEntryCopyTotp->setIcon(icons()->icon("totp-copy"));
381
m_ui->actionEntryCopyPasswordTotp->setIcon(icons()->icon("totp-copy-password"));
382
m_ui->actionEntryTotpQRCode->setIcon(icons()->icon("qrcode"));
383
m_ui->actionEntrySetupTotp->setIcon(icons()->icon("totp-edit"));
384
m_ui->actionEntryAddToAgent->setIcon(icons()->icon("utilities-terminal"));
385
m_ui->actionEntryRemoveFromAgent->setIcon(icons()->icon("utilities-terminal"));
386
m_ui->menuTags->setIcon(icons()->icon("tag-multiple"));
387
m_ui->actionEntryDownloadIcon->setIcon(icons()->icon("favicon-download"));
388
m_ui->actionGroupSortAsc->setIcon(icons()->icon("sort-alphabetical-ascending"));
389
m_ui->actionGroupSortDesc->setIcon(icons()->icon("sort-alphabetical-descending"));
391
m_ui->actionGroupNew->setIcon(icons()->icon("group-new"));
392
m_ui->actionGroupEdit->setIcon(icons()->icon("group-edit"));
393
m_ui->actionGroupClone->setIcon(icons()->icon("group-clone"));
394
m_ui->actionGroupDelete->setIcon(icons()->icon("group-delete"));
395
m_ui->actionGroupEmptyRecycleBin->setIcon(icons()->icon("group-empty-trash"));
396
m_ui->actionEntryOpenUrl->setIcon(icons()->icon("web"));
397
m_ui->actionGroupDownloadFavicons->setIcon(icons()->icon("favicon-download"));
399
m_ui->actionSettings->setIcon(icons()->icon("configure"));
400
m_ui->actionPasswordGenerator->setIcon(icons()->icon("password-generator"));
402
m_ui->actionAbout->setIcon(icons()->icon("help-about"));
403
m_ui->actionDonate->setIcon(icons()->icon("donate"));
404
m_ui->actionBugReport->setIcon(icons()->icon("bugreport"));
405
m_ui->actionGettingStarted->setIcon(icons()->icon("getting-started"));
406
m_ui->actionUserGuide->setIcon(icons()->icon("user-guide"));
407
m_ui->actionOnlineHelp->setIcon(icons()->icon("system-help"));
408
m_ui->actionKeyboardShortcuts->setIcon(icons()->icon("keyboard-shortcuts"));
409
m_ui->actionCheckForUpdates->setIcon(icons()->icon("system-software-update"));
411
#ifdef WITH_XC_BROWSER_PASSKEYS
412
m_ui->actionPasskeys->setIcon(icons()->icon("passkey"));
413
m_ui->actionImportPasskey->setIcon(icons()->icon("document-import"));
414
m_ui->actionEntryImportPasskey->setIcon(icons()->icon("document-import"));
417
m_actionMultiplexer.connect(
418
SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(setMenuActionState(DatabaseWidget::Mode)));
419
m_actionMultiplexer.connect(SIGNAL(groupChanged()), this, SLOT(setMenuActionState()));
420
m_actionMultiplexer.connect(SIGNAL(entrySelectionChanged()), this, SLOT(setMenuActionState()));
421
m_actionMultiplexer.connect(SIGNAL(databaseNonDataChanged()), this, SLOT(setMenuActionState()));
422
m_actionMultiplexer.connect(SIGNAL(groupContextMenuRequested(QPoint)), this, SLOT(showGroupContextMenu(QPoint)));
423
m_actionMultiplexer.connect(SIGNAL(entryContextMenuRequested(QPoint)), this, SLOT(showEntryContextMenu(QPoint)));
424
m_actionMultiplexer.connect(SIGNAL(groupChanged()), this, SLOT(updateEntryCountLabel()));
425
m_actionMultiplexer.connect(SIGNAL(databaseUnlocked()), this, SLOT(updateEntryCountLabel()));
426
m_actionMultiplexer.connect(SIGNAL(databaseModified()), this, SLOT(updateEntryCountLabel()));
427
m_actionMultiplexer.connect(SIGNAL(searchModeActivated()), this, SLOT(updateEntryCountLabel()));
428
m_actionMultiplexer.connect(SIGNAL(listModeActivated()), this, SLOT(updateEntryCountLabel()));
431
connect(m_ui->tabWidget,
432
SIGNAL(activeDatabaseChanged(DatabaseWidget*)),
434
SLOT(databaseChanged(DatabaseWidget*)));
435
connect(m_ui->tabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), m_searchWidget, SLOT(databaseChanged()));
437
connect(m_ui->tabWidget, SIGNAL(tabNameChanged()), SLOT(updateWindowTitle()));
438
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(updateWindowTitle()));
439
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(databaseTabChanged(int)));
440
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(setMenuActionState()));
441
connect(m_ui->tabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), SLOT(databaseStatusChanged(DatabaseWidget*)));
442
connect(m_ui->tabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), SLOT(databaseStatusChanged(DatabaseWidget*)));
443
connect(m_ui->tabWidget, SIGNAL(tabVisibilityChanged(bool)), SLOT(updateToolbarSeparatorVisibility()));
444
connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(setMenuActionState()));
445
connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(updateWindowTitle()));
446
connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(updateToolbarSeparatorVisibility()));
447
connect(m_ui->settingsWidget, SIGNAL(accepted()), SLOT(applySettingsChanges()));
448
connect(m_ui->settingsWidget, SIGNAL(settingsReset()), SLOT(applySettingsChanges()));
449
connect(m_ui->settingsWidget, SIGNAL(accepted()), SLOT(switchToDatabases()));
450
connect(m_ui->settingsWidget, SIGNAL(rejected()), SLOT(switchToDatabases()));
452
connect(m_ui->actionDatabaseNew, SIGNAL(triggered()), m_ui->tabWidget, SLOT(newDatabase()));
453
connect(m_ui->actionDatabaseOpen, SIGNAL(triggered()), m_ui->tabWidget, SLOT(openDatabase()));
454
connect(m_ui->actionDatabaseSave, SIGNAL(triggered()), m_ui->tabWidget, SLOT(saveDatabase()));
455
connect(m_ui->actionDatabaseSaveAs, SIGNAL(triggered()), m_ui->tabWidget, SLOT(saveDatabaseAs()));
456
connect(m_ui->actionDatabaseSaveBackup, SIGNAL(triggered()), m_ui->tabWidget, SLOT(saveDatabaseBackup()));
457
connect(m_ui->actionDatabaseClose, SIGNAL(triggered()), m_ui->tabWidget, SLOT(closeCurrentDatabaseTab()));
458
connect(m_ui->actionDatabaseMerge, SIGNAL(triggered()), m_ui->tabWidget, SLOT(mergeDatabase()));
459
connect(m_ui->actionDatabaseSecurity, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showDatabaseSecurity()));
460
connect(m_ui->actionReports, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showDatabaseReports()));
461
connect(m_ui->actionDatabaseSettings, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showDatabaseSettings()));
462
#ifdef WITH_XC_BROWSER_PASSKEYS
463
connect(m_ui->actionPasskeys, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showPasskeys()));
464
connect(m_ui->actionImportPasskey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importPasskey()));
465
connect(m_ui->actionEntryImportPasskey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importPasskeyToEntry()));
467
connect(m_ui->actionImport, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importFile()));
468
connect(m_ui->actionExportCsv, SIGNAL(triggered()), m_ui->tabWidget, SLOT(exportToCsv()));
469
connect(m_ui->actionExportHtml, SIGNAL(triggered()), m_ui->tabWidget, SLOT(exportToHtml()));
470
connect(m_ui->actionExportXML, SIGNAL(triggered()), m_ui->tabWidget, SLOT(exportToXML()));
472
m_ui->actionLockDatabase, SIGNAL(triggered()), m_ui->tabWidget, SLOT(lockAndSwitchToFirstUnlockedDatabase()));
473
connect(m_ui->actionLockDatabaseToolbar, SIGNAL(triggered()), m_ui->actionLockDatabase, SIGNAL(triggered()));
474
connect(m_ui->actionLockAllDatabases, SIGNAL(triggered()), m_ui->tabWidget, SLOT(lockDatabases()));
475
connect(m_ui->actionQuit, SIGNAL(triggered()), SLOT(appExit()));
477
m_actionMultiplexer.connect(m_ui->actionEntryNew, SIGNAL(triggered()), SLOT(createEntry()));
478
m_actionMultiplexer.connect(m_ui->actionEntryClone, SIGNAL(triggered()), SLOT(cloneEntry()));
479
m_actionMultiplexer.connect(m_ui->actionEntryEdit, SIGNAL(triggered()), SLOT(switchToEntryEdit()));
480
m_actionMultiplexer.connect(m_ui->actionEntryDelete, SIGNAL(triggered()), SLOT(deleteSelectedEntries()));
481
m_actionMultiplexer.connect(m_ui->actionEntryRestore, SIGNAL(triggered()), SLOT(restoreSelectedEntries()));
483
m_actionMultiplexer.connect(m_ui->actionEntryTotp, SIGNAL(triggered()), SLOT(showTotp()));
484
m_actionMultiplexer.connect(m_ui->actionEntrySetupTotp, SIGNAL(triggered()), SLOT(setupTotp()));
486
m_actionMultiplexer.connect(m_ui->actionEntryCopyTotp, SIGNAL(triggered()), SLOT(copyTotp()));
487
m_actionMultiplexer.connect(m_ui->actionEntryCopyPasswordTotp, SIGNAL(triggered()), SLOT(copyPasswordTotp()));
488
m_actionMultiplexer.connect(m_ui->actionEntryTotpQRCode, SIGNAL(triggered()), SLOT(showTotpKeyQrCode()));
489
m_actionMultiplexer.connect(m_ui->actionEntryCopyTitle, SIGNAL(triggered()), SLOT(copyTitle()));
490
m_actionMultiplexer.connect(m_ui->actionEntryMoveUp, SIGNAL(triggered()), SLOT(moveEntryUp()));
491
m_actionMultiplexer.connect(m_ui->actionEntryMoveDown, SIGNAL(triggered()), SLOT(moveEntryDown()));
492
m_actionMultiplexer.connect(m_ui->actionEntryCopyUsername, SIGNAL(triggered()), SLOT(copyUsername()));
493
m_actionMultiplexer.connect(m_ui->actionEntryCopyPassword, SIGNAL(triggered()), SLOT(copyPassword()));
494
m_actionMultiplexer.connect(m_ui->actionEntryCopyURL, SIGNAL(triggered()), SLOT(copyURL()));
495
m_actionMultiplexer.connect(m_ui->actionEntryCopyNotes, SIGNAL(triggered()), SLOT(copyNotes()));
496
m_actionMultiplexer.connect(m_ui->actionEntryAutoType, SIGNAL(triggered()), SLOT(performAutoType()));
497
m_actionMultiplexer.connect(m_ui->actionEntryAutoTypeSequence, SIGNAL(triggered()), SLOT(performAutoType()));
498
m_actionMultiplexer.connect(
499
m_ui->actionEntryAutoTypeUsername, SIGNAL(triggered()), SLOT(performAutoTypeUsername()));
500
m_actionMultiplexer.connect(
501
m_ui->actionEntryAutoTypeUsernameEnter, SIGNAL(triggered()), SLOT(performAutoTypeUsernameEnter()));
502
m_actionMultiplexer.connect(
503
m_ui->actionEntryAutoTypePassword, SIGNAL(triggered()), SLOT(performAutoTypePassword()));
504
m_actionMultiplexer.connect(
505
m_ui->actionEntryAutoTypePasswordEnter, SIGNAL(triggered()), SLOT(performAutoTypePasswordEnter()));
506
m_actionMultiplexer.connect(m_ui->actionEntryAutoTypeTOTP, SIGNAL(triggered()), SLOT(performAutoTypeTOTP()));
507
m_actionMultiplexer.connect(m_ui->actionEntryOpenUrl, SIGNAL(triggered()), SLOT(openUrl()));
508
m_actionMultiplexer.connect(m_ui->actionEntryDownloadIcon, SIGNAL(triggered()), SLOT(downloadSelectedFavicons()));
509
#ifdef WITH_XC_SSHAGENT
510
m_actionMultiplexer.connect(m_ui->actionEntryAddToAgent, SIGNAL(triggered()), SLOT(addToAgent()));
511
m_actionMultiplexer.connect(m_ui->actionEntryRemoveFromAgent, SIGNAL(triggered()), SLOT(removeFromAgent()));
514
m_actionMultiplexer.connect(m_ui->actionGroupNew, SIGNAL(triggered()), SLOT(createGroup()));
515
m_actionMultiplexer.connect(m_ui->actionGroupEdit, SIGNAL(triggered()), SLOT(switchToGroupEdit()));
516
m_actionMultiplexer.connect(m_ui->actionGroupClone, SIGNAL(triggered()), SLOT(cloneGroup()));
517
m_actionMultiplexer.connect(m_ui->actionGroupDelete, SIGNAL(triggered()), SLOT(deleteGroup()));
518
m_actionMultiplexer.connect(m_ui->actionGroupEmptyRecycleBin, SIGNAL(triggered()), SLOT(emptyRecycleBin()));
519
m_actionMultiplexer.connect(m_ui->actionGroupSortAsc, SIGNAL(triggered()), SLOT(sortGroupsAsc()));
520
m_actionMultiplexer.connect(m_ui->actionGroupSortDesc, SIGNAL(triggered()), SLOT(sortGroupsDesc()));
521
m_actionMultiplexer.connect(m_ui->actionGroupDownloadFavicons, SIGNAL(triggered()), SLOT(downloadAllFavicons()));
523
connect(m_ui->actionSettings, SIGNAL(toggled(bool)), SLOT(switchToSettings(bool)));
524
connect(m_ui->actionPasswordGenerator, SIGNAL(toggled(bool)), SLOT(togglePasswordGenerator(bool)));
525
connect(m_ui->passwordGeneratorWidget, &PasswordGeneratorWidget::closed, this, [this] {
526
togglePasswordGenerator(false);
528
m_ui->passwordGeneratorWidget->setStandaloneMode(true);
530
connect(m_ui->welcomeWidget, SIGNAL(newDatabase()), SLOT(switchToNewDatabase()));
531
connect(m_ui->welcomeWidget, SIGNAL(openDatabase()), SLOT(switchToOpenDatabase()));
532
connect(m_ui->welcomeWidget, SIGNAL(openDatabaseFile(QString)), SLOT(switchToDatabaseFile(QString)));
533
connect(m_ui->welcomeWidget, SIGNAL(importFile()), m_ui->tabWidget, SLOT(importFile()));
535
connect(m_ui->actionAbout, SIGNAL(triggered()), SLOT(showAboutDialog()));
536
connect(m_ui->actionDonate, SIGNAL(triggered()), SLOT(openDonateUrl()));
537
connect(m_ui->actionBugReport, SIGNAL(triggered()), SLOT(openBugReportUrl()));
538
connect(m_ui->actionGettingStarted, SIGNAL(triggered()), SLOT(openGettingStartedGuide()));
539
connect(m_ui->actionUserGuide, SIGNAL(triggered()), SLOT(openUserGuide()));
540
connect(m_ui->actionOnlineHelp, SIGNAL(triggered()), SLOT(openOnlineHelp()));
541
connect(m_ui->actionKeyboardShortcuts, SIGNAL(triggered()), SLOT(openKeyboardShortcuts()));
542
connect(m_ui->actionAllowScreenCapture, &QAction::toggled, this, &MainWindow::setAllowScreenCapture);
544
connect(osUtils, &OSUtilsBase::statusbarThemeChanged, this, &MainWindow::updateTrayIcon);
546
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
548
auto* eventFilter = new MainWindowEventFilter(this);
549
m_ui->menubar->installEventFilter(eventFilter);
550
m_ui->toolBar->installEventFilter(eventFilter);
551
m_ui->tabWidget->tabBar()->installEventFilter(eventFilter);
552
installEventFilter(eventFilter);
556
setUnifiedTitleAndToolBarOnMac(true);
559
#ifdef WITH_XC_UPDATECHECK
560
connect(m_ui->actionCheckForUpdates, SIGNAL(triggered()), SLOT(showUpdateCheckDialog()));
561
connect(UpdateChecker::instance(),
562
SIGNAL(updateCheckFinished(bool, QString, bool)),
563
SLOT(hasUpdateAvailable(bool, QString, bool)));
565
connect(&m_updateCheckTimer, &QTimer::timeout, this, &MainWindow::performUpdateCheck);
566
m_updateCheckTimer.start(3.6e6);
568
QTimer::singleShot(500, this, SLOT(performUpdateCheck()));
570
m_ui->actionCheckForUpdates->setVisible(false);
573
#ifndef WITH_XC_NETWORKING
574
m_ui->actionGroupDownloadFavicons->setVisible(false);
575
m_ui->actionEntryDownloadIcon->setVisible(false);
578
m_ui->actionGettingStarted->setVisible(false);
579
m_ui->actionUserGuide->setVisible(false);
580
m_ui->actionKeyboardShortcuts->setVisible(false);
584
connect(m_ui->tabWidget, SIGNAL(messageGlobal(QString,MessageWidget::MessageType)),
585
SLOT(displayGlobalMessage(QString,MessageWidget::MessageType)));
588
connect(m_ui->tabWidget, SIGNAL(messageDismissGlobal()), this, SLOT(hideGlobalMessage()));
591
m_screenLockListener = new ScreenLockListener(this);
592
connect(m_screenLockListener, SIGNAL(screenLocked()), SLOT(handleScreenLock()));
596
connect(Application::instance(), SIGNAL(focusWindowChanged(QWindow*)), SLOT(focusWindowChanged(QWindow*)));
597
m_trayIconTriggerReason = QSystemTrayIcon::Unknown;
598
m_trayIconTriggerTimer.setSingleShot(true);
599
connect(&m_trayIconTriggerTimer, SIGNAL(timeout()), SLOT(processTrayIconTrigger()));
601
if (config()->hasAccessError()) {
602
m_ui->globalMessageWidget->showMessage(tr("Access error for config file %1").arg(config()->getFileName()),
603
MessageWidget::Error);
607
connect(qApp, &QGuiApplication::commitDataRequest, this, [this] { m_appExitCalled = true; });
609
#if defined(KEEPASSXC_BUILD_TYPE_SNAPSHOT) || defined(KEEPASSXC_BUILD_TYPE_PRE_RELEASE)
610
auto* hidePreRelWarn = new QAction(tr("Don't show again for this version"), m_ui->globalMessageWidget);
611
m_ui->globalMessageWidget->addAction(hidePreRelWarn);
612
auto hidePreRelWarnConn = QSharedPointer<QMetaObject::Connection>::create();
613
*hidePreRelWarnConn = connect(m_ui->globalMessageWidget, &KMessageWidget::hideAnimationFinished, [=] {
614
m_ui->globalMessageWidget->removeAction(hidePreRelWarn);
615
disconnect(*hidePreRelWarnConn);
616
hidePreRelWarn->deleteLater();
618
connect(hidePreRelWarn, &QAction::triggered, [=] {
619
m_ui->globalMessageWidget->animatedHide();
620
config()->set(Config::Messages_HidePreReleaseWarning, KEEPASSXC_VERSION);
623
#if defined(KEEPASSXC_BUILD_TYPE_SNAPSHOT)
624
if (config()->get(Config::Messages_HidePreReleaseWarning) != KEEPASSXC_VERSION) {
625
m_ui->globalMessageWidget->showMessage(
626
tr("WARNING: You are using an unstable build of KeePassXC.\n"
627
"There is a high risk of corruption, maintain a backup of your databases.\n"
628
"This version is not meant for production use."),
629
MessageWidget::Warning,
632
#elif defined(KEEPASSXC_BUILD_TYPE_PRE_RELEASE)
633
if (config()->get(Config::Messages_HidePreReleaseWarning) != KEEPASSXC_VERSION) {
634
m_ui->globalMessageWidget->showMessage(
635
tr("NOTE: You are using a pre-release version of KeePassXC.\n"
636
"Expect some bugs and minor issues, this version is meant for testing purposes."),
637
MessageWidget::Information,
640
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) && QT_VERSION < QT_VERSION_CHECK(5, 6, 0))
641
if (!config()->get(Config::Messages_Qt55CompatibilityWarning).toBool()) {
642
m_ui->globalMessageWidget->showMessage(
643
tr("WARNING: Your Qt version may cause KeePassXC to crash with an On-Screen Keyboard.\n"
644
"We recommend you use the AppImage available on our downloads page."),
645
MessageWidget::Warning,
647
config()->set(Config::Messages_Qt55CompatibilityWarning, true);
651
connect(qApp, SIGNAL(anotherInstanceStarted()), this, SLOT(bringToFront()));
652
connect(qApp, SIGNAL(applicationActivated()), this, SLOT(bringToFront()));
653
connect(qApp, SIGNAL(openFile(QString)), this, SLOT(openDatabase(QString)));
654
connect(qApp, SIGNAL(quitSignalReceived()), this, SLOT(appExit()), Qt::DirectConnection);
657
statusBar()->setFixedHeight(24);
658
m_progressBarLabel = new QLabel(statusBar());
659
m_progressBarLabel->setVisible(false);
660
statusBar()->addPermanentWidget(m_progressBarLabel);
661
m_progressBar = new QProgressBar(statusBar());
662
m_progressBar->setVisible(false);
663
m_progressBar->setTextVisible(false);
664
m_progressBar->setMaximumWidth(100);
665
m_progressBar->setFixedHeight(15);
666
m_progressBar->setMaximum(100);
667
statusBar()->addPermanentWidget(m_progressBar);
668
connect(clipboard(), SIGNAL(updateCountdown(int, QString)), this, SLOT(updateProgressBar(int, QString)));
669
m_statusBarLabel = new QLabel(statusBar());
670
m_statusBarLabel->setObjectName("statusBarLabel");
671
statusBar()->addPermanentWidget(m_statusBarLabel);
673
restoreConfigState();
674
setMenuActionState();
677
MainWindow::~MainWindow()
679
#ifdef WITH_XC_SSHAGENT
680
sshAgent()->removeAllIdentities();
687
void MainWindow::restoreConfigState()
689
if (config()->get(Config::OpenPreviousDatabasesOnStartup).toBool()) {
690
const QStringList fileNames = config()->get(Config::LastOpenedDatabases).toStringList();
691
for (const QString& filename : fileNames) {
692
if (!filename.isEmpty() && QFile::exists(filename)) {
693
openDatabase(filename);
696
auto lastActiveFile = config()->get(Config::LastActiveDatabase).toString();
697
if (!lastActiveFile.isEmpty()) {
698
openDatabase(lastActiveFile);
703
QList<DatabaseWidget*> MainWindow::getOpenDatabases()
705
QList<DatabaseWidget*> dbWidgets;
706
for (int i = 0; i < m_ui->tabWidget->count(); ++i) {
707
dbWidgets << m_ui->tabWidget->databaseWidgetFromIndex(i);
712
void MainWindow::showErrorMessage(const QString& message)
714
m_ui->globalMessageWidget->showMessage(message, MessageWidget::Error);
717
void MainWindow::appExit()
719
m_appExitCalled = true;
729
bool MainWindow::isHardwareKeySupported()
731
#ifdef WITH_XC_YUBIKEY
745
bool MainWindow::refreshHardwareKeys()
747
#ifdef WITH_XC_YUBIKEY
748
auto yk = YubiKey::instance();
750
bool found = yk->findValidKeys();
753
emit yk->detectComplete(found);
760
void MainWindow::updateLastDatabasesMenu()
762
m_ui->menuRecentDatabases->clear();
764
const QStringList lastDatabases = config()->get(Config::LastDatabases).toStringList();
765
for (const QString& database : lastDatabases) {
766
QAction* action = m_ui->menuRecentDatabases->addAction(database);
767
action->setData(database);
768
m_lastDatabasesActions->addAction(action);
770
m_ui->menuRecentDatabases->addSeparator();
771
m_ui->menuRecentDatabases->addAction(m_clearHistoryAction);
774
void MainWindow::updateCopyAttributesMenu()
776
DatabaseWidget* dbWidget = m_ui->tabWidget->currentDatabaseWidget();
781
if (dbWidget->numberOfSelectedEntries() != 1) {
785
QList<QAction*> actions = m_ui->menuEntryCopyAttribute->actions();
786
for (int i = m_countDefaultAttributes; i < actions.size(); i++) {
790
const QStringList customEntryAttributes = dbWidget->customEntryAttributes();
791
for (const QString& key : customEntryAttributes) {
792
QAction* action = m_ui->menuEntryCopyAttribute->addAction(key);
793
action->setData(QVariant(key));
794
m_copyAdditionalAttributeActions->addAction(action);
798
void MainWindow::updateSetTagsMenu()
801
m_ui->menuTags->clear();
803
auto dbWidget = m_ui->tabWidget->currentDatabaseWidget();
806
QSet<QString> selectedTags;
807
for (auto entry : dbWidget->entryView()->selectedEntries()) {
808
for (auto tag : entry->tagList()) {
809
selectedTags.insert(tag);
815
for (auto tag : dbWidget->database()->tagList()) {
816
auto action = m_ui->menuTags->addAction(icons()->icon("tag"), tag);
817
action->setCheckable(true);
818
action->setChecked(selectedTags.contains(tag));
819
m_setTagsMenuActions->addAction(action);
824
if (m_ui->menuTags->isEmpty()) {
825
auto action = m_ui->menuTags->addAction(tr("No Tags"));
826
action->setEnabled(false);
830
void MainWindow::openRecentDatabase(QAction* action)
832
openDatabase(action->data().toString());
835
void MainWindow::clearLastDatabases()
837
config()->remove(Config::LastDatabases);
838
bool inWelcomeWidget = (m_ui->stackedWidget->currentIndex() == 2);
840
if (inWelcomeWidget) {
841
m_ui->welcomeWidget->refreshLastDatabases();
845
void MainWindow::openDatabase(const QString& filePath, const QString& password, const QString& keyfile)
847
m_ui->tabWidget->addDatabaseTab(filePath, false, password, keyfile);
850
void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
852
int currentIndex = m_ui->stackedWidget->currentIndex();
854
bool inDatabaseTabWidget = (currentIndex == DatabaseTabScreen);
855
bool inWelcomeWidget = (currentIndex == WelcomeScreen);
856
bool inDatabaseTabWidgetOrWelcomeWidget = inDatabaseTabWidget || inWelcomeWidget;
858
m_ui->actionDatabaseClose->setEnabled(true);
859
m_ui->actionDatabaseMerge->setEnabled(inDatabaseTabWidget);
860
m_ui->actionDatabaseNew->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
861
m_ui->actionDatabaseOpen->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
862
m_ui->menuRecentDatabases->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
863
m_ui->actionImport->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
864
m_ui->actionLockDatabase->setEnabled(m_ui->tabWidget->hasLockableDatabases());
865
m_ui->actionLockDatabaseToolbar->setEnabled(m_ui->tabWidget->hasLockableDatabases());
866
m_ui->actionLockAllDatabases->setEnabled(m_ui->tabWidget->hasLockableDatabases());
868
if (inDatabaseTabWidget && m_ui->tabWidget->currentIndex() != -1) {
869
DatabaseWidget* dbWidget = m_ui->tabWidget->currentDatabaseWidget();
872
if (mode == DatabaseWidget::Mode::None) {
873
mode = dbWidget->currentMode();
877
case DatabaseWidget::Mode::ViewMode: {
878
bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1;
879
bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0;
880
bool groupSelected = dbWidget->isGroupSelected();
881
bool currentGroupHasChildren = dbWidget->currentGroup()->hasChildren();
882
bool currentGroupHasEntries = !dbWidget->currentGroup()->entries().isEmpty();
883
bool recycleBinSelected = dbWidget->isRecycleBinSelected();
884
bool sorted = dbWidget->isSorted();
885
int entryIndex = dbWidget->currentEntryIndex();
886
int numEntries = dbWidget->currentGroup()->entries().size();
888
m_ui->actionEntryNew->setEnabled(true);
889
m_ui->actionEntryClone->setEnabled(singleEntrySelected);
890
m_ui->actionEntryEdit->setEnabled(singleEntrySelected);
891
m_ui->actionEntryDelete->setEnabled(entriesSelected);
892
m_ui->actionEntryRestore->setVisible(entriesSelected && recycleBinSelected);
893
m_ui->actionEntryRestore->setEnabled(entriesSelected && recycleBinSelected);
894
m_ui->actionEntryRestore->setText(tr("Restore Entry(s)", "", dbWidget->numberOfSelectedEntries()));
895
m_ui->actionEntryRestore->setToolTip(tr("Restore Entry(s)", "", dbWidget->numberOfSelectedEntries()));
896
m_ui->actionEntryMoveUp->setVisible(!sorted);
897
m_ui->actionEntryMoveDown->setVisible(!sorted);
898
m_ui->actionEntryMoveUp->setEnabled(singleEntrySelected && !sorted && entryIndex > 0);
899
m_ui->actionEntryMoveDown->setEnabled(singleEntrySelected && !sorted && entryIndex >= 0
900
&& entryIndex < numEntries - 1);
901
m_ui->actionEntryCopyTitle->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTitle());
902
m_ui->actionEntryCopyUsername->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUsername());
905
m_ui->actionEntryCopyPassword->setEnabled(singleEntrySelected);
906
m_ui->actionEntryCopyURL->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUrl());
907
m_ui->actionEntryCopyNotes->setEnabled(singleEntrySelected && dbWidget->currentEntryHasNotes());
908
m_ui->menuEntryCopyAttribute->setEnabled(singleEntrySelected);
909
m_ui->menuEntryTotp->setEnabled(singleEntrySelected);
910
m_ui->menuTags->setEnabled(entriesSelected);
911
m_ui->actionEntryAutoType->setEnabled(singleEntrySelected && dbWidget->currentEntryHasAutoTypeEnabled());
912
m_ui->actionEntryAutoType->menu()->setEnabled(singleEntrySelected
913
&& dbWidget->currentEntryHasAutoTypeEnabled());
914
m_ui->actionEntryAutoTypeSequence->setText(
915
singleEntrySelected ? dbWidget->currentSelectedEntry()->effectiveAutoTypeSequence()
916
: Group::RootAutoTypeSequence);
917
m_ui->actionEntryAutoTypeSequence->setEnabled(singleEntrySelected);
918
m_ui->actionEntryAutoTypeUsername->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUsername());
919
m_ui->actionEntryAutoTypeUsernameEnter->setEnabled(singleEntrySelected
920
&& dbWidget->currentEntryHasUsername());
921
m_ui->actionEntryAutoTypePassword->setEnabled(singleEntrySelected && dbWidget->currentEntryHasPassword());
922
m_ui->actionEntryAutoTypePasswordEnter->setEnabled(singleEntrySelected
923
&& dbWidget->currentEntryHasPassword());
924
m_ui->actionEntryAutoTypeTOTP->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
925
m_ui->actionEntryAutoTypeTOTP->setVisible(singleEntrySelected && dbWidget->currentEntryHasTotp());
926
m_ui->actionEntryOpenUrl->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUrl());
927
m_ui->actionEntryTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
928
m_ui->actionEntryCopyTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
929
m_ui->actionEntryCopyPasswordTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
930
m_ui->actionEntrySetupTotp->setEnabled(singleEntrySelected);
931
m_ui->actionEntryTotpQRCode->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
932
m_ui->actionEntryDownloadIcon->setEnabled((entriesSelected && !singleEntrySelected)
933
|| (singleEntrySelected && dbWidget->currentEntryHasUrl()));
934
m_ui->actionGroupNew->setEnabled(groupSelected);
935
m_ui->actionGroupEdit->setEnabled(groupSelected);
936
m_ui->actionGroupClone->setEnabled(groupSelected && dbWidget->canCloneCurrentGroup());
937
m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup());
938
m_ui->actionGroupSortAsc->setEnabled(groupSelected && currentGroupHasChildren);
939
m_ui->actionGroupSortDesc->setEnabled(groupSelected && currentGroupHasChildren);
940
m_ui->actionGroupEmptyRecycleBin->setVisible(recycleBinSelected);
941
m_ui->actionGroupEmptyRecycleBin->setEnabled(recycleBinSelected);
942
#ifdef WITH_XC_NETWORKING
943
m_ui->actionGroupDownloadFavicons->setVisible(!recycleBinSelected);
945
m_ui->actionGroupDownloadFavicons->setEnabled(groupSelected && currentGroupHasEntries
946
&& !recycleBinSelected);
947
m_ui->actionDatabaseSecurity->setEnabled(true);
948
m_ui->actionReports->setEnabled(true);
949
m_ui->actionDatabaseSettings->setEnabled(true);
950
m_ui->actionDatabaseSave->setEnabled(m_ui->tabWidget->canSave());
951
m_ui->actionDatabaseSaveAs->setEnabled(true);
952
m_ui->actionDatabaseSaveBackup->setEnabled(true);
953
m_ui->menuExport->setEnabled(true);
954
m_ui->actionExportCsv->setEnabled(true);
955
m_ui->actionExportHtml->setEnabled(true);
956
m_ui->actionExportXML->setEnabled(true);
957
m_ui->actionDatabaseMerge->setEnabled(m_ui->tabWidget->currentIndex() != -1);
958
#ifdef WITH_XC_BROWSER_PASSKEYS
959
m_ui->actionPasskeys->setEnabled(true);
960
m_ui->actionImportPasskey->setEnabled(true);
961
m_ui->actionEntryImportPasskey->setEnabled(singleEntrySelected);
963
#ifdef WITH_XC_SSHAGENT
964
bool singleEntryHasSshKey =
965
singleEntrySelected && sshAgent()->isEnabled() && dbWidget->currentEntryHasSshKey();
966
m_ui->actionEntryAddToAgent->setVisible(singleEntryHasSshKey);
967
m_ui->actionEntryAddToAgent->setEnabled(singleEntryHasSshKey);
968
m_ui->actionEntryRemoveFromAgent->setVisible(singleEntryHasSshKey);
969
m_ui->actionEntryRemoveFromAgent->setEnabled(singleEntryHasSshKey);
972
m_searchWidgetAction->setEnabled(true);
976
case DatabaseWidget::Mode::EditMode:
977
case DatabaseWidget::Mode::LockedMode: {
979
bool editEntryActive = dbWidget->isEntryEditActive();
980
const auto editEntryActionsMask = QList<QAction*>({m_ui->actionEntryCopyUsername,
981
m_ui->actionEntryCopyPassword,
982
m_ui->actionEntryCopyURL,
983
m_ui->actionEntryOpenUrl,
984
m_ui->actionEntryAutoType,
985
m_ui->actionEntryDownloadIcon,
986
m_ui->actionEntryCopyNotes,
987
m_ui->actionEntryCopyTitle,
988
m_ui->menuEntryCopyAttribute->menuAction(),
989
m_ui->menuEntryTotp->menuAction(),
990
m_ui->actionEntrySetupTotp});
992
auto entryActions = m_ui->menuEntries->actions();
993
entryActions << m_ui->menuEntryCopyAttribute->actions();
994
entryActions << m_ui->menuEntryTotp->actions();
995
for (auto action : entryActions) {
996
bool enabled = editEntryActive && editEntryActionsMask.contains(action);
997
if (action->menu()) {
998
action->menu()->setEnabled(enabled);
1000
action->setEnabled(enabled);
1003
const auto groupActions = m_ui->menuGroups->actions();
1004
for (auto action : groupActions) {
1005
action->setEnabled(false);
1008
m_ui->actionDatabaseSecurity->setEnabled(false);
1009
m_ui->actionReports->setEnabled(false);
1010
m_ui->actionDatabaseSettings->setEnabled(false);
1011
m_ui->actionDatabaseSave->setEnabled(false);
1012
m_ui->actionDatabaseSaveAs->setEnabled(false);
1013
m_ui->actionDatabaseSaveBackup->setEnabled(false);
1014
m_ui->menuExport->setEnabled(false);
1015
m_ui->actionExportCsv->setEnabled(false);
1016
m_ui->actionExportHtml->setEnabled(false);
1017
m_ui->actionDatabaseMerge->setEnabled(false);
1020
m_ui->actionLockDatabase->setEnabled(false);
1022
m_ui->actionEntryMoveUp->setVisible(false);
1023
m_ui->actionEntryMoveDown->setVisible(false);
1024
m_ui->actionEntryRestore->setVisible(false);
1025
m_ui->actionEntryAddToAgent->setVisible(false);
1026
m_ui->actionEntryRemoveFromAgent->setVisible(false);
1027
m_ui->actionGroupEmptyRecycleBin->setVisible(false);
1029
#ifdef WITH_XC_BROWSER_PASSKEYS
1030
m_ui->actionPasskeys->setEnabled(false);
1031
m_ui->actionImportPasskey->setEnabled(false);
1032
m_ui->actionEntryImportPasskey->setEnabled(false);
1034
m_ui->actionPasskeys->setVisible(false);
1035
m_ui->actionImportPasskey->setVisible(false);
1036
m_ui->actionEntryImportPasskey->setVisible(false);
1039
m_searchWidgetAction->setEnabled(false);
1046
const auto entryActions = m_ui->menuEntries->actions();
1047
for (auto action : entryActions) {
1048
action->setEnabled(false);
1051
const auto groupActions = m_ui->menuGroups->actions();
1052
for (auto action : groupActions) {
1053
action->setEnabled(false);
1056
m_ui->actionDatabaseSecurity->setEnabled(false);
1057
m_ui->actionReports->setEnabled(false);
1058
m_ui->actionDatabaseSettings->setEnabled(false);
1059
m_ui->actionDatabaseSave->setEnabled(false);
1060
m_ui->actionDatabaseSaveAs->setEnabled(false);
1061
m_ui->actionDatabaseSaveBackup->setEnabled(false);
1062
m_ui->actionDatabaseClose->setEnabled(false);
1063
m_ui->menuExport->setEnabled(false);
1064
m_ui->actionExportCsv->setEnabled(false);
1065
m_ui->actionExportHtml->setEnabled(false);
1066
m_ui->actionDatabaseMerge->setEnabled(false);
1068
m_ui->actionEntryMoveUp->setVisible(false);
1069
m_ui->actionEntryMoveDown->setVisible(false);
1070
m_ui->actionEntryRestore->setVisible(false);
1071
m_ui->actionEntryAddToAgent->setVisible(false);
1072
m_ui->actionEntryRemoveFromAgent->setVisible(false);
1073
m_ui->actionGroupEmptyRecycleBin->setVisible(false);
1075
m_searchWidgetAction->setEnabled(false);
1078
if ((currentIndex == PasswordGeneratorScreen) != m_ui->actionPasswordGenerator->isChecked()) {
1079
bool blocked = m_ui->actionPasswordGenerator->blockSignals(true);
1080
m_ui->actionPasswordGenerator->toggle();
1081
m_ui->actionPasswordGenerator->blockSignals(blocked);
1082
} else if ((currentIndex == SettingsScreen) != m_ui->actionSettings->isChecked()) {
1083
bool blocked = m_ui->actionSettings->blockSignals(true);
1084
m_ui->actionSettings->toggle();
1085
m_ui->actionSettings->blockSignals(blocked);
1089
void MainWindow::updateToolbarSeparatorVisibility()
1091
if (!m_showToolbarSeparator) {
1092
m_ui->toolbarSeparator->setVisible(false);
1096
switch (m_ui->stackedWidget->currentIndex()) {
1097
case DatabaseTabScreen:
1098
m_ui->toolbarSeparator->setVisible(!m_ui->tabWidget->tabBar()->isVisible()
1099
&& m_ui->tabWidget->tabBar()->count() == 1);
1101
case SettingsScreen:
1102
m_ui->toolbarSeparator->setVisible(true);
1105
m_ui->toolbarSeparator->setVisible(false);
1109
void MainWindow::updateWindowTitle()
1111
QString customWindowTitlePart;
1112
int stackedWidgetIndex = m_ui->stackedWidget->currentIndex();
1113
int tabWidgetIndex = m_ui->tabWidget->currentIndex();
1114
bool isModified = m_ui->tabWidget->isModified(tabWidgetIndex);
1116
if (stackedWidgetIndex == DatabaseTabScreen && tabWidgetIndex != -1) {
1117
customWindowTitlePart = m_ui->tabWidget->tabName(tabWidgetIndex);
1120
customWindowTitlePart.remove(customWindowTitlePart.size() - 1, 1);
1122
m_ui->actionDatabaseSave->setEnabled(m_ui->tabWidget->canSave(tabWidgetIndex));
1123
} else if (stackedWidgetIndex == 1) {
1124
customWindowTitlePart = tr("Settings");
1127
QString windowTitle;
1128
if (customWindowTitlePart.isEmpty()) {
1129
windowTitle = BaseWindowTitle;
1131
windowTitle = QString("%1[*] - %2").arg(customWindowTitlePart, BaseWindowTitle);
1134
if (customWindowTitlePart.isEmpty() || stackedWidgetIndex == 1) {
1135
setWindowFilePath("");
1137
setWindowFilePath(m_ui->tabWidget->databaseWidgetFromIndex(tabWidgetIndex)->database()->filePath());
1140
setWindowTitle(windowTitle);
1141
setWindowModified(isModified);
1146
void MainWindow::showAboutDialog()
1148
auto* aboutDialog = new AboutDialog(this);
1150
if (m_ui->tabWidget->currentDatabaseWidget()) {
1151
connect(m_ui->tabWidget->currentDatabaseWidget(),
1152
&DatabaseWidget::databaseLockRequested,
1154
&AboutDialog::close);
1156
aboutDialog->open();
1159
void MainWindow::performUpdateCheck()
1161
#ifdef WITH_XC_UPDATECHECK
1162
if (!config()->get(Config::UpdateCheckMessageShown).toBool()) {
1164
MessageBox::question(this,
1165
tr("Check for updates on startup?"),
1166
tr("Would you like KeePassXC to check for updates on startup?") + "\n\n"
1167
+ tr("You can always check for updates manually from the application menu."),
1168
MessageBox::Yes | MessageBox::No,
1171
config()->set(Config::GUI_CheckForUpdates, (result == MessageBox::Yes));
1172
config()->set(Config::UpdateCheckMessageShown, true);
1175
if (config()->get(Config::GUI_CheckForUpdates).toBool()) {
1176
updateCheck()->checkForUpdates(false);
1182
void MainWindow::hasUpdateAvailable(bool hasUpdate, const QString& version, bool isManuallyRequested)
1184
#ifdef WITH_XC_UPDATECHECK
1185
if (hasUpdate && !isManuallyRequested) {
1186
auto* updateCheckDialog = new UpdateCheckDialog(this);
1187
updateCheckDialog->showUpdateCheckResponse(hasUpdate, version);
1188
updateCheckDialog->show();
1193
Q_UNUSED(isManuallyRequested)
1197
void MainWindow::showUpdateCheckDialog()
1199
#ifdef WITH_XC_UPDATECHECK
1200
updateCheck()->checkForUpdates(true);
1201
auto* updateCheckDialog = new UpdateCheckDialog(this);
1202
updateCheckDialog->show();
1206
void MainWindow::customOpenUrl(QString url)
1208
#ifdef KEEPASSXC_DIST_APPIMAGE
1209
QProcess::execute("xdg-open", {url});
1211
QDesktopServices::openUrl(QUrl(url));
1215
void MainWindow::openDonateUrl()
1217
customOpenUrl("https://keepassxc.org/donate");
1220
void MainWindow::openBugReportUrl()
1222
customOpenUrl("https://github.com/keepassxreboot/keepassxc/issues");
1225
void MainWindow::openGettingStartedGuide()
1227
customOpenUrl(QString("file:///%1").arg(resources()->dataPath("docs/KeePassXC_GettingStarted.html")));
1230
void MainWindow::openUserGuide()
1232
customOpenUrl(QString("file:///%1").arg(resources()->dataPath("docs/KeePassXC_UserGuide.html")));
1235
void MainWindow::openOnlineHelp()
1237
customOpenUrl("https://keepassxc.org/docs/");
1240
void MainWindow::openKeyboardShortcuts()
1242
customOpenUrl(QString("file:///%1").arg(resources()->dataPath("docs/KeePassXC_KeyboardShortcuts.html")));
1245
void MainWindow::switchToDatabases()
1247
if (m_ui->tabWidget->currentIndex() == -1) {
1248
m_ui->stackedWidget->setCurrentIndex(WelcomeScreen);
1250
m_ui->stackedWidget->setCurrentIndex(DatabaseTabScreen);
1254
void MainWindow::switchToSettings(bool enabled)
1257
m_ui->settingsWidget->loadSettings();
1258
m_ui->stackedWidget->setCurrentIndex(SettingsScreen);
1260
switchToDatabases();
1264
void MainWindow::togglePasswordGenerator(bool enabled)
1267
m_ui->passwordGeneratorWidget->loadSettings();
1268
m_ui->passwordGeneratorWidget->regeneratePassword();
1269
m_ui->stackedWidget->setCurrentIndex(PasswordGeneratorScreen);
1271
m_ui->passwordGeneratorWidget->saveSettings();
1272
switchToDatabases();
1276
void MainWindow::switchToNewDatabase()
1278
m_ui->tabWidget->newDatabase();
1279
switchToDatabases();
1282
void MainWindow::switchToOpenDatabase()
1284
m_ui->tabWidget->openDatabase();
1285
switchToDatabases();
1288
void MainWindow::switchToDatabaseFile(const QString& file)
1290
m_ui->tabWidget->addDatabaseTab(file);
1291
switchToDatabases();
1294
void MainWindow::databaseStatusChanged(DatabaseWidget* dbWidget)
1307
void MainWindow::selectDatabaseTab(int tabIndex, bool wrap)
1309
if (m_ui->stackedWidget->currentIndex() == DatabaseTabScreen) {
1312
tabIndex = m_ui->tabWidget->count() - 1;
1313
} else if (tabIndex >= m_ui->tabWidget->count()) {
1317
tabIndex = qBound(0, tabIndex, m_ui->tabWidget->count() - 1);
1320
m_ui->tabWidget->setCurrentIndex(tabIndex);
1324
void MainWindow::selectNextDatabaseTab()
1326
selectDatabaseTab(m_ui->tabWidget->currentIndex() + 1, true);
1329
void MainWindow::selectPreviousDatabaseTab()
1331
selectDatabaseTab(m_ui->tabWidget->currentIndex() - 1, true);
1334
void MainWindow::databaseTabChanged(int tabIndex)
1336
if (tabIndex != -1 && m_ui->stackedWidget->currentIndex() == WelcomeScreen) {
1337
m_ui->stackedWidget->setCurrentIndex(DatabaseTabScreen);
1338
} else if (tabIndex == -1 && m_ui->stackedWidget->currentIndex() == DatabaseTabScreen) {
1339
m_ui->stackedWidget->setCurrentIndex(WelcomeScreen);
1342
m_actionMultiplexer.setCurrentObject(m_ui->tabWidget->currentDatabaseWidget());
1343
updateEntryCountLabel();
1346
void MainWindow::showEvent(QShowEvent* event)
1351
QTimer::singleShot(50, this, [=] { setProperty("windowOpacity", 1.0); });
1355
void MainWindow::hideEvent(QHideEvent* event)
1360
setProperty("windowOpacity", 0.0);
1364
void MainWindow::closeEvent(QCloseEvent* event)
1373
if (config()->get(Config::GUI_MinimizeOnClose).toBool() && !m_appExitCalled && !isHidden()
1374
&& !qApp->isSavingSession()) {
1380
m_appExiting = saveLastDatabases();
1382
saveWindowInformation();
1384
m_restartRequested ? kpxcApp->restart() : QApplication::quit();
1388
m_appExitCalled = false;
1389
m_restartRequested = false;
1393
void MainWindow::changeEvent(QEvent* event)
1395
if ((event->type() == QEvent::WindowStateChange) && isMinimized()) {
1396
if (isTrayIconEnabled() && config()->get(Config::GUI_MinimizeToTray).toBool()) {
1401
if (config()->get(Config::Security_LockDatabaseMinimize).toBool()) {
1402
m_ui->tabWidget->lockDatabasesDelayed();
1405
QMainWindow::changeEvent(event);
1409
void MainWindow::keyPressEvent(QKeyEvent* event)
1411
if (!event->modifiers()) {
1413
auto dbWidget = m_ui->tabWidget->currentDatabaseWidget();
1414
if (dbWidget && dbWidget->isEntryViewActive()) {
1415
if (event->key() == Qt::Key_F1) {
1416
dbWidget->focusOnGroups(true);
1418
} else if (event->key() == Qt::Key_F2) {
1419
dbWidget->focusOnEntries(true);
1421
} else if (event->key() == Qt::Key_F3 || event->key() == Qt::Key_F6) {
1422
focusSearchWidget();
1424
} else if (event->key() == Qt::Key_Escape && dbWidget->isSearchActive()) {
1425
m_searchWidget->clearSearch();
1431
QWidget::keyPressEvent(event);
1434
bool MainWindow::focusNextPrevChild(bool next)
1437
auto dbWidget = m_ui->tabWidget->currentDatabaseWidget();
1438
if (dbWidget && dbWidget->isVisible() && dbWidget->isEntryViewActive()) {
1441
if (m_searchWidget->hasFocus()) {
1442
if (m_ui->tabWidget->count() > 1) {
1443
m_ui->tabWidget->setFocus(Qt::TabFocusReason);
1445
dbWidget->setFocus(Qt::TabFocusReason);
1447
} else if (m_ui->tabWidget->hasFocus()) {
1448
dbWidget->setFocus(Qt::TabFocusReason);
1450
focusSearchWidget();
1453
if (m_searchWidget->hasFocus()) {
1454
dbWidget->setFocus(Qt::BacktabFocusReason);
1455
} else if (m_ui->tabWidget->hasFocus()) {
1456
focusSearchWidget();
1458
if (m_ui->tabWidget->count() > 1) {
1459
m_ui->tabWidget->setFocus(Qt::BacktabFocusReason);
1461
focusSearchWidget();
1469
return QMainWindow::focusNextPrevChild(next);
1472
void MainWindow::focusSearchWidget()
1474
if (m_searchWidgetAction->isEnabled()) {
1475
m_ui->toolBar->setVisible(true);
1476
m_ui->toolBar->setExpanded(true);
1477
m_searchWidget->focusSearch();
1481
void MainWindow::saveWindowInformation()
1484
config()->set(Config::GUI_MainWindowGeometry, saveGeometry());
1485
config()->set(Config::GUI_MainWindowState, saveState());
1489
bool MainWindow::saveLastDatabases()
1491
if (config()->get(Config::OpenPreviousDatabasesOnStartup).toBool()) {
1492
auto currentDbWidget = m_ui->tabWidget->currentDatabaseWidget();
1493
if (currentDbWidget) {
1494
config()->set(Config::LastActiveDatabase, currentDbWidget->database()->filePath());
1496
config()->remove(Config::LastActiveDatabase);
1499
QStringList openDatabases;
1500
for (int i = 0; i < m_ui->tabWidget->count(); ++i) {
1501
auto dbWidget = m_ui->tabWidget->databaseWidgetFromIndex(i);
1502
openDatabases.append(QDir::toNativeSeparators(dbWidget->database()->filePath()));
1505
config()->set(Config::LastOpenedDatabases, openDatabases);
1507
config()->remove(Config::LastActiveDatabase);
1508
config()->remove(Config::LastOpenedDatabases);
1511
return m_ui->tabWidget->closeAllDatabaseTabs();
1514
void MainWindow::updateTrayIcon()
1516
if (config()->get(Config::GUI_ShowTrayIcon).toBool()) {
1518
m_trayIcon = new QSystemTrayIcon(this);
1519
auto* menu = new QMenu(this);
1521
auto* actionToggle = new QAction(tr("Toggle window"), menu);
1522
menu->addAction(actionToggle);
1523
actionToggle->setIcon(icons()->icon("keepassxc-monochrome-dark"));
1525
menu->addAction(m_ui->actionLockAllDatabases);
1528
auto actionQuit = new QAction(tr("Quit KeePassXC"), menu);
1529
connect(actionQuit, SIGNAL(triggered()), SLOT(appExit()));
1530
menu->addAction(actionQuit);
1532
menu->addAction(m_ui->actionQuit);
1534
m_trayIcon->setContextMenu(menu);
1537
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
1538
SLOT(trayIconTriggered(QSystemTrayIcon::ActivationReason)));
1539
connect(actionToggle, SIGNAL(triggered()), SLOT(toggleWindow()));
1542
bool showUnlocked = m_ui->tabWidget->hasLockableDatabases();
1543
m_trayIcon->setIcon(icons()->trayIcon(showUnlocked));
1544
m_trayIcon->setToolTip(windowTitle().replace("[*]", isWindowModified() ? "*" : ""));
1547
if (!isTrayIconEnabled() || !QSystemTrayIcon::isSystemTrayAvailable()) {
1550
static int trayIconAttempts = 0;
1551
if (trayIconAttempts < 5) {
1552
QTimer::singleShot(5000, this, &MainWindow::updateTrayIcon);
1563
QApplication::setQuitOnLastWindowClosed(!isTrayIconEnabled());
1566
void MainWindow::updateProgressBar(int percentage, QString message)
1568
if (percentage < 0) {
1569
m_progressBar->setVisible(false);
1570
m_progressBarLabel->setVisible(false);
1572
m_progressBar->setValue(percentage);
1573
m_progressBar->setVisible(true);
1574
m_progressBarLabel->setText(message);
1575
m_progressBarLabel->setVisible(true);
1579
void MainWindow::updateEntryCountLabel()
1581
auto dbWidget = m_ui->tabWidget->currentDatabaseWidget();
1582
if (dbWidget && dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode) {
1583
int numEntries = dbWidget->entryView()->model()->rowCount();
1584
m_statusBarLabel->setText(tr("%1 Entry(s)", "", numEntries).arg(numEntries));
1586
m_statusBarLabel->setText("");
1590
void MainWindow::obtainContextFocusLock()
1592
m_contextMenuFocusLock = true;
1595
void MainWindow::releaseContextFocusLock()
1597
m_contextMenuFocusLock = false;
1600
void MainWindow::agentEnabled(bool enabled)
1602
m_ui->actionEntryAddToAgent->setVisible(enabled);
1603
m_ui->actionEntryRemoveFromAgent->setVisible(enabled);
1606
void MainWindow::showEntryContextMenu(const QPoint& globalPos)
1608
bool entrySelected = false;
1609
auto dbWidget = m_ui->tabWidget->currentDatabaseWidget();
1611
entrySelected = dbWidget->numberOfSelectedEntries() > 0;
1614
if (entrySelected) {
1615
m_entryContextMenu->popup(globalPos);
1617
m_entryNewContextMenu->popup(globalPos);
1621
void MainWindow::showGroupContextMenu(const QPoint& globalPos)
1623
m_ui->menuGroups->popup(globalPos);
1626
void MainWindow::applySettingsChanges()
1628
int timeout = config()->get(Config::Security_LockDatabaseIdleSeconds).toInt() * 1000;
1633
m_inactivityTimer->setInactivityTimeout(timeout);
1634
if (config()->get(Config::Security_LockDatabaseIdle).toBool()) {
1635
m_inactivityTimer->activate();
1637
m_inactivityTimer->deactivate();
1640
m_ui->menubar->setHidden(config()->get(Config::GUI_HideMenubar).toBool());
1641
m_ui->toolBar->setHidden(config()->get(Config::GUI_HideToolbar).toBool());
1642
auto movable = config()->get(Config::GUI_MovableToolbar).toBool();
1643
m_ui->toolBar->setMovable(movable);
1646
addToolBar(Qt::TopToolBarArea, m_ui->toolBar);
1650
const auto toolButtonStyle =
1651
static_cast<Qt::ToolButtonStyle>(config()->get(Config::GUI_ToolButtonStyle).toInt(&isOk));
1653
m_ui->toolBar->setToolButtonStyle(toolButtonStyle);
1659
void MainWindow::setAllowScreenCapture(bool state)
1661
m_allowScreenCapture = state;
1662
for (auto window : qApp->topLevelWindows()) {
1663
if (window->isVisible()) {
1664
osUtils->setPreventScreenCapture(window, !m_allowScreenCapture);
1667
m_ui->actionAllowScreenCapture->blockSignals(true);
1668
m_ui->actionAllowScreenCapture->setChecked(m_allowScreenCapture);
1669
m_ui->actionAllowScreenCapture->blockSignals(false);
1672
void MainWindow::focusWindowChanged(QWindow* window)
1674
if (window != windowHandle()) {
1675
m_lastFocusOutTime = Clock::currentMilliSecondsSinceEpoch();
1678
if (!osUtils->setPreventScreenCapture(window, !m_allowScreenCapture) && !m_allowScreenCapture) {
1679
displayGlobalMessage(QObject::tr("Warning: Failed to block screenshot capture on a top-level window."),
1680
MessageWidget::Error);
1684
void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason)
1686
if (!m_trayIconTriggerTimer.isActive()) {
1687
m_trayIconTriggerTimer.start(150);
1692
m_trayIconTriggerReason = reason;
1695
void MainWindow::processTrayIconTrigger()
1707
if (m_trayIconTriggerReason == QSystemTrayIcon::DoubleClick) {
1710
} else if (m_trayIconTriggerReason == QSystemTrayIcon::Trigger
1711
|| m_trayIconTriggerReason == QSystemTrayIcon::MiddleClick) {
1716
if (isHidden() || (Clock::currentMilliSecondsSinceEpoch() - m_lastFocusOutTime) <= 500) {
1719
if (hasFocus() || isHidden() || windowHandle()->isActive()) {
1728
void MainWindow::show()
1731
m_lastShowTime = Clock::currentMilliSecondsSinceEpoch();
1735
setWindowState(windowState() & ~Qt::WindowMinimized);
1736
macUtils()->toggleForegroundApp(true);
1738
QMainWindow::show();
1741
void MainWindow::hide()
1744
qint64 current_time = Clock::currentMilliSecondsSinceEpoch();
1745
if (current_time - m_lastShowTime < 50) {
1749
QMainWindow::hide();
1751
macUtils()->toggleForegroundApp(false);
1755
void MainWindow::hideWindow()
1757
saveWindowInformation();
1760
if (isTrayIconEnabled()) {
1763
if (QGuiApplication::platformName() != "xcb" && QGuiApplication::platformName() != "cocoa") {
1764
setWindowState(windowState() | Qt::WindowMinimized);
1771
if (config()->get(Config::Security_LockDatabaseMinimize).toBool()) {
1772
m_ui->tabWidget->lockDatabasesDelayed();
1776
void MainWindow::minimizeOrHide()
1778
if (config()->get(Config::GUI_MinimizeToTray).toBool()) {
1785
void MainWindow::toggleWindow()
1787
if (isVisible() && !isMinimized()) {
1792
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS) && (QT_VERSION < QT_VERSION_CHECK(5, 9, 0))
1797
static const auto isDesktopSessionUnity = qgetenv("XDG_CURRENT_DESKTOP") == "Unity";
1799
if (isDesktopSessionUnity && Tools::qtRuntimeVersion() < QT_VERSION_CHECK(5, 9, 0)
1800
&& !m_ui->menubar->isVisible()) {
1801
QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("com.canonical.AppMenu.Registrar"),
1802
QStringLiteral("/com/canonical/AppMenu/Registrar"),
1803
QStringLiteral("com.canonical.AppMenu.Registrar"),
1804
QStringLiteral("RegisterWindow"));
1805
QList<QVariant> args;
1806
args << QVariant::fromValue(static_cast<uint32_t>(winId()))
1807
<< QVariant::fromValue(QDBusObjectPath("/MenuBar/1"));
1808
msg.setArguments(args);
1809
QDBusConnection::sessionBus().send(msg);
1815
void MainWindow::closeModalWindow()
1817
if (qApp->modalWindow()) {
1818
qApp->modalWindow()->close();
1822
void MainWindow::lockDatabasesAfterInactivity()
1824
if (!m_ui->tabWidget->lockDatabases()) {
1825
m_inactivityTimer->activate();
1829
bool MainWindow::isTrayIconEnabled() const
1831
return m_trayIcon && m_trayIcon->isVisible();
1834
void MainWindow::displayGlobalMessage(const QString& text,
1835
MessageWidget::MessageType type,
1836
bool showClosebutton,
1837
int autoHideTimeout)
1839
m_ui->globalMessageWidget->setCloseButtonVisible(showClosebutton);
1840
m_ui->globalMessageWidget->showMessage(text, type, autoHideTimeout);
1843
void MainWindow::displayTabMessage(const QString& text,
1844
MessageWidget::MessageType type,
1845
bool showClosebutton,
1846
int autoHideTimeout)
1848
m_ui->tabWidget->currentDatabaseWidget()->showMessage(text, type, showClosebutton, autoHideTimeout);
1851
void MainWindow::hideGlobalMessage()
1853
m_ui->globalMessageWidget->hideMessage();
1856
void MainWindow::showYubiKeyPopup()
1858
displayGlobalMessage(tr("Please present or touch your YubiKey to continue…"),
1859
MessageWidget::Information,
1861
MessageWidget::DisableAutoHide);
1865
void MainWindow::hideYubiKeyPopup()
1867
hideGlobalMessage();
1871
void MainWindow::bringToFront()
1874
setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
1880
void MainWindow::handleScreenLock()
1882
if (config()->get(Config::Security_LockDatabaseScreenLock).toBool()) {
1883
lockDatabasesAfterInactivity();
1887
QStringList MainWindow::kdbxFilesFromUrls(const QList<QUrl>& urls)
1889
QStringList kdbxFiles;
1890
for (const QUrl& url : urls) {
1891
const QFileInfo fInfo(url.toLocalFile());
1892
const bool isKdbxFile = fInfo.isFile() && fInfo.suffix().toLower() == "kdbx";
1894
kdbxFiles.append(fInfo.absoluteFilePath());
1901
void MainWindow::dragEnterEvent(QDragEnterEvent* event)
1903
const QMimeData* mimeData = event->mimeData();
1904
if (mimeData->hasUrls()) {
1905
const QStringList kdbxFiles = kdbxFilesFromUrls(mimeData->urls());
1906
if (!kdbxFiles.isEmpty()) {
1907
event->acceptProposedAction();
1912
void MainWindow::dropEvent(QDropEvent* event)
1914
const QMimeData* mimeData = event->mimeData();
1915
if (mimeData->hasUrls()) {
1916
const QStringList kdbxFiles = kdbxFilesFromUrls(mimeData->urls());
1917
if (!kdbxFiles.isEmpty()) {
1918
event->acceptProposedAction();
1920
for (const QString& kdbxFile : kdbxFiles) {
1921
openDatabase(kdbxFile);
1926
void MainWindow::closeAllDatabases()
1928
m_ui->tabWidget->closeAllDatabaseTabs();
1931
void MainWindow::lockAllDatabases()
1933
lockDatabasesAfterInactivity();
1936
void MainWindow::displayDesktopNotification(const QString& msg, QString title, int msTimeoutHint)
1938
if (!m_trayIcon || !QSystemTrayIcon::supportsMessages()) {
1942
if (title.isEmpty()) {
1943
title = BaseWindowTitle;
1946
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
1947
m_trayIcon->showMessage(title, msg, icons()->applicationIcon(), msTimeoutHint);
1949
m_trayIcon->showMessage(title, msg, QSystemTrayIcon::Information, msTimeoutHint);
1953
void MainWindow::restartApp(const QString& message)
1955
auto ans = MessageBox::question(
1956
this, tr("Restart Application?"), message, MessageBox::Yes | MessageBox::No, MessageBox::Yes);
1957
if (ans == MessageBox::Yes) {
1958
m_appExitCalled = true;
1959
m_restartRequested = true;
1962
m_restartRequested = false;
1966
void MainWindow::initViewMenu()
1968
m_ui->actionThemeAuto->setData("auto");
1969
m_ui->actionThemeLight->setData("light");
1970
m_ui->actionThemeDark->setData("dark");
1971
m_ui->actionThemeClassic->setData("classic");
1973
auto themeActions = new QActionGroup(this);
1974
themeActions->addAction(m_ui->actionThemeAuto);
1975
themeActions->addAction(m_ui->actionThemeLight);
1976
themeActions->addAction(m_ui->actionThemeDark);
1977
themeActions->addAction(m_ui->actionThemeClassic);
1979
auto theme = config()->get(Config::GUI_ApplicationTheme).toString();
1980
for (auto action : themeActions->actions()) {
1981
if (action->data() == theme) {
1982
action->setChecked(true);
1987
connect(themeActions, &QActionGroup::triggered, this, [this, theme](QAction* action) {
1988
config()->set(Config::GUI_ApplicationTheme, action->data());
1989
if ((action->data() == "classic" || theme == "classic") && action->data() != theme) {
1990
restartApp(tr("You must restart the application to apply this setting. Would you like to restart now?"));
1992
kpxcApp->applyTheme();
1996
bool compact = config()->get(Config::GUI_CompactMode).toBool();
1997
m_ui->actionCompactMode->setChecked(compact);
1998
connect(m_ui->actionCompactMode, &QAction::toggled, this, [this, compact](bool checked) {
1999
config()->set(Config::GUI_CompactMode, checked);
2000
if (checked != compact) {
2001
restartApp(tr("You must restart the application to apply this setting. Would you like to restart now?"));
2006
m_ui->actionShowMenubar->setVisible(false);
2008
m_ui->actionShowMenubar->setChecked(!config()->get(Config::GUI_HideMenubar).toBool());
2009
connect(m_ui->actionShowMenubar, &QAction::toggled, this, [this](bool checked) {
2010
config()->set(Config::GUI_HideMenubar, !checked);
2011
applySettingsChanges();
2015
m_ui->actionShowToolbar->setChecked(!config()->get(Config::GUI_HideToolbar).toBool());
2016
connect(m_ui->actionShowToolbar, &QAction::toggled, this, [this](bool checked) {
2017
config()->set(Config::GUI_HideToolbar, !checked);
2018
applySettingsChanges();
2021
m_ui->actionShowPreviewPanel->setChecked(!config()->get(Config::GUI_HidePreviewPanel).toBool());
2022
connect(m_ui->actionShowPreviewPanel, &QAction::toggled, this, [](bool checked) {
2023
config()->set(Config::GUI_HidePreviewPanel, !checked);
2026
connect(m_ui->actionAlwaysOnTop, &QAction::toggled, this, [this](bool checked) {
2027
config()->set(Config::GUI_AlwaysOnTop, checked);
2029
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
2031
setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
2036
m_ui->actionAlwaysOnTop->setChecked(config()->get(Config::GUI_AlwaysOnTop).toBool());
2038
m_ui->actionHideUsernames->setChecked(config()->get(Config::GUI_HideUsernames).toBool());
2039
connect(m_ui->actionHideUsernames, &QAction::toggled, this, [](bool checked) {
2040
config()->set(Config::GUI_HideUsernames, checked);
2043
m_ui->actionHidePasswords->setChecked(config()->get(Config::GUI_HidePasswords).toBool());
2044
connect(m_ui->actionHidePasswords, &QAction::toggled, this, [](bool checked) {
2045
config()->set(Config::GUI_HidePasswords, checked);
2049
void MainWindow::initActionCollection()
2051
auto ac = ActionCollection::instance();
2053
m_ui->actionDatabaseNew,
2054
m_ui->actionDatabaseOpen,
2055
m_ui->actionDatabaseSave,
2056
m_ui->actionDatabaseSaveAs,
2057
m_ui->actionDatabaseSaveBackup,
2058
m_ui->actionDatabaseClose,
2059
m_ui->actionLockDatabase,
2060
m_ui->actionLockAllDatabases,
2061
m_ui->actionDatabaseSettings,
2062
m_ui->actionDatabaseSecurity,
2063
m_ui->actionReports,
2064
m_ui->actionPasskeys,
2065
m_ui->actionDatabaseMerge,
2066
m_ui->actionImportPasskey,
2067
m_ui->actionImportCsv,
2068
m_ui->actionImportOpVault,
2069
m_ui->actionImportKeePass1,
2070
m_ui->actionExportCsv,
2071
m_ui->actionExportHtml,
2072
m_ui->actionExportXML,
2075
m_ui->actionEntryNew,
2076
m_ui->actionEntryEdit,
2077
m_ui->actionEntryClone,
2078
m_ui->actionEntryDelete,
2079
m_ui->actionEntryCopyUsername,
2080
m_ui->actionEntryCopyPassword,
2081
m_ui->actionEntryCopyURL,
2082
m_ui->actionEntryCopyTitle,
2083
m_ui->actionEntryCopyNotes,
2084
m_ui->actionEntryTotp,
2085
m_ui->actionEntryTotpQRCode,
2086
m_ui->actionEntrySetupTotp,
2087
m_ui->actionEntryCopyTotp,
2088
m_ui->actionEntryCopyPasswordTotp,
2089
m_ui->actionEntryAutoTypeSequence,
2090
m_ui->actionEntryAutoTypeUsername,
2091
m_ui->actionEntryAutoTypeUsernameEnter,
2092
m_ui->actionEntryAutoTypePassword,
2093
m_ui->actionEntryAutoTypePasswordEnter,
2094
m_ui->actionEntryAutoTypeTOTP,
2095
m_ui->actionEntryDownloadIcon,
2096
m_ui->actionEntryOpenUrl,
2097
m_ui->actionEntryMoveUp,
2098
m_ui->actionEntryMoveDown,
2099
m_ui->actionEntryAddToAgent,
2100
m_ui->actionEntryRemoveFromAgent,
2101
m_ui->actionEntryRestore,
2103
m_ui->actionGroupNew,
2104
m_ui->actionGroupEdit,
2105
m_ui->actionGroupClone,
2106
m_ui->actionGroupDelete,
2107
m_ui->actionGroupDownloadFavicons,
2108
m_ui->actionGroupSortAsc,
2109
m_ui->actionGroupSortDesc,
2110
m_ui->actionGroupEmptyRecycleBin,
2112
m_ui->actionPasswordGenerator,
2113
m_ui->actionSettings,
2115
m_ui->actionThemeAuto,
2116
m_ui->actionThemeLight,
2117
m_ui->actionThemeDark,
2118
m_ui->actionThemeClassic,
2119
m_ui->actionCompactMode,
2121
m_ui->actionShowMenubar,
2123
m_ui->actionShowToolbar,
2124
m_ui->actionShowPreviewPanel,
2125
m_ui->actionAllowScreenCapture,
2126
m_ui->actionAlwaysOnTop,
2127
m_ui->actionHideUsernames,
2128
m_ui->actionHidePasswords,
2130
m_ui->actionGettingStarted,
2131
m_ui->actionUserGuide,
2132
m_ui->actionKeyboardShortcuts,
2133
m_ui->actionOnlineHelp,
2134
m_ui->actionCheckForUpdates,
2136
m_ui->actionBugReport,
2137
m_ui->actionAbout});
2140
for (const auto action : ac->actions()) {
2141
if (!action->shortcut().isEmpty()) {
2142
ac->setDefaultShortcut(action, action->shortcut());
2147
ac->setDefaultShortcut(m_ui->actionDatabaseOpen, QKeySequence::Open, Qt::CTRL + Qt::Key_O);
2148
ac->setDefaultShortcut(m_ui->actionDatabaseSave, QKeySequence::Save, Qt::CTRL + Qt::Key_S);
2149
ac->setDefaultShortcut(m_ui->actionDatabaseSaveAs, QKeySequence::SaveAs, Qt::CTRL + Qt::SHIFT + Qt::Key_S);
2150
ac->setDefaultShortcut(m_ui->actionDatabaseClose, QKeySequence::Close, Qt::CTRL + Qt::Key_W);
2151
ac->setDefaultShortcut(m_ui->actionSettings, QKeySequence::Preferences, Qt::CTRL + Qt::Key_Comma);
2152
ac->setDefaultShortcut(m_ui->actionQuit, QKeySequence::Quit, Qt::CTRL + Qt::Key_Q);
2153
ac->setDefaultShortcut(m_ui->actionEntryNew, QKeySequence::New, Qt::CTRL + Qt::Key_N);
2157
auto modifier = Qt::META;
2159
auto modifier = Qt::CTRL;
2163
ac->setDefaultShortcut(m_ui->actionDatabaseNew, Qt::CTRL + Qt::SHIFT + Qt::Key_N);
2164
ac->setDefaultShortcut(m_ui->actionDatabaseSettings, Qt::CTRL + Qt::SHIFT + Qt::Key_Comma);
2165
ac->setDefaultShortcut(m_ui->actionReports, Qt::CTRL + Qt::SHIFT + Qt::Key_R);
2166
ac->setDefaultShortcut(m_ui->actionLockDatabase, Qt::CTRL + Qt::Key_L);
2167
ac->setDefaultShortcut(m_ui->actionLockAllDatabases, Qt::CTRL + Qt::SHIFT + Qt::Key_L);
2168
ac->setDefaultShortcut(m_ui->actionEntryEdit, Qt::CTRL + Qt::Key_E);
2169
ac->setDefaultShortcut(m_ui->actionEntryDelete, Qt::CTRL + Qt::Key_D);
2170
ac->setDefaultShortcut(m_ui->actionEntryDelete, Qt::Key_Delete);
2171
ac->setDefaultShortcut(m_ui->actionEntryClone, Qt::CTRL + Qt::Key_K);
2172
ac->setDefaultShortcut(m_ui->actionEntryTotp, Qt::CTRL + Qt::SHIFT + Qt::Key_T);
2173
ac->setDefaultShortcut(m_ui->actionEntryDownloadIcon, Qt::CTRL + Qt::SHIFT + Qt::Key_D);
2174
ac->setDefaultShortcut(m_ui->actionEntryCopyTotp, Qt::CTRL + Qt::Key_T);
2175
ac->setDefaultShortcut(m_ui->actionEntryCopyPasswordTotp, Qt::CTRL + Qt::Key_Y);
2176
ac->setDefaultShortcut(m_ui->actionEntryMoveUp, Qt::CTRL + Qt::ALT + Qt::Key_Up);
2177
ac->setDefaultShortcut(m_ui->actionEntryMoveDown, Qt::CTRL + Qt::ALT + Qt::Key_Down);
2178
ac->setDefaultShortcut(m_ui->actionEntryCopyUsername, Qt::CTRL + Qt::Key_B);
2179
ac->setDefaultShortcut(m_ui->actionEntryCopyPassword, Qt::CTRL + Qt::Key_C);
2180
ac->setDefaultShortcut(m_ui->actionEntryCopyTitle, Qt::CTRL + Qt::Key_I);
2181
ac->setDefaultShortcut(m_ui->actionEntryAutoTypeSequence, Qt::CTRL + Qt::SHIFT + Qt::Key_V);
2182
ac->setDefaultShortcut(m_ui->actionEntryOpenUrl, Qt::CTRL + Qt::SHIFT + Qt::Key_U);
2183
ac->setDefaultShortcut(m_ui->actionEntryCopyURL, Qt::CTRL + Qt::Key_U);
2184
ac->setDefaultShortcut(m_ui->actionEntryRestore, Qt::CTRL + Qt::Key_R);
2185
ac->setDefaultShortcut(m_ui->actionEntryAddToAgent, modifier + Qt::Key_H);
2186
ac->setDefaultShortcut(m_ui->actionEntryRemoveFromAgent, modifier + Qt::SHIFT + Qt::Key_H);
2188
QTimer::singleShot(1, ac, &ActionCollection::restoreShortcuts);
2191
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2193
MainWindowEventFilter::MainWindowEventFilter(QObject* parent)
2202
bool MainWindowEventFilter::eventFilter(QObject* watched, QEvent* event)
2204
auto* mainWindow = getMainWindow();
2205
if (!mainWindow || !mainWindow->m_ui) {
2206
return QObject::eventFilter(watched, event);
2209
auto eventType = event->type();
2210
if (eventType == QEvent::MouseButtonPress) {
2211
auto mouseEvent = dynamic_cast<QMouseEvent*>(event);
2212
if (watched == mainWindow->m_ui->menubar) {
2213
if (!mainWindow->m_ui->menubar->actionAt(mouseEvent->pos())) {
2214
mainWindow->windowHandle()->startSystemMove();
2217
} else if (watched == mainWindow->m_ui->toolBar) {
2218
if (!mainWindow->m_ui->toolBar->isMovable() || mainWindow->m_ui->toolBar->cursor() != Qt::SizeAllCursor) {
2219
mainWindow->windowHandle()->startSystemMove();
2222
} else if (watched == mainWindow->m_ui->tabWidget->tabBar()) {
2223
if (mainWindow->m_ui->tabWidget->tabBar()->tabAt(mouseEvent->pos()) == -1) {
2224
mainWindow->windowHandle()->startSystemMove();
2228
} else if (eventType == QEvent::KeyRelease) {
2229
if (watched == mainWindow) {
2230
auto keyEvent = dynamic_cast<QKeyEvent*>(event);
2231
if (keyEvent->key() == Qt::Key_Alt && !keyEvent->modifiers()
2232
&& config()->get(Config::GUI_HideMenubar).toBool()) {
2233
auto menubar = mainWindow->m_ui->menubar;
2234
menubar->setVisible(!menubar->isVisible());
2235
if (menubar->isVisible()) {
2236
menubar->setActiveAction(mainWindow->m_ui->menuFile->menuAction());
2243
return QObject::eventFilter(watched, event);