diff --git a/Pdf4QtDocPageOrganizer/Pdf4QtDocPageOrganizer.pro b/Pdf4QtDocPageOrganizer/Pdf4QtDocPageOrganizer.pro index 31c12bc..349f1e3 100644 --- a/Pdf4QtDocPageOrganizer/Pdf4QtDocPageOrganizer.pro +++ b/Pdf4QtDocPageOrganizer/Pdf4QtDocPageOrganizer.pro @@ -43,6 +43,7 @@ INSTALLS += application SOURCES += \ main.cpp \ mainwindow.cpp \ + pageitemdelegate.cpp \ pageitemmodel.cpp FORMS += \ @@ -50,6 +51,7 @@ FORMS += \ HEADERS += \ mainwindow.h \ + pageitemdelegate.h \ pageitemmodel.h RESOURCES += \ diff --git a/Pdf4QtDocPageOrganizer/mainwindow.cpp b/Pdf4QtDocPageOrganizer/mainwindow.cpp index 0e3f381..16415e4 100644 --- a/Pdf4QtDocPageOrganizer/mainwindow.cpp +++ b/Pdf4QtDocPageOrganizer/mainwindow.cpp @@ -19,25 +19,152 @@ #include "ui_mainwindow.h" #include "pdfwidgetutils.h" +#include "pdfdocumentreader.h" + +#include +#include +#include +#include namespace pdfdocpage { -MainWindow::MainWindow(QWidget *parent) : +MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow), - m_model(new PageItemModel(this)) + m_model(new PageItemModel(this)), + m_delegate(new PageItemDelegate(m_model, this)) { ui->setupUi(this); - ui->documentItemsView->setModel(m_model); + m_delegate->setPageImageSize(getDefaultPageImageSize()); + ui->documentItemsView->setModel(m_model); + ui->documentItemsView->setItemDelegate(m_delegate); setMinimumSize(pdf::PDFWidgetUtils::scaleDPI(this, QSize(800, 600))); + + loadSettings(); } MainWindow::~MainWindow() { + saveSettings(); delete ui; } +QSize MainWindow::getMinPageImageSize() const +{ + return pdf::PDFWidgetUtils::scaleDPI(this, QSize(40, 40)); +} + +QSize MainWindow::getDefaultPageImageSize() const +{ + return pdf::PDFWidgetUtils::scaleDPI(this, QSize(100, 100)); +} + +QSize MainWindow::getMaxPageImageSize() const +{ + return pdf::PDFWidgetUtils::scaleDPI(this, QSize(250, 250)); +} + +void MainWindow::on_actionClose_triggered() +{ + close(); +} + +void MainWindow::on_actionAddDocument_triggered() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Select PDF document"), m_settings.directory, tr("PDF document (*.pdf)")); + if (!fileName.isEmpty()) + { + addDocument(fileName); + } +} + +void MainWindow::updateActions() +{ + +} + +void MainWindow::loadSettings() +{ + QSettings settings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName()); + settings.beginGroup("MainWindow"); + QByteArray geometry = settings.value("geometry", QByteArray()).toByteArray(); + if (geometry.isEmpty()) + { + QRect availableGeometry = QApplication::desktop()->availableGeometry(this); + QRect windowRect(0, 0, availableGeometry.width() / 2, availableGeometry.height() / 2); + windowRect = windowRect.translated(availableGeometry.center() - windowRect.center()); + setGeometry(windowRect); + } + else + { + restoreGeometry(geometry); + } + + QByteArray state = settings.value("windowState", QByteArray()).toByteArray(); + if (!state.isEmpty()) + { + restoreState(state); + } + settings.endGroup(); + + settings.beginGroup("Settings"); + m_settings.directory = settings.value("directory").toString(); + settings.endGroup(); +} + +void MainWindow::saveSettings() +{ + QSettings settings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName()); + settings.beginGroup("MainWindow"); + settings.setValue("geometry", saveGeometry()); + settings.setValue("windowState", saveState()); + settings.endGroup(); + + settings.beginGroup("Settings"); + settings.setValue("directory", m_settings.directory); + settings.endGroup(); +} + +void MainWindow::addDocument(const QString& fileName) +{ + auto queryPassword = [this](bool* ok) + { + *ok = false; + return QInputDialog::getText(this, tr("Encrypted document"), tr("Enter password to access document content"), QLineEdit::Password, QString(), ok); + }; + + // Mark current directory as this + QFileInfo fileInfo(fileName); + m_settings.directory = fileInfo.dir().absolutePath(); + + // Try to open a new document + pdf::PDFDocumentReader reader(nullptr, qMove(queryPassword), true, false); + pdf::PDFDocument document = reader.readFromFile(fileName); + + QString errorMessage = reader.getErrorMessage(); + pdf::PDFDocumentReader::Result result = reader.getReadingResult(); + if (result == pdf::PDFDocumentReader::Result::OK) + { + const pdf::PDFSecurityHandler* securityHandler = document.getStorage().getSecurityHandler(); + if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::Assemble) || + securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::Modify)) + { + m_model->addDocument(fileName, qMove(document)); + } + else + { + QMessageBox::critical(this, tr("Error"), tr("Document security doesn't permit to organize pages.")); + } + } + else if (result == pdf::PDFDocumentReader::Result::Failed) + { + QMessageBox::critical(this, tr("Error"), errorMessage); + } + + updateActions(); +} + } // namespace pdfdocpage diff --git a/Pdf4QtDocPageOrganizer/mainwindow.h b/Pdf4QtDocPageOrganizer/mainwindow.h index 05425d3..91010ca 100644 --- a/Pdf4QtDocPageOrganizer/mainwindow.h +++ b/Pdf4QtDocPageOrganizer/mainwindow.h @@ -21,6 +21,7 @@ #include #include "pageitemmodel.h" +#include "pageitemdelegate.h" namespace Ui { @@ -38,10 +39,30 @@ public: explicit MainWindow(QWidget* parent); virtual ~MainWindow() override; + QSize getMinPageImageSize() const; + QSize getDefaultPageImageSize() const; + QSize getMaxPageImageSize() const; + +private slots: + void on_actionClose_triggered(); + void on_actionAddDocument_triggered(); + void updateActions(); + private: + void loadSettings(); + void saveSettings(); + void addDocument(const QString& fileName); + + struct Settings + { + QString directory; + }; + Ui::MainWindow* ui; PageItemModel* m_model; + PageItemDelegate* m_delegate; + Settings m_settings; }; } // namespace pdfdocpage diff --git a/Pdf4QtDocPageOrganizer/pageitemdelegate.cpp b/Pdf4QtDocPageOrganizer/pageitemdelegate.cpp new file mode 100644 index 0000000..bfa6c45 --- /dev/null +++ b/Pdf4QtDocPageOrganizer/pageitemdelegate.cpp @@ -0,0 +1,103 @@ +// Copyright (C) 2021 Jakub Melka +// +// This file is part of Pdf4Qt. +// +// Pdf4Qt 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 +// with the written consent of the copyright owner, any later version. +// +// Pdf4Qt 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 Pdf4Qt. If not, see . + +#include "pageitemdelegate.h" +#include "pageitemmodel.h" +#include "pdfwidgetutils.h" + +#include + +namespace pdfdocpage +{ + +PageItemDelegate::PageItemDelegate(PageItemModel* model, QObject* parent) : + BaseClass(parent), + m_model(model) +{ + +} + +PageItemDelegate::~PageItemDelegate() +{ + +} + +void PageItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + const PageGroupItem* item = m_model->getItem(index); + + if (!item) + { + return; + } + + QRect rect = option.rect; + + QSize scaledSize = pdf::PDFWidgetUtils::scaleDPI(option.widget, m_pageImageSize); + int verticalSpacing = pdf::PDFWidgetUtils::scaleDPI_y(option.widget, getVerticalSpacing()); + + QRect pageBoundingRect = QRect(QPoint(rect.left() + (rect.width() - scaledSize.width()) / 2, rect.top() + verticalSpacing), scaledSize); + + // Draw page preview + if (!item->groups.empty()) + { + const PageGroupItem::GroupItem& groupItem = item->groups.front(); + QSize pageImageSize = groupItem.rotatedPageDimensionsMM.scaled(pageBoundingRect.size(), Qt::KeepAspectRatio).toSize(); + QRect pageImageRect(pageBoundingRect.topLeft() + QPoint((pageBoundingRect.width() - pageImageSize.width()) / 2, (pageBoundingRect.height() - pageImageSize.height()) / 2), pageImageSize); + + painter->setPen(QPen(Qt::black)); + painter->setBrush(Qt::white); + painter->drawRect(pageImageRect); + } + + int textOffset = pageBoundingRect.bottom() + verticalSpacing; + QRect textRect = option.rect; + textRect.setTop(textOffset); + textRect.setHeight(option.fontMetrics.lineSpacing()); + painter->drawText(textRect, Qt::AlignCenter | Qt::TextSingleLine, item->groupName); + textRect.translate(0, textRect.height()); + painter->drawText(textRect, Qt::AlignCenter | Qt::TextSingleLine, item->pagesCaption); + + if (option.state.testFlag(QStyle::State_Selected)) + { + QColor selectedColor = option.palette.color(QPalette::Active, QPalette::Highlight); + selectedColor.setAlphaF(0.3); + painter->fillRect(rect, selectedColor); + } +} + +QSize PageItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + Q_UNUSED(index); + + QSize scaledSize = pdf::PDFWidgetUtils::scaleDPI(option.widget, m_pageImageSize); + int height = scaledSize.height() + option.fontMetrics.lineSpacing() * 2 + 2 * pdf::PDFWidgetUtils::scaleDPI_y(option.widget, getVerticalSpacing()); + int width = qMax(pdf::PDFWidgetUtils::scaleDPI_x(option.widget, 40), scaledSize.width() + 2 * pdf::PDFWidgetUtils::scaleDPI_x(option.widget, getHorizontalSpacing())); + return QSize(height, width); +} + +QSize PageItemDelegate::getPageImageSize() const +{ + return m_pageImageSize; +} + +void PageItemDelegate::setPageImageSize(const QSize& pageImageSize) +{ + m_pageImageSize = pageImageSize; +} + +} // namespace pdfdocpage diff --git a/Pdf4QtDocPageOrganizer/pageitemdelegate.h b/Pdf4QtDocPageOrganizer/pageitemdelegate.h new file mode 100644 index 0000000..f9a6d33 --- /dev/null +++ b/Pdf4QtDocPageOrganizer/pageitemdelegate.h @@ -0,0 +1,55 @@ +// Copyright (C) 2021 Jakub Melka +// +// This file is part of Pdf4Qt. +// +// Pdf4Qt 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 +// with the written consent of the copyright owner, any later version. +// +// Pdf4Qt 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 Pdf4Qt. If not, see . + +#ifndef PDFDOCPAGEORGANIZER_PAGEITEMDELEGATE_H +#define PDFDOCPAGEORGANIZER_PAGEITEMDELEGATE_H + +#include + +namespace pdfdocpage +{ + +class PageItemModel; + +class PageItemDelegate : public QAbstractItemDelegate +{ + Q_OBJECT + +private: + using BaseClass = QAbstractItemDelegate; + +public: + explicit PageItemDelegate(PageItemModel* model, QObject* parent); + virtual ~PageItemDelegate() override; + + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + + QSize getPageImageSize() const; + void setPageImageSize(const QSize& pageImageSize); + +private: + static constexpr int getVerticalSpacing() { return 5; } + static constexpr int getHorizontalSpacing() { return 5; } + + PageItemModel* m_model; + QSize m_pageImageSize; +}; + +} // namespace pdfdocpage + +#endif // PDFDOCPAGEORGANIZER_PAGEITEMDELEGATE_H diff --git a/Pdf4QtDocPageOrganizer/pageitemmodel.cpp b/Pdf4QtDocPageOrganizer/pageitemmodel.cpp index d0dbd10..77990dd 100644 --- a/Pdf4QtDocPageOrganizer/pageitemmodel.cpp +++ b/Pdf4QtDocPageOrganizer/pageitemmodel.cpp @@ -55,7 +55,7 @@ QModelIndex PageItemModel::parent(const QModelIndex& index) const int PageItemModel::rowCount(const QModelIndex& parent) const { - if (!parent.isValid()) + if (parent.isValid()) { return 0; } @@ -65,7 +65,7 @@ int PageItemModel::rowCount(const QModelIndex& parent) const int PageItemModel::columnCount(const QModelIndex& parent) const { - if (!parent.isValid()) + if (parent.isValid()) { return 0; } @@ -112,6 +112,19 @@ int PageItemModel::addDocument(QString fileName, pdf::PDFDocument document) return newIndex; } +const PageGroupItem* PageItemModel::getItem(const QModelIndex& index) const +{ + if (index.isValid()) + { + if (index.row() < m_pageGroupItems.size()) + { + return &m_pageGroupItems.at(index.row()); + } + } + + return nullptr; +} + void PageItemModel::createDocumentGroup(int index) { const DocumentItem& item = m_documents.at(index); @@ -134,7 +147,7 @@ void PageItemModel::createDocumentGroup(int index) PageGroupItem::GroupItem groupItem; groupItem.documentIndex = index; groupItem.pageIndex = i; - groupItem.rotatedPageDimensionsMM = item.document.getCatalog()->getPage(i)->getRotatedMediaBoxMM().size(); + groupItem.rotatedPageDimensionsMM = item.document.getCatalog()->getPage(i - 1)->getRotatedMediaBoxMM().size(); newItem.groups.push_back(qMove(groupItem)); } diff --git a/Pdf4QtDocPageOrganizer/pageitemmodel.h b/Pdf4QtDocPageOrganizer/pageitemmodel.h index 74cc9e9..946db0d 100644 --- a/Pdf4QtDocPageOrganizer/pageitemmodel.h +++ b/Pdf4QtDocPageOrganizer/pageitemmodel.h @@ -70,6 +70,11 @@ public: /// \returns Identifier of the document (internal index) int addDocument(QString fileName, pdf::PDFDocument document); + /// Returns item at a given index. If item doesn't exist, + /// then nullptr is returned. + /// \param index Index + const PageGroupItem* getItem(const QModelIndex& index) const; + private: void createDocumentGroup(int index); QString getGroupNameFromDocument(int index) const;