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
)
176 if (hasCounter(name
)) {
177 LYXERR(Debug::TCLASS
, "Reading existing counter " << to_utf8(name
));
178 return counterList_
[name
].read(lex
);
180 LYXERR(Debug::TCLASS
, "Reading new counter " << to_utf8(name
));
182 bool success
= cnt
.read(lex
);
184 counterList_
[name
] = cnt
;
186 LYXERR0("Error reading counter `" << name
<< "'!");
191 void Counters::set(docstring
const & ctr
, int const val
)
193 CounterList::iterator
const it
= counterList_
.find(ctr
);
194 if (it
== counterList_
.end()) {
195 lyxerr
<< "set: Counter does not exist: "
196 << to_utf8(ctr
) << endl
;
203 void Counters::addto(docstring
const & ctr
, int const val
)
205 CounterList::iterator
const it
= counterList_
.find(ctr
);
206 if (it
== counterList_
.end()) {
207 lyxerr
<< "addto: Counter does not exist: "
208 << to_utf8(ctr
) << endl
;
211 it
->second
.addto(val
);
215 int Counters::value(docstring
const & ctr
) const
217 CounterList::const_iterator
const cit
= counterList_
.find(ctr
);
218 if (cit
== counterList_
.end()) {
219 lyxerr
<< "value: Counter does not exist: "
220 << to_utf8(ctr
) << endl
;
223 return cit
->second
.value();
227 void Counters::step(docstring
const & ctr
)
229 CounterList::iterator it
= counterList_
.find(ctr
);
230 if (it
== counterList_
.end()) {
231 lyxerr
<< "step: Counter does not exist: "
232 << to_utf8(ctr
) << endl
;
237 it
= counterList_
.begin();
238 CounterList::iterator
const end
= counterList_
.end();
239 for (; it
!= end
; ++it
) {
240 if (it
->second
.master() == ctr
) {
247 void Counters::reset()
251 current_float_
.erase();
252 CounterList::iterator it
= counterList_
.begin();
253 CounterList::iterator
const end
= counterList_
.end();
254 for (; it
!= end
; ++it
)
259 void Counters::reset(docstring
const & match
)
261 LASSERT(!match
.empty(), /**/);
263 CounterList::iterator it
= counterList_
.begin();
264 CounterList::iterator end
= counterList_
.end();
265 for (; it
!= end
; ++it
) {
266 if (it
->first
.find(match
) != string::npos
)
272 void Counters::copy(Counters
& from
, Counters
& to
, docstring
const & match
)
274 CounterList::iterator it
= counterList_
.begin();
275 CounterList::iterator end
= counterList_
.end();
276 for (; it
!= end
; ++it
) {
277 if (it
->first
.find(match
) != string::npos
|| match
== "") {
278 to
.set(it
->first
, from
.value(it
->first
));
286 char loweralphaCounter(int const n
)
294 char alphaCounter(int const n
)
302 char hebrewCounter(int const n
)
304 static const char hebrew
[22] = {
305 '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7', '\xe8',
306 '\xe9', '\xeb', '\xec', '\xee', '\xf0', '\xf1', '\xf2', '\xf4', '\xf6',
307 '\xf7', '\xf8', '\xf9', '\xfa'
312 return hebrew
[n
- 1];
317 // On the special cases, see http://mathworld.wolfram.com/RomanNumerals.html
318 // and for a list of roman numerals up to and including 3999, see
319 // http://www.research.att.com/~njas/sequences/a006968.txt. (Thanks to Joost
321 docstring
const romanCounter(int const n
)
323 static char const * const ones
[9] = {
324 "I", "II", "III", "IV", "V",
325 "VI", "VII", "VIII", "IX"
328 static char const * const tens
[9] = {
329 "X", "XX", "XXX", "XL", "L",
330 "LX", "LXX", "LXXX", "XC"
333 static char const * const hunds
[9] = {
334 "C", "CC", "CCC", "CD", "D",
335 "DC", "DCC", "DCCC", "CM"
338 if (n
> 1000 || n
< 1)
339 return from_ascii("??");
353 int hundreds
= val
/ 100;
354 roman
= hunds
[hundreds
- 1];
361 roman
= roman
+ "XC";
365 int tensnum
= val
/ 10;
366 roman
= roman
+ tens
[tensnum
- 1];
371 roman
= roman
+ ones
[val
-1];
373 return from_ascii(roman
);
377 docstring
const lowerromanCounter(int const n
)
379 return lowercase(romanCounter(n
));
385 docstring
Counters::labelItem(docstring
const & ctr
,
386 docstring
const & numbertype
) const
388 CounterList::const_iterator
const cit
= counterList_
.find(ctr
);
389 if (cit
== counterList_
.end()) {
392 << " does not exist." << endl
;
396 int val
= cit
->second
.value();
398 if (numbertype
== "hebrew")
399 return docstring(1, hebrewCounter(val
));
401 if (numbertype
== "alph")
402 return docstring(1, loweralphaCounter(val
));
404 if (numbertype
== "Alph")
405 return docstring(1, alphaCounter(val
));
407 if (numbertype
== "roman")
408 return lowerromanCounter(val
);
410 if (numbertype
== "Roman")
411 return romanCounter(val
);
413 return convert
<docstring
>(val
);
417 docstring
Counters::theCounter(docstring
const & counter
,
418 string
const & lang
) const
420 CounterList::const_iterator it
= counterList_
.find(counter
);
421 if (it
== counterList_
.end())
422 return from_ascii("??");
423 Counter
const & ctr
= it
->second
;
424 Counter::StringMap
& sm
= ctr
.flatLabelStrings(appendix());
425 Counter::StringMap::iterator smit
= sm
.find(lang
);
426 if (smit
!= sm
.end())
427 return counterLabel(smit
->second
, lang
);
429 vector
<docstring
> callers
;
430 docstring
const & fls
= flattenLabelString(counter
, appendix(),
433 return counterLabel(fls
, lang
);
437 docstring
Counters::flattenLabelString(docstring
const & counter
,
440 vector
<docstring
> & callers
) const
444 if (find(callers
.begin(), callers
.end(), counter
) != callers
.end()) {
445 // recursion detected
446 lyxerr
<< "Warning: Recursion in label for counter `"
447 << counter
<< "' detected"
449 return from_ascii("??");
452 CounterList::const_iterator it
= counterList_
.find(counter
);
453 if (it
== counterList_
.end())
454 return from_ascii("??");
455 Counter
const & c
= it
->second
;
457 docstring ls
= translateIfPossible(c
.labelString(in_appendix
), lang
);
459 callers
.push_back(counter
);
461 if (!c
.master().empty())
462 ls
= flattenLabelString(c
.master(), in_appendix
, lang
, callers
)
465 return ls
+ from_ascii("\\arabic{") + counter
+ "}";
469 //lyxerr << "ls=" << to_utf8(ls) << endl;
470 size_t const i
= ls
.find(from_ascii("\\the"), 0);
471 if (i
== docstring::npos
)
473 size_t const j
= i
+ 4;
475 while (k
< ls
.size() && lowercase(ls
[k
]) >= 'a'
476 && lowercase(ls
[k
]) <= 'z')
478 docstring
const newc
= ls
.substr(j
, k
- j
);
479 docstring
const repl
= flattenLabelString(newc
, in_appendix
,
481 ls
.replace(i
, k
- j
+ 4, repl
);
489 docstring
Counters::counterLabel(docstring
const & format
,
490 string
const & lang
) const
492 docstring label
= format
;
494 // FIXME: Using regexps would be better, but we compile boost without
495 // wide regexps currently.
496 docstring
const the
= from_ascii("\\the");
498 //lyxerr << "label=" << label << endl;
499 size_t const i
= label
.find(the
, 0);
500 if (i
== docstring::npos
)
502 size_t const j
= i
+ 4;
504 while (k
< label
.size() && lowercase(label
[k
]) >= 'a'
505 && lowercase(label
[k
]) <= 'z')
507 docstring
const newc(label
, j
, k
- j
);
508 label
.replace(i
, k
- i
, theCounter(newc
, lang
));
511 //lyxerr << "label=" << label << endl;
513 size_t const i
= label
.find('\\', 0);
514 if (i
== docstring::npos
)
516 size_t const j
= label
.find('{', i
+ 1);
517 if (j
== docstring::npos
)
519 size_t const k
= label
.find('}', j
+ 1);
520 if (k
== docstring::npos
)
522 docstring
const numbertype(label
, i
+ 1, j
- i
- 1);
523 docstring
const counter(label
, j
+ 1, k
- j
- 1);
524 label
.replace(i
, k
+ 1 - i
, labelItem(counter
, numbertype
));
526 //lyxerr << "DONE! label=" << label << endl;