]> git.lyx.org Git - lyx.git/blob - src/Counters.cpp
Refactor InsetQuotes.h enums
[lyx.git] / src / Counters.cpp
1 /**
2  * \file Counters.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Martin Vermeer
8  * \author André Pönitz
9  * \author Richard Heck (roman numerals)
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "Counters.h"
17 #include "Layout.h"
18 #include "Lexer.h"
19
20 #include "support/convert.h"
21 #include "support/counter_reps.h"
22 #include "support/debug.h"
23 #include "support/docstring.h"
24 #include "support/gettext.h"
25 #include "support/lassert.h"
26 #include "support/lstrings.h"
27
28 #include <algorithm>
29 #include <sstream>
30
31 using namespace std;
32 using namespace lyx::support;
33
34 namespace lyx {
35
36
37 Counter::Counter()
38         : initial_value_(0), saved_value_(0)
39 {
40         reset();
41 }
42
43
44 Counter::Counter(docstring const & mc, docstring const & ls,
45                 docstring const & lsa, docstring const & guiname)
46         : initial_value_(0), saved_value_(0), parent_(mc), labelstring_(ls),
47           labelstringappendix_(lsa), guiname_(guiname)
48 {
49         reset();
50 }
51
52
53 bool Counter::read(Lexer & lex)
54 {
55         enum {
56                 CT_WITHIN = 1,
57                 CT_LABELSTRING,
58                 CT_LABELSTRING_APPENDIX,
59                 CT_PRETTYFORMAT,
60                 CT_INITIALVALUE,
61                 CT_GUINAME,
62                 CT_END
63         };
64
65         LexerKeyword counterTags[] = {
66                 { "end", CT_END },
67                 { "guiname", CT_GUINAME },
68                 { "initialvalue", CT_INITIALVALUE},
69                 { "labelstring", CT_LABELSTRING },
70                 { "labelstringappendix", CT_LABELSTRING_APPENDIX },
71                 { "prettyformat", CT_PRETTYFORMAT },
72                 { "within", CT_WITHIN }
73         };
74
75         lex.pushTable(counterTags);
76
77         bool getout = false;
78         while (!getout && lex.isOK()) {
79                 int le = lex.lex();
80                 switch (le) {
81                         case Lexer::LEX_UNDEF:
82                                 lex.printError("Unknown counter tag `$$Token'");
83                                 continue;
84                         default:
85                                 break;
86                 }
87                 switch (le) {
88                         case CT_WITHIN:
89                                 lex.next();
90                                 parent_ = lex.getDocString();
91                                 if (parent_ == "none")
92                                         parent_.erase();
93                                 break;
94                         case CT_INITIALVALUE:
95                                 lex.next();
96                                 initial_value_ = lex.getInteger();
97                                 // getInteger() returns -1 on error, and larger
98                                 // negative values do not make much sense.
99                                 // In the other case, we subtract one, since the
100                                 // counter will be incremented before its first use.
101                                 if (initial_value_ <= -1)
102                                         initial_value_ = 0;
103                                 else
104                                         initial_value_ -= 1;
105                                 break;
106                         case CT_PRETTYFORMAT:
107                                 lex.next();
108                                 prettyformat_ = lex.getDocString();
109                                 break;
110                         case CT_LABELSTRING:
111                                 lex.next();
112                                 labelstring_ = lex.getDocString();
113                                 labelstringappendix_ = labelstring_;
114                                 break;
115                         case CT_LABELSTRING_APPENDIX:
116                                 lex.next();
117                                 labelstringappendix_ = lex.getDocString();
118                                 break;
119                         case CT_GUINAME:
120                                 lex.next();
121                                 guiname_ = lex.getDocString();
122                                 break;
123                         case CT_END:
124                                 getout = true;
125                                 break;
126                 }
127         }
128
129         // Here if have a full counter if getout == true
130         if (!getout)
131                 LYXERR0("No End tag found for counter!");
132         lex.popTable();
133         return getout;
134 }
135
136
137 void Counter::set(int v)
138 {
139         value_ = v;
140 }
141
142
143 void Counter::addto(int v)
144 {
145         value_ += v;
146 }
147
148
149 int Counter::value() const
150 {
151         return value_;
152 }
153
154
155 void Counter::saveValue()
156 {
157         saved_value_ = value_;
158 }
159
160
161 void Counter::restoreValue()
162 {
163         value_ = saved_value_;
164 }
165
166
167 void Counter::step()
168 {
169         ++value_;
170 }
171
172
173 void Counter::reset()
174 {
175         value_ = initial_value_;
176 }
177
178
179 docstring const & Counter::parent() const
180 {
181         return parent_;
182 }
183
184
185 bool Counter::checkAndRemoveParent(docstring const & cnt)
186 {
187         if (parent_ != cnt)
188                 return false;
189         parent_ = docstring();
190         return true;
191 }
192
193
194 docstring const & Counter::labelString(bool in_appendix) const
195 {
196         return in_appendix ? labelstringappendix_ : labelstring_;
197 }
198
199
200 Counter::StringMap & Counter::flatLabelStrings(bool in_appendix) const
201 {
202         return in_appendix ? flatlabelstringappendix_ : flatlabelstring_;
203 }
204
205
206 Counters::Counters() : appendix_(false), subfloat_(false), longtable_(false)
207 {
208         layout_stack_.push_back(nullptr);
209         counter_stack_.push_back(from_ascii(""));
210 }
211
212
213 void Counters::newCounter(docstring const & newc,
214                           docstring const & parentc,
215                           docstring const & ls,
216                           docstring const & lsa,
217                           docstring const & guiname)
218 {
219         if (!parentc.empty() && !hasCounter(parentc)) {
220                 lyxerr << "Parent counter does not exist: "
221                            << to_utf8(parentc)
222                        << endl;
223                 return;
224         }
225         counterList_[newc] = Counter(parentc, ls, lsa, guiname);
226 }
227
228
229 bool Counters::hasCounter(docstring const & c) const
230 {
231         return counterList_.find(c) != counterList_.end();
232 }
233
234
235 bool Counters::read(Lexer & lex, docstring const & name, bool makenew)
236 {
237         if (hasCounter(name)) {
238                 LYXERR(Debug::TCLASS, "Reading existing counter " << to_utf8(name));
239                 return counterList_[name].read(lex);
240         }
241
242         LYXERR(Debug::TCLASS, "Reading new counter " << to_utf8(name));
243         Counter cnt;
244         bool success = cnt.read(lex);
245         // if makenew is false, we will just discard what we read
246         if (success && makenew)
247                 counterList_[name] = cnt;
248         else if (!success)
249                 LYXERR0("Error reading counter `" << name << "'!");
250         return success;
251 }
252
253
254 void Counters::set(docstring const & ctr, int const val)
255 {
256         CounterList::iterator const it = counterList_.find(ctr);
257         if (it == counterList_.end()) {
258                 lyxerr << "set: Counter does not exist: "
259                        << to_utf8(ctr) << endl;
260                 return;
261         }
262         it->second.set(val);
263 }
264
265
266 void Counters::addto(docstring const & ctr, int const val)
267 {
268         CounterList::iterator const it = counterList_.find(ctr);
269         if (it == counterList_.end()) {
270                 lyxerr << "addto: Counter does not exist: "
271                        << to_utf8(ctr) << endl;
272                 return;
273         }
274         it->second.addto(val);
275 }
276
277
278 int Counters::value(docstring const & ctr) const
279 {
280         CounterList::const_iterator const cit = counterList_.find(ctr);
281         if (cit == counterList_.end()) {
282                 lyxerr << "value: Counter does not exist: "
283                        << to_utf8(ctr) << endl;
284                 return 0;
285         }
286         return cit->second.value();
287 }
288
289
290 void Counters::saveValue(docstring const & ctr) const
291 {
292         CounterList::const_iterator const cit = counterList_.find(ctr);
293         if (cit == counterList_.end()) {
294                 lyxerr << "value: Counter does not exist: "
295                        << to_utf8(ctr) << endl;
296                 return;
297         }
298         Counter const & cnt = cit->second;
299         Counter & ccnt = const_cast<Counter &>(cnt);
300         ccnt.saveValue();
301 }
302
303
304 void Counters::restoreValue(docstring const & ctr) const
305 {
306         CounterList::const_iterator const cit = counterList_.find(ctr);
307         if (cit == counterList_.end()) {
308                 lyxerr << "value: Counter does not exist: "
309                        << to_utf8(ctr) << endl;
310                 return;
311         }
312         Counter const & cnt = cit->second;
313         Counter & ccnt = const_cast<Counter &>(cnt);
314         ccnt.restoreValue();
315 }
316
317
318 void Counters::resetChildren(docstring const & count)
319 {
320         for (auto & ctr : counterList_) {
321                 if (ctr.second.parent() == count) {
322                         ctr.second.reset();
323                         resetChildren(ctr.first);
324                 }
325         }
326 }
327
328
329 void Counters::stepParent(docstring const & ctr, UpdateType utype)
330 {
331         CounterList::iterator it = counterList_.find(ctr);
332         if (it == counterList_.end()) {
333                 lyxerr << "step: Counter does not exist: "
334                        << to_utf8(ctr) << endl;
335                 return;
336         }
337         step(it->second.parent(), utype);
338 }
339
340
341 void Counters::step(docstring const & ctr, UpdateType utype)
342 {
343         CounterList::iterator it = counterList_.find(ctr);
344         if (it == counterList_.end()) {
345                 lyxerr << "step: Counter does not exist: "
346                        << to_utf8(ctr) << endl;
347                 return;
348         }
349
350         it->second.step();
351         if (utype == OutputUpdate) {
352                 LBUFERR(!counter_stack_.empty());
353                 counter_stack_.pop_back();
354                 counter_stack_.push_back(ctr);
355         }
356
357         resetChildren(ctr);
358 }
359
360
361 docstring const & Counters::guiName(docstring const & cntr) const
362 {
363         CounterList::const_iterator it = counterList_.find(cntr);
364         if (it == counterList_.end()) {
365                 lyxerr << "step: Counter does not exist: "
366                            << to_utf8(cntr) << endl;
367                 return empty_docstring();
368         }
369
370         docstring const & guiname = it->second.guiName();
371         if (guiname.empty())
372                 return cntr;
373         return guiname;
374 }
375
376
377 void Counters::reset()
378 {
379         appendix_ = false;
380         subfloat_ = false;
381         current_float_.erase();
382         for (auto & ctr : counterList_)
383                 ctr.second.reset();
384         counter_stack_.clear();
385         counter_stack_.push_back(from_ascii(""));
386         layout_stack_.clear();
387         layout_stack_.push_back(nullptr);
388 }
389
390
391 void Counters::reset(docstring const & match)
392 {
393         LASSERT(!match.empty(), return);
394
395         for (auto & ctr : counterList_) {
396                 if (ctr.first.find(match) != string::npos)
397                         ctr.second.reset();
398         }
399 }
400
401
402 bool Counters::remove(docstring const & cnt)
403 {
404         bool retval = counterList_.erase(cnt);
405         if (!retval)
406                 return false;
407         for (auto & ctr : counterList_) {
408                 if (ctr.second.checkAndRemoveParent(cnt))
409                         LYXERR(Debug::TCLASS, "Removed parent counter `" +
410                                         to_utf8(cnt) + "' from counter: " + to_utf8(ctr.first));
411         }
412         return retval;
413 }
414
415
416 docstring Counters::labelItem(docstring const & ctr,
417                               docstring const & numbertype) const
418 {
419         CounterList::const_iterator const cit = counterList_.find(ctr);
420         if (cit == counterList_.end()) {
421                 lyxerr << "Counter "
422                        << to_utf8(ctr)
423                        << " does not exist." << endl;
424                 return docstring();
425         }
426
427         int val = cit->second.value();
428
429         if (numbertype == "hebrew")
430                 return docstring(1, hebrewCounter(val));
431
432         if (numbertype == "alph")
433                 return docstring(1, loweralphaCounter(val));
434
435         if (numbertype == "Alph")
436                 return docstring(1, alphaCounter(val));
437
438         if (numbertype == "roman")
439                 return lowerromanCounter(val);
440
441         if (numbertype == "Roman")
442                 return romanCounter(val);
443
444         if (numbertype == "fnsymbol")
445                 return fnsymbolCounter(val);
446
447         return convert<docstring>(val);
448 }
449
450
451 docstring Counters::theCounter(docstring const & counter,
452                                string const & lang) const
453 {
454         CounterList::const_iterator it = counterList_.find(counter);
455         if (it == counterList_.end())
456                 return from_ascii("#");
457         Counter const & ctr = it->second;
458         Counter::StringMap & sm = ctr.flatLabelStrings(appendix());
459         Counter::StringMap::iterator smit = sm.find(lang);
460         if (smit != sm.end())
461                 return counterLabel(smit->second, lang);
462
463         vector<docstring> callers;
464         docstring const & fls = flattenLabelString(counter, appendix(),
465                                                    lang, callers);
466         sm[lang] = fls;
467         return counterLabel(fls, lang);
468 }
469
470
471 docstring Counters::flattenLabelString(docstring const & counter,
472                                        bool in_appendix,
473                                        string const & lang,
474                                        vector<docstring> & callers) const
475 {
476         if (find(callers.begin(), callers.end(), counter) != callers.end()) {
477                 // recursion detected
478                 lyxerr << "Warning: Recursion in label for counter `"
479                        << counter << "' detected"
480                        << endl;
481                 return from_ascii("??");
482         }
483
484         CounterList::const_iterator it = counterList_.find(counter);
485         if (it == counterList_.end())
486                 return from_ascii("#");
487         Counter const & c = it->second;
488
489         docstring ls = translateIfPossible(c.labelString(in_appendix), lang);
490
491         callers.push_back(counter);
492         if (ls.empty()) {
493                 if (!c.parent().empty())
494                         ls = flattenLabelString(c.parent(), in_appendix, lang, callers)
495                                 + from_ascii(".");
496                 callers.pop_back();
497                 return ls + from_ascii("\\arabic{") + counter + "}";
498         }
499
500         while (true) {
501                 //lyxerr << "ls=" << to_utf8(ls) << endl;
502                 size_t const i = ls.find(from_ascii("\\the"), 0);
503                 if (i == docstring::npos)
504                         break;
505                 size_t const j = i + 4;
506                 size_t k = j;
507                 while (k < ls.size() && lowercase(ls[k]) >= 'a'
508                        && lowercase(ls[k]) <= 'z')
509                         ++k;
510                 docstring const newc = ls.substr(j, k - j);
511                 docstring const repl = flattenLabelString(newc, in_appendix,
512                                                           lang, callers);
513                 ls.replace(i, k - j + 4, repl);
514         }
515         callers.pop_back();
516
517         return ls;
518 }
519
520
521 docstring Counters::counterLabel(docstring const & format,
522                                  string const & lang) const
523 {
524         docstring label = format;
525
526         // FIXME: Using regexps would be better, but we compile boost without
527         // wide regexps currently.
528         docstring const the = from_ascii("\\the");
529         while (true) {
530                 //lyxerr << "label=" << label << endl;
531                 size_t const i = label.find(the, 0);
532                 if (i == docstring::npos)
533                         break;
534                 size_t const j = i + 4;
535                 size_t k = j;
536                 while (k < label.size() && lowercase(label[k]) >= 'a'
537                        && lowercase(label[k]) <= 'z')
538                         ++k;
539                 docstring const newc(label, j, k - j);
540                 label.replace(i, k - i, theCounter(newc, lang));
541         }
542         while (true) {
543                 //lyxerr << "label=" << label << endl;
544
545                 size_t const i = label.find('\\', 0);
546                 if (i == docstring::npos)
547                         break;
548                 size_t const j = label.find('{', i + 1);
549                 if (j == docstring::npos)
550                         break;
551                 size_t const k = label.find('}', j + 1);
552                 if (k == docstring::npos)
553                         break;
554                 docstring const numbertype(label, i + 1, j - i - 1);
555                 docstring const counter(label, j + 1, k - j - 1);
556                 label.replace(i, k + 1 - i, labelItem(counter, numbertype));
557         }
558         //lyxerr << "DONE! label=" << label << endl;
559         return label;
560 }
561
562
563 docstring Counters::prettyCounter(docstring const & name,
564                                string const & lang) const
565 {
566         CounterList::const_iterator it = counterList_.find(name);
567         if (it == counterList_.end())
568                 return from_ascii("#");
569         Counter const & ctr = it->second;
570
571         docstring const value = theCounter(name, lang);
572         docstring const & format =
573             translateIfPossible(ctr.prettyFormat(), lang);
574         if (format.empty())
575                 return value;
576         return subst(format, from_ascii("##"), value);
577 }
578
579
580 docstring Counters::currentCounter() const
581 {
582         LBUFERR(!counter_stack_.empty());
583         return counter_stack_.back();
584 }
585
586
587 void Counters::setActiveLayout(Layout const & lay)
588 {
589         LASSERT(!layout_stack_.empty(), return);
590         Layout const * const lastlay = layout_stack_.back();
591         // we want to check whether the layout has changed and, if so,
592         // whether we are coming out of or going into an environment.
593         if (!lastlay) {
594                 layout_stack_.pop_back();
595                 layout_stack_.push_back(&lay);
596                 if (lay.isEnvironment())
597                         beginEnvironment();
598         } else if (lastlay->name() != lay.name()) {
599                 layout_stack_.pop_back();
600                 layout_stack_.push_back(&lay);
601                 if (lastlay->isEnvironment()) {
602                         // we are coming out of an environment
603                         // LYXERR0("Out: " << lastlay->name());
604                         endEnvironment();
605                 }
606                 if (lay.isEnvironment()) {
607                         // we are going into a new environment
608                         // LYXERR0("In: " << lay.name());
609                         beginEnvironment();
610                 }
611         }
612 }
613
614
615 void Counters::beginEnvironment()
616 {
617         counter_stack_.push_back(counter_stack_.back());
618 }
619
620
621 void Counters::endEnvironment()
622 {
623         LASSERT(!counter_stack_.empty(), return);
624         counter_stack_.pop_back();
625 }
626
627
628 vector<docstring> Counters::listOfCounters() const {
629         vector<docstring> ret;
630         for(auto const & k : counterList_)
631                 ret.emplace_back(k.first);
632         return ret;
633 }
634
635
636 } // namespace lyx