From: Richard Heck Date: Mon, 11 Jan 2010 16:11:55 +0000 (+0000) Subject: Author-year citations for XHTML output. X-Git-Tag: 2.0.0~4425 X-Git-Url: https://git.lyx.org/gitweb/?a=commitdiff_plain;h=782db81f771cb12977f19b050fb2cbaa8511e16e;p=features.git Author-year citations for XHTML output. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@32969 a592a061-630c-0410-9148-cb99ea01b6c8 --- diff --git a/src/BiblioInfo.cpp b/src/BiblioInfo.cpp index 5fee10ccc2..81234b89d8 100644 --- a/src/BiblioInfo.cpp +++ b/src/BiblioInfo.cpp @@ -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::iterator last; + vector::const_iterator it = cited_entries_.begin(); vector::const_iterator const en = cited_entries_.end(); - int keynumber = 0; for (; it != en; ++it) { map::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(++keynumber); - entry.setCiteKey(key); + if (numbers) { + docstring const num = convert(++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; + } } } diff --git a/src/BiblioInfo.h b/src/BiblioInfo.h index 150e77f0c4..77c3dab729 100644 --- a/src/BiblioInfo.h +++ b/src/BiblioInfo.h @@ -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: std::map bimap_; }; @@ -136,10 +143,15 @@ public: std::vector 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 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 const & citedEntries() const + { return cited_entries_; } /// void makeCitationLabels(Buffer const & buf); /// diff --git a/src/insets/InsetBibtex.cpp b/src/insets/InsetBibtex.cpp index b4179a4f9e..8d7b33f792 100644 --- a/src/insets/InsetBibtex.cpp +++ b/src/insets/InsetBibtex.cpp @@ -909,10 +909,17 @@ void InsetBibtex::validate(LaTeXFeatures & features) const } +// FIXME +// docstring InsetBibtex::entriesAsXHTML(vector const & entries) +// And then here just: entriesAsXHTML(buffer().masterBibInfo().citedEntries()) docstring InsetBibtex::xhtml(XHTMLStream & xs, OutputParams const &) const { BiblioInfo const & bibinfo = buffer().masterBibInfo(); vector 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(); diff --git a/src/insets/InsetCitation.cpp b/src/insets/InsetCitation.cpp index 5921e7f130..7c4caa742e 100644 --- a/src/insets/InsetCitation.cpp +++ b/src/insets/InsetCitation.cpp @@ -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 "" + html::htmlize(content) + ""; +} + } // 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/; ... ; // authors_last, 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 ( year); ... ; // authors_last ( year, ) - } 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 year; // authors_last year, ) - } 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 const keys = getVectorFromString(key_list); - vector::const_iterator it = keys.begin(); - vector::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(); } diff --git a/src/insets/InsetCitation.h b/src/insets/InsetCitation.h index 602ff95416..aff0894560 100644 --- a/src/insets/InsetCitation.h +++ b/src/insets/InsetCitation.h @@ -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_;