Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions share/translations/keepassxc_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5959,6 +5959,10 @@ Are you sure you want to continue with this file?</source>
<source>Show Toolbar</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show Entries of Subgroups</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show Preview Panel</source>
<translation type="unfinished"></translation>
Expand Down Expand Up @@ -6312,6 +6316,10 @@ Expect some bugs and minor issues, this version is meant for testing purposes.</
<source>Toggle Show Toolbar</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Toggle Show Entries of Subgroups</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Toggle Show Preview Panel</source>
<translation type="unfinished"></translation>
Expand Down
1 change: 1 addition & 0 deletions src/core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::GUI_CheckForUpdatesIncludeBetas, {QS("GUI/CheckForUpdatesIncludeBetas"), Roaming, false}},
{Config::GUI_ShowExpiredEntriesOnDatabaseUnlock, {QS("GUI/ShowExpiredEntriesOnDatabaseUnlock"), Roaming, true}},
{Config::GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays, {QS("GUI/ShowExpiredEntriesOnDatabaseUnlockOffsetDays"), Roaming, 3}},
{Config::GUI_ShowSubgroupEntries, {QS("GUI/ShowSubgroupEntries"), Roaming, false}},
{Config::GUI_FontSizeOffset, {QS("GUI/FontSizeOffset"), Local, 0}},

{Config::GUI_MainWindowGeometry, {QS("GUI/MainWindowGeometry"), Local, {}}},
Expand Down
1 change: 1 addition & 0 deletions src/core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class Config : public QObject
SearchWaitForEnter,
GUI_ShowExpiredEntriesOnDatabaseUnlock,
GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays,
GUI_ShowSubgroupEntries,
GUI_FontSizeOffset,

GUI_MainWindowGeometry,
Expand Down
5 changes: 5 additions & 0 deletions src/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2045,6 +2045,11 @@ void MainWindow::initViewMenu()
applySettingsChanges();
});

m_ui->actionShowEntriesOfSubgroups->setChecked(config()->get(Config::GUI_ShowSubgroupEntries).toBool());
connect(m_ui->actionShowEntriesOfSubgroups, &QAction::toggled, this, [](bool checked) {
config()->set(Config::GUI_ShowSubgroupEntries, checked);
});

m_ui->actionShowGroupPanel->setChecked(!config()->get(Config::GUI_HideGroupPanel).toBool());
connect(m_ui->actionShowGroupPanel, &QAction::toggled, this, [](bool checked) {
config()->set(Config::GUI_HideGroupPanel, !checked);
Expand Down
13 changes: 13 additions & 0 deletions src/gui/MainWindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@
<addaction name="actionShowMenubar"/>
<addaction name="actionShowToolbar"/>
<addaction name="separator"/>
<addaction name="actionShowEntriesOfSubgroups"/>
<addaction name="separator"/>
<addaction name="actionHideUsernames"/>
<addaction name="actionHidePasswords"/>
</widget>
Expand Down Expand Up @@ -1244,6 +1246,17 @@
<string notr="true">Ctrl+Shift+A</string>
</property>
</action>
<action name="actionShowEntriesOfSubgroups">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Show Entries of Subgroups</string>
</property>
<property name="toolTip">
<string>Toggle Show Entries of Subgroups</string>
</property>
</action>
<action name="actionHideUsernames">
<property name="checkable">
<bool>true</bool>
Expand Down
99 changes: 87 additions & 12 deletions src/gui/entry/EntryModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,38 @@ void EntryModel::setGroup(Group* group)

m_group = group;
m_allGroups.clear();
m_entries = group->entries();
m_orgEntries.clear();

makeConnections(group);
// Check if we should show subgroup entries
if (config()->get(Config::GUI_ShowSubgroupEntries).toBool()) {
m_entries = group->entriesRecursive();

// When showing subgroup entries, we need to connect to all groups
// that contain the entries being displayed
for (const auto& entry : asConst(m_entries)) {
if (entry->group() && !m_allGroups.contains(entry->group())) {
m_allGroups.append(entry->group());
}
}

// Always include the current group itself to handle new entries added directly to it
if (!m_allGroups.contains(group)) {
m_allGroups.append(group);
}

// Connect to all groups that have entries in the view (or could have entries)
for (const auto& groupToConnect : m_allGroups) {
if (groupToConnect) {
makeConnections(groupToConnect);
}
}

// Also connect to groupAdded signal from the main group to detect new subgroups
connect(group, SIGNAL(groupAdded()), SLOT(groupAdded()));
} else {
m_entries = group->entries();
makeConnections(group);
}
m_orgEntries.clear();

endResetModel();
}
Expand All @@ -88,13 +116,15 @@ void EntryModel::setEntries(const QList<Entry*>& entries)
m_orgEntries = entries;

for (const auto entry : asConst(m_entries)) {
if (entry->group()) {
m_allGroups.insert(entry->group());
if (entry->group() && !m_allGroups.contains(entry->group())) {
m_allGroups.append(entry->group());
}
}

for (const auto group : m_allGroups) {
makeConnections(group);
if (group) {
makeConnections(group);
}
}

endResetModel();
Expand Down Expand Up @@ -539,7 +569,12 @@ void EntryModel::entryAdded(Entry* entry)
}

if (m_group) {
m_entries = m_group->entries();
// Check if we should show subgroup entries
if (config()->get(Config::GUI_ShowSubgroupEntries).toBool()) {
m_entries = m_group->entriesRecursive();
} else {
m_entries = m_group->entries();
}
}
endInsertRows();
}
Expand All @@ -555,7 +590,12 @@ void EntryModel::entryAboutToRemove(Entry* entry)
void EntryModel::entryRemoved()
{
if (m_group) {
m_entries = m_group->entries();
// Check if we should show subgroup entries
if (config()->get(Config::GUI_ShowSubgroupEntries).toBool()) {
m_entries = m_group->entriesRecursive();
} else {
m_entries = m_group->entries();
}
}
endRemoveRows();
}
Expand All @@ -571,7 +611,12 @@ void EntryModel::entryAboutToMoveUp(int row)
void EntryModel::entryMovedUp()
{
if (m_group) {
m_entries = m_group->entries();
// Check if we should show subgroup entries
if (config()->get(Config::GUI_ShowSubgroupEntries).toBool()) {
m_entries = m_group->entriesRecursive();
} else {
m_entries = m_group->entries();
}
}
endMoveRows();
}
Expand All @@ -587,11 +632,26 @@ void EntryModel::entryAboutToMoveDown(int row)
void EntryModel::entryMovedDown()
{
if (m_group) {
m_entries = m_group->entries();
// Check if we should show subgroup entries
if (config()->get(Config::GUI_ShowSubgroupEntries).toBool()) {
m_entries = m_group->entriesRecursive();
} else {
m_entries = m_group->entries();
}
}
endMoveRows();
}

void EntryModel::groupAdded()
{
// When a new subgroup is added to the current group and we're showing subgroup entries,
// we need to refresh our connections to include the new subgroup
if (m_group && config()->get(Config::GUI_ShowSubgroupEntries).toBool()) {
// Refresh the entry list and connections to include new subgroups
setGroup(m_group);
}
}

void EntryModel::entryDataChanged(Entry* entry)
{
int row = m_entries.indexOf(entry);
Expand All @@ -607,6 +667,12 @@ void EntryModel::onConfigChanged(Config::ConfigKey key)
case Config::GUI_HidePasswords:
emit dataChanged(index(0, Password), index(rowCount() - 1, Password), {Qt::DisplayRole});
break;
case Config::GUI_ShowSubgroupEntries:
// Refresh the entry list when the subgroup setting changes
if (m_group) {
setGroup(m_group);
}
break;
default:
break;
}
Expand All @@ -618,8 +684,17 @@ void EntryModel::severConnections()
disconnect(m_group, nullptr, this, nullptr);
}

for (const Group* group : asConst(m_allGroups)) {
disconnect(group, nullptr, this, nullptr);
// Use an iterator to safely remove null pointers while iterating
auto it = m_allGroups.begin();
while (it != m_allGroups.end()) {
if (*it) {
// Group is still valid, disconnect from it
disconnect(*it, nullptr, this, nullptr);
++it;
} else {
// Group has been deleted, remove the null pointer
it = m_allGroups.erase(it);
}
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/gui/entry/EntryModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
#define KEEPASSX_ENTRYMODEL_H

#include <QAbstractTableModel>
#include <QList>
#include <QPixmap>
#include <QPointer>
#include <QSet>

#include "core/Config.h"
Expand Down Expand Up @@ -81,6 +83,7 @@ private slots:
void entryAboutToMoveDown(int row);
void entryMovedDown();
void entryDataChanged(Entry* entry);
void groupAdded();

void onConfigChanged(Config::ConfigKey key);

Expand All @@ -89,10 +92,10 @@ private slots:
void makeConnections(const Group* group);

bool m_backgroundColorVisible = true;
Group* m_group;
QPointer<Group> m_group;
QList<Entry*> m_entries;
QList<Entry*> m_orgEntries;
QSet<const Group*> m_allGroups;
QList<QPointer<const Group>> m_allGroups;

const QString HiddenContentDisplay;
};
Expand Down
48 changes: 45 additions & 3 deletions src/gui/entry/EntryView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include "gui/Icons.h"
#include "gui/SortFilterHideProxyModel.h"

#include "core/Config.h"

#define ICON_ONLY_SECTION_SIZE 26

class PasswordStrengthItemDelegate : public QStyledItemDelegate
Expand Down Expand Up @@ -94,6 +96,9 @@ EntryView::EntryView(QWidget* parent)
emit entrySelectionChanged(currentEntry());
});

// Listen for config changes to update Group column visibility
connect(config(), &Config::changed, this, &EntryView::onConfigChanged);

new QShortcut(Qt::CTRL + Qt::Key_F10, this, SLOT(contextMenuShortcutPressed()), nullptr, Qt::WidgetShortcut);
new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_J, this, SLOT(jumpToGroupShortcut()), nullptr, Qt::WidgetShortcut);

Expand Down Expand Up @@ -210,7 +215,16 @@ void EntryView::focusInEvent(QFocusEvent* event)
void EntryView::displayGroup(Group* group)
{
m_model->setGroup(group);
header()->hideSection(EntryModel::ParentGroup);

// Show Group column when subgroup entries are enabled, since entries from different groups will be shown
// But respect user's preference if they've manually hidden it
if (config()->get(Config::GUI_ShowSubgroupEntries).toBool() && !m_userHidGroupColumnInSubgroupMode) {
header()->showSection(EntryModel::ParentGroup);
} else if (!config()->get(Config::GUI_ShowSubgroupEntries).toBool()) {
header()->hideSection(EntryModel::ParentGroup);
}
// If user has hidden the column in subgroup mode, don't force it to show

setFirstEntryActive();
m_inSearchMode = false;
}
Expand Down Expand Up @@ -356,7 +370,8 @@ void EntryView::showHeaderMenu(const QPoint& position)
int columnIndex = action->data().toInt();
action->setChecked(!isColumnHidden(columnIndex));
}
actions[EntryModel::ParentGroup]->setVisible(inSearchMode());
actions[EntryModel::ParentGroup]->setVisible(inSearchMode()
|| config()->get(Config::GUI_ShowSubgroupEntries).toBool());

m_headerMenu->popup(mapToGlobal(position));
}
Expand Down Expand Up @@ -386,11 +401,21 @@ void EntryView::toggleColumnVisibility(QAction* action)
if (header()->sectionSize(columnIndex) == 0) {
header()->resizeSection(columnIndex, header()->defaultSectionSize());
}
// Reset flag when user manually shows Group column
if (columnIndex == EntryModel::ParentGroup && !m_inSearchMode
&& config()->get(Config::GUI_ShowSubgroupEntries).toBool()) {
m_userHidGroupColumnInSubgroupMode = false;
}
resetFixedColumns();
return;
}
if ((header()->count() - header()->hiddenSectionCount()) > 1) {
header()->hideSection(columnIndex);
// Track when user manually hides Group column while subgroup entries is enabled
if (columnIndex == EntryModel::ParentGroup && !m_inSearchMode
&& config()->get(Config::GUI_ShowSubgroupEntries).toBool()) {
m_userHidGroupColumnInSubgroupMode = true;
}
return;
}
action->setChecked(true);
Expand Down Expand Up @@ -462,7 +487,8 @@ void EntryView::resetFixedColumns()
void EntryView::resetViewToDefaults()
{
// Reduce number of columns that are shown by default
if (m_inSearchMode) {
if (m_inSearchMode
|| (config()->get(Config::GUI_ShowSubgroupEntries).toBool() && !m_userHidGroupColumnInSubgroupMode)) {
header()->showSection(EntryModel::ParentGroup);
} else {
header()->hideSection(EntryModel::ParentGroup);
Expand Down Expand Up @@ -597,6 +623,22 @@ bool EntryView::isColumnHidden(int logicalIndex)
return header()->isSectionHidden(logicalIndex) || header()->sectionSize(logicalIndex) == 0;
}

void EntryView::onConfigChanged(Config::ConfigKey key)
{
if (key == Config::GUI_ShowSubgroupEntries && !m_inSearchMode) {
// Reset user preference when setting is toggled - this allows the
// Group column to auto-appear when re-enabling the feature
m_userHidGroupColumnInSubgroupMode = false;

// Update Group column visibility when subgroup entries setting changes
if (config()->get(Config::GUI_ShowSubgroupEntries).toBool()) {
header()->showSection(EntryModel::ParentGroup);
} else {
header()->hideSection(EntryModel::ParentGroup);
}
}
}

void EntryView::jumpToGroupShortcut()
{
// Only allow jump to group in search mode
Expand Down
3 changes: 3 additions & 0 deletions src/gui/entry/EntryView.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include <QTreeView>

#include "core/Config.h"
#include "gui/entry/EntryModel.h"

class Entry;
Expand Down Expand Up @@ -72,6 +73,7 @@ private slots:
void resetViewToDefaults();
void contextMenuShortcutPressed();
void sortIndicatorChanged(int logicalIndex, Qt::SortOrder order);
void onConfigChanged(Config::ConfigKey key);
void jumpToGroupShortcut();

private:
Expand All @@ -85,6 +87,7 @@ private slots:
Qt::SortOrder m_lastOrder;
bool m_inSearchMode = false;
bool m_columnsNeedRelayout = true;
bool m_userHidGroupColumnInSubgroupMode = false;

QMenu* m_headerMenu;
QActionGroup* m_columnActions;
Expand Down