mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-26 15:59:50 -05:00
Allow setting group icons to children groups/entries (#3273)
* Add combo menu button to apply an icon to children - allow more options to apply icons (child groups, child entries) - extend tests in TestGroup (applying icons for groups/entries only) - prevent blue folder icon being set for entries (on entry creation only) * Do not show the combo menu button for entries
This commit is contained in:
parent
84eec03cb7
commit
bb8377ae6a
@ -1080,8 +1080,10 @@ Entry* Group::addEntryWithPath(const QString& entryPath)
|
||||
return entry;
|
||||
}
|
||||
|
||||
void Group::applyGroupIconTo(Entry* entry)
|
||||
void Group::applyGroupIconOnCreateTo(Entry* entry)
|
||||
{
|
||||
Q_ASSERT(entry);
|
||||
|
||||
if (!config()->get("UseGroupIconOnEntryCreation").toBool()) {
|
||||
return;
|
||||
}
|
||||
@ -1090,6 +1092,13 @@ void Group::applyGroupIconTo(Entry* entry)
|
||||
return;
|
||||
}
|
||||
|
||||
applyGroupIconTo(entry);
|
||||
}
|
||||
|
||||
void Group::applyGroupIconTo(Entry* entry)
|
||||
{
|
||||
Q_ASSERT(entry);
|
||||
|
||||
if (iconUuid().isNull()) {
|
||||
entry->setIcon(iconNumber());
|
||||
} else {
|
||||
@ -1097,6 +1106,31 @@ void Group::applyGroupIconTo(Entry* entry)
|
||||
}
|
||||
}
|
||||
|
||||
void Group::applyGroupIconTo(Group* other)
|
||||
{
|
||||
Q_ASSERT(other);
|
||||
|
||||
if (iconUuid().isNull()) {
|
||||
other->setIcon(iconNumber());
|
||||
} else {
|
||||
other->setIcon(iconUuid());
|
||||
}
|
||||
}
|
||||
|
||||
void Group::applyGroupIconToChildGroups()
|
||||
{
|
||||
for (Group* recursiveChild : groupsRecursive(false)) {
|
||||
applyGroupIconTo(recursiveChild);
|
||||
}
|
||||
}
|
||||
|
||||
void Group::applyGroupIconToChildEntries()
|
||||
{
|
||||
for (Entry* recursiveEntry : entriesRecursive(false)) {
|
||||
applyGroupIconTo(recursiveEntry);
|
||||
}
|
||||
}
|
||||
|
||||
void Group::sortChildrenRecursively(bool reverse)
|
||||
{
|
||||
std::sort(
|
||||
|
@ -168,7 +168,11 @@ public:
|
||||
void addEntry(Entry* entry);
|
||||
void removeEntry(Entry* entry);
|
||||
|
||||
void applyGroupIconOnCreateTo(Entry* entry);
|
||||
void applyGroupIconTo(Entry* entry);
|
||||
void applyGroupIconTo(Group* other);
|
||||
void applyGroupIconToChildGroups();
|
||||
void applyGroupIconToChildEntries();
|
||||
|
||||
void sortChildrenRecursively(bool reverse = false);
|
||||
|
||||
|
@ -308,7 +308,7 @@ namespace FdoSecrets
|
||||
entry->setUuid(QUuid::createUuid());
|
||||
entry->setTitle(itemName);
|
||||
entry->setUsername(m_backend->database()->metadata()->defaultUserName());
|
||||
group->applyGroupIconTo(entry);
|
||||
group->applyGroupIconOnCreateTo(entry);
|
||||
|
||||
entry->setGroup(group);
|
||||
|
||||
|
@ -368,7 +368,7 @@ void DatabaseWidget::createEntry()
|
||||
m_newEntry->setUuid(QUuid::createUuid());
|
||||
m_newEntry->setUsername(m_db->metadata()->defaultUserName());
|
||||
m_newParent = m_groupView->currentGroup();
|
||||
m_newParent->applyGroupIconTo(m_newEntry.data());
|
||||
m_newParent->applyGroupIconOnCreateTo(m_newEntry.data());
|
||||
switchToEntryEdit(m_newEntry.data(), true);
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::EditWidgetIcons())
|
||||
, m_db(nullptr)
|
||||
, m_applyIconTo(ApplyIconToOptions::THIS_ONLY)
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
, m_netMgr(new QNetworkAccessManager(this))
|
||||
, m_reply(nullptr)
|
||||
@ -57,6 +58,8 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent)
|
||||
m_ui->defaultIconsView->setModel(m_defaultIconModel);
|
||||
m_ui->customIconsView->setModel(m_customIconModel);
|
||||
|
||||
m_ui->applyIconToPushButton->setMenu(createApplyIconToMenu());
|
||||
|
||||
// clang-format off
|
||||
connect(m_ui->defaultIconsView, SIGNAL(clicked(QModelIndex)), this, SLOT(updateRadioButtonDefaultIcons()));
|
||||
connect(m_ui->customIconsView, SIGNAL(clicked(QModelIndex)), this, SLOT(updateRadioButtonCustomIcons()));
|
||||
@ -65,6 +68,7 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent)
|
||||
connect(m_ui->addButton, SIGNAL(clicked()), SLOT(addCustomIconFromFile()));
|
||||
connect(m_ui->deleteButton, SIGNAL(clicked()), SLOT(removeCustomIcon()));
|
||||
connect(m_ui->faviconButton, SIGNAL(clicked()), SLOT(downloadFavicon()));
|
||||
connect(m_ui->applyIconToPushButton->menu(), SIGNAL(triggered(QAction*)), SLOT(confirmApplyIconTo(QAction*)));
|
||||
|
||||
connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SIGNAL(widgetUpdated()));
|
||||
connect(m_ui->defaultIconsView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
||||
@ -103,6 +107,8 @@ IconStruct EditWidgetIcons::state()
|
||||
}
|
||||
}
|
||||
|
||||
iconStruct.applyTo = m_applyIconTo;
|
||||
|
||||
return iconStruct;
|
||||
}
|
||||
|
||||
@ -142,6 +148,30 @@ void EditWidgetIcons::load(const QUuid& currentUuid,
|
||||
m_ui->defaultIconsRadio->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
m_applyIconTo = ApplyIconToOptions::THIS_ONLY;
|
||||
m_ui->applyIconToPushButton->menu()->defaultAction()->activate(QAction::Trigger);
|
||||
}
|
||||
|
||||
void EditWidgetIcons::setShowApplyIconToButton(bool state)
|
||||
{
|
||||
m_ui->applyIconToPushButton->setVisible(state);
|
||||
}
|
||||
|
||||
QMenu* EditWidgetIcons::createApplyIconToMenu()
|
||||
{
|
||||
auto* applyIconToMenu = new QMenu(this);
|
||||
QAction* defaultAction = applyIconToMenu->addAction(tr("Apply to this only"));
|
||||
defaultAction->setData(QVariant::fromValue(ApplyIconToOptions::THIS_ONLY));
|
||||
applyIconToMenu->setDefaultAction(defaultAction);
|
||||
applyIconToMenu->addSeparator();
|
||||
applyIconToMenu->addAction(tr("Also apply to child groups"))
|
||||
->setData(QVariant::fromValue(ApplyIconToOptions::CHILD_GROUPS));
|
||||
applyIconToMenu->addAction(tr("Also apply to child entries"))
|
||||
->setData(QVariant::fromValue(ApplyIconToOptions::CHILD_ENTRIES));
|
||||
applyIconToMenu->addAction(tr("Also apply to all children"))
|
||||
->setData(QVariant::fromValue(ApplyIconToOptions::ALL_CHILDREN));
|
||||
return applyIconToMenu;
|
||||
}
|
||||
|
||||
void EditWidgetIcons::setUrl(const QString& url)
|
||||
@ -528,3 +558,9 @@ void EditWidgetIcons::updateRadioButtonCustomIcons()
|
||||
{
|
||||
m_ui->customIconsRadio->setChecked(true);
|
||||
}
|
||||
|
||||
void EditWidgetIcons::confirmApplyIconTo(QAction* action)
|
||||
{
|
||||
m_applyIconTo = action->data().value<ApplyIconToOptions>();
|
||||
m_ui->applyIconToPushButton->setText(action->text());
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#ifndef KEEPASSX_EDITWIDGETICONS_H
|
||||
#define KEEPASSX_EDITWIDGETICONS_H
|
||||
|
||||
#include <QMenu>
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
#include <QWidget>
|
||||
@ -40,12 +41,23 @@ namespace Ui
|
||||
class EditWidgetIcons;
|
||||
}
|
||||
|
||||
enum ApplyIconToOptions
|
||||
{
|
||||
THIS_ONLY = 0b00,
|
||||
CHILD_GROUPS = 0b10,
|
||||
CHILD_ENTRIES = 0b01,
|
||||
ALL_CHILDREN = 0b11
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ApplyIconToOptions)
|
||||
|
||||
struct IconStruct
|
||||
{
|
||||
IconStruct();
|
||||
|
||||
QUuid uuid;
|
||||
int number;
|
||||
ApplyIconToOptions applyTo;
|
||||
};
|
||||
|
||||
class EditWidgetIcons : public QWidget
|
||||
@ -62,6 +74,7 @@ public:
|
||||
const QSharedPointer<Database>& database,
|
||||
const IconStruct& iconStruct,
|
||||
const QString& url = "");
|
||||
void setShowApplyIconToButton(bool state);
|
||||
|
||||
public slots:
|
||||
void setUrl(const QString& url);
|
||||
@ -84,11 +97,15 @@ private slots:
|
||||
void updateWidgetsCustomIcons(bool checked);
|
||||
void updateRadioButtonDefaultIcons();
|
||||
void updateRadioButtonCustomIcons();
|
||||
void confirmApplyIconTo(QAction* action);
|
||||
|
||||
private:
|
||||
QMenu* createApplyIconToMenu();
|
||||
|
||||
const QScopedPointer<Ui::EditWidgetIcons> m_ui;
|
||||
QSharedPointer<Database> m_db;
|
||||
QUuid m_currentUuid;
|
||||
ApplyIconToOptions m_applyIconTo;
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
QUrl m_url;
|
||||
QUrl m_fetchUrl;
|
||||
|
@ -88,7 +88,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QHBoxLayout" name="customIconButtonsHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="addButton">
|
||||
<property name="text">
|
||||
@ -112,6 +112,39 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="applyToChildrenHorizontalLayout" stretch="0,0">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="applyIconToPushButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">padding: 4px 10px</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Apply icon &to ...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
@ -121,6 +154,7 @@
|
||||
<tabstop>customIconsView</tabstop>
|
||||
<tabstop>addButton</tabstop>
|
||||
<tabstop>deleteButton</tabstop>
|
||||
<tabstop>applyIconToPushButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
@ -188,6 +188,7 @@ void EditEntryWidget::setupAdvanced()
|
||||
|
||||
void EditEntryWidget::setupIcon()
|
||||
{
|
||||
m_iconsWidget->setShowApplyIconToButton(false);
|
||||
addPage(tr("Icon"), FilePath::instance()->icon("apps", "preferences-desktop-icons"), m_iconsWidget);
|
||||
connect(this, SIGNAL(accepted()), m_iconsWidget, SLOT(abortRequests()));
|
||||
connect(this, SIGNAL(rejected()), m_iconsWidget, SLOT(abortRequests()));
|
||||
|
@ -212,6 +212,17 @@ void EditGroupWidget::apply()
|
||||
// Icons add/remove are applied globally outside the transaction!
|
||||
m_group->copyDataFrom(m_temporaryGroup.data());
|
||||
|
||||
// Assign the icon to children if selected
|
||||
if (iconStruct.applyTo == ApplyIconToOptions::CHILD_GROUPS
|
||||
|| iconStruct.applyTo == ApplyIconToOptions::ALL_CHILDREN) {
|
||||
m_group->applyGroupIconToChildGroups();
|
||||
}
|
||||
|
||||
if (iconStruct.applyTo == ApplyIconToOptions::CHILD_ENTRIES
|
||||
|| iconStruct.applyTo == ApplyIconToOptions::ALL_CHILDREN) {
|
||||
m_group->applyGroupIconToChildEntries();
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
|
@ -1081,3 +1081,103 @@ void TestGroup::testHierarchy()
|
||||
QVERIFY(hierarchy.contains("group2"));
|
||||
QVERIFY(hierarchy.contains("group3"));
|
||||
}
|
||||
|
||||
void TestGroup::testApplyGroupIconRecursively()
|
||||
{
|
||||
// Create a database with two nested groups with one entry each
|
||||
Database* database = new Database();
|
||||
|
||||
Group* subgroup = new Group();
|
||||
subgroup->setName("Subgroup");
|
||||
subgroup->setParent(database->rootGroup());
|
||||
QVERIFY(subgroup);
|
||||
|
||||
Group* subsubgroup = new Group();
|
||||
subsubgroup->setName("Subsubgroup");
|
||||
subsubgroup->setParent(subgroup);
|
||||
QVERIFY(subsubgroup);
|
||||
|
||||
Entry* subgroupEntry = subgroup->addEntryWithPath("Subgroup entry");
|
||||
QVERIFY(subgroupEntry);
|
||||
subgroup->setIcon(1);
|
||||
|
||||
Entry* subsubgroupEntry = subsubgroup->addEntryWithPath("Subsubgroup entry");
|
||||
QVERIFY(subsubgroupEntry);
|
||||
subsubgroup->setIcon(2);
|
||||
|
||||
// Set an icon per number to the root group and apply recursively
|
||||
// -> all groups and entries have the same icon
|
||||
const int rootIconNumber = 42;
|
||||
database->rootGroup()->setIcon(rootIconNumber);
|
||||
QVERIFY(database->rootGroup()->iconNumber() == rootIconNumber);
|
||||
database->rootGroup()->applyGroupIconToChildGroups();
|
||||
database->rootGroup()->applyGroupIconToChildEntries();
|
||||
QVERIFY(subgroup->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subgroupEntry->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subsubgroup->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subsubgroupEntry->iconNumber() == rootIconNumber);
|
||||
|
||||
// Set an icon per number to the subsubgroup and apply recursively
|
||||
// -> only the subsubgroup related groups and entries have updated icons
|
||||
const int subsubgroupIconNumber = 24;
|
||||
subsubgroup->setIcon(subsubgroupIconNumber);
|
||||
QVERIFY(subsubgroup->iconNumber() == subsubgroupIconNumber);
|
||||
subsubgroup->applyGroupIconToChildGroups();
|
||||
subsubgroup->applyGroupIconToChildEntries();
|
||||
QVERIFY(database->rootGroup()->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subgroup->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subgroupEntry->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subsubgroup->iconNumber() == subsubgroupIconNumber);
|
||||
QVERIFY(subsubgroupEntry->iconNumber() == subsubgroupIconNumber);
|
||||
|
||||
// Set an icon per UUID to the subgroup and apply recursively
|
||||
// -> all groups and entries except the root group have the same icon
|
||||
const QUuid subgroupIconUuid = QUuid::createUuid();
|
||||
QImage subgroupIcon(16, 16, QImage::Format_RGB32);
|
||||
subgroupIcon.setPixel(0, 0, qRgb(255, 0, 0));
|
||||
database->metadata()->addCustomIcon(subgroupIconUuid, subgroupIcon);
|
||||
subgroup->setIcon(subgroupIconUuid);
|
||||
subgroup->applyGroupIconToChildGroups();
|
||||
subgroup->applyGroupIconToChildEntries();
|
||||
QVERIFY(database->rootGroup()->iconNumber() == rootIconNumber);
|
||||
QCOMPARE(subgroup->iconUuid(), subgroupIconUuid);
|
||||
QCOMPARE(subgroup->icon(), subgroupIcon);
|
||||
QCOMPARE(subgroupEntry->iconUuid(), subgroupIconUuid);
|
||||
QCOMPARE(subgroupEntry->icon(), subgroupIcon);
|
||||
QCOMPARE(subsubgroup->iconUuid(), subgroupIconUuid);
|
||||
QCOMPARE(subsubgroup->icon(), subgroupIcon);
|
||||
QCOMPARE(subsubgroupEntry->iconUuid(), subgroupIconUuid);
|
||||
QCOMPARE(subsubgroupEntry->icon(), subgroupIcon);
|
||||
|
||||
// Reset all icons to root icon
|
||||
database->rootGroup()->setIcon(rootIconNumber);
|
||||
QVERIFY(database->rootGroup()->iconNumber() == rootIconNumber);
|
||||
database->rootGroup()->applyGroupIconToChildGroups();
|
||||
database->rootGroup()->applyGroupIconToChildEntries();
|
||||
QVERIFY(subgroup->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subgroupEntry->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subsubgroup->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subsubgroupEntry->iconNumber() == rootIconNumber);
|
||||
|
||||
// Apply only for child groups
|
||||
const int iconForGroups = 10;
|
||||
database->rootGroup()->setIcon(iconForGroups);
|
||||
QVERIFY(database->rootGroup()->iconNumber() == iconForGroups);
|
||||
database->rootGroup()->applyGroupIconToChildGroups();
|
||||
QVERIFY(database->rootGroup()->iconNumber() == iconForGroups);
|
||||
QVERIFY(subgroup->iconNumber() == iconForGroups);
|
||||
QVERIFY(subgroupEntry->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subsubgroup->iconNumber() == iconForGroups);
|
||||
QVERIFY(subsubgroupEntry->iconNumber() == rootIconNumber);
|
||||
|
||||
// Apply only for child entries
|
||||
const int iconForEntries = 20;
|
||||
database->rootGroup()->setIcon(iconForEntries);
|
||||
QVERIFY(database->rootGroup()->iconNumber() == iconForEntries);
|
||||
database->rootGroup()->applyGroupIconToChildEntries();
|
||||
QVERIFY(database->rootGroup()->iconNumber() == iconForEntries);
|
||||
QVERIFY(subgroup->iconNumber() == iconForGroups);
|
||||
QVERIFY(subgroupEntry->iconNumber() == iconForEntries);
|
||||
QVERIFY(subsubgroup->iconNumber() == iconForGroups);
|
||||
QVERIFY(subsubgroupEntry->iconNumber() == iconForEntries);
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ private slots:
|
||||
void testEquals();
|
||||
void testChildrenSort();
|
||||
void testHierarchy();
|
||||
void testApplyGroupIconRecursively();
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTGROUP_H
|
||||
|
Loading…
Reference in New Issue
Block a user