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