]> git.lyx.org Git - lyx.git/blob - src/Counters.cpp
Display equation/theorem numbers in insert cross reference dialog.
[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 Kimberly 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 & prettyformat,
46                 docstring const & guiname)
47         : initial_value_(0), saved_value_(0), parent_(mc), labelstring_(ls),
48           labelstringappendix_(lsa), prettyformat_(prettyformat), guiname_(guiname)
49 {
50         reset();
51 }
52
53
54 bool Counter::read(Lexer & lex)
55 {
56         enum {
57                 CT_WITHIN = 1,
58                 CT_LABELSTRING,
59                 CT_LABELSTRING_APPENDIX,
60                 CT_PRETTYFORMAT,
61                 CT_INITIALVALUE,
62                 CT_GUINAME,
63                 CT_LATEXNAME,
64                 CT_END
65         };
66
67         LexerKeyword counterTags[] = {
68                 { "end", CT_END },
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 }
76         };
77
78         lex.pushTable(counterTags);
79
80         bool getout = false;
81         while (!getout && lex.isOK()) {
82                 int le = lex.lex();
83                 switch (le) {
84                         case Lexer::LEX_UNDEF:
85                                 lex.printError("Unknown counter tag `$$Token'");
86                                 continue;
87                         default:
88                                 break;
89                 }
90                 switch (le) {
91                         case CT_WITHIN:
92                                 lex.next();
93                                 parent_ = lex.getDocString();
94                                 if (parent_ == "none")
95                                         parent_.erase();
96                                 break;
97                         case CT_INITIALVALUE:
98                                 lex.next();
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)
105                                         initial_value_ = 0;
106                                 else
107                                         initial_value_ -= 1;
108                                 break;
109                         case CT_PRETTYFORMAT:
110                                 lex.next();
111                                 prettyformat_ = lex.getDocString();
112                                 break;
113                         case CT_LABELSTRING:
114                                 lex.next();
115                                 labelstring_ = lex.getDocString();
116                                 labelstringappendix_ = labelstring_;
117                                 break;
118                         case CT_LABELSTRING_APPENDIX:
119                                 lex.next();
120                                 labelstringappendix_ = lex.getDocString();
121                                 break;
122                         case CT_GUINAME:
123                                 lex.next();
124                                 guiname_ = lex.getDocString();
125                                 break;
126                         case CT_LATEXNAME:
127                                 lex.next();
128                                 latexname_ = lex.getDocString();
129                                 break;
130                         case CT_END:
131                                 getout = true;
132                                 break;
133                 }
134                 if (prettyformat_ == "") { // fall back on GuiName if PrettyFormat is empty
135                         if (guiname_ == "")
136                                 prettyformat_ = from_ascii("##");
137                         else
138                                 prettyformat_ = "## (" + guiname_ + " counter)";
139                 }
140         }
141
142         // Here if have a full counter if getout == true
143         if (!getout)
144                 LYXERR0("No End tag found for counter!");
145         lex.popTable();
146         return getout;
147 }
148
149
150 void Counter::set(int v)
151 {
152         value_ = v;
153 }
154
155
156 void Counter::addto(int v)
157 {
158         value_ += v;
159 }
160
161
162 int Counter::value() const
163 {
164         return value_;
165 }
166
167
168 void Counter::saveValue()
169 {
170         saved_value_ = value_;
171 }
172
173
174 void Counter::restoreValue()
175 {
176         value_ = saved_value_;
177 }
178
179
180 void Counter::step()
181 {
182         ++value_;
183 }
184
185
186 void Counter::reset()
187 {
188         value_ = initial_value_;
189 }
190
191
192 docstring const & Counter::parent() const
193 {
194         return parent_;
195 }
196
197
198 bool Counter::checkAndRemoveParent(docstring const & cnt)
199 {
200         if (parent_ != cnt)
201                 return false;
202         parent_ = docstring();
203         return true;
204 }
205
206
207 docstring const & Counter::labelString(bool in_appendix) const
208 {
209         return in_appendix ? labelstringappendix_ : labelstring_;
210 }
211
212
213 Counter::StringMap & Counter::flatLabelStrings(bool in_appendix) const
214 {
215         return in_appendix ? flatlabelstringappendix_ : flatlabelstring_;
216 }
217
218
219 Counters::Counters() : appendix_(false), subfloat_(false), longtable_(false)
220 {
221         layout_stack_.push_back(nullptr);
222         counter_stack_.push_back(from_ascii(""));
223 }
224
225
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)
232 {
233         if (!parentc.empty() && !hasCounter(parentc)) {
234                 lyxerr << "Parent counter does not exist: "
235                            << to_utf8(parentc)
236                        << endl;
237                 return;
238         }
239         counterList_[newc] = Counter(parentc, ls, lsa, prettyformat, guiname);
240 }
241
242
243 bool Counters::hasCounter(docstring const & c) const
244 {
245         return counterList_.find(c) != counterList_.end();
246 }
247
248
249 bool Counters::read(Lexer & lex, docstring const & name, bool makenew)
250 {
251         if (hasCounter(name)) {
252                 LYXERR(Debug::TCLASS, "Reading existing counter " << to_utf8(name));
253                 return counterList_[name].read(lex);
254         }
255
256         LYXERR(Debug::TCLASS, "Reading new counter " << to_utf8(name));
257         Counter cnt;
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;
262         else if (!success)
263                 LYXERR0("Error reading counter `" << name << "'!");
264         return success;
265 }
266
267
268 void Counters::set(docstring const & ctr, int const val)
269 {
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;
274                 return;
275         }
276         it->second.set(val);
277 }
278
279
280 void Counters::addto(docstring const & ctr, int const val)
281 {
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;
286                 return;
287         }
288         it->second.addto(val);
289 }
290
291
292 int Counters::value(docstring const & ctr) const
293 {
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;
298                 return 0;
299         }
300         return cit->second.value();
301 }
302
303
304 void Counters::saveValue(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.saveValue();
315 }
316
317
318 void Counters::restoreValue(docstring const & ctr) const
319 {
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;
324                 return;
325         }
326         Counter const & cnt = cit->second;
327         Counter & ccnt = const_cast<Counter &>(cnt);
328         ccnt.restoreValue();
329 }
330
331
332 void Counters::resetChildren(docstring const & count)
333 {
334         for (auto & ctr : counterList_) {
335                 if (ctr.second.parent() == count) {
336                         ctr.second.reset();
337                         resetChildren(ctr.first);
338                 }
339         }
340 }
341
342
343 void Counters::stepParent(docstring const & ctr, UpdateType utype)
344 {
345         CounterList::iterator it = counterList_.find(ctr);
346         if (it == counterList_.end()) {
347                 lyxerr << "step: Counter does not exist: "
348                        << to_utf8(ctr) << endl;
349                 return;
350         }
351         step(it->second.parent(), utype);
352 }
353
354
355 void Counters::step(docstring const & ctr, UpdateType /* deleted */)
356 {
357         CounterList::iterator it = counterList_.find(ctr);
358         if (it == counterList_.end()) {
359                 lyxerr << "step: Counter does not exist: "
360                        << to_utf8(ctr) << endl;
361                 return;
362         }
363
364         it->second.step();
365         LBUFERR(!counter_stack_.empty());
366         counter_stack_.pop_back();
367         counter_stack_.push_back(ctr);
368
369         resetChildren(ctr);
370 }
371
372
373 docstring const & Counters::guiName(docstring const & cntr) const
374 {
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();
380         }
381
382         docstring const & guiname = it->second.guiName();
383         if (guiname.empty())
384                 return cntr;
385         return guiname;
386 }
387
388
389 docstring const & Counters::latexName(docstring const & cntr) const
390 {
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();
396         }
397
398         docstring const & latexname = it->second.latexName();
399         if (latexname.empty())
400                 return cntr;
401         return latexname;
402 }
403
404
405 void Counters::reset()
406 {
407         appendix_ = false;
408         subfloat_ = false;
409         current_float_.erase();
410         for (auto & ctr : counterList_)
411                 ctr.second.reset();
412         counter_stack_.clear();
413         counter_stack_.push_back(from_ascii(""));
414         layout_stack_.clear();
415         layout_stack_.push_back(nullptr);
416 }
417
418
419 void Counters::reset(docstring const & match)
420 {
421         LASSERT(!match.empty(), return);
422
423         for (auto & ctr : counterList_) {
424                 if (ctr.first.find(match) != string::npos)
425                         ctr.second.reset();
426         }
427 }
428
429
430 bool Counters::copy(docstring const & cnt, docstring const & newcnt)
431 {
432         auto const it = counterList_.find(cnt);
433         if (it == counterList_.end())
434                 return false;
435         counterList_[newcnt] = it->second;
436         return true;
437 }
438
439
440 bool Counters::remove(docstring const & cnt)
441 {
442         bool retval = counterList_.erase(cnt);
443         if (!retval)
444                 return false;
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));
449         }
450         return retval;
451 }
452
453
454 docstring Counters::labelItem(docstring const & ctr,
455                               docstring const & numbertype) const
456 {
457         CounterList::const_iterator const cit = counterList_.find(ctr);
458         if (cit == counterList_.end()) {
459                 lyxerr << "Counter "
460                        << to_utf8(ctr)
461                        << " does not exist." << endl;
462                 return docstring();
463         }
464
465         int val = cit->second.value();
466
467         if (numbertype == "hebrew")
468                 return docstring(1, hebrewCounter(val));
469
470         if (numbertype == "alph")
471                 return docstring(1, loweralphaCounter(val));
472
473         if (numbertype == "Alph")
474                 return docstring(1, alphaCounter(val));
475
476         if (numbertype == "roman")
477                 return lowerromanCounter(val);
478
479         if (numbertype == "Roman")
480                 return romanCounter(val);
481
482         if (numbertype == "fnsymbol")
483                 return fnsymbolCounter(val);
484
485         if (numbertype == "superarabic")
486                 return superarabicCounter(val);
487
488         return convert<docstring>(val);
489 }
490
491
492 docstring Counters::theCounter(docstring const & counter,
493                                string const & lang) const
494 {
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);
503
504         vector<docstring> callers;
505         docstring const & fls = flattenLabelString(counter, appendix(),
506                                                    lang, callers);
507         sm[lang] = fls;
508         return counterLabel(fls, lang);
509 }
510
511
512 docstring Counters::flattenLabelString(docstring const & counter,
513                                        bool in_appendix,
514                                        string const & lang,
515                                        vector<docstring> & callers) const
516 {
517         if (find(callers.begin(), callers.end(), counter) != callers.end()) {
518                 // recursion detected
519                 lyxerr << "Warning: Recursion in label for counter `"
520                        << counter << "' detected"
521                        << endl;
522                 return from_ascii("??");
523         }
524
525         CounterList::const_iterator it = counterList_.find(counter);
526         if (it == counterList_.end())
527                 return from_ascii("#");
528         Counter const & c = it->second;
529
530         docstring ls = translateIfPossible(c.labelString(in_appendix), lang);
531
532         callers.push_back(counter);
533         if (ls.empty()) {
534                 if (!c.parent().empty())
535                         ls = flattenLabelString(c.parent(), in_appendix, lang, callers)
536                                 + from_ascii(".");
537                 callers.pop_back();
538                 return ls + from_ascii("\\arabic{") + counter + "}";
539         }
540
541         while (true) {
542                 //lyxerr << "ls=" << to_utf8(ls) << endl;
543                 size_t const i = ls.find(from_ascii("\\the"), 0);
544                 if (i == docstring::npos)
545                         break;
546                 size_t const j = i + 4;
547                 size_t k = j;
548                 while (k < ls.size() && lowercase(ls[k]) >= 'a'
549                        && lowercase(ls[k]) <= 'z')
550                         ++k;
551                 docstring const newc = ls.substr(j, k - j);
552                 docstring const repl = flattenLabelString(newc, in_appendix,
553                                                           lang, callers);
554                 ls.replace(i, k - j + 4, repl);
555         }
556         callers.pop_back();
557
558         return ls;
559 }
560
561
562 docstring Counters::counterLabel(docstring const & format,
563                                  string const & lang) const
564 {
565         docstring label = format;
566
567         // FIXME: Using regexps would be better, but we compile boost without
568         // wide regexps currently.
569         docstring const the = from_ascii("\\the");
570         while (true) {
571                 //lyxerr << "label=" << label << endl;
572                 size_t const i = label.find(the, 0);
573                 if (i == docstring::npos)
574                         break;
575                 size_t const j = i + 4;
576                 size_t k = j;
577                 while (k < label.size() && lowercase(label[k]) >= 'a'
578                        && lowercase(label[k]) <= 'z')
579                         ++k;
580                 docstring const newc(label, j, k - j);
581                 label.replace(i, k - i, theCounter(newc, lang));
582         }
583         while (true) {
584                 //lyxerr << "label=" << label << endl;
585
586                 size_t const i = label.find('\\', 0);
587                 if (i == docstring::npos)
588                         break;
589                 size_t const j = label.find('{', i + 1);
590                 if (j == docstring::npos)
591                         break;
592                 size_t const k = label.find('}', j + 1);
593                 if (k == docstring::npos)
594                         break;
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));
598         }
599         //lyxerr << "DONE! label=" << label << endl;
600         return label;
601 }
602
603
604 docstring Counters::prettyCounter(docstring const & name,
605                                string const & lang) const
606 {
607         CounterList::const_iterator it = counterList_.find(name);
608         if (it == counterList_.end())
609                 return from_ascii("#");
610         Counter const & ctr = it->second;
611
612         docstring const value = theCounter(name, lang);
613         docstring const & format =
614             translateIfPossible(ctr.prettyFormat(), lang);
615         if (format.empty())
616                 return value;
617         return subst(format, from_ascii("##"), value);
618 }
619
620
621 docstring Counters::currentCounter() const
622 {
623         LBUFERR(!counter_stack_.empty());
624         return counter_stack_.back();
625 }
626
627
628 void Counters::setActiveLayout(Layout const & lay)
629 {
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.
634         if (!lastlay) {
635                 layout_stack_.pop_back();
636                 layout_stack_.push_back(&lay);
637                 if (lay.isEnvironment())
638                         beginEnvironment();
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());
645                         endEnvironment();
646                 }
647                 if (lay.isEnvironment()) {
648                         // we are going into a new environment
649                         // LYXERR0("In: " << lay.name());
650                         beginEnvironment();
651                 }
652         }
653 }
654
655
656 void Counters::beginEnvironment()
657 {
658         counter_stack_.push_back(counter_stack_.back());
659 }
660
661
662 void Counters::endEnvironment()
663 {
664         LASSERT(!counter_stack_.empty(), return);
665         counter_stack_.pop_back();
666 }
667
668
669 vector<docstring> Counters::listOfCounters() const {
670         vector<docstring> ret;
671         for(auto const & k : counterList_)
672                 ret.emplace_back(k.first);
673         return ret;
674 }
675
676
677 } // namespace lyx