diff --git a/development/scons/scons_manifest.py b/development/scons/scons_manifest.py index ebde867e50..b45ac7ef61 100644 --- a/development/scons/scons_manifest.py +++ b/development/scons/scons_manifest.py @@ -33,6 +33,7 @@ TOP_extra_files = Split(''' src_header_files = Split(''' ASpell_local.h Author.h + Biblio.h Bidi.h Box.h BranchList.h @@ -143,6 +144,7 @@ src_header_files = Split(''' src_pre_files = Split(''' Author.cpp + Biblio.cpp Bidi.cpp BranchList.cpp Buffer.cpp diff --git a/po/POTFILES.in b/po/POTFILES.in index 8596c09906..df3ec42c32 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,3 +1,4 @@ +src/Biblio.cpp src/Buffer.cpp src/BufferList.cpp src/BufferParams.cpp diff --git a/src/Biblio.cpp b/src/Biblio.cpp new file mode 100644 index 0000000000..8c483321b3 --- /dev/null +++ b/src/Biblio.cpp @@ -0,0 +1,849 @@ +/** + * \file Biblio.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Angus Leeming + * \author Herbert Voß + * \author Richard Heck (re-write of BibTeX representation) + * + * Full author contact details are available in file CREDITS. + */ + +#include "Biblio.h" + +#include "gettext.h" +#include "InsetIterator.h" +#include "Paragraph.h" + +#include "insets/Inset.h" +#include "insets/InsetBibitem.h" +#include "insets/InsetBibtex.h" +#include "insets/InsetInclude.h" + +#include "support/lstrings.h" + +using std::string; +using std::vector; +using std::pair; +using std::endl; + +namespace lyx { +using support::ascii_lowercase; +using support::bformat; +using support::compare_ascii_no_case; +using support::contains; +using support::getVectorFromString; +using support::ltrim; +using support::prefixIs; +using support::rtrim; +using support::split; +using support::subst; +using support::token; +using support::trim; + +namespace biblio { + +namespace { + + vector const init_possible_cite_commands() + { + char const * const pos[] = { + "cite", "citet", "citep", "citealt", "citealp", + "citeauthor", "citeyear", "citeyearpar", + "citet*", "citep*", "citealt*", "citealp*", "citeauthor*", + "Citet", "Citep", "Citealt", "Citealp", "Citeauthor", + "Citet*", "Citep*", "Citealt*", "Citealp*", "Citeauthor*", + "fullcite", + "footcite", "footcitet", "footcitep", "footcitealt", + "footcitealp", "footciteauthor", "footciteyear", "footciteyearpar", + "citefield", "citetitle", "cite*" + }; + size_t const size_pos = sizeof(pos) / sizeof(pos[0]); + + return vector(pos, pos + size_pos); + } + + + vector const & possible_cite_commands() + { + static vector const pos = init_possible_cite_commands(); + return pos; + } + + + bool is_possible_cite_command(string const & input) + { + vector const & possibles = possible_cite_commands(); + vector::const_iterator const end = possibles.end(); + return std::find(possibles.begin(), end, input) != end; + } + + + string const default_cite_command(CiteEngine engine) + { + string str; + switch (engine) { + case ENGINE_BASIC: + str = "cite"; + break; + case ENGINE_NATBIB_AUTHORYEAR: + str = "citet"; + break; + case ENGINE_NATBIB_NUMERICAL: + str = "citep"; + break; + case ENGINE_JURABIB: + str = "cite"; + break; + } + return str; + } + + +} // namespace anon + + +const docstring TheBibliographyRef(from_ascii("TheBibliographyRef")); + +string const asValidLatexCommand(string const & input, + CiteEngine const engine) +{ + string const default_str = default_cite_command(engine); + if (!is_possible_cite_command(input)) + return default_str; + + string output; + switch (engine) { + case ENGINE_BASIC: + output = default_str; + break; + + case ENGINE_NATBIB_AUTHORYEAR: + case ENGINE_NATBIB_NUMERICAL: + if (input == "cite" || input == "citefield" || + input == "citetitle" || input == "cite*") + output = default_str; + else if (prefixIs(input, "foot")) + output = input.substr(4); + else + output = input; + break; + + case ENGINE_JURABIB: { + // Jurabib does not support the 'uppercase' natbib style. + if (input[0] == 'C') + output = string(1, 'c') + input.substr(1); + else + output = input; + + // Jurabib does not support the 'full' natbib style. + string::size_type const n = output.size() - 1; + if (output != "cite*" && output[n] == '*') + output = output.substr(0, n); + + break; + } + } + + return output; +} + + +docstring const familyName(docstring const & name) +{ + if (name.empty()) + return docstring(); + +// Very simple parser + docstring fname = name; + +// possible authorname combinations are: +// "Surname, FirstName" +// "Surname, F." +// "FirstName Surname" +// "F. Surname" + docstring::size_type idx = fname.find(','); + if (idx != docstring::npos) + return ltrim(fname.substr(0, idx)); + idx = fname.rfind('.'); + if (idx != docstring::npos && idx + 1 < fname.size()) + fname = ltrim(fname.substr(idx + 1)); +// test if we have a LaTeX Space in front + if (fname[0] == '\\') + return fname.substr(2); + + return rtrim(fname); +} + + +docstring const getAbbreviatedAuthor(InfoMap const & map, string const & key) +{ + BOOST_ASSERT(!map.empty()); + + InfoMap::const_iterator it = map.find(key); + if (it == map.end()) + return docstring(); + docstring const & data = it->second; + +// Is the entry a BibTeX one or one from lyx-layout "bibliography"? + docstring::size_type const pos = data.find(TheBibliographyRef); + if (pos != docstring::npos) { + if (pos <= 2) { + return docstring(); + } + + docstring const opt = trim(data.substr(0, pos - 1)); + if (opt.empty()) + return docstring(); + + docstring authors; + split(opt, authors, '('); + return authors; + } + + docstring author = parseBibTeX(data, "author"); + + if (author.empty()) + author = parseBibTeX(data, "editor"); + + if (author.empty()) { + author = parseBibTeX(data, "key"); + if (author.empty()) + // FIXME UNICODE + return from_utf8(key); + return author; + } + + vector const authors = getVectorFromString(author, from_ascii(" and ")); + if (authors.empty()) + return author; + + if (authors.size() == 2) + return bformat(_("%1$s and %2$s"), + familyName(authors[0]), familyName(authors[1])); + + if (authors.size() > 2) + return bformat(_("%1$s et al."), familyName(authors[0])); + + return familyName(authors[0]); +} + + +docstring const getYear(InfoMap const & map, string const & key) +{ + BOOST_ASSERT(!map.empty()); + + InfoMap::const_iterator it = map.find(key); + if (it == map.end()) + return docstring(); + docstring const & data = it->second; + +// Is the entry a BibTeX one or one from lyx-layout "bibliography"? + docstring::size_type const pos = data.find(TheBibliographyRef); + if (pos != docstring::npos) { + if (pos <= 2) { + return docstring(); + } + + docstring const opt = + trim(data.substr(0, pos - 1)); + if (opt.empty()) + return docstring(); + + docstring authors; + docstring const tmp = split(opt, authors, '('); + docstring year; + split(tmp, year, ')'); + return year; + + } + + docstring year = parseBibTeX(data, "year"); + if (year.empty()) + year = _("No year"); + + return year; +} + + +namespace { +// A functor for use with std::sort, leading to case insensitive sorting +class compareNoCase: public std::binary_function +{ + public: + bool operator()(string const & s1, string const & s2) const { + return compare_ascii_no_case(s1, s2) < 0; + } +}; +} // namespace anon + + +vector const getKeys(InfoMap const & map) +{ + vector bibkeys; + InfoMap::const_iterator it = map.begin(); + InfoMap::const_iterator end = map.end(); + for (; it != end; ++it) { + bibkeys.push_back(it->first); + } + + std::sort(bibkeys.begin(), bibkeys.end(), compareNoCase()); + return bibkeys; +} + + +docstring const getInfo(InfoMap const & map, string const & key) +{ + BOOST_ASSERT(!map.empty()); + + InfoMap::const_iterator it = map.find(key); + if (it == map.end()) + return docstring(); + docstring const & data = it->second; + +// is the entry a BibTeX one or one from lyx-layout "bibliography"? + docstring::size_type const pos = data.find(TheBibliographyRef); + if (pos != docstring::npos) { + docstring::size_type const pos2 = pos + TheBibliographyRef.size(); + docstring const info = trim(data.substr(pos2)); + return info; + } + +// Search for all possible "required" keys + docstring author = parseBibTeX(data, "author"); + if (author.empty()) + author = parseBibTeX(data, "editor"); + + docstring year = parseBibTeX(data, "year"); + docstring title = parseBibTeX(data, "title"); + docstring booktitle = parseBibTeX(data, "booktitle"); + docstring chapter = parseBibTeX(data, "chapter"); + docstring number = parseBibTeX(data, "number"); + docstring volume = parseBibTeX(data, "volume"); + docstring pages = parseBibTeX(data, "pages"); + docstring annote = parseBibTeX(data, "annote"); + docstring media = parseBibTeX(data, "journal"); + if (media.empty()) + media = parseBibTeX(data, "publisher"); + if (media.empty()) + media = parseBibTeX(data, "school"); + if (media.empty()) + media = parseBibTeX(data, "institution"); + + odocstringstream result; + if (!author.empty()) + result << author << ", "; + if (!title.empty()) + result << title; + if (!booktitle.empty()) + result << ", in " << booktitle; + if (!chapter.empty()) + result << ", Ch. " << chapter; + if (!media.empty()) + result << ", " << media; + if (!volume.empty()) + result << ", vol. " << volume; + if (!number.empty()) + result << ", no. " << number; + if (!pages.empty()) + result << ", pp. " << pages; + if (!year.empty()) + result << ", " << year; + if (!annote.empty()) + result << "\n\n" << annote; + + docstring const result_str = rtrim(result.str()); + if (!result_str.empty()) + return result_str; + +// This should never happen (or at least be very unusual!) + return data; +} + + +namespace { + +// Escape special chars. +// All characters are literals except: '.|*?+(){}[]^$\' +// These characters are literals when preceded by a "\", which is done here +// @todo: This function should be moved to support, and then the test in tests +// should be moved there as well. +string const escape_special_chars(string const & expr) +{ + // Search for all chars '.|*?+(){}[^$]\' + // Note that '[' and '\' must be escaped. + // This is a limitation of boost::regex, but all other chars in BREs + // are assumed literal. + boost::regex reg("[].|*?+(){}^$\\[\\\\]"); + + // $& is a perl-like expression that expands to all + // of the current match + // The '$' must be prefixed with the escape character '\' for + // boost to treat it as a literal. + // Thus, to prefix a matched expression with '\', we use: + return boost::regex_replace(expr, reg, "\\\\$&"); +} + + +// A functor for use with std::find_if, used to ascertain whether a +// data entry matches the required regex_ +// @throws: boost::regex_error if the supplied regex pattern is not valid +// @todo: This function should be moved to support. +class RegexMatch : public std::unary_function +{ + public: +// re and icase are used to construct an instance of boost::RegEx. +// if icase is true, then matching is insensitive to case + RegexMatch(InfoMap const & m, string const & re, bool icase) + : map_(m), regex_(re, icase) {} + + bool operator()(string const & key) const { +// the data searched is the key + its associated BibTeX/biblio +// fields + string data = key; + InfoMap::const_iterator info = map_.find(key); + if (info != map_.end()) + // FIXME UNICODE + data += ' ' + to_utf8(info->second); + +// Attempts to find a match for the current RE +// somewhere in data. + return boost::regex_search(data, regex_); + } + private: + InfoMap const map_; + mutable boost::regex regex_; +}; + +} // namespace anon + + +vector::const_iterator searchKeys(InfoMap const & theMap, + vector const & keys, + string const & search_expr, + vector::const_iterator start, + Search type, + Direction dir, + bool caseSensitive) +{ + // Preliminary checks + if (start < keys.begin() || start >= keys.end()) + return keys.end(); + + string expr = trim(search_expr); + if (expr.empty()) + return keys.end(); + + if (type == SIMPLE) + // We must escape special chars in the search_expr so that + // it is treated as a simple string by boost::regex. + expr = escape_special_chars(expr); + + try { + // Build the functor that will be passed to find_if. + RegexMatch const match(theMap, expr, !caseSensitive); + + // Search the vector of 'keys' from 'start' for one + // that matches the predicate 'match'. Searching can + // be forward or backward from start. + if (dir == FORWARD) + return std::find_if(start, keys.end(), match); + + vector::const_reverse_iterator rit(start); + vector::const_reverse_iterator rend = keys.rend(); + rit = std::find_if(rit, rend, match); + + if (rit == rend) + return keys.end(); + // This is correct and always safe. + // (See Meyer's Effective STL, Item 28.) + return (++rit).base(); + } + catch (boost::regex_error &) { + return keys.end(); + } +} + + +docstring const parseBibTeX(docstring data, string const & findkey) +{ + // at first we delete all characters right of '%' and + // replace tabs through a space and remove leading spaces + // we read the data line by line so that the \n are + // ignored, too. + docstring data_; + int Entries = 0; + docstring dummy = token(data,'\n', Entries); + while (!dummy.empty()) { + // no tabs + dummy = subst(dummy, '\t', ' '); + // no leading spaces + dummy = ltrim(dummy); + // ignore lines with a beginning '%' or ignore all right of % + docstring::size_type const idx = + dummy.empty() ? docstring::npos : dummy.find('%'); + if (idx != docstring::npos) + // Check if this is really a comment or just "\%" + if (idx == 0 || dummy[idx - 1] != '\\') + dummy.erase(idx, docstring::npos); + else + // This is "\%", so just erase the '\' + dummy.erase(idx - 1, 1); + // do we have a new token or a new line of + // the same one? In the first case we ignore + // the \n and in the second we replace it + // with a space + if (!dummy.empty()) { + if (!contains(dummy, '=')) + data_ += ' ' + dummy; + else + data_ += dummy; + } + dummy = token(data, '\n', ++Entries); + } //end while + + // replace double commas with "" for easy scanning + data = subst(data_, from_ascii(",,"), from_ascii("\"\"")); + + // unlikely! + if (data.empty()) + return docstring(); + + // now get only the important line of the bibtex entry. + // all entries are devided by ',' except the last one. + data += ','; + // now we have same behaviour for all entries because the last one + // is "blah ... }" + Entries = 0; + bool found = false; + // parsing of title and booktitle is different from the + // others, because booktitle contains title + do { + dummy = token(data, ',', Entries++); + if (!dummy.empty()) { + found = contains(ascii_lowercase(dummy), from_ascii(findkey)); + if (findkey == "title" && + contains(ascii_lowercase(dummy), from_ascii("booktitle"))) + found = false; + } + } while (!found && !dummy.empty()); + if (dummy.empty()) + // no such keyword + return docstring(); + + // we are not sure, if we get all, because "key= "blah, blah" is + // allowed. + // Therefore we read all until the next "=" character, which follows a + // new keyword + docstring keyvalue = dummy; + dummy = token(data, ',', Entries++); + while (!contains(dummy, '=') && !dummy.empty()) { + keyvalue += ',' + dummy; + dummy = token(data, ',', Entries++); + } + + // replace double "" with originals ,, (two commas) + // leaving us with the all-important line + data = subst(keyvalue, from_ascii("\"\""), from_ascii(",,")); + + // Clean-up. + // 1. Spaces + data = rtrim(data); + // 2. if there is no opening '{' then a closing '{' is probably cruft. + if (!contains(data, '{')) + data = rtrim(data, "}"); + // happens, when last keyword + docstring::size_type const idx = + !data.empty() ? data.find('=') : docstring::npos; + + if (idx == docstring::npos) + return docstring(); + + data = trim(data.substr(idx)); + + // a valid entry? + if (data.length() < 2 || data[0] != '=') + return docstring(); + else { + // delete '=' and the following spaces + data = ltrim(data, " ="); + if (data.length() < 2) { + // not long enough to find delimiters + return data; + } else { + docstring::size_type keypos = 1; + char_type enclosing; + if (data[0] == '{') { + enclosing = '}'; + } else if (data[0] == '"') { + enclosing = '"'; + } else { + // no {} and no "", pure data but with a + // possible ',' at the end + return rtrim(data, ","); + } + docstring tmp = data.substr(keypos); + while (tmp.find('{') != docstring::npos && + tmp.find('}') != docstring::npos && + tmp.find('{') < tmp.find('}') && + tmp.find('{') < tmp.find(enclosing)) { + keypos += tmp.find('{') + 1; + tmp = data.substr(keypos); + keypos += tmp.find('}') + 1; + tmp = data.substr(keypos); + } + if (tmp.find(enclosing) == docstring::npos) + return data; + else { + keypos += tmp.find(enclosing); + return data.substr(1, keypos - 1); + } + } + } +} + + +namespace { + + +char const * const citeCommands[] = { + "cite", "citet", "citep", "citealt", "citealp", "citeauthor", + "citeyear", "citeyearpar" }; + +unsigned int const nCiteCommands = + sizeof(citeCommands) / sizeof(char *); + +CiteStyle const citeStyles[] = { + CITE, CITET, CITEP, CITEALT, CITEALP, +CITEAUTHOR, CITEYEAR, CITEYEARPAR }; + +unsigned int const nCiteStyles = + sizeof(citeStyles) / sizeof(CiteStyle); + +CiteStyle const citeStylesFull[] = { + CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR }; + +unsigned int const nCiteStylesFull = + sizeof(citeStylesFull) / sizeof(CiteStyle); + +CiteStyle const citeStylesUCase[] = { + CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR }; + +unsigned int const nCiteStylesUCase = + sizeof(citeStylesUCase) / sizeof(CiteStyle); + +} // namespace anon + + +CitationStyle::CitationStyle(string const & command) + : style(CITE), full(false), forceUCase(false) +{ + if (command.empty()) + return; + + string cmd = command; + if (cmd[0] == 'C') { + forceUCase = true; + cmd[0] = 'c'; + } + + string::size_type const n = cmd.size() - 1; + if (cmd != "cite" && cmd[n] == '*') { + full = true; + cmd = cmd.substr(0,n); + } + + char const * const * const last = citeCommands + nCiteCommands; + char const * const * const ptr = std::find(citeCommands, last, cmd); + + if (ptr != last) { + size_t idx = ptr - citeCommands; + style = citeStyles[idx]; + } +} + + +string const CitationStyle::asLatexStr() const +{ + string cite = citeCommands[style]; + if (full) { + CiteStyle const * last = citeStylesFull + nCiteStylesFull; + if (std::find(citeStylesFull, last, style) != last) + cite += '*'; + } + + if (forceUCase) { + CiteStyle const * last = citeStylesUCase + nCiteStylesUCase; + if (std::find(citeStylesUCase, last, style) != last) + cite[0] = 'C'; + } + + return cite; +} + + +vector const getCiteStyles(CiteEngine const engine) +{ + unsigned int nStyles = 0; + unsigned int start = 0; + + switch (engine) { + case ENGINE_BASIC: + nStyles = 1; + start = 0; + break; + case ENGINE_NATBIB_AUTHORYEAR: + case ENGINE_NATBIB_NUMERICAL: + nStyles = nCiteStyles - 1; + start = 1; + break; + case ENGINE_JURABIB: + nStyles = nCiteStyles; + start = 0; + break; + } + + typedef vector cite_vec; + + cite_vec styles(nStyles); + cite_vec::size_type i = 0; + int j = start; + for (; i != styles.size(); ++i, ++j) + styles[i] = citeStyles[j]; + + return styles; +} + + +vector const + getNumericalStrings(string const & key, + InfoMap const & map, vector const & styles) +{ + if (map.empty()) + return vector(); + + docstring const author = getAbbreviatedAuthor(map, key); + docstring const year = getYear(map, key); + if (author.empty() || year.empty()) + return vector(); + + vector vec(styles.size()); + for (vector::size_type i = 0; i != vec.size(); ++i) { + docstring str; + + switch (styles[i]) { + case CITE: + case CITEP: + str = from_ascii("[#ID]"); + break; + + case CITET: + str = author + " [#ID]"; + break; + + case CITEALT: + str = author + " #ID"; + break; + + case CITEALP: + str = from_ascii("#ID"); + break; + + case CITEAUTHOR: + str = author; + break; + + case CITEYEAR: + str = year; + break; + + case CITEYEARPAR: + str = '(' + year + ')'; + break; + } + + vec[i] = str; + } + + return vec; +} + + +vector const + getAuthorYearStrings(string const & key, + InfoMap const & map, vector const & styles) +{ + if (map.empty()) + return vector(); + + docstring const author = getAbbreviatedAuthor(map, key); + docstring const year = getYear(map, key); + if (author.empty() || year.empty()) + return vector(); + + vector vec(styles.size()); + for (vector::size_type i = 0; i != vec.size(); ++i) { + docstring str; + + switch (styles[i]) { + case CITE: + // jurabib only: Author/Annotator + // (i.e. the "before" field, 2nd opt arg) + str = author + "/<" + _("before") + '>'; + break; + + case CITET: + str = author + " (" + year + ')'; + break; + + case CITEP: + str = '(' + author + ", " + year + ')'; + break; + + case CITEALT: + str = author + ' ' + year ; + break; + + case CITEALP: + str = author + ", " + year ; + break; + + case CITEAUTHOR: + str = author; + break; + + case CITEYEAR: + str = year; + break; + + case CITEYEARPAR: + str = '(' + year + ')'; + break; + } + + vec[i] = str; + } + + return vec; +} + + +void fillWithBibKeys(Buffer const * const buf, + vector > & keys) +{ + /// if this is a child document and the parent is already loaded + /// use the parent's list instead [ale990412] + Buffer const * const tmp = buf->getMasterBuffer(); + BOOST_ASSERT(tmp); + if (tmp != buf) { + fillWithBibKeys(tmp, keys); + return; + } + + for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it) + it->fillWithBibKeys(*buf, keys, it); +} +} // namespace biblio +} // namespace lyx + diff --git a/src/Biblio.h b/src/Biblio.h new file mode 100644 index 0000000000..a6fe6c6acd --- /dev/null +++ b/src/Biblio.h @@ -0,0 +1,175 @@ +// -*- C++ -*- +/** + * \file Biblio.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Angus Leeming + * \author Herbert Voß + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef BIBLIO_H +#define BIBLIO_H + +#include "Buffer.h" +#include "support/docstring.h" + +#include + +namespace lyx { + +namespace biblio { + + extern const docstring TheBibliographyRef; + + enum CiteEngine { + ENGINE_BASIC, + ENGINE_NATBIB_AUTHORYEAR, + ENGINE_NATBIB_NUMERICAL, + ENGINE_JURABIB + }; + + + enum CiteStyle { + CITE, + CITET, + CITEP, + CITEALT, + CITEALP, + CITEAUTHOR, + CITEYEAR, + CITEYEARPAR + }; + + + enum Search { + SIMPLE, + REGEX + }; + + + enum Direction { + FORWARD, + BACKWARD + }; + + +/** Fills keys with BibTeX information derived from the various + * in this document or its master document. + */ + void fillWithBibKeys(Buffer const * const buf, + std::vector > & keys); + +/** Each citation engine recognizes only a subset of all possible + * citation commands. Given a latex command \c input, this function + * returns an appropriate command, valid for \c engine. + */ + std::string const asValidLatexCommand(std::string const & input, + CiteEngine const engine); + +/// First entry is the bibliography key, second the data + typedef std::map InfoMap; + +/// Returns a vector of bibliography keys + std::vector const getKeys(InfoMap const &); + +/** Returns the BibTeX data associated with a given key. + Empty if no info exists. */ + docstring const getInfo(InfoMap const &, std::string const & key); + +/// return the year from the bibtex data record + docstring const getYear(InfoMap const & map, std::string const & key); + +/// return the short form of an authorlist + docstring const getAbbreviatedAuthor(InfoMap const & map, std::string const & key); + +// return only the family name + docstring const familyName(docstring const & name); + +/** Search a BibTeX info field for the given key and return the + associated field. */ + docstring const parseBibTeX(docstring data, std::string const & findkey); + +/** Returns an iterator to the first key that meets the search + criterion, or end() if unsuccessful. + + User supplies : + the InfoMap of bibkeys info, + the vector of keys to be searched, + the search criterion, + an iterator defining the starting point of the search, + an enum defining a Simple or Regex search, + an enum defining the search direction. + */ + + std::vector::const_iterator + searchKeys(InfoMap const & map, + std::vector const & keys_to_search, + docstring const & search_expression, + std::vector::const_iterator start, + Search, + Direction, + bool caseSensitive=false); + + + class CitationStyle { + public: + /// + CitationStyle(CiteStyle s = CITE, bool f = false, bool force = false) + : style(s), full(f), forceUCase(force) {} + /// \param latex_str a LaTeX command, "cite", "Citep*", etc + CitationStyle(std::string const & latex_str); + /// + std::string const asLatexStr() const; + /// + CiteStyle style; + /// + bool full; + /// + bool forceUCase; + }; + + +/// Returns a vector of available Citation styles. + std::vector const getCiteStyles(CiteEngine const ); + +/** + "Translates" the available Citation Styles into strings for this key. + The returned string is displayed by the GUI. + + + [XX] is used in place of the actual reference + Eg, the vector will contain: [XX], Jones et al. [XX], ... + + User supplies : + the key, + the InfoMap of bibkeys info, + the available citation styles + */ + std::vector const + getNumericalStrings(std::string const & key, + InfoMap const & map, + std::vector const & styles); + +/** + "Translates" the available Citation Styles into strings for this key. + The returned string is displayed by the GUI. + + Eg, the vector will contain: + Jones et al. (1990), (Jones et al. 1990), Jones et al. 1990, ... + + User supplies : + the key, + the InfoMap of bibkeys info, + the available citation styles + */ + std::vector const + getAuthorYearStrings(std::string const & key, + InfoMap const & map, + std::vector const & styles); + +} // namespace biblio +} // namespace lyx +#endif diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 987605cb69..18ce36f4d3 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -1354,36 +1354,7 @@ void Buffer::getLabelList(vector & list) const void Buffer::fillWithBibKeys(vector > & keys) const { - /// if this is a child document and the parent is already loaded - /// use the parent's list instead [ale990412] - Buffer const * tmp = getMasterBuffer(); - BOOST_ASSERT(tmp); - if (tmp != this) { - tmp->fillWithBibKeys(keys); - return; - } - - for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) { - if (it->lyxCode() == Inset::BIBTEX_CODE) { - InsetBibtex const & inset = - static_cast(*it); - inset.fillWithBibKeys(*this, keys); - } else if (it->lyxCode() == Inset::INCLUDE_CODE) { - InsetInclude const & inset = - static_cast(*it); - inset.fillWithBibKeys(*this, keys); - } else if (it->lyxCode() == Inset::BIBITEM_CODE) { - InsetBibitem const & inset = - static_cast(*it); - // FIXME UNICODE - string const key = to_utf8(inset.getParam("key")); - docstring const label = inset.getParam("label"); - DocIterator doc_it(it); doc_it.forwardPos(); - docstring const ref = doc_it.paragraph().asString(*this, false); - docstring const info = label + "TheBibliographyRef" + ref; - keys.push_back(pair(key, info)); - } - } + biblio::fillWithBibKeys(this, keys); } diff --git a/src/BufferParams.h b/src/BufferParams.h index 9b9136ff0e..d0de105f03 100644 --- a/src/BufferParams.h +++ b/src/BufferParams.h @@ -15,6 +15,7 @@ #ifndef BUFFERPARAMS_H #define BUFFERPARAMS_H +#include "Biblio.h" #include "TextClass.h" #include "paper.h" diff --git a/src/Makefile.am b/src/Makefile.am index c275a12eb9..4be7c7b9c0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -83,6 +83,8 @@ endif liblyxcore_la_SOURCES = \ Author.cpp \ Author.h \ + Biblio.h \ + Biblio.cpp \ Bidi.cpp \ Bidi.h \ boost.cpp \ diff --git a/src/frontends/controllers/ControlCitation.h b/src/frontends/controllers/ControlCitation.h index 652094e3c2..4bf16fde4e 100644 --- a/src/frontends/controllers/ControlCitation.h +++ b/src/frontends/controllers/ControlCitation.h @@ -13,9 +13,8 @@ #ifndef CONTROLCITATION_H #define CONTROLCITATION_H - +#include "Biblio.h" #include "ControlCommand.h" -#include "frontend_helpers.h" namespace lyx { namespace frontend { diff --git a/src/frontends/controllers/frontend_helpers.cpp b/src/frontends/controllers/frontend_helpers.cpp index 25c1434641..1d383c5b41 100644 --- a/src/frontends/controllers/frontend_helpers.cpp +++ b/src/frontends/controllers/frontend_helpers.cpp @@ -48,815 +48,6 @@ using std::endl; namespace lyx { -using support::ascii_lowercase; -using support::bformat; -using support::compare_ascii_no_case; -using support::contains; -using support::getVectorFromString; -using support::ltrim; -using support::prefixIs; -using support::rtrim; -using support::split; -using support::subst; -using support::token; -using support::trim; - -namespace biblio { - -namespace { - -vector const init_possible_cite_commands() -{ - char const * const pos[] = { - "cite", - "citet", "citep", "citealt", "citealp", - "citeauthor", "citeyear", "citeyearpar", - "citet*", "citep*", "citealt*", "citealp*", "citeauthor*", - "Citet", "Citep", "Citealt", "Citealp", "Citeauthor", - "Citet*", "Citep*", "Citealt*", "Citealp*", "Citeauthor*", - "fullcite", - "footcite", "footcitet", "footcitep", "footcitealt", - "footcitealp", "footciteauthor", "footciteyear", - "footciteyearpar", - "citefield", - "citetitle", - "cite*" - }; - size_t const size_pos = sizeof(pos) / sizeof(pos[0]); - - return vector(pos, pos + size_pos); -} - - -vector const & possible_cite_commands() -{ - static vector const pos = init_possible_cite_commands(); - return pos; -} - - -bool is_possible_cite_command(string const & input) -{ - vector const & possibles = possible_cite_commands(); - vector::const_iterator const end = possibles.end(); - return std::find(possibles.begin(), end, input) != end; -} - - -string const default_cite_command(CiteEngine engine) -{ - string str; - switch (engine) { - case ENGINE_BASIC: - str = "cite"; - break; - case ENGINE_NATBIB_AUTHORYEAR: - str = "citet"; - break; - case ENGINE_NATBIB_NUMERICAL: - str = "citep"; - break; - case ENGINE_JURABIB: - str = "cite"; - break; - } - return str; -} - - -static const docstring TheBibliographyRef(from_ascii("TheBibliographyRef")); - -} // namespace anon - - -string const asValidLatexCommand(string const & input, - CiteEngine const engine) -{ - string const default_str = default_cite_command(engine); - if (!is_possible_cite_command(input)) - return default_str; - - string output; - switch (engine) { - case ENGINE_BASIC: - output = default_str; - break; - - case ENGINE_NATBIB_AUTHORYEAR: - case ENGINE_NATBIB_NUMERICAL: - if (input == "cite" || input == "citefield" || - input == "citetitle" || input == "cite*") - output = default_str; - else if (prefixIs(input, "foot")) - output = input.substr(4); - else - output = input; - break; - - case ENGINE_JURABIB: { - // Jurabib does not support the 'uppercase' natbib style. - if (input[0] == 'C') - output = string(1, 'c') + input.substr(1); - else - output = input; - - // Jurabib does not support the 'full' natbib style. - string::size_type const n = output.size() - 1; - if (output != "cite*" && output[n] == '*') - output = output.substr(0, n); - - break; - } - } - - return output; -} - - -docstring const familyName(docstring const & name) -{ - if (name.empty()) - return docstring(); - - // Very simple parser - docstring fname = name; - - // possible authorname combinations are: - // "Surname, FirstName" - // "Surname, F." - // "FirstName Surname" - // "F. Surname" - docstring::size_type idx = fname.find(','); - if (idx != docstring::npos) - return ltrim(fname.substr(0, idx)); - idx = fname.rfind('.'); - if (idx != docstring::npos && idx + 1 < fname.size()) - fname = ltrim(fname.substr(idx + 1)); - // test if we have a LaTeX Space in front - if (fname[0] == '\\') - return fname.substr(2); - - return rtrim(fname); -} - - -docstring const getAbbreviatedAuthor(InfoMap const & map, string const & key) -{ - BOOST_ASSERT(!map.empty()); - - InfoMap::const_iterator it = map.find(key); - if (it == map.end()) - return docstring(); - docstring const & data = it->second; - - // Is the entry a BibTeX one or one from lyx-layout "bibliography"? - docstring::size_type const pos = data.find(TheBibliographyRef); - if (pos != docstring::npos) { - if (pos <= 2) { - return docstring(); - } - - docstring const opt = trim(data.substr(0, pos - 1)); - if (opt.empty()) - return docstring(); - - docstring authors; - split(opt, authors, '('); - return authors; - } - - docstring author = parseBibTeX(data, "author"); - - if (author.empty()) - author = parseBibTeX(data, "editor"); - - if (author.empty()) { - author = parseBibTeX(data, "key"); - if (author.empty()) - // FIXME UNICODE - return from_utf8(key); - return author; - } - - vector const authors = getVectorFromString(author, from_ascii(" and ")); - if (authors.empty()) - return author; - - if (authors.size() == 2) - return bformat(_("%1$s and %2$s"), - familyName(authors[0]), familyName(authors[1])); - - if (authors.size() > 2) - return bformat(_("%1$s et al."), familyName(authors[0])); - - return familyName(authors[0]); -} - - -docstring const getYear(InfoMap const & map, string const & key) -{ - BOOST_ASSERT(!map.empty()); - - InfoMap::const_iterator it = map.find(key); - if (it == map.end()) - return docstring(); - docstring const & data = it->second; - - // Is the entry a BibTeX one or one from lyx-layout "bibliography"? - docstring::size_type const pos = data.find(TheBibliographyRef); - if (pos != docstring::npos) { - if (pos <= 2) { - return docstring(); - } - - docstring const opt = - trim(data.substr(0, pos - 1)); - if (opt.empty()) - return docstring(); - - docstring authors; - docstring const tmp = split(opt, authors, '('); - docstring year; - split(tmp, year, ')'); - return year; - - } - - docstring year = parseBibTeX(data, "year"); - if (year.empty()) - year = _("No year"); - - return year; -} - - -namespace { - -// A functor for use with std::sort, leading to case insensitive sorting -class compareNoCase: public std::binary_function -{ -public: - bool operator()(string const & s1, string const & s2) const { - return compare_ascii_no_case(s1, s2) < 0; - } -}; - -} // namespace anon - - -vector const getKeys(InfoMap const & map) -{ - vector bibkeys; - InfoMap::const_iterator it = map.begin(); - InfoMap::const_iterator end = map.end(); - for (; it != end; ++it) { - bibkeys.push_back(it->first); - } - - std::sort(bibkeys.begin(), bibkeys.end(), compareNoCase()); - return bibkeys; -} - - -docstring const getInfo(InfoMap const & map, string const & key) -{ - BOOST_ASSERT(!map.empty()); - - InfoMap::const_iterator it = map.find(key); - if (it == map.end()) - return docstring(); - docstring const & data = it->second; - - // is the entry a BibTeX one or one from lyx-layout "bibliography"? - docstring::size_type const pos = data.find(TheBibliographyRef); - if (pos != docstring::npos) { - docstring::size_type const pos2 = pos + TheBibliographyRef.size(); - docstring const info = trim(data.substr(pos2)); - return info; - } - - // Search for all possible "required" keys - docstring author = parseBibTeX(data, "author"); - if (author.empty()) - author = parseBibTeX(data, "editor"); - - docstring year = parseBibTeX(data, "year"); - docstring title = parseBibTeX(data, "title"); - docstring booktitle = parseBibTeX(data, "booktitle"); - docstring chapter = parseBibTeX(data, "chapter"); - docstring number = parseBibTeX(data, "number"); - docstring volume = parseBibTeX(data, "volume"); - docstring pages = parseBibTeX(data, "pages"); - docstring annote = parseBibTeX(data, "annote"); - docstring media = parseBibTeX(data, "journal"); - if (media.empty()) - media = parseBibTeX(data, "publisher"); - if (media.empty()) - media = parseBibTeX(data, "school"); - if (media.empty()) - media = parseBibTeX(data, "institution"); - - odocstringstream result; - if (!author.empty()) - result << author << ", "; - if (!title.empty()) - result << title; - if (!booktitle.empty()) - result << ", in " << booktitle; - if (!chapter.empty()) - result << ", Ch. " << chapter; - if (!media.empty()) - result << ", " << media; - if (!volume.empty()) - result << ", vol. " << volume; - if (!number.empty()) - result << ", no. " << number; - if (!pages.empty()) - result << ", pp. " << pages; - if (!year.empty()) - result << ", " << year; - if (!annote.empty()) - result << "\n\n" << annote; - - docstring const result_str = rtrim(result.str()); - if (!result_str.empty()) - return result_str; - - // This should never happen (or at least be very unusual!) - return data; -} - - -namespace { - -// Escape special chars. -// All characters are literals except: '.|*?+(){}[]^$\' -// These characters are literals when preceded by a "\", which is done here -// @todo: This function should be moved to support, and then the test in tests -// should be moved there as well. -string const escape_special_chars(string const & expr) -{ - // Search for all chars '.|*?+(){}[^$]\' - // Note that '[' and '\' must be escaped. - // This is a limitation of boost::regex, but all other chars in BREs - // are assumed literal. - boost::regex reg("[].|*?+(){}^$\\[\\\\]"); - - // $& is a perl-like expression that expands to all - // of the current match - // The '$' must be prefixed with the escape character '\' for - // boost to treat it as a literal. - // Thus, to prefix a matched expression with '\', we use: - return boost::regex_replace(expr, reg, "\\\\$&"); -} - - -// A functor for use with std::find_if, used to ascertain whether a -// data entry matches the required regex_ -// @throws: boost::regex_error if the supplied regex pattern is not valid -// @todo: This function should be moved to support. -class RegexMatch : public std::unary_function -{ -public: - // re and icase are used to construct an instance of boost::RegEx. - // if icase is true, then matching is insensitive to case - RegexMatch(InfoMap const & m, string const & re, bool icase) - : map_(m), regex_(re, icase) {} - - bool operator()(string const & key) const { - // the data searched is the key + its associated BibTeX/biblio - // fields - string data = key; - InfoMap::const_iterator info = map_.find(key); - if (info != map_.end()) - // FIXME UNICODE - data += ' ' + to_utf8(info->second); - - // Attempts to find a match for the current RE - // somewhere in data. - return boost::regex_search(data, regex_); - } -private: - InfoMap const map_; - mutable boost::regex regex_; -}; - -} // namespace anon - - -vector::const_iterator -searchKeys(InfoMap const & theMap, - vector const & keys, - string const & search_expr, - vector::const_iterator start, - Search type, - Direction dir, - bool caseSensitive) -{ - // Preliminary checks - if (start < keys.begin() || start >= keys.end()) - return keys.end(); - - string expr = trim(search_expr); - if (expr.empty()) - return keys.end(); - - if (type == SIMPLE) - // We must escape special chars in the search_expr so that - // it is treated as a simple string by boost::regex. - expr = escape_special_chars(expr); - - try { - // Build the functor that will be passed to find_if. - RegexMatch const match(theMap, expr, !caseSensitive); - - // Search the vector of 'keys' from 'start' for one - // that matches the predicate 'match'. Searching can - // be forward or backward from start. - if (dir == FORWARD) - return std::find_if(start, keys.end(), match); - - vector::const_reverse_iterator rit(start); - vector::const_reverse_iterator rend = keys.rend(); - rit = std::find_if(rit, rend, match); - - if (rit == rend) - return keys.end(); - // This is correct and always safe. - // (See Meyer's Effective STL, Item 28.) - return (++rit).base(); - } - catch (boost::regex_error &) { - return keys.end(); - } -} - - -docstring const parseBibTeX(docstring data, string const & findkey) -{ - // at first we delete all characters right of '%' and - // replace tabs through a space and remove leading spaces - // we read the data line by line so that the \n are - // ignored, too. - docstring data_; - int Entries = 0; - docstring dummy = token(data,'\n', Entries); - while (!dummy.empty()) { - // no tabs - dummy = subst(dummy, '\t', ' '); - // no leading spaces - dummy = ltrim(dummy); - // ignore lines with a beginning '%' or ignore all right of % - docstring::size_type const idx = - dummy.empty() ? docstring::npos : dummy.find('%'); - if (idx != docstring::npos) - // Check if this is really a comment or just "\%" - if (idx == 0 || dummy[idx - 1] != '\\') - dummy.erase(idx, docstring::npos); - else - // This is "\%", so just erase the '\' - dummy.erase(idx - 1, 1); - // do we have a new token or a new line of - // the same one? In the first case we ignore - // the \n and in the second we replace it - // with a space - if (!dummy.empty()) { - if (!contains(dummy, '=')) - data_ += ' ' + dummy; - else - data_ += dummy; - } - dummy = token(data, '\n', ++Entries); - } - - // replace double commas with "" for easy scanning - data = subst(data_, from_ascii(",,"), from_ascii("\"\"")); - - // unlikely! - if (data.empty()) - return docstring(); - - // now get only the important line of the bibtex entry. - // all entries are devided by ',' except the last one. - data += ','; - // now we have same behaviour for all entries because the last one - // is "blah ... }" - Entries = 0; - bool found = false; - // parsing of title and booktitle is different from the - // others, because booktitle contains title - do { - dummy = token(data, ',', Entries++); - if (!dummy.empty()) { - found = contains(ascii_lowercase(dummy), from_ascii(findkey)); - if (findkey == "title" && - contains(ascii_lowercase(dummy), from_ascii("booktitle"))) - found = false; - } - } while (!found && !dummy.empty()); - if (dummy.empty()) - // no such keyword - return docstring(); - - // we are not sure, if we get all, because "key= "blah, blah" is - // allowed. - // Therefore we read all until the next "=" character, which follows a - // new keyword - docstring keyvalue = dummy; - dummy = token(data, ',', Entries++); - while (!contains(dummy, '=') && !dummy.empty()) { - keyvalue += ',' + dummy; - dummy = token(data, ',', Entries++); - } - - // replace double "" with originals ,, (two commas) - // leaving us with the all-important line - data = subst(keyvalue, from_ascii("\"\""), from_ascii(",,")); - - // Clean-up. - // 1. Spaces - data = rtrim(data); - // 2. if there is no opening '{' then a closing '{' is probably cruft. - if (!contains(data, '{')) - data = rtrim(data, "}"); - // happens, when last keyword - docstring::size_type const idx = - !data.empty() ? data.find('=') : docstring::npos; - - if (idx == docstring::npos) - return docstring(); - - data = trim(data.substr(idx)); - - // a valid entry? - if (data.length() < 2 || data[0] != '=') - return docstring(); - else { - // delete '=' and the following spaces - data = ltrim(data, " ="); - if (data.length() < 2) { - // not long enough to find delimiters - return data; - } else { - docstring::size_type keypos = 1; - char_type enclosing; - if (data[0] == '{') { - enclosing = '}'; - } else if (data[0] == '"') { - enclosing = '"'; - } else { - // no {} and no "", pure data but with a - // possible ',' at the end - return rtrim(data, ","); - } - docstring tmp = data.substr(keypos); - while (tmp.find('{') != docstring::npos && - tmp.find('}') != docstring::npos && - tmp.find('{') < tmp.find('}') && - tmp.find('{') < tmp.find(enclosing)) { - - keypos += tmp.find('{') + 1; - tmp = data.substr(keypos); - keypos += tmp.find('}') + 1; - tmp = data.substr(keypos); - } - if (tmp.find(enclosing) == docstring::npos) - return data; - else { - keypos += tmp.find(enclosing); - return data.substr(1, keypos - 1); - } - } - } -} - - -namespace { - - -char const * const citeCommands[] = { - "cite", "citet", "citep", "citealt", "citealp", "citeauthor", - "citeyear", "citeyearpar" }; - -unsigned int const nCiteCommands = - sizeof(citeCommands) / sizeof(char *); - -CiteStyle const citeStyles[] = { - CITE, CITET, CITEP, CITEALT, CITEALP, - CITEAUTHOR, CITEYEAR, CITEYEARPAR }; - -unsigned int const nCiteStyles = - sizeof(citeStyles) / sizeof(CiteStyle); - -CiteStyle const citeStylesFull[] = { - CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR }; - -unsigned int const nCiteStylesFull = - sizeof(citeStylesFull) / sizeof(CiteStyle); - -CiteStyle const citeStylesUCase[] = { - CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR }; - -unsigned int const nCiteStylesUCase = - sizeof(citeStylesUCase) / sizeof(CiteStyle); - -} // namespace anon - - -CitationStyle::CitationStyle(string const & command) - : style(CITE), full(false), forceUCase(false) -{ - if (command.empty()) - return; - - string cmd = command; - if (cmd[0] == 'C') { - forceUCase = true; - cmd[0] = 'c'; - } - - string::size_type const n = cmd.size() - 1; - if (cmd != "cite" && cmd[n] == '*') { - full = true; - cmd = cmd.substr(0,n); - } - - char const * const * const last = citeCommands + nCiteCommands; - char const * const * const ptr = std::find(citeCommands, last, cmd); - - if (ptr != last) { - size_t idx = ptr - citeCommands; - style = citeStyles[idx]; - } -} - - -string const CitationStyle::asLatexStr() const -{ - string cite = citeCommands[style]; - if (full) { - CiteStyle const * last = citeStylesFull + nCiteStylesFull; - if (std::find(citeStylesFull, last, style) != last) - cite += '*'; - } - - if (forceUCase) { - CiteStyle const * last = citeStylesUCase + nCiteStylesUCase; - if (std::find(citeStylesUCase, last, style) != last) - cite[0] = 'C'; - } - - return cite; -} - - -vector const getCiteStyles(CiteEngine const engine) -{ - unsigned int nStyles = 0; - unsigned int start = 0; - - switch (engine) { - case ENGINE_BASIC: - nStyles = 1; - start = 0; - break; - case ENGINE_NATBIB_AUTHORYEAR: - case ENGINE_NATBIB_NUMERICAL: - nStyles = nCiteStyles - 1; - start = 1; - break; - case ENGINE_JURABIB: - nStyles = nCiteStyles; - start = 0; - break; - } - - typedef vector cite_vec; - - cite_vec styles(nStyles); - cite_vec::size_type i = 0; - int j = start; - for (; i != styles.size(); ++i, ++j) - styles[i] = citeStyles[j]; - - return styles; -} - - -vector const -getNumericalStrings(string const & key, - InfoMap const & map, vector const & styles) -{ - if (map.empty()) - return vector(); - - docstring const author = getAbbreviatedAuthor(map, key); - docstring const year = getYear(map, key); - if (author.empty() || year.empty()) - return vector(); - - vector vec(styles.size()); - for (vector::size_type i = 0; i != vec.size(); ++i) { - docstring str; - - switch (styles[i]) { - case CITE: - case CITEP: - str = from_ascii("[#ID]"); - break; - - case CITET: - str = author + " [#ID]"; - break; - - case CITEALT: - str = author + " #ID"; - break; - - case CITEALP: - str = from_ascii("#ID"); - break; - - case CITEAUTHOR: - str = author; - break; - - case CITEYEAR: - str = year; - break; - - case CITEYEARPAR: - str = '(' + year + ')'; - break; - } - - vec[i] = str; - } - - return vec; -} - - -vector const -getAuthorYearStrings(string const & key, - InfoMap const & map, vector const & styles) -{ - if (map.empty()) - return vector(); - - docstring const author = getAbbreviatedAuthor(map, key); - docstring const year = getYear(map, key); - if (author.empty() || year.empty()) - return vector(); - - vector vec(styles.size()); - for (vector::size_type i = 0; i != vec.size(); ++i) { - docstring str; - - switch (styles[i]) { - case CITE: - // jurabib only: Author/Annotator - // (i.e. the "before" field, 2nd opt arg) - str = author + "/<" + _("before") + '>'; - break; - - case CITET: - str = author + " (" + year + ')'; - break; - - case CITEP: - str = '(' + author + ", " + year + ')'; - break; - - case CITEALT: - str = author + ' ' + year ; - break; - - case CITEALP: - str = author + ", " + year ; - break; - - case CITEAUTHOR: - str = author; - break; - - case CITEYEAR: - str = year; - break; - - case CITEYEARPAR: - str = '(' + year + ')'; - break; - } - - vec[i] = str; - } - - return vec; -} - -} // namespace biblio - namespace frontend { vector const getFamilyData() diff --git a/src/frontends/controllers/frontend_helpers.h b/src/frontends/controllers/frontend_helpers.h index 2d71a2668b..896c65b46e 100644 --- a/src/frontends/controllers/frontend_helpers.h +++ b/src/frontends/controllers/frontend_helpers.h @@ -26,157 +26,8 @@ class Buffer; -/** Functions of use to citation and bibtex GUI controllers and views */ -namespace lyx { -namespace biblio { - -enum CiteEngine { - ENGINE_BASIC, - ENGINE_NATBIB_AUTHORYEAR, - ENGINE_NATBIB_NUMERICAL, - ENGINE_JURABIB -}; - - -enum CiteStyle { - CITE, - CITET, - CITEP, - CITEALT, - CITEALP, - CITEAUTHOR, - CITEYEAR, - CITEYEARPAR -}; - - -enum Search { - SIMPLE, - REGEX -}; - - -enum Direction { - FORWARD, - BACKWARD -}; - - -/** Each citation engine recognizes only a subset of all possible - * citation commands. Given a latex command \c input, this function - * returns an appropriate command, valid for \c engine. - */ -std::string const asValidLatexCommand(std::string const & input, - CiteEngine const engine); - -/// First entry is the bibliography key, second the data -typedef std::map InfoMap; - -/// Returns a vector of bibliography keys -std::vector const getKeys(InfoMap const &); - -/** Returns the BibTeX data associated with a given key. - Empty if no info exists. */ -docstring const getInfo(InfoMap const &, std::string const & key); - -/// return the year from the bibtex data record -docstring const getYear(InfoMap const & map, std::string const & key); - -/// return the short form of an authorlist -docstring const getAbbreviatedAuthor(InfoMap const & map, std::string const & key); - -// return only the family name -docstring const familyName(docstring const & name); - -/** Search a BibTeX info field for the given key and return the - associated field. */ -docstring const parseBibTeX(docstring data, std::string const & findkey); - -/** Returns an iterator to the first key that meets the search - criterion, or end() if unsuccessful. - - User supplies : - the InfoMap of bibkeys info, - the vector of keys to be searched, - the search criterion, - an iterator defining the starting point of the search, - an enum defining a Simple or Regex search, - an enum defining the search direction. -*/ - -std::vector::const_iterator -searchKeys(InfoMap const & map, - std::vector const & keys_to_search, - docstring const & search_expression, - std::vector::const_iterator start, - Search, - Direction, - bool caseSensitive=false); - - -class CitationStyle { -public: - /// - CitationStyle(CiteStyle s = CITE, bool f = false, bool force = false) - : style(s), full(f), forceUCase(force) {} - /// \param latex_str a LaTeX command, "cite", "Citep*", etc - CitationStyle(std::string const & latex_str); - /// - std::string const asLatexStr() const; - /// - CiteStyle style; - /// - bool full; - /// - bool forceUCase; -}; - - -/// Returns a vector of available Citation styles. -std::vector const getCiteStyles(CiteEngine const ); - -/** - "Translates" the available Citation Styles into strings for this key. - The returned string is displayed by the GUI. - - - [XX] is used in place of the actual reference - Eg, the vector will contain: [XX], Jones et al. [XX], ... - - User supplies : - the key, - the InfoMap of bibkeys info, - the available citation styles -*/ -std::vector const -getNumericalStrings(std::string const & key, - InfoMap const & map, - std::vector const & styles); - -/** - "Translates" the available Citation Styles into strings for this key. - The returned string is displayed by the GUI. - - Eg, the vector will contain: - Jones et al. (1990), (Jones et al. 1990), Jones et al. 1990, ... - - User supplies : - the key, - the InfoMap of bibkeys info, - the available citation styles -*/ -std::vector const -getAuthorYearStrings(std::string const & key, - InfoMap const & map, - std::vector const & styles); - -} // namespace biblio -} // namespace lyx - - class Color_color; - /** Functions of use to the character GUI controller and view */ namespace lyx { namespace frontend { diff --git a/src/insets/Inset.h b/src/insets/Inset.h index 614342084d..bae6669ee6 100644 --- a/src/insets/Inset.h +++ b/src/insets/Inset.h @@ -31,6 +31,7 @@ class BufferView; class ParIterator; class ParConstIterator; class CursorSlice; +class InsetIterator; class FuncRequest; class FuncStatus; class InsetLayout; @@ -437,9 +438,14 @@ public: /// Add an entry to the TocList /// pit is the ParConstIterator of the paragraph containing the inset virtual void addToToc(TocList &, Buffer const &, ParConstIterator const &) const {} - // Update the counters of this inset and of its contents + /// Fill keys with BibTeX information + virtual void fillWithBibKeys(Buffer const &, + std::vector > &, + InsetIterator const &) const { return; } + /// Update the counters of this inset and of its contents virtual void updateLabels(Buffer const &, ParIterator const &) {} + public: /// returns LyX code associated with the inset. Used for TOC, ...) virtual Code lyxCode() const { return NO_CODE; } diff --git a/src/insets/InsetBibitem.cpp b/src/insets/InsetBibitem.cpp index b315608498..166bcdd5a8 100644 --- a/src/insets/InsetBibitem.cpp +++ b/src/insets/InsetBibitem.cpp @@ -12,11 +12,13 @@ #include "InsetBibitem.h" +#include "Biblio.h" #include "Buffer.h" #include "BufferView.h" #include "DispatchResult.h" #include "FuncRequest.h" #include "Font.h" +#include "InsetIterator.h" #include "Lexer.h" #include "Paragraph.h" #include "ParagraphList.h" @@ -184,4 +186,17 @@ docstring const bibitemWidest(Buffer const & buffer) } +void InsetBibitem::fillWithBibKeys(Buffer const & buf, + std::vector > & keys, + InsetIterator const & it) const +{ + string const key = to_utf8(getParam("key")); + docstring const label = getParam("label"); + DocIterator doc_it(it); + doc_it.forwardPos(); + docstring const ref = doc_it.paragraph().asString(buf, false); + docstring const info = label + biblio::TheBibliographyRef + ref; + keys.push_back(std::pair(key, info)); +} + } // namespace lyx diff --git a/src/insets/InsetBibitem.h b/src/insets/InsetBibitem.h index 6dfaf2cc22..98db70e68d 100644 --- a/src/insets/InsetBibitem.h +++ b/src/insets/InsetBibitem.h @@ -43,6 +43,11 @@ public: docstring const getBibLabel() const; /// int plaintext(Buffer const &, odocstream &, OutputParams const &) const; + /// + virtual void fillWithBibKeys(Buffer const &, + std::vector > &, + InsetIterator const &) const; + protected: /// virtual void doDispatch(Cursor & cur, FuncRequest & cmd); diff --git a/src/insets/InsetBibtex.cpp b/src/insets/InsetBibtex.cpp index f8ea110504..7ba0a2814b 100644 --- a/src/insets/InsetBibtex.cpp +++ b/src/insets/InsetBibtex.cpp @@ -556,7 +556,8 @@ namespace { // This method returns a comma separated list of Bibtex entries void InsetBibtex::fillWithBibKeys(Buffer const & buffer, - std::vector > & keys) const + std::vector > & keys, + InsetIterator const & /*di*/) const { vector const files = getFiles(buffer); for (vector::const_iterator it = files.begin(); diff --git a/src/insets/InsetBibtex.h b/src/insets/InsetBibtex.h index 94f8836ec1..82defd4fd7 100644 --- a/src/insets/InsetBibtex.h +++ b/src/insets/InsetBibtex.h @@ -38,8 +38,9 @@ public: /// int latex(Buffer const &, odocstream &, OutputParams const &) const; /// - void fillWithBibKeys(Buffer const & buffer, - std::vector > & keys) const; + virtual void fillWithBibKeys(Buffer const &, + std::vector > &, + InsetIterator const &) const; /// std::vector const getFiles(Buffer const &) const; /// diff --git a/src/insets/InsetCitation.h b/src/insets/InsetCitation.h index e705fcb6bd..1d469c2e2c 100644 --- a/src/insets/InsetCitation.h +++ b/src/insets/InsetCitation.h @@ -16,7 +16,7 @@ #include "InsetCommand.h" -#include "frontends/controllers/frontend_helpers.h" +#include "Biblio.h" namespace lyx { diff --git a/src/insets/InsetInclude.cpp b/src/insets/InsetInclude.cpp index ac0eb6b06a..4b7ef4e055 100644 --- a/src/insets/InsetInclude.cpp +++ b/src/insets/InsetInclude.cpp @@ -728,7 +728,8 @@ void InsetInclude::getLabelList(Buffer const & buffer, void InsetInclude::fillWithBibKeys(Buffer const & buffer, - std::vector > & keys) const + std::vector > & keys, + InsetIterator const & /*di*/) const { if (loadIfNeeded(buffer, params_)) { string const included_file = includedFilename(buffer, params_).absFilename(); diff --git a/src/insets/InsetInclude.h b/src/insets/InsetInclude.h index 4eb0409269..a89d4df765 100644 --- a/src/insets/InsetInclude.h +++ b/src/insets/InsetInclude.h @@ -59,8 +59,9 @@ public: * \param buffer the Buffer containing this inset. * \param keys the list of bibkeys in the child buffer. */ - void fillWithBibKeys(Buffer const & buffer, - std::vector > & keys) const; + virtual void fillWithBibKeys(Buffer const &, + std::vector > &, + InsetIterator const & /*di*/) const; /** Update the cache with all bibfiles in use of the child buffer * (including bibfiles of grandchild documents). * Does nothing if the child document is not loaded to prevent