]> git.lyx.org Git - lyx.git/blob - src/Counters.cpp
Comment.
[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         subfloat_ = false;
175         current_float_.erase();
176         CounterList::iterator it = counterList.begin();
177         CounterList::iterator const end = counterList.end();
178         for (; it != end; ++it) {
179                 it->second.reset();
180         }
181 }
182
183
184 void Counters::reset(docstring const & match)
185 {
186         BOOST_ASSERT(!match.empty());
187
188         CounterList::iterator it = counterList.begin();
189         CounterList::iterator end = counterList.end();
190         for (; it != end; ++it) {
191                 if (it->first.find(match) != string::npos)
192                         it->second.reset();
193         }
194 }
195
196
197 void Counters::copy(Counters & from, Counters & to, docstring const & match)
198 {
199         CounterList::iterator it = counterList.begin();
200         CounterList::iterator end = counterList.end();
201         for (; it != end; ++it) {
202                 if (it->first.find(match) != string::npos || match == "") {
203                         to.set(it->first, from.value(it->first));
204                 }
205         }
206 }
207
208
209 namespace {
210
211 char loweralphaCounter(int const n)
212 {
213         if (n < 1 || n > 26)
214                 return '?';
215         return 'a' + n - 1;
216 }
217
218
219 char alphaCounter(int const n)
220 {
221         if (n < 1 || n > 26)
222                 return '?';
223         return 'A' + n - 1;
224 }
225
226
227 char hebrewCounter(int const n)
228 {
229         static const char hebrew[22] = {
230                 '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7', '\xe8',
231                 '\xe9', '\xeb', '\xec', '\xee', '\xf0', '\xf1', '\xf2', '\xf4', '\xf6',
232                 '\xf7', '\xf8', '\xf9', '\xfa'
233         };
234
235         if (n < 1 || n > 22)
236                 return '?';
237         return hebrew[n - 1];
238 }
239
240
241
242 //On the special cases, see http://mathworld.wolfram.com/RomanNumerals.html
243 //and for a list of roman numerals up to and including 3999, see 
244 //http://www.research.att.com/~njas/sequences/a006968.txt. (Thanks to Joost
245 //for this info.)
246 docstring const romanCounter(int const n)
247 {
248         static char const * const ones[9] = {
249                 "I",   "II",  "III", "IV", "V",
250                 "VI",  "VII", "VIII", "IX"
251         };
252         
253         static char const * const tens[9] = {
254                 "X", "XX", "XXX", "XL", "L",
255                 "LX", "LXX", "LXXX", "XC"
256         };
257         
258         static char const * const hunds[9] = {
259                 "C", "CC", "CCC", "CD", "D",
260                 "DC", "DCC", "DCCC", "CM"
261         };
262         
263         if (n > 1000 || n < 1) 
264                 return from_ascii("??");
265         
266         int val = n;
267         string roman;
268         switch (n) {
269         //special cases
270         case 900: 
271                 roman = "CM";
272                 break;
273         case 400:
274                 roman = "CD";
275                 break;
276         default:
277                 if (val >= 100) {
278                         int hundreds = val / 100;
279                         roman = hunds[hundreds - 1];
280                         val = val % 100;
281                 }
282                 if (val >= 10) {
283                         switch (val) {
284                         //special case
285                         case 90:
286                                 roman = roman + "XC";
287                                 val = 0; //skip next
288                                 break;
289                         default:
290                                 int tensnum = val / 10;
291                                 roman = roman + tens[tensnum - 1];
292                                 val = val % 10;
293                         } // end switch
294                 } // end tens
295                 if (val > 0)
296                         roman = roman + ones[val -1];
297         }
298         return from_ascii(roman);
299 }
300
301
302 docstring const lowerromanCounter(int const n)
303 {
304         return lowercase(romanCounter(n));
305 }
306
307 } // namespace anon
308
309
310 docstring Counters::labelItem(docstring const & ctr,
311                               docstring const & numbertype)
312 {
313         CounterList::const_iterator const cit = counterList.find(ctr);
314         if (cit == counterList.end()) {
315                 lyxerr << "Counter "
316                        << to_utf8(ctr)
317                        << " does not exist." << endl;
318                 return docstring();
319         }
320
321         int val = cit->second.value();
322
323         if (numbertype == "hebrew")
324                 return docstring(1, hebrewCounter(val));
325
326         if (numbertype == "alph")
327                 return docstring(1, loweralphaCounter(val));
328
329         if (numbertype == "Alph")
330                 return docstring(1, alphaCounter(val));
331
332         if (numbertype == "roman")
333                 return lowerromanCounter(val);
334
335         if (numbertype == "Roman")
336                 return romanCounter(val);
337
338         return convert<docstring>(val);
339 }
340
341
342 docstring Counters::theCounter(docstring const & counter)
343 {
344         std::set<docstring> callers;
345         return theCounter(counter, callers);
346 }
347
348 docstring Counters::theCounter(docstring const & counter,
349                                std::set<docstring> & callers)
350 {
351         if (!hasCounter(counter))
352                 return from_ascii("??");
353
354         docstring label;
355
356         if (callers.find(counter) == callers.end()) {
357                 
358                 pair<std::set<docstring>::iterator, bool> result = callers.insert(counter);
359
360                 Counter const & c = counterList[counter];
361                 docstring ls = appendix() ? c.labelStringAppendix() : c.labelString();
362
363                 if (ls.empty()) {
364                         if (!c.master().empty())
365                                 ls = from_ascii("\\the") + c.master() + from_ascii(".");
366                         ls += from_ascii("\\arabic{") + counter + "}";
367                 }
368
369                 label = counterLabel(ls, &callers);
370
371                 callers.erase(result.first);
372         } else {
373                 // recursion detected
374                 lyxerr << "Warning: Recursion in label for counter `"
375                            << counter << "' detected"
376                            << endl;
377         }
378
379         return label;
380 }
381
382
383 docstring Counters::counterLabel(docstring const & format,
384                                  std::set<docstring> * callers)
385 {
386         docstring label = format;
387
388         // FIXME: Using regexps would be better, but we compile boost without
389         // wide regexps currently.
390
391         while (true) {
392                 //lyxerr << "label=" << to_utf8(label) << endl;
393                 size_t const i = label.find(from_ascii("\\the"), 0);
394                 if (i == docstring::npos)
395                         break;
396                 size_t j = i + 4;
397                 size_t k = j;
398                 while (k < label.size() && lowercase(label[k]) >= 'a' 
399                        && lowercase(label[k]) <= 'z')
400                         ++k;
401                 docstring counter = label.substr(j, k - j);
402                 docstring repl = callers? theCounter(counter, *callers): 
403                                               theCounter(counter);
404                 label.replace(i, k - j + 4, repl);
405         }
406
407         while (true) {
408                 //lyxerr << "label=" << to_utf8(label) << endl;
409
410                 size_t const i = label.find('\\', 0);
411                 if (i == docstring::npos)
412                         break;
413                 size_t const j = label.find('{', i + 1);
414                 if (j == docstring::npos)
415                         break;
416                 size_t const k = label.find('}', j + 1);
417                 if (k == docstring::npos)
418                         break;
419                 docstring const numbertype(label, i + 1, j - i - 1);
420                 docstring const counter(label, j + 1, k - j - 1);
421                 docstring const rep = labelItem(counter, numbertype);
422                 label = docstring(label, 0, i) + rep
423                         + docstring(label, k + 1, docstring::npos);
424         }
425         //lyxerr << "DONE! label=" << to_utf8(label) << endl;
426         return label;
427 }
428
429
430 } // namespace lyx