From 47d1bb0931bd6902d1ba3cbb404bcb78f9795fcd Mon Sep 17 00:00:00 2001 From: w15dev Date: Wed, 8 Jan 2025 21:17:25 +0300 Subject: [PATCH] Add preview functionality for entry attachments Supported types: - text - image Fixes #11506 --- src/CMakeLists.txt | 1 + ...ntsDialog.ui => EntryAttachmentsDialog.ui} | 6 +- src/gui/entry/EntryAttachmentsWidget.cpp | 25 +++- src/gui/entry/EntryAttachmentsWidget.h | 1 + src/gui/entry/EntryAttachmentsWidget.ui | 10 ++ src/gui/entry/NewEntryAttachmentsDialog.cpp | 12 +- src/gui/entry/NewEntryAttachmentsDialog.h | 4 +- .../entry/PreviewEntryAttachmentsDialog.cpp | 109 ++++++++++++++++++ src/gui/entry/PreviewEntryAttachmentsDialog.h | 65 +++++++++++ 9 files changed, 223 insertions(+), 10 deletions(-) rename src/gui/entry/{NewEntryAttachmentsDialog.ui => EntryAttachmentsDialog.ui} (89%) create mode 100644 src/gui/entry/PreviewEntryAttachmentsDialog.cpp create mode 100644 src/gui/entry/PreviewEntryAttachmentsDialog.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 59c4587f3f..84c6090ba5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -158,6 +158,7 @@ set(gui_SOURCES gui/entry/EntryAttachmentsWidget.cpp gui/entry/EntryAttributesModel.cpp gui/entry/NewEntryAttachmentsDialog.cpp + gui/entry/PreviewEntryAttachmentsDialog.cpp gui/entry/EntryHistoryModel.cpp gui/entry/EntryModel.cpp gui/entry/EntryView.cpp diff --git a/src/gui/entry/NewEntryAttachmentsDialog.ui b/src/gui/entry/EntryAttachmentsDialog.ui similarity index 89% rename from src/gui/entry/NewEntryAttachmentsDialog.ui rename to src/gui/entry/EntryAttachmentsDialog.ui index 7c7397bb8f..2b13ea0be1 100644 --- a/src/gui/entry/NewEntryAttachmentsDialog.ui +++ b/src/gui/entry/EntryAttachmentsDialog.ui @@ -1,7 +1,7 @@ - NewEntryAttachmentsDialog - + EntryAttachmentsDialog + 0 @@ -11,7 +11,7 @@ - New entry attachments + Form diff --git a/src/gui/entry/EntryAttachmentsWidget.cpp b/src/gui/entry/EntryAttachmentsWidget.cpp index a649844980..9e4d1e1e0c 100644 --- a/src/gui/entry/EntryAttachmentsWidget.cpp +++ b/src/gui/entry/EntryAttachmentsWidget.cpp @@ -17,6 +17,7 @@ #include "EntryAttachmentsWidget.h" #include "NewEntryAttachmentsDialog.h" +#include "PreviewEntryAttachmentsDialog.h" #include "ui_EntryAttachmentsWidget.h" #include @@ -26,11 +27,11 @@ #include #include "EntryAttachmentsModel.h" -#include "core/Config.h" #include "core/EntryAttachments.h" #include "core/Tools.h" #include "gui/FileDialog.h" #include "gui/MessageBox.h" +#include EntryAttachmentsWidget::EntryAttachmentsWidget(QWidget* parent) : QWidget(parent) @@ -70,6 +71,7 @@ EntryAttachmentsWidget::EntryAttachmentsWidget(QWidget* parent) connect(m_ui->openAttachmentButton, SIGNAL(clicked()), SLOT(openSelectedAttachments())); connect(m_ui->addAttachmentButton, SIGNAL(clicked()), SLOT(insertAttachments())); connect(m_ui->newAttachmentButton, SIGNAL(clicked()), SLOT(newAttachments())); + connect(m_ui->previewButton, SIGNAL(clicked()), SLOT(previewAttachments())); connect(m_ui->removeAttachmentButton, SIGNAL(clicked()), SLOT(removeSelectedAttachments())); connect(m_ui->renameAttachmentButton, SIGNAL(clicked()), SLOT(renameSelectedAttachments())); @@ -173,12 +175,28 @@ void EntryAttachmentsWidget::newAttachments() return; } - auto newWidnow = new NewEntryAttachmentsDialog(m_entryAttachments, this); - if (newWidnow->exec() == QDialog::Accepted) { + NewEntryAttachmentsDialog newEntryDialog{m_entryAttachments, this}; + if (newEntryDialog.exec() == QDialog::Accepted) { emit widgetUpdated(); } } +void EntryAttachmentsWidget::previewAttachments() +{ + Q_ASSERT(m_entryAttachments); + + const auto index = m_ui->attachmentsView->selectionModel()->selectedIndexes().first(); + if (!index.isValid()) { + qWarning() << tr("Failed to preview an attachment: Attachment not found"); + return; + } + + PreviewEntryAttachmentsDialog previewDialog{m_entryAttachments, this}; + previewDialog.setAttachment(m_attachmentsModel->keyByIndex(index)); + + previewDialog.exec(); +} + void EntryAttachmentsWidget::removeSelectedAttachments() { Q_ASSERT(m_entryAttachments); @@ -318,6 +336,7 @@ void EntryAttachmentsWidget::updateButtonsEnabled() m_ui->addAttachmentButton->setEnabled(!m_readOnly); m_ui->newAttachmentButton->setEnabled(!m_readOnly); m_ui->removeAttachmentButton->setEnabled(hasSelection && !m_readOnly); + m_ui->previewButton->setEnabled(hasSelection && !m_readOnly); m_ui->renameAttachmentButton->setEnabled(hasSelection && !m_readOnly); m_ui->saveAttachmentButton->setEnabled(hasSelection); diff --git a/src/gui/entry/EntryAttachmentsWidget.h b/src/gui/entry/EntryAttachmentsWidget.h index 5424dfa64d..3dfdf0ef6f 100644 --- a/src/gui/entry/EntryAttachmentsWidget.h +++ b/src/gui/entry/EntryAttachmentsWidget.h @@ -58,6 +58,7 @@ public slots: private slots: void insertAttachments(); void newAttachments(); + void previewAttachments(); void removeSelectedAttachments(); void renameSelectedAttachments(); void saveSelectedAttachments(); diff --git a/src/gui/entry/EntryAttachmentsWidget.ui b/src/gui/entry/EntryAttachmentsWidget.ui index a0c9f37830..16e578613c 100644 --- a/src/gui/entry/EntryAttachmentsWidget.ui +++ b/src/gui/entry/EntryAttachmentsWidget.ui @@ -100,6 +100,16 @@ + + + + false + + + Preview + + + diff --git a/src/gui/entry/NewEntryAttachmentsDialog.cpp b/src/gui/entry/NewEntryAttachmentsDialog.cpp index d7c4ee8e80..9bff623c38 100644 --- a/src/gui/entry/NewEntryAttachmentsDialog.cpp +++ b/src/gui/entry/NewEntryAttachmentsDialog.cpp @@ -17,7 +17,7 @@ #include "NewEntryAttachmentsDialog.h" #include "core/EntryAttachments.h" -#include "ui_NewEntryAttachmentsDialog.h" +#include "ui_EntryAttachmentsDialog.h" #include #include @@ -25,10 +25,18 @@ NewEntryAttachmentsDialog::NewEntryAttachmentsDialog(QPointer attachments, QWidget* parent) : QDialog(parent) , m_attachments(std::move(attachments)) - , m_ui(new Ui::NewEntryAttachmentsDialog) + , m_ui(new Ui::EntryAttachmentsDialog) { + Q_ASSERT(m_attachments); + m_ui->setupUi(this); + setWindowTitle(tr("New entry attachment")); + + m_ui->dialogButtons->clear(); + m_ui->dialogButtons->addButton(QDialogButtonBox::Ok); + m_ui->dialogButtons->addButton(QDialogButtonBox::Cancel); + connect(m_ui->dialogButtons, SIGNAL(accepted()), this, SLOT(saveAttachment())); connect(m_ui->dialogButtons, SIGNAL(rejected()), this, SLOT(reject())); connect(m_ui->titleEdit, SIGNAL(textChanged(const QString&)), this, SLOT(fileNameTextChanged(const QString&))); diff --git a/src/gui/entry/NewEntryAttachmentsDialog.h b/src/gui/entry/NewEntryAttachmentsDialog.h index ff861fd23e..09dfdaed6d 100644 --- a/src/gui/entry/NewEntryAttachmentsDialog.h +++ b/src/gui/entry/NewEntryAttachmentsDialog.h @@ -25,7 +25,7 @@ namespace Ui { - class NewEntryAttachmentsDialog; + class EntryAttachmentsDialog; } class QByteArray; @@ -48,7 +48,7 @@ private slots: private: QPointer m_attachments; - QScopedPointer m_ui; + QScopedPointer m_ui; }; #endif // NEWENTRYATTACHMENTSWIDGET_H diff --git a/src/gui/entry/PreviewEntryAttachmentsDialog.cpp b/src/gui/entry/PreviewEntryAttachmentsDialog.cpp new file mode 100644 index 0000000000..51d1180611 --- /dev/null +++ b/src/gui/entry/PreviewEntryAttachmentsDialog.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PreviewEntryAttachmentsDialog.h" +#include "core/EntryAttachments.h" +#include "ui_EntryAttachmentsDialog.h" + +#include +#include +#include +#include + +PreviewEntryAttachmentsDialog::PreviewEntryAttachmentsDialog(QPointer attachments, QWidget* parent) + : QDialog(parent) + , m_attachments(std::move(attachments)) + , m_ui(new Ui::EntryAttachmentsDialog) +{ + Q_ASSERT(m_attachments); + + m_ui->setupUi(this); + + setWindowTitle(tr("Preview entry attachment")); + + m_ui->titleEdit->setReadOnly(true); + m_ui->attachmentTextEdit->setReadOnly(true); + + m_ui->dialogButtons->clear(); + m_ui->dialogButtons->addButton(QDialogButtonBox::Close); + + connect(m_ui->dialogButtons, SIGNAL(rejected()), this, SLOT(reject())); +} + +PreviewEntryAttachmentsDialog::~PreviewEntryAttachmentsDialog() = default; + +void PreviewEntryAttachmentsDialog::setAttachment(const QString& name) +{ + m_name = name; + m_type = attachmentType(m_name); + + m_ui->titleEdit->setText(m_name); + + update(); +} + +void PreviewEntryAttachmentsDialog::update() +{ + if (m_type == AttachmentType::Unknown) { + updateTextAttachment(tr("No preview available").toUtf8()); + } else if (const auto data = m_attachments->value(m_name); m_type == AttachmentType::Image) { + updateImageAttachment(data); + } else if (m_type == AttachmentType::PlantText) { + updateTextAttachment(data); + } +} + +void PreviewEntryAttachmentsDialog::updateTextAttachment(const QByteArray& data) +{ + m_ui->attachmentTextEdit->setPlainText(QString::fromUtf8(data)); +} + +void PreviewEntryAttachmentsDialog::updateImageAttachment(const QByteArray& data) +{ + QImage image{}; + image.loadFromData(data); + + m_ui->attachmentTextEdit->clear(); + auto cursor = m_ui->attachmentTextEdit->textCursor(); + + cursor.insertImage(image.scaled(m_ui->attachmentTextEdit->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); +} + +PreviewEntryAttachmentsDialog::AttachmentType PreviewEntryAttachmentsDialog::attachmentType(const QString& name) const +{ + const auto data = m_attachments->value(name); + + QMimeDatabase mimeDb{}; + const auto mime = mimeDb.mimeTypeForData(data); + + if (auto mimeName = mime.name(); mimeName.startsWith("image/")) { + return AttachmentType::Image; + } else if (mimeName.startsWith("text/")) { + return AttachmentType::PlantText; + } + + return AttachmentType::Unknown; +} + +void PreviewEntryAttachmentsDialog::resizeEvent(QResizeEvent* event) +{ + QDialog::resizeEvent(event); + + if (m_type == AttachmentType::Image) { + update(); + } +} diff --git a/src/gui/entry/PreviewEntryAttachmentsDialog.h b/src/gui/entry/PreviewEntryAttachmentsDialog.h new file mode 100644 index 0000000000..ae1d8dde20 --- /dev/null +++ b/src/gui/entry/PreviewEntryAttachmentsDialog.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PREVIEWENTRYATTACHMENTSWIDGET_H +#define PREVIEWENTRYATTACHMENTSWIDGET_H + +#include +#include + +namespace Ui +{ + class EntryAttachmentsDialog; +} + +class QByteArray; +class EntryAttachments; + +class PreviewEntryAttachmentsDialog : public QDialog +{ + Q_OBJECT +public: + explicit PreviewEntryAttachmentsDialog(QPointer attachments, QWidget* parent = nullptr); + ~PreviewEntryAttachmentsDialog() override; + + void setAttachment(const QString& name); + +private: + enum class AttachmentType + { + Image, + PlantText, + Unknown + }; + + void resizeEvent(QResizeEvent* event) override; + + AttachmentType attachmentType(const QString& name) const; + + void update(); + void updateTextAttachment(const QByteArray& data); + void updateImageAttachment(const QByteArray& data); +private: + QPointer m_attachments; + + QScopedPointer m_ui; + + QString m_name; + AttachmentType m_type{AttachmentType::Unknown}; +}; + +#endif // PREVIEWENTRYATTACHMENTSWIDGET_H