#include "Buffer.h"
#include "BufferParams.h"
#include "buffer_funcs.h"
+#include "Citation.h"
#include "Encoding.h"
#include "InsetIterator.h"
#include "Language.h"
+#include "output_xhtml.h"
#include "Paragraph.h"
#include "TextClass.h"
#include "TocBackend.h"
{}
-docstring const BibTeXInfo::getAbbreviatedAuthor(bool jurabib_style, string lang) const
+docstring const BibTeXInfo::getAuthorList(
+ Buffer const * buf, bool full, bool forceshort) const
{
+ // Maxnames treshold depend on engine
+ size_t maxnames = buf ?
+ buf->params().documentClass().max_citenames() : 2;
+
if (!is_bibtex_) {
docstring const opt = label();
if (opt.empty())
return authors;
}
- docstring author = convertLaTeXCommands(operator[]("author"));
+ docstring author = operator[]("author");
if (author.empty()) {
- author = convertLaTeXCommands(operator[]("editor"));
+ author = operator[]("editor");
if (author.empty())
return author;
}
+ "/" + familyName(authors[1]);
if (authors.size() == 3)
shortauthor += "/" + familyName(authors[2]);
- return shortauthor;
+ return convertLaTeXCommands(shortauthor);
}
+ docstring retval;
- if (authors.size() == 2)
- return bformat(translateIfPossible(from_ascii("%1$s and %2$s"), lang),
- familyName(authors[0]), familyName(authors[1]));
-
- if (authors.size() > 2)
- return bformat(translateIfPossible(from_ascii("%1$s et al."), lang),
- familyName(authors[0]));
+ CiteEngineType const engine_type = buf ? buf->params().citeEngineType()
+ : ENGINE_TYPE_DEFAULT;
+
+ // These are defined in the styles
+ string const etal =
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "_etal")
+ : " et al.";
+ string const namesep =
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "_namesep")
+ : ", ";
+ string const lastnamesep =
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "_lastnamesep")
+ : ", and ";
+ string const pairnamesep =
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "_pairnamesep")
+ : " and ";
+
+ // Shorten the list (with et al.) if forceshort is set
+ // and the list can actually be shorten, else if maxcitenames
+ // is passed and full is not set.
+ bool shorten = forceshort && authors.size() > 1;
+ vector<docstring>::const_iterator it = authors.begin();
+ vector<docstring>::const_iterator en = authors.end();
+ for (size_t i = 0; it != en; ++it, ++i) {
+ if (i >= maxnames && !full) {
+ shorten = true;
+ break;
+ }
+ if (*it == "others") {
+ retval += buf ? buf->B_(etal) : from_ascii(etal);
+ break;
+ }
+ if (i > 0 && i == authors.size() - 1) {
+ if (authors.size() == 2)
+ retval += buf ? buf->B_(pairnamesep) : from_ascii(pairnamesep);
+ else
+ retval += buf ? buf->B_(lastnamesep) : from_ascii(lastnamesep);
+ } else if (i > 0)
+ retval += buf ? buf->B_(namesep) : from_ascii(namesep);
+ retval += familyName(*it);
+ }
+ if (shorten)
+ retval = familyName(authors[0]) + (buf ? buf->B_(etal) : from_ascii(etal));
- return familyName(authors[0]);
+ return convertLaTeXCommands(retval);
}
docstring const BibTeXInfo::getYear() const
{
- if (is_bibtex_)
- return operator[]("year");
+ if (is_bibtex_) {
+ // first try legacy year field
+ docstring year = operator[]("year");
+ if (!year.empty())
+ return year;
+ // now try biblatex's date field
+ year = operator[]("date");
+ // Format is [-]YYYY-MM-DD*/[-]YYYY-MM-DD*
+ // We only want the years.
+ static regex const yreg("[-]?([\\d]{4}).*");
+ static regex const ereg(".*/[-]?([\\d]{4}).*");
+ smatch sm;
+ string const date = to_utf8(year);
+ regex_match(date, sm, yreg);
+ year = from_ascii(sm[1]);
+ // check for an endyear
+ if (regex_match(date, sm, ereg))
+ year += char_type(0x2013) + from_ascii(sm[1]);
+ return year;
+ }
docstring const opt = label();
if (opt.empty())
}
-docstring const BibTeXInfo::getXRef() const
-{
- if (!is_bibtex_)
- return docstring();
- return operator[]("crossref");
-}
-
-
namespace {
-string parseOptions(string const & format, string & optkey,
- string & ifpart, string & elsepart);
+docstring parseOptions(docstring const & format, string & optkey,
+ docstring & ifpart, docstring & elsepart);
// Calls parseOptions to deal with an embedded option, such as:
// {%number%[[, no.~%number%]]}
// which must appear at the start of format. ifelsepart gets the
// whole of the option, and we return what's left after the option.
// we return format if there is an error.
-string parseEmbeddedOption(string const & format, string & ifelsepart)
+docstring parseEmbeddedOption(docstring const & format, docstring & ifelsepart)
{
LASSERT(format[0] == '{' && format[1] == '%', return format);
string optkey;
- string ifpart;
- string elsepart;
- string const rest = parseOptions(format, optkey, ifpart, elsepart);
+ docstring ifpart;
+ docstring elsepart;
+ docstring const rest = parseOptions(format, optkey, ifpart, elsepart);
if (format == rest) { // parse error
LYXERR0("ERROR! Couldn't parse `" << format <<"'.");
return format;
}
- LASSERT(rest.size() <= format.size(), /* */);
+ LASSERT(rest.size() <= format.size(),
+ { ifelsepart = docstring(); return format; });
ifelsepart = format.substr(0, format.size() - rest.size());
- return rest;
+ return rest;
}
// Gets a "clause" from a format string, where the clause is
// delimited by '[[' and ']]'. Returns what is left after the
// clause is removed, and returns format if there is an error.
-string getClause(string const & format, string & clause)
+docstring getClause(docstring const & format, docstring & clause)
{
- string fmt = format;
+ docstring fmt = format;
// remove '[['
fmt = fmt.substr(2);
// we'll remove characters from the front of fmt as we
}
// check for an embedded option
if (fmt[0] == '{' && fmt.size() > 1 && fmt[1] == '%') {
- string part;
- string const rest = parseEmbeddedOption(fmt, part);
+ docstring part;
+ docstring const rest = parseEmbeddedOption(fmt, part);
if (fmt == rest) {
LYXERR0("ERROR! Couldn't parse embedded option in `" << format <<"'.");
return format;
// format parameter. puts the parsed bits in optkey, ifpart, and
// elsepart and returns what's left after the option is removed.
// if there's an error, it returns format itself.
-string parseOptions(string const & format, string & optkey,
- string & ifpart, string & elsepart)
+docstring parseOptions(docstring const & format, string & optkey,
+ docstring & ifpart, docstring & elsepart)
{
LASSERT(format[0] == '{' && format[1] == '%', return format);
// strip '{%'
- string fmt = format.substr(2);
+ docstring fmt = format.substr(2);
size_t pos = fmt.find('%'); // end of key
if (pos == string::npos) {
LYXERR0("Error parsing `" << format <<"'. Can't find end of key.");
return format;
}
- optkey = fmt.substr(0,pos);
+ optkey = to_utf8(fmt.substr(0, pos));
fmt = fmt.substr(pos + 1);
// [[format]] should be next
if (fmt[0] != '[' || fmt[1] != '[') {
return format;
}
- string curfmt = fmt;
+ docstring curfmt = fmt;
fmt = getClause(curfmt, ifpart);
if (fmt == curfmt) {
LYXERR0("Error parsing `" << format <<"'. Couldn't get if clause.");
} // anon namespace
-
-docstring BibTeXInfo::expandFormat(string const & format,
- BibTeXInfo const * const xref, int & counter, Buffer const & buf,
- docstring before, docstring after, docstring dialog, bool next) const
+/* FIXME
+Bug #9131 revealed an oddity in how we are generating citation information
+when more than one key is given. We end up building a longer and longer format
+string as we go, which we then have to re-parse, over and over and over again,
+rather than generating the information for the individual keys and then putting
+all of that together. We do that to deal with the way separators work, from what
+I can tell, but it still feels like a hack. Fixing this would require quite a
+bit of work, however.
+*/
+docstring BibTeXInfo::expandFormat(docstring const & format,
+ BibTeXInfoList const xrefs, int & counter, Buffer const & buf,
+ CiteItem const & ci, bool next, bool second) const
{
// incorrect use of macros could put us in an infinite loop
- static int max_passes = 5000;
- docstring ret; // return value
+ static int const max_passes = 5000;
+ // the use of overly large keys can lead to performance problems, due
+ // to eventual attempts to convert LaTeX macros to unicode. See bug
+ // #8944. This is perhaps not the best solution, but it will have to
+ // do for now.
+ static size_t const max_keysize = 128;
+ odocstringstream ret; // return value
string key;
- string lang = buf.params().language->code();
bool scanning_key = false;
bool scanning_rich = false;
CiteEngineType const engine_type = buf.params().citeEngineType();
- string fmt = format;
+ docstring fmt = format;
// we'll remove characters from the front of fmt as we
// deal with them
while (!fmt.empty()) {
- if (counter++ > max_passes) {
+ if (counter > max_passes) {
LYXERR0("Recursion limit reached while parsing `"
<< format << "'.");
return _("ERROR!");
// so we replace the key with its value, which may be empty
if (key[0] == '!') {
// macro
- // FIXME: instead of passing the buf, just past the macros
- // FIXME: and the language code
string const val =
buf.params().documentClass().getCiteMacro(engine_type, key);
- fmt = val + fmt.substr(1);
+ fmt = from_utf8(val) + fmt.substr(1);
+ counter += 1;
continue;
} else if (key[0] == '_') {
// a translatable bit
string const val =
buf.params().documentClass().getCiteMacro(engine_type, key);
docstring const trans =
- translateIfPossible(from_utf8(val), lang);
- ret += trans;
+ translateIfPossible(from_utf8(val), buf.params().language->code());
+ ret << trans;
} else {
docstring const val =
- getValueForKey(key, before, after, dialog, xref, lang);
- ret += val;
+ getValueForKey(key, buf, ci, xrefs, max_keysize);
+ if (!scanning_rich)
+ ret << from_ascii("{!<span class=\"bib-" + key + "\">!}");
+ ret << val;
+ if (!scanning_rich)
+ ret << from_ascii("{!</span>!}");
}
} else {
// beginning of key
if (fmt[1] == '%') {
// it is the beginning of an optional format
string optkey;
- string ifpart;
- string elsepart;
- string const newfmt =
+ docstring ifpart;
+ docstring elsepart;
+ docstring const newfmt =
parseOptions(fmt, optkey, ifpart, elsepart);
if (newfmt == fmt) // parse error
return _("ERROR!");
fmt = newfmt;
docstring const val =
- getValueForKey(optkey, before, after, dialog, xref, lang);
+ getValueForKey(optkey, buf, ci, xrefs);
if (optkey == "next" && next)
- ret += from_utf8(ifpart); // without expansion
- else if (!val.empty())
- ret += expandFormat(ifpart, xref, counter, buf,
- before, after, dialog, next);
- else if (!elsepart.empty())
- ret += expandFormat(elsepart, xref, counter, buf,
- before, after, dialog, next);
+ ret << ifpart; // without expansion
+ else if (!val.empty()) {
+ int newcounter = 0;
+ ret << expandFormat(ifpart, xrefs, newcounter, buf,
+ ci, next);
+ } else if (!elsepart.empty()) {
+ int newcounter = 0;
+ ret << expandFormat(elsepart, xrefs, newcounter, buf,
+ ci, next);
+ }
// fmt will have been shortened for us already
continue;
}
// beginning of rich text
scanning_rich = true;
fmt = fmt.substr(2);
- ret += from_ascii("{!");
+ ret << from_ascii("{!");
continue;
}
}
// we are here if '{' was not followed by % or !.
// So it's just a character.
- ret += thischar;
+ ret << thischar;
}
else if (scanning_rich && thischar == '!'
&& fmt.size() > 1 && fmt[1] == '}') {
// end of rich text
scanning_rich = false;
fmt = fmt.substr(2);
- ret += from_ascii("!}");
+ ret << from_ascii("!}");
continue;
}
else if (scanning_key)
key += char(thischar);
- else
- ret += thischar;
+ else {
+ try {
+ ret.put(thischar);
+ } catch (EncodingException & /* e */) {
+ LYXERR0("Uncodable character '" << docstring(1, thischar) << " in citation label!");
+ }
+ }
fmt = fmt.substr(1);
} // for loop
if (scanning_key) {
LYXERR0("Never found end of rich text in `" << format << "'!");
return _("ERROR!");
}
- return ret;
+ return ret.str();
}
-docstring const & BibTeXInfo::getInfo(BibTeXInfo const * const xref,
- Buffer const & buf, bool richtext) const
+docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
+ Buffer const & buf, CiteItem const & ci) const
{
- if (!info_.empty())
+ bool const richtext = ci.richtext;
+
+ if (!richtext && !info_.empty())
return info_;
+ if (richtext && !info_richtext_.empty())
+ return info_richtext_;
if (!is_bibtex_) {
BibTeXInfo::const_iterator it = find(from_ascii("ref"));
CiteEngineType const engine_type = buf.params().citeEngineType();
DocumentClass const & dc = buf.params().documentClass();
- string const & format = dc.getCiteFormat(engine_type, to_utf8(entry_type_));
+ docstring const & format =
+ from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
int counter = 0;
- info_ = expandFormat(format, xref, counter, buf,
- docstring(), docstring(), docstring(), false);
+ info_ = expandFormat(format, xrefs, counter, buf,
+ ci, false, false);
+
+ if (info_.empty()) {
+ // this probably shouldn't happen
+ return info_;
+ }
+
+ if (richtext) {
+ info_richtext_ = convertLaTeXCommands(processRichtext(info_, true));
+ return info_richtext_;
+ }
- if (!info_.empty())
- info_ = convertLaTeXCommands(info_);
+ info_ = convertLaTeXCommands(processRichtext(info_, false));
return info_;
}
-docstring const BibTeXInfo::getLabel(BibTeXInfo const * const xref,
- Buffer const & buf, string const & format, bool richtext,
- docstring before, docstring after, docstring dialog, bool next) const
+docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs,
+ Buffer const & buf, docstring const & format,
+ CiteItem const & ci, bool next, bool second) const
{
docstring loclabel;
- /*
- if (!is_bibtex_) {
- BibTeXInfo::const_iterator it = find(from_ascii("ref"));
- label_ = it->second;
- return label_;
- }
- */
-
int counter = 0;
- loclabel = expandFormat(format, xref, counter, buf,
- before, after, dialog, next);
+ loclabel = expandFormat(format, xrefs, counter, buf, ci, next, second);
if (!loclabel.empty() && !next) {
- loclabel = processRichtext(loclabel, richtext);
+ loclabel = processRichtext(loclabel, ci.richtext);
loclabel = convertLaTeXCommands(loclabel);
}
+
return loclabel;
}
}
-docstring BibTeXInfo::getValueForKey(string const & key,
- docstring const & before, docstring const & after, docstring const & dialog,
- BibTeXInfo const * const xref, string lang) const
+docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
+ CiteItem const & ci, BibTeXInfoList const xrefs, size_t maxsize) const
{
+ // anything less is pointless
+ LASSERT(maxsize >= 16, maxsize = 16);
+ string key = oldkey;
+ bool cleanit = false;
+ if (prefixIs(oldkey, "clean:")) {
+ key = oldkey.substr(6);
+ cleanit = true;
+ }
+
docstring ret = operator[](key);
- if (ret.empty() && xref)
- ret = (*xref)[key];
- if (!ret.empty())
- return ret;
- // some special keys
- // FIXME: dialog, textbefore and textafter have nothing to do with this
- if (key == "dialog")
- return dialog;
- else if (key == "entrytype")
- return entry_type_;
- else if (key == "key")
- return bib_key_;
- else if (key == "label")
- return label_;
- else if (key == "abbrvauthor")
- // Special key to provide abbreviated author names.
- return getAbbreviatedAuthor(false, lang);
- else if (key == "shortauthor")
- // When shortauthor is not defined, jurabib automatically
- // provides jurabib-style abbreviated author names. We do
- // this as well.
- return getAbbreviatedAuthor(true, lang);
- else if (key == "shorttitle") {
- // When shorttitle is not defined, jurabib uses for `article'
- // and `periodical' entries the form `journal volume [year]'
- // and for other types of entries it uses the `title' field.
- if (entry_type_ == "article" || entry_type_ == "periodical")
- return operator[]("journal") + " " + operator[]("volume")
- + " [" + operator[]("year") + "]";
- else
- return operator[]("title");
- } else if (key == "textbefore")
- return before;
- else if (key == "textafter")
- return after;
- else if (key == "year")
- return getYear();
+ if (ret.empty() && !xrefs.empty()) {
+ vector<BibTeXInfo const *>::const_iterator it = xrefs.begin();
+ vector<BibTeXInfo const *>::const_iterator en = xrefs.end();
+ for (; it != en; ++it) {
+ if (*it && !(**it)[key].empty()) {
+ ret = (**it)[key];
+ break;
+ }
+ }
+ }
+ if (ret.empty()) {
+ // some special keys
+ // FIXME: dialog, textbefore and textafter have nothing to do with this
+ if (key == "dialog" && ci.context == CiteItem::Dialog)
+ ret = from_ascii("x"); // any non-empty string will do
+ else if (key == "ifstar" && ci.Starred)
+ ret = from_ascii("x"); // any non-empty string will do
+ else if (key == "entrytype")
+ ret = entry_type_;
+ else if (key == "key")
+ ret = bib_key_;
+ else if (key == "label")
+ ret = label_;
+ else if (key == "modifier" && modifier_ != 0)
+ ret = modifier_;
+ else if (key == "numericallabel")
+ ret = cite_number_;
+ else if (key == "shortauthor")
+ // When shortauthor is not defined, jurabib automatically
+ // provides jurabib-style abbreviated author names. We do
+ // this as well.
+ ret = getAbbreviatedAuthor(&buf, true);
+ else if (key == "shorttitle") {
+ // When shorttitle is not defined, jurabib uses for `article'
+ // and `periodical' entries the form `journal volume [year]'
+ // and for other types of entries it uses the `title' field.
+ if (entry_type_ == "article" || entry_type_ == "periodical")
+ ret = operator[]("journal") + " " + operator[]("volume")
+ + " [" + operator[]("year") + "]";
+ else
+ ret = operator[]("title");
+ else if (key == "abbrvauthor") {
+ // Special key to provide abbreviated author names,
+ // with respect to maxcitenames.
+ ret = getAuthorList(&buf, false, false);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
+ } else if (key == "fullauthor") {
+ // Return a full author list
+ ret = getAuthorList(&buf, true, false);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
+ } else if (key == "forceabbrvauthor") {
+ // Special key to provide abbreviated author names,
+ // irrespective of maxcitenames.
+ ret = getAuthorList(&buf, false, true);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
+ } else if (key == "bibentry") {
+ // Special key to provide the full bibliography entry: see getInfo()
+ CiteEngineType const engine_type = buf.params().citeEngineType();
+ DocumentClass const & dc = buf.params().documentClass();
+ docstring const & format =
+ from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
+ int counter = 0;
+ ret = expandFormat(format, xrefs, counter, buf, ci, false, false);
+ } else if (key == "textbefore")
+ ret = ci.textBefore;
+ else if (key == "textafter")
+ ret = ci.textAfter;
+ else if (key == "year")
+ ret = getYear();
+ }
+
+ if (cleanit)
+ ret = html::cleanAttr(ret);
+
+ // make sure it is not too big
+ support::truncateWithEllipsis(ret, maxsize);
return ret;
}
} // namespace anon
+vector<docstring> const BiblioInfo::getXRefs(BibTeXInfo const & data, bool const nested) const
+{
+ vector<docstring> result;
+ if (!data.isBibTeX())
+ return result;
+ // Legacy crossref field. This is not nestable.
+ if (!nested && !data["crossref"].empty()) {
+ docstring const xrefkey = data["crossref"];
+ result.push_back(xrefkey);
+ // However, check for nested xdatas
+ BiblioInfo::const_iterator it = find(xrefkey);
+ if (it != end()) {
+ BibTeXInfo const & xref = it->second;
+ vector<docstring> const nxdata = getXRefs(xref, true);
+ if (!nxdata.empty())
+ result.insert(result.end(), nxdata.begin(), nxdata.end());
+ }
+ }
+ // Biblatex's xdata field. Infinitely nestable.
+ // XData field can consist of a comma-separated list of keys
+ vector<docstring> const xdatakeys = getVectorFromString(data["xdata"]);
+ if (!xdatakeys.empty()) {
+ vector<docstring>::const_iterator xit = xdatakeys.begin();
+ vector<docstring>::const_iterator xen = xdatakeys.end();
+ for (; xit != xen; ++xit) {
+ docstring const xdatakey = *xit;
+ result.push_back(xdatakey);
+ BiblioInfo::const_iterator it = find(xdatakey);
+ if (it != end()) {
+ BibTeXInfo const & xdata = it->second;
+ vector<docstring> const nxdata = getXRefs(xdata, true);
+ if (!nxdata.empty())
+ result.insert(result.end(), nxdata.begin(), nxdata.end());
+ }
+ }
+ }
+ return result;
+}
+
+
vector<docstring> const BiblioInfo::getKeys() const
{
vector<docstring> bibkeys;
}
-docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key, string lang) const
+docstring const BiblioInfo::getAuthorList(docstring const & key, Buffer const & buf) const
{
BiblioInfo::const_iterator it = find(key);
if (it == end())
return docstring();
BibTeXInfo const & data = it->second;
- return data.getAbbreviatedAuthor(false, lang);
+ return data.getAuthorList(&buf, false);
}
}
-docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier, string lang) const
+docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
{
BiblioInfo::const_iterator it = find(key);
if (it == end())
BibTeXInfo const & data = it->second;
docstring year = data.getYear();
if (year.empty()) {
- // let's try the crossref
- docstring const xref = data.getXRef();
- if (xref.empty())
+ // let's try the crossrefs
+ vector<docstring> const xrefs = getXRefs(data);
+ if (xrefs.empty())
// no luck
- return translateIfPossible(from_ascii("No year"), lang);
- BiblioInfo::const_iterator const xrefit = find(xref);
- if (xrefit == end())
- // no luck again
- return translateIfPossible(from_ascii("No year"), lang);
- BibTeXInfo const & xref_data = xrefit->second;
- year = xref_data.getYear();
+ return docstring();
+ vector<docstring>::const_iterator it = xrefs.begin();
+ vector<docstring>::const_iterator en = xrefs.end();
+ for (; it != en; ++it) {
+ BiblioInfo::const_iterator const xrefit = find(*it);
+ if (xrefit == end())
+ continue;
+ BibTeXInfo const & xref_data = xrefit->second;
+ year = xref_data.getYear();
+ if (!year.empty())
+ // success!
+ break;
+ }
}
if (use_modifier && data.modifier() != 0)
year += data.modifier();
}
+docstring const BiblioInfo::getYear(docstring const & key, Buffer const & buf, bool use_modifier) const
+{
+ docstring const year = getYear(key, use_modifier);
+ if (year.empty())
+ return buf.B_("No year");
+ return year;
+}
+
+
docstring const BiblioInfo::getInfo(docstring const & key,
- Buffer const & buf, bool richtext) const
+ Buffer const & buf, CiteItem const & ci) const
{
BiblioInfo::const_iterator it = find(key);
if (it == end())
return docstring(_("Bibliography entry not found!"));
BibTeXInfo const & data = it->second;
- BibTeXInfo const * xrefptr = 0;
- docstring const xref = data.getXRef();
- if (!xref.empty()) {
- BiblioInfo::const_iterator const xrefit = find(xref);
- if (xrefit != end())
- xrefptr = &(xrefit->second);
+ BibTeXInfoList xrefptrs;
+ vector<docstring> const xrefs = getXRefs(data);
+ if (!xrefs.empty()) {
+ vector<docstring>::const_iterator it = xrefs.begin();
+ vector<docstring>::const_iterator en = xrefs.end();
+ for (; it != en; ++it) {
+ BiblioInfo::const_iterator const xrefit = find(*it);
+ if (xrefit != end())
+ xrefptrs.push_back(&(xrefit->second));
+ }
}
- return data.getInfo(xrefptr, buf, richtext);
+ return data.getInfo(xrefptrs, buf, ci);
}
-docstring const BiblioInfo::getLabel(vector<docstring> const & keys,
- Buffer const & buf, string const & style, bool richtext,
- docstring const & before, docstring const & after, docstring const & dialog) const
+docstring const BiblioInfo::getLabel(vector<docstring> keys,
+ Buffer const & buf, string const & style, CiteItem const & ci) const
{
+ size_t max_size = ci.max_size;
+ // shorter makes no sense
+ LASSERT(max_size >= 16, max_size = 16);
+
+ // we can't display more than 10 of these, anyway
+ bool const too_many_keys = keys.size() > 10;
+ if (too_many_keys)
+ keys.resize(10);
+
CiteEngineType const engine_type = buf.params().citeEngineType();
DocumentClass const & dc = buf.params().documentClass();
- string const & format = dc.getCiteFormat(engine_type, style, "cite");
- docstring ret = from_utf8(format);
+ docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite"));
+ docstring ret = format;
vector<docstring>::const_iterator key = keys.begin();
vector<docstring>::const_iterator ken = keys.end();
for (; key != ken; ++key) {
BibTeXInfo empty_data;
empty_data.key(*key);
BibTeXInfo & data = empty_data;
- BibTeXInfo const * xrefptr = 0;
+ vector<BibTeXInfo const *> xrefptrs;
if (it != end()) {
data = it->second;
- docstring const xref = data.getXRef();
- if (!xref.empty()) {
- BiblioInfo::const_iterator const xrefit = find(xref);
- if (xrefit != end())
- xrefptr = &(xrefit->second);
+ vector<docstring> const xrefs = getXRefs(data);
+ if (!xrefs.empty()) {
+ vector<docstring>::const_iterator it = xrefs.begin();
+ vector<docstring>::const_iterator en = xrefs.end();
+ for (; it != en; ++it) {
+ BiblioInfo::const_iterator const xrefit = find(*it);
+ if (xrefit != end())
+ xrefptrs.push_back(&(xrefit->second));
+ }
}
}
- ret = data.getLabel(xrefptr, buf, to_utf8(ret), richtext,
- before, after, dialog, key+1 != ken);
+ ret = data.getLabel(xrefptrs, buf, ret, ci, key + 1 != ken, i == 1);
}
+
+ if (too_many_keys)
+ ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
+ support::truncateWithEllipsis(ret, max_size);
return ret;
}
bool BiblioInfo::isBibtex(docstring const & key) const
{
- BiblioInfo::const_iterator it = find(key);
+ docstring key1;
+ split(key, key1, ',');
+ BiblioInfo::const_iterator it = find(key1);
if (it == end())
return false;
return it->second.isBibTeX();
vector<docstring> const BiblioInfo::getCiteStrings(
vector<docstring> const & keys, vector<CitationStyle> const & styles,
- Buffer const & buf, bool richtext, docstring const & before,
- docstring const & after, docstring const & dialog) const
+ Buffer const & buf, CiteItem const & ci) const
{
if (empty())
return vector<docstring>();
string style;
vector<docstring> vec(styles.size());
for (size_t i = 0; i != vec.size(); ++i) {
- style = styles[i].cmd;
- vec[i] = getLabel(keys, buf, style, richtext, before, after, dialog);
+ style = styles[i].name;
+ vec[i] = getLabel(keys, buf, style, ci);
}
return vec;
// used in xhtml to sort a list of BibTeXInfo objects
bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
{
- docstring const lauth = lhs->getAbbreviatedAuthor();
- docstring const rauth = rhs->getAbbreviatedAuthor();
+ docstring const lauth = lhs->getAuthorList();
+ docstring const rauth = rhs->getAuthorList();
docstring const lyear = lhs->getYear();
docstring const ryear = rhs->getYear();
docstring const ltitl = lhs->operator[]("title");
// FIXME We may want to collect these differently, in the first case,
// so that we might have them in order of appearance.
set<docstring> citekeys;
- Toc const & toc = buf.tocBackend().toc("citation");
- Toc::const_iterator it = toc.begin();
- Toc::const_iterator const en = toc.end();
+ shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
+ Toc::const_iterator it = toc->begin();
+ Toc::const_iterator const en = toc->end();
for (; it != en; ++it) {
if (it->str().empty())
continue;
{
collectCitedEntries(buf);
CiteEngineType const engine_type = buf.params().citeEngineType();
- bool const numbers = (engine_type == ENGINE_TYPE_NUMERICAL);
+ bool const numbers = (engine_type & ENGINE_TYPE_NUMERICAL);
int keynumber = 0;
char modifier = 0;
docstring const num = convert<docstring>(++keynumber);
entry.setCiteNumber(num);
} else {
+ // coverity complains about our derefercing the iterator last,
+ // which was not initialized above. but it does get initialized
+ // after the first time through the loop, which is the point of
+ // the first test.
+ // coverity[FORWARD_NULL]
if (it != cited_entries_.begin()
- && entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
+ && entry.getAuthorList() == last->second.getAuthorList()
// we access the year via getYear() so as to get it from the xref,
// if we need to do so
&& getYear(entry.key()) == getYear(last->second.key())) {
last = biit;
}
}
+ // Set the labels
+ it = cited_entries_.begin();
+ for (; it != en; ++it) {
+ map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
+ // this shouldn't happen, but...
+ if (biit == bimap_.end())
+ // ...fail gracefully, anyway.
+ continue;
+ BibTeXInfo & entry = biit->second;
+ if (numbers) {
+ entry.label(entry.citeNumber());
+ } else {
+ docstring const auth = entry.getAuthorList(&buf, false);
+ // we do it this way so as to access the xref, if necessary
+ // note that this also gives us the modifier
+ docstring const year = getYear(*it, buf, true);
+ if (!auth.empty() && !year.empty())
+ entry.label(auth + ' ' + year);
+ else
+ entry.label(entry.key());
+ }
+ }
}
//////////////////////////////////////////////////////////////////////
-CitationStyle citationStyleFromString(string const & command)
+CitationStyle citationStyleFromString(string const & command,
+ BufferParams const & params)
{
CitationStyle cs;
if (command.empty())
return cs;
- string cmd = command;
- if (cmd[0] == 'C') {
+ string const alias = params.getCiteAlias(command);
+ string cmd = alias.empty() ? command : alias;
+ if (isUpperCase(command[0])) {
cs.forceUpperCase = true;
- cmd[0] = 'c';
+ cmd[0] = lowercase(cmd[0]);
}
- size_t const n = cmd.size() - 1;
- if (cmd[n] == '*') {
- cs.fullAuthorList = true;
- cmd = cmd.substr(0, n);
+ size_t const n = command.size() - 1;
+ if (command[n] == '*') {
+ cs.hasStarredVersion = true;
+ if (suffixIs(cmd, '*'))
+ cmd = cmd.substr(0, cmd.size() - 1);
}
- cs.cmd = cmd;
+ cs.name = cmd;
return cs;
}
-string citationStyleToString(const CitationStyle & cs)
+string citationStyleToString(const CitationStyle & cs, bool const latex)
{
- string cmd = cs.cmd;
+ string cmd = latex ? cs.cmd : cs.name;
if (cs.forceUpperCase)
- cmd[0] = 'C';
- if (cs.fullAuthorList)
+ cmd[0] = uppercase(cmd[0]);
+ if (cs.hasStarredVersion)
cmd += '*';
return cmd;
}