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 Kimberly 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/docstring.h"
24 #include "support/gettext.h"
25 #include "support/lassert.h"
26 #include "support/lstrings.h"
32 using namespace lyx::support;
38 : initial_value_(0), saved_value_(0)
44 Counter::Counter(docstring const & mc, docstring const & ls,
45 docstring const & lsa, docstring const & prettyformat,
46 docstring const & guiname)
47 : initial_value_(0), saved_value_(0), parent_(mc), labelstring_(ls),
48 labelstringappendix_(lsa), prettyformat_(prettyformat), guiname_(guiname)
54 bool Counter::read(Lexer & lex)
59 CT_LABELSTRING_APPENDIX,
67 LexerKeyword counterTags[] = {
69 { "guiname", CT_GUINAME },
70 { "initialvalue", CT_INITIALVALUE},
71 { "labelstring", CT_LABELSTRING },
72 { "labelstringappendix", CT_LABELSTRING_APPENDIX },
73 { "latexname", CT_LATEXNAME },
74 { "prettyformat", CT_PRETTYFORMAT },
75 { "within", CT_WITHIN }
78 lex.pushTable(counterTags);
81 while (!getout && lex.isOK()) {
84 case Lexer::LEX_UNDEF:
85 lex.printError("Unknown counter tag `$$Token'");
93 parent_ = lex.getDocString();
94 if (parent_ == "none")
99 initial_value_ = lex.getInteger();
100 // getInteger() returns -1 on error, and larger
101 // negative values do not make much sense.
102 // In the other case, we subtract one, since the
103 // counter will be incremented before its first use.
104 if (initial_value_ <= -1)
109 case CT_PRETTYFORMAT:
111 prettyformat_ = lex.getDocString();
115 labelstring_ = lex.getDocString();
116 labelstringappendix_ = labelstring_;
118 case CT_LABELSTRING_APPENDIX:
120 labelstringappendix_ = lex.getDocString();
124 guiname_ = lex.getDocString();
128 latexname_ = lex.getDocString();
134 if (prettyformat_ == "") { // fall back on GuiName if PrettyFormat is empty
136 prettyformat_ = from_ascii("##");
138 prettyformat_ = "## (" + guiname_ + " counter)";
142 // Here if have a full counter if getout == true
144 LYXERR0("No End tag found for counter!");
150 void Counter::set(int v)
156 void Counter::addto(int v)
162 int Counter::value() const
168 void Counter::saveValue()
170 saved_value_ = value_;
174 void Counter::restoreValue()
176 value_ = saved_value_;
186 void Counter::reset()
188 value_ = initial_value_;
192 docstring const & Counter::parent() const
198 bool Counter::checkAndRemoveParent(docstring const & cnt)
202 parent_ = docstring();
207 docstring const & Counter::labelString(bool in_appendix) const
209 return in_appendix ? labelstringappendix_ : labelstring_;
213 Counter::StringMap & Counter::flatLabelStrings(bool in_appendix) const
215 return in_appendix ? flatlabelstringappendix_ : flatlabelstring_;
219 Counters::Counters() : appendix_(false), subfloat_(false), longtable_(false)
221 layout_stack_.push_back(nullptr);
222 counter_stack_.push_back(from_ascii(""));
226 void Counters::newCounter(docstring const & newc,
227 docstring const & parentc,
228 docstring const & ls,
229 docstring const & lsa,
230 docstring const & prettyformat,
231 docstring const & guiname)
233 if (!parentc.empty() && !hasCounter(parentc)) {
234 lyxerr << "Parent counter does not exist: "
239 counterList_[newc] = Counter(parentc, ls, lsa, prettyformat, guiname);
243 bool Counters::hasCounter(docstring const & c) const
245 return counterList_.find(c) != counterList_.end();
249 bool Counters::read(Lexer & lex, docstring const & name, bool makenew)
251 if (hasCounter(name)) {
252 LYXERR(Debug::TCLASS, "Reading existing counter " << to_utf8(name));
253 return counterList_[name].read(lex);
256 LYXERR(Debug::TCLASS, "Reading new counter " << to_utf8(name));
258 bool success = cnt.read(lex);
259 // if makenew is false, we will just discard what we read
260 if (success && makenew)
261 counterList_[name] = cnt;
263 LYXERR0("Error reading counter `" << name << "'!");
268 void Counters::set(docstring const & ctr, int const val)
270 CounterList::iterator const it = counterList_.find(ctr);
271 if (it == counterList_.end()) {
272 lyxerr << "set: Counter does not exist: "
273 << to_utf8(ctr) << endl;
280 void Counters::addto(docstring const & ctr, int const val)
282 CounterList::iterator const it = counterList_.find(ctr);
283 if (it == counterList_.end()) {
284 lyxerr << "addto: Counter does not exist: "
285 << to_utf8(ctr) << endl;
288 it->second.addto(val);
292 int Counters::value(docstring const & ctr) const
294 CounterList::const_iterator const cit = counterList_.find(ctr);
295 if (cit == counterList_.end()) {
296 lyxerr << "value: Counter does not exist: "
297 << to_utf8(ctr) << endl;
300 return cit->second.value();
304 void Counters::saveValue(docstring const & ctr) const
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;
312 Counter const & cnt = cit->second;
313 Counter & ccnt = const_cast<Counter &>(cnt);
318 void Counters::restoreValue(docstring const & ctr) const
320 CounterList::const_iterator const cit = counterList_.find(ctr);
321 if (cit == counterList_.end()) {
322 lyxerr << "value: Counter does not exist: "
323 << to_utf8(ctr) << endl;
326 Counter const & cnt = cit->second;
327 Counter & ccnt = const_cast<Counter &>(cnt);
332 void Counters::resetChildren(docstring const & count)
334 for (auto & ctr : counterList_) {
335 if (ctr.second.parent() == count) {
337 resetChildren(ctr.first);
343 void Counters::stepParent(docstring const & ctr, UpdateType utype)
345 CounterList::iterator it = counterList_.find(ctr);
346 if (it == counterList_.end()) {
347 lyxerr << "step: Counter does not exist: "
348 << to_utf8(ctr) << endl;
351 step(it->second.parent(), utype);
355 void Counters::step(docstring const & ctr, UpdateType /* deleted */)
357 CounterList::iterator it = counterList_.find(ctr);
358 if (it == counterList_.end()) {
359 lyxerr << "step: Counter does not exist: "
360 << to_utf8(ctr) << endl;
365 LBUFERR(!counter_stack_.empty());
366 counter_stack_.pop_back();
367 counter_stack_.push_back(ctr);
373 docstring const & Counters::guiName(docstring const & cntr) const
375 CounterList::const_iterator it = counterList_.find(cntr);
376 if (it == counterList_.end()) {
377 lyxerr << "step: Counter does not exist: "
378 << to_utf8(cntr) << endl;
379 return empty_docstring();
382 docstring const & guiname = it->second.guiName();
389 docstring const & Counters::latexName(docstring const & cntr) const
391 CounterList::const_iterator it = counterList_.find(cntr);
392 if (it == counterList_.end()) {
393 lyxerr << "step: Counter does not exist: "
394 << to_utf8(cntr) << endl;
395 return empty_docstring();
398 docstring const & latexname = it->second.latexName();
399 if (latexname.empty())
405 void Counters::reset()
409 current_float_.erase();
410 for (auto & ctr : counterList_)
412 counter_stack_.clear();
413 counter_stack_.push_back(from_ascii(""));
414 layout_stack_.clear();
415 layout_stack_.push_back(nullptr);
419 void Counters::reset(docstring const & match)
421 LASSERT(!match.empty(), return);
423 for (auto & ctr : counterList_) {
424 if (ctr.first.find(match) != string::npos)
430 bool Counters::copy(docstring const & cnt, docstring const & newcnt)
432 auto const it = counterList_.find(cnt);
433 if (it == counterList_.end())
435 counterList_[newcnt] = it->second;
440 bool Counters::remove(docstring const & cnt)
442 bool retval = counterList_.erase(cnt);
445 for (auto & ctr : counterList_) {
446 if (ctr.second.checkAndRemoveParent(cnt))
447 LYXERR(Debug::TCLASS, "Removed parent counter `" +
448 to_utf8(cnt) + "' from counter: " + to_utf8(ctr.first));
454 docstring Counters::labelItem(docstring const & ctr,
455 docstring const & numbertype) const
457 CounterList::const_iterator const cit = counterList_.find(ctr);
458 if (cit == counterList_.end()) {
461 << " does not exist." << endl;
465 int val = cit->second.value();
467 if (numbertype == "hebrew")
468 return docstring(1, hebrewCounter(val));
470 if (numbertype == "alph")
471 return docstring(1, loweralphaCounter(val));
473 if (numbertype == "Alph")
474 return docstring(1, alphaCounter(val));
476 if (numbertype == "roman")
477 return lowerromanCounter(val);
479 if (numbertype == "Roman")
480 return romanCounter(val);
482 if (numbertype == "fnsymbol")
483 return fnsymbolCounter(val);
485 if (numbertype == "superarabic")
486 return superarabicCounter(val);
488 return convert<docstring>(val);
492 docstring Counters::theCounter(docstring const & counter,
493 string const & lang) const
495 CounterList::const_iterator it = counterList_.find(counter);
496 if (it == counterList_.end())
497 return from_ascii("#");
498 Counter const & ctr = it->second;
499 Counter::StringMap & sm = ctr.flatLabelStrings(appendix());
500 Counter::StringMap::iterator smit = sm.find(lang);
501 if (smit != sm.end())
502 return counterLabel(smit->second, lang);
504 vector<docstring> callers;
505 docstring const & fls = flattenLabelString(counter, appendix(),
508 return counterLabel(fls, lang);
512 docstring Counters::flattenLabelString(docstring const & counter,
515 vector<docstring> & callers) const
517 if (find(callers.begin(), callers.end(), counter) != callers.end()) {
518 // recursion detected
519 lyxerr << "Warning: Recursion in label for counter `"
520 << counter << "' detected"
522 return from_ascii("??");
525 CounterList::const_iterator it = counterList_.find(counter);
526 if (it == counterList_.end())
527 return from_ascii("#");
528 Counter const & c = it->second;
530 docstring ls = translateIfPossible(c.labelString(in_appendix), lang);
532 callers.push_back(counter);
534 if (!c.parent().empty())
535 ls = flattenLabelString(c.parent(), in_appendix, lang, callers)
538 return ls + from_ascii("\\arabic{") + counter + "}";
542 //lyxerr << "ls=" << to_utf8(ls) << endl;
543 size_t const i = ls.find(from_ascii("\\the"), 0);
544 if (i == docstring::npos)
546 size_t const j = i + 4;
548 while (k < ls.size() && lowercase(ls[k]) >= 'a'
549 && lowercase(ls[k]) <= 'z')
551 docstring const newc = ls.substr(j, k - j);
552 docstring const repl = flattenLabelString(newc, in_appendix,
554 ls.replace(i, k - j + 4, repl);
562 docstring Counters::counterLabel(docstring const & format,
563 string const & lang) const
565 docstring label = format;
567 // FIXME: Using regexps would be better, but we compile boost without
568 // wide regexps currently.
569 docstring const the = from_ascii("\\the");
571 //lyxerr << "label=" << label << endl;
572 size_t const i = label.find(the, 0);
573 if (i == docstring::npos)
575 size_t const j = i + 4;
577 while (k < label.size() && lowercase(label[k]) >= 'a'
578 && lowercase(label[k]) <= 'z')
580 docstring const newc(label, j, k - j);
581 label.replace(i, k - i, theCounter(newc, lang));
584 //lyxerr << "label=" << label << endl;
586 size_t const i = label.find('\\', 0);
587 if (i == docstring::npos)
589 size_t const j = label.find('{', i + 1);
590 if (j == docstring::npos)
592 size_t const k = label.find('}', j + 1);
593 if (k == docstring::npos)
595 docstring const numbertype(label, i + 1, j - i - 1);
596 docstring const counter(label, j + 1, k - j - 1);
597 label.replace(i, k + 1 - i, labelItem(counter, numbertype));
599 //lyxerr << "DONE! label=" << label << endl;
604 docstring Counters::prettyCounter(docstring const & name,
605 string const & lang) const
607 CounterList::const_iterator it = counterList_.find(name);
608 if (it == counterList_.end())
609 return from_ascii("#");
610 Counter const & ctr = it->second;
612 docstring const value = theCounter(name, lang);
613 docstring const & format =
614 translateIfPossible(ctr.prettyFormat(), lang);
617 return subst(format, from_ascii("##"), value);
621 docstring Counters::currentCounter() const
623 LBUFERR(!counter_stack_.empty());
624 return counter_stack_.back();
628 void Counters::setActiveLayout(Layout const & lay)
630 LASSERT(!layout_stack_.empty(), return);
631 Layout const * const lastlay = layout_stack_.back();
632 // we want to check whether the layout has changed and, if so,
633 // whether we are coming out of or going into an environment.
635 layout_stack_.pop_back();
636 layout_stack_.push_back(&lay);
637 if (lay.isEnvironment())
639 } else if (lastlay->name() != lay.name()) {
640 layout_stack_.pop_back();
641 layout_stack_.push_back(&lay);
642 if (lastlay->isEnvironment()) {
643 // we are coming out of an environment
644 // LYXERR0("Out: " << lastlay->name());
647 if (lay.isEnvironment()) {
648 // we are going into a new environment
649 // LYXERR0("In: " << lay.name());
656 void Counters::beginEnvironment()
658 counter_stack_.push_back(counter_stack_.back());
662 void Counters::endEnvironment()
664 LASSERT(!counter_stack_.empty(), return);
665 counter_stack_.pop_back();
669 vector<docstring> Counters::listOfCounters() const {
670 vector<docstring> ret;
671 for(auto const & k : counterList_)
672 ret.emplace_back(k.first);