]> git.lyx.org Git - lyx.git/blob - src/Counters.cpp
39e177b1f6b42b2a921599e88a14432ced912150
[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
18 #include "support/convert.h"
19 #include "support/debug.h"
20 #include "support/lstrings.h"
21
22 #include <boost/assert.hpp>
23
24 #include <sstream>
25
26 using namespace std;
27 using namespace lyx::support;
28
29 namespace lyx {
30
31
32 Counter::Counter()
33 {
34         reset();
35 }
36
37
38 Counter::Counter(docstring const & mc, docstring const & ls, 
39                  docstring const & lsa)
40         : master_(mc), labelstring_(ls), labelstringappendix_(lsa)
41 {
42         reset();
43 }
44
45
46 void Counter::set(int v)
47 {
48         value_ = v;
49 }
50
51
52 void Counter::addto(int v)
53 {
54         value_ += v;
55 }
56
57
58 int Counter::value() const
59 {
60         return value_;
61 }
62
63
64 void Counter::step()
65 {
66         ++value_;
67 }
68
69
70 void Counter::reset()
71 {
72         value_ = 0;
73 }
74
75
76 docstring const & Counter::master() const
77 {
78         return master_;
79 }
80
81
82 docstring const & Counter::labelString() const
83 {
84         return labelstring_;
85 }
86
87
88 docstring const & Counter::labelStringAppendix() const
89 {
90         return labelstringappendix_;
91 }
92
93
94 void Counters::newCounter(docstring const & newc,
95                           docstring const & masterc, 
96                           docstring const & ls,
97                           docstring const & lsa)
98 {
99         if (!masterc.empty() && !hasCounter(masterc)) {
100                 lyxerr << "Master counter does not exist: "
101                        << to_utf8(masterc)
102                        << endl;
103                 return;
104         }
105         counterList[newc] = Counter(masterc, ls, lsa);
106 }
107
108
109 bool Counters::hasCounter(docstring const & c) const
110 {
111         return counterList.find(c) != counterList.end();
112 }
113
114
115 void Counters::set(docstring const & ctr, int const val)
116 {
117         CounterList::iterator const it = counterList.find(ctr);
118         if (it == counterList.end()) {
119                 lyxerr << "set: Counter does not exist: "
120                        << to_utf8(ctr) << endl;
121                 return;
122         }
123         it->second.set(val);
124 }
125
126
127 void Counters::addto(docstring const & ctr, int const val)
128 {
129         CounterList::iterator const it = counterList.find(ctr);
130         if (it == counterList.end()) {
131                 lyxerr << "addto: Counter does not exist: "
132                        << to_utf8(ctr) << endl;
133                 return;
134         }
135         it->second.addto(val);
136 }
137
138
139 int Counters::value(docstring const & ctr) const
140 {
141         CounterList::const_iterator const cit = counterList.find(ctr);
142         if (cit == counterList.end()) {
143                 lyxerr << "value: Counter does not exist: "
144                        << to_utf8(ctr) << endl;
145                 return 0;
146         }
147         return cit->second.value();
148 }
149
150
151 void Counters::step(docstring const & ctr)
152 {
153         CounterList::iterator it = counterList.find(ctr);
154         if (it == counterList.end()) {
155                 lyxerr << "step: Counter does not exist: "
156                        << to_utf8(ctr) << endl;
157                 return;
158         }
159
160         it->second.step();
161         it = counterList.begin();
162         CounterList::iterator const end = counterList.end();
163         for (; it != end; ++it) {
164                 if (it->second.master() == ctr) {
165                         it->second.reset();
166                 }
167         }
168 }
169
170
171 void Counters::reset()
172 {
173         appendix_ = false;
174         current_float_.erase();
175         CounterList::iterator it = counterList.begin();
176         CounterList::iterator const end = counterList.end();
177         for (; it != end; ++it) {
178                 it->second.reset();
179         }
180 }
181
182
183 void Counters::reset(docstring const & match)
184 {
185         BOOST_ASSERT(!match.empty());
186
187         CounterList::iterator it = counterList.begin();
188         CounterList::iterator end = counterList.end();
189         for (; it != end; ++it) {
190                 if (it->first.find(match) != string::npos)
191                         it->second.reset();
192         }
193 }
194
195
196 void Counters::copy(Counters & from, Counters & to, docstring const & match)
197 {
198         CounterList::iterator it = counterList.begin();
199         CounterList::iterator end = counterList.end();
200         for (; it != end; ++it) {
201                 if (it->first.find(match) != string::npos || match == "") {
202                         to.set(it->first, from.value(it->first));
203                 }
204         }
205 }
206
207
208 namespace {
209
210 char loweralphaCounter(int const n)
211 {
212         if (n < 1 || n > 26)
213                 return '?';
214         return 'a' + n - 1;
215 }
216
217
218 char alphaCounter(int const n)
219 {
220         if (n < 1 || n > 26)
221                 return '?';
222         return 'A' + n - 1;
223 }
224
225
226 char hebrewCounter(int const n)
227 {
228         static const char hebrew[22] = {
229                 '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7', '\xe8',
230                 '\xe9', '\xeb', '\xec', '\xee', '\xf0', '\xf1', '\xf2', '\xf4', '\xf6',
231                 '\xf7', '\xf8', '\xf9', '\xfa'
232         };
233
234         if (n < 1 || n > 22)
235                 return '?';
236         return hebrew[n - 1];
237 }
238
239
240
241 //On the special cases, see http://mathworld.wolfram.com/RomanNumerals.html
242 //and for a list of roman numerals up to and including 3999, see 
243 //http://www.research.att.com/~njas/sequences/a006968.txt. (Thanks to Joost
244 //for this info.)
245 docstring const romanCounter(int const n)
246 {
247         static char const * const ones[9] = {
248                 "I",   "II",  "III", "IV", "V",
249                 "VI",  "VII", "VIII", "IX"
250         };
251         
252         static char const * const tens[9] = {
253                 "X", "XX", "XXX", "XL", "L",
254                 "LX", "LXX", "LXXX", "XC"
255         };
256         
257         static char const * const hunds[9] = {
258                 "C", "CC", "CCC", "CD", "D",
259                 "DC", "DCC", "DCCC", "CM"
260         };
261         
262         if (n > 1000 || n < 1) 
263                 return from_ascii("??");
264         
265         int val = n;
266         string roman;
267         switch (n) {
268         //special cases
269         case 900: 
270                 roman = "CM";
271                 break;
272         case 400:
273                 roman = "CD";
274                 break;
275         default:
276                 if (val >= 100) {
277                         int hundreds = val / 100;
278                         roman = hunds[hundreds - 1];
279                         val = val % 100;
280                 }
281                 if (val >= 10) {
282                         switch (val) {
283                         //special case
284                         case 90:
285                                 roman = roman + "XC";
286                                 val = 0; //skip next
287                                 break;
288                         default:
289                                 int tensnum = val / 10;
290                                 roman = roman + tens[tensnum - 1];
291                                 val = val % 10;
292                         } // end switch
293                 } // end tens
294                 if (val > 0)
295                         roman = roman + ones[val -1];
296         }
297         return from_ascii(roman);
298 }
299
300
301 docstring const lowerromanCounter(int const n)
302 {
303         return lowercase(romanCounter(n));
304 }
305
306 } // namespace anon
307
308
309 docstring Counters::labelItem(docstring const & ctr,
310                               docstring const & numbertype)
311 {
312         CounterList::const_iterator const cit = counterList.find(ctr);
313         if (cit == counterList.end()) {
314                 lyxerr << "Counter "
315                        << to_utf8(ctr)
316                        << " does not exist." << endl;
317                 return docstring();
318         }
319
320         int val = cit->second.value();
321
322         if (numbertype == "hebrew")
323                 return docstring(1, hebrewCounter(val));
324
325         if (numbertype == "alph")
326                 return docstring(1, loweralphaCounter(val));
327
328         if (numbertype == "Alph")
329                 return docstring(1, alphaCounter(val));
330
331         if (numbertype == "roman")
332                 return lowerromanCounter(val);
333
334         if (numbertype == "Roman")
335                 return romanCounter(val);
336
337         return convert<docstring>(val);
338 }
339
340
341 docstring Counters::theCounter(docstring const & counter)
342 {
343         std::set<docstring> callers;
344         return theCounter(counter, callers);
345 }
346
347 docstring Counters::theCounter(docstring const & counter,
348                                std::set<docstring> & callers)
349 {
350         if (!hasCounter(counter))
351                 return from_ascii("??");
352
353         docstring label;
354
355         if (callers.find(counter) == callers.end()) {
356                 
357                 pair<std::set<docstring>::iterator, bool> result = callers.insert(counter);
358
359                 Counter const & c = counterList[counter];
360                 docstring ls = appendix() ? c.labelStringAppendix() : c.labelString();
361
362                 if (ls.empty()) {
363                         if (!c.master().empty())
364                                 ls = from_ascii("\\the") + c.master() + from_ascii(".");
365                         ls += from_ascii("\\arabic{") + counter + "}";
366                 }
367
368                 label = counterLabel(ls, &callers);
369
370                 callers.erase(result.first);
371         } else {
372                 // recursion detected
373                 lyxerr << "Warning: Recursion in label for counter `"
374                            << counter << "' detected"
375                            << endl;
376         }
377
378         return label;
379 }
380
381
382 docstring Counters::counterLabel(docstring const & format,
383                                  std::set<docstring> * callers)
384 {
385         docstring label = format;
386
387         // FIXME: Using regexps would be better, but we compile boost without
388         // wide regexps currently.
389
390         while (true) {
391                 //lyxerr << "label=" << to_utf8(label) << endl;
392                 size_t const i = label.find(from_ascii("\\the"), 0);
393                 if (i == docstring::npos)
394                         break;
395                 size_t j = i + 4;
396                 size_t k = j;
397                 while (k < label.size() && lowercase(label[k]) >= 'a' 
398                        && lowercase(label[k]) <= 'z')
399                         ++k;
400                 docstring counter = label.substr(j, k - j);
401                 docstring repl = callers? theCounter(counter, *callers): 
402                                               theCounter(counter);
403                 label.replace(i, k - j + 4, repl);
404         }
405
406         while (true) {
407                 //lyxerr << "label=" << to_utf8(label) << endl;
408
409                 size_t const i = label.find('\\', 0);
410                 if (i == docstring::npos)
411                         break;
412                 size_t const j = label.find('{', i + 1);
413                 if (j == docstring::npos)
414                         break;
415                 size_t const k = label.find('}', j + 1);
416                 if (k == docstring::npos)
417                         break;
418                 docstring const numbertype(label, i + 1, j - i - 1);
419                 docstring const counter(label, j + 1, k - j - 1);
420                 docstring const rep = labelItem(counter, numbertype);
421                 label = docstring(label, 0, i) + rep
422                         + docstring(label, k + 1, docstring::npos);
423         }
424         //lyxerr << "DONE! label=" << to_utf8(label) << endl;
425         return label;
426 }
427
428
429 } // namespace lyx