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()
174 current_float_.erase();
175 CounterList::iterator it = counterList.begin();
176 CounterList::iterator const end = counterList.end();
177 for (; it != end; ++it) {
183 void Counters::reset(docstring const & match)
185 BOOST_ASSERT(!match.empty());
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)
196 void Counters::copy(Counters & from, Counters & to, docstring const & match)
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));
210 char loweralphaCounter(int const n)
218 char alphaCounter(int const n)
226 char hebrewCounter(int const n)
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'
236 return hebrew[n - 1];
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
245 docstring const romanCounter(int const n)
247 static char const * const ones[9] = {
248 "I", "II", "III", "IV", "V",
249 "VI", "VII", "VIII", "IX"
252 static char const * const tens[9] = {
253 "X", "XX", "XXX", "XL", "L",
254 "LX", "LXX", "LXXX", "XC"
257 static char const * const hunds[9] = {
258 "C", "CC", "CCC", "CD", "D",
259 "DC", "DCC", "DCCC", "CM"
262 if (n > 1000 || n < 1)
263 return from_ascii("??");
277 int hundreds = val / 100;
278 roman = hunds[hundreds - 1];
285 roman = roman + "XC";
289 int tensnum = val / 10;
290 roman = roman + tens[tensnum - 1];
295 roman = roman + ones[val -1];
297 return from_ascii(roman);
301 docstring const lowerromanCounter(int const n)
303 return lowercase(romanCounter(n));
309 docstring Counters::labelItem(docstring const & ctr,
310 docstring const & numbertype)
312 CounterList::const_iterator const cit = counterList.find(ctr);
313 if (cit == counterList.end()) {
316 << " does not exist." << endl;
320 int val = cit->second.value();
322 if (numbertype == "hebrew")
323 return docstring(1, hebrewCounter(val));
325 if (numbertype == "alph")
326 return docstring(1, loweralphaCounter(val));
328 if (numbertype == "Alph")
329 return docstring(1, alphaCounter(val));
331 if (numbertype == "roman")
332 return lowerromanCounter(val);
334 if (numbertype == "Roman")
335 return romanCounter(val);
337 return convert<docstring>(val);
341 docstring Counters::theCounter(docstring const & counter)
343 std::set<docstring> callers;
344 return theCounter(counter, callers);
347 docstring Counters::theCounter(docstring const & counter,
348 std::set<docstring> & callers)
350 if (!hasCounter(counter))
351 return from_ascii("??");
355 if (callers.find(counter) == callers.end()) {
357 pair<std::set<docstring>::iterator, bool> result = callers.insert(counter);
359 Counter const & c = counterList[counter];
360 docstring ls = appendix() ? c.labelStringAppendix() : c.labelString();
363 if (!c.master().empty())
364 ls = from_ascii("\\the") + c.master() + from_ascii(".");
365 ls += from_ascii("\\arabic{") + counter + "}";
368 label = counterLabel(ls, &callers);
370 callers.erase(result.first);
372 // recursion detected
373 lyxerr << "Warning: Recursion in label for counter `"
374 << counter << "' detected"
382 docstring Counters::counterLabel(docstring const & format,
383 std::set<docstring> * callers)
385 docstring label = format;
387 // FIXME: Using regexps would be better, but we compile boost without
388 // wide regexps currently.
391 //lyxerr << "label=" << to_utf8(label) << endl;
392 size_t const i = label.find(from_ascii("\\the"), 0);
393 if (i == docstring::npos)
397 while (k < label.size() && lowercase(label[k]) >= 'a'
398 && lowercase(label[k]) <= 'z')
400 docstring counter = label.substr(j, k - j);
401 docstring repl = callers? theCounter(counter, *callers):
403 label.replace(i, k - j + 4, repl);
407 //lyxerr << "label=" << to_utf8(label) << endl;
409 size_t const i = label.find('\\', 0);
410 if (i == docstring::npos)
412 size_t const j = label.find('{', i + 1);
413 if (j == docstring::npos)
415 size_t const k = label.find('}', j + 1);
416 if (k == docstring::npos)
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);
424 //lyxerr << "DONE! label=" << to_utf8(label) << endl;