]> git.lyx.org Git - features.git/blob - src/TocBackend.cpp
Better construction of the TOC for floats and captions
[features.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 "FloatList.h"
21 #include "FuncRequest.h"
22 #include "InsetList.h"
23 #include "Layout.h"
24 #include "LyXAction.h"
25 #include "Paragraph.h"
26 #include "ParIterator.h"
27 #include "TextClass.h"
28
29 #include "insets/InsetArgument.h"
30
31 #include "support/convert.h"
32 #include "support/debug.h"
33 #include "support/docstream.h"
34
35 #include "support/lassert.h"
36
37 using namespace std;
38
39
40 namespace lyx {
41
42 ///////////////////////////////////////////////////////////////////////////
43 //
44 // TocItem implementation
45 //
46 ///////////////////////////////////////////////////////////////////////////
47
48 TocItem::TocItem(DocIterator const & dit, int d, docstring const & s,
49         bool output_active, docstring const & t) :
50   dit_(dit), depth_(d), str_(s), tooltip_(t), output_(output_active)
51 {
52 }
53
54
55 int TocItem::id() const
56 {
57         return dit_.paragraph().id();
58 }
59
60
61 docstring const & TocItem::tooltip() const
62 {
63         return tooltip_.empty() ? str_ : tooltip_;
64 }
65
66
67 docstring const TocItem::asString() const
68 {
69         return docstring(4 * depth_, ' ') + str_;
70 }
71
72
73 FuncRequest TocItem::action() const
74 {
75         string const arg = convert<string>(dit_.paragraph().id())
76                 + ' ' + convert<string>(dit_.pos());
77         return FuncRequest(LFUN_PARAGRAPH_GOTO, arg);
78 }
79
80
81 ///////////////////////////////////////////////////////////////////////////
82 //
83 // Toc implementation
84 //
85 ///////////////////////////////////////////////////////////////////////////
86
87 TocIterator Toc::item(DocIterator const & dit) const
88 {
89         TocIterator last = begin();
90         TocIterator it = end();
91         if (it == last)
92                 return it;
93
94         --it;
95
96         DocIterator dit_text = dit;
97         if (dit_text.inMathed()) {
98                 // We are only interested in text so remove the math CursorSlice.
99                 while (dit_text.inMathed())
100                         dit_text.pop_back();
101         }
102
103         for (; it != last; --it) {
104                 // We verify that we don't compare contents of two
105                 // different document. This happens when you
106                 // have parent and child documents.
107                 if (&it->dit_[0].inset() != &dit_text[0].inset())
108                         continue;
109                 if (it->dit_ <= dit_text)
110                         return it;
111         }
112
113         // We are before the first Toc Item:
114         return last;
115 }
116
117
118 Toc::iterator Toc::item(int depth, docstring const & str)
119 {
120         if (empty())
121                 return end();
122         iterator it = begin();
123         iterator itend = end();
124         for (; it != itend; ++it) {
125                 if (it->depth() == depth && it->str() == str)
126                         break;
127         }
128         return it;
129 }
130
131
132 ///////////////////////////////////////////////////////////////////////////
133 //
134 // TocBuilder implementation
135 //
136 ///////////////////////////////////////////////////////////////////////////
137
138 TocBuilder::TocBuilder(shared_ptr<Toc> toc)
139         : toc_(toc ? toc : make_shared<Toc>()),
140           stack_()
141 {
142         LATTEST(toc);
143 }
144
145 void TocBuilder::pushItem(DocIterator const & dit, docstring const & s,
146                                                   bool output_active, bool is_captioned)
147 {
148         toc_->push_back(TocItem(dit, stack_.size(), s, output_active));
149         frame f = {
150                 toc_->size() - 1, //pos
151                 is_captioned, //is_captioned
152         };
153         stack_.push(f);
154 }
155
156 void TocBuilder::captionItem(DocIterator const & dit, docstring const & s,
157                                                          bool output_active)
158 {
159         if (!stack_.empty() && !stack_.top().is_captioned) {
160                 // The float we entered has not yet been assigned a caption.
161                 // Assign the caption string to it.
162                 (*toc_)[stack_.top().pos].str(s);
163                 stack_.top().is_captioned = true;
164         } else {
165                 // This is a new entry.
166                 pop();
167                 pushItem(dit, s, output_active, true);
168         }
169 }
170
171 void TocBuilder::pop()
172 {
173         if (!stack_.empty())
174                 stack_.pop();
175 }
176
177
178
179 ///////////////////////////////////////////////////////////////////////////
180 //
181 // TocBuilderStore implementation
182 //
183 ///////////////////////////////////////////////////////////////////////////
184
185 shared_ptr<TocBuilder> TocBuilderStore::get(string const & type,
186                                                                                         shared_ptr<Toc> toc)
187 {
188         map_t::const_iterator it = map_.find(type);
189         if (it == map_.end()) {
190                 it = map_.insert(std::make_pair(type,
191                                                                                 make_shared<TocBuilder>(toc))).first;
192         }
193         return it->second;
194 }
195
196
197
198 ///////////////////////////////////////////////////////////////////////////
199 //
200 // TocBackend implementation
201 //
202 ///////////////////////////////////////////////////////////////////////////
203
204 shared_ptr<Toc const> TocBackend::toc(string const & type) const
205 {
206         // Is the type already supported?
207         TocList::const_iterator it = tocs_.find(type);
208         LASSERT(it != tocs_.end(), { return make_shared<Toc>(); });
209         return it->second;
210 }
211
212
213 shared_ptr<Toc> TocBackend::toc(string const & type)
214 {
215         TocList::const_iterator it = tocs_.find(type);
216         if (it == tocs_.end()) {
217                 it = tocs_.insert(std::make_pair(type, make_shared<Toc>())).first;
218         }
219         return it->second;
220 }
221
222
223 shared_ptr<TocBuilder> TocBackend::builder(string const & type)
224 {
225         return builders_.get(type, toc(type));
226 }
227
228
229 bool TocBackend::updateItem(DocIterator const & dit)
230 {
231         if (dit.text()->getTocLevel(dit.pit()) == Layout::NOT_IN_TOC)
232                 return false;
233
234         if (toc("tableofcontents")->empty()) {
235                 // FIXME: should not happen, 
236                 // a call to TocBackend::update() is missing somewhere
237                 LYXERR0("TocBackend::updateItem called but the TOC is empty!");
238                 return false;
239         }
240
241         BufferParams const & bufparams = buffer_->params();
242         const int min_toclevel = bufparams.documentClass().min_toclevel();
243
244         TocIterator toc_item = item("tableofcontents", dit);
245
246         docstring tocstring;
247
248         // For each paragraph, traverse its insets and let them add
249         // their toc items
250         Paragraph & par = toc_item->dit_.paragraph();
251         InsetList::const_iterator it = par.insetList().begin();
252         InsetList::const_iterator end = par.insetList().end();
253         for (; it != end; ++it) {
254                 Inset & inset = *it->inset;
255                 if (inset.lyxCode() == ARG_CODE) {
256                         if (!tocstring.empty())
257                                 break;
258                         Paragraph const & inset_par =
259                                 *static_cast<InsetArgument&>(inset).paragraphs().begin();
260                         if (!par.labelString().empty())
261                                 tocstring = par.labelString() + ' ';
262                         tocstring += inset_par.asString(AS_STR_INSETS);
263                         break;
264                 }
265         }
266
267         int const toclevel = toc_item->dit_.text()->getTocLevel(toc_item->dit_.pit());
268         if (toclevel != Layout::NOT_IN_TOC && toclevel >= min_toclevel
269                 && tocstring.empty())
270                         tocstring = par.asString(AS_STR_LABEL | AS_STR_INSETS);
271
272         const_cast<TocItem &>(*toc_item).str_ = tocstring;
273
274         buffer_->updateTocItem("tableofcontents", dit);
275         return true;
276 }
277
278
279 void TocBackend::update(bool output_active)
280 {
281         for (TocList::iterator it = tocs_.begin(); it != tocs_.end(); ++it)
282                 it->second->clear();
283         tocs_.clear();
284         builders_.clear();
285         if (!buffer_->isInternal()) {
286                 DocIterator dit;
287                 buffer_->inset().addToToc(dit, output_active);
288         }
289 }
290
291
292 TocIterator TocBackend::item(string const & type,
293                 DocIterator const & dit) const
294 {
295         TocList::const_iterator toclist_it = tocs_.find(type);
296         // Is the type supported?
297         // We will try to make the best of it in release mode
298         LASSERT(toclist_it != tocs_.end(), toclist_it = tocs_.begin());
299         return toclist_it->second->item(dit);
300 }
301
302
303 void TocBackend::writePlaintextTocList(string const & type,
304         odocstringstream & os, size_t max_length) const
305 {
306         TocList::const_iterator cit = tocs_.find(type);
307         if (cit != tocs_.end()) {
308                 TocIterator ccit = cit->second->begin();
309                 TocIterator end = cit->second->end();
310                 for (; ccit != end; ++ccit) {
311                         os << ccit->asString() << from_utf8("\n");
312                         if (os.str().size() > max_length)
313                                 break;
314                 }
315         }
316 }
317
318
319 } // namespace lyx