1/*
2 * Copyright (C) 2008, Pino Toscano <pino@kde.org>
3 * Copyright (C) 2018, Adam Reichold <adam.reichold@t-online.de>
4 * Copyright (C) 2019, Albert Astals Cid <aacid@kde.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#include "toc.h"
22
23#include <poppler-qt6.h>
24
25#include <QtGui/QStandardItemModel>
26#include <QtWidgets/QHeaderView>
27#include <QtWidgets/QTreeWidget>
28
29#include <QDebug>
30
31struct Node
32{
33 Node(Poppler::OutlineItem &&item, int row, Node *parent) : m_row(row), m_parent(parent), m_item(std::move(item)) { }
34
35 ~Node() { qDeleteAll(c: m_children); }
36
37 Node(const Node &) = delete;
38 Node &operator=(const Node &) = delete;
39
40 int m_row;
41 Node *m_parent;
42 Poppler::OutlineItem m_item;
43 QVector<Node *> m_children;
44};
45
46class TocModel : public QAbstractItemModel
47{
48 Q_OBJECT
49public:
50 TocModel(QVector<Poppler::OutlineItem> &&items, QObject *parent) : QAbstractItemModel(parent)
51 {
52 for (int i = 0; i < items.count(); ++i) {
53 m_topItems << new Node(std::move(items[i]), i, nullptr);
54 }
55 }
56
57 ~TocModel() override { qDeleteAll(c: m_topItems); }
58
59 QVariant data(const QModelIndex &index, int role) const override
60 {
61 if (role != Qt::DisplayRole) {
62 return {};
63 }
64
65 Node *n = static_cast<Node *>(index.internalPointer());
66 return n->m_item.name();
67 }
68
69 QModelIndex index(int row, int column, const QModelIndex &parent) const override
70 {
71 Node *p = static_cast<Node *>(parent.internalPointer());
72 const QVector<Node *> &children = p ? p->m_children : m_topItems;
73
74 return createIndex(arow: row, acolumn: column, adata: children[row]);
75 }
76
77 QModelIndex parent(const QModelIndex &child) const override
78 {
79 Node *n = static_cast<Node *>(child.internalPointer());
80 if (n->m_parent == nullptr) {
81 return QModelIndex();
82 } else {
83 return createIndex(arow: n->m_parent->m_row, acolumn: 0, adata: n->m_parent);
84 }
85 }
86
87 int rowCount(const QModelIndex &parent) const override
88 {
89 Node *n = static_cast<Node *>(parent.internalPointer());
90 if (!n) {
91 return m_topItems.count();
92 }
93
94 if (n->m_children.isEmpty() && !n->m_item.isNull()) {
95 QVector<Poppler::OutlineItem> items = n->m_item.children();
96 for (int i = 0; i < items.count(); ++i) {
97 n->m_children << new Node(std::move(items[i]), i, n);
98 }
99 }
100
101 return n->m_children.count();
102 }
103
104 bool hasChildren(const QModelIndex &parent) const override
105 {
106 Node *n = static_cast<Node *>(parent.internalPointer());
107 if (!n) {
108 return true;
109 }
110
111 return n->m_item.hasChildren();
112 }
113
114 int columnCount(const QModelIndex &parent) const override { return 1; }
115
116private:
117 QVector<Node *> m_topItems;
118};
119
120TocDock::TocDock(QWidget *parent) : AbstractInfoDock(parent)
121{
122 m_tree = new QTreeView(this);
123 setWidget(m_tree);
124 m_tree->setAlternatingRowColors(true);
125 m_tree->header()->hide();
126 setWindowTitle(tr(s: "TOC"));
127 m_tree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
128}
129
130TocDock::~TocDock() { }
131
132void TocDock::expandItemModels(const QModelIndex &parent)
133{
134 TocModel *model = static_cast<TocModel *>(m_tree->model());
135 for (int i = 0; i < model->rowCount(parent); ++i) {
136 QModelIndex index = model->index(row: i, column: 0, parent);
137 Node *n = static_cast<Node *>(index.internalPointer());
138 if (n->m_item.isOpen()) {
139 m_tree->setExpanded(index, expand: true);
140 expandItemModels(parent: index);
141 }
142 }
143}
144
145void TocDock::fillInfo()
146{
147 auto outline = document()->outline();
148 if (!outline.isEmpty()) {
149 TocModel *model = new TocModel(std::move(outline), this);
150 m_tree->setModel(model);
151
152 expandItemModels(parent: QModelIndex());
153 } else {
154 QStandardItemModel *model = new QStandardItemModel(this);
155 QStandardItem *item = new QStandardItem(tr(s: "No TOC"));
156 item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
157 model->appendRow(aitem: item);
158 m_tree->setModel(model);
159 }
160}
161
162void TocDock::documentClosed()
163{
164 m_tree->setModel(nullptr);
165 AbstractInfoDock::documentClosed();
166}
167
168#include "toc.moc"
169

source code of poppler/qt6/demos/toc.cpp