diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index 7180159138..daaa67606e 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -2194,6 +2194,50 @@ removed from the database. Autosave delay since last change checkbox + + Public Database Metadata + + + + Warning: the following settings are not encrypted. + + + + Display name: + + + + Publically visible display name used on the unlock dialog + + + + Database public display name + + + + Display color: + + + + Publically visible color used on the unlock dialog + + + + Database public display color chooser + + + + Clear + + + + Display icon: + + + + Select Database Icon + + DatabaseSettingsWidgetKeeShare diff --git a/src/core/Database.cpp b/src/core/Database.cpp index b91601256e..5734f9521d 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -1051,6 +1051,54 @@ QUuid Database::publicUuid() return QUuid::fromRfc4122(publicCustomData()["KPXC_PUBLIC_UUID"].toByteArray()); } +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(); +} + void Database::markAsTemporaryDatabase() { m_isTemporaryDatabase = true; diff --git a/src/core/Database.h b/src/core/Database.h index d4e8aac2ab..29314650e2 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -108,6 +108,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(); diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 5d45b3531b..14d8f4650e 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -72,7 +72,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( @@ -229,6 +228,31 @@ void DatabaseOpenWidget::load(const QString& 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)) { diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui index a045ba82ce..1ef04a5287 100644 --- a/src/gui/DatabaseOpenWidget.ui +++ b/src/gui/DatabaseOpenWidget.ui @@ -55,18 +55,54 @@ - - - - 12 - 75 - true - + + + 9 - - Unlock KeePassXC Database - - + + + + + 32 + 32 + + + + + + + true + + + + + + + + 12 + 75 + true + + + + Unlock KeePassXC Database + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp index 84b903b5c2..c8a9ec8dcd 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp +++ b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp @@ -18,9 +18,16 @@ #include "DatabaseSettingsWidgetGeneral.h" #include "ui_DatabaseSettingsWidgetGeneral.h" +#include +#include +#include +#include + #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) @@ -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))); @@ -46,6 +58,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); @@ -116,6 +132,10 @@ bool DatabaseSettingsWidgetGeneral::saveSettings() 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; @@ -155,3 +175,79 @@ bool DatabaseSettingsWidgetGeneral::saveSettings() 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); + } +} diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h index 548490cf0d..7c0c1ebe9a 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h +++ b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h @@ -20,6 +20,8 @@ #include "DatabaseSettingsWidget.h" +#include + class Database; namespace Ui { @@ -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 m_ui; }; diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.ui b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.ui index 939b699631..324ae6d3db 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.ui +++ b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.ui @@ -7,7 +7,7 @@ 0 0 453 - 394 + 647 @@ -92,6 +92,151 @@ + + + + Public Database Metadata + + + + + + + true + + + + Warning: the following settings are not encrypted. + + + + + + + 0 + + + + + Display name: + + + + + + + Publically visible display name used on the unlock dialog + + + Database public display name + + + + + + + Display color: + + + + + + + + + + 30 + 30 + + + + Publically visible color used on the unlock dialog + + + Database public display color chooser + + + + + + + + + + Clear + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Display icon: + + + + + + + + + + 30 + 30 + + + + + + + + 30 + 30 + + + + + + + + Clear + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + @@ -113,6 +258,19 @@ of entries remain at most. + + + + Move entries to a recycle bin group +instead of deleting them from the database. +Entries deleted from the recycle bin are +removed from the database. + + + Use recycle bin + + + @@ -158,19 +316,6 @@ add up to the specified amount at most. - - - - Move entries to a recycle bin group -instead of deleting them from the database. -Entries deleted from the recycle bin are -removed from the database. - - - Use recycle bin - - - diff --git a/src/gui/styles/base/basestyle.qss b/src/gui/styles/base/basestyle.qss index ee0fa4e021..8d40281a38 100644 --- a/src/gui/styles/base/basestyle.qss +++ b/src/gui/styles/base/basestyle.qss @@ -38,7 +38,7 @@ EntryPreviewWidget TagsEdit padding-left: 0px; } -DatabaseOpenWidget #centralStack { +DatabaseOpenWidget #centralStack, DatabaseOpenWidget #publicSummaryLabel { border: 1px solid palette(mid); background: palette(light); } diff --git a/src/gui/styles/base/classicstyle.qss b/src/gui/styles/base/classicstyle.qss index d0ab2b88fc..72308f39e2 100644 --- a/src/gui/styles/base/classicstyle.qss +++ b/src/gui/styles/base/classicstyle.qss @@ -1,4 +1,4 @@ -DatabaseOpenWidget #centralStack { +DatabaseOpenWidget #centralStack, DatabaseOpenWidget #publicSummaryLabel { border: 2px groove palette(mid); background: palette(light); }