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-qt5.h>
24 
25 #include <QtGui/QStandardItemModel>
26 #include <QtWidgets/QHeaderView>
27 #include <QtWidgets/QTreeWidget>
28 
29 #include <QDebug>
30 
31 struct Node
32 {
NodeNode33     Node(Poppler::OutlineItem &&item, int row, Node *parent) : m_row(row), m_parent(parent), m_item(std::move(item)) { }
34 
~NodeNode35     ~Node() { qDeleteAll(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:
TocModel(QVector<Poppler::OutlineItem> && items,QObject * parent)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 
~TocModel()57     ~TocModel() override { qDeleteAll(m_topItems); }
58 
data(const QModelIndex & index,int role) const59     QVariant data(const QModelIndex &index, int role) const override
60     {
61         if (role != Qt::DisplayRole)
62             return {};
63 
64         Node *n = static_cast<Node *>(index.internalPointer());
65         return n->m_item.name();
66     }
67 
index(int row,int column,const QModelIndex & parent) const68     QModelIndex index(int row, int column, const QModelIndex &parent) const override
69     {
70         Node *p = static_cast<Node *>(parent.internalPointer());
71         const QVector<Node *> &children = p ? p->m_children : m_topItems;
72 
73         return createIndex(row, column, children[row]);
74     }
75 
parent(const QModelIndex & child) const76     QModelIndex parent(const QModelIndex &child) const override
77     {
78         Node *n = static_cast<Node *>(child.internalPointer());
79         if (n->m_parent == nullptr)
80             return QModelIndex();
81         else
82             return createIndex(n->m_parent->m_row, 0, n->m_parent);
83     }
84 
rowCount(const QModelIndex & parent) const85     int rowCount(const QModelIndex &parent) const override
86     {
87         Node *n = static_cast<Node *>(parent.internalPointer());
88         if (!n) {
89             return m_topItems.count();
90         }
91 
92         if (n->m_children.isEmpty() && !n->m_item.isNull()) {
93             QVector<Poppler::OutlineItem> items = n->m_item.children();
94             for (int i = 0; i < items.count(); ++i) {
95                 n->m_children << new Node(std::move(items[i]), i, n);
96             }
97         }
98 
99         return n->m_children.count();
100     }
101 
hasChildren(const QModelIndex & parent) const102     bool hasChildren(const QModelIndex &parent) const override
103     {
104         Node *n = static_cast<Node *>(parent.internalPointer());
105         if (!n)
106             return true;
107 
108         return n->m_item.hasChildren();
109     }
110 
columnCount(const QModelIndex & parent) const111     int columnCount(const QModelIndex &parent) const override { return 1; }
112 
113 private:
114     QVector<Node *> m_topItems;
115 };
116 
TocDock(QWidget * parent)117 TocDock::TocDock(QWidget *parent) : AbstractInfoDock(parent)
118 {
119     m_tree = new QTreeView(this);
120     setWidget(m_tree);
121     m_tree->setAlternatingRowColors(true);
122     m_tree->header()->hide();
123     setWindowTitle(tr("TOC"));
124     m_tree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
125 }
126 
~TocDock()127 TocDock::~TocDock() { }
128 
expandItemModels(const QModelIndex & parent)129 void TocDock::expandItemModels(const QModelIndex &parent)
130 {
131     TocModel *model = static_cast<TocModel *>(m_tree->model());
132     for (int i = 0; i < model->rowCount(parent); ++i) {
133         QModelIndex index = model->index(i, 0, parent);
134         Node *n = static_cast<Node *>(index.internalPointer());
135         if (n->m_item.isOpen()) {
136             m_tree->setExpanded(index, true);
137             expandItemModels(index);
138         }
139     }
140 }
141 
fillInfo()142 void TocDock::fillInfo()
143 {
144     auto outline = document()->outline();
145     if (!outline.isEmpty()) {
146         TocModel *model = new TocModel(std::move(outline), this);
147         m_tree->setModel(model);
148 
149         expandItemModels(QModelIndex());
150     } else {
151         QStandardItemModel *model = new QStandardItemModel(this);
152         QStandardItem *item = new QStandardItem(tr("No TOC"));
153         item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
154         model->appendRow(item);
155         m_tree->setModel(model);
156     }
157 }
158 
documentClosed()159 void TocDock::documentClosed()
160 {
161     m_tree->setModel(nullptr);
162     AbstractInfoDock::documentClosed();
163 }
164 
165 #include "toc.moc"
166