3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Martin Vermeer
9 * \author Richard Heck (roman numerals)
11 * Full author contact details are available in file CREDITS.
20 #include "support/convert.h"
21 #include "support/counter_reps.h"
22 #include "support/debug.h"
23 #include "support/gettext.h"
24 #include "support/lassert.h"
25 #include "support/lstrings.h"
31 using namespace lyx::support;
43 Counter::Counter(docstring const & mc, docstring const & ls,
44 docstring const & lsa, docstring const & guiname)
45 : initial_value_(0), master_(mc), labelstring_(ls),
46 labelstringappendix_(lsa), guiname_(guiname)
52 bool Counter::read(Lexer & lex)
57 CT_LABELSTRING_APPENDIX,
64 LexerKeyword counterTags[] = {
66 { "guiname", CT_GUINAME },
67 { "initialvalue", CT_INITIALVALUE},
68 { "labelstring", CT_LABELSTRING },
69 { "labelstringappendix", CT_LABELSTRING_APPENDIX },
70 { "prettyformat", CT_PRETTYFORMAT },
71 { "within", CT_WITHIN }
74 lex.pushTable(counterTags);
77 while (!getout && lex.isOK()) {
80 case Lexer::LEX_UNDEF:
81 lex.printError("Unknown counter tag `$$Token'");
89 master_ = lex.getDocString();
90 if (master_ == "none")
95 initial_value_ = lex.getInteger();
96 // getInteger() returns -1 on error, and larger
97 // negative values do not make much sense.
98 // In the other case, we subtract one, since the
99 // counter will be incremented before its first use.
100 if (initial_value_ <= -1)
105 case CT_PRETTYFORMAT:
107 prettyformat_ = lex.getDocString();
111 labelstring_ = lex.getDocString();
112 labelstringappendix_ = labelstring_;
114 case CT_LABELSTRING_APPENDIX:
116 labelstringappendix_ = lex.getDocString();
120 guiname_ = lex.getDocString();
128 // Here if have a full counter if getout == true
130 LYXERR0("No End tag found for counter!");
136 void Counter::set(int v)
142 void Counter::addto(int v)
148 int Counter::value() const
154 void Counter::saveValue()
156 saved_value_ = value_;
160 void Counter::restoreValue()
162 value_ = saved_value_;
172 void Counter::reset()
174 value_ = initial_value_;
178 docstring const & Counter::master() const
184 bool Counter::checkAndRemoveMaster(docstring const & cnt)
188 master_ = docstring();
193 docstring const & Counter::labelString(bool in_appendix) const
195 return in_appendix ? labelstringappendix_ : labelstring_;
199 Counter::StringMap & Counter::flatLabelStrings(bool in_appendix) const
201 return in_appendix ? flatlabelstringappendix_ : flatlabelstring_;
205 Counters::Counters() : appendix_(false), subfloat_(false), longtable_(false)
207 layout_stack_.push_back(nullptr);
208 counter_stack_.push_back(from_ascii(""));
212 void Counters::newCounter(docstring const & newc,
213 docstring const & masterc,
214 docstring const & ls,
215 docstring const & lsa,
216 docstring const & guiname)
218 if (!masterc.empty() && !hasCounter(masterc)) {
219 lyxerr << "Master counter does not exist: "
224 counterList_[newc] = Counter(masterc, ls, lsa, guiname);
228 bool Counters::hasCounter(docstring const & c) const
230 return counterList_.find(c) != counterList_.end();
234 bool Counters::read(Lexer & lex, docstring const & name, bool makenew)
236 if (hasCounter(name)) {
237 LYXERR(Debug::TCLASS, "Reading existing counter " << to_utf8(name));
238 return counterList_[name].read(lex);
241 LYXERR(Debug::TCLASS, "Reading new counter " << to_utf8(name));
243 bool success = cnt.read(lex);
244 // if makenew is false, we will just discard what we read
245 if (success && makenew)
246 counterList_[name] = cnt;
248 LYXERR0("Error reading counter `" << name << "'!");
253 void Counters::set(docstring const & ctr, int const val)
255 CounterList::iterator const it = counterList_.find(ctr);
256 if (it == counterList_.end()) {
257 lyxerr << "set: Counter does not exist: "
258 << to_utf8(ctr) << endl;
265 void Counters::addto(docstring const & ctr, int const val)
267 CounterList::iterator const it = counterList_.find(ctr);
268 if (it == counterList_.end()) {
269 lyxerr << "addto: Counter does not exist: "
270 << to_utf8(ctr) << endl;
273 it->second.addto(val);
277 int Counters::value(docstring const & ctr) const
279 CounterList::const_iterator const cit = counterList_.find(ctr);
280 if (cit == counterList_.end()) {
281 lyxerr << "value: Counter does not exist: "
282 << to_utf8(ctr) << endl;
285 return cit->second.value();
289 void Counters::saveValue(docstring const & ctr) const
291 CounterList::const_iterator const cit = counterList_.find(ctr);
292 if (cit == counterList_.end()) {
293 lyxerr << "value: Counter does not exist: "
294 << to_utf8(ctr) << endl;
297 Counter const & cnt = cit->second;
298 Counter & ccnt = const_cast<Counter &>(cnt);
303 void Counters::restoreValue(docstring const & ctr) const
305 CounterList::const_iterator const cit = counterList_.find(ctr);
306 if (cit == counterList_.end()) {
307 lyxerr << "value: Counter does not exist: "
308 << to_utf8(ctr) << endl;
311 Counter const & cnt = cit->second;
312 Counter & ccnt = const_cast<Counter &>(cnt);
317 void Counters::resetSlaves(docstring const & count)
319 for (auto & ctr : counterList_) {
320 if (ctr.second.master() == count) {
322 resetSlaves(ctr.first);
328 void Counters::stepMaster(docstring const & ctr, UpdateType utype)
330 CounterList::iterator it = counterList_.find(ctr);
331 if (it == counterList_.end()) {
332 lyxerr << "step: Counter does not exist: "
333 << to_utf8(ctr) << endl;
336 step(it->second.master(), utype);
340 void Counters::step(docstring const & ctr, UpdateType utype)
342 CounterList::iterator it = counterList_.find(ctr);
343 if (it == counterList_.end()) {
344 lyxerr << "step: Counter does not exist: "
345 << to_utf8(ctr) << endl;
350 if (utype == OutputUpdate) {
351 LBUFERR(!counter_stack_.empty());
352 counter_stack_.pop_back();
353 counter_stack_.push_back(ctr);
360 docstring const & Counters::guiName(docstring const & cntr) const
362 CounterList::const_iterator it = counterList_.find(cntr);
363 if (it == counterList_.end()) {
364 lyxerr << "step: Counter does not exist: "
365 << to_utf8(cntr) << endl;
366 return empty_docstring();
369 docstring const & guiname = it->second.guiName();
376 void Counters::reset()
380 current_float_.erase();
381 for (auto & ctr : counterList_)
383 counter_stack_.clear();
384 counter_stack_.push_back(from_ascii(""));
385 layout_stack_.clear();
386 layout_stack_.push_back(nullptr);
390 void Counters::reset(docstring const & match)
392 LASSERT(!match.empty(), return);
394 for (auto & ctr : counterList_) {
395 if (ctr.first.find(match) != string::npos)
401 bool Counters::remove(docstring const & cnt)
403 bool retval = counterList_.erase(cnt);
406 for (auto & ctr : counterList_) {
407 if (ctr.second.checkAndRemoveMaster(cnt))
408 LYXERR(Debug::TCLASS, "Removed master counter `" +
409 to_utf8(cnt) + "' from counter: " + to_utf8(ctr.first));
415 void Counters::copy(Counters & from, Counters & to, docstring const & match)
417 for (auto const & ctr : counterList_) {
418 if (ctr.first.find(match) != string::npos || match == "") {
419 to.set(ctr.first, from.value(ctr.first));
425 docstring Counters::labelItem(docstring const & ctr,
426 docstring const & numbertype) const
428 CounterList::const_iterator const cit = counterList_.find(ctr);
429 if (cit == counterList_.end()) {
432 << " does not exist." << endl;
436 int val = cit->second.value();
438 if (numbertype == "hebrew")
439 return docstring(1, hebrewCounter(val));
441 if (numbertype == "alph")
442 return docstring(1, loweralphaCounter(val));
444 if (numbertype == "Alph")
445 return docstring(1, alphaCounter(val));
447 if (numbertype == "roman")
448 return lowerromanCounter(val);
450 if (numbertype == "Roman")
451 return romanCounter(val);
453 if (numbertype == "fnsymbol")
454 return fnsymbolCounter(val);
456 return convert<docstring>(val);
460 docstring Counters::theCounter(docstring const & counter,
461 string const & lang) const
463 CounterList::const_iterator it = counterList_.find(counter);
464 if (it == counterList_.end())
465 return from_ascii("#");
466 Counter const & ctr = it->second;
467 Counter::StringMap & sm = ctr.flatLabelStrings(appendix());
468 Counter::StringMap::iterator smit = sm.find(lang);
469 if (smit != sm.end())
470 return counterLabel(smit->second, lang);
472 vector<docstring> callers;
473 docstring const & fls = flattenLabelString(counter, appendix(),
476 return counterLabel(fls, lang);
480 docstring Counters::flattenLabelString(docstring const & counter,
483 vector<docstring> & callers) const
485 if (find(callers.begin(), callers.end(), counter) != callers.end()) {
486 // recursion detected
487 lyxerr << "Warning: Recursion in label for counter `"
488 << counter << "' detected"
490 return from_ascii("??");
493 CounterList::const_iterator it = counterList_.find(counter);
494 if (it == counterList_.end())
495 return from_ascii("#");
496 Counter const & c = it->second;
498 docstring ls = translateIfPossible(c.labelString(in_appendix), lang);
500 callers.push_back(counter);
502 if (!c.master().empty())
503 ls = flattenLabelString(c.master(), in_appendix, lang, callers)
506 return ls + from_ascii("\\arabic{") + counter + "}";
510 //lyxerr << "ls=" << to_utf8(ls) << endl;
511 size_t const i = ls.find(from_ascii("\\the"), 0);
512 if (i == docstring::npos)
514 size_t const j = i + 4;
516 while (k < ls.size() && lowercase(ls[k]) >= 'a'
517 && lowercase(ls[k]) <= 'z')
519 docstring const newc = ls.substr(j, k - j);
520 docstring const repl = flattenLabelString(newc, in_appendix,
522 ls.replace(i, k - j + 4, repl);
530 docstring Counters::counterLabel(docstring const & format,
531 string const & lang) const
533 docstring label = format;
535 // FIXME: Using regexps would be better, but we compile boost without
536 // wide regexps currently.
537 docstring const the = from_ascii("\\the");
539 //lyxerr << "label=" << label << endl;
540 size_t const i = label.find(the, 0);
541 if (i == docstring::npos)
543 size_t const j = i + 4;
545 while (k < label.size() && lowercase(label[k]) >= 'a'
546 && lowercase(label[k]) <= 'z')
548 docstring const newc(label, j, k - j);
549 label.replace(i, k - i, theCounter(newc, lang));
552 //lyxerr << "label=" << label << endl;
554 size_t const i = label.find('\\', 0);
555 if (i == docstring::npos)
557 size_t const j = label.find('{', i + 1);
558 if (j == docstring::npos)
560 size_t const k = label.find('}', j + 1);
561 if (k == docstring::npos)
563 docstring const numbertype(label, i + 1, j - i - 1);
564 docstring const counter(label, j + 1, k - j - 1);
565 label.replace(i, k + 1 - i, labelItem(counter, numbertype));
567 //lyxerr << "DONE! label=" << label << endl;
572 docstring Counters::prettyCounter(docstring const & name,
573 string const & lang) const
575 CounterList::const_iterator it = counterList_.find(name);
576 if (it == counterList_.end())
577 return from_ascii("#");
578 Counter const & ctr = it->second;
580 docstring const value = theCounter(name, lang);
581 docstring const & format =
582 translateIfPossible(ctr.prettyFormat(), lang);
585 return subst(format, from_ascii("##"), value);
589 docstring Counters::currentCounter() const
591 LBUFERR(!counter_stack_.empty());
592 return counter_stack_.back();
596 void Counters::setActiveLayout(Layout const & lay)
598 LASSERT(!layout_stack_.empty(), return);
599 Layout const * const lastlay = layout_stack_.back();
600 // we want to check whether the layout has changed and, if so,
601 // whether we are coming out of or going into an environment.
603 layout_stack_.pop_back();
604 layout_stack_.push_back(&lay);
605 if (lay.isEnvironment())
607 } else if (lastlay->name() != lay.name()) {
608 layout_stack_.pop_back();
609 layout_stack_.push_back(&lay);
610 if (lastlay->isEnvironment()) {
611 // we are coming out of an environment
612 // LYXERR0("Out: " << lastlay->name());
615 if (lay.isEnvironment()) {
616 // we are going into a new environment
617 // LYXERR0("In: " << lay.name());
624 void Counters::beginEnvironment()
626 counter_stack_.push_back(counter_stack_.back());
630 void Counters::endEnvironment()
632 LASSERT(!counter_stack_.empty(), return);
633 counter_stack_.pop_back();
637 vector<docstring> Counters::listOfCounters() const {
638 vector<docstring> ret;
639 for(auto const & k : counterList_)
640 ret.emplace_back(k.first);