Skip to content

Commit

Permalink
Add database name, color, and icon options for unlock view (#10819)
Browse files Browse the repository at this point in the history
Closes #10783

Adds three database configuration options (stored as public custom data) that allow a database to have a public name/summary, color, and/or icon to be displayed on the unlock screen. This information is configured in the Database Settings and stored in the database public custom data (ie, unencrypted).

The name/summary is stored in KPXC_PUBLIC_NAME, the color is stored in KPXC_PUBLIC_COLOR, and the icon is stored in KPXC_PUBLIC_ICON.

---------

Co-authored-by: Jonathan White <[email protected]>
  • Loading branch information
droidmonkey committed Oct 8, 2024
1 parent 36ec1aa commit 0731a64
Show file tree
Hide file tree
Showing 10 changed files with 437 additions and 32 deletions.
48 changes: 44 additions & 4 deletions share/translations/keepassxc_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1749,10 +1749,6 @@ Are you sure you want to continue with this file?.</source>
<source>Maintenance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Database Settings: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DatabaseSettingsWidgetBrowser</name>
Expand Down Expand Up @@ -2237,6 +2233,50 @@ removed from the database.</source>
<source>Autosave delay since last change checkbox</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Public Database Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: the following settings are not encrypted.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Display name:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Publically visible display name used on the unlock dialog</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Database public display name</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Display color:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Publically visible color used on the unlock dialog</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Database public display color chooser</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Display icon:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select Database Icon</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DatabaseSettingsWidgetKeeShare</name>
Expand Down
48 changes: 48 additions & 0 deletions src/core/Database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1036,3 +1036,51 @@ void Database::stopModifiedTimer()
{
QMetaObject::invokeMethod(&m_modifiedTimer, "stop");
}

QString Database::publicName()
{
return publicCustomData().value("KPXC_PUBLIC_NAME").toString();
}

void Database::setPublicName(const QString& name)
{
if (name.isEmpty()) {
publicCustomData().remove("KPXC_PUBLIC_NAME");
} else {
publicCustomData().insert("KPXC_PUBLIC_NAME", name);
}
markAsModified();
}

QString Database::publicColor()
{
return publicCustomData().value("KPXC_PUBLIC_COLOR").toString();
}

void Database::setPublicColor(const QString& color)
{
if (color.isEmpty()) {
publicCustomData().remove("KPXC_PUBLIC_COLOR");
} else {
publicCustomData().insert("KPXC_PUBLIC_COLOR", color);
}
markAsModified();
}

int Database::publicIcon()
{
if (publicCustomData().contains("KPXC_PUBLIC_ICON")) {
return publicCustomData().value("KPXC_PUBLIC_ICON").toInt();
}
return -1;
}

void Database::setPublicIcon(int iconIndex)
{
if (iconIndex < 0) {
publicCustomData().remove("KPXC_PUBLIC_ICON");
} else {
publicCustomData().insert("KPXC_PUBLIC_ICON", iconIndex);
}
markAsModified();
}
7 changes: 7 additions & 0 deletions src/core/Database.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ class Database : public ModifiableObject
QString canonicalFilePath() const;
void setFilePath(const QString& filePath);

QString publicName();
void setPublicName(const QString& name);
QString publicColor();
void setPublicColor(const QString& color);
int publicIcon();
void setPublicIcon(int iconIndex);

Metadata* metadata();
const Metadata* metadata() const;
Group* rootGroup();
Expand Down
26 changes: 25 additions & 1 deletion src/gui/DatabaseOpenWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
font.setPointSize(font.pointSize() + 4);
font.setBold(true);
m_ui->labelHeadline->setFont(font);
m_ui->labelHeadline->setText(tr("Unlock KeePassXC Database"));

m_ui->quickUnlockButton->setFont(font);
m_ui->quickUnlockButton->setIcon(
Expand Down Expand Up @@ -235,6 +234,31 @@ void DatabaseOpenWidget::load(const QString& filename)
m_filename = filename;
m_ui->fileNameLabel->setRawText(m_filename);

// Set the public name if defined
auto label = tr("Unlock KeePassXC Database");
if (!m_db->publicName().isEmpty()) {
label.append(QString(": %1").arg(m_db->publicName()));
}
m_ui->labelHeadline->setText(label);

// Apply the public color to the central unlock stack if defined
auto color = m_db->publicColor();
if (!color.isEmpty()) {
m_ui->centralStack->setStyleSheet(QString("QStackedWidget {border: 4px solid %1}").arg(color));
} else {
m_ui->centralStack->setStyleSheet("");
}

// Show the database icon if defined
auto iconIndex = m_db->publicIcon();
if (iconIndex >= 0 && iconIndex < databaseIcons()->count()) {
m_ui->dbIconLabel->setPixmap(databaseIcons()->icon(iconIndex, IconSize::Large));
m_ui->dbIconLabel->setVisible(true);
} else {
m_ui->dbIconLabel->setPixmap({});
m_ui->dbIconLabel->setVisible(false);
}

if (config()->get(Config::RememberLastKeyFiles).toBool()) {
auto lastKeyFiles = config()->get(Config::LastKeyFiles).toHash();
if (lastKeyFiles.contains(m_filename)) {
Expand Down
58 changes: 47 additions & 11 deletions src/gui/DatabaseOpenWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,54 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="labelHeadline">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>9</number>
</property>
<property name="text">
<string>Unlock KeePassXC Database</string>
</property>
</widget>
<item>
<widget class="QLabel" name="dbIconLabel">
<property name="minimumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelHeadline">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Unlock KeePassXC Database</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="ElidedLabel" name="fileNameLabel">
Expand Down
96 changes: 96 additions & 0 deletions src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@
#include "DatabaseSettingsWidgetGeneral.h"
#include "ui_DatabaseSettingsWidgetGeneral.h"

#include <QColorDialog>
#include <QDialogButtonBox>
#include <QInputDialog>
#include <QListView>

#include "core/Clock.h"
#include "core/Group.h"
#include "core/Metadata.h"
#include "gui/DatabaseIcons.h"
#include "gui/IconModels.h"
#include "gui/MessageBox.h"

DatabaseSettingsWidgetGeneral::DatabaseSettingsWidgetGeneral(QWidget* parent)
Expand All @@ -29,6 +36,11 @@ DatabaseSettingsWidgetGeneral::DatabaseSettingsWidgetGeneral(QWidget* parent)
{
m_ui->setupUi(this);

connect(m_ui->dbPublicColorButton, &QPushButton::clicked, this, &DatabaseSettingsWidgetGeneral::pickPublicColor);
connect(m_ui->dbPublicColorClearButton, &QPushButton::clicked, this, [this] { setupPublicColorButton({}); });
connect(m_ui->dbPublicIconButton, &QPushButton::clicked, this, &DatabaseSettingsWidgetGeneral::pickPublicIcon);
connect(m_ui->dbPublicIconClearButton, &QPushButton::clicked, this, [this] { setupPublicIconButton(-1); });

connect(m_ui->historyMaxItemsCheckBox, SIGNAL(toggled(bool)), m_ui->historyMaxItemsSpinBox, SLOT(setEnabled(bool)));
connect(m_ui->historyMaxSizeCheckBox, SIGNAL(toggled(bool)), m_ui->historyMaxSizeSpinBox, SLOT(setEnabled(bool)));
connect(m_ui->autosaveDelayCheckBox, SIGNAL(toggled(bool)), m_ui->autosaveDelaySpinBox, SLOT(setEnabled(bool)));
Expand All @@ -48,6 +60,10 @@ void DatabaseSettingsWidgetGeneral::initialize()
m_ui->defaultUsernameEdit->setText(meta->defaultUserName());
m_ui->compressionCheckbox->setChecked(m_db->compressionAlgorithm() != Database::CompressionNone);

m_ui->dbPublicName->setText(m_db->publicName());
setupPublicColorButton(m_db->publicColor());
setupPublicIconButton(m_db->publicIcon());

if (meta->historyMaxItems() > -1) {
m_ui->historyMaxItemsSpinBox->setValue(meta->historyMaxItems());
m_ui->historyMaxItemsCheckBox->setChecked(true);
Expand Down Expand Up @@ -118,6 +134,10 @@ bool DatabaseSettingsWidgetGeneral::save()
meta->setRecycleBinEnabled(m_ui->recycleBinEnabledCheckBox->isChecked());
meta->setSettingsChanged(Clock::currentDateTimeUtc());

m_db->setPublicName(m_ui->dbPublicName->text());
m_db->setPublicColor(m_ui->dbPublicColorButton->property("color").toString());
m_db->setPublicIcon(m_ui->dbPublicIconButton->property("iconIndex").toInt());

bool truncate = false;

int historyMaxItems;
Expand Down Expand Up @@ -157,3 +177,79 @@ bool DatabaseSettingsWidgetGeneral::save()

return true;
}

void DatabaseSettingsWidgetGeneral::pickPublicColor()
{
auto oldColor = QColor(m_ui->dbPublicColorButton->property("color").toString());
auto newColor = QColorDialog::getColor(oldColor);
if (newColor.isValid()) {
setupPublicColorButton(newColor);
}
}

void DatabaseSettingsWidgetGeneral::setupPublicColorButton(const QColor& color)
{
m_ui->dbPublicColorClearButton->setVisible(color.isValid());
if (color.isValid()) {
m_ui->dbPublicColorButton->setStyleSheet(QString("background-color:%1").arg(color.name()));
m_ui->dbPublicColorButton->setProperty("color", color.name());
} else {
m_ui->dbPublicColorButton->setStyleSheet("");
m_ui->dbPublicColorButton->setProperty("color", {});
}
}

void DatabaseSettingsWidgetGeneral::pickPublicIcon()
{
QDialog dialog(this);
dialog.setSizeGripEnabled(false);
dialog.setWindowTitle(tr("Select Database Icon"));

auto iconList = new QListView;
iconList->setFlow(QListView::LeftToRight);
iconList->setMovement(QListView::Static);
iconList->setResizeMode(QListView::Adjust);
iconList->setWrapping(true);
iconList->setSpacing(4);

auto iconModel = new DefaultIconModel;
iconList->setModel(iconModel);
if (m_ui->dbPublicIconButton->property("iconIndex").toInt() >= 0) {
iconList->setCurrentIndex(iconModel->index(m_ui->dbPublicIconButton->property("iconIndex").toInt(), 0));
} else {
iconList->setCurrentIndex(iconModel->index(0, 0));
}

auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
auto layout = new QVBoxLayout(&dialog);
layout->addWidget(iconList);
layout->addWidget(buttonBox);

// Resize the dialog to fit the default icon list
auto cellSize = iconList->sizeHintForIndex(iconModel->index(0, 0));
auto spacing = iconList->spacing() * 2;
dialog.resize((cellSize.width() + spacing) * 15, (cellSize.height() + spacing) * 6 + 16);

connect(iconList, &QListView::doubleClicked, &dialog, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
connect(
&dialog, &QDialog::accepted, this, [this, iconList] { setupPublicIconButton(iconList->currentIndex().row()); });

dialog.exec();
}

void DatabaseSettingsWidgetGeneral::setupPublicIconButton(int iconIndex)
{
auto valid = iconIndex >= 0 && iconIndex < databaseIcons()->count();
m_ui->dbPublicIconClearButton->setVisible(valid);
if (valid) {
m_ui->dbPublicIconButton->setIcon(databaseIcons()->icon(iconIndex));
m_ui->dbPublicIconButton->setProperty("iconIndex", iconIndex);
m_ui->dbPublicIconClearButton->setVisible(true);
} else {
m_ui->dbPublicIconButton->setIcon(QIcon());
m_ui->dbPublicIconButton->setProperty("iconIndex", -1);
m_ui->dbPublicIconClearButton->setVisible(false);
}
}
9 changes: 9 additions & 0 deletions src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#include "DatabaseSettingsWidget.h"

#include <QColor>

class Database;
namespace Ui
{
Expand All @@ -43,6 +45,13 @@ public slots:
protected:
void showEvent(QShowEvent* event) override;

private slots:
void pickPublicColor();
void setupPublicColorButton(const QColor& color);
void pickPublicIcon();
void setupPublicIconButton(int iconIndex);

private:
const QScopedPointer<Ui::DatabaseSettingsWidgetGeneral> m_ui;
};

Expand Down
Loading

0 comments on commit 0731a64

Please sign in to comment.