PDF4QT/PdfForQtViewer/pdfsidebarwidget.cpp

295 lines
9.2 KiB
C++
Raw Normal View History

2019-11-28 18:20:32 +01:00
// Copyright (C) 2019 Jakub Melka
//
// This file is part of PdfForQt.
//
// PdfForQt is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PdfForQt 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
#include "pdfsidebarwidget.h"
#include "ui_pdfsidebarwidget.h"
#include "pdfdocument.h"
#include "pdfitemmodels.h"
2019-12-03 19:24:43 +01:00
#include "pdfexception.h"
#include <QMenu>
#include <QAction>
#include <QFileDialog>
#include <QStandardPaths>
#include <QMessageBox>
2019-11-28 18:20:32 +01:00
namespace pdfviewer
{
PDFSidebarWidget::PDFSidebarWidget(QWidget* parent) :
QWidget(parent),
ui(new Ui::PDFSidebarWidget),
m_outlineTreeModel(nullptr),
m_optionalContentTreeModel(nullptr),
m_document(nullptr),
2019-12-01 18:10:11 +01:00
m_optionalContentActivity(nullptr),
m_attachmentsTreeModel(nullptr)
2019-11-28 18:20:32 +01:00
{
ui->setupUi(this);
// Outline
2019-11-29 19:10:29 +01:00
QIcon bookmarkIcon(":/resources/bookmark.svg");
m_outlineTreeModel = new pdf::PDFOutlineTreeItemModel(qMove(bookmarkIcon), this);
2019-11-28 18:20:32 +01:00
ui->bookmarksTreeView->setModel(m_outlineTreeModel);
ui->bookmarksTreeView->header()->hide();
2019-11-29 19:10:29 +01:00
connect(ui->bookmarksTreeView, &QTreeView::clicked, this, &PDFSidebarWidget::onOutlineItemClicked);
2019-11-28 18:20:32 +01:00
// Optional content
ui->optionalContentTreeView->header()->hide();
m_optionalContentTreeModel = new pdf::PDFOptionalContentTreeItemModel(this);
ui->optionalContentTreeView->setModel(m_optionalContentTreeModel);
2019-12-01 18:10:11 +01:00
// Attachments
ui->attachmentsTreeView->header()->hide();
m_attachmentsTreeModel = new pdf::PDFAttachmentsTreeItemModel(this);
ui->attachmentsTreeView->setModel(m_attachmentsTreeModel);
ui->attachmentsTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
2019-12-03 19:24:43 +01:00
ui->attachmentsTreeView->setSelectionBehavior(QAbstractItemView::SelectItems);
ui->attachmentsTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->attachmentsTreeView, &QTreeView::customContextMenuRequested, this, &PDFSidebarWidget::onAttachmentCustomContextMenuRequested);
2019-12-01 18:10:11 +01:00
2019-11-28 18:20:32 +01:00
m_pageInfo[Invalid] = { nullptr, ui->emptyPage };
m_pageInfo[OptionalContent] = { ui->optionalContentButton, ui->optionalContentPage };
m_pageInfo[Bookmarks] = { ui->bookmarksButton, ui->bookmarksPage };
m_pageInfo[Thumbnails] = { ui->thumbnailsButton, ui->thumbnailsPage };
2019-12-01 18:10:11 +01:00
m_pageInfo[Attachments] = { ui->attachmentsButton, ui->attachmentsPage };
2019-11-28 18:20:32 +01:00
setAutoFillBackground(true);
selectPage(Invalid);
updateButtons();
}
PDFSidebarWidget::~PDFSidebarWidget()
{
delete ui;
}
void PDFSidebarWidget::setDocument(const pdf::PDFDocument* document, pdf::PDFOptionalContentActivity* optionalContentActivity)
{
m_document = document;
m_optionalContentActivity = optionalContentActivity;
// Update outline
m_outlineTreeModel->setDocument(document);
// Update optional content
m_optionalContentTreeModel->setDocument(document);
m_optionalContentTreeModel->setActivity(m_optionalContentActivity);
ui->optionalContentTreeView->expandAll();
2019-12-01 18:10:11 +01:00
// Update attachments
m_attachmentsTreeModel->setDocument(document);
ui->attachmentsTreeView->expandAll();
ui->attachmentsTreeView->resizeColumnToContents(0);
2019-11-28 18:20:32 +01:00
Page preferred = Invalid;
if (m_document)
{
2019-11-29 19:10:29 +01:00
const pdf::PageMode pageMode = m_document->getCatalog()->getPageMode();
2019-11-28 18:20:32 +01:00
switch (pageMode)
{
2019-11-29 19:10:29 +01:00
case pdf::PageMode::UseOutlines:
2019-11-28 18:20:32 +01:00
preferred = Bookmarks;
break;
2019-11-29 19:10:29 +01:00
case pdf::PageMode::UseThumbnails:
2019-11-28 18:20:32 +01:00
preferred = Thumbnails;
break;
2019-11-29 19:10:29 +01:00
case pdf::PageMode::UseOptionalContent:
2019-11-28 18:20:32 +01:00
preferred = OptionalContent;
break;
2019-12-01 18:10:11 +01:00
case pdf::PageMode::UseAttachments:
preferred = Attachments;
break;
2019-11-28 18:20:32 +01:00
default:
break;
}
}
// Update GUI
updateGUI(preferred);
updateButtons();
}
bool PDFSidebarWidget::isEmpty() const
{
for (int i = _BEGIN; i < _END; ++i)
{
if (!isEmpty(static_cast<Page>(i)))
{
return false;
}
}
return true;
}
bool PDFSidebarWidget::isEmpty(Page page) const
{
switch (page)
{
case Invalid:
return true;
case Bookmarks:
return m_outlineTreeModel->isEmpty();
case Thumbnails:
return true;
2019-12-01 18:10:11 +01:00
case OptionalContent:
return m_optionalContentTreeModel->isEmpty();
case Attachments:
return m_attachmentsTreeModel->isEmpty();
2019-11-28 18:20:32 +01:00
default:
Q_ASSERT(false);
break;
}
return true;
}
void PDFSidebarWidget::selectPage(Page page)
{
// Switch state of the buttons and select the page
for (const auto& pageInfo : m_pageInfo)
{
if (pageInfo.second.button)
{
pageInfo.second.button->setChecked(pageInfo.first == page);
}
if (pageInfo.first == page)
{
ui->stackedWidget->setCurrentWidget(pageInfo.second.page);
}
}
}
std::vector<PDFSidebarWidget::Page> PDFSidebarWidget::getValidPages() const
{
std::vector<PDFSidebarWidget::Page> result;
result.reserve(_END - _BEGIN + 1);
for (int i = _BEGIN; i < _END; ++i)
{
if (!isEmpty(static_cast<Page>(i)))
{
result.push_back(static_cast<Page>(i));
}
}
return result;
}
void PDFSidebarWidget::updateGUI(Page preferredPage)
{
if (preferredPage != Invalid && !isEmpty(preferredPage))
{
selectPage(preferredPage);
}
else
{
// Select first nonempty page
std::vector<Page> validPages = getValidPages();
if (!validPages.empty())
{
selectPage(validPages.front());
}
else
{
selectPage(Invalid);
}
}
}
void PDFSidebarWidget::updateButtons()
{
for (const auto& pageInfo : m_pageInfo)
{
if (pageInfo.second.button)
{
pageInfo.second.button->setEnabled(!isEmpty(pageInfo.first));
}
}
}
2019-11-29 19:10:29 +01:00
void PDFSidebarWidget::onOutlineItemClicked(const QModelIndex& index)
{
if (const pdf::PDFAction* action = m_outlineTreeModel->getAction(index))
{
emit actionTriggered(action);
}
}
2019-12-03 19:24:43 +01:00
void PDFSidebarWidget::onAttachmentCustomContextMenuRequested(const QPoint& pos)
{
if (const pdf::PDFFileSpecification* fileSpecification = m_attachmentsTreeModel->getFileSpecification(ui->attachmentsTreeView->indexAt(pos)))
{
QMenu menu(this);
QAction* action = new QAction(QApplication::style()->standardIcon(QStyle::SP_DialogSaveButton, nullptr, ui->attachmentsTreeView), tr("Save to File..."), &menu);
auto onSaveTriggered = [this, fileSpecification]()
{
const pdf::PDFEmbeddedFile* platformFile = fileSpecification->getPlatformFile();
if (platformFile && platformFile->isValid())
{
QString defaultFileName = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + QDir::separator() + fileSpecification->getPlatformFileName();
QString saveFileName = QFileDialog::getSaveFileName(this, tr("Save attachment"), defaultFileName);
if (!saveFileName.isEmpty())
{
try
{
QByteArray data = m_document->getDecodedStream(platformFile->getStream());
QFile file(saveFileName);
if (file.open(QFile::WriteOnly | QFile::Truncate))
{
file.write(data);
file.close();
}
else
{
QMessageBox::critical(this, tr("Error"), tr("Failed to save attachment to file. %1").arg(file.errorString()));
}
}
catch (pdf::PDFException e)
{
QMessageBox::critical(this, tr("Error"), tr("Failed to save attachment to file. %1").arg(e.getMessage()));
}
}
}
else
{
QMessageBox::critical(this, tr("Error"), tr("Failed to save attachment to file. Attachment is corrupted."));
}
};
connect(action, &QAction::triggered, this, onSaveTriggered);
menu.addAction(action);
menu.exec(ui->attachmentsTreeView->viewport()->mapToGlobal(pos));
}
}
2019-11-28 18:20:32 +01:00
} // namespace pdfviewer