]> git.lyx.org Git - lyx.git/blob - src/TocBackend.cpp
Update tex2lyx tests.
[lyx.git] / src / TocBackend.cpp
1 /**
2  * \file TocBackend.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jean-Marc Lasgouttes
7  * \author Angus Leeming
8  * \author Abdelrazak Younes
9  * \author Guillaume Munch
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "TocBackend.h"
17
18 #include "Buffer.h"
19 #include "BufferParams.h"
20 #include "InsetList.h"
21 #include "Paragraph.h"
22 #include "TextClass.h"
23
24 #include "insets/InsetText.h"
25
26 #include "support/debug.h"
27 #include "support/docstream.h"
28 #include "support/gettext.h"
29 #include "support/lassert.h"
30 #include "support/lstrings.h"
31
32 using namespace std;
33
34
35 namespace lyx {
36
37
38 ///////////////////////////////////////////////////////////////////////////
39 //
40 // TocItem implementation
41 //
42 ///////////////////////////////////////////////////////////////////////////
43
44 TocItem::TocItem(DocIterator const & dit, int d, docstring const & s,
45                  bool output_active, FuncRequest action)
46         : dit_(dit), depth_(d), str_(s), output_(output_active),
47           action_(action)
48 {
49 }
50
51
52 int TocItem::id() const
53 {
54         return dit_.paragraph().id();
55 }
56
57
58 docstring const TocItem::asString() const
59 {
60         static char_type const cross = 0x2716; // ✖ U+2716 HEAVY MULTIPLICATION X
61         static char_type const thin = 0x2009; // U+2009 THIN SPACE
62         docstring prefix;
63         if (!output_) {
64                 prefix += cross;
65                 prefix += thin;
66         }
67         return prefix + str_;
68 }
69
70
71 FuncRequest TocItem::action() const
72 {
73         if (action_.action() == LFUN_UNKNOWN_ACTION) {
74                 return FuncRequest(LFUN_PARAGRAPH_GOTO, dit_.paragraphGotoArgument());
75         } else
76                 return action_;
77 }
78
79
80 ///////////////////////////////////////////////////////////////////////////
81 //
82 // TocBackend implementation
83 //
84 ///////////////////////////////////////////////////////////////////////////
85
86 Toc::const_iterator TocBackend::findItem(Toc const & toc,
87                                          DocIterator const & dit)
88 {
89         Toc::const_iterator last = toc.begin();
90         Toc::const_iterator it = toc.end();
91         if (it == last)
92                 return it;
93         --it;
94         DocIterator dit_text = dit.getInnerText();
95
96         for (; it != last; --it) {
97                 // We verify that we don't compare contents of two
98                 // different document. This happens when you
99                 // have parent and child documents.
100                 if (&it->dit()[0].inset() != &dit_text[0].inset())
101                         continue;
102                 if (it->dit() <= dit_text)
103                         return it;
104         }
105
106         // We are before the first Toc Item:
107         return last;
108 }
109
110
111 Toc::iterator TocBackend::findItem(Toc & toc, int depth, docstring const & str)
112 {
113         if (toc.empty())
114                 return toc.end();
115         Toc::iterator it = toc.begin();
116         Toc::iterator itend = toc.end();
117         for (; it != itend; ++it) {
118                 if (it->depth() == depth && it->str() == str)
119                         break;
120         }
121         return it;
122 }
123
124
125 shared_ptr<Toc const> TocBackend::toc(string const & type) const
126 {
127         // Is the type already supported?
128         TocList::const_iterator it = tocs_.find(type);
129         LASSERT(it != tocs_.end(), { return make_shared<Toc>(); });
130         return it->second;
131 }
132
133
134 shared_ptr<Toc> TocBackend::toc(string const & type)
135 {
136         // std::map::insert only really performs the insertion if the key is not
137         // already bound, and otherwise returns an iterator to the element already
138         // there, see manual.
139         return tocs_.insert({type, make_shared<Toc>()}).first->second;
140 }
141
142
143 TocBuilder & TocBackend::builder(string const & type)
144 {
145         auto p = make_unique<TocBuilder>(toc(type));
146         return * builders_.insert(make_pair(type, move(p))).first->second;
147 }
148
149
150 // FIXME: This function duplicates functionality from InsetText::iterateForToc.
151 // Both have their own way of computing the TocItem for "tableofcontents". The
152 // TocItem creation and update should be made in a dedicated function and
153 // updateItem should be rewritten to uniformly update the matching items from
154 // all TOCs.
155 bool TocBackend::updateItem(DocIterator const & dit_in)
156 {
157         // we need a text
158         DocIterator dit = dit_in.getInnerText();
159
160         if (dit.text()->getTocLevel(dit.pit()) == Layout::NOT_IN_TOC)
161                 return false;
162
163         if (toc("tableofcontents")->empty()) {
164                 // FIXME: should not happen, 
165                 // a call to TocBackend::update() is missing somewhere
166                 LYXERR0("TocBackend::updateItem called but the TOC is empty!");
167                 return false;
168         }
169
170         BufferParams const & bufparams = buffer_->params();
171         const int min_toclevel = bufparams.documentClass().min_toclevel();
172
173         Toc::const_iterator toc_item = item("tableofcontents", dit);
174
175         docstring tocstring;
176
177         // For each paragraph, traverse its insets and let them add
178         // their toc items
179         //
180         // FIXME: This is supposed to accomplish the same as the body of
181         // InsetText::iterateForToc(), probably
182         Paragraph & par = toc_item->dit().paragraph();
183         InsetList::const_iterator it = par.insetList().begin();
184         InsetList::const_iterator end = par.insetList().end();
185         for (; it != end; ++it) {
186                 Inset & inset = *it->inset;
187                 if (inset.lyxCode() == ARG_CODE) {
188                         tocstring = par.labelString();
189                         if (!tocstring.empty())
190                                 tocstring += ' ';
191                         inset.asInsetText()->text().forOutliner(tocstring,TOC_ENTRY_LENGTH);
192                         break;
193                 }
194         }
195
196         int const toclevel = toc_item->dit().text()->
197                 getTocLevel(toc_item->dit().pit());
198         if (toclevel != Layout::NOT_IN_TOC && toclevel >= min_toclevel
199                 && tocstring.empty())
200                 par.forOutliner(tocstring, TOC_ENTRY_LENGTH);
201
202         support::truncateWithEllipsis(tocstring, TOC_ENTRY_LENGTH);
203         const_cast<TocItem &>(*toc_item).str(tocstring);
204
205         buffer_->updateTocItem("tableofcontents", dit);
206         return true;
207 }
208
209
210 void TocBackend::update(bool output_active, UpdateType utype)
211 {
212         for (TocList::iterator it = tocs_.begin(); it != tocs_.end(); ++it)
213                 it->second->clear();
214         tocs_.clear();
215         builders_.clear();
216         if (!buffer_->isInternal()) {
217                 DocIterator dit;
218                 buffer_->inset().addToToc(dit, output_active, utype);
219         }
220 }
221
222
223 Toc::const_iterator TocBackend::item(string const & type,
224                                      DocIterator const & dit) const
225 {
226         TocList::const_iterator toclist_it = tocs_.find(type);
227         // Is the type supported?
228         // We will try to make the best of it in release mode
229         LASSERT(toclist_it != tocs_.end(), toclist_it = tocs_.begin());
230         return findItem(*toclist_it->second, dit);
231 }
232
233
234 void TocBackend::writePlaintextTocList(string const & type,
235         odocstringstream & os, size_t max_length) const
236 {
237         TocList::const_iterator cit = tocs_.find(type);
238         if (cit != tocs_.end()) {
239                 Toc::const_iterator ccit = cit->second->begin();
240                 Toc::const_iterator end = cit->second->end();
241                 for (; ccit != end; ++ccit) {
242                         os << ccit->asString() << from_utf8("\n");
243                         if (os.str().size() > max_length)
244                                 break;
245                 }
246         }
247 }
248
249
250 docstring TocBackend::outlinerName(string const & type) const
251 {
252         return translateIfPossible(
253             buffer_->params().documentClass().outlinerName(type));
254 }
255
256
257 bool TocBackend::isOther(std::string const & type)
258 {
259         return type == "graphics"
260                 || type == "note"
261                 || type == "branch"
262                 || type == "change"
263                 || type == "label"
264                 || type == "citation"
265                 || type == "equation"
266                 || type == "footnote"
267                 || type == "marginalnote"
268                 || type == "nomencl"
269                 || type == "listings"
270                 || type == "math-macro"
271                 || type == "external"
272                 || type == "senseless"
273                 || type == "index"
274                 || type.substr(0,6) == "index:";
275 }
276
277
278 } // namespace lyx