File indexing completed on 2024-04-28 15:51:44
0001 /* 0002 SPDX-FileCopyrightText: 2006 Albert Astals Cid <aacid@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "embeddedfilesdialog.h" 0008 0009 #include <QAction> 0010 #include <QCursor> 0011 #include <QDateTime> 0012 #include <QDir> 0013 #include <QFileInfo> 0014 #include <QMenu> 0015 #include <QTemporaryFile> 0016 #include <QTreeWidget> 0017 0018 #include <KConfigGroup> 0019 #include <KFormat> 0020 #include <KIO/JobUiDelegate> 0021 #include <KIO/JobUiDelegateFactory> 0022 #include <KIO/OpenUrlJob> 0023 #include <KLocalizedString> 0024 #include <KStandardGuiItem> 0025 #include <QDialogButtonBox> 0026 #include <QIcon> 0027 #include <QMimeDatabase> 0028 #include <QMimeType> 0029 #include <QPushButton> 0030 #include <QVBoxLayout> 0031 #include <kio_version.h> 0032 0033 #include "core/document.h" 0034 #include "gui/guiutils.h" 0035 0036 Q_DECLARE_METATYPE(Okular::EmbeddedFile *) 0037 0038 static const int EmbeddedFileRole = Qt::UserRole + 100; 0039 0040 static QString dateToString(const QDateTime &date) 0041 { 0042 return date.isValid() ? QLocale().toString(date, QLocale::LongFormat) : i18nc("Unknown date", "Unknown"); 0043 } 0044 0045 EmbeddedFilesDialog::EmbeddedFilesDialog(QWidget *parent, const Okular::Document *document) 0046 : QDialog(parent) 0047 { 0048 setWindowTitle(i18nc("@title:window", "Embedded Files")); 0049 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); 0050 QVBoxLayout *mainLayout = new QVBoxLayout; 0051 setLayout(mainLayout); 0052 mUser1Button = new QPushButton; 0053 buttonBox->addButton(mUser1Button, QDialogButtonBox::ActionRole); 0054 connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); 0055 connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); 0056 buttonBox->button(QDialogButtonBox::Close)->setDefault(true); 0057 KGuiItem::assign(mUser1Button, KStandardGuiItem::save()); 0058 mUser1Button->setEnabled(false); 0059 0060 mUser2Button = new QPushButton; 0061 buttonBox->addButton(mUser2Button, QDialogButtonBox::ActionRole); 0062 KGuiItem::assign(mUser2Button, KGuiItem(i18nc("@action:button", "View"), QStringLiteral("document-open"))); 0063 mUser2Button->setEnabled(false); 0064 0065 m_tw = new QTreeWidget(this); 0066 mainLayout->addWidget(m_tw); 0067 mainLayout->addWidget(buttonBox); 0068 0069 QStringList header; 0070 header.append(i18nc("@title:column", "Name")); 0071 header.append(i18nc("@title:column", "Description")); 0072 header.append(i18nc("@title:column", "Size")); 0073 header.append(i18nc("@title:column", "Created")); 0074 header.append(i18nc("@title:column", "Modified")); 0075 m_tw->setHeaderLabels(header); 0076 m_tw->setRootIsDecorated(false); 0077 m_tw->setSelectionMode(QAbstractItemView::ExtendedSelection); 0078 m_tw->setContextMenuPolicy(Qt::CustomContextMenu); 0079 0080 // embeddedFiles() returns a const QList 0081 for (Okular::EmbeddedFile *ef : *document->embeddedFiles()) { 0082 QTreeWidgetItem *twi = new QTreeWidgetItem(); 0083 twi->setText(0, ef->name()); 0084 QMimeDatabase db; 0085 QMimeType mime = db.mimeTypeForFile(ef->name(), QMimeDatabase::MatchExtension); 0086 if (mime.isValid()) { 0087 twi->setIcon(0, QIcon::fromTheme(mime.iconName())); 0088 } 0089 twi->setText(1, ef->description()); 0090 twi->setText(2, ef->size() <= 0 ? i18nc("Not available size", "N/A") : KFormat().formatByteSize(ef->size())); 0091 twi->setText(3, dateToString(ef->creationDate())); 0092 twi->setText(4, dateToString(ef->modificationDate())); 0093 twi->setData(0, EmbeddedFileRole, QVariant::fromValue(ef)); 0094 m_tw->addTopLevelItem(twi); 0095 } 0096 // Having filled the columns, it is nice to resize them to be able to read the contents 0097 for (int lv = 0; lv < m_tw->columnCount(); ++lv) { 0098 m_tw->resizeColumnToContents(lv); 0099 } 0100 // This is a bit dubious, but I'm not seeing a nice way to say "expand to fit contents" 0101 m_tw->setMinimumWidth(640); 0102 m_tw->updateGeometry(); 0103 0104 connect(mUser1Button, &QPushButton::clicked, this, &EmbeddedFilesDialog::saveFileFromButton); 0105 connect(mUser2Button, &QPushButton::clicked, this, &EmbeddedFilesDialog::viewFileFromButton); 0106 connect(m_tw, &QWidget::customContextMenuRequested, this, &EmbeddedFilesDialog::attachViewContextMenu); 0107 connect(m_tw, &QTreeWidget::itemSelectionChanged, this, &EmbeddedFilesDialog::updateSaveButton); 0108 connect(m_tw, &QTreeWidget::itemDoubleClicked, this, &EmbeddedFilesDialog::viewFileItem); 0109 } 0110 0111 void EmbeddedFilesDialog::updateSaveButton() 0112 { 0113 bool enable = (m_tw->selectedItems().count() > 0); 0114 mUser1Button->setEnabled(enable); 0115 mUser2Button->setEnabled(enable); 0116 } 0117 0118 void EmbeddedFilesDialog::saveFileFromButton() 0119 { 0120 const QList<QTreeWidgetItem *> selected = m_tw->selectedItems(); 0121 for (const QTreeWidgetItem *twi : selected) { 0122 Okular::EmbeddedFile *ef = qvariant_cast<Okular::EmbeddedFile *>(twi->data(0, EmbeddedFileRole)); 0123 saveFile(ef); 0124 } 0125 } 0126 0127 void EmbeddedFilesDialog::viewFileFromButton() 0128 { 0129 const QList<QTreeWidgetItem *> selected = m_tw->selectedItems(); 0130 for (QTreeWidgetItem *twi : selected) { 0131 Okular::EmbeddedFile *ef = qvariant_cast<Okular::EmbeddedFile *>(twi->data(0, EmbeddedFileRole)); 0132 viewFile(ef); 0133 } 0134 } 0135 0136 void EmbeddedFilesDialog::viewFileItem(QTreeWidgetItem *item, int /*column*/) 0137 { 0138 Okular::EmbeddedFile *ef = qvariant_cast<Okular::EmbeddedFile *>(item->data(0, EmbeddedFileRole)); 0139 viewFile(ef); 0140 } 0141 0142 void EmbeddedFilesDialog::attachViewContextMenu() 0143 { 0144 QList<QTreeWidgetItem *> selected = m_tw->selectedItems(); 0145 if (selected.isEmpty()) { 0146 return; 0147 } 0148 0149 if (selected.size() > 1) { 0150 return; 0151 } 0152 0153 QMenu menu(this); 0154 const QAction *saveAsAct = menu.addAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18nc("@action:inmenu", "&Save As...")); 0155 const QAction *viewAct = menu.addAction(QIcon::fromTheme(QStringLiteral("document-open")), i18nc("@action:inmenu", "&View...")); 0156 0157 const QAction *act = menu.exec(QCursor::pos()); 0158 if (!act) { 0159 return; 0160 } 0161 0162 Okular::EmbeddedFile *ef = qvariant_cast<Okular::EmbeddedFile *>(selected.at(0)->data(0, EmbeddedFileRole)); 0163 if (act == saveAsAct) { 0164 saveFile(ef); 0165 } else if (act == viewAct) { 0166 viewFile(ef); 0167 } 0168 } 0169 0170 void EmbeddedFilesDialog::viewFile(Okular::EmbeddedFile *ef) 0171 { 0172 // get name and extension 0173 QFileInfo fileInfo(ef->name()); 0174 0175 // save in temporary directory with a unique name resembling the attachment name, 0176 // using QTemporaryFile's XXXXXX placeholder 0177 QTemporaryFile *tmpFile = 0178 new QTemporaryFile(QDir::tempPath() + QLatin1Char('/') + fileInfo.baseName() + QStringLiteral(".XXXXXX") + (fileInfo.completeSuffix().isEmpty() ? QLatin1String("") : QString(QLatin1Char('.') + fileInfo.completeSuffix()))); 0179 GuiUtils::writeEmbeddedFile(ef, this, *tmpFile); 0180 0181 // set readonly to prevent the viewer application from modifying it 0182 tmpFile->setPermissions(QFile::ReadOwner); 0183 0184 // keep temporary file alive while the dialog is open 0185 m_openedFiles.push_back(QSharedPointer<QTemporaryFile>(tmpFile)); 0186 0187 // view the temporary file with the default application 0188 auto *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(tmpFile->fileName())); 0189 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this)); 0190 job->start(); 0191 } 0192 0193 void EmbeddedFilesDialog::saveFile(Okular::EmbeddedFile *ef) 0194 { 0195 GuiUtils::saveEmbeddedFile(ef, this); 0196 } 0197 0198 #include "moc_embeddedfilesdialog.cpp"