From b3bff4232a9d0410bae21b9d74584ee469c70e19 Mon Sep 17 00:00:00 2001 From: Richard Heck Date: Mon, 20 Aug 2007 16:30:02 +0000 Subject: [PATCH] Improvements to the citation dialog UI. It is now possible to search particular fields (author, title, etc) and to filter keys by entry types (article, book, etc). The changes are essentially of three types. First, the std::maps previously used here for storing BibTeX data have become full-fledged classes and so what were previously just a bunch of functions have now become methods of these new classes. This leads to a lot of minor changes in other files, to adapt to the new signature. The changes in insets/ are mostly of this kind. Second, there are some slightly more substantial changes in src/insets/InsetBibtex.cpp. We now store lists of the field names and entry types that have been used. Finally, there are the changes to the citation UI. These are mostly straightforward, though adding the new functionality exposed some inadequacies in the previous code that also had to be fixed. One other change: BibTeX keys are now docstring. This solves some Unicode issues. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@19657 a592a061-630c-0410-9148-cb99ea01b6c8 --- development/scons/scons_manifest.py | 1 - src/Biblio.cpp | 749 +++++++----------- src/Biblio.h | 266 +++---- src/Biblio_typedefs.h | 51 -- src/Buffer.cpp | 18 +- src/Buffer.h | 3 - src/Makefile.am | 1 - src/frontends/controllers/ControlCitation.cpp | 83 +- src/frontends/controllers/ControlCitation.h | 27 +- src/frontends/qt4/QCitation.cpp | 54 +- src/frontends/qt4/QCitation.h | 16 +- src/frontends/qt4/QCitationDialog.cpp | 167 +++- src/frontends/qt4/QCitationDialog.h | 27 +- src/frontends/qt4/ui/CitationUi.ui | 288 ++++--- src/insets/Inset.h | 4 +- src/insets/InsetBibitem.cpp | 9 +- src/insets/InsetBibitem.h | 4 +- src/insets/InsetBibtex.cpp | 10 +- src/insets/InsetBibtex.h | 4 +- src/insets/InsetCitation.cpp | 147 +++- src/insets/InsetInclude.cpp | 4 +- src/insets/InsetInclude.h | 4 +- 22 files changed, 1037 insertions(+), 900 deletions(-) diff --git a/development/scons/scons_manifest.py b/development/scons/scons_manifest.py index aed1ae1c45..5ea690fe8c 100644 --- a/development/scons/scons_manifest.py +++ b/development/scons/scons_manifest.py @@ -34,7 +34,6 @@ src_header_files = Split(''' ASpell_local.h Author.h Biblio.h - Biblio_typedefs.h Bidi.h Box.h BranchList.h diff --git a/src/Biblio.cpp b/src/Biblio.cpp index e314afb6d6..a295defa19 100644 --- a/src/Biblio.cpp +++ b/src/Biblio.cpp @@ -1,11 +1,11 @@ /** - * \file Biblio.cpp + * \file BiblioInfo.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) + * \author Richard Heck * * Full author contact details are available in file CREDITS. */ @@ -13,7 +13,8 @@ #include #include "Biblio.h" - +#include "Buffer.h" +#include "BufferParams.h" #include "buffer_funcs.h" #include "gettext.h" #include "InsetIterator.h" @@ -32,297 +33,150 @@ using std::string; using std::vector; using std::pair; using std::endl; +using std::set; namespace lyx { -using support::ascii_lowercase; using support::bformat; -using support::compare_ascii_no_case; -using support::contains; +using support::compare_no_case; 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 { +//////////////////////////////////////// +// +// BibTeXInfo +// +//////////////////////////////////////// - -BibTeXInfo::BibTeXInfo(): isBibTeX(true) +BibTeXInfo::BibTeXInfo() : isBibTeX(true) {} -BibTeXInfo::BibTeXInfo(bool isBibTeX): isBibTeX(isBibTeX) -{}; +bool BibTeXInfo::hasField(docstring const & field) const +{ + return count(field) == 1; +} -bool BibTeXInfo::hasKey(docstring const & key) +docstring const & BibTeXInfo::getValueForField(docstring const & field) const { - const_iterator it = find(key); - return it == end(); + BibTeXInfo::const_iterator it = find(field); + if (it != end()) + return it->second; + static docstring const empty_value = docstring(); + return empty_value; +} + + +docstring const & BibTeXInfo::getValueForField(string const & field) const +{ + return getValueForField(from_ascii(field)); } 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) + docstring const familyName(docstring const & name) { - 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; + 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); } - - } // 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(BibKeyList const & map, string const & key) +docstring const BibTeXInfo::getAbbreviatedAuthor() const { - BOOST_ASSERT(!map.empty()); - BibKeyList::const_iterator it = map.find(key); - if (it == map.end()) - return docstring(); - BibTeXInfo const & data = it->second; - - if (!data.isBibTeX) + if (!isBibTeX) return docstring(); - docstring author = getValueForKey(data, "author"); + docstring author = getValueForField("author"); if (author.empty()) { - author = getValueForKey(data, "editor"); - if (author.empty()) { - author = getValueForKey(data, "key"); - if (author.empty()) - // FIXME UNICODE - return from_utf8(key); - else - return author; //this is the key - } + author = getValueForField("editor"); + if (author.empty()) + return bibKey; } //OK, we've got some names. Let's format them. - //try to split the author list on " and " - vector const authors = getVectorFromString(author, from_ascii(" and ")); + //Try to split the author list on " and " + vector const authors = + getVectorFromString(author, from_ascii(" and ")); if (authors.size() == 2) return bformat(_("%1$s and %2$s"), - familyName(authors[0]), familyName(authors[1])); + familyName(authors[0]), familyName(authors[1])); else if (authors.size() > 2) return bformat(_("%1$s et al."), familyName(authors[0])); else return familyName(authors[0]); } - -docstring const getYear(BibKeyList const & map, string const & key) +docstring const BibTeXInfo::getYear() const { - BOOST_ASSERT(!map.empty()); - - BibKeyList::const_iterator it = map.find(key); - if (it == map.end()) - return docstring(); - BibTeXInfo const & data = it->second; - - if (!data.isBibTeX) + if (!isBibTeX) return docstring(); - docstring year = getValueForKey(data, "year"); + docstring year = getValueForField("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(BibKeyList const & map) -{ - vector bibkeys; - BibKeyList::const_iterator it = map.begin(); - BibKeyList::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(BibKeyList const & map, string const & key) +docstring const BibTeXInfo::getInfo() const { - BOOST_ASSERT(!map.empty()); - - BibKeyList::const_iterator it = map.find(key); - if (it == map.end()) - return docstring(); - BibTeXInfo const & data = it->second; - - if (!data.isBibTeX) { - BibTeXInfo::const_iterator it3 = data.find(from_ascii("ref")); - return it3->second; + if (!isBibTeX) { + BibTeXInfo::const_iterator it = find(from_ascii("ref")); + return it->second; } //FIXME - //This could be made alot better using the biblio::TheEntryType + //This could be made alot better using the entryType //field to customize the output based upon entry type. //Search for all possible "required" fields - docstring author = getValueForKey(data, "author"); + docstring author = getValueForField("author"); if (author.empty()) - author = getValueForKey(data, "editor"); + author = getValueForField("editor"); - docstring year = getValueForKey(data, "year"); - docstring title = getValueForKey(data, "title"); - docstring docLoc = getValueForKey(data, "pages"); + docstring year = getValueForField("year"); + docstring title = getValueForField("title"); + docstring docLoc = getValueForField("pages"); if (docLoc.empty()) { - docLoc = getValueForKey(data, "chapter"); + docLoc = getValueForField("chapter"); if (!docLoc.empty()) docLoc = from_ascii("Ch. ") + docLoc; } else docLoc = from_ascii("pp. ") + docLoc; - docstring media = getValueForKey(data, "journal"); + docstring media = getValueForField("journal"); if (media.empty()) { - media = getValueForKey(data, "publisher"); + media = getValueForField("publisher"); if (media.empty()) { - media = getValueForKey(data, "school"); + media = getValueForField("school"); if (media.empty()) - media = getValueForKey(data, "institution"); + media = getValueForField("institution"); } } - docstring volume = getValueForKey(data, "volume"); + docstring volume = getValueForField("volume"); odocstringstream result; if (!author.empty()) @@ -341,121 +195,246 @@ docstring const getInfo(BibKeyList const & map, string const & key) return result_str; // This should never happen (or at least be very unusual!) - return docstring(); + return docstring(); } +//////////////////////////////////////// +// +// BiblioInfo +// +//////////////////////////////////////// + namespace { +// A functor for use with std::sort, leading to case insensitive sorting + class compareNoCase: public std::binary_function + { + public: + bool operator()(docstring const & s1, docstring const & s2) const { + return compare_no_case(s1, s2) < 0; + } + }; +} // namespace anon + -// 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) +vector const BiblioInfo::getKeys() const { - // 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, "\\\\$&"); + vector bibkeys; + BiblioInfo::const_iterator it = begin(); + for (; it != end(); ++it) + bibkeys.push_back(it->first); + std::sort(bibkeys.begin(), bibkeys.end(), compareNoCase()); + return bibkeys; } -// 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 +vector const BiblioInfo::getFields() const { - public: -// re and icase are used to construct an instance of boost::RegEx. -// if icase is true, then matching is insensitive to case - RegexMatch(BibKeyList const & m, string const & re, bool icase) - : map_(m), regex_(re, icase) {} - - bool operator()(string const & key) const { - //FIXME This should search the monolith. - BibKeyList::const_iterator info = map_.find(key); - if (info == map_.end()) - return false; - - BibTeXInfo const kvm = info->second; - string const data = key + ' ' + to_utf8(kvm.allData); - - return boost::regex_search(data, regex_); - } - private: - BibKeyList const map_; - mutable boost::regex regex_; -}; + vector bibfields; + set::const_iterator it = fieldNames.begin(); + set::const_iterator end = fieldNames.end(); + for (; it != end; ++it) + bibfields.push_back(*it); + std::sort(bibfields.begin(), bibfields.end()); + return bibfields; +} -} // namespace anon + +vector const BiblioInfo::getEntries() const +{ + vector bibentries; + set::const_iterator it = entryTypes.begin(); + set::const_iterator end = entryTypes.end(); + for (; it != end; ++it) + bibentries.push_back(*it); + std::sort(bibentries.begin(), bibentries.end()); + return bibentries; +} -vector::const_iterator searchKeys(BibKeyList const & theMap, - vector const & keys, - string const & search_expr, - vector::const_iterator start, - Search type, Direction dir, bool caseSensitive) +docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key) const { - // 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(); - } + BiblioInfo::const_iterator it = find(key); + if (it == end()) + return docstring(); + BibTeXInfo const & data = it->second; + return data.getAbbreviatedAuthor(); +} + + +docstring const BiblioInfo::getYear(docstring const & key) const +{ + BiblioInfo::const_iterator it = find(key); + if (it == end()) + return docstring(); + BibTeXInfo const & data = it->second; + return data.getYear(); } -docstring const getValueForKey(BibTeXInfo const & data, string const & findkey) +docstring const BiblioInfo::getInfo(docstring const & key) const { - docstring key = from_ascii(findkey); - BibTeXInfo::const_iterator it = data.find(key); - if (it == data.end()) + BiblioInfo::const_iterator it = find(key); + if (it == end()) return docstring(); - //FIXME ?? return it->second?? - BibTeXInfo & data2 = const_cast(data); - return data2[key]; + BibTeXInfo const & data = it->second; + return data.getInfo(); +} + + +vector const BiblioInfo::getCiteStrings( + docstring const & key, Buffer const & buf) const +{ + biblio::CiteEngine const engine = buf.params().getEngine(); + if (engine == biblio::ENGINE_NATBIB_NUMERICAL) + return getNumericalStrings(key, buf); + else + return getAuthorYearStrings(key, buf); +} + + +vector const BiblioInfo::getNumericalStrings( + docstring const & key, Buffer const & buf) const +{ + if (empty()) + return vector(); + + docstring const author = getAbbreviatedAuthor(key); + docstring const year = getYear(key); + if (author.empty() || year.empty()) + return vector(); + + vector const & styles = + biblio::getCiteStyles(buf.params().getEngine()); + + vector vec(styles.size()); + for (vector::size_type i = 0; i != vec.size(); ++i) { + docstring str; + + switch (styles[i]) { + case biblio::CITE: + case biblio::CITEP: + str = from_ascii("[#ID]"); + break; + + case biblio::CITET: + str = author + " [#ID]"; + break; + + case biblio::CITEALT: + str = author + " #ID"; + break; + + case biblio::CITEALP: + str = from_ascii("#ID"); + break; + + case biblio::CITEAUTHOR: + str = author; + break; + + case biblio::CITEYEAR: + str = year; + break; + + case biblio::CITEYEARPAR: + str = '(' + year + ')'; + break; + } + + vec[i] = str; + } + + return vec; +} + + +vector const BiblioInfo::getAuthorYearStrings( + docstring const & key, Buffer const & buf) const +{ + if (empty()) + return vector(); + + docstring const author = getAbbreviatedAuthor(key); + docstring const year = getYear(key); + if (author.empty() || year.empty()) + return vector(); + + vector const & styles = + getCiteStyles(buf.params().getEngine()); + + vector vec(styles.size()); + for (vector::size_type i = 0; i != vec.size(); ++i) { + docstring str; + + switch (styles[i]) { + case biblio::CITE: + // jurabib only: Author/Annotator + // (i.e. the "before" field, 2nd opt arg) + str = author + "/<" + _("before") + '>'; + break; + + case biblio::CITET: + str = author + " (" + year + ')'; + break; + + case biblio::CITEP: + str = '(' + author + ", " + year + ')'; + break; + + case biblio::CITEALT: + str = author + ' ' + year ; + break; + + case biblio::CITEALP: + str = author + ", " + year ; + break; + + case biblio::CITEAUTHOR: + str = author; + break; + + case biblio::CITEYEAR: + str = year; + break; + + case biblio::CITEYEARPAR: + str = '(' + year + ')'; + break; + } + vec[i] = str; + } + return vec; } + +void BiblioInfo::fillWithBibKeys(Buffer const * const buf) +{ + /// 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) { + this->fillWithBibKeys(tmp); + return; + } + + // Pre-load all child documents. + loadChildDocuments(*buf); + + for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it) + it->fillWithBibKeys(*buf, *this, it); +} + + +namespace biblio { + +//////////////////////////////////////// +// +// CitationStyle +// +//////////////////////////////////////// + namespace { @@ -567,138 +546,6 @@ vector const getCiteStyles(CiteEngine const engine) return styles; } - -vector const - getNumericalStrings(string const & key, - BibKeyList 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, - BibKeyList 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, - BibKeyList & 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; - } - - // Pre-load all child documents. - loadChildDocuments(*buf); - - 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 index 4e8e39f13b..7739f21856 100644 --- a/src/Biblio.h +++ b/src/Biblio.h @@ -6,6 +6,7 @@ * * \author Angus Leeming * \author Herbert Voß + * \author Richard Heck * * Full author contact details are available in file CREDITS. */ @@ -13,154 +14,149 @@ #ifndef BIBLIO_H #define BIBLIO_H -#include "Biblio_typedefs.h" -#include "Buffer.h" +#include "support/docstring.h" #include +#include +#include + 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 - }; - - -/** Fills keys with BibTeX information derived from the various - * in this document or its master document. - */ - void fillWithBibKeys(Buffer const * const buf, BibKeyList & 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); - -/// Returns a vector of bibliography keys - std::vector const getKeys(BibKeyList const &); - -/** Returns the BibTeX data associated with a given key. - Empty if no info exists. */ - docstring const getInfo(BibKeyList const &, std::string const & key); - -/// return the year from the bibtex data record - docstring const getYear(BibKeyList const & map, std::string const & key); - -/// return the short form of an authorlist - docstring const getAbbreviatedAuthor(BibKeyList const & map, std::string const & key); - -/// return only the family name - docstring const familyName(docstring const & name); +class Buffer; -/** Search a BibTeX info field for the given key and return the - associated field. */ - docstring const getValueForKey(BibTeXInfo const & data, std::string const & findkey); - -/** Returns an iterator to the first key that meets the search - criterion, or end() if unsuccessful. - - User supplies : - the BibKeyList of bibliography 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(BibKeyList 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; - }; +namespace biblio { +enum CiteEngine { + ENGINE_BASIC, + ENGINE_NATBIB_AUTHORYEAR, + ENGINE_NATBIB_NUMERICAL, + ENGINE_JURABIB +}; + +enum CiteStyle { + CITE, + CITET, + CITEP, + CITEALT, + CITEALP, + CITEAUTHOR, + CITEYEAR, + CITEYEARPAR +}; + + +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 ); +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], ... +} // namespace biblio - User supplies : - the key, - the BibKeyList of bibkeys info, - the available citation styles - */ +/// Class to represent information about a BibTeX or +/// bibliography entry. +/// The keys are BibTeX fields (e.g., author, title, etc), +/// and the values are the associated field values. +class BibTeXInfo : public std::map { +public: + /// + BibTeXInfo(); + ///Search for the given field and return the associated info. + ///The point of this is that BibTeXInfo::operator[] has no const + ///form. + docstring const & getValueForField(docstring const & field) const; + /// + docstring const & getValueForField(std::string const & field) const; + /// + bool hasField(docstring const & field) const; + /// return the short form of an authorlist + docstring const getAbbreviatedAuthor() const; + /// + docstring const getYear() const; + /// Returns formatted BibTeX data suitable for framing. + docstring const getInfo() const; + /// the BibTeX key for this entry + docstring bibKey; + /// a single string containing all BibTeX data associated with this key + docstring allData; + /// the BibTeX entry type (article, book, incollection, ...) + docstring entryType; + /// true if from BibTeX; false if from bibliography environment + bool isBibTeX; +}; + + +/// Class to represent a collection of bibliographical data, whether +/// from BibTeX or from bibliography environments. +/// BiblioInfo.first is the bibliography key +/// BiblioInfo.second is the data for that key +class BiblioInfo : public std::map { +public: + /// Returns a sorted vector of bibliography keys + std::vector const getKeys() const; + /// Returns a sorted vector of present BibTeX fields + std::vector const getFields() const; + /// Returns a sorted vector of BibTeX entry types in use + std::vector const getEntries() const; + /// Fills keys with BibTeX information derived from the various insets + /// in a given buffer, in its master document. + void fillWithBibKeys(Buffer const * const buf); + /// return the short form of an authorlist + docstring const getAbbreviatedAuthor(docstring const & key) const; + /// return the year from the bibtex data record + docstring const getYear(docstring const & key) const; + /// Returns formatted BibTeX data associated with a given key. + /// Empty if no info exists. + docstring const getInfo(docstring const & key) const; + + /** + * "Translates the available Citation Styles into strings for a given key, + * either numerical or author-year depending upon the active engine. (See + * below for those methods.) + */ std::vector const - getNumericalStrings(std::string const & key, - BibKeyList 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 BibKeyList of bibkeys info, - the available citation styles - */ + getCiteStrings(docstring const & key, Buffer const & buf) const; + /** + * "Translates" the available Citation Styles into strings for a given 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 buffer + */ + std::vector const + getNumericalStrings(docstring const & key, Buffer const & buf) const; + /** + * "Translates" the available Citation Styles into strings for a given 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 buffer + */ std::vector const - getAuthorYearStrings(std::string const & key, - BibKeyList const & map, - std::vector const & styles); + getAuthorYearStrings(docstring const & key, Buffer const & buf) const; + + std::set fieldNames; + std::set entryTypes; +}; -} // namespace biblio } // namespace lyx #endif diff --git a/src/Biblio_typedefs.h b/src/Biblio_typedefs.h index 18d345a475..e69de29bb2 100644 --- a/src/Biblio_typedefs.h +++ b/src/Biblio_typedefs.h @@ -1,51 +0,0 @@ -// -*- C++ -*- -/** - * \file Biblio_typedef.h - * This file is part of LyX, the document processor. - * Licence details can be found in the file COPYING. - * - * \author Richard Heck - * - * Full author contact details are available in file CREDITS. - */ - -#ifndef BIBLIO_TYPEDEFS_H -#define BIBLIO_TYPEDEFS_H - -#include "support/docstring.h" -#include - -namespace lyx { -namespace biblio { - -/// Class to represent information about a BibTeX or -/// bibliography entry. -/// The keys are BibTeX fields, and the values are the -/// associated field values. -/// \param isBibTex false if this is from an InsetBibitem -/// \param allData the entire BibTeX entry, more or less -/// \param entryType the BibTeX entry type -class BibTeXInfo : public std::map { - public: - BibTeXInfo(); - BibTeXInfo(bool isBibTeX); - bool hasKey(docstring const & key); - bool isBibTeX; - docstring allData; - docstring entryType; -}; - -/* -class BibKeyList : public std::set { - public: - std::set keys; -} - -*/ - -/// First entry is the bibliography key, second the data -typedef std::map BibKeyList; - -} -} -#endif diff --git a/src/Buffer.cpp b/src/Buffer.cpp index f13798bd27..98cf4ebdb8 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -1359,14 +1359,6 @@ void Buffer::getLabelList(vector & list) const } -// This is also a buffer property (ale) -void Buffer::fillWithBibKeys(biblio::BibKeyList & keys) - const -{ - biblio::fillWithBibKeys(this, keys); -} - - void Buffer::updateBibfilesCache() { // if this is a child document and the parent is already loaded @@ -1731,14 +1723,14 @@ void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to, vector labels; if (code == Inset::CITE_CODE) { - biblio::BibKeyList keys; - fillWithBibKeys(keys); - biblio::BibKeyList::const_iterator bit = keys.begin(); - biblio::BibKeyList::const_iterator bend = keys.end(); + BiblioInfo keys; + keys.fillWithBibKeys(this); + BiblioInfo::const_iterator bit = keys.begin(); + BiblioInfo::const_iterator bend = keys.end(); for (; bit != bend; ++bit) // FIXME UNICODE - labels.push_back(from_utf8(bit->first)); + labels.push_back(bit->first); } else getLabelList(labels); diff --git a/src/Buffer.h b/src/Buffer.h index b611fd0b31..7db748fb1e 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -12,7 +12,6 @@ #ifndef BUFFER_H #define BUFFER_H -#include "Biblio_typedefs.h" #include "DocIterator.h" #include "support/FileName.h" @@ -302,8 +301,6 @@ public: */ void validate(LaTeXFeatures &) const; - /// return all bibkeys from buffer and its childs - void fillWithBibKeys(biblio::BibKeyList & keys) const; /// Update the cache with all bibfiles in use (including bibfiles /// of loaded child documents). void updateBibfilesCache(); diff --git a/src/Makefile.am b/src/Makefile.am index 511ff8afbe..7daedf1db3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -78,7 +78,6 @@ endif liblyxcore_la_SOURCES = \ Author.cpp \ Author.h \ - Biblio_typedefs.h \ Biblio.h \ Biblio.cpp \ Bidi.cpp \ diff --git a/src/frontends/controllers/ControlCitation.cpp b/src/frontends/controllers/ControlCitation.cpp index bd716a95dc..5a7244dacd 100644 --- a/src/frontends/controllers/ControlCitation.cpp +++ b/src/frontends/controllers/ControlCitation.cpp @@ -48,7 +48,7 @@ bool ControlCitation::initialiseParams(string const & data) bool use_styles = engine != biblio::ENGINE_BASIC; - kernel().buffer().fillWithBibKeys(bibkeysInfo_); + bibkeysInfo_.fillWithBibKeys(&(kernel().buffer())); if (citeStyles_.empty()) citeStyles_ = biblio::getCiteStyles(engine); @@ -70,9 +70,43 @@ void ControlCitation::clearParams() } -vector const ControlCitation::availableKeys() const +vector const ControlCitation::availableKeys() const { - return biblio::getKeys(bibkeysInfo_); + return bibkeysInfo_.getKeys(); +} + + +vector const ControlCitation::availableFields() const +{ + return bibkeysInfo_.getFields(); +} + + +vector const ControlCitation::availableEntries() const +{ + return bibkeysInfo_.getEntries(); +} + + +void ControlCitation::filterByEntryType( + vector & keyVector, docstring entryType) +{ + if (entryType.empty()) + return; + + vector::iterator it = keyVector.begin(); + vector::iterator end = keyVector.end(); + + vector result; + for (; it != end; ++it) { + docstring const key = *it; + BiblioInfo::const_iterator cit = bibkeysInfo_.find(key); + if (cit == bibkeysInfo_.end()) + continue; + if (cit->second.entryType == entryType) + result.push_back(key); + } + keyVector = result; } @@ -82,12 +116,12 @@ biblio::CiteEngine const ControlCitation::getEngine() const } -docstring const ControlCitation::getInfo(std::string const & key) const +docstring const ControlCitation::getInfo(docstring const & key) const { if (bibkeysInfo_.empty()) return docstring(); - return biblio::getInfo(bibkeysInfo_, key); + return bibkeysInfo_.getInfo(key); } namespace { @@ -117,12 +151,12 @@ docstring const escape_special_chars(docstring const & expr) } // namespace anon -vector ControlCitation::searchKeys( - vector const & keys_to_search, - docstring const & search_expression, +vector ControlCitation::searchKeys( + vector const & keys_to_search, bool only_keys, + docstring const & search_expression, docstring field, bool case_sensitive, bool regex) { - vector foundKeys; + vector foundKeys; docstring expr = support::trim(search_expression); if (expr.empty()) @@ -136,37 +170,40 @@ vector ControlCitation::searchKeys( boost::regex reg_exp(to_utf8(expr), case_sensitive ? boost::regex_constants::normal : boost::regex_constants::icase); - vector::const_iterator it = keys_to_search.begin(); - vector::const_iterator end = keys_to_search.end(); + vector::const_iterator it = keys_to_search.begin(); + vector::const_iterator end = keys_to_search.end(); for (; it != end; ++it ) { - biblio::BibKeyList::const_iterator info = bibkeysInfo_.find(*it); + BiblioInfo::const_iterator info = bibkeysInfo_.find(*it); if (info == bibkeysInfo_.end()) continue; - biblio::BibTeXInfo const kvm = info->second; - string const data = *it + ' ' + to_utf8(kvm.allData); + BibTeXInfo const & kvm = info->second; + string data; + if (only_keys) + data = to_utf8(*it); + else if (field.empty()) + data = to_utf8(*it) + ' ' + to_utf8(kvm.allData); + else if (kvm.hasField(field)) + data = to_utf8(kvm.getValueForField(field)); + if (data.empty()) + continue; + try { if (boost::regex_search(data, reg_exp)) foundKeys.push_back(*it); } catch (boost::regex_error &) { - return vector(); + return vector(); } } return foundKeys; } -vector const ControlCitation::getCiteStrings(string const & key) const +vector const ControlCitation::getCiteStrings(docstring const & key) const { - biblio::CiteEngine const engine = kernel().buffer().params().getEngine(); - vector const cs = biblio::getCiteStyles(engine); - - if (engine == biblio::ENGINE_NATBIB_NUMERICAL) - return biblio::getNumericalStrings(key, bibkeysInfo_, cs); - else - return biblio::getAuthorYearStrings(key, bibkeysInfo_, cs); + return bibkeysInfo_.getCiteStrings(key, kernel().buffer()); } } // namespace frontend diff --git a/src/frontends/controllers/ControlCitation.h b/src/frontends/controllers/ControlCitation.h index 93219dc535..b3170c6267 100644 --- a/src/frontends/controllers/ControlCitation.h +++ b/src/frontends/controllers/ControlCitation.h @@ -37,32 +37,41 @@ public: virtual bool disconnectOnApply() const { return true; } /// \return the list of all available bibliography keys. - std::vector const availableKeys() const; + std::vector const availableKeys() const; + /// \return the list of all used BibTeX fields + std::vector const availableFields() const; + /// \return the list of all used BibTeX entry types + std::vector const availableEntries() const; + /// + void filterByEntryType( + std::vector & keyVector, docstring entryType); /// biblio::CiteEngine const getEngine() const; /// \return information for this key. - docstring const getInfo(std::string const & key) const; + docstring const getInfo(docstring const & key) const; /// Search a given string within the passed keys. /// \return the vector of matched keys. - std::vector searchKeys( - std::vector const & keys_to_search, //< Keys to search. + std::vector searchKeys( + std::vector const & keys_to_search, //< Keys to search. + bool only_keys, //< whether to search only the keys docstring const & search_expression, //< Search expression (regex possible) - bool case_sensitive = false, // set to true is the search should be case sensitive - bool regex = false /// \set to true if \c search_expression is a regex + docstring field, //< field to search, empty for all fields + bool case_sensitive = false, //< set to true is the search should be case sensitive + bool regex = false //< \set to true if \c search_expression is a regex ); // /// \return possible citations based on this key. - std::vector const getCiteStrings(std::string const & key) const; + std::vector const getCiteStrings(docstring const & key) const; /// available CiteStyle-s (depends on availability of Natbib/Jurabib) static std::vector const & getCiteStyles() { return citeStyles_; } private: - /// The info associated with each key - biblio::BibKeyList bibkeysInfo_; + /// The BibTeX information available to the dialog + BiblioInfo bibkeysInfo_; /// static std::vector citeStyles_; diff --git a/src/frontends/qt4/QCitation.cpp b/src/frontends/qt4/QCitation.cpp index 2c0e6219ad..f0292887f0 100644 --- a/src/frontends/qt4/QCitation.cpp +++ b/src/frontends/qt4/QCitation.cpp @@ -17,6 +17,7 @@ #include "qt_helpers.h" #include "support/lstrings.h" +#include "support/docstring.h" #include "debug.h" @@ -40,13 +41,13 @@ template static QStringList to_qstring_list(vector cons } -static vector const to_string_vector(QStringList const & qlist) +static vector const to_docstring_vector(QStringList const & qlist) { - vector v; + vector v; for (int i = 0; i != qlist.size(); ++i) { if (qlist[i].isEmpty()) continue; - v.push_back(lyx::fromqstr(qlist[i])); + v.push_back(lyx::qstring_to_ucs4(qlist[i])); } return v; } @@ -123,19 +124,14 @@ void QCitation::init() cited_keys_.clear(); else cited_keys_ = str.split(","); - selected_model_.setStringList(cited_keys_); } void QCitation::findKey(QString const & str, bool only_keys, - bool case_sensitive, bool reg_exp) + docstring field, docstring entryType, + bool case_sensitive, bool reg_exp, bool reset) { - if (str.isEmpty()) { - available_model_.setStringList(all_keys_); - return; - } - // Used for optimisation: store last searched string. static QString last_searched_string; // Used to disable the above optimisation. @@ -155,8 +151,10 @@ void QCitation::findKey(QString const & str, bool only_keys, Qt::CaseSensitive: Qt::CaseInsensitive; QStringList keys; // If new string (str) contains the last searched one... - if (!last_searched_string.isEmpty() && str.size() > 1 - && str.contains(last_searched_string, qtcase)) + if (!reset && + !last_searched_string.isEmpty() && + str.size() > 1 && + str.contains(last_searched_string, qtcase)) // ... then only search within already found list. keys = available_model_.stringList(); else @@ -166,18 +164,32 @@ void QCitation::findKey(QString const & str, bool only_keys, last_searched_string = str; QStringList result; - if (only_keys) - // Search only within the matching keys: - result = keys.filter(str, qtcase); + + //First, filter by entryType, which will be faster than + //what follows, so we may get to do that on less. + vector keyVector = to_docstring_vector(keys); + filterByEntryType(keyVector, entryType); + + if (str.isEmpty()) + result = to_qstring_list(keyVector); else - // Search within matching keys and associated info. - result = to_qstring_list(searchKeys(to_string_vector(keys), - qstring_to_ucs4(str), case_sensitive, reg_exp)); - + result = to_qstring_list(searchKeys(keyVector, only_keys, + qstring_to_ucs4(str), field, case_sensitive, reg_exp)); + available_model_.setStringList(result); } +QStringList QCitation::getFieldsAsQStringList() { + return to_qstring_list(availableFields()); +} + + +QStringList QCitation::getEntriesAsQStringList() { + return to_qstring_list(availableEntries()); +} + + void QCitation::addKey(QModelIndex const & index) { cited_keys_.append(index.data().toString()); @@ -210,14 +222,14 @@ void QCitation::downKey(QModelIndex const & index) QStringList QCitation::citationStyles(int sel) { - string const key = fromqstr(cited_keys_[sel]); + docstring const key = qstring_to_ucs4(cited_keys_[sel]); return to_qstring_list(getCiteStrings(key)); } QString QCitation::getKeyInfo(QString const & sel) { - return toqstr(getInfo(fromqstr(sel))); + return toqstr(getInfo(qstring_to_ucs4(sel))); } diff --git a/src/frontends/qt4/QCitation.h b/src/frontends/qt4/QCitation.h index 66bcdcdd48..3224982580 100644 --- a/src/frontends/qt4/QCitation.h +++ b/src/frontends/qt4/QCitation.h @@ -15,6 +15,7 @@ #define QCITATION_H #include "frontends/controllers/ControlCitation.h" +#include "support/docstring.h" #include #include @@ -50,13 +51,22 @@ public: /// Clear selected keys void clearSelection(); - + + /// Return a list of available fields + QStringList getFieldsAsQStringList(); + + /// Return a list of available fields + QStringList getEntriesAsQStringList(); + /// Find keys containing a string. void findKey( QString const & str, //< string expression bool only_keys, //< set to true if only keys shall be searched. + docstring field, //installEventFilter(this); selectedLV->installEventFilter(this); + availableFocused_ = true; } @@ -120,7 +123,7 @@ bool QCitationDialog::eventFilter(QObject * obj, QEvent * event) on_deletePB_clicked(); else if (keyModifiers == Qt::ControlModifier) { form_->clearSelection(); - update(); + updateDialog(); } else //ignore it otherwise return QObject::eventFilter(obj, event); @@ -215,19 +218,33 @@ void QCitationDialog::on_restorePB_clicked() void QCitationDialog::update() { - if (selectedLV->selectionModel()->selectedIndexes().isEmpty()) { + fillFields(); + fillEntries(); + updateDialog(); +} + + +//The main point of separating this out is that the fill*() methods +//called in update() do not need to be called for INTERNAL updates, +//such as when addPB is pressed, as the list of fields, entries, etc, +//will not have changed. At the moment, however, the division between +//fillStyles() and updateStyles() doesn't lend itself to dividing the +//two methods, though they should be divisible. +void QCitationDialog::updateDialog() +{ + if (availableFocused_ || + selectedLV->selectionModel()->selectedIndexes().isEmpty()) { if (availableLV->selectionModel()->selectedIndexes().isEmpty() - && availableLV->model()->rowCount() > 0) - availableLV->setCurrentIndex(availableLV->model()->index(0,0)); - updateInfo(availableLV->currentIndex()); + && availableLV->model()->rowCount() > 0) + availableLV->setCurrentIndex(availableLV->model()->index(0,0)); + updateInfo(availableLV); } else - updateInfo(selectedLV->currentIndex()); + updateInfo(selectedLV); setButtons(); textBeforeED->setText(form_->textBefore()); textAfterED->setText(form_->textAfter()); - fillStyles(); updateStyle(); } @@ -241,10 +258,16 @@ void QCitationDialog::updateStyle() engine == biblio::ENGINE_NATBIB_NUMERICAL; bool const basic_engine = engine == biblio::ENGINE_BASIC; - fulllistCB->setEnabled(natbib_engine); - forceuppercaseCB->setEnabled(natbib_engine); - textBeforeED->setEnabled(!basic_engine); - textBeforeLA->setEnabled(!basic_engine); + bool const haveSelection = + selectedLV->model()->rowCount() > 0; + fulllistCB->setEnabled(natbib_engine && haveSelection); + forceuppercaseCB->setEnabled(natbib_engine && haveSelection); + textBeforeED->setEnabled(!basic_engine && haveSelection); + textBeforeLA->setEnabled(!basic_engine && haveSelection); + textAfterED->setEnabled(haveSelection); + textAfterLA->setEnabled(haveSelection); + citationStyleCO->setEnabled(!basic_engine && haveSelection); + citationStyleLA->setEnabled(!basic_engine && haveSelection); string const & command = form_->params().getCmdName(); @@ -262,21 +285,24 @@ void QCitationDialog::updateStyle() else citationStyleCO->setCurrentIndex(0); - fulllistCB->setChecked(false); - forceuppercaseCB->setChecked(false); - if (cit != styles.end()) { int const i = int(cit - styles.begin()); citationStyleCO->setCurrentIndex(i); fulllistCB->setChecked(cs.full); forceuppercaseCB->setChecked(cs.forceUCase); + } else { + fulllistCB->setChecked(false); + forceuppercaseCB->setChecked(false); } } +//This one needs to be called whenever citationStyleCO needs +//to be updated---and this would be on anything that changes the +//selection in selectedLV, or on a general update. void QCitationDialog::fillStyles() { - int const orig = citationStyleCO->currentIndex(); + int const oldIndex = citationStyleCO->currentIndex(); citationStyleCO->clear(); @@ -297,7 +323,7 @@ void QCitationDialog::fillStyles() QStringList sty = form_->citationStyles(curr); bool const basic_engine = - (form_->getEngine() == biblio::ENGINE_BASIC); + (form_->getEngine() == biblio::ENGINE_BASIC); citationStyleCO->setEnabled(!sty.isEmpty() && !basic_engine); citationStyleLA->setEnabled(!sty.isEmpty() && !basic_engine); @@ -307,8 +333,37 @@ void QCitationDialog::fillStyles() citationStyleCO->insertItems(0, sty); - if (orig != -1 && orig < citationStyleCO->count()) - citationStyleCO->setCurrentIndex(orig); + if (oldIndex != -1 && oldIndex < citationStyleCO->count()) + citationStyleCO->setCurrentIndex(oldIndex); +} + + +void QCitationDialog::fillFields() +{ + fieldsCO->blockSignals(true); + int const oldIndex = fieldsCO->currentIndex(); + fieldsCO->clear(); + QStringList const & fields = form_->getFieldsAsQStringList(); + fieldsCO->insertItem(0, qt_("All Fields")); + fieldsCO->insertItem(1, qt_("Keys")); + fieldsCO->insertItems(2, fields); + if (oldIndex != -1 && oldIndex < fieldsCO->count()) + fieldsCO->setCurrentIndex(oldIndex); + fieldsCO->blockSignals(false); +} + + +void QCitationDialog::fillEntries() +{ + entriesCO->blockSignals(true); + int const oldIndex = entriesCO->currentIndex(); + entriesCO->clear(); + QStringList const & entries = form_->getEntriesAsQStringList(); + entriesCO->insertItem(0, qt_("All Entry Types")); + entriesCO->insertItems(1, entries); + if (oldIndex != -1 && oldIndex < entriesCO->count()) + entriesCO->setCurrentIndex(oldIndex); + entriesCO->blockSignals(false); } @@ -338,8 +393,9 @@ void QCitationDialog::setButtons() } -void QCitationDialog::updateInfo(const QModelIndex & idx) +void QCitationDialog::updateInfo(QListView const * const qlv) { + QModelIndex idx = qlv->currentIndex(); if (idx.isValid()) { QString const keytxt = form_->getKeyInfo(idx.data().toString()); infoML->document()->setPlainText(keytxt); @@ -350,7 +406,8 @@ void QCitationDialog::updateInfo(const QModelIndex & idx) void QCitationDialog::on_selectedLV_clicked(const QModelIndex &) { - update(); + availableFocused_ = false; + updateDialog(); } @@ -358,13 +415,14 @@ void QCitationDialog::selectedChanged(const QModelIndex & idx, const QModelIndex { if (!idx.isValid()) return; - update(); + updateDialog(); } void QCitationDialog::on_availableLV_clicked(const QModelIndex &) { - update(); + availableFocused_ = true; + updateDialog(); } @@ -372,7 +430,8 @@ void QCitationDialog::availableChanged(const QModelIndex & idx, const QModelInde { if (!idx.isValid()) return; - update(); + availableFocused_ = true; + updateDialog(); } @@ -380,6 +439,7 @@ void QCitationDialog::on_availableLV_doubleClicked(const QModelIndex & idx) { if (isSelected(idx)) return; + availableFocused_ = true; on_addPB_clicked(); } @@ -407,7 +467,8 @@ void QCitationDialog::on_addPB_clicked() form_->addKey(idxToAdd); if (idx.isValid()) selectedLV->setCurrentIndex(idx); - update(); + availableFocused_ = true; + updateDialog(); } @@ -425,8 +486,8 @@ void QCitationDialog::on_deletePB_clicked() if (nrows>1) selectedLV->setCurrentIndex(idx); - - update(); + availableFocused_ = true; + updateDialog(); } @@ -436,7 +497,8 @@ void QCitationDialog::on_upPB_clicked() form_->upKey(idx); selectedLV->setCurrentIndex(idx.sibling(idx.row() - 1, idx.column())); availableLV->selectionModel()->reset(); - update(); + availableFocused_ = false; + updateDialog(); } @@ -446,17 +508,58 @@ void QCitationDialog::on_downPB_clicked() form_->downKey(idx); selectedLV->setCurrentIndex(idx.sibling(idx.row() + 1, idx.column())); availableLV->selectionModel()->reset(); - update(); + availableFocused_ = false; + updateDialog(); } -void QCitationDialog::findText(QString const & text) +void QCitationDialog::findText(QString const & text, bool reset) { + //"All Fields" and "Keys" are the first two + int index = fieldsCO->currentIndex() - 2; + vector const & fields = form_->availableFields(); + docstring field; + + if (index <= -1 || index >= fields.size()) + //either "All Fields" or "Keys" or an invalid value + field = from_ascii(""); + else + field = fields[index]; + + //Was it "Keys"? + bool const onlyKeys = index == -1; + + //"All Entry Types" is first. + index = entriesCO->currentIndex() - 1; + vector const & entries = form_->availableEntries(); + docstring entryType; + if (index < 0 || index >= entries.size()) + entryType = from_ascii(""); + else + entryType = entries[index]; + bool const case_sentitive = caseCB->checkState(); bool const reg_exp = regexCB->checkState(); - form_->findKey(text, false, case_sentitive, reg_exp); - selectedLV->selectionModel()->reset(); - update(); + form_->findKey(text, onlyKeys, field, entryType, + case_sentitive, reg_exp, reset); + //FIXME + //It'd be nice to save and restore the current selection in + //availableLV. Currently, we get an automatic reset, since the + //model is reset. + + updateDialog(); +} + + +void QCitationDialog::on_fieldsCO_currentIndexChanged(int /*index*/) +{ + findText(findLE->text(), true); +} + + +void QCitationDialog::on_entriesCO_currentIndexChanged(int /*index*/) +{ + findText(findLE->text(), true); } diff --git a/src/frontends/qt4/QCitationDialog.h b/src/frontends/qt4/QCitationDialog.h index 17875a7d96..04469b9a32 100644 --- a/src/frontends/qt4/QCitationDialog.h +++ b/src/frontends/qt4/QCitationDialog.h @@ -54,7 +54,13 @@ public: protected: void closeEvent (QCloseEvent * e); - void findText(QString const & text); + /// prepares a call to QCitation::searchKeys when we + /// are ready to search the BibTeX entries + void findText(QString const & text, bool reset = false); + /// check whether key is already selected + bool isSelected(const QModelIndex &); + /// update the display of BibTeX information + void updateInfo(QListView const * const); protected Q_SLOTS: void cleanUp(); @@ -67,6 +73,8 @@ protected Q_SLOTS: void on_upPB_clicked(); void on_downPB_clicked(); void on_findLE_textChanged(const QString & text); + void on_fieldsCO_currentIndexChanged(int index); + void on_entriesCO_currentIndexChanged(int index); void on_caseCB_stateChanged(int); void on_regexCB_stateChanged(int); void on_selectedLV_clicked(const QModelIndex &); @@ -75,23 +83,26 @@ protected Q_SLOTS: void on_availableLV_doubleClicked(const QModelIndex &); void availableChanged(const QModelIndex &, const QModelIndex &); virtual void changed(); - /// check whether key is already selected - bool isSelected(const QModelIndex &); - /// update infobox - void updateInfo(const QModelIndex &); private: /// enable/disable buttons void setButtons(); - /// fill the styles combo void fillStyles(); - + /// fill the fields combo + void fillFields(); + /// fill the entries combo + void fillEntries(); /// set the styles combo void updateStyle(); - + /// performs a limited update, suitable for internal call + void updateDialog(); /// last used citation style int style_; + /// which of available and selected is "focused", in the sense + /// of which one should be used for updating the info via updateInfo(). + /// true, obviously, if it is availableLV. + bool availableFocused_; QCitation * form_; }; diff --git a/src/frontends/qt4/ui/CitationUi.ui b/src/frontends/qt4/ui/CitationUi.ui index a1be0eab50..8091e2be51 100644 --- a/src/frontends/qt4/ui/CitationUi.ui +++ b/src/frontends/qt4/ui/CitationUi.ui @@ -5,14 +5,12 @@ 0 0 - 369 - 509 + 449 + 617 - - 1 - 1 + 0 0 @@ -24,26 +22,30 @@ true - - 9 - - - 6 - - + 0 - + + 0 + + + 0 + + + 0 + + + 6 + + 6 - - 0 - 0 + 0 0 @@ -62,9 +64,7 @@ - - 0 - 0 + 0 0 @@ -156,6 +156,141 @@ + + + + Search Citation + + + true + + + + + + F&ind: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + findLE + + + + + + + + + + + + + + false + + + <- C&lear + + + + + + + Search Field: + + + fieldsCO + + + + + + + 16 + + + QComboBox::NoInsert + + + QComboBox::AdjustToContents + + + + All Fields + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Regular E&xpression + + + + + + + Entry Types: + + + entriesCO + + + + + + + QComboBox::NoInsert + + + QComboBox::AdjustToContents + + + + All Entry Types + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Case Se&nsitive + + + + + + @@ -165,18 +300,28 @@ true - + 9 - + + 9 + + + 9 + + + 9 + + + 6 + + 6 - - 3 - 0 + 0 0 @@ -229,9 +374,7 @@ - - 5 - 0 + 0 0 @@ -254,9 +397,7 @@ - - 5 - 0 + 0 0 @@ -271,12 +412,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -329,78 +479,6 @@ - - - - Search Citation - - - true - - - - 9 - - - 6 - - - - - 0 - - - 6 - - - - - Case Se&nsitive - - - - - - - Regular E&xpression - - - - - - - - - false - - - <- C&lear - - - - - - - - - - - - - - F&ind: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - findLE - - - - - - diff --git a/src/insets/Inset.h b/src/insets/Inset.h index 75127fea8d..c5b2946144 100644 --- a/src/insets/Inset.h +++ b/src/insets/Inset.h @@ -15,7 +15,7 @@ #ifndef INSETBASE_H #define INSETBASE_H -#include "Biblio_typedefs.h" +#include "Biblio.h" #include "Changes.h" #include "Dimension.h" @@ -441,7 +441,7 @@ public: virtual void addToToc(TocList &, Buffer const &, ParConstIterator const &) const {} /// Fill keys with BibTeX information virtual void fillWithBibKeys(Buffer const &, - biblio::BibKeyList &, InsetIterator const &) const { return; } + BiblioInfo &, InsetIterator const &) const { return; } /// Update the counters of this inset and of its contents virtual void updateLabels(Buffer const &, ParIterator const &) {} diff --git a/src/insets/InsetBibitem.cpp b/src/insets/InsetBibitem.cpp index 829e15a52a..a9d5dee14e 100644 --- a/src/insets/InsetBibitem.cpp +++ b/src/insets/InsetBibitem.cpp @@ -12,7 +12,6 @@ #include "InsetBibitem.h" -#include "Biblio.h" #include "Buffer.h" #include "BufferParams.h" #include "BufferView.h" @@ -183,14 +182,14 @@ docstring const bibitemWidest(Buffer const & buffer) void InsetBibitem::fillWithBibKeys(Buffer const & buf, - biblio::BibKeyList & keys, InsetIterator const & it) const + BiblioInfo & keys, InsetIterator const & it) const { - string const key = to_utf8(getParam("key")); - biblio::BibTeXInfo keyvalmap; + docstring const key = getParam("key"); + BibTeXInfo keyvalmap; keyvalmap[from_ascii("label")] = getParam("label"); DocIterator doc_it(it); doc_it.forwardPos(); - keyvalmap [from_ascii("ref")] = doc_it.paragraph().asString(buf, false); + keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(buf, false); keyvalmap.isBibTeX = false; keys[key] = keyvalmap; } diff --git a/src/insets/InsetBibitem.h b/src/insets/InsetBibitem.h index 3ed6356a91..e497814111 100644 --- a/src/insets/InsetBibitem.h +++ b/src/insets/InsetBibitem.h @@ -14,7 +14,7 @@ #include "InsetCommand.h" -#include "Biblio_typedefs.h" +#include "Biblio.h" namespace lyx { @@ -42,7 +42,7 @@ public: int plaintext(Buffer const &, odocstream &, OutputParams const &) const; /// virtual void fillWithBibKeys(Buffer const &, - biblio::BibKeyList &, InsetIterator const &) const; + BiblioInfo &, InsetIterator const &) const; /// Update the counter of this inset virtual void updateLabels(Buffer const &, ParIterator const &); diff --git a/src/insets/InsetBibtex.cpp b/src/insets/InsetBibtex.cpp index 4c612bd495..020c08afb3 100644 --- a/src/insets/InsetBibtex.cpp +++ b/src/insets/InsetBibtex.cpp @@ -580,7 +580,7 @@ namespace { // This method returns a comma separated list of Bibtex entries void InsetBibtex::fillWithBibKeys(Buffer const & buffer, - biblio::BibKeyList & keys, InsetIterator const & /*di*/) const + BiblioInfo & keylist, InsetIterator const & /*di*/) const { vector const files = getFiles(buffer); for (vector::const_iterator it = files.begin(); @@ -695,7 +695,7 @@ void InsetBibtex::fillWithBibKeys(Buffer const & buffer, docstring value; docstring commaNewline; docstring data; - biblio::BibTeXInfo keyvalmap; + BibTeXInfo keyvalmap; keyvalmap.entryType = entryType; bool readNext = removeWSAndComma(ifs); @@ -722,14 +722,16 @@ void InsetBibtex::fillWithBibKeys(Buffer const & buffer, keyvalmap[name] = value; data += "\n\n" + value; - + keylist.fieldNames.insert(name); readNext = removeWSAndComma(ifs); } // add the new entry + keylist.entryTypes.insert(entryType); keyvalmap.allData = data; keyvalmap.isBibTeX = true; - keys[to_utf8(key)] = keyvalmap; + keyvalmap.bibKey = key; + keylist[key] = keyvalmap; } } //< searching '@' } //< for loop over files diff --git a/src/insets/InsetBibtex.h b/src/insets/InsetBibtex.h index fcbf0b716c..ea558ca5ff 100644 --- a/src/insets/InsetBibtex.h +++ b/src/insets/InsetBibtex.h @@ -14,7 +14,7 @@ #include #include "InsetCommand.h" -#include "Biblio_typedefs.h" +#include "Biblio.h" #include "support/FileName.h" @@ -39,7 +39,7 @@ public: int latex(Buffer const &, odocstream &, OutputParams const &) const; /// virtual void fillWithBibKeys(Buffer const &, - biblio::BibKeyList &, InsetIterator const &) const; + BiblioInfo &, InsetIterator const &) const; /// std::vector const getFiles(Buffer const &) const; /// diff --git a/src/insets/InsetCitation.cpp b/src/insets/InsetCitation.cpp index 6541e3ceb6..fafb585855 100644 --- a/src/insets/InsetCitation.cpp +++ b/src/insets/InsetCitation.cpp @@ -13,6 +13,7 @@ #include "InsetCitation.h" +#include "Biblio.h" #include "Buffer.h" #include "BufferParams.h" #include "debug.h" @@ -39,6 +40,7 @@ using support::FileName; using support::getStringFromVector; using support::getVectorFromString; using support::ltrim; +using support::prefixIs; using support::rtrim; using support::split; using support::tokenPos; @@ -55,8 +57,107 @@ namespace fs = boost::filesystem; 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(biblio::CiteEngine engine) +{ + string str; + switch (engine) { + case biblio::ENGINE_BASIC: + str = "cite"; + break; + case biblio::ENGINE_NATBIB_AUTHORYEAR: + str = "citet"; + break; + case biblio::ENGINE_NATBIB_NUMERICAL: + str = "citep"; + break; + case biblio::ENGINE_JURABIB: + str = "cite"; + break; + } + return str; +} + + +string const + asValidLatexCommand(string const & input, biblio::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 biblio::ENGINE_BASIC: + output = default_str; + break; + + case biblio::ENGINE_NATBIB_AUTHORYEAR: + case biblio::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 biblio::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 getNatbibLabel(Buffer const & buffer, - string const & citeType, string const & keyList, + string const & citeType, docstring const & keyList, docstring const & before, docstring const & after, biblio::CiteEngine engine) { @@ -65,13 +166,13 @@ docstring const getNatbibLabel(Buffer const & buffer, return docstring(); // Cache the labels - typedef std::map CachedMap; + typedef std::map CachedMap; static CachedMap cached_keys; // and cache the timestamp of the bibliography files. static std::map bibfileStatus; - biblio::BibKeyList keylist; + BiblioInfo biblist; vector const & bibfilesCache = buffer.getBibfilesCache(); // compare the cached timestamps with the actual ones. @@ -95,15 +196,15 @@ docstring const getNatbibLabel(Buffer const & buffer, } } - // build the keylist only if the bibfiles have been changed + // build the list only if the bibfiles have been changed if (cached_keys[&buffer].empty() || bibfileStatus.empty() || changed) { - buffer.fillWithBibKeys(keylist); - cached_keys[&buffer] = keylist; + biblist.fillWithBibKeys(&buffer); + cached_keys[&buffer] = biblist; } else // use the cached keys - keylist = cached_keys[&buffer]; + biblist = cached_keys[&buffer]; - if (keylist.empty()) + if (biblist.empty()) return docstring(); // the natbib citation-styles @@ -118,10 +219,12 @@ docstring const getNatbibLabel(Buffer const & buffer, // CITE: author/ // We don't currently use the full or forceUCase fields. - string cite_type = biblio::asValidLatexCommand(citeType, engine); + string cite_type = asValidLatexCommand(citeType, engine); if (cite_type[0] == 'C') + //If we were going to use them, this would mean ForceUCase cite_type = string(1, 'c') + cite_type.substr(1); if (cite_type[cite_type.size() - 1] == '*') + //and this would mean FULL cite_type = cite_type.substr(0, cite_type.size() - 1); docstring before_str; @@ -161,13 +264,13 @@ docstring const getNatbibLabel(Buffer const & buffer, docstring const sep_str(from_ascii(sep) + ' '); docstring label; - vector keys = getVectorFromString(keyList); - vector::const_iterator it = keys.begin(); - vector::const_iterator end = keys.end(); + vector keys = getVectorFromString(keyList); + vector::const_iterator it = keys.begin(); + vector::const_iterator end = keys.end(); for (; it != end; ++it) { // get the bibdata corresponding to the key - docstring const author(biblio::getAbbreviatedAuthor(keylist, *it)); - docstring const year(biblio::getYear(keylist, *it)); + docstring const author(biblist.getAbbreviatedAuthor(*it)); + docstring const year(biblist.getYear(*it)); // Something isn't right. Fail safely. if (author.empty() || year.empty()) @@ -190,9 +293,7 @@ docstring const getNatbibLabel(Buffer const & buffer, year + cp + sep_str; break; case biblio::ENGINE_NATBIB_NUMERICAL: - // FIXME UNICODE - label += author + op_str + before_str + - '#' + from_utf8(*it) + cp + sep_str; + label += author + op_str + before_str + '#' + *it + cp + sep_str; break; case biblio::ENGINE_JURABIB: label += before_str + author + op_str + @@ -206,8 +307,7 @@ docstring const getNatbibLabel(Buffer const & buffer, } else if (cite_type == "citep" || cite_type == "citealp") { if (engine == biblio::ENGINE_NATBIB_NUMERICAL) { - // FIXME UNICODE - label += from_utf8(*it) + sep_str; + label += *it + sep_str; } else { label += author + ", " + year + sep_str; } @@ -221,9 +321,7 @@ docstring const getNatbibLabel(Buffer const & buffer, year + sep_str; break; case biblio::ENGINE_NATBIB_NUMERICAL: - // FIXME UNICODE - label += author + ' ' + before_str + - '#' + from_utf8(*it) + sep_str; + label += author + ' ' + before_str + '#' + *it + sep_str; break; case biblio::ENGINE_JURABIB: label += before_str + author + ' ' + @@ -310,8 +408,7 @@ docstring const InsetCitation::generateLabel(Buffer const & buffer) const docstring label; biblio::CiteEngine const engine = buffer.params().getEngine(); if (engine != biblio::ENGINE_BASIC) { - // FIXME UNICODE - label = getNatbibLabel(buffer, getCmdName(), to_utf8(getParam("key")), + label = getNatbibLabel(buffer, getCmdName(), getParam("key"), before, after, engine); } @@ -415,7 +512,7 @@ int InsetCitation::latex(Buffer const & buffer, odocstream & os, biblio::CiteEngine cite_engine = buffer.params().getEngine(); // FIXME UNICODE docstring const cite_str = from_utf8( - biblio::asValidLatexCommand(getCmdName(), cite_engine)); + asValidLatexCommand(getCmdName(), cite_engine)); os << "\\" << cite_str; diff --git a/src/insets/InsetInclude.cpp b/src/insets/InsetInclude.cpp index 08461aadc6..2097994776 100644 --- a/src/insets/InsetInclude.cpp +++ b/src/insets/InsetInclude.cpp @@ -727,14 +727,14 @@ void InsetInclude::getLabelList(Buffer const & buffer, void InsetInclude::fillWithBibKeys(Buffer const & buffer, - biblio::BibKeyList & keys, InsetIterator const & /*di*/) const + BiblioInfo & keys, InsetIterator const & /*di*/) const { if (loadIfNeeded(buffer, params_)) { string const included_file = includedFilename(buffer, params_).absFilename(); Buffer * tmp = theBufferList().getBuffer(included_file); //FIXME This is kind of a dirty hack and should be made reasonable. tmp->setParentName(""); - tmp->fillWithBibKeys(keys); + keys.fillWithBibKeys(tmp); tmp->setParentName(parentFilename(buffer)); } } diff --git a/src/insets/InsetInclude.h b/src/insets/InsetInclude.h index dbdaa340f3..a2ef0d551a 100644 --- a/src/insets/InsetInclude.h +++ b/src/insets/InsetInclude.h @@ -12,7 +12,7 @@ #ifndef INSET_INCLUDE_H #define INSET_INCLUDE_H -#include "Biblio_typedefs.h" +#include "Biblio.h" #include "Inset.h" #include "InsetCommandParams.h" #include "RenderButton.h" @@ -62,7 +62,7 @@ public: * \param it not used here */ virtual void fillWithBibKeys(Buffer const & buffer, - biblio::BibKeyList & keys, InsetIterator const & it) const; + BiblioInfo & keys, InsetIterator const & it) const; /** Update the cache with all bibfiles in use of the child buffer * (including bibfiles of grandchild documents). -- 2.39.5