diff --git a/docs/images/edit_entry.png b/docs/images/edit_entry.png index ad5b146523..9c7f6d303f 100644 Binary files a/docs/images/edit_entry.png and b/docs/images/edit_entry.png differ diff --git a/docs/images/edit_entry_attachments.png b/docs/images/edit_entry_attachments.png index fea995b525..d6c9e910cb 100644 Binary files a/docs/images/edit_entry_attachments.png and b/docs/images/edit_entry_attachments.png differ diff --git a/docs/images/edit_entry_attributes.png b/docs/images/edit_entry_attributes.png index 2a48669fbf..3654ffb86b 100644 Binary files a/docs/images/edit_entry_attributes.png and b/docs/images/edit_entry_attributes.png differ diff --git a/docs/images/edit_entry_colors.png b/docs/images/edit_entry_colors.png index c2eff39693..9ddcdd8888 100644 Binary files a/docs/images/edit_entry_colors.png and b/docs/images/edit_entry_colors.png differ diff --git a/docs/images/edit_entry_icons.png b/docs/images/edit_entry_icons.png index 7b2f0fae55..f136e5bab7 100644 Binary files a/docs/images/edit_entry_icons.png and b/docs/images/edit_entry_icons.png differ diff --git a/docs/topics/DatabaseOperations.adoc b/docs/topics/DatabaseOperations.adoc index 5f315cd459..e9a46df507 100644 --- a/docs/topics/DatabaseOperations.adoc +++ b/docs/topics/DatabaseOperations.adoc @@ -143,16 +143,6 @@ After an entry is configured with TOTP, you will see a clock icon in that entry' .TOTP Usage image::totp_usage_examples.png[] -==== Entry Icons -You can select an icon to be displayed with each entry for easy identification. KeePassXC comes with a set of default icons that you can use or you can use your own custom icons. If you defined a URL with an entry, you can also download the favorite icon for that particular website. - -NOTE: To delete a custom icon, go to <> where you can purge unused icons and delete one or more icons at a time. - -.Entry icon selection -image::edit_entry_icons.png[] - -TIP: Each KeePass application has different default icons. If you use a mobile app or KeePass2, be aware that the default icons may not be exactly correspond to the KeePassXC icons. - ==== Deleting an Entry To delete an entry, perform the following steps: @@ -207,9 +197,9 @@ KeePassXC can handle URLs in various ways. Standard URLs will be opened in your |=== === Advanced Entry Handling -KeePassXC offers several advanced options for managing your database entries. Additional Attributes allow you to store extra information required by some applications and websites. Attachments enable you to attach files to entries, stored as encrypted binaries, which can be previewed directly in the application (text and images). Icons can be selected or downloaded for easy identification of entries. The Properties section lets you view basic properties such as creation, modification, and last accessed times, and retrieve an entry's UUID for references. KeePassXC also maintains a history of changes to entries, allowing you to view, restore, or delete previous versions of an entry. +KeePassXC offers several advanced options for managing your database entries. Attributes allow you to store extra information required by some applications and websites. Attachments enable you to attach files to entries, stored as encrypted binaries, which can be previewed directly in the application (text and images). The Style page lets you select icons and customize foreground and background colors for entries. The Properties section lets you view basic properties such as creation, modification, and last accessed times, and retrieve an entry's UUID for references. KeePassXC also maintains a history of changes to entries, allowing you to view, restore, or delete previous versions of an entry. -==== Additional Attributes +==== Attributes A lot of applications and web sites now require providing additional information when you create accounts. The additional information is used to block hackers if any suspicious activity is detected. In addition, the additional information you provide can be used to reset passwords if you forget them. You can also store arbitrary information here that can be copied to the clipboard or Auto-Typed using the `{S:}` action code. To protect an attribute from being displayed by default, activate the _Protect_ checkbox *(A)*. To show the contents of the attribute while keeping it protected, press the _Reveal_ button *(B)*. @@ -225,12 +215,21 @@ NOTE: When you try to open the attached file, KeePassXC extracts the attachment .Attachments interface image::edit_entry_attachments.png[] -==== Foreground and Background Color -You can change the foreground *(A)* and/or background *(B)* color that this entry will use in the entry lists. Click the corresponding box to open the color picker dialog. +==== Style +The Style page lets you customize the visual appearance of an entry. You can change the foreground *(A)* and/or background *(B)* color that this entry will use in the entry lists. Click the corresponding box to open the color picker dialog. .Color picker dialog image::edit_entry_colors.png[] +You can also select an icon to be displayed with each entry for easy identification. KeePassXC comes with a set of default icons that you can use or you can use your own custom icons. If you defined a URL with an entry, you can also download the favorite icon for that particular website. + +NOTE: To delete a custom icon, go to <> where you can purge unused icons and delete one or more icons at a time. + +.Entry icon selection +image::edit_entry_icons.png[] + +TIP: Each KeePass application has different default icons. If you use a mobile app or KeePass2, be aware that the default icons may not be exactly correspond to the KeePassXC icons. + ==== Properties KeePassXC lets you view the basic properties such as date and time of creation, modification, and when last accessed. This is also where you can retrieve an entry's UUID for use in references. diff --git a/share/icons/application/scalable/actions/format-list-bulleted-square.svg b/share/icons/application/scalable/actions/format-list-bulleted-square.svg new file mode 100644 index 0000000000..cc6350d79c --- /dev/null +++ b/share/icons/application/scalable/actions/format-list-bulleted-square.svg @@ -0,0 +1 @@ + diff --git a/share/icons/application/scalable/actions/palette.svg b/share/icons/application/scalable/actions/palette.svg new file mode 100644 index 0000000000..feb6c21a5d --- /dev/null +++ b/share/icons/application/scalable/actions/palette.svg @@ -0,0 +1 @@ + diff --git a/share/icons/icons.qrc b/share/icons/icons.qrc index 3e82e172d5..a8c6e50203 100644 --- a/share/icons/icons.qrc +++ b/share/icons/icons.qrc @@ -45,6 +45,7 @@ application/scalable/actions/entry-edit.svg application/scalable/actions/entry-new.svg application/scalable/actions/favicon-download.svg + application/scalable/actions/format-list-bulleted-square.svg application/scalable/actions/fingerprint.svg application/scalable/actions/getting-started.svg application/scalable/actions/group-delete.svg @@ -67,6 +68,7 @@ application/scalable/actions/object-locked.svg application/scalable/actions/object-unlocked.svg application/scalable/actions/onepassword.svg + application/scalable/actions/palette.svg application/scalable/actions/paperclip.svg application/scalable/actions/passkey.svg application/scalable/actions/password-copy.svg diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index f974db170b..96a3a6fc8c 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -2935,15 +2935,35 @@ Disable safe saves and try again? EditEntryWidget - Entry + General - Advanced + Attributes - Icon + Attachments + + + + Style + + + + Foreground: + + + + Foreground color selection + + + + Background: + + + + Background color selection @@ -3097,11 +3117,7 @@ Would you like to correct it? - EditEntryWidgetAdvanced - - Additional attributes - - + EditEntryWidgetAttributes Attribute selection @@ -3150,34 +3166,6 @@ Would you like to correct it? Reveal - - Attachments - - - - If checked, the entry will not appear in reports like Health Check and HIBP even if it doesn't match the quality requirements. - - - - Exclude from database reports - - - - Foreground Color: - - - - Foreground color selection - - - - Background Color: - - - - Background color selection - - EditEntryWidgetAutoType @@ -3428,6 +3416,19 @@ Would you like to correct it? &Expires: + + E&xclude +from Reports: + + + + If checked, the entry will not appear in reports like Health Check and HIBP even if it doesn't match the quality requirements. + + + + Exclude from database reports + + EditEntryWidgetSSHAgent diff --git a/src/gui/EntryPreviewWidget.cpp b/src/gui/EntryPreviewWidget.cpp index ad29bed0c8..896629eb0d 100644 --- a/src/gui/EntryPreviewWidget.cpp +++ b/src/gui/EntryPreviewWidget.cpp @@ -194,7 +194,8 @@ void EntryPreviewWidget::refresh() updateEntryHeaderLine(); updateEntryTotp(); updateEntryGeneralTab(); - updateEntryAdvancedTab(); + updateEntryAttributesTab(); + updateEntryAttachmentsTab(); updateEntryAutotypeTab(); setVisible(!config()->get(Config::GUI_HidePreviewPanel).toBool()); @@ -412,7 +413,7 @@ void EntryPreviewWidget::updateEntryGeneralTab() m_ui->entryTagsList->setReadOnly(true); } -void EntryPreviewWidget::updateEntryAdvancedTab() +void EntryPreviewWidget::updateEntryAttributesTab() { Q_ASSERT(m_currentEntry); m_ui->entryAttributesTable->clear(); @@ -420,11 +421,10 @@ void EntryPreviewWidget::updateEntryAdvancedTab() const EntryAttributes* attributes = m_currentEntry->attributes(); const QStringList customAttributes = attributes->customKeys(); const bool hasAttributes = !customAttributes.isEmpty(); - const bool hasAttachments = !m_currentEntry->attachments()->isEmpty(); m_ui->entryAttributesTable->setRowCount(customAttributes.size()); m_ui->entryAttributesTable->setColumnCount(3); - setTabEnabled(m_ui->entryTabWidget, m_ui->entryAdvancedTab, hasAttributes || hasAttachments); + setTabEnabled(m_ui->entryTabWidget, m_ui->entryAttributesTab, hasAttributes); if (hasAttributes) { auto i = 0; QFont font; @@ -476,6 +476,14 @@ void EntryPreviewWidget::updateEntryAdvancedTab() m_ui->entryAttributesTable->horizontalHeader()->setStretchLastSection(true); m_ui->entryAttributesTable->resizeColumnsToContents(); m_ui->entryAttributesTable->resizeRowsToContents(); +} + +void EntryPreviewWidget::updateEntryAttachmentsTab() +{ + Q_ASSERT(m_currentEntry); + + const bool hasAttachments = !m_currentEntry->attachments()->isEmpty(); + setTabEnabled(m_ui->entryTabWidget, m_ui->entryAttachmentsTab, hasAttachments); m_ui->entryAttachmentsWidget->linkAttachments(m_currentEntry->attachments()); } diff --git a/src/gui/EntryPreviewWidget.h b/src/gui/EntryPreviewWidget.h index b5c497a769..0647fef3a7 100644 --- a/src/gui/EntryPreviewWidget.h +++ b/src/gui/EntryPreviewWidget.h @@ -55,7 +55,8 @@ private slots: void updateEntryHeaderLine(); void updateEntryTotp(); void updateEntryGeneralTab(); - void updateEntryAdvancedTab(); + void updateEntryAttributesTab(); + void updateEntryAttachmentsTab(); void updateEntryAutotypeTab(); void setUsernameVisible(bool state); void setPasswordVisible(bool state); diff --git a/src/gui/EntryPreviewWidget.ui b/src/gui/EntryPreviewWidget.ui index b6d4cadcb0..3a0bb4aa64 100644 --- a/src/gui/EntryPreviewWidget.ui +++ b/src/gui/EntryPreviewWidget.ui @@ -663,11 +663,11 @@ - + - Advanced + Attributes - + 5 @@ -680,61 +680,7 @@ 5 - - 8 - - - - - - 0 - 0 - - - - - 75 - true - - - - Attachments - - - - - - - - 0 - 0 - - - - Qt::ClickFocus - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Attributes - - - - + Qt::NoFocus @@ -773,6 +719,38 @@ + + + Attachments + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Qt::ClickFocus + + + + + Autotype diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index e8a72db1a1..b842430f70 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -17,7 +17,8 @@ */ #include "EditEntryWidget.h" -#include "ui_EditEntryWidgetAdvanced.h" +#include "ui_EditEntryWidgetAttachments.h" +#include "ui_EditEntryWidgetAttributes.h" #include "ui_EditEntryWidgetAutoType.h" #include "ui_EditEntryWidgetBrowser.h" #include "ui_EditEntryWidgetHistory.h" @@ -26,6 +27,7 @@ #include #include +#include #include #include @@ -65,7 +67,8 @@ EditEntryWidget::EditEntryWidget(QWidget* parent) : EditWidget(parent) , m_entry(nullptr) , m_mainUi(new Ui::EditEntryWidgetMain()) - , m_advancedUi(new Ui::EditEntryWidgetAdvanced()) + , m_attributesUi(new Ui::EditEntryWidgetAttributes()) + , m_attachmentsUi(new Ui::EditEntryWidgetAttachments()) , m_autoTypeUi(new Ui::EditEntryWidgetAutoType()) , m_sshAgentUi(new Ui::EditEntryWidgetSSHAgent()) , m_historyUi(new Ui::EditEntryWidgetHistory()) @@ -73,8 +76,14 @@ EditEntryWidget::EditEntryWidget(QWidget* parent) , m_attachments(new EntryAttachments()) , m_customData(new CustomData()) , m_mainWidget(new QScrollArea(this)) - , m_advancedWidget(new QWidget(this)) + , m_attributesWidget(new QWidget(this)) + , m_attachmentsWidget(new QWidget(this)) , m_iconsWidget(new EditWidgetIcons(this)) + , m_styleWidget(new QWidget(this)) + , m_fgColorCheckBox(nullptr) + , m_fgColorButton(nullptr) + , m_bgColorCheckBox(nullptr) + , m_bgColorButton(nullptr) , m_autoTypeWidget(new QWidget(this)) #ifdef WITH_XC_SSHAGENT , m_sshAgentWidget(new QWidget(this)) @@ -87,7 +96,7 @@ EditEntryWidget::EditEntryWidget(QWidget* parent) , m_editWidgetProperties(new EditWidgetProperties(this)) , m_historyWidget(new QWidget(this)) , m_entryAttributes(new EntryAttributes(this)) - , m_attributesModel(new EntryAttributesModel(m_advancedWidget)) + , m_attributesModel(new EntryAttributesModel(m_attributesWidget)) , m_historyModel(new EntryHistoryModel(this)) , m_sortModel(new QSortFilterProxyModel(this)) , m_autoTypeAssoc(new AutoTypeAssociations(this)) @@ -98,8 +107,9 @@ EditEntryWidget::EditEntryWidget(QWidget* parent) , m_usernameCompleterModel(new QStringListModel(this)) { setupMain(); - setupAdvanced(); - setupIcon(); + setupAttributes(); + setupAttachments(); + setupStyle(); setupAutoType(); #ifdef WITH_XC_SSHAGENT @@ -156,10 +166,12 @@ QWidget* EditEntryWidget::widgetForPage(Page page) const switch (page) { case Page::Main: return m_mainWidget; - case Page::Advanced: - return m_advancedWidget; - case Page::Icon: - return m_iconsWidget; + case Page::Attributes: + return m_attributesWidget; + case Page::Attachments: + return m_attachmentsWidget; + case Page::Style: + return m_styleWidget; case Page::AutoType: return m_autoTypeWidget; case Page::Browser: @@ -185,7 +197,7 @@ QWidget* EditEntryWidget::widgetForPage(Page page) const void EditEntryWidget::setupMain() { m_mainUi->setupUi(m_mainWidget); - addPage(tr("Entry"), icons()->icon("document-edit"), m_mainWidget); + addPage(tr("General"), icons()->icon("document-edit"), m_mainWidget); // Disable mouse wheel grab when scrolling m_mainUi->usernameComboBox->installEventFilter(new MouseWheelEventFilter(this)); @@ -224,42 +236,83 @@ void EditEntryWidget::setupMain() connect(m_mainUi->expirePresets->menu(), SIGNAL(triggered(QAction*)), this, SLOT(useExpiryPreset(QAction*))); } -void EditEntryWidget::setupAdvanced() +void EditEntryWidget::setupAttributes() { - m_advancedUi->setupUi(m_advancedWidget); - addPage(tr("Advanced"), icons()->icon("preferences-other"), m_advancedWidget); - - m_advancedUi->attachmentsWidget->setReadOnly(false); - m_advancedUi->attachmentsWidget->setButtonsVisible(true); - - connect(m_advancedUi->attachmentsWidget, - &EntryAttachmentsWidget::errorOccurred, - this, - [this](const QString& error) { showMessage(error, MessageWidget::Error); }); + m_attributesUi->setupUi(m_attributesWidget); + addPage(tr("Attributes"), icons()->icon("format-list-bulleted-square"), m_attributesWidget); m_attributesModel->setEntryAttributes(m_entryAttributes); - m_advancedUi->attributesView->setModel(m_attributesModel); + m_attributesUi->attributesView->setModel(m_attributesModel); // clang-format off - connect(m_advancedUi->addAttributeButton, SIGNAL(clicked()), SLOT(insertAttribute())); - connect(m_advancedUi->editAttributeButton, SIGNAL(clicked()), SLOT(editCurrentAttribute())); - connect(m_advancedUi->removeAttributeButton, SIGNAL(clicked()), SLOT(removeCurrentAttribute())); - connect(m_advancedUi->protectAttributeButton, SIGNAL(toggled(bool)), SLOT(protectCurrentAttribute(bool))); - connect(m_advancedUi->revealAttributeButton, SIGNAL(clicked(bool)), SLOT(toggleCurrentAttributeVisibility())); - connect(m_advancedUi->attributesView->selectionModel(), + connect(m_attributesUi->addAttributeButton, SIGNAL(clicked()), SLOT(insertAttribute())); + connect(m_attributesUi->editAttributeButton, SIGNAL(clicked()), SLOT(editCurrentAttribute())); + connect(m_attributesUi->removeAttributeButton, SIGNAL(clicked()), SLOT(removeCurrentAttribute())); + connect(m_attributesUi->protectAttributeButton, SIGNAL(toggled(bool)), SLOT(protectCurrentAttribute(bool))); + connect(m_attributesUi->revealAttributeButton, SIGNAL(clicked(bool)), SLOT(toggleCurrentAttributeVisibility())); + connect(m_attributesUi->attributesView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(updateCurrentAttribute())); - connect(m_advancedUi->fgColorButton, SIGNAL(clicked()), SLOT(pickColor())); - connect(m_advancedUi->bgColorButton, SIGNAL(clicked()), SLOT(pickColor())); // clang-format on } -void EditEntryWidget::setupIcon() +void EditEntryWidget::setupAttachments() +{ + m_attachmentsUi->setupUi(m_attachmentsWidget); + addPage(tr("Attachments"), icons()->icon("paperclip"), m_attachmentsWidget); + + m_attachmentsUi->attachmentsWidget->setReadOnly(false); + m_attachmentsUi->attachmentsWidget->setButtonsVisible(true); + + connect(m_attachmentsUi->attachmentsWidget, + &EntryAttachmentsWidget::errorOccurred, + this, + [this](const QString& error) { showMessage(error, MessageWidget::Error); }); +} + +void EditEntryWidget::setupStyle() { m_iconsWidget->setShowApplyIconToButton(false); - addPage(tr("Icon"), icons()->icon("preferences-desktop-icons"), m_iconsWidget); + + auto* layout = new QVBoxLayout(m_styleWidget); + layout->setContentsMargins(0, 0, 0, 0); + + // Color controls - aligned with radio button text in the icons widget + m_fgColorCheckBox = new QCheckBox(tr("Foreground Color:"), m_styleWidget); + m_fgColorCheckBox->setObjectName("fgColorCheckBox"); + m_fgColorButton = new QPushButton(m_styleWidget); + m_fgColorButton->setObjectName("fgColorButton"); + m_fgColorButton->setMaximumSize(25, 25); + m_fgColorButton->setAccessibleName(tr("Foreground color selection")); + + m_bgColorCheckBox = new QCheckBox(tr("Background Color:"), m_styleWidget); + m_bgColorCheckBox->setObjectName("bgColorCheckBox"); + m_bgColorButton = new QPushButton(m_styleWidget); + m_bgColorButton->setObjectName("bgColorButton"); + m_bgColorButton->setMaximumSize(25, 25); + m_bgColorButton->setAccessibleName(tr("Background color selection")); + + auto* colorLayout = new QHBoxLayout(); + colorLayout->setSpacing(8); + colorLayout->addWidget(m_fgColorCheckBox); + colorLayout->addWidget(m_fgColorButton); + colorLayout->addSpacing(20); + colorLayout->addWidget(m_bgColorCheckBox); + colorLayout->addWidget(m_bgColorButton); + colorLayout->addStretch(); + + layout->addLayout(colorLayout); + layout->addSpacing(10); + layout->addWidget(m_iconsWidget); + + addPage(tr("Style"), icons()->icon("palette"), m_styleWidget); connect(this, SIGNAL(accepted()), m_iconsWidget, SLOT(abortRequests())); connect(this, SIGNAL(rejected()), m_iconsWidget, SLOT(abortRequests())); + + // clang-format off + connect(m_fgColorButton, SIGNAL(clicked()), SLOT(pickColor())); + connect(m_bgColorButton, SIGNAL(clicked()), SLOT(pickColor())); + // clang-format on } void EditEntryWidget::openAutotypeHelp() @@ -509,12 +562,12 @@ void EditEntryWidget::setupEntryUpdate() connect(m_mainUi->notesEdit, SIGNAL(textChanged()), this, SLOT(setModified())); // Advanced tab - connect(m_advancedUi->attributesEdit, SIGNAL(textChanged()), this, SLOT(setModified())); - connect(m_advancedUi->protectAttributeButton, SIGNAL(stateChanged(int)), this, SLOT(setModified())); - connect(m_advancedUi->excludeReportsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setModified())); - connect(m_advancedUi->fgColorCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setModified())); - connect(m_advancedUi->bgColorCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setModified())); - connect(m_advancedUi->attachmentsWidget, SIGNAL(widgetUpdated()), this, SLOT(setModified())); + connect(m_attributesUi->attributesEdit, SIGNAL(textChanged()), this, SLOT(setModified())); + connect(m_attributesUi->protectAttributeButton, SIGNAL(stateChanged(int)), this, SLOT(setModified())); + connect(m_mainUi->excludeReportsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setModified())); + connect(m_fgColorCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setModified())); + connect(m_bgColorCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setModified())); + connect(m_attachmentsUi->attachmentsWidget, SIGNAL(widgetUpdated()), this, SLOT(setModified())); // Icon tab connect(m_iconsWidget, SIGNAL(widgetUpdated()), this, SLOT(setModified())); @@ -991,19 +1044,19 @@ void EditEntryWidget::setForms(Entry* entry, bool restore) m_mainUi->notesEdit->setTabStopDistance( QFontMetrics(m_mainUi->notesEdit->font()).horizontalAdvance(QString(4, ' '))); - m_advancedUi->attachmentsWidget->setReadOnly(m_history); - m_advancedUi->addAttributeButton->setEnabled(!m_history); - m_advancedUi->editAttributeButton->setEnabled(false); - m_advancedUi->removeAttributeButton->setEnabled(false); - m_advancedUi->attributesEdit->setReadOnly(m_history); + m_attachmentsUi->attachmentsWidget->setReadOnly(m_history); + m_attributesUi->addAttributeButton->setEnabled(!m_history); + m_attributesUi->editAttributeButton->setEnabled(false); + m_attributesUi->removeAttributeButton->setEnabled(false); + m_attributesUi->attributesEdit->setReadOnly(m_history); QAbstractItemView::EditTriggers editTriggers; if (m_history) { editTriggers = QAbstractItemView::NoEditTriggers; } else { editTriggers = QAbstractItemView::DoubleClicked; } - m_advancedUi->attributesView->setEditTriggers(editTriggers); - m_advancedUi->excludeReportsCheckBox->setChecked(entry->excludeFromReports()); + m_attributesUi->attributesView->setEditTriggers(editTriggers); + m_mainUi->excludeReportsCheckBox->setChecked(entry->excludeFromReports()); setupColorButton(true, entry->foregroundColor()); setupColorButton(false, entry->backgroundColor()); m_iconsWidget->setEnabled(!m_history); @@ -1034,20 +1087,20 @@ void EditEntryWidget::setForms(Entry* entry, bool restore) m_mainUi->notesEdit->setPlainText(entry->notes()); - m_advancedUi->attachmentsWidget->linkAttachments(m_attachments.data()); + m_attachmentsUi->attachmentsWidget->linkAttachments(m_attachments.data()); m_entryAttributes->copyCustomKeysFrom(entry->attributes()); if (m_attributesModel->rowCount() != 0) { - m_advancedUi->attributesView->setCurrentIndex(m_attributesModel->index(0, 0)); + m_attributesUi->attributesView->setCurrentIndex(m_attributesModel->index(0, 0)); } else { - m_advancedUi->attributesEdit->setPlainText(""); - m_advancedUi->attributesEdit->setEnabled(false); + m_attributesUi->attributesEdit->setPlainText(""); + m_attributesUi->attributesEdit->setEnabled(false); } - QList sizes = m_advancedUi->attributesSplitter->sizes(); - sizes.replace(0, m_advancedUi->attributesSplitter->width() * 0.3); - sizes.replace(1, m_advancedUi->attributesSplitter->width() * 0.7); - m_advancedUi->attributesSplitter->setSizes(sizes); + QList sizes = m_attributesUi->attributesSplitter->sizes(); + sizes.replace(0, m_attributesUi->attributesSplitter->width() * 0.3); + sizes.replace(1, m_attributesUi->attributesSplitter->width() * 0.7); + m_attributesUi->attributesSplitter->setSizes(sizes); IconStruct iconStruct; iconStruct.uuid = entry->iconUuid(); @@ -1193,9 +1246,9 @@ bool EditEntryWidget::commitEntry() } } - if (m_advancedUi->attributesView->currentIndex().isValid() && m_advancedUi->attributesEdit->isEnabled()) { - QString key = m_attributesModel->keyByIndex(m_advancedUi->attributesView->currentIndex()); - m_entryAttributes->set(key, m_advancedUi->attributesEdit->toPlainText(), m_entryAttributes->isProtected(key)); + if (m_attributesUi->attributesView->currentIndex().isValid() && m_attributesUi->attributesEdit->isEnabled()) { + QString key = m_attributesModel->keyByIndex(m_attributesUi->attributesView->currentIndex()); + m_entryAttributes->set(key, m_attributesUi->attributesEdit->toPlainText(), m_entryAttributes->isProtected(key)); } m_currentAttribute = QPersistentModelIndex(); @@ -1231,7 +1284,7 @@ bool EditEntryWidget::commitEntry() m_historyModel->setEntries(m_entry->historyItems(), m_entry); setPageHidden(m_historyWidget, m_history || m_entry->historyItems().count() < 1); - m_advancedUi->attachmentsWidget->linkAttachments(m_entry->attachments()); + m_attachmentsUi->attachmentsWidget->linkAttachments(m_entry->attachments()); showMessage(tr("Entry updated successfully."), MessageWidget::Positive); setModified(false); @@ -1269,18 +1322,18 @@ void EditEntryWidget::updateEntryData(Entry* entry) const entry->setNotes(m_mainUi->notesEdit->toPlainText()); - if (entry->excludeFromReports() != m_advancedUi->excludeReportsCheckBox->isChecked()) { - entry->setExcludeFromReports(m_advancedUi->excludeReportsCheckBox->isChecked()); + if (entry->excludeFromReports() != m_mainUi->excludeReportsCheckBox->isChecked()) { + entry->setExcludeFromReports(m_mainUi->excludeReportsCheckBox->isChecked()); } - if (m_advancedUi->fgColorCheckBox->isChecked() && m_advancedUi->fgColorButton->property("color").isValid()) { - entry->setForegroundColor(m_advancedUi->fgColorButton->property("color").toString()); + if (m_fgColorCheckBox->isChecked() && m_fgColorButton->property("color").isValid()) { + entry->setForegroundColor(m_fgColorButton->property("color").toString()); } else { entry->setForegroundColor(QString()); } - if (m_advancedUi->bgColorCheckBox->isChecked() && m_advancedUi->bgColorButton->property("color").isValid()) { - entry->setBackgroundColor(m_advancedUi->bgColorButton->property("color").toString()); + if (m_bgColorCheckBox->isChecked() && m_bgColorButton->property("color").isValid()) { + entry->setBackgroundColor(m_bgColorButton->property("color").toString()); } else { entry->setBackgroundColor(QString()); } @@ -1412,8 +1465,8 @@ void EditEntryWidget::insertAttribute() m_entryAttributes->set(name, ""); QModelIndex index = m_attributesModel->indexByKey(name); - m_advancedUi->attributesView->setCurrentIndex(index); - m_advancedUi->attributesView->edit(index); + m_attributesUi->attributesView->setCurrentIndex(index); + m_attributesUi->attributesView->edit(index); setModified(true); } @@ -1422,10 +1475,10 @@ void EditEntryWidget::editCurrentAttribute() { Q_ASSERT(!m_history); - QModelIndex index = m_advancedUi->attributesView->currentIndex(); + QModelIndex index = m_attributesUi->attributesView->currentIndex(); if (index.isValid()) { - m_advancedUi->attributesView->edit(index); + m_attributesUi->attributesView->edit(index); setModified(true); } } @@ -1434,7 +1487,7 @@ void EditEntryWidget::removeCurrentAttribute() { Q_ASSERT(!m_history); - QModelIndex index = m_advancedUi->attributesView->currentIndex(); + QModelIndex index = m_attributesUi->attributesView->currentIndex(); if (index.isValid()) { @@ -1453,15 +1506,15 @@ void EditEntryWidget::removeCurrentAttribute() void EditEntryWidget::updateCurrentAttribute() { - QModelIndex newIndex = m_advancedUi->attributesView->currentIndex(); + QModelIndex newIndex = m_attributesUi->attributesView->currentIndex(); QString newKey = m_attributesModel->keyByIndex(newIndex); if (!m_history && m_currentAttribute != newIndex) { // Save changes to the currently selected attribute if editing is enabled - if (m_currentAttribute.isValid() && m_advancedUi->attributesEdit->isEnabled()) { + if (m_currentAttribute.isValid() && m_attributesUi->attributesEdit->isEnabled()) { QString currKey = m_attributesModel->keyByIndex(m_currentAttribute); m_entryAttributes->set( - currKey, m_advancedUi->attributesEdit->toPlainText(), m_entryAttributes->isProtected(currKey)); + currKey, m_attributesUi->attributesEdit->toPlainText(), m_entryAttributes->isProtected(currKey)); } } @@ -1473,50 +1526,50 @@ void EditEntryWidget::updateCurrentAttribute() void EditEntryWidget::displayAttribute(QModelIndex index, bool showProtected) { // Block signals to prevent modified being set - m_advancedUi->protectAttributeButton->blockSignals(true); - m_advancedUi->attributesEdit->blockSignals(true); - m_advancedUi->revealAttributeButton->setText(tr("Reveal")); + m_attributesUi->protectAttributeButton->blockSignals(true); + m_attributesUi->attributesEdit->blockSignals(true); + m_attributesUi->revealAttributeButton->setText(tr("Reveal")); if (index.isValid()) { QString key = m_attributesModel->keyByIndex(index); if (showProtected) { - m_advancedUi->attributesEdit->setPlainText(tr("[PROTECTED] Press Reveal to view or edit")); - m_advancedUi->attributesEdit->setEnabled(false); - m_advancedUi->revealAttributeButton->setEnabled(true); - m_advancedUi->protectAttributeButton->setChecked(true); + m_attributesUi->attributesEdit->setPlainText(tr("[PROTECTED] Press Reveal to view or edit")); + m_attributesUi->attributesEdit->setEnabled(false); + m_attributesUi->revealAttributeButton->setEnabled(true); + m_attributesUi->protectAttributeButton->setChecked(true); } else { - m_advancedUi->attributesEdit->setPlainText(m_entryAttributes->value(key)); - m_advancedUi->attributesEdit->setEnabled(true); - m_advancedUi->revealAttributeButton->setEnabled(false); - m_advancedUi->protectAttributeButton->setChecked(false); + m_attributesUi->attributesEdit->setPlainText(m_entryAttributes->value(key)); + m_attributesUi->attributesEdit->setEnabled(true); + m_attributesUi->revealAttributeButton->setEnabled(false); + m_attributesUi->protectAttributeButton->setChecked(false); } // Don't allow editing in history view - m_advancedUi->protectAttributeButton->setEnabled(!m_history); - m_advancedUi->editAttributeButton->setEnabled(!m_history); - m_advancedUi->removeAttributeButton->setEnabled(!m_history); + m_attributesUi->protectAttributeButton->setEnabled(!m_history); + m_attributesUi->editAttributeButton->setEnabled(!m_history); + m_attributesUi->removeAttributeButton->setEnabled(!m_history); } else { - m_advancedUi->attributesEdit->setPlainText(""); - m_advancedUi->attributesEdit->setEnabled(false); - m_advancedUi->revealAttributeButton->setEnabled(false); - m_advancedUi->protectAttributeButton->setChecked(false); - m_advancedUi->protectAttributeButton->setEnabled(false); - m_advancedUi->editAttributeButton->setEnabled(false); - m_advancedUi->removeAttributeButton->setEnabled(false); + m_attributesUi->attributesEdit->setPlainText(""); + m_attributesUi->attributesEdit->setEnabled(false); + m_attributesUi->revealAttributeButton->setEnabled(false); + m_attributesUi->protectAttributeButton->setChecked(false); + m_attributesUi->protectAttributeButton->setEnabled(false); + m_attributesUi->editAttributeButton->setEnabled(false); + m_attributesUi->removeAttributeButton->setEnabled(false); } - m_advancedUi->protectAttributeButton->blockSignals(false); - m_advancedUi->attributesEdit->blockSignals(false); + m_attributesUi->protectAttributeButton->blockSignals(false); + m_attributesUi->attributesEdit->blockSignals(false); } void EditEntryWidget::protectCurrentAttribute(bool state) { - QModelIndex index = m_advancedUi->attributesView->currentIndex(); + QModelIndex index = m_attributesUi->attributesView->currentIndex(); if (!m_history && index.isValid()) { QString key = m_attributesModel->keyByIndex(index); if (state) { // Save the current text and protect the attribute - m_entryAttributes->set(key, m_advancedUi->attributesEdit->toPlainText(), true); + m_entryAttributes->set(key, m_attributesUi->attributesEdit->toPlainText(), true); } else { // Unprotect the current attribute value (don't save text as it is obscured) m_entryAttributes->set(key, m_entryAttributes->value(key), false); @@ -1529,19 +1582,19 @@ void EditEntryWidget::protectCurrentAttribute(bool state) void EditEntryWidget::toggleCurrentAttributeVisibility() { - if (!m_advancedUi->attributesEdit->isEnabled()) { - QModelIndex index = m_advancedUi->attributesView->currentIndex(); + if (!m_attributesUi->attributesEdit->isEnabled()) { + QModelIndex index = m_attributesUi->attributesView->currentIndex(); if (index.isValid()) { - bool oldBlockSignals = m_advancedUi->attributesEdit->blockSignals(true); + bool oldBlockSignals = m_attributesUi->attributesEdit->blockSignals(true); QString key = m_attributesModel->keyByIndex(index); - m_advancedUi->attributesEdit->setPlainText(m_entryAttributes->value(key)); - m_advancedUi->attributesEdit->setEnabled(true); - m_advancedUi->attributesEdit->blockSignals(oldBlockSignals); + m_attributesUi->attributesEdit->setPlainText(m_entryAttributes->value(key)); + m_attributesUi->attributesEdit->setEnabled(true); + m_attributesUi->attributesEdit->blockSignals(oldBlockSignals); } - m_advancedUi->revealAttributeButton->setText(tr("Hide")); + m_attributesUi->revealAttributeButton->setText(tr("Hide")); } else { protectCurrentAttribute(true); - m_advancedUi->revealAttributeButton->setText(tr("Reveal")); + m_attributesUi->revealAttributeButton->setText(tr("Reveal")); } } @@ -1698,11 +1751,11 @@ QMenu* EditEntryWidget::createPresetsMenu() void EditEntryWidget::setupColorButton(bool foreground, const QColor& color) { - QWidget* button = m_advancedUi->fgColorButton; - QCheckBox* checkBox = m_advancedUi->fgColorCheckBox; + QWidget* button = m_fgColorButton; + QCheckBox* checkBox = m_fgColorCheckBox; if (!foreground) { - button = m_advancedUi->bgColorButton; - checkBox = m_advancedUi->bgColorCheckBox; + button = m_bgColorButton; + checkBox = m_bgColorCheckBox; } if (color.isValid()) { @@ -1718,10 +1771,10 @@ void EditEntryWidget::setupColorButton(bool foreground, const QColor& color) void EditEntryWidget::pickColor() { - bool isForeground = (sender() == m_advancedUi->fgColorButton); - QColor oldColor = QColor(m_advancedUi->fgColorButton->property("color").toString()); + bool isForeground = (sender() == m_fgColorButton); + QColor oldColor = QColor(m_fgColorButton->property("color").toString()); if (!isForeground) { - oldColor = QColor(m_advancedUi->bgColorButton->property("color").toString()); + oldColor = QColor(m_bgColorButton->property("color").toString()); } QColor newColor = QColorDialog::getColor(oldColor); diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index 3fce4d56d0..64ab6a6332 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -54,7 +54,8 @@ class EntryURLModel; namespace Ui { - class EditEntryWidgetAdvanced; + class EditEntryWidgetAttributes; + class EditEntryWidgetAttachments; class EditEntryWidgetAutoType; class EditEntryWidgetBrowser; class EditEntryWidgetSSHAgent; @@ -80,8 +81,9 @@ class EditEntryWidget : public EditWidget enum class Page { Main, - Advanced, - Icon, + Attributes, + Attachments, + Style, AutoType, Browser, SSHAgent, @@ -152,8 +154,9 @@ private slots: private: void setupMain(); - void setupAdvanced(); - void setupIcon(); + void setupAttributes(); + void setupAttachments(); + void setupStyle(); void setupAutoType(); #ifdef WITH_XC_BROWSER void setupBrowser(); @@ -188,7 +191,8 @@ private slots: QString m_pendingPrivateKey; #endif const QScopedPointer m_mainUi; - const QScopedPointer m_advancedUi; + const QScopedPointer m_attributesUi; + const QScopedPointer m_attachmentsUi; const QScopedPointer m_autoTypeUi; const QScopedPointer m_sshAgentUi; const QScopedPointer m_historyUi; @@ -197,8 +201,14 @@ private slots: const QScopedPointer m_customData; QScrollArea* const m_mainWidget; - QWidget* const m_advancedWidget; + QWidget* const m_attributesWidget; + QWidget* const m_attachmentsWidget; EditWidgetIcons* const m_iconsWidget; + QWidget* const m_styleWidget; + QCheckBox* m_fgColorCheckBox; + QPushButton* m_fgColorButton; + QCheckBox* m_bgColorCheckBox; + QPushButton* m_bgColorButton; QWidget* const m_autoTypeWidget; #ifdef WITH_XC_SSHAGENT QWidget* const m_sshAgentWidget; diff --git a/src/gui/entry/EditEntryWidgetAdvanced.ui b/src/gui/entry/EditEntryWidgetAdvanced.ui deleted file mode 100644 index 044145226d..0000000000 --- a/src/gui/entry/EditEntryWidgetAdvanced.ui +++ /dev/null @@ -1,331 +0,0 @@ - - - EditEntryWidgetAdvanced - - - - 0 - 0 - 532 - 469 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Additional attributes - - - - - - Qt::Horizontal - - - false - - - - - 0 - 0 - - - - Attribute selection - - - QAbstractScrollArea::AdjustToContents - - - QListView::Adjust - - - - - false - - - - 0 - 0 - - - - - 170 - 0 - - - - Attribute value - - - - - - - - - - Add a new attribute - - - Add - - - - - - - false - - - Remove selected attribute - - - Remove - - - - - - - false - - - Edit attribute name - - - Edit Name - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - true - - - Toggle attribute protection - - - margin-left:50%;margin-right:50% - - - Protect - - - true - - - - - - - false - - - Show a protected attribute - - - Reveal - - - false - - - - - - - - - - - - Attachments - - - - - - - - - - - - If checked, the entry will not appear in reports like Health Check and HIBP even if it doesn't match the quality requirements. - - - Exclude from database reports - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Foreground Color: - - - - - - - - 0 - 0 - - - - - 25 - 25 - - - - Foreground color selection - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Maximum - - - - 30 - 20 - - - - - - - - Background Color: - - - - - - - - 0 - 0 - - - - - 25 - 25 - - - - Background color selection - - - - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - EntryAttachmentsWidget - QWidget -
gui/entry/EntryAttachmentsWidget.h
- 1 -
- - AttributesListView - QListView -
gui/entry/EditEntryWidget_p.h
-
-
- - attributesView - attributesEdit - addAttributeButton - removeAttributeButton - editAttributeButton - protectAttributeButton - revealAttributeButton - excludeReportsCheckBox - fgColorCheckBox - fgColorButton - bgColorCheckBox - bgColorButton - - - -
diff --git a/src/gui/entry/EditEntryWidgetAttachments.ui b/src/gui/entry/EditEntryWidgetAttachments.ui new file mode 100644 index 0000000000..bb7c281c77 --- /dev/null +++ b/src/gui/entry/EditEntryWidgetAttachments.ui @@ -0,0 +1,41 @@ + + + EditEntryWidgetAttachments + + + + 0 + 0 + 532 + 300 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + EntryAttachmentsWidget + QWidget +
gui/entry/EntryAttachmentsWidget.h
+ 1 +
+
+ + +
diff --git a/src/gui/entry/EditEntryWidgetAttributes.ui b/src/gui/entry/EditEntryWidgetAttributes.ui new file mode 100644 index 0000000000..5cc4fd6b68 --- /dev/null +++ b/src/gui/entry/EditEntryWidgetAttributes.ui @@ -0,0 +1,181 @@ + + + EditEntryWidgetAttributes + + + + 0 + 0 + 532 + 300 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + false + + + + + 0 + 0 + + + + Attribute selection + + + QAbstractScrollArea::AdjustToContents + + + QListView::Adjust + + + + + false + + + + 0 + 0 + + + + + 170 + 0 + + + + Attribute value + + + + + + + + + + Add a new attribute + + + Add + + + + + + + false + + + Remove selected attribute + + + Remove + + + + + + + false + + + Edit attribute name + + + Edit Name + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + Toggle attribute protection + + + margin-left:50%;margin-right:50% + + + Protect + + + true + + + + + + + false + + + Show a protected attribute + + + Reveal + + + false + + + + + + + + + + AttributesListView + QListView +
gui/entry/EditEntryWidget_p.h
+
+
+ + attributesView + attributesEdit + addAttributeButton + removeAttributeButton + editAttributeButton + protectAttributeButton + revealAttributeButton + + + +
diff --git a/src/gui/entry/EditEntryWidgetMain.ui b/src/gui/entry/EditEntryWidgetMain.ui index 2f785f0350..1b37d65edb 100644 --- a/src/gui/entry/EditEntryWidgetMain.ui +++ b/src/gui/entry/EditEntryWidgetMain.ui @@ -342,6 +342,33 @@
+ + + + E&xclude +from Reports: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + excludeReportsCheckBox + + + + + + + If checked, the entry will not appear in reports like Health Check and HIBP even if it doesn't match the quality requirements. + + + + + +
@@ -377,6 +404,7 @@ expirePresets revealNotesButton notesEdit + excludeReportsCheckBox diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 4c95669290..2903fd7f24 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -57,6 +57,7 @@ #include "gui/dbsettings/DatabaseSettingsDialog.h" #include "gui/dbsettings/DatabaseSettingsWidgetEncryption.h" #include "gui/entry/EditEntryWidget.h" +#include "gui/entry/EntryAttachmentsWidget.h" #include "gui/entry/EntryView.h" #include "gui/group/EditGroupWidget.h" #include "gui/group/GroupModel.h" @@ -657,7 +658,6 @@ void TestGui::testEditEntry() QVERIFY(historyView->isVisible()); // Test the "known bad" checkbox - editEntryWidget->switchToPage(EditEntryWidget::Page::Advanced); auto excludeReportsCheckBox = editEntryWidget->findChild("excludeReportsCheckBox"); QVERIFY(excludeReportsCheckBox); QCOMPARE(excludeReportsCheckBox->isChecked(), false); @@ -679,25 +679,35 @@ void TestGui::testEditEntry() QTest::keyClick(tags, Qt::Key_Return); QCOMPARE(tags->tags().last(), QString("tag 2_is!awesome")); - // Test entry colors (simulate choosing a color) - editEntryWidget->switchToPage(EditEntryWidget::Page::Advanced); + // Test entry colors on Style page (simulate choosing a color) + editEntryWidget->switchToPage(EditEntryWidget::Page::Style); auto fgColor = QString("#FF0000"); auto bgColor = QString("#0000FF"); // Set foreground color auto colorButton = editEntryWidget->findChild("fgColorButton"); auto colorCheckBox = editEntryWidget->findChild("fgColorCheckBox"); + QVERIFY(colorButton); + QVERIFY(colorCheckBox); colorButton->setProperty("color", fgColor); colorCheckBox->setChecked(true); // Set background color colorButton = editEntryWidget->findChild("bgColorButton"); colorCheckBox = editEntryWidget->findChild("bgColorCheckBox"); + QVERIFY(colorButton); + QVERIFY(colorCheckBox); colorButton->setProperty("color", bgColor); colorCheckBox->setChecked(true); QTest::mouseClick(applyButton, Qt::LeftButton); QCOMPARE(entry->historyItems().size(), ++editCount); + // Test Attachments page is accessible + editEntryWidget->switchToPage(EditEntryWidget::Page::Attachments); + auto* attachmentsWidget = editEntryWidget->findChild("attachmentsWidget"); + QVERIFY(attachmentsWidget); + QVERIFY(attachmentsWidget->isVisible()); + // Test protected attributes - editEntryWidget->switchToPage(EditEntryWidget::Page::Advanced); + editEntryWidget->switchToPage(EditEntryWidget::Page::Attributes); auto* attrTextEdit = editEntryWidget->findChild("attributesEdit"); QTest::mouseClick(editEntryWidget->findChild("addAttributeButton"), Qt::LeftButton); QString attrText = "TEST TEXT"; @@ -1087,7 +1097,7 @@ void TestGui::testTotp() QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); - editEntryWidget->switchToPage(EditEntryWidget::Page::Advanced); + editEntryWidget->switchToPage(EditEntryWidget::Page::Attributes); auto* attrTextEdit = editEntryWidget->findChild("attributesEdit"); QTest::mouseClick(editEntryWidget->findChild("revealAttributeButton"), Qt::LeftButton); QCOMPARE(attrTextEdit->toPlainText(), expectedFinalSeed);