diff --git a/COPYING b/COPYING index 797bc7b07a..6eec65ea40 100644 --- a/COPYING +++ b/COPYING @@ -146,6 +146,8 @@ License: MIT Files: share/icons/application/scalable/actions/chevron-double-down.svg share/icons/application/scalable/actions/chevron-double-right.svg + share/icons/application/scalable/actions/database-lock.svg + share/icons/application/scalable/actions/database-lock-all.svg share/icons/application/scalable/actions/document-close.svg share/icons/application/scalable/actions/document-edit.svg share/icons/application/scalable/actions/document-export.svg diff --git a/share/icons/application/scalable/actions/database-lock-all.svg b/share/icons/application/scalable/actions/database-lock-all.svg new file mode 100644 index 0000000000..fcd67aad18 --- /dev/null +++ b/share/icons/application/scalable/actions/database-lock-all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/share/icons/application/scalable/actions/database-lock.svg b/share/icons/application/scalable/actions/database-lock.svg index 1c1c86e8db..0f59c4c37b 100644 --- a/share/icons/application/scalable/actions/database-lock.svg +++ b/share/icons/application/scalable/actions/database-lock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/share/icons/icons.qrc b/share/icons/icons.qrc index b35ad77be2..a0d6905052 100644 --- a/share/icons/icons.qrc +++ b/share/icons/icons.qrc @@ -15,6 +15,7 @@ application/scalable/actions/configure.svg application/scalable/actions/database-change-key.svg application/scalable/actions/database-lock.svg + application/scalable/actions/database-lock-all.svg application/scalable/actions/database-merge.svg application/scalable/actions/dialog-close.svg application/scalable/actions/dialog-ok.svg diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index 098d5ea153..dc58e114d3 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -5142,10 +5142,6 @@ Are you sure you want to continue with this file? Download &Favicon - - &Lock Databases - - &CSV Fileā€¦ @@ -5298,6 +5294,14 @@ We recommend you use the AppImage available on our downloads page. Restore Entry(s) + + &Lock Database + + + + Lock &All Databases + + ManageDatabase diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index bcdf8f10de..74ecc0a60e 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -179,6 +179,33 @@ void DatabaseTabWidget::addDatabaseTab(const QString& filePath, updateLastDatabases(filePath); } +/** + * Tries to lock the database at the given index and if + * it succeeds proceed to switch to the first unlocked database tab + */ +void DatabaseTabWidget::lockAndSwitchToFirstUnlockedDatabase(int index) +{ + if (index == -1) { + index = currentIndex(); + } + auto dbWidget = databaseWidgetFromIndex(index); + if (!dbWidget) { + return; + } + + if (dbWidget->isLocked()) { + // Database is already locked, act like lock all databases instead + lockDatabases(); + } else if (dbWidget->lock()) { + for (int i = 0, c = count(); i < c; ++i) { + if (!databaseWidgetFromIndex(i)->isLocked()) { + setCurrentIndex(i); + return; + } + } + } +} + /** * Add a new database tab containing the given DatabaseWidget * @param filePath diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index faad74552f..ffcd9748f0 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -47,6 +47,7 @@ class DatabaseTabWidget : public QTabWidget bool hasLockableDatabases() const; public slots: + void lockAndSwitchToFirstUnlockedDatabase(int index = -1); void addDatabaseTab(const QString& filePath, bool inBackground = false, const QString& password = {}, diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 5aa39ad58a..4d88b6e683 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -168,6 +168,15 @@ MainWindow::MainWindow() autoTypeButton->setPopupMode(QToolButton::MenuButtonPopup); } + auto databaseLockMenu = new QMenu({}, this); + databaseLockMenu->addAction(m_ui->actionLockAllDatabases); + m_ui->actionLockDatabaseToolbar->setMenu(databaseLockMenu); + auto databaseLockButton = + qobject_cast(m_ui->toolBar->widgetForAction(m_ui->actionLockDatabaseToolbar)); + if (databaseLockButton) { + databaseLockButton->setPopupMode(QToolButton::MenuButtonPopup); + } + restoreGeometry(config()->get(Config::GUI_MainWindowGeometry).toByteArray()); restoreState(config()->get(Config::GUI_MainWindowState).toByteArray()); @@ -261,7 +270,8 @@ MainWindow::MainWindow() 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->actionLockDatabases->setShortcut(Qt::CTRL + Qt::Key_L); + 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); @@ -369,7 +379,9 @@ MainWindow::MainWindow() m_ui->actionReports->setIcon(icons()->icon("reports")); m_ui->actionDatabaseSettings->setIcon(icons()->icon("document-edit")); m_ui->actionDatabaseSecurity->setIcon(icons()->icon("database-change-key")); - m_ui->actionLockDatabases->setIcon(icons()->icon("database-lock")); + m_ui->actionLockDatabase->setIcon(icons()->icon("database-lock")); + m_ui->actionLockDatabaseToolbar->setIcon(icons()->icon("database-lock")); + m_ui->actionLockAllDatabases->setIcon(icons()->icon("database-lock-all")); m_ui->actionQuit->setIcon(icons()->icon("application-exit")); m_ui->actionDatabaseMerge->setIcon(icons()->icon("database-merge")); m_ui->menuImport->setIcon(icons()->icon("document-import")); @@ -460,7 +472,10 @@ MainWindow::MainWindow() connect(m_ui->actionImportOpVault, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importOpVaultDatabase())); connect(m_ui->actionExportCsv, SIGNAL(triggered()), m_ui->tabWidget, SLOT(exportToCsv())); connect(m_ui->actionExportHtml, SIGNAL(triggered()), m_ui->tabWidget, SLOT(exportToHtml())); - connect(m_ui->actionLockDatabases, SIGNAL(triggered()), m_ui->tabWidget, SLOT(lockDatabases())); + connect( + m_ui->actionLockDatabase, SIGNAL(triggered()), m_ui->tabWidget, SLOT(lockAndSwitchToFirstUnlockedDatabase())); + connect(m_ui->actionLockDatabaseToolbar, SIGNAL(triggered()), m_ui->actionLockDatabase, SIGNAL(triggered())); + connect(m_ui->actionLockAllDatabases, SIGNAL(triggered()), m_ui->tabWidget, SLOT(lockDatabases())); connect(m_ui->actionQuit, SIGNAL(triggered()), SLOT(appExit())); m_actionMultiplexer.connect(m_ui->actionEntryNew, SIGNAL(triggered()), SLOT(createEntry())); @@ -781,7 +796,9 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionDatabaseOpen->setEnabled(inDatabaseTabWidgetOrWelcomeWidget); m_ui->menuRecentDatabases->setEnabled(inDatabaseTabWidgetOrWelcomeWidget); m_ui->menuImport->setEnabled(inDatabaseTabWidgetOrWelcomeWidget); - m_ui->actionLockDatabases->setEnabled(m_ui->tabWidget->hasLockableDatabases()); + m_ui->actionLockDatabase->setEnabled(m_ui->tabWidget->hasLockableDatabases()); + m_ui->actionLockDatabaseToolbar->setEnabled(m_ui->tabWidget->hasLockableDatabases()); + m_ui->actionLockAllDatabases->setEnabled(m_ui->tabWidget->hasLockableDatabases()); if (inDatabaseTabWidget && m_ui->tabWidget->currentIndex() != -1) { DatabaseWidget* dbWidget = m_ui->tabWidget->currentDatabaseWidget(); @@ -924,6 +941,9 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionExportCsv->setEnabled(false); m_ui->actionExportHtml->setEnabled(false); m_ui->actionDatabaseMerge->setEnabled(false); + // Only disable the action in the database menu so that the + // menu remains active in the toolbar, if necessary + m_ui->actionLockDatabase->setEnabled(false); m_searchWidgetAction->setEnabled(false); break; @@ -1392,7 +1412,7 @@ void MainWindow::updateTrayIcon() menu->addAction(actionToggle); actionToggle->setIcon(icons()->icon("keepassxc-monochrome-dark")); - menu->addAction(m_ui->actionLockDatabases); + menu->addAction(m_ui->actionLockAllDatabases); #ifdef Q_OS_MACOS auto actionQuit = new QAction(tr("Quit KeePassXC"), menu); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index f83e8077d3..c877313b92 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -216,7 +216,7 @@ 0 0 800 - 21 + 22 @@ -254,6 +254,9 @@ + + + @@ -355,7 +358,6 @@ &Tools - @@ -409,6 +411,7 @@ + @@ -419,8 +422,6 @@ - - @@ -773,12 +774,20 @@ Open &URL - + + + false + + + &Lock Database + + + false - &Lock Databases + Lock &All Databases @@ -1084,6 +1093,14 @@ Ctrl+R + + + false + + + &Lock Database + + diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 7c672c6d3a..34b047e9ba 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -1384,7 +1384,7 @@ void TestGui::testDatabaseLocking() QString origDbName = m_tabWidget->tabText(0); MessageBox::setNextAnswer(MessageBox::Cancel); - triggerAction("actionLockDatabases"); + triggerAction("actionLockAllDatabases"); QCOMPARE(m_tabWidget->tabText(0), origDbName + " [Locked]");