okular/part/embeddedfilesdialog.cpp
2023-12-18 22:56:22 +00:00

199 lines
7.2 KiB
C++

/*
SPDX-FileCopyrightText: 2006 Albert Astals Cid <aacid@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "embeddedfilesdialog.h"
#include <QAction>
#include <QCursor>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
#include <QMenu>
#include <QTemporaryFile>
#include <QTreeWidget>
#include <KConfigGroup>
#include <KFormat>
#include <KIO/JobUiDelegate>
#include <KIO/JobUiDelegateFactory>
#include <KIO/OpenUrlJob>
#include <KLocalizedString>
#include <KStandardGuiItem>
#include <QDialogButtonBox>
#include <QIcon>
#include <QMimeDatabase>
#include <QMimeType>
#include <QPushButton>
#include <QVBoxLayout>
#include <kio_version.h>
#include "core/document.h"
#include "gui/guiutils.h"
Q_DECLARE_METATYPE(Okular::EmbeddedFile *)
static const int EmbeddedFileRole = Qt::UserRole + 100;
static QString dateToString(const QDateTime &date)
{
return date.isValid() ? QLocale().toString(date, QLocale::LongFormat) : i18nc("Unknown date", "Unknown");
}
EmbeddedFilesDialog::EmbeddedFilesDialog(QWidget *parent, const Okular::Document *document)
: QDialog(parent)
{
setWindowTitle(i18nc("@title:window", "Embedded Files"));
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
QVBoxLayout *mainLayout = new QVBoxLayout;
setLayout(mainLayout);
mUser1Button = new QPushButton;
buttonBox->addButton(mUser1Button, QDialogButtonBox::ActionRole);
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
KGuiItem::assign(mUser1Button, KStandardGuiItem::save());
mUser1Button->setEnabled(false);
mUser2Button = new QPushButton;
buttonBox->addButton(mUser2Button, QDialogButtonBox::ActionRole);
KGuiItem::assign(mUser2Button, KGuiItem(i18nc("@action:button", "View"), QStringLiteral("document-open")));
mUser2Button->setEnabled(false);
m_tw = new QTreeWidget(this);
mainLayout->addWidget(m_tw);
mainLayout->addWidget(buttonBox);
QStringList header;
header.append(i18nc("@title:column", "Name"));
header.append(i18nc("@title:column", "Description"));
header.append(i18nc("@title:column", "Size"));
header.append(i18nc("@title:column", "Created"));
header.append(i18nc("@title:column", "Modified"));
m_tw->setHeaderLabels(header);
m_tw->setRootIsDecorated(false);
m_tw->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_tw->setContextMenuPolicy(Qt::CustomContextMenu);
// embeddedFiles() returns a const QList
for (Okular::EmbeddedFile *ef : *document->embeddedFiles()) {
QTreeWidgetItem *twi = new QTreeWidgetItem();
twi->setText(0, ef->name());
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFile(ef->name(), QMimeDatabase::MatchExtension);
if (mime.isValid()) {
twi->setIcon(0, QIcon::fromTheme(mime.iconName()));
}
twi->setText(1, ef->description());
twi->setText(2, ef->size() <= 0 ? i18nc("Not available size", "N/A") : KFormat().formatByteSize(ef->size()));
twi->setText(3, dateToString(ef->creationDate()));
twi->setText(4, dateToString(ef->modificationDate()));
twi->setData(0, EmbeddedFileRole, QVariant::fromValue(ef));
m_tw->addTopLevelItem(twi);
}
// Having filled the columns, it is nice to resize them to be able to read the contents
for (int lv = 0; lv < m_tw->columnCount(); ++lv) {
m_tw->resizeColumnToContents(lv);
}
// This is a bit dubious, but I'm not seeing a nice way to say "expand to fit contents"
m_tw->setMinimumWidth(640);
m_tw->updateGeometry();
connect(mUser1Button, &QPushButton::clicked, this, &EmbeddedFilesDialog::saveFileFromButton);
connect(mUser2Button, &QPushButton::clicked, this, &EmbeddedFilesDialog::viewFileFromButton);
connect(m_tw, &QWidget::customContextMenuRequested, this, &EmbeddedFilesDialog::attachViewContextMenu);
connect(m_tw, &QTreeWidget::itemSelectionChanged, this, &EmbeddedFilesDialog::updateSaveButton);
connect(m_tw, &QTreeWidget::itemDoubleClicked, this, &EmbeddedFilesDialog::viewFileItem);
}
void EmbeddedFilesDialog::updateSaveButton()
{
bool enable = (m_tw->selectedItems().count() > 0);
mUser1Button->setEnabled(enable);
mUser2Button->setEnabled(enable);
}
void EmbeddedFilesDialog::saveFileFromButton()
{
const QList<QTreeWidgetItem *> selected = m_tw->selectedItems();
for (const QTreeWidgetItem *twi : selected) {
Okular::EmbeddedFile *ef = qvariant_cast<Okular::EmbeddedFile *>(twi->data(0, EmbeddedFileRole));
saveFile(ef);
}
}
void EmbeddedFilesDialog::viewFileFromButton()
{
const QList<QTreeWidgetItem *> selected = m_tw->selectedItems();
for (QTreeWidgetItem *twi : selected) {
Okular::EmbeddedFile *ef = qvariant_cast<Okular::EmbeddedFile *>(twi->data(0, EmbeddedFileRole));
viewFile(ef);
}
}
void EmbeddedFilesDialog::viewFileItem(QTreeWidgetItem *item, int /*column*/)
{
Okular::EmbeddedFile *ef = qvariant_cast<Okular::EmbeddedFile *>(item->data(0, EmbeddedFileRole));
viewFile(ef);
}
void EmbeddedFilesDialog::attachViewContextMenu()
{
QList<QTreeWidgetItem *> selected = m_tw->selectedItems();
if (selected.isEmpty()) {
return;
}
if (selected.size() > 1) {
return;
}
QMenu menu(this);
const QAction *saveAsAct = menu.addAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18nc("@action:inmenu", "&Save As..."));
const QAction *viewAct = menu.addAction(QIcon::fromTheme(QStringLiteral("document-open")), i18nc("@action:inmenu", "&View..."));
const QAction *act = menu.exec(QCursor::pos());
if (!act) {
return;
}
Okular::EmbeddedFile *ef = qvariant_cast<Okular::EmbeddedFile *>(selected.at(0)->data(0, EmbeddedFileRole));
if (act == saveAsAct) {
saveFile(ef);
} else if (act == viewAct) {
viewFile(ef);
}
}
void EmbeddedFilesDialog::viewFile(Okular::EmbeddedFile *ef)
{
// get name and extension
QFileInfo fileInfo(ef->name());
// save in temporary directory with a unique name resembling the attachment name,
// using QTemporaryFile's XXXXXX placeholder
QTemporaryFile *tmpFile =
new QTemporaryFile(QDir::tempPath() + QLatin1Char('/') + fileInfo.baseName() + QStringLiteral(".XXXXXX") + (fileInfo.completeSuffix().isEmpty() ? QLatin1String("") : QString(QLatin1Char('.') + fileInfo.completeSuffix())));
GuiUtils::writeEmbeddedFile(ef, this, *tmpFile);
// set readonly to prevent the viewer application from modifying it
tmpFile->setPermissions(QFile::ReadOwner);
// keep temporary file alive while the dialog is open
m_openedFiles.push_back(QSharedPointer<QTemporaryFile>(tmpFile));
// view the temporary file with the default application
auto *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(tmpFile->fileName()));
job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
job->start();
}
void EmbeddedFilesDialog::saveFile(Okular::EmbeddedFile *ef)
{
GuiUtils::saveEmbeddedFile(ef, this);
}
#include "moc_embeddedfilesdialog.cpp"