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.
19 #include "support/convert.h"
20 #include "support/counter_reps.h"
21 #include "support/debug.h"
22 #include "support/docstring.h"
23 #include "support/gettext.h"
24 #include "support/lassert.h"
25 #include "support/Lexer.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,
68 LexerKeyword counterTags[] = {
70 { "guiname", CT_GUINAME },
71 { "initialvalue", CT_INITIALVALUE},
72 { "labelstring", CT_LABELSTRING },
73 { "labelstringappendix", CT_LABELSTRING_APPENDIX },
74 { "latexname", CT_LATEXNAME },
75 { "prettyformat", CT_PRETTYFORMAT },
76 { "refformat", CT_REFFORMAT },
77 { "within", CT_WITHIN }
80 lex.pushTable(counterTags);
83 while (!getout && lex.isOK()) {
86 case Lexer::LEX_UNDEF:
87 lex.printError("Unknown counter tag `$$Token'");
95 parent_ = lex.getDocString();
96 if (parent_ == "none")
101 initial_value_ = lex.getInteger();
102 // getInteger() returns -1 on error, and larger
103 // negative values do not make much sense.
104 // In the other case, we subtract one, since the
105 // counter will be incremented before its first use.
106 if (initial_value_ <= -1)
111 case CT_PRETTYFORMAT:
113 prettyformat_ = lex.getDocString();
117 docstring const key = lex.getDocString();
119 docstring const value = lex.getDocString();
120 ref_formats_[key] = value;
121 // LYXERR0("refformat: " << key << " => " << value);
126 labelstring_ = lex.getDocString();
127 labelstringappendix_ = labelstring_;
129 case CT_LABELSTRING_APPENDIX:
131 labelstringappendix_ = lex.getDocString();
135 guiname_ = lex.getDocString();
139 latexname_ = lex.getDocString();
145 // fall back on GuiName if PrettyFormat is empty
146 if (prettyformat_.empty()) {
147 if (guiname_.empty())
148 prettyformat_ = from_ascii("##");
150 prettyformat_ = "## (" + guiname_ + ")";
154 // Here if have a full counter if getout == true
156 LYXERR0("No End tag found for counter!");
162 void Counter::set(int v)
168 void Counter::addto(int v)
174 int Counter::value() const
180 void Counter::saveValue()
182 saved_value_ = value_;
186 void Counter::restoreValue()
188 value_ = saved_value_;
198 void Counter::reset()
200 value_ = initial_value_;
204 docstring const & Counter::refFormat(docstring const & prefix) const
206 map<docstring, docstring>::const_iterator it = ref_formats_.find(prefix);
207 if (it == ref_formats_.end())
208 return prettyformat_;
213 docstring const & Counter::parent() const
219 bool Counter::checkAndRemoveParent(docstring const & cnt)
223 parent_ = docstring();
228 docstring const & Counter::labelString(bool in_appendix) const
230 return in_appendix ? labelstringappendix_ : labelstring_;
234 Counter::StringMap & Counter::flatLabelStrings(bool in_appendix) const
236 return in_appendix ? flatlabelstringappendix_ : flatlabelstring_;
240 Counters::Counters() : appendix_(false), subfloat_(false), longtable_(false)
242 layout_stack_.push_back(nullptr);
243 counter_stack_.push_back(from_ascii(""));
247 void Counters::newCounter(docstring const & newc,
248 docstring const & parentc,
249 docstring const & ls,
250 docstring const & lsa,
251 docstring const & prettyformat,
252 docstring const & guiname)
254 if (!parentc.empty() && !hasCounter(parentc)) {
255 lyxerr << "Parent counter does not exist: "
260 counterList_[newc] = Counter(parentc, ls, lsa, prettyformat, guiname);
264 bool Counters::hasCounter(docstring const & c) const
266 return counterList_.find(c) != counterList_.end();
270 bool Counters::read(Lexer & lex, docstring const & name, bool makenew)
272 if (hasCounter(name)) {
273 LYXERR(Debug::TCLASS, "Reading existing counter " << to_utf8(name));
274 return counterList_[name].read(lex);
277 LYXERR(Debug::TCLASS, "Reading new counter " << to_utf8(name));
279 bool success = cnt.read(lex);
280 // if makenew is false, we will just discard what we read
281 if (success && makenew)
282 counterList_[name] = cnt;
284 LYXERR0("Error reading counter `" << name << "'!");
289 void Counters::set(docstring const & ctr, int const val)
291 CounterList::iterator const it = counterList_.find(ctr);
292 if (it == counterList_.end()) {
293 lyxerr << "set: Counter does not exist: "
294 << to_utf8(ctr) << endl;
301 void Counters::addto(docstring const & ctr, int const val)
303 CounterList::iterator const it = counterList_.find(ctr);
304 if (it == counterList_.end()) {
305 lyxerr << "addto: Counter does not exist: "
306 << to_utf8(ctr) << endl;
309 it->second.addto(val);
313 int Counters::value(docstring const & ctr) const
315 CounterList::const_iterator const cit = counterList_.find(ctr);
316 if (cit == counterList_.end()) {
317 lyxerr << "value: Counter does not exist: "
318 << to_utf8(ctr) << endl;
321 return cit->second.value();
325 void Counters::saveValue(docstring const & ctr) const
327 CounterList::const_iterator const cit = counterList_.find(ctr);
328 if (cit == counterList_.end()) {
329 lyxerr << "value: Counter does not exist: "
330 << to_utf8(ctr) << endl;
333 Counter const & cnt = cit->second;
334 Counter & ccnt = const_cast<Counter &>(cnt);
339 void Counters::restoreValue(docstring const & ctr) const
341 CounterList::const_iterator const cit = counterList_.find(ctr);
342 if (cit == counterList_.end()) {
343 lyxerr << "value: Counter does not exist: "
344 << to_utf8(ctr) << endl;
347 Counter const & cnt = cit->second;
348 Counter & ccnt = const_cast<Counter &>(cnt);
353 void Counters::resetChildren(docstring const & count)
355 for (auto & ctr : counterList_) {
356 if (ctr.second.parent() == count) {
358 resetChildren(ctr.first);
364 void Counters::stepParent(docstring const & ctr, UpdateType utype)
366 CounterList::iterator it = counterList_.find(ctr);
367 if (it == counterList_.end()) {
368 lyxerr << "step: Counter does not exist: "
369 << to_utf8(ctr) << endl;
372 step(it->second.parent(), utype);
376 void Counters::step(docstring const & ctr, UpdateType /* deleted */)
378 CounterList::iterator it = counterList_.find(ctr);
379 if (it == counterList_.end()) {
380 lyxerr << "step: Counter does not exist: "
381 << to_utf8(ctr) << endl;
386 LBUFERR(!counter_stack_.empty());
387 counter_stack_.pop_back();
388 counter_stack_.push_back(ctr);
394 docstring const & Counters::guiName(docstring const & cntr) const
396 CounterList::const_iterator it = counterList_.find(cntr);
397 if (it == counterList_.end()) {
398 lyxerr << "step: Counter does not exist: "
399 << to_utf8(cntr) << endl;
400 return empty_docstring();
403 docstring const & guiname = it->second.guiName();
410 docstring const & Counters::latexName(docstring const & cntr) const
412 CounterList::const_iterator it = counterList_.find(cntr);
413 if (it == counterList_.end()) {
414 lyxerr << "step: Counter does not exist: "
415 << to_utf8(cntr) << endl;
416 return empty_docstring();
419 docstring const & latexname = it->second.latexName();
420 if (latexname.empty())
426 void Counters::reset()
430 current_float_.erase();
431 for (auto & ctr : counterList_)
433 counter_stack_.clear();
434 counter_stack_.push_back(from_ascii(""));
435 layout_stack_.clear();
436 layout_stack_.push_back(nullptr);
440 void Counters::reset(docstring const & match)
442 LASSERT(!match.empty(), return);
444 for (auto & ctr : counterList_) {
445 if (ctr.first.find(match) != string::npos)
451 bool Counters::copy(docstring const & cnt, docstring const & newcnt)
453 auto const it = counterList_.find(cnt);
454 if (it == counterList_.end())
456 counterList_[newcnt] = it->second;
461 bool Counters::remove(docstring const & cnt)
463 bool retval = counterList_.erase(cnt);
466 for (auto & ctr : counterList_) {
467 if (ctr.second.checkAndRemoveParent(cnt))
468 LYXERR(Debug::TCLASS, "Removed parent counter `" +
469 to_utf8(cnt) + "' from counter: " + to_utf8(ctr.first));
475 docstring Counters::labelItem(docstring const & ctr,
476 docstring const & numbertype) const
478 CounterList::const_iterator const cit = counterList_.find(ctr);
479 if (cit == counterList_.end()) {
482 << " does not exist." << endl;
486 int val = cit->second.value();
488 if (numbertype == "hebrew")
489 return docstring(1, hebrewCounter(val));
491 if (numbertype == "alph")
492 return docstring(1, loweralphaCounter(val));
494 if (numbertype == "Alph")
495 return docstring(1, alphaCounter(val));
497 if (numbertype == "roman")
498 return lowerromanCounter(val);
500 if (numbertype == "Roman")
501 return romanCounter(val);
503 if (numbertype == "fnsymbol")
504 return fnsymbolCounter(val);
506 if (numbertype == "superarabic")
507 return superarabicCounter(val);
509 return convert<docstring>(val);
513 docstring Counters::theCounter(docstring const & counter,
514 string const & lang) const
516 CounterList::const_iterator it = counterList_.find(counter);
517 if (it == counterList_.end())
518 return from_ascii("#");
519 Counter const & ctr = it->second;
520 Counter::StringMap & sm = ctr.flatLabelStrings(appendix());
521 Counter::StringMap::iterator smit = sm.find(lang);
522 if (smit != sm.end())
523 return counterLabel(smit->second, lang);
525 vector<docstring> callers;
526 docstring const & fls = flattenLabelString(counter, appendix(),
529 return counterLabel(fls, lang);
533 docstring Counters::flattenLabelString(docstring const & counter,
536 vector<docstring> & callers) const
538 if (find(callers.begin(), callers.end(), counter) != callers.end()) {
539 // recursion detected
540 lyxerr << "Warning: Recursion in label for counter `"
541 << counter << "' detected"
543 return from_ascii("??");
546 CounterList::const_iterator it = counterList_.find(counter);
547 if (it == counterList_.end())
548 return from_ascii("#");
549 Counter const & c = it->second;
551 docstring ls = translateIfPossible(c.labelString(in_appendix), lang);
553 callers.push_back(counter);
555 if (!c.parent().empty())
556 ls = flattenLabelString(c.parent(), in_appendix, lang, callers)
559 return ls + from_ascii("\\arabic{") + counter + "}";
563 //lyxerr << "ls=" << to_utf8(ls) << endl;
564 size_t const i = ls.find(from_ascii("\\the"), 0);
565 if (i == docstring::npos)
567 size_t const j = i + 4;
569 while (k < ls.size() && lowercase(ls[k]) >= 'a'
570 && lowercase(ls[k]) <= 'z')
572 docstring const newc = ls.substr(j, k - j);
573 docstring const repl = flattenLabelString(newc, in_appendix,
575 ls.replace(i, k - j + 4, repl);
583 docstring Counters::counterLabel(docstring const & format,
584 string const & lang) const
586 docstring label = format;
588 // FIXME: Using regexps would be better, but we compile boost without
589 // wide regexps currently.
590 docstring const the = from_ascii("\\the");
592 //lyxerr << "label=" << label << endl;
593 size_t const i = label.find(the, 0);
594 if (i == docstring::npos)
596 size_t const j = i + 4;
598 while (k < label.size() && lowercase(label[k]) >= 'a'
599 && lowercase(label[k]) <= 'z')
601 docstring const newc(label, j, k - j);
602 label.replace(i, k - i, theCounter(newc, lang));
605 //lyxerr << "label=" << label << endl;
607 size_t const i = label.find('\\', 0);
608 if (i == docstring::npos)
610 size_t const j = label.find('{', i + 1);
611 if (j == docstring::npos)
613 size_t const k = label.find('}', j + 1);
614 if (k == docstring::npos)
616 docstring const numbertype(label, i + 1, j - i - 1);
617 docstring const counter(label, j + 1, k - j - 1);
618 label.replace(i, k + 1 - i, labelItem(counter, numbertype));
620 //lyxerr << "DONE! label=" << label << endl;
625 docstring Counters::formattedCounter(docstring const & name,
626 docstring const & prex, string const & lang) const
628 CounterList::const_iterator it = counterList_.find(name);
629 if (it == counterList_.end())
630 return from_ascii("#");
631 Counter const & ctr = it->second;
633 docstring const value = theCounter(name, lang);
634 docstring const format =
635 translateIfPossible(counterLabel(ctr.refFormat(prex), lang), lang);
638 return subst(format, from_ascii("##"), value);
642 docstring Counters::prettyCounter(docstring const & name,
643 string const & lang) const
645 CounterList::const_iterator it = counterList_.find(name);
646 if (it == counterList_.end())
647 return from_ascii("#");
648 Counter const & ctr = it->second;
650 docstring const value = theCounter(name, lang);
651 docstring const & format =
652 translateIfPossible(counterLabel(ctr.prettyFormat(), lang), lang);
655 return subst(format, from_ascii("##"), value);
659 docstring Counters::currentCounter() const
661 LBUFERR(!counter_stack_.empty());
662 return counter_stack_.back();
666 void Counters::setActiveLayout(Layout const & lay)
668 LASSERT(!layout_stack_.empty(), return);
669 Layout const * const lastlay = layout_stack_.back();
670 // we want to check whether the layout has changed and, if so,
671 // whether we are coming out of or going into an environment.
673 layout_stack_.pop_back();
674 layout_stack_.push_back(&lay);
675 if (lay.isEnvironment())
677 } else if (lastlay->name() != lay.name()) {
678 layout_stack_.pop_back();
679 layout_stack_.push_back(&lay);
680 if (lastlay->isEnvironment()) {
681 // we are coming out of an environment
682 // LYXERR0("Out: " << lastlay->name());
685 if (lay.isEnvironment()) {
686 // we are going into a new environment
687 // LYXERR0("In: " << lay.name());
694 void Counters::beginEnvironment()
696 counter_stack_.push_back(counter_stack_.back());
700 void Counters::endEnvironment()
702 LASSERT(!counter_stack_.empty(), return);
703 counter_stack_.pop_back();
707 vector<docstring> Counters::listOfCounters() const {
708 vector<docstring> ret;
709 for(auto const & k : counterList_)
710 ret.emplace_back(k.first);