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.
19 #include "support/convert.h"
20 #include "support/debug.h"
21 #include "support/gettext.h"
22 #include "support/lassert.h"
23 #include "support/lstrings.h"
29 using namespace lyx::support
;
40 Counter::Counter(docstring
const & mc
, docstring
const & ls
,
41 docstring
const & lsa
)
42 : master_(mc
), labelstring_(ls
), labelstringappendix_(lsa
)
48 bool Counter::read(Lexer
& lex
)
53 CT_LABELSTRING_APPENDIX
,
57 LexerKeyword counterTags
[] = {
59 { "labelstring", CT_LABELSTRING
},
60 { "labelstringappendix", CT_LABELSTRING_APPENDIX
},
61 { "within", CT_WITHIN
}
64 lex
.pushTable(counterTags
);
67 while (!getout
&& lex
.isOK()) {
70 case Lexer::LEX_UNDEF
:
71 lex
.printError("Unknown counter tag `$$Token'");
79 master_
= lex
.getDocString();
80 if (master_
== "none")
85 labelstring_
= lex
.getDocString();
86 labelstringappendix_
= labelstring_
;
88 case CT_LABELSTRING_APPENDIX
:
90 labelstringappendix_
= lex
.getDocString();
98 // Here if have a full counter if getout == true
100 LYXERR0("No End tag found for counter!");
105 void Counter::set(int v
)
111 void Counter::addto(int v
)
117 int Counter::value() const
129 void Counter::reset()
135 docstring
const & Counter::master() const
141 docstring
const & Counter::labelString(bool in_appendix
) const
143 return in_appendix
? labelstringappendix_
: labelstring_
;
147 Counter::StringMap
& Counter::flatLabelStrings(bool in_appendix
) const
149 return in_appendix
? flatlabelstringappendix_
: flatlabelstring_
;
153 void Counters::newCounter(docstring
const & newc
,
154 docstring
const & masterc
,
155 docstring
const & ls
,
156 docstring
const & lsa
)
158 if (!masterc
.empty() && !hasCounter(masterc
)) {
159 lyxerr
<< "Master counter does not exist: "
164 counterList_
[newc
] = Counter(masterc
, ls
, lsa
);
168 bool Counters::hasCounter(docstring
const & c
) const
170 return counterList_
.find(c
) != counterList_
.end();
174 bool Counters::read(Lexer
& lex
, docstring
const & name
, bool makenew
)
176 if (hasCounter(name
)) {
177 LYXERR(Debug::TCLASS
, "Reading existing counter " << to_utf8(name
));
178 return counterList_
[name
].read(lex
);
181 LYXERR(Debug::TCLASS
, "Reading new counter " << to_utf8(name
));
183 bool success
= cnt
.read(lex
);
184 // if makenew is false, we will just discard what we read
185 if (success
&& makenew
)
186 counterList_
[name
] = cnt
;
188 LYXERR0("Error reading counter `" << name
<< "'!");
193 void Counters::set(docstring
const & ctr
, int const val
)
195 CounterList::iterator
const it
= counterList_
.find(ctr
);
196 if (it
== counterList_
.end()) {
197 lyxerr
<< "set: Counter does not exist: "
198 << to_utf8(ctr
) << endl
;
205 void Counters::addto(docstring
const & ctr
, int const val
)
207 CounterList::iterator
const it
= counterList_
.find(ctr
);
208 if (it
== counterList_
.end()) {
209 lyxerr
<< "addto: Counter does not exist: "
210 << to_utf8(ctr
) << endl
;
213 it
->second
.addto(val
);
217 int Counters::value(docstring
const & ctr
) const
219 CounterList::const_iterator
const cit
= counterList_
.find(ctr
);
220 if (cit
== counterList_
.end()) {
221 lyxerr
<< "value: Counter does not exist: "
222 << to_utf8(ctr
) << endl
;
225 return cit
->second
.value();
229 void Counters::step(docstring
const & ctr
)
231 CounterList::iterator it
= counterList_
.find(ctr
);
232 if (it
== counterList_
.end()) {
233 lyxerr
<< "step: Counter does not exist: "
234 << to_utf8(ctr
) << endl
;
239 it
= counterList_
.begin();
240 CounterList::iterator
const end
= counterList_
.end();
241 for (; it
!= end
; ++it
) {
242 if (it
->second
.master() == ctr
) {
249 void Counters::reset()
253 current_float_
.erase();
254 CounterList::iterator it
= counterList_
.begin();
255 CounterList::iterator
const end
= counterList_
.end();
256 for (; it
!= end
; ++it
)
261 void Counters::reset(docstring
const & match
)
263 LASSERT(!match
.empty(), /**/);
265 CounterList::iterator it
= counterList_
.begin();
266 CounterList::iterator end
= counterList_
.end();
267 for (; it
!= end
; ++it
) {
268 if (it
->first
.find(match
) != string::npos
)
274 void Counters::copy(Counters
& from
, Counters
& to
, docstring
const & match
)
276 CounterList::iterator it
= counterList_
.begin();
277 CounterList::iterator end
= counterList_
.end();
278 for (; it
!= end
; ++it
) {
279 if (it
->first
.find(match
) != string::npos
|| match
== "") {
280 to
.set(it
->first
, from
.value(it
->first
));
288 char loweralphaCounter(int const n
)
296 char alphaCounter(int const n
)
304 char hebrewCounter(int const n
)
306 static const char hebrew
[22] = {
307 '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7', '\xe8',
308 '\xe9', '\xeb', '\xec', '\xee', '\xf0', '\xf1', '\xf2', '\xf4', '\xf6',
309 '\xf7', '\xf8', '\xf9', '\xfa'
314 return hebrew
[n
- 1];
319 // On the special cases, see http://mathworld.wolfram.com/RomanNumerals.html
320 // and for a list of roman numerals up to and including 3999, see
321 // http://www.research.att.com/~njas/sequences/a006968.txt. (Thanks to Joost
323 docstring
const romanCounter(int const n
)
325 static char const * const ones
[9] = {
326 "I", "II", "III", "IV", "V",
327 "VI", "VII", "VIII", "IX"
330 static char const * const tens
[9] = {
331 "X", "XX", "XXX", "XL", "L",
332 "LX", "LXX", "LXXX", "XC"
335 static char const * const hunds
[9] = {
336 "C", "CC", "CCC", "CD", "D",
337 "DC", "DCC", "DCCC", "CM"
340 if (n
> 1000 || n
< 1)
341 return from_ascii("??");
355 int hundreds
= val
/ 100;
356 roman
= hunds
[hundreds
- 1];
363 roman
= roman
+ "XC";
367 int tensnum
= val
/ 10;
368 roman
= roman
+ tens
[tensnum
- 1];
373 roman
= roman
+ ones
[val
-1];
375 return from_ascii(roman
);
379 docstring
const lowerromanCounter(int const n
)
381 return lowercase(romanCounter(n
));
387 docstring
Counters::labelItem(docstring
const & ctr
,
388 docstring
const & numbertype
) const
390 CounterList::const_iterator
const cit
= counterList_
.find(ctr
);
391 if (cit
== counterList_
.end()) {
394 << " does not exist." << endl
;
398 int val
= cit
->second
.value();
400 if (numbertype
== "hebrew")
401 return docstring(1, hebrewCounter(val
));
403 if (numbertype
== "alph")
404 return docstring(1, loweralphaCounter(val
));
406 if (numbertype
== "Alph")
407 return docstring(1, alphaCounter(val
));
409 if (numbertype
== "roman")
410 return lowerromanCounter(val
);
412 if (numbertype
== "Roman")
413 return romanCounter(val
);
415 return convert
<docstring
>(val
);
419 docstring
Counters::theCounter(docstring
const & counter
,
420 string
const & lang
) const
422 CounterList::const_iterator it
= counterList_
.find(counter
);
423 if (it
== counterList_
.end())
424 return from_ascii("??");
425 Counter
const & ctr
= it
->second
;
426 Counter::StringMap
& sm
= ctr
.flatLabelStrings(appendix());
427 Counter::StringMap::iterator smit
= sm
.find(lang
);
428 if (smit
!= sm
.end())
429 return counterLabel(smit
->second
, lang
);
431 vector
<docstring
> callers
;
432 docstring
const & fls
= flattenLabelString(counter
, appendix(),
435 return counterLabel(fls
, lang
);
439 docstring
Counters::flattenLabelString(docstring
const & counter
,
442 vector
<docstring
> & callers
) const
446 if (find(callers
.begin(), callers
.end(), counter
) != callers
.end()) {
447 // recursion detected
448 lyxerr
<< "Warning: Recursion in label for counter `"
449 << counter
<< "' detected"
451 return from_ascii("??");
454 CounterList::const_iterator it
= counterList_
.find(counter
);
455 if (it
== counterList_
.end())
456 return from_ascii("??");
457 Counter
const & c
= it
->second
;
459 docstring ls
= translateIfPossible(c
.labelString(in_appendix
), lang
);
461 callers
.push_back(counter
);
463 if (!c
.master().empty())
464 ls
= flattenLabelString(c
.master(), in_appendix
, lang
, callers
)
467 return ls
+ from_ascii("\\arabic{") + counter
+ "}";
471 //lyxerr << "ls=" << to_utf8(ls) << endl;
472 size_t const i
= ls
.find(from_ascii("\\the"), 0);
473 if (i
== docstring::npos
)
475 size_t const j
= i
+ 4;
477 while (k
< ls
.size() && lowercase(ls
[k
]) >= 'a'
478 && lowercase(ls
[k
]) <= 'z')
480 docstring
const newc
= ls
.substr(j
, k
- j
);
481 docstring
const repl
= flattenLabelString(newc
, in_appendix
,
483 ls
.replace(i
, k
- j
+ 4, repl
);
491 docstring
Counters::counterLabel(docstring
const & format
,
492 string
const & lang
) const
494 docstring label
= format
;
496 // FIXME: Using regexps would be better, but we compile boost without
497 // wide regexps currently.
498 docstring
const the
= from_ascii("\\the");
500 //lyxerr << "label=" << label << endl;
501 size_t const i
= label
.find(the
, 0);
502 if (i
== docstring::npos
)
504 size_t const j
= i
+ 4;
506 while (k
< label
.size() && lowercase(label
[k
]) >= 'a'
507 && lowercase(label
[k
]) <= 'z')
509 docstring
const newc(label
, j
, k
- j
);
510 label
.replace(i
, k
- i
, theCounter(newc
, lang
));
513 //lyxerr << "label=" << label << endl;
515 size_t const i
= label
.find('\\', 0);
516 if (i
== docstring::npos
)
518 size_t const j
= label
.find('{', i
+ 1);
519 if (j
== docstring::npos
)
521 size_t const k
= label
.find('}', j
+ 1);
522 if (k
== docstring::npos
)
524 docstring
const numbertype(label
, i
+ 1, j
- i
- 1);
525 docstring
const counter(label
, j
+ 1, k
- j
- 1);
526 label
.replace(i
, k
+ 1 - i
, labelItem(counter
, numbertype
));
528 //lyxerr << "DONE! label=" << label << endl;