3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Martin Vermeer
9 * \author Richard Heck (roman numerals)
11 * Full author contact details are available in file CREDITS.
18 #include "support/convert.h"
19 #include "support/debug.h"
20 #include "support/lstrings.h"
22 #include <boost/assert.hpp>
27 using namespace lyx::support;
38 Counter::Counter(docstring const & mc, docstring const & ls,
39 docstring const & lsa)
40 : master_(mc), labelstring_(ls), labelstringappendix_(lsa)
46 void Counter::set(int v)
52 void Counter::addto(int v)
58 int Counter::value() const
76 docstring const & Counter::master() const
82 docstring const & Counter::labelString() const
88 docstring const & Counter::labelStringAppendix() const
90 return labelstringappendix_;
94 void Counters::newCounter(docstring const & newc,
95 docstring const & masterc,
97 docstring const & lsa)
99 if (!masterc.empty() && !hasCounter(masterc)) {
100 lyxerr << "Master counter does not exist: "
105 counterList[newc] = Counter(masterc, ls, lsa);
109 bool Counters::hasCounter(docstring const & c) const
111 return counterList.find(c) != counterList.end();
115 void Counters::set(docstring const & ctr, int const val)
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;
127 void Counters::addto(docstring const & ctr, int const val)
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;
135 it->second.addto(val);
139 int Counters::value(docstring const & ctr) const
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;
147 return cit->second.value();
151 void Counters::step(docstring const & ctr)
153 CounterList::iterator it = counterList.find(ctr);
154 if (it == counterList.end()) {
155 lyxerr << "step: Counter does not exist: "
156 << to_utf8(ctr) << endl;
161 it = counterList.begin();
162 CounterList::iterator const end = counterList.end();
163 for (; it != end; ++it) {
164 if (it->second.master() == ctr) {
171 void Counters::reset()
175 current_float_.erase();
176 CounterList::iterator it = counterList.begin();
177 CounterList::iterator const end = counterList.end();
178 for (; it != end; ++it) {
184 void Counters::reset(docstring const & match)
186 BOOST_ASSERT(!match.empty());
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)
197 void Counters::copy(Counters & from, Counters & to, docstring const & match)
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));
211 char loweralphaCounter(int const n)
219 char alphaCounter(int const n)
227 char hebrewCounter(int const n)
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'
237 return hebrew[n - 1];
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
246 docstring const romanCounter(int const n)
248 static char const * const ones[9] = {
249 "I", "II", "III", "IV", "V",
250 "VI", "VII", "VIII", "IX"
253 static char const * const tens[9] = {
254 "X", "XX", "XXX", "XL", "L",
255 "LX", "LXX", "LXXX", "XC"
258 static char const * const hunds[9] = {
259 "C", "CC", "CCC", "CD", "D",
260 "DC", "DCC", "DCCC", "CM"
263 if (n > 1000 || n < 1)
264 return from_ascii("??");
278 int hundreds = val / 100;
279 roman = hunds[hundreds - 1];
286 roman = roman + "XC";
290 int tensnum = val / 10;
291 roman = roman + tens[tensnum - 1];
296 roman = roman + ones[val -1];
298 return from_ascii(roman);
302 docstring const lowerromanCounter(int const n)
304 return lowercase(romanCounter(n));
310 docstring Counters::labelItem(docstring const & ctr,
311 docstring const & numbertype)
313 CounterList::const_iterator const cit = counterList.find(ctr);
314 if (cit == counterList.end()) {
317 << " does not exist." << endl;
321 int val = cit->second.value();
323 if (numbertype == "hebrew")
324 return docstring(1, hebrewCounter(val));
326 if (numbertype == "alph")
327 return docstring(1, loweralphaCounter(val));
329 if (numbertype == "Alph")
330 return docstring(1, alphaCounter(val));
332 if (numbertype == "roman")
333 return lowerromanCounter(val);
335 if (numbertype == "Roman")
336 return romanCounter(val);
338 return convert<docstring>(val);
342 docstring Counters::theCounter(docstring const & counter)
344 std::set<docstring> callers;
345 return theCounter(counter, callers);
348 docstring Counters::theCounter(docstring const & counter,
349 std::set<docstring> & callers)
351 if (!hasCounter(counter))
352 return from_ascii("??");
356 if (callers.find(counter) == callers.end()) {
358 pair<std::set<docstring>::iterator, bool> result = callers.insert(counter);
360 Counter const & c = counterList[counter];
361 docstring ls = appendix() ? c.labelStringAppendix() : c.labelString();
364 if (!c.master().empty())
365 ls = from_ascii("\\the") + c.master() + from_ascii(".");
366 ls += from_ascii("\\arabic{") + counter + "}";
369 label = counterLabel(ls, &callers);
371 callers.erase(result.first);
373 // recursion detected
374 lyxerr << "Warning: Recursion in label for counter `"
375 << counter << "' detected"
383 docstring Counters::counterLabel(docstring const & format,
384 std::set<docstring> * callers)
386 docstring label = format;
388 // FIXME: Using regexps would be better, but we compile boost without
389 // wide regexps currently.
392 //lyxerr << "label=" << to_utf8(label) << endl;
393 size_t const i = label.find(from_ascii("\\the"), 0);
394 if (i == docstring::npos)
398 while (k < label.size() && lowercase(label[k]) >= 'a'
399 && lowercase(label[k]) <= 'z')
401 docstring counter = label.substr(j, k - j);
402 docstring repl = callers? theCounter(counter, *callers):
404 label.replace(i, k - j + 4, repl);
408 //lyxerr << "label=" << to_utf8(label) << endl;
410 size_t const i = label.find('\\', 0);
411 if (i == docstring::npos)
413 size_t const j = label.find('{', i + 1);
414 if (j == docstring::npos)
416 size_t const k = label.find('}', j + 1);
417 if (k == docstring::npos)
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);
425 //lyxerr << "DONE! label=" << to_utf8(label) << endl;