1 /*
2 SPDX-FileCopyrightText: 2004-2006 Albert Astals Cid <aacid@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "toc.h"
8
9 // qt/kde includes
10 #include <QContextMenuEvent>
11 #include <QHeaderView>
12 #include <QLayout>
13 #include <QTreeView>
14 #include <qdom.h>
15
16 #include <KLineEdit>
17 #include <KLocalizedString>
18 #include <KTitleWidget>
19
20 #include <kwidgetsaddons_version.h>
21
22 // local includes
23 #include "core/action.h"
24 #include "ktreeviewsearchline.h"
25 #include "pageitemdelegate.h"
26 #include "settings.h"
27 #include "tocmodel.h"
28
TOC(QWidget * parent,Okular::Document * document)29 TOC::TOC(QWidget *parent, Okular::Document *document)
30 : QWidget(parent)
31 , m_document(document)
32 {
33 QVBoxLayout *mainlay = new QVBoxLayout(this);
34 mainlay->setSpacing(6);
35
36 KTitleWidget *titleWidget = new KTitleWidget(this);
37 titleWidget->setLevel(2);
38 titleWidget->setText(i18n("Contents"));
39 mainlay->addWidget(titleWidget);
40 mainlay->setAlignment(titleWidget, Qt::AlignHCenter);
41 m_searchLine = new KTreeViewSearchLine(this);
42 mainlay->addWidget(m_searchLine);
43 m_searchLine->setPlaceholderText(i18n("Search..."));
44 m_searchLine->setCaseSensitivity(Okular::Settings::self()->contentsSearchCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive);
45 m_searchLine->setRegularExpression(Okular::Settings::self()->contentsSearchRegularExpression());
46 connect(m_searchLine, &KTreeViewSearchLine::searchOptionsChanged, this, &TOC::saveSearchOptions);
47
48 m_treeView = new QTreeView(this);
49 mainlay->addWidget(m_treeView);
50 m_model = new TOCModel(document, m_treeView);
51 m_treeView->setModel(m_model);
52 m_treeView->setSortingEnabled(false);
53 m_treeView->setRootIsDecorated(true);
54 m_treeView->setAlternatingRowColors(true);
55 m_treeView->setItemDelegate(new PageItemDelegate(m_treeView));
56 m_treeView->header()->hide();
57 m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
58 connect(m_treeView, &QTreeView::clicked, this, &TOC::slotExecuted);
59 connect(m_treeView, &QTreeView::activated, this, &TOC::slotExecuted);
60 m_searchLine->setTreeView(m_treeView);
61 }
62
~TOC()63 TOC::~TOC()
64 {
65 m_document->removeObserver(this);
66 }
67
notifySetup(const QVector<Okular::Page * > &,int setupFlags)68 void TOC::notifySetup(const QVector<Okular::Page *> & /*pages*/, int setupFlags)
69 {
70 if (!(setupFlags & Okular::DocumentObserver::DocumentChanged))
71 return;
72
73 // clear contents
74 m_model->clear();
75
76 // request synopsis description (is a dom tree)
77 const Okular::DocumentSynopsis *syn = m_document->documentSynopsis();
78 if (!syn) {
79 if (m_document->isOpened()) {
80 // Make sure we clear the reload old model data
81 m_model->setOldModelData(nullptr, QVector<QModelIndex>());
82 }
83 emit hasTOC(false);
84 return;
85 }
86
87 m_model->fill(syn);
88 emit hasTOC(!m_model->isEmpty());
89 }
90
notifyCurrentPageChanged(int,int)91 void TOC::notifyCurrentPageChanged(int, int)
92 {
93 m_model->setCurrentViewport(m_document->viewport());
94 }
95
prepareForReload()96 void TOC::prepareForReload()
97 {
98 if (m_model->isEmpty())
99 return;
100
101 const QVector<QModelIndex> list = expandedNodes();
102 TOCModel *m = m_model;
103 m_model = new TOCModel(m_document, m_treeView);
104 m_model->setOldModelData(m, list);
105 m->setParent(nullptr);
106 }
107
rollbackReload()108 void TOC::rollbackReload()
109 {
110 if (!m_model->hasOldModelData())
111 return;
112
113 TOCModel *m = m_model;
114 m_model = m->clearOldModelData();
115 m_model->setParent(m_treeView);
116 delete m;
117 }
118
finishReload()119 void TOC::finishReload()
120 {
121 m_treeView->setModel(m_model);
122 m_model->setParent(m_treeView);
123 }
124
expandedNodes(const QModelIndex & parent) const125 QVector<QModelIndex> TOC::expandedNodes(const QModelIndex &parent) const
126 {
127 QVector<QModelIndex> list;
128 for (int i = 0; i < m_model->rowCount(parent); i++) {
129 const QModelIndex index = m_model->index(i, 0, parent);
130 if (m_treeView->isExpanded(index)) {
131 list << index;
132 }
133 if (m_model->hasChildren(index)) {
134 list << expandedNodes(index);
135 }
136 }
137 return list;
138 }
139
reparseConfig()140 void TOC::reparseConfig()
141 {
142 m_searchLine->setCaseSensitivity(Okular::Settings::contentsSearchCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive);
143 m_searchLine->setRegularExpression(Okular::Settings::contentsSearchRegularExpression());
144 m_treeView->update();
145 }
146
slotExecuted(const QModelIndex & index)147 void TOC::slotExecuted(const QModelIndex &index)
148 {
149 if (!index.isValid())
150 return;
151
152 QString url = m_model->urlForIndex(index);
153 if (!url.isEmpty()) {
154 Okular::BrowseAction action(QUrl::fromLocalFile(url));
155 m_document->processAction(&action);
156 return;
157 }
158
159 QString externalFileName = m_model->externalFileNameForIndex(index);
160 Okular::DocumentViewport viewport = m_model->viewportForIndex(index);
161 if (!externalFileName.isEmpty()) {
162 Okular::GotoAction action(externalFileName, viewport);
163 m_document->processAction(&action);
164 } else if (viewport.isValid()) {
165 m_document->setViewport(viewport);
166 }
167 }
168
saveSearchOptions()169 void TOC::saveSearchOptions()
170 {
171 Okular::Settings::setContentsSearchRegularExpression(m_searchLine->regularExpression());
172 Okular::Settings::setContentsSearchCaseSensitive(m_searchLine->caseSensitivity() == Qt::CaseSensitive ? true : false);
173 Okular::Settings::self()->save();
174 }
175
contextMenuEvent(QContextMenuEvent * e)176 void TOC::contextMenuEvent(QContextMenuEvent *e)
177 {
178 QModelIndex index = m_treeView->currentIndex();
179 if (!index.isValid())
180 return;
181
182 Okular::DocumentViewport viewport = m_model->viewportForIndex(index);
183
184 emit rightClick(viewport, e->globalPos(), m_model->data(index).toString());
185 }
186
expandRecursively()187 void TOC::expandRecursively()
188 {
189 QList<QModelIndex> worklist = {m_treeView->currentIndex()};
190 if (!worklist[0].isValid()) {
191 return;
192 }
193 while (!worklist.isEmpty()) {
194 QModelIndex index = worklist.takeLast();
195 m_treeView->expand(index);
196 for (int i = 0; i < m_model->rowCount(index); i++) {
197 worklist += m_model->index(i, 0, index);
198 }
199 }
200 }
201
collapseRecursively()202 void TOC::collapseRecursively()
203 {
204 QList<QModelIndex> worklist = {m_treeView->currentIndex()};
205 if (!worklist[0].isValid()) {
206 return;
207 }
208 while (!worklist.isEmpty()) {
209 QModelIndex index = worklist.takeLast();
210 m_treeView->collapse(index);
211 for (int i = 0; i < m_model->rowCount(index); i++) {
212 worklist += m_model->index(i, 0, index);
213 }
214 }
215 }
216
expandAll()217 void TOC::expandAll()
218 {
219 m_treeView->expandAll();
220 }
221
collapseAll()222 void TOC::collapseAll()
223 {
224 m_treeView->collapseAll();
225 }
226