From 1f0525d3fe58646850e8390138d64a5d123b897c Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sat, 28 Oct 2023 14:13:44 -0400 Subject: [PATCH] Overhaul action states and add icons to toolbar * Fixes #10981 --- COPYING | 7 +- .../scalable/actions/database-settings.svg | 1 + .../scalable/actions/entry-delete.svg | 2 +- .../application/scalable/actions/reports.svg | 2 +- share/icons/icons.qrc | 1 + share/translations/keepassxc_en.ts | 4 + src/fdosecrets/objects/Service.cpp | 2 +- src/gui/DatabaseTabWidget.cpp | 32 +- src/gui/DatabaseTabWidget.h | 8 +- src/gui/DatabaseWidget.cpp | 25 +- src/gui/DatabaseWidget.h | 8 +- src/gui/EditWidget.cpp | 2 + src/gui/MainWindow.cpp | 400 +++++++----------- src/gui/MainWindow.h | 2 +- src/gui/MainWindow.ui | 12 + src/gui/dbsettings/DatabaseSettingsDialog.cpp | 2 + src/gui/reports/ReportsDialog.cpp | 5 + src/gui/reports/ReportsDialog.h | 1 + tests/gui/TestGui.cpp | 68 ++- tests/gui/TestGui.h | 1 + tests/gui/TestGuiBrowser.cpp | 4 +- 21 files changed, 294 insertions(+), 295 deletions(-) create mode 100644 share/icons/application/scalable/actions/database-settings.svg diff --git a/COPYING b/COPYING index 3436ec3e47..23dd1d168e 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 0000000000..7bd0b9cab5 --- /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 66ae96f1bc..f052113af1 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 3d62971d2c..5453525343 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 40e0d5416a..4e86186d63 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 6bb5ae88d3..1bb7afc75f 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?. Remote Sync + + Database Settings: %1 + + DatabaseSettingsWidgetBrowser diff --git a/src/fdosecrets/objects/Service.cpp b/src/fdosecrets/objects/Service.cpp index ae1e9d4b64..e3fcefeb5f 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 b9b26d3775..168b7d9b71 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -537,25 +537,41 @@ bool DatabaseTabWidget::warnOnExport() return ans == MessageBox::Yes; } -void DatabaseTabWidget::showDatabaseSecurity() +void DatabaseTabWidget::showDatabaseSecurity(bool state) { - currentDatabaseWidget()->switchToDatabaseSecurity(); + if (state) { + currentDatabaseWidget()->switchToDatabaseSecurity(); + } else { + currentDatabaseWidget()->switchToMainView(); + } } -void DatabaseTabWidget::showDatabaseReports() +void DatabaseTabWidget::showDatabaseReports(bool state) { - currentDatabaseWidget()->switchToDatabaseReports(); + if (state) { + currentDatabaseWidget()->switchToDatabaseReports(); + } else { + currentDatabaseWidget()->switchToMainView(); + } } -void DatabaseTabWidget::showDatabaseSettings() +void DatabaseTabWidget::showDatabaseSettings(bool state) { - currentDatabaseWidget()->switchToDatabaseSettings(); + if (state) { + currentDatabaseWidget()->switchToDatabaseSettings(); + } else { + currentDatabaseWidget()->switchToMainView(); + } } #ifdef WITH_XC_BROWSER_PASSKEYS -void DatabaseTabWidget::showPasskeys() +void DatabaseTabWidget::showPasskeys(bool state) { - currentDatabaseWidget()->switchToPasskeys(); + if (state) { + currentDatabaseWidget()->switchToPasskeys(); + } else { + currentDatabaseWidget()->switchToMainView(); + } } void DatabaseTabWidget::importPasskey() diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index aa8542dd9b..f0d89fadd4 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -82,11 +82,11 @@ public slots: void unlockAnyDatabaseInDialog(DatabaseOpenDialog::Intent intent); void relockPendingDatabase(); - void showDatabaseSecurity(); - void showDatabaseReports(); - void showDatabaseSettings(); + void showDatabaseSecurity(bool state); + void showDatabaseReports(bool state); + void showDatabaseSettings(bool state); #ifdef WITH_XC_BROWSER_PASSKEYS - void showPasskeys(); + void showPasskeys(bool state); void importPasskey(); void importPasskeyToEntry(); void removePasskeyFromEntry(); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index b497420b65..25d154af7f 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 = m_reportsDialog->onPassKeysPage() ? Mode::PassKeysMode : 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 diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 6622394e1a..072400073e 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -64,8 +64,12 @@ class DatabaseWidget : public QStackedWidget { None, ViewMode, - EditMode, - LockedMode + EditEntryMode, + EditGroupMode, + LockedMode, + ReportsMode, + PassKeysMode, + DatabaseSettingsMode }; explicit DatabaseWidget(QSharedPointer db, QWidget* parent = nullptr); diff --git a/src/gui/EditWidget.cpp b/src/gui/EditWidget.cpp index 3a42154c92..291a08b171 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 9e38dcda9a..0a6f4c3d29 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -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())); @@ -458,10 +466,10 @@ MainWindow::MainWindow() 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->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->actionDatabaseSettings, &QAction::toggled, m_ui->tabWidget, &DatabaseTabWidget::showDatabaseSettings); + connect(m_ui->actionReports, &QAction::toggled, m_ui->tabWidget, &DatabaseTabWidget::showDatabaseReports); #ifdef WITH_XC_BROWSER_PASSKEYS - connect(m_ui->actionPasskeys, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showPasskeys())); + connect(m_ui->actionPasskeys, &QAction::toggled, m_ui->tabWidget, &DatabaseTabWidget::showPasskeys); connect(m_ui->actionImportPasskey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importPasskey())); connect(m_ui->actionEntryImportPasskey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importPasskeyToEntry())); connect(m_ui->actionEntryRemovePasskey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(removePasskeyFromEntry())); @@ -668,7 +676,7 @@ MainWindow::MainWindow() statusBar()->addPermanentWidget(m_statusBarLabel); restoreConfigState(); - setMenuActionState(); + updateMenuActionState(); } MainWindow::~MainWindow() @@ -844,251 +852,147 @@ 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 inDatabaseTabWidget = (currentIndex == DatabaseTabScreen); - bool inWelcomeWidget = (currentIndex == WelcomeScreen); - bool inDatabaseTabWidgetOrWelcomeWidget = inDatabaseTabWidget || inWelcomeWidget; - - 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()); - - if (inDatabaseTabWidget && m_ui->tabWidget->currentIndex() != -1) { - DatabaseWidget* dbWidget = m_ui->tabWidget->currentDatabaseWidget(); - Q_ASSERT(dbWidget); - - if (mode == DatabaseWidget::Mode::None) { - mode = dbWidget->currentMode(); - } - - 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->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); + bool hasLockableDatabase = m_ui->tabWidget->hasLockableDatabases(); + bool inAppSettings = (currentIndex == SettingsScreen); + bool inPasswordGenerator = (currentIndex == PasswordGeneratorScreen); + + auto dbWidget = (currentIndex == DatabaseTabScreen ? m_ui->tabWidget->currentDatabaseWidget() : nullptr); + auto dbMode = (dbWidget ? dbWidget->currentMode() : DatabaseWidget::Mode::None); + + // 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); + // bool editingGroup = (dbMode == DatabaseWidget::Mode::EditGroupMode); + + // Synchronize toggle buttons + m_ui->actionDatabaseSettings->blockSignals(true); + m_ui->actionReports->blockSignals(true); + m_ui->actionPasswordGenerator->blockSignals(true); + m_ui->actionSettings->blockSignals(true); + + m_ui->actionDatabaseSettings->setChecked(inDatabaseSettings); + m_ui->actionReports->setChecked(inReports); + m_ui->actionPasswordGenerator->setChecked(inPasswordGenerator); + m_ui->actionSettings->setChecked(inAppSettings); + + m_ui->actionDatabaseSettings->blockSignals(false); + m_ui->actionReports->blockSignals(false); + m_ui->actionPasswordGenerator->blockSignals(false); + m_ui->actionSettings->blockSignals(false); + + // Entry State + bool singleEntrySelected = (inDatabase && dbWidget->numberOfSelectedEntries() == 1); + bool multiEntrySelected = (inDatabase && dbWidget->numberOfSelectedEntries() > 0); + bool entryViewSorted = (inDatabase && dbWidget->isSorted()); + bool entryViewAtTop = (inDatabase && dbWidget->currentEntryIndex() == 0); + bool entryViewAtBottom = + (inDatabase && dbWidget->currentEntryIndex() == dbWidget->currentGroup()->entries().size() - 1); + + // Group State + bool groupSelected = (inDatabase && dbWidget->isGroupSelected()); + bool currentGroupHasChildren = (groupSelected && dbWidget->currentGroup()->hasChildren()); + bool currentGroupHasEntries = (groupSelected && !dbWidget->currentGroup()->entries().isEmpty()); + bool recycleBinSelected = (inDatabase && dbWidget->isRecycleBinSelected()); + + m_ui->actionEntryNew->setEnabled(inDatabase); + m_ui->actionEntryClone->setEnabled(singleEntrySelected); + m_ui->actionEntryEdit->setEnabled(singleEntrySelected); + m_ui->actionEntryDelete->setEnabled(multiEntrySelected); + m_ui->actionEntryRestore->setVisible(multiEntrySelected && recycleBinSelected); + m_ui->actionEntryRestore->setEnabled(multiEntrySelected && recycleBinSelected); + 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(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(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(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((multiEntrySelected && !singleEntrySelected) + || (singleEntrySelected && dbWidget->currentEntryHasUrl())); +#ifdef WITH_XC_BROWSER_PASSKEYS + m_ui->actionEntryImportPasskey->setEnabled(singleEntrySelected); +#endif + 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); + 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); + m_ui->actionGroupDownloadFavicons->setEnabled(groupSelected && currentGroupHasEntries && !recycleBinSelected); + + // Database Menu + m_ui->actionDatabaseSecurity->setEnabled(databaseUnlocked); + m_ui->actionReports->setEnabled(databaseUnlocked); + m_ui->actionDatabaseSettings->setEnabled(databaseUnlocked); + 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->menuExport->setEnabled(databaseUnlocked); + m_ui->actionExportCsv->setEnabled(databaseUnlocked); + m_ui->actionExportHtml->setEnabled(databaseUnlocked); + m_ui->actionExportXML->setEnabled(databaseUnlocked); + m_ui->actionDatabaseMerge->setEnabled(databaseUnlocked); #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->actionPasskeys->setEnabled(databaseUnlocked); + m_ui->actionImportPasskey->setEnabled(databaseUnlocked); + m_ui->actionEntryImportPasskey->setEnabled(singleEntrySelected); + m_ui->actionEntryRemovePasskey->setEnabled(singleEntrySelected && dbWidget->currentEntryHasPasskey()); #endif - m_ui->menuRemoteSync->setEnabled(true); + 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); -#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); - -#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); + 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); #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 59ff6042a0..578d6a91f8 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -106,7 +106,7 @@ public slots: 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 7708df2f34..f7993ac3cb 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -436,6 +436,9 @@ + + + @@ -659,6 +662,9 @@ + + true + false @@ -676,6 +682,9 @@ + + true + false @@ -693,6 +702,9 @@ + + true + false diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.cpp b/src/gui/dbsettings/DatabaseSettingsDialog.cpp index f309e56bdb..b708357703 100644 --- a/src/gui/dbsettings/DatabaseSettingsDialog.cpp +++ b/src/gui/dbsettings/DatabaseSettingsDialog.cpp @@ -99,6 +99,8 @@ DatabaseSettingsDialog::~DatabaseSettingsDialog() = default; void DatabaseSettingsDialog::load(const QSharedPointer& db) { + setHeadline(tr("Database Settings: %1").arg(db->canonicalFilePath())); + m_generalWidget->loadSettings(db); m_databaseKeyWidget->loadSettings(db); m_encryptionWidget->loadSettings(db); diff --git a/src/gui/reports/ReportsDialog.cpp b/src/gui/reports/ReportsDialog.cpp index bdbeca8a9e..09fdee1b40 100644 --- a/src/gui/reports/ReportsDialog.cpp +++ b/src/gui/reports/ReportsDialog.cpp @@ -135,6 +135,11 @@ void ReportsDialog::activatePasskeysPage() auto index = m_ui->stackedWidget->currentIndex(); m_ui->categoryList->setCurrentCategory(index); } + +bool ReportsDialog::onPassKeysPage() +{ + return m_ui->stackedWidget->currentWidget() == m_passkeysPage->m_passkeysWidget; +} #endif void ReportsDialog::reject() diff --git a/src/gui/reports/ReportsDialog.h b/src/gui/reports/ReportsDialog.h index 6400787b44..5f1ecfc7f9 100644 --- a/src/gui/reports/ReportsDialog.h +++ b/src/gui/reports/ReportsDialog.h @@ -65,6 +65,7 @@ class ReportsDialog : public DialogyWidget void addPage(QSharedPointer page); #ifdef WITH_XC_BROWSER_PASSKEYS void activatePasskeysPage(); + bool onPassKeysPage(); #endif signals: diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 6799cc64d8..da496b906c 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -561,7 +561,7 @@ void TestGui::testEditEntry() // Edit the first entry ("Sample Entry") QTest::mouseClick(entryEditWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); auto* titleEdit = editEntryWidget->findChild("titleEdit"); QTest::keyClicks(titleEdit, "_test"); @@ -576,7 +576,7 @@ void TestGui::testEditEntry() // Apply the edit QTRY_VERIFY(applyButton->isEnabled()); QTest::mouseClick(applyButton, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); QCOMPARE(entry->title(), QString("Sample Entry_test")); QCOMPARE(entry->historyItems().size(), ++editCount); QVERIFY(!applyButton->isEnabled()); @@ -654,7 +654,7 @@ void TestGui::testEditEntry() QTest::mouseClick(entryEditWidget, Qt::LeftButton); okButton = editEntryWidgetButtonBox->button(QDialogButtonBox::Ok); QVERIFY(okButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); titleEdit->setText("multiline\ntitle"); editEntryWidget->findChild("usernameComboBox")->lineEdit()->setText("multiline\nusername"); editEntryWidget->findChild("passwordEdit")->setText("multiline\npassword"); @@ -713,7 +713,7 @@ void TestGui::testSearchEditEntry() // Goto "Doggy"'s edit view QTest::keyClick(searchTextEdit, Qt::Key_Return); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); // Check the path in header is "parent-group > entry" QCOMPARE(m_dbWidget->findChild("editEntryWidget")->findChild("headerLabel")->text(), @@ -739,7 +739,7 @@ void TestGui::testAddEntry() // Click the new entry button and check that we enter edit mode QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); // Add entry "test" and confirm added auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); @@ -859,7 +859,7 @@ void TestGui::testPasswordEntryEntropy() // Click the new entry button and check that we enter edit mode QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); // Add entry "test" and confirm added auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); @@ -921,7 +921,7 @@ void TestGui::testDicewareEntryEntropy() // Click the new entry button and check that we enter edit mode QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); // Add entry "test" and confirm added auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); @@ -1008,7 +1008,7 @@ void TestGui::testTotp() QVERIFY(entryEditWidget->isVisible()); QVERIFY(entryEditWidget->isEnabled()); QTest::mouseClick(entryEditWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); editEntryWidget->setCurrentPage(1); @@ -1212,7 +1212,7 @@ void TestGui::testSearch() QModelIndex item = entryView->model()->index(0, 1); Entry* entry = entryView->entryFromIndex(item); QTest::keyClick(searchTextEdit, Qt::Key_Return); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); // Perform the edit and save it EditEntryWidget* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); @@ -1370,7 +1370,7 @@ void TestGui::testEntryPlaceholders() // Click the new entry button and check that we enter edit mode QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); // Add entry "test" and confirm added auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); @@ -1723,7 +1723,7 @@ void TestGui::testDatabaseSettings() QWidget* entryNewWidget = toolBar->widgetForAction(entryNewAction); QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); QVERIFY(editEntryWidget); @@ -1743,7 +1743,7 @@ void TestGui::testDatabaseSettings() // 2.d) Create second entry to test delay timer reset QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); QTest::keyClicks(titleEdit, "Test autosaveDelay 2"); // 2.e) Save changes @@ -1763,7 +1763,7 @@ void TestGui::testDatabaseSettings() // 4 Test no delay when disabled autosave or autosaveDelay // 4.a) create new entry QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); QTest::keyClicks(titleEdit, "Test autosaveDelay 3"); // 4.b) Save changes @@ -1784,7 +1784,7 @@ void TestGui::testDatabaseSettings() // 4.f) Repeat for autosaveDelay config()->set(Config::AutoSaveAfterEveryChange, true); QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); QTest::keyClicks(titleEdit, "Test autosaveDelay 4"); editEntryWidget->setCurrentPage(0); editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); @@ -2061,7 +2061,7 @@ void TestGui::testAutoType() QVERIFY(entryNewWidget->isEnabled()); QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); QVERIFY(editEntryWidget); @@ -2096,7 +2096,7 @@ void TestGui::testAutoType() // 2.a) Click the new entry button and set the title QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); QTest::keyClicks(titleEdit, "2. Entry With Default Auto-Type Sequence"); QTest::mouseClick(usernameComboBox, Qt::LeftButton); QTest::keyClicks(usernameComboBox, "AutocompletionUsername"); @@ -2115,7 +2115,7 @@ void TestGui::testAutoType() // 3.a) Click the new entry button and set the title QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); QTest::keyClicks(titleEdit, "3. Entry With Custom Auto-Type Sequence"); QTest::mouseClick(usernameComboBox, Qt::LeftButton); QTest::keyClicks(usernameComboBox, "AutocompletionUsername"); @@ -2192,6 +2192,40 @@ void TestGui::testAutoType() entryView->selectionModel()->clearSelection(); } +void TestGui::testMenuActionStates() +{ + /* + * MainWindow Content: + * Welcome Screen (No Databases Open) + * Database Widget + * Lock Screen / Import View + * Entry View + * Entry Edit + * Reports + * PassKey Report + * Database Settings + * Application Settings + * Password Generator + * + * Database States: + * None + * Locked + * Unlocked + * + * Entry States: + * Cannot interact with an entry + * Entry is being edited + * One entry selected + * More than one entry selected + * + * Group States: + * Cannot interact with a group + * Group is being edited + * One group is selected + */ + // TODO: Write this test +} + void TestGui::addCannedEntries() { // Find buttons diff --git a/tests/gui/TestGui.h b/tests/gui/TestGui.h index 70ff5dfc5b..320ce3d648 100644 --- a/tests/gui/TestGui.h +++ b/tests/gui/TestGui.h @@ -69,6 +69,7 @@ private slots: void testAutoType(); void testTrayRestoreHide(); void testShortcutConfig(); + void testMenuActionStates(); private: void addCannedEntries(); diff --git a/tests/gui/TestGuiBrowser.cpp b/tests/gui/TestGuiBrowser.cpp index ba45c1f1a9..51bd01f522 100644 --- a/tests/gui/TestGuiBrowser.cpp +++ b/tests/gui/TestGuiBrowser.cpp @@ -142,7 +142,7 @@ void TestGuiBrowser::testEntrySettings() auto* entryEditAction = m_mainWindow->findChild("actionEntryEdit"); QWidget* entryEditWidget = toolBar->widgetForAction(entryEditAction); QTest::mouseClick(entryEditWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); // Switch to Properties page and select all rows from the custom data table @@ -186,7 +186,7 @@ void TestGuiBrowser::testAdditionalURLs() auto* entryEditAction = m_mainWindow->findChild("actionEntryEdit"); QWidget* entryEditWidget = toolBar->widgetForAction(entryEditAction); QTest::mouseClick(entryEditWidget, Qt::LeftButton); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); // Switch to Browser Integration page and add three URL's