diff options
Diffstat (limited to 'src/pdf/qpdfbookmarkmodel.cpp')
-rw-r--r-- | src/pdf/qpdfbookmarkmodel.cpp | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/src/pdf/qpdfbookmarkmodel.cpp b/src/pdf/qpdfbookmarkmodel.cpp new file mode 100644 index 000000000..c9c365568 --- /dev/null +++ b/src/pdf/qpdfbookmarkmodel.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpdfbookmarkmodel.h" + +#include "qpdfdocument.h" +#include "qpdfdocument_p.h" + +#include "third_party/pdfium/public/fpdf_doc.h" +#include "third_party/pdfium/public/fpdfview.h" + +#include <QPointer> +#include <QScopedPointer> +#include <private/qabstractitemmodel_p.h> + +QT_BEGIN_NAMESPACE + +class BookmarkNode +{ +public: + explicit BookmarkNode(BookmarkNode *parentNode = nullptr) + : m_parentNode(parentNode) + , m_level(0) + , m_pageNumber(0) + { + } + + ~BookmarkNode() + { + clear(); + } + + void clear() + { + qDeleteAll(m_childNodes); + m_childNodes.clear(); + } + + void appendChild(BookmarkNode *child) + { + m_childNodes.append(child); + } + + BookmarkNode *child(int row) const + { + return m_childNodes.at(row); + } + + int childCount() const + { + return m_childNodes.count(); + } + + int row() const + { + if (m_parentNode) + return m_parentNode->m_childNodes.indexOf(const_cast<BookmarkNode*>(this)); + + return 0; + } + + BookmarkNode *parentNode() const + { + return m_parentNode; + } + + QString title() const + { + return m_title; + } + + void setTitle(const QString &title) + { + m_title = title; + } + + int level() const + { + return m_level; + } + + void setLevel(int level) + { + m_level = level; + } + + int pageNumber() const + { + return m_pageNumber; + } + + void setPageNumber(int pageNumber) + { + m_pageNumber = pageNumber; + } + +private: + QVector<BookmarkNode*> m_childNodes; + BookmarkNode *m_parentNode; + + QString m_title; + int m_level; + int m_pageNumber; +}; + + +class QPdfBookmarkModelPrivate : public QAbstractItemModelPrivate +{ +public: + QPdfBookmarkModelPrivate() + : QAbstractItemModelPrivate() + , m_rootNode(new BookmarkNode(nullptr)) + , m_document(nullptr) + , m_structureMode(QPdfBookmarkModel::TreeMode) + { + } + + void rebuild() + { + Q_Q(QPdfBookmarkModel); + + const bool documentAvailable = (m_document && m_document->status() == QPdfDocument::Ready); + + if (documentAvailable) { + q->beginResetModel(); + m_rootNode->clear(); + QPdfMutexLocker lock; + appendChildNode(m_rootNode.data(), nullptr, 0, m_document->d->doc); + lock.unlock(); + q->endResetModel(); + } else { + if (m_rootNode->childCount() == 0) { + return; + } else { + q->beginResetModel(); + m_rootNode->clear(); + q->endResetModel(); + } + } + } + + void appendChildNode(BookmarkNode *parentBookmarkNode, FPDF_BOOKMARK parentBookmark, int level, FPDF_DOCUMENT document) + { + FPDF_BOOKMARK bookmark = FPDFBookmark_GetFirstChild(document, parentBookmark); + + while (bookmark) { + BookmarkNode *childBookmarkNode = nullptr; + + if (m_structureMode == QPdfBookmarkModel::TreeMode) { + childBookmarkNode = new BookmarkNode(parentBookmarkNode); + parentBookmarkNode->appendChild(childBookmarkNode); + } else if (m_structureMode == QPdfBookmarkModel::ListMode) { + childBookmarkNode = new BookmarkNode(m_rootNode.data()); + m_rootNode->appendChild(childBookmarkNode); + } + + const int titleLength = int(FPDFBookmark_GetTitle(bookmark, nullptr, 0)); + + QVector<ushort> titleBuffer(titleLength); + FPDFBookmark_GetTitle(bookmark, titleBuffer.data(), quint32(titleBuffer.length())); + + const FPDF_DEST dest = FPDFBookmark_GetDest(document, bookmark); + const int pageNumber = FPDFDest_GetDestPageIndex(document, dest); + + childBookmarkNode->setTitle(QString::fromUtf16(titleBuffer.data())); + childBookmarkNode->setLevel(level); + childBookmarkNode->setPageNumber(pageNumber); + + // recurse down + appendChildNode(childBookmarkNode, bookmark, level + 1, document); + + bookmark = FPDFBookmark_GetNextSibling(document, bookmark); + } + } + + void _q_documentStatusChanged() + { + rebuild(); + } + + Q_DECLARE_PUBLIC(QPdfBookmarkModel) + + QScopedPointer<BookmarkNode> m_rootNode; + QPointer<QPdfDocument> m_document; + QPdfBookmarkModel::StructureMode m_structureMode; +}; + + +QPdfBookmarkModel::QPdfBookmarkModel(QObject *parent) + : QAbstractItemModel(*new QPdfBookmarkModelPrivate, parent) +{ +} + +QPdfDocument* QPdfBookmarkModel::document() const +{ + Q_D(const QPdfBookmarkModel); + + return d->m_document; +} + +void QPdfBookmarkModel::setDocument(QPdfDocument *document) +{ + Q_D(QPdfBookmarkModel); + + if (d->m_document == document) + return; + + if (d->m_document) + disconnect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged())); + + d->m_document = document; + emit documentChanged(d->m_document); + + if (d->m_document) + connect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged())); + + d->rebuild(); +} + +QPdfBookmarkModel::StructureMode QPdfBookmarkModel::structureMode() const +{ + Q_D(const QPdfBookmarkModel); + + return d->m_structureMode; +} + +void QPdfBookmarkModel::setStructureMode(StructureMode mode) +{ + Q_D(QPdfBookmarkModel); + + if (d->m_structureMode == mode) + return; + + d->m_structureMode = mode; + emit structureModeChanged(d->m_structureMode); + + d->rebuild(); +} + +int QPdfBookmarkModel::columnCount(const QModelIndex &parent) const +{ + return 1; +} + +QHash<int, QByteArray> QPdfBookmarkModel::roleNames() const +{ + QHash<int, QByteArray> names; + + names[TitleRole] = "title"; + names[LevelRole] = "level"; + names[PageNumberRole] = "pageNumber"; + + return names; +} + +QVariant QPdfBookmarkModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + const BookmarkNode *node = static_cast<BookmarkNode*>(index.internalPointer()); + switch (role) { + case TitleRole: + return node->title(); + case LevelRole: + return node->level(); + case PageNumberRole: + return node->pageNumber(); + default: + return QVariant(); + } +} + +QModelIndex QPdfBookmarkModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_D(const QPdfBookmarkModel); + + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + BookmarkNode *parentNode; + + if (!parent.isValid()) + parentNode = d->m_rootNode.data(); + else + parentNode = static_cast<BookmarkNode*>(parent.internalPointer()); + + BookmarkNode *childNode = parentNode->child(row); + if (childNode) + return createIndex(row, column, childNode); + else + return QModelIndex(); +} + +QModelIndex QPdfBookmarkModel::parent(const QModelIndex &index) const +{ + Q_D(const QPdfBookmarkModel); + + if (!index.isValid()) + return QModelIndex(); + + const BookmarkNode *childNode = static_cast<BookmarkNode*>(index.internalPointer()); + BookmarkNode *parentNode = childNode->parentNode(); + + if (parentNode == d->m_rootNode.data()) + return QModelIndex(); + + return createIndex(parentNode->row(), 0, parentNode); +} + +int QPdfBookmarkModel::rowCount(const QModelIndex &parent) const +{ + Q_D(const QPdfBookmarkModel); + + if (parent.column() > 0) + return 0; + + BookmarkNode *parentNode = nullptr; + + if (!parent.isValid()) + parentNode = d->m_rootNode.data(); + else + parentNode = static_cast<BookmarkNode*>(parent.internalPointer()); + + return parentNode->childCount(); +} + +QT_END_NAMESPACE + +#include "moc_qpdfbookmarkmodel.cpp" |