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