Author-year citations for XHTML output.

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@32969 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Richard Heck 2010-01-11 16:11:55 +00:00
parent 374b5ebd72
commit 782db81f77
5 changed files with 175 additions and 109 deletions

View File

@ -204,7 +204,8 @@ docstring convertLaTeXCommands(docstring const & str)
//////////////////////////////////////////////////////////////////////
BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
: is_bibtex_(true), bib_key_(key), entry_type_(type), info_()
: is_bibtex_(true), bib_key_(key), entry_type_(type), info_(),
modifier_(0)
{}
@ -423,25 +424,37 @@ docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key) const
}
docstring const BiblioInfo::getYear(docstring const & key) const
docstring const BiblioInfo::getCiteNumber(docstring const & key) const
{
BiblioInfo::const_iterator it = find(key);
if (it == end())
return docstring();
BibTeXInfo const & data = it->second;
return data.citeNumber();
}
docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
{
BiblioInfo::const_iterator it = find(key);
if (it == end())
return docstring();
BibTeXInfo const & data = it->second;
docstring year = data.getYear();
if (!year.empty())
return year;
// let's try the crossref
docstring const xref = data.getXRef();
if (xref.empty())
return _("No year"); // no luck
BiblioInfo::const_iterator const xrefit = find(xref);
if (xrefit == end())
return _("No year"); // no luck again
BibTeXInfo const & xref_data = xrefit->second;
return xref_data.getYear();
return data.getYear();
if (year.empty()) {
// let's try the crossref
docstring const xref = data.getXRef();
if (xref.empty())
return _("No year"); // no luck
BiblioInfo::const_iterator const xrefit = find(xref);
if (xrefit == end())
return _("No year"); // no luck again
BibTeXInfo const & xref_data = xrefit->second;
year = xref_data.getYear();
}
if (use_modifier && data.modifier() != 0)
year += data.modifier();
return year;
}
@ -604,7 +617,15 @@ namespace {
// used in xhtml to sort a list of BibTeXInfo objects
bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
{
return lhs->getAbbreviatedAuthor() < rhs->getAbbreviatedAuthor();
docstring const lauth = lhs->getAbbreviatedAuthor();
docstring const rauth = rhs->getAbbreviatedAuthor();
docstring const lyear = lhs->getYear();
docstring const ryear = rhs->getYear();
docstring const ltitl = lhs->operator[]("title");
docstring const rtitl = rhs->operator[]("title");
return (lauth < rauth)
|| (lauth == rauth && lyear < ryear)
|| (lauth == rauth && lyear == ryear && ltitl < rtitl);
}
}
@ -655,19 +676,50 @@ void BiblioInfo::collectCitedEntries(Buffer const & buf)
void BiblioInfo::makeCitationLabels(Buffer const & buf)
{
collectCitedEntries(buf);
// FIXME It'd be nice to do author-year as well as numerical
// and maybe even some other sorts of labels.
CiteEngine const engine = buf.params().citeEngine();
bool const numbers =
(engine == ENGINE_BASIC || engine == ENGINE_NATBIB_NUMERICAL);
int keynumber = 0;
char modifier = 0;
// used to remember the last one we saw
// we'll be comparing entries to see if we need to add
// modifiers, like "1984a"
map<docstring, BibTeXInfo>::iterator last;
vector<docstring>::const_iterator it = cited_entries_.begin();
vector<docstring>::const_iterator const en = cited_entries_.end();
int keynumber = 0;
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;
docstring const key = convert<docstring>(++keynumber);
entry.setCiteKey(key);
if (numbers) {
docstring const num = convert<docstring>(++keynumber);
entry.setCiteNumber(num);
} else {
if (it != cited_entries_.begin()
&& entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
// 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())) {
if (modifier == 0) {
// so the last one should have been 'a'
last->second.setModifier('a');
modifier = 'b';
} else if (modifier == 'z')
modifier = 'A';
else
modifier++;
} else {
modifier = 0;
}
entry.setModifier(modifier);
// remember the last one
last = biit;
}
}
}

View File

@ -86,10 +86,16 @@ public:
docstring const & label() const { return label_; }
///
docstring const & key() const { return bib_key_; }
/// numerical key for citing this entry. currently used only
/// by XHTML output routines.
docstring citeNumber() const { return cite_number_; }
///
docstring citeKey() const { return cite_key_; }
void setCiteNumber(docstring const & num) { cite_number_ = num; }
/// a,b,c, etc, for author-year. currently used only by XHTML
/// output routines.
char modifier() const { return modifier_; }
///
void setCiteKey(docstring const & k) { cite_key_ = k; }
void setModifier(char c) { modifier_ = c; }
///
docstring entryType() const { return entry_type_; }
///
@ -114,9 +120,10 @@ private:
docstring entry_type_;
/// a cache for getInfo()
mutable docstring info_;
/// key to use when citing this entry
/// currently used only by XHTML output routines
docstring cite_key_;
///
docstring cite_number_;
///
char modifier_;
/// our map: <field, value>
std::map <docstring, docstring> bimap_;
};
@ -136,10 +143,15 @@ public:
std::vector<docstring> const getEntries() const;
/// \return the short form of an authorlist
docstring const getAbbreviatedAuthor(docstring const & key) const;
/// \return the year from the bibtex data record
/// \return the year from the bibtex data record for \param key
/// if \param use_modifier is true, then we will also append any
/// modifier for this entry (e.g., 1998b).
/// Note that this will get the year from the crossref if it's
/// not present in the record itself
docstring const getYear(docstring const & key) const;
/// not present in the record itself.
docstring const getYear(docstring const & key,
bool use_modifier = false) const;
///
docstring const getCiteNumber(docstring const & key) const;
/// \return formatted BibTeX data associated with a given key.
/// Empty if no info exists.
/// Note that this will retrieve data from the crossref as needed.
@ -177,7 +189,10 @@ public:
void collectCitedEntries(Buffer const & buf);
/// A list of BibTeX keys cited in the current document, sorted by
/// the last name of the author.
std::vector<docstring> const & citedEntries() const { return cited_entries_; }
/// Make sure you have called collectCitedEntries() before you try to
/// use this. You should probably call it just before you use this.
std::vector<docstring> const & citedEntries() const
{ return cited_entries_; }
///
void makeCitationLabels(Buffer const & buf);
///

View File

@ -909,10 +909,17 @@ void InsetBibtex::validate(LaTeXFeatures & features) const
}
// FIXME
// docstring InsetBibtex::entriesAsXHTML(vector<docstring> const & entries)
// And then here just: entriesAsXHTML(buffer().masterBibInfo().citedEntries())
docstring InsetBibtex::xhtml(XHTMLStream & xs, OutputParams const &) const
{
BiblioInfo const & bibinfo = buffer().masterBibInfo();
vector<docstring> const & cites = bibinfo.citedEntries();
CiteEngine const engine = buffer().params().citeEngine();
bool const numbers =
(engine == ENGINE_BASIC || engine == ENGINE_NATBIB_NUMERICAL);
xs << StartTag("h2", "class='bibtex'")
<< _("References")
<< EndTag("h2")
@ -931,7 +938,17 @@ docstring InsetBibtex::xhtml(XHTMLStream & xs, OutputParams const &) const
// The same name/id problem we have elsewhere.
string const attr = "id='" + to_utf8(entry.key()) + "'";
xs << CompTag("a", attr);
docstring citekey = entry.citeKey();
docstring citekey;
if (numbers)
citekey = entry.citeNumber();
else {
docstring const auth = entry.getAbbreviatedAuthor();
// we do it this way so as to access the xref, if necessary
// note that this also gives us the modifier
docstring const year = bibinfo.getYear(*vit, true);
if (!auth.empty() && !year.empty())
citekey = auth + ' ' + year;
}
if (citekey.empty()) {
citekey = entry.label();
if (citekey.empty())
@ -947,6 +964,7 @@ docstring InsetBibtex::xhtml(XHTMLStream & xs, OutputParams const &) const
<< bibinfo.getInfo(entry.key())
<< EndTag("span")
<< EndTag("div");
xs.cr();
}
xs << EndTag("div");
return docstring();

View File

@ -199,22 +199,33 @@ string asValidLatexCommand(string const & input, CiteEngine const engine)
return output;
}
inline docstring wrapCitation(docstring const & key,
docstring const & content, bool for_xhtml)
{
if (!for_xhtml)
return content;
// we have to do the escaping here, because we will ultimately
// write this as a raw string, so as not to escape the tags.
return "<a href='#" + key + "'>" + html::htmlize(content) + "</a>";
}
} // anonymous namespace
docstring InsetCitation::generateLabel() const
docstring InsetCitation::generateLabel(bool for_xhtml) const
{
docstring label;
label = complexLabel();
label = complexLabel(for_xhtml);
// Fallback to fail-safe
if (label.empty())
label = basicLabel();
label = basicLabel(for_xhtml);
return label;
}
docstring InsetCitation::complexLabel() const
docstring InsetCitation::complexLabel(bool for_xhtml) const
{
Buffer const & buf = buffer();
// Only start the process off after the buffer is loaded from file.
@ -300,83 +311,90 @@ docstring InsetCitation::complexLabel() const
for (; it != end; ++it) {
// get the bibdata corresponding to the key
docstring const author = biblist.getAbbreviatedAuthor(*it);
docstring const year = biblist.getYear(*it);
docstring const year = biblist.getYear(*it, for_xhtml);
docstring const citenum = for_xhtml ? biblist.getCiteNumber(*it) : *it;
// Something isn't right. Fail safely.
if (author.empty() || year.empty())
// We can't construct a "complex" label without that info.
// So fail safely.
return docstring();
// authors1/<before>; ... ;
// authors_last, <after>
if (cite_type == "cite") {
if (engine == ENGINE_BASIC) {
label += *it + sep_str;
label += wrapCitation(*it, citenum, for_xhtml) + sep_str;
} else if (engine == ENGINE_JURABIB) {
if (it == keys.begin())
label += author + before_str + sep_str;
label += wrapCitation(*it, author, for_xhtml) + before_str + sep_str;
else
label += author + sep_str;
label += wrapCitation(*it, author, for_xhtml) + sep_str;
}
}
// nocite
} else if (cite_type == "nocite") {
else if (cite_type == "nocite") {
label += *it + sep_str;
}
// (authors1 (<before> year); ... ;
// authors_last (<before> year, <after>)
} else if (cite_type == "citet") {
else if (cite_type == "citet") {
switch (engine) {
case ENGINE_NATBIB_AUTHORYEAR:
label += author + op_str + before_str +
year + cp + sep_str;
wrapCitation(*it, year, for_xhtml) + cp + sep_str;
break;
case ENGINE_NATBIB_NUMERICAL:
label += author + op_str + before_str + '#' + *it + cp + sep_str;
label += author + op_str + before_str +
wrapCitation(*it, citenum, for_xhtml) + cp + sep_str;
break;
case ENGINE_JURABIB:
label += before_str + author + op_str +
year + cp + sep_str;
wrapCitation(*it, year, for_xhtml) + cp + sep_str;
break;
case ENGINE_BASIC:
break;
}
// author, year; author, year; ...
} else if (cite_type == "citep" ||
}
// author, year; author, year; ...
else if (cite_type == "citep" ||
cite_type == "citealp") {
if (engine == ENGINE_NATBIB_NUMERICAL) {
label += *it + sep_str;
label += wrapCitation(*it, citenum, for_xhtml) + sep_str;
} else {
label += author + ", " + year + sep_str;
label += wrapCitation(*it, author + ", " + year, for_xhtml) + sep_str;
}
}
// (authors1 <before> year;
// authors_last <before> year, <after>)
} else if (cite_type == "citealt") {
else if (cite_type == "citealt") {
switch (engine) {
case ENGINE_NATBIB_AUTHORYEAR:
label += author + ' ' + before_str +
year + sep_str;
wrapCitation(*it, year, for_xhtml) + sep_str;
break;
case ENGINE_NATBIB_NUMERICAL:
label += author + ' ' + before_str + '#' + *it + sep_str;
label += author + ' ' + before_str + '#' +
wrapCitation(*it, citenum, for_xhtml) + sep_str;
break;
case ENGINE_JURABIB:
label += before_str + author + ' ' +
year + sep_str;
label += before_str +
wrapCitation(*it, author + ' ' + year, for_xhtml) + sep_str;
break;
case ENGINE_BASIC:
break;
}
}
// author; author; ...
} else if (cite_type == "citeauthor") {
label += author + sep_str;
else if (cite_type == "citeauthor") {
label += wrapCitation(*it, author, for_xhtml) + sep_str;
}
// year; year; ...
} else if (cite_type == "citeyear" ||
else if (cite_type == "citeyear" ||
cite_type == "citeyearpar") {
label += year + sep_str;
label += wrapCitation(*it, year, for_xhtml) + sep_str;
}
}
label = rtrim(rtrim(label), sep);
@ -409,7 +427,7 @@ docstring InsetCitation::complexLabel() const
}
docstring InsetCitation::basicLabel() const
docstring InsetCitation::basicLabel(bool for_xhtml) const
{
docstring keys = getParam("key");
docstring label;
@ -420,10 +438,10 @@ docstring InsetCitation::basicLabel() const
while (contains(keys, ',')) {
docstring key;
keys = ltrim(split(keys, key, ','));
label += ", " + key;
label += ", " + wrapCitation(key, key, for_xhtml);
}
} else {
label = keys;
label = wrapCitation(keys, keys, for_xhtml);
}
docstring const & after = getParam("after");
@ -510,46 +528,9 @@ docstring InsetCitation::xhtml(XHTMLStream & xs, OutputParams const &) const
if (cmd == "nocite")
return docstring();
BiblioInfo const & bi = buffer().masterBibInfo();
docstring const & key_list = getParam("key");
if (key_list.empty())
return docstring();
// have to output this raw, because generateLabel() will include tags
xs << XHTMLStream::NextRaw() << generateLabel(true);
// FIXME We should do a better job outputing different things for the
// different citation styles. For now, we use square brackets for every
// case.
xs << "[";
docstring const & before = getParam("before");
if (!before.empty())
xs << before << " ";
vector<docstring> const keys = getVectorFromString(key_list);
vector<docstring>::const_iterator it = keys.begin();
vector<docstring>::const_iterator const en = keys.end();
bool first = true;
for (; it != en; ++it) {
BiblioInfo::const_iterator const bt = bi.find(*it);
if (bt == bi.end())
continue;
BibTeXInfo const & bibinfo = bt->second;
if (!first) {
xs << ", ";
first = false;
}
docstring citekey = bibinfo.citeKey();
if (citekey.empty()) {
citekey = bibinfo.label();
if (citekey.empty())
citekey = *it;
}
string const attr = "href='#" + to_utf8(*it) + "'";
xs << StartTag("a", attr) << citekey << EndTag("a");
}
docstring const & after = getParam("after");
if (!after.empty())
xs << ", " << after;
xs << "]";
return docstring();
}

View File

@ -74,12 +74,12 @@ public:
private:
///
Inset * clone() const { return new InsetCitation(*this); }
/// This function does the donkey work of creating the pretty label
docstring generateLabel() const;
///
docstring complexLabel() const;
///
docstring basicLabel() const;
/// tries to make a pretty label and makes a basic one if not
docstring generateLabel(bool for_xhtml = false) const;
/// makes a pretty label
docstring complexLabel(bool for_xhtml = false) const;
/// makes a very basic label, in case we can't make a pretty one
docstring basicLabel(bool for_xhtml = false) const;
/// we'll eventually want to be able to get info on this from the
/// various CiteEngines
static ParamInfo param_info_;