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 | |
31 | struct 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 | |
46 | class TocModel : public QAbstractItemModel |
47 | { |
48 | Q_OBJECT |
49 | public: |
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 | |
116 | private: |
117 | QVector<Node *> m_topItems; |
118 | }; |
119 | |
120 | TocDock::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 | |
130 | TocDock::~TocDock() { } |
131 | |
132 | void 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 | |
145 | void 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 | |
162 | void TocDock::documentClosed() |
163 | { |
164 | m_tree->setModel(nullptr); |
165 | AbstractInfoDock::documentClosed(); |
166 | } |
167 | |
168 | #include "toc.moc" |
169 | |