diff --git a/COPYING b/COPYING
index 3436ec3e4..23dd1d168 100644
--- a/COPYING
+++ b/COPYING
@@ -155,6 +155,7 @@ Files: share/icons/application/scalable/actions/application-exit.svg
share/icons/application/scalable/actions/database-lock-all.svg
share/icons/application/scalable/actions/database-merge.svg
share/icons/application/scalable/actions/database-search.svg
+ share/icons/application/scalable/actions/database-settings.svg
share/icons/application/scalable/actions/dialog-close.svg
share/icons/application/scalable/actions/dialog-ok.svg
share/icons/application/scalable/actions/document-close.svg
@@ -243,9 +244,9 @@ Files: share/icons/application/scalable/actions/application-exit.svg
share/icons/application/scalable/actions/lock-open-alert.svg
share/icons/application/scalable/actions/lock-open.svg
share/icons/application/scalable/actions/lock.svg
-Copyright: 2019 Austin Andrews
-License: SIL OPEN FONT LICENSE Version 1.1
-Comment: Taken from Material Design icon set (https://github.com/templarian/MaterialDesign/)
+Copyright: 2023 Pictogrammers
+License: Apache-2.0
+Comment: Some icons are modified to fit KeePassXC design (https://pictogrammers.com/library/mdi/)
Files: src/streams/qtiocompressor.*
src/streams/QtIOCompressor
diff --git a/share/icons/application/scalable/actions/database-settings.svg b/share/icons/application/scalable/actions/database-settings.svg
new file mode 100644
index 000000000..7bd0b9cab
--- /dev/null
+++ b/share/icons/application/scalable/actions/database-settings.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/share/icons/application/scalable/actions/entry-delete.svg b/share/icons/application/scalable/actions/entry-delete.svg
index 66ae96f1b..f052113af 100644
--- a/share/icons/application/scalable/actions/entry-delete.svg
+++ b/share/icons/application/scalable/actions/entry-delete.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/share/icons/application/scalable/actions/reports.svg b/share/icons/application/scalable/actions/reports.svg
index 3d62971d2..545352534 100644
--- a/share/icons/application/scalable/actions/reports.svg
+++ b/share/icons/application/scalable/actions/reports.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 40e0d5416..4e86186d6 100644
--- a/share/icons/icons.qrc
+++ b/share/icons/icons.qrc
@@ -20,6 +20,7 @@
application/scalable/actions/database-lock-all.svg
application/scalable/actions/database-merge.svg
application/scalable/actions/database-search.svg
+ application/scalable/actions/database-settings.svg
application/scalable/actions/dialog-close.svg
application/scalable/actions/dialog-ok.svg
application/scalable/actions/document-close.svg
diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts
index 6bb5ae88d..1bb7afc75 100644
--- a/share/translations/keepassxc_en.ts
+++ b/share/translations/keepassxc_en.ts
@@ -1689,6 +1689,10 @@ Are you sure you want to continue with this file?.
+
+
+
+
DatabaseSettingsWidgetBrowser
diff --git a/src/fdosecrets/objects/Service.cpp b/src/fdosecrets/objects/Service.cpp
index ae1e9d4b6..e3fcefeb5 100644
--- a/src/fdosecrets/objects/Service.cpp
+++ b/src/fdosecrets/objects/Service.cpp
@@ -543,7 +543,7 @@ namespace FdoSecrets
}
// switch selected to current
m_databases->setCurrentWidget(dbWidget);
- m_databases->showDatabaseSettings();
+ m_databases->showDatabaseSettings(true);
// open settings (switch from app settings to m_dbTabs)
m_plugin->emitRequestSwitchToDatabases();
diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp
index b9b26d377..6e4574278 100644
--- a/src/gui/DatabaseTabWidget.cpp
+++ b/src/gui/DatabaseTabWidget.cpp
@@ -537,21 +537,29 @@ bool DatabaseTabWidget::warnOnExport()
return ans == MessageBox::Yes;
}
+void DatabaseTabWidget::showDatabaseReports(bool state)
+{
+ if (state) {
+ currentDatabaseWidget()->switchToDatabaseReports();
+ } else {
+ currentDatabaseWidget()->switchToMainView();
+ }
+}
+
+void DatabaseTabWidget::showDatabaseSettings(bool state)
+{
+ if (state) {
+ currentDatabaseWidget()->switchToDatabaseSettings();
+ } else {
+ currentDatabaseWidget()->switchToMainView();
+ }
+}
+
void DatabaseTabWidget::showDatabaseSecurity()
{
currentDatabaseWidget()->switchToDatabaseSecurity();
}
-void DatabaseTabWidget::showDatabaseReports()
-{
- currentDatabaseWidget()->switchToDatabaseReports();
-}
-
-void DatabaseTabWidget::showDatabaseSettings()
-{
- currentDatabaseWidget()->switchToDatabaseSettings();
-}
-
#ifdef WITH_XC_BROWSER_PASSKEYS
void DatabaseTabWidget::showPasskeys()
{
diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h
index aa8542dd9..792475861 100644
--- a/src/gui/DatabaseTabWidget.h
+++ b/src/gui/DatabaseTabWidget.h
@@ -82,9 +82,9 @@ public slots:
void unlockAnyDatabaseInDialog(DatabaseOpenDialog::Intent intent);
void relockPendingDatabase();
+ void showDatabaseReports(bool state);
+ void showDatabaseSettings(bool state);
void showDatabaseSecurity();
- void showDatabaseReports();
- void showDatabaseSettings();
#ifdef WITH_XC_BROWSER_PASSKEYS
void showPasskeys();
void importPasskey();
diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp
index b497420b6..68b0fdcc2 100644
--- a/src/gui/DatabaseWidget.cpp
+++ b/src/gui/DatabaseWidget.cpp
@@ -265,15 +265,26 @@ QSharedPointer DatabaseWidget::database() const
DatabaseWidget::Mode DatabaseWidget::currentMode() const
{
- if (currentWidget() == nullptr) {
- return Mode::None;
- } else if (currentWidget() == m_mainWidget) {
- return Mode::ViewMode;
- } else if (currentWidget() == m_databaseOpenWidget) {
- return Mode::LockedMode;
+ auto mode = Mode::None;
+ auto widget = currentWidget();
+ if (widget == m_mainWidget) {
+ mode = Mode::ViewMode;
+ } else if (widget == m_databaseOpenWidget) {
+ mode = Mode::LockedMode;
+ } else if (widget == m_reportsDialog) {
+ mode = Mode::ReportsMode;
+ } else if (widget == m_databaseSettingDialog) {
+ mode = Mode::DatabaseSettingsMode;
+ } else if (widget == m_editEntryWidget) {
+ mode = Mode::EditEntryMode;
+ } else if (widget == m_editGroupWidget) {
+ mode = Mode::EditGroupMode;
} else {
- return Mode::EditMode;
+ // We are missing a condition if we reach here
+ Q_ASSERT(false);
}
+
+ return mode;
}
bool DatabaseWidget::isLocked() const
@@ -1014,7 +1025,7 @@ void DatabaseWidget::openUrlForEntry(Entry* entry)
}
}
-Entry* DatabaseWidget::currentSelectedEntry()
+Entry* DatabaseWidget::currentSelectedEntry() const
{
if (currentWidget() == m_editEntryWidget) {
return m_editEntryWidget->currentEntry();
@@ -1516,14 +1527,18 @@ void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::Mod
void DatabaseWidget::switchToDatabaseReports()
{
- m_reportsDialog->load(m_db);
- setCurrentWidget(m_reportsDialog);
+ if (currentMode() != Mode::ReportsMode) {
+ m_reportsDialog->load(m_db);
+ setCurrentWidget(m_reportsDialog);
+ }
}
void DatabaseWidget::switchToDatabaseSettings()
{
- m_databaseSettingDialog->load(m_db);
- setCurrentWidget(m_databaseSettingDialog);
+ if (currentMode() != Mode::DatabaseSettingsMode) {
+ m_databaseSettingDialog->load(m_db);
+ setCurrentWidget(m_databaseSettingDialog);
+ }
}
void DatabaseWidget::switchToOpenDatabase()
@@ -1865,16 +1880,13 @@ void DatabaseWidget::onEntryChanged(Entry* entry)
bool DatabaseWidget::canCloneCurrentGroup() const
{
- bool isRootGroup = m_db->rootGroup() == m_groupView->currentGroup();
- // bool isRecycleBin = isRecycleBinSelected();
-
- return !isRootGroup;
+ auto currentGroup = m_groupView->currentGroup();
+ return currentGroup != m_db->rootGroup() && currentGroup != m_db->metadata()->recycleBin();
}
bool DatabaseWidget::canDeleteCurrentGroup() const
{
- bool isRootGroup = m_db->rootGroup() == m_groupView->currentGroup();
- return !isRootGroup;
+ return currentGroup() != m_db->rootGroup();
}
Group* DatabaseWidget::currentGroup() const
@@ -2483,7 +2495,9 @@ void DatabaseWidget::hideMessage()
bool DatabaseWidget::isRecycleBinSelected() const
{
- return m_groupView->currentGroup() && m_groupView->currentGroup() == m_db->metadata()->recycleBin();
+ auto group = currentGroup();
+ auto entry = currentSelectedEntry();
+ return (group && group->isRecycled()) || (entry && entry->isRecycled());
}
void DatabaseWidget::emptyRecycleBin()
diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h
index 6622394e1..b66589d7e 100644
--- a/src/gui/DatabaseWidget.h
+++ b/src/gui/DatabaseWidget.h
@@ -64,8 +64,11 @@ public:
{
None,
ViewMode,
- EditMode,
- LockedMode
+ EditEntryMode,
+ EditGroupMode,
+ LockedMode,
+ ReportsMode,
+ DatabaseSettingsMode
};
explicit DatabaseWidget(QSharedPointer db, QWidget* parent = nullptr);
@@ -106,7 +109,7 @@ public:
QStringList customEntryAttributes() const;
bool isEditWidgetModified() const;
void clearAllWidgets();
- Entry* currentSelectedEntry();
+ Entry* currentSelectedEntry() const;
bool currentEntryHasTitle();
bool currentEntryHasUsername();
bool currentEntryHasPassword();
diff --git a/src/gui/EditWidget.cpp b/src/gui/EditWidget.cpp
index 3a42154c9..291a08b17 100644
--- a/src/gui/EditWidget.cpp
+++ b/src/gui/EditWidget.cpp
@@ -31,6 +31,7 @@ EditWidget::EditWidget(QWidget* parent)
setModified(false);
m_ui->messageWidget->setHidden(true);
+ m_ui->headerLabel->setHidden(true);
QFont headerLabelFont = m_ui->headerLabel->font();
headerLabelFont.setBold(true);
@@ -118,6 +119,7 @@ void EditWidget::setCurrentPage(int index)
void EditWidget::setHeadline(const QString& text)
{
+ m_ui->headerLabel->setHidden(text.isEmpty());
m_ui->headerLabel->setText(text);
}
diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp
index 9e38dcda9..5613dab30 100644
--- a/src/gui/MainWindow.cpp
+++ b/src/gui/MainWindow.cpp
@@ -127,6 +127,8 @@ MainWindow::MainWindow()
m_entryContextMenu = new QMenu(this);
m_entryContextMenu->setSeparatorsCollapsible(true);
+ m_entryContextMenu->addAction(m_ui->actionEntryRestore);
+ m_entryContextMenu->addSeparator();
m_entryContextMenu->addAction(m_ui->actionEntryCopyUsername);
m_entryContextMenu->addAction(m_ui->actionEntryCopyPassword);
m_entryContextMenu->addAction(m_ui->actionEntryCopyURL);
@@ -154,8 +156,6 @@ MainWindow::MainWindow()
m_entryContextMenu->addSeparator();
m_entryContextMenu->addAction(m_ui->actionEntryAddToAgent);
m_entryContextMenu->addAction(m_ui->actionEntryRemoveFromAgent);
- m_entryContextMenu->addSeparator();
- m_entryContextMenu->addAction(m_ui->actionEntryRestore);
m_entryNewContextMenu = new QMenu(this);
m_entryNewContextMenu->addAction(m_ui->actionEntryNew);
@@ -346,8 +346,10 @@ MainWindow::MainWindow()
m_ui->actionDatabaseSaveBackup->setIcon(icons()->icon("document-save-copy"));
m_ui->actionDatabaseClose->setIcon(icons()->icon("document-close"));
m_ui->actionReports->setIcon(icons()->icon("reports"));
- m_ui->actionDatabaseSettings->setIcon(icons()->icon("document-edit"));
+ m_ui->actionDatabaseSettings->setIcon(icons()->icon("database-settings"));
m_ui->actionDatabaseSecurity->setIcon(icons()->icon("database-change-key"));
+ m_ui->actionPasskeys->setIcon(icons()->icon("passkey"));
+ m_ui->actionImportPasskey->setIcon(icons()->icon("document-import"));
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"));
@@ -357,6 +359,12 @@ MainWindow::MainWindow()
m_ui->actionImport->setIcon(icons()->icon("document-import"));
m_ui->menuExport->setIcon(icons()->icon("document-export"));
+#ifndef WITH_XC_BROWSER_PASSKEYS
+ m_ui->actionPasskeys->setVisible(false);
+ m_ui->actionImportPasskey->setVisible(false);
+ m_ui->actionEntryImportPasskey->setVisible(false);
+#endif
+
m_ui->actionEntryNew->setIcon(icons()->icon("entry-new"));
m_ui->actionEntryClone->setIcon(icons()->icon("entry-clone"));
m_ui->actionEntryEdit->setIcon(icons()->icon("entry-edit"));
@@ -381,6 +389,7 @@ MainWindow::MainWindow()
m_ui->actionEntryCopyPasswordTotp->setIcon(icons()->icon("totp-copy-password"));
m_ui->actionEntryTotpQRCode->setIcon(icons()->icon("qrcode"));
m_ui->actionEntrySetupTotp->setIcon(icons()->icon("totp-edit"));
+ m_ui->actionEntryImportPasskey->setIcon(icons()->icon("document-import"));
m_ui->actionEntryAddToAgent->setIcon(icons()->icon("utilities-terminal"));
m_ui->actionEntryRemoveFromAgent->setIcon(icons()->icon("utilities-terminal"));
m_ui->menuTags->setIcon(icons()->icon("tag-multiple"));
@@ -415,11 +424,10 @@ MainWindow::MainWindow()
m_ui->actionEntryRemovePasskey->setIcon(icons()->icon("document-close"));
#endif
- m_actionMultiplexer.connect(
- SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(setMenuActionState(DatabaseWidget::Mode)));
- m_actionMultiplexer.connect(SIGNAL(groupChanged()), this, SLOT(setMenuActionState()));
- m_actionMultiplexer.connect(SIGNAL(entrySelectionChanged()), this, SLOT(setMenuActionState()));
- m_actionMultiplexer.connect(SIGNAL(databaseNonDataChanged()), this, SLOT(setMenuActionState()));
+ m_actionMultiplexer.connect(SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(updateMenuActionState()));
+ m_actionMultiplexer.connect(SIGNAL(groupChanged()), this, SLOT(updateMenuActionState()));
+ m_actionMultiplexer.connect(SIGNAL(entrySelectionChanged()), this, SLOT(updateMenuActionState()));
+ m_actionMultiplexer.connect(SIGNAL(databaseNonDataChanged()), this, SLOT(updateMenuActionState()));
m_actionMultiplexer.connect(SIGNAL(groupContextMenuRequested(QPoint)), this, SLOT(showGroupContextMenu(QPoint)));
m_actionMultiplexer.connect(SIGNAL(entryContextMenuRequested(QPoint)), this, SLOT(showEntryContextMenu(QPoint)));
m_actionMultiplexer.connect(SIGNAL(groupChanged()), this, SLOT(updateEntryCountLabel()));
@@ -438,11 +446,11 @@ MainWindow::MainWindow()
connect(m_ui->tabWidget, SIGNAL(tabNameChanged()), SLOT(updateWindowTitle()));
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(updateWindowTitle()));
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(databaseTabChanged(int)));
- connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(setMenuActionState()));
+ connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(updateMenuActionState()));
connect(m_ui->tabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), SLOT(databaseStatusChanged(DatabaseWidget*)));
connect(m_ui->tabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), SLOT(databaseStatusChanged(DatabaseWidget*)));
connect(m_ui->tabWidget, SIGNAL(tabVisibilityChanged(bool)), SLOT(updateToolbarSeparatorVisibility()));
- connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(setMenuActionState()));
+ connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(updateMenuActionState()));
connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(updateWindowTitle()));
connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(updateToolbarSeparatorVisibility()));
connect(m_ui->settingsWidget, SIGNAL(accepted()), SLOT(applySettingsChanges()));
@@ -457,9 +465,9 @@ MainWindow::MainWindow()
connect(m_ui->actionDatabaseSaveBackup, SIGNAL(triggered()), m_ui->tabWidget, SLOT(saveDatabaseBackup()));
connect(m_ui->actionDatabaseClose, SIGNAL(triggered()), m_ui->tabWidget, SLOT(closeCurrentDatabaseTab()));
connect(m_ui->actionDatabaseMerge, SIGNAL(triggered()), m_ui->tabWidget, SLOT(mergeDatabase()));
+ connect(m_ui->actionDatabaseSettings, SIGNAL(toggled(bool)), m_ui->tabWidget, SLOT(showDatabaseSettings(bool)));
connect(m_ui->actionDatabaseSecurity, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showDatabaseSecurity()));
- connect(m_ui->actionReports, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showDatabaseReports()));
- connect(m_ui->actionDatabaseSettings, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showDatabaseSettings()));
+ connect(m_ui->actionReports, SIGNAL(toggled(bool)), m_ui->tabWidget, SLOT(showDatabaseReports(bool)));
#ifdef WITH_XC_BROWSER_PASSKEYS
connect(m_ui->actionPasskeys, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showPasskeys()));
connect(m_ui->actionImportPasskey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importPasskey()));
@@ -668,7 +676,7 @@ MainWindow::MainWindow()
statusBar()->addPermanentWidget(m_statusBarLabel);
restoreConfigState();
- setMenuActionState();
+ updateMenuActionState();
}
MainWindow::~MainWindow()
@@ -844,251 +852,149 @@ void MainWindow::openDatabase(const QString& filePath, const QString& password,
m_ui->tabWidget->addDatabaseTab(filePath, false, password, keyfile);
}
-void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
+void MainWindow::updateMenuActionState()
{
+ // MainWindow State
int currentIndex = m_ui->stackedWidget->currentIndex();
+ bool hasLockableDatabase = m_ui->tabWidget->hasLockableDatabases();
+ bool inAppSettings = (currentIndex == SettingsScreen);
+ bool inPasswordGenerator = (currentIndex == PasswordGeneratorScreen);
- bool inDatabaseTabWidget = (currentIndex == DatabaseTabScreen);
- bool inWelcomeWidget = (currentIndex == WelcomeScreen);
- bool inDatabaseTabWidgetOrWelcomeWidget = inDatabaseTabWidget || inWelcomeWidget;
+ auto dbWidget = (currentIndex == DatabaseTabScreen ? m_ui->tabWidget->currentDatabaseWidget() : nullptr);
+ auto dbMode = (dbWidget ? dbWidget->currentMode() : DatabaseWidget::Mode::None);
- m_ui->actionDatabaseClose->setEnabled(true);
- m_ui->actionDatabaseMerge->setEnabled(inDatabaseTabWidget);
- m_ui->menuRemoteSync->setEnabled(inDatabaseTabWidget);
- m_ui->actionDatabaseNew->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
- m_ui->actionDatabaseOpen->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
- m_ui->menuRecentDatabases->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
- m_ui->actionImport->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
- m_ui->actionLockDatabase->setEnabled(m_ui->tabWidget->hasLockableDatabases());
- m_ui->actionLockDatabaseToolbar->setEnabled(m_ui->tabWidget->hasLockableDatabases());
- m_ui->actionLockAllDatabases->setEnabled(m_ui->tabWidget->hasLockableDatabases());
+ // Database State
+ bool databaseUnlocked = (dbWidget && !dbWidget->isLocked());
+ bool inDatabase = (dbMode == DatabaseWidget::Mode::ViewMode);
+ bool inDatabaseSettings = (dbMode == DatabaseWidget::Mode::DatabaseSettingsMode);
+ bool inReports = (dbMode == DatabaseWidget::Mode::ReportsMode);
+ bool editingEntry = (dbMode == DatabaseWidget::Mode::EditEntryMode);
- if (inDatabaseTabWidget && m_ui->tabWidget->currentIndex() != -1) {
- DatabaseWidget* dbWidget = m_ui->tabWidget->currentDatabaseWidget();
- Q_ASSERT(dbWidget);
+ // Synchronize toggle buttons
+ m_ui->actionDatabaseSettings->blockSignals(true);
+ m_ui->actionPasswordGenerator->blockSignals(true);
+ m_ui->actionReports->blockSignals(true);
+ m_ui->actionSettings->blockSignals(true);
- if (mode == DatabaseWidget::Mode::None) {
- mode = dbWidget->currentMode();
- }
+ m_ui->actionDatabaseSettings->setChecked(inDatabaseSettings);
+ m_ui->actionPasswordGenerator->setChecked(inPasswordGenerator);
+ m_ui->actionReports->setChecked(inReports);
+ m_ui->actionSettings->setChecked(inAppSettings);
- switch (mode) {
- case DatabaseWidget::Mode::ViewMode: {
- bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1;
- bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0;
- bool groupSelected = dbWidget->isGroupSelected();
- bool currentGroupHasChildren = dbWidget->currentGroup()->hasChildren();
- bool currentGroupHasEntries = !dbWidget->currentGroup()->entries().isEmpty();
- bool recycleBinSelected = dbWidget->isRecycleBinSelected();
- bool sorted = dbWidget->isSorted();
- int entryIndex = dbWidget->currentEntryIndex();
- int numEntries = dbWidget->currentGroup()->entries().size();
+ m_ui->actionDatabaseSettings->blockSignals(false);
+ m_ui->actionPasswordGenerator->blockSignals(false);
+ m_ui->actionReports->blockSignals(false);
+ m_ui->actionSettings->blockSignals(false);
- m_ui->actionEntryNew->setEnabled(true);
- m_ui->actionEntryClone->setEnabled(singleEntrySelected);
- m_ui->actionEntryEdit->setEnabled(singleEntrySelected);
- m_ui->actionEntryDelete->setEnabled(entriesSelected);
- m_ui->actionEntryRestore->setVisible(entriesSelected && recycleBinSelected);
- m_ui->actionEntryRestore->setEnabled(entriesSelected && recycleBinSelected);
- m_ui->actionEntryRestore->setText(tr("Restore Entry(s)", "", dbWidget->numberOfSelectedEntries()));
- m_ui->actionEntryRestore->setToolTip(tr("Restore Entry(s)", "", dbWidget->numberOfSelectedEntries()));
- m_ui->actionEntryMoveUp->setVisible(!sorted);
- m_ui->actionEntryMoveDown->setVisible(!sorted);
- m_ui->actionEntryMoveUp->setEnabled(singleEntrySelected && !sorted && entryIndex > 0);
- m_ui->actionEntryMoveDown->setEnabled(singleEntrySelected && !sorted && entryIndex >= 0
- && entryIndex < numEntries - 1);
- m_ui->actionEntryCopyTitle->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTitle());
- m_ui->actionEntryCopyUsername->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUsername());
- // NOTE: Copy password is enabled even if the selected entry's password is blank to prevent Ctrl+C
- // from copying information from the currently selected cell in the entry view table.
- m_ui->actionEntryCopyPassword->setEnabled(singleEntrySelected);
- m_ui->actionEntryCopyURL->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUrl());
- m_ui->actionEntryCopyNotes->setEnabled(singleEntrySelected && dbWidget->currentEntryHasNotes());
- m_ui->menuEntryCopyAttribute->setEnabled(singleEntrySelected);
- m_ui->menuEntryTotp->setEnabled(singleEntrySelected);
- m_ui->menuTags->setEnabled(entriesSelected);
- m_ui->actionEntryAutoType->setEnabled(singleEntrySelected && dbWidget->currentEntryHasAutoTypeEnabled());
- m_ui->actionEntryAutoType->menu()->setEnabled(singleEntrySelected
- && dbWidget->currentEntryHasAutoTypeEnabled());
- m_ui->actionEntryAutoTypeSequence->setText(
- singleEntrySelected ? dbWidget->currentSelectedEntry()->effectiveAutoTypeSequence()
- : Group::RootAutoTypeSequence);
- m_ui->actionEntryAutoTypeSequence->setEnabled(singleEntrySelected);
- m_ui->actionEntryAutoTypeUsername->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUsername());
- m_ui->actionEntryAutoTypeUsernameEnter->setEnabled(singleEntrySelected
- && dbWidget->currentEntryHasUsername());
- m_ui->actionEntryAutoTypePassword->setEnabled(singleEntrySelected && dbWidget->currentEntryHasPassword());
- m_ui->actionEntryAutoTypePasswordEnter->setEnabled(singleEntrySelected
- && dbWidget->currentEntryHasPassword());
- m_ui->actionEntryAutoTypeTOTP->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
- m_ui->actionEntryAutoTypeTOTP->setVisible(singleEntrySelected && dbWidget->currentEntryHasTotp());
- m_ui->actionEntryOpenUrl->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUrl());
- m_ui->actionEntryTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
- m_ui->actionEntryCopyTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
- m_ui->actionEntryCopyPasswordTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
- m_ui->actionEntrySetupTotp->setEnabled(singleEntrySelected);
- m_ui->actionEntryTotpQRCode->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
- m_ui->actionEntryDownloadIcon->setEnabled((entriesSelected && !singleEntrySelected)
- || (singleEntrySelected && dbWidget->currentEntryHasUrl()));
- m_ui->actionGroupNew->setEnabled(groupSelected);
- m_ui->actionGroupEdit->setEnabled(groupSelected);
- m_ui->actionGroupClone->setEnabled(groupSelected && dbWidget->canCloneCurrentGroup());
- m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup());
- m_ui->actionGroupSortAsc->setEnabled(groupSelected && currentGroupHasChildren);
- m_ui->actionGroupSortDesc->setEnabled(groupSelected && currentGroupHasChildren);
- m_ui->actionGroupEmptyRecycleBin->setVisible(recycleBinSelected);
- m_ui->actionGroupEmptyRecycleBin->setEnabled(recycleBinSelected);
-#ifdef WITH_XC_NETWORKING
- m_ui->actionGroupDownloadFavicons->setVisible(!recycleBinSelected);
-#endif
- m_ui->actionGroupDownloadFavicons->setEnabled(groupSelected && currentGroupHasEntries
- && !recycleBinSelected);
- m_ui->actionDatabaseSecurity->setEnabled(true);
- m_ui->actionReports->setEnabled(true);
- m_ui->actionDatabaseSettings->setEnabled(true);
- m_ui->actionDatabaseSave->setEnabled(m_ui->tabWidget->canSave());
- m_ui->actionDatabaseSaveAs->setEnabled(true);
- m_ui->actionDatabaseSaveBackup->setEnabled(true);
- m_ui->menuExport->setEnabled(true);
- m_ui->actionExportCsv->setEnabled(true);
- m_ui->actionExportHtml->setEnabled(true);
- m_ui->actionExportXML->setEnabled(true);
- m_ui->actionDatabaseMerge->setEnabled(m_ui->tabWidget->currentIndex() != -1);
+ // Entry State
+ bool singleEntrySelected = (inDatabase && dbWidget->numberOfSelectedEntries() == 1);
+ bool singleEntryOrEditing = (singleEntrySelected || editingEntry);
+ bool multiEntrySelected = (inDatabase && dbWidget->numberOfSelectedEntries() > 0);
+
+ // Group State
+ bool groupSelected = (inDatabase && dbWidget->isGroupSelected());
+ bool groupHasChildren = (groupSelected && dbWidget->currentGroup()->hasChildren());
+ bool groupHasEntries = (groupSelected && !dbWidget->currentGroup()->entries().isEmpty());
+ bool inRecycleBin = (inDatabase && dbWidget->isRecycleBinSelected());
+
+ bool entryViewSorted = (inDatabase && dbWidget->isSorted());
+ bool entryViewAtTop = (inDatabase && dbWidget->currentEntryIndex() == 0);
+ bool entryViewAtBottom =
+ (groupSelected && dbWidget->currentEntryIndex() == dbWidget->currentGroup()->entries().size() - 1);
+
+ m_ui->actionEntryNew->setEnabled(inDatabase && !inRecycleBin);
+ m_ui->actionEntryClone->setEnabled(singleEntrySelected && !inRecycleBin);
+ m_ui->actionEntryEdit->setEnabled(singleEntrySelected);
+ m_ui->actionEntryDelete->setEnabled(multiEntrySelected);
+ m_ui->actionEntryRestore->setVisible(multiEntrySelected && inRecycleBin);
+ m_ui->actionEntryRestore->setEnabled(multiEntrySelected && inRecycleBin);
+ if (dbWidget) {
+ m_ui->actionEntryRestore->setText(tr("Restore Entry(s)", "", dbWidget->numberOfSelectedEntries()));
+ m_ui->actionEntryRestore->setToolTip(tr("Restore Entry(s)", "", dbWidget->numberOfSelectedEntries()));
+ }
+ m_ui->actionEntryMoveUp->setVisible(inDatabase && !entryViewSorted);
+ m_ui->actionEntryMoveDown->setVisible(inDatabase && !entryViewSorted);
+ m_ui->actionEntryMoveUp->setEnabled(singleEntrySelected && !entryViewSorted && !entryViewAtTop);
+ m_ui->actionEntryMoveDown->setEnabled(singleEntrySelected && !entryViewSorted && !entryViewAtBottom);
+ m_ui->actionEntryCopyTitle->setEnabled(singleEntryOrEditing && dbWidget->currentEntryHasTitle());
+ m_ui->actionEntryCopyUsername->setEnabled(singleEntryOrEditing && dbWidget->currentEntryHasUsername());
+ // NOTE: Copy password is enabled even if the selected entry's password is blank to prevent Ctrl+C
+ // from copying information from the currently selected cell in the entry view table.
+ m_ui->actionEntryCopyPassword->setEnabled(singleEntryOrEditing);
+ m_ui->actionEntryCopyURL->setEnabled(singleEntryOrEditing && dbWidget->currentEntryHasUrl());
+ m_ui->actionEntryCopyNotes->setEnabled(singleEntryOrEditing && dbWidget->currentEntryHasNotes());
+ m_ui->menuEntryCopyAttribute->setEnabled(singleEntryOrEditing);
+ m_ui->menuEntryTotp->setEnabled(singleEntrySelected);
+ m_ui->menuTags->setEnabled(multiEntrySelected);
+ m_ui->actionEntryAutoType->setEnabled(singleEntrySelected && dbWidget->currentEntryHasAutoTypeEnabled());
+ m_ui->actionEntryAutoType->menu()->setEnabled(singleEntrySelected && dbWidget->currentEntryHasAutoTypeEnabled());
+ m_ui->actionEntryAutoTypeSequence->setText(singleEntrySelected
+ ? dbWidget->currentSelectedEntry()->effectiveAutoTypeSequence()
+ : Group::RootAutoTypeSequence);
+ m_ui->actionEntryAutoTypeSequence->setEnabled(singleEntrySelected);
+ m_ui->actionEntryAutoTypeUsername->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUsername());
+ m_ui->actionEntryAutoTypeUsernameEnter->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUsername());
+ m_ui->actionEntryAutoTypePassword->setEnabled(singleEntrySelected && dbWidget->currentEntryHasPassword());
+ m_ui->actionEntryAutoTypePasswordEnter->setEnabled(singleEntrySelected && dbWidget->currentEntryHasPassword());
+ m_ui->actionEntryAutoTypeTOTP->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
+ m_ui->actionEntryAutoTypeTOTP->setVisible(singleEntrySelected && dbWidget->currentEntryHasTotp());
+ m_ui->actionEntryOpenUrl->setEnabled(singleEntryOrEditing && dbWidget->currentEntryHasUrl());
+ m_ui->actionEntryTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
+ m_ui->actionEntryCopyTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
+ m_ui->actionEntryCopyPasswordTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
+ m_ui->actionEntrySetupTotp->setEnabled(singleEntrySelected);
+ m_ui->actionEntryTotpQRCode->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
+ m_ui->actionEntryDownloadIcon->setEnabled((multiEntrySelected && !singleEntrySelected)
+ || (singleEntrySelected && dbWidget->currentEntryHasUrl()));
#ifdef WITH_XC_BROWSER_PASSKEYS
- bool singleEntryHasPasskey = singleEntrySelected && dbWidget->currentEntryHasPasskey();
- m_ui->actionPasskeys->setEnabled(true);
- m_ui->actionImportPasskey->setEnabled(true);
- m_ui->actionEntryImportPasskey->setEnabled(singleEntrySelected);
- m_ui->actionEntryRemovePasskey->setEnabled(singleEntryHasPasskey);
+ m_ui->actionEntryImportPasskey->setVisible(singleEntrySelected);
+ m_ui->actionEntryImportPasskey->setEnabled(singleEntrySelected);
+ m_ui->actionEntryRemovePasskey->setVisible(singleEntrySelected && dbWidget->currentEntryHasPasskey());
+ m_ui->actionEntryRemovePasskey->setEnabled(singleEntrySelected && dbWidget->currentEntryHasPasskey());
#endif
- m_ui->menuRemoteSync->setEnabled(true);
#ifdef WITH_XC_SSHAGENT
- bool singleEntryHasSshKey =
- singleEntrySelected && sshAgent()->isEnabled() && dbWidget->currentEntryHasSshKey();
- m_ui->actionEntryAddToAgent->setVisible(singleEntryHasSshKey);
- m_ui->actionEntryAddToAgent->setEnabled(singleEntryHasSshKey);
- m_ui->actionEntryRemoveFromAgent->setVisible(singleEntryHasSshKey);
- m_ui->actionEntryRemoveFromAgent->setEnabled(singleEntryHasSshKey);
+ bool hasSSHKey = singleEntrySelected && sshAgent()->isEnabled() && dbWidget->currentEntryHasSshKey();
+ m_ui->actionEntryAddToAgent->setVisible(hasSSHKey);
+ m_ui->actionEntryAddToAgent->setEnabled(hasSSHKey);
+ m_ui->actionEntryRemoveFromAgent->setVisible(hasSSHKey);
+ m_ui->actionEntryRemoveFromAgent->setEnabled(hasSSHKey);
#endif
- m_searchWidgetAction->setEnabled(true);
-
- break;
- }
- case DatabaseWidget::Mode::EditMode:
- case DatabaseWidget::Mode::LockedMode: {
- // Enable select actions when editing an entry
- bool editEntryActive = dbWidget->isEntryEditActive();
- const QList editEntryActionsMask{m_ui->actionEntryCopyUsername,
- m_ui->actionEntryCopyPassword,
- m_ui->actionEntryCopyURL,
- m_ui->actionEntryOpenUrl,
- m_ui->actionEntryAutoType,
- m_ui->actionEntryDownloadIcon,
- m_ui->actionEntryCopyNotes,
- m_ui->actionEntryCopyTitle,
- m_ui->menuEntryCopyAttribute->menuAction(),
- m_ui->menuEntryTotp->menuAction(),
- m_ui->actionEntrySetupTotp};
-
- auto entryActions = m_ui->menuEntries->actions();
- entryActions << m_ui->menuEntryCopyAttribute->actions();
- entryActions << m_ui->menuEntryTotp->actions();
- for (auto action : entryActions) {
- bool enabled = editEntryActive && editEntryActionsMask.contains(action);
- if (action->menu()) {
- action->menu()->setEnabled(enabled);
- }
- action->setEnabled(enabled);
- }
-
- const auto groupActions = m_ui->menuGroups->actions();
- for (auto action : groupActions) {
- action->setEnabled(false);
- }
-
- m_ui->actionDatabaseSecurity->setEnabled(false);
- m_ui->actionReports->setEnabled(false);
- m_ui->actionDatabaseSettings->setEnabled(false);
- m_ui->actionDatabaseSave->setEnabled(false);
- m_ui->actionDatabaseSaveAs->setEnabled(false);
- m_ui->actionDatabaseSaveBackup->setEnabled(false);
- m_ui->menuExport->setEnabled(false);
- m_ui->actionExportCsv->setEnabled(false);
- m_ui->actionExportHtml->setEnabled(false);
- m_ui->actionDatabaseMerge->setEnabled(false);
- m_ui->menuRemoteSync->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);
- // Never show in these modes
- m_ui->actionEntryMoveUp->setVisible(false);
- m_ui->actionEntryMoveDown->setVisible(false);
- m_ui->actionEntryRestore->setVisible(false);
- m_ui->actionEntryAddToAgent->setVisible(false);
- m_ui->actionEntryRemoveFromAgent->setVisible(false);
- m_ui->actionGroupEmptyRecycleBin->setVisible(false);
+ m_ui->actionGroupNew->setEnabled(groupSelected && !inRecycleBin);
+ m_ui->actionGroupEdit->setEnabled(groupSelected);
+ m_ui->actionGroupClone->setEnabled(groupSelected && dbWidget->canCloneCurrentGroup());
+ m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup());
+ m_ui->actionGroupSortAsc->setVisible(groupHasChildren);
+ m_ui->actionGroupSortAsc->setEnabled(groupHasChildren);
+ m_ui->actionGroupSortDesc->setVisible(groupHasChildren);
+ m_ui->actionGroupSortDesc->setEnabled(groupHasChildren);
+ m_ui->actionGroupEmptyRecycleBin->setVisible(inRecycleBin);
+ m_ui->actionGroupEmptyRecycleBin->setEnabled(inRecycleBin);
+#ifdef WITH_XC_NETWORKING
+ m_ui->actionGroupDownloadFavicons->setVisible(!inRecycleBin);
+#endif
+ m_ui->actionGroupDownloadFavicons->setEnabled(groupSelected && groupHasEntries && !inRecycleBin);
+ // Database Menu
+ m_ui->actionDatabaseSave->setEnabled(m_ui->tabWidget->canSave());
+ m_ui->actionDatabaseSaveAs->setEnabled(databaseUnlocked);
+ m_ui->actionDatabaseSaveBackup->setEnabled(databaseUnlocked);
+ m_ui->actionDatabaseClose->setEnabled(dbWidget);
+ m_ui->actionLockDatabase->setEnabled(databaseUnlocked);
+ m_ui->actionLockAllDatabases->setEnabled(hasLockableDatabase);
+ m_ui->actionLockDatabaseToolbar->setEnabled(hasLockableDatabase);
+ m_ui->actionDatabaseSettings->setEnabled(inDatabase || inDatabaseSettings);
+ m_ui->actionDatabaseSecurity->setEnabled(inDatabase || inDatabaseSettings);
+ m_ui->actionReports->setEnabled(inDatabase || inReports);
+ m_ui->menuRemoteSync->setEnabled(inDatabase || inDatabaseSettings);
+ m_ui->menuExport->setEnabled(inDatabase);
+ m_ui->actionDatabaseMerge->setEnabled(inDatabase);
#ifdef WITH_XC_BROWSER_PASSKEYS
- m_ui->actionPasskeys->setEnabled(false);
- m_ui->actionImportPasskey->setEnabled(false);
- m_ui->actionEntryImportPasskey->setEnabled(false);
- m_ui->actionEntryRemovePasskey->setEnabled(false);
-#else
- m_ui->actionPasskeys->setVisible(false);
- m_ui->actionImportPasskey->setVisible(false);
- m_ui->actionEntryImportPasskey->setVisible(false);
- m_ui->actionEntryRemovePasskey->setVisible(false);
+ m_ui->actionPasskeys->setEnabled(inDatabase || inReports);
+ m_ui->actionImportPasskey->setEnabled(inDatabase);
#endif
- m_searchWidgetAction->setEnabled(false);
- break;
- }
- default:
- Q_ASSERT(false);
- }
- } else {
- const auto entryActions = m_ui->menuEntries->actions();
- for (auto action : entryActions) {
- action->setEnabled(false);
- }
-
- const auto groupActions = m_ui->menuGroups->actions();
- for (auto action : groupActions) {
- action->setEnabled(false);
- }
-
- m_ui->actionDatabaseSecurity->setEnabled(false);
- m_ui->actionReports->setEnabled(false);
- m_ui->actionDatabaseSettings->setEnabled(false);
- m_ui->actionDatabaseSave->setEnabled(false);
- m_ui->actionDatabaseSaveAs->setEnabled(false);
- m_ui->actionDatabaseSaveBackup->setEnabled(false);
- m_ui->actionDatabaseClose->setEnabled(false);
- m_ui->menuExport->setEnabled(false);
- m_ui->actionExportCsv->setEnabled(false);
- m_ui->actionExportHtml->setEnabled(false);
- m_ui->actionDatabaseMerge->setEnabled(false);
- m_ui->menuRemoteSync->setEnabled(false);
- // Hide entry-specific actions
- m_ui->actionEntryMoveUp->setVisible(false);
- m_ui->actionEntryMoveDown->setVisible(false);
- m_ui->actionEntryRestore->setVisible(false);
- m_ui->actionEntryAddToAgent->setVisible(false);
- m_ui->actionEntryRemoveFromAgent->setVisible(false);
- m_ui->actionGroupEmptyRecycleBin->setVisible(false);
-
- m_searchWidgetAction->setEnabled(false);
- }
-
- if ((currentIndex == PasswordGeneratorScreen) != m_ui->actionPasswordGenerator->isChecked()) {
- bool blocked = m_ui->actionPasswordGenerator->blockSignals(true);
- m_ui->actionPasswordGenerator->toggle();
- m_ui->actionPasswordGenerator->blockSignals(blocked);
- } else if ((currentIndex == SettingsScreen) != m_ui->actionSettings->isChecked()) {
- bool blocked = m_ui->actionSettings->blockSignals(true);
- m_ui->actionSettings->toggle();
- m_ui->actionSettings->blockSignals(blocked);
- }
+ m_searchWidgetAction->setEnabled(inDatabase);
}
void MainWindow::updateToolbarSeparatorVisibility()
diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h
index 59ff6042a..578d6a91f 100644
--- a/src/gui/MainWindow.h
+++ b/src/gui/MainWindow.h
@@ -106,7 +106,7 @@ protected:
bool focusNextPrevChild(bool next) override;
private slots:
- void setMenuActionState(DatabaseWidget::Mode mode = DatabaseWidget::Mode::None);
+ void updateMenuActionState();
void updateToolbarSeparatorVisibility();
void updateWindowTitle();
void showAboutDialog();
diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui
index 7708df2f3..de3946c88 100644
--- a/src/gui/MainWindow.ui
+++ b/src/gui/MainWindow.ui
@@ -256,9 +256,9 @@
-
+
@@ -293,9 +293,6 @@
true
+
@@ -356,16 +351,17 @@
&Groups
+
+
-
-
-
+
+