X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2Finsetcite.C;h=a8bf9a068e5a87daedc172ce1e9636476a567b06;hb=e28331ed63062dea10d0a21b9ec12034b4b17b9a;hp=f294cde0e60b632a7291ef2bc1969426f9d352ff;hpb=6af5ac769a78a428120dd8e501a8a0f89aa5392f;p=lyx.git diff --git a/src/insets/insetcite.C b/src/insets/insetcite.C index f294cde0e6..a8bf9a068e 100644 --- a/src/insets/insetcite.C +++ b/src/insets/insetcite.C @@ -1,83 +1,119 @@ /** * \file insetcite.C - * Copyright 2001 the LyX Team - * Read the file COPYING + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. * - * \author Angus Leeming, a.leeming@ic.ac.uk - * \author Herbert Voss, voss@lyx.org 2002-03-17 + * \author Angus Leeming + * \author Herbert Voß + * + * Full author contact details are available in file CREDITS. */ #include -#ifdef __GNUG__ -#pragma implementation -#endif - #include "insetcite.h" + #include "buffer.h" -#include "BufferView.h" -#include "LaTeXFeatures.h" -#include "frontends/LyXView.h" +#include "bufferparams.h" #include "debug.h" -#include "gettext.h" +#include "dispatchresult.h" +#include "funcrequest.h" +#include "LaTeXFeatures.h" #include "frontends/controllers/biblio.h" -#include "frontends/Dialogs.h" -#include "support/filetools.h" +#include "support/fs_extras.h" #include "support/lstrings.h" -#include "support/path.h" -#include "support/os.h" -#include "support/lstrings.h" -#include "support/LAssert.h" -#include +#include + +#include +#include + + +namespace lyx { + +using support::ascii_lowercase; +using support::contains; +using support::FileName; +using support::getStringFromVector; +using support::getVectorFromString; +using support::ltrim; +using support::rtrim; +using support::split; +using support::tokenPos; +using std::endl; +using std::replace; +using std::string; using std::ostream; using std::vector; using std::map; -namespace { +namespace fs = boost::filesystem; + -// An optimisation. We assume that until the first InsetCitation::edit is -// called, we're loading the buffer and that, therefore, we don't need to -// reload the bibkey list -std::map loading_buffer; +namespace { -string const getNatbibLabel(Buffer const * buffer, +docstring const getNatbibLabel(Buffer const & buffer, string const & citeType, string const & keyList, - string const & before, string const & after, - bool numerical) + docstring const & before, docstring const & after, + biblio::CiteEngine engine) { - // Only reload the bibkeys if we have to... - map::iterator lit = loading_buffer.find(buffer); - if (lit != loading_buffer.end()) - loading_buffer[buffer] = true; + // Only start the process off after the buffer is loaded from file. + if (!buffer.fully_loaded()) + return docstring(); + // Cache the labels typedef std::map CachedMap; static CachedMap cached_keys; - CachedMap::iterator kit = cached_keys.find(buffer); + // and cache the timestamp of the bibliography files. + static std::map bibfileStatus; + + biblio::InfoMap infomap; + + vector const & bibfilesCache = buffer.getBibfilesCache(); + // compare the cached timestamps with the actual ones. + bool changed = false; + for (vector::const_iterator it = bibfilesCache.begin(); + it != bibfilesCache.end(); ++ it) { + FileName const f = *it; + try { + std::time_t lastw = fs::last_write_time(f.toFilesystemEncoding()); + if (lastw != bibfileStatus[f]) { + changed = true; + bibfileStatus[f] = lastw; + } + } + catch (fs::filesystem_error & fserr) { + changed = true; + lyxerr << "Couldn't find or read bibtex file " + << f << endl; + lyxerr[Debug::DEBUG] << "Fs error: " + << fserr.what() << endl; + } + } - if (!loading_buffer[buffer] || kit == cached_keys.end()) { - // build the keylist - typedef vector > InfoType; - InfoType bibkeys = buffer->getBibkeyList(); + // build the keylist only if the bibfiles have been changed + if (cached_keys[&buffer].empty() || bibfileStatus.empty() || changed) { + typedef vector > InfoType; + InfoType bibkeys; + buffer.fillWithBibKeys(bibkeys); InfoType::const_iterator bit = bibkeys.begin(); InfoType::const_iterator bend = bibkeys.end(); - biblio::InfoMap infomap; - for (; bit != bend; ++bit) { + for (; bit != bend; ++bit) infomap[bit->first] = bit->second; - } - if (infomap.empty()) - return string(); - cached_keys[buffer] = infomap; - } + cached_keys[&buffer] = infomap; + } else + // use the cached keys + infomap = cached_keys[&buffer]; - biblio::InfoMap infomap = cached_keys[buffer]; + if (infomap.empty()) + return docstring(); // the natbib citation-styles // CITET: author (year) @@ -87,16 +123,17 @@ string const getNatbibLabel(Buffer const * buffer, // CITEAUTHOR: author // CITEYEAR: year // CITEYEARPAR: (year) + // jurabib supports these plus + // CITE: author/ // We don't currently use the full or forceUCase fields. - // bool const forceUCase = citeType[0] == 'C'; - bool const full = citeType[citeType.size() - 1] == '*'; - - string const cite_type = full ? - ascii_lowercase(citeType.substr(0, citeType.size() - 1)) : - ascii_lowercase(citeType); + string cite_type = biblio::asValidLatexCommand(citeType, engine); + if (cite_type[0] == 'C') + cite_type = string(1, 'c') + cite_type.substr(1); + if (cite_type[cite_type.size() - 1] == '*') + cite_type = cite_type.substr(0, cite_type.size() - 1); - string before_str; + docstring before_str; if (!before.empty()) { // In CITET and CITEALT mode, the "before" string is // attached to the label associated with each and every key. @@ -109,9 +146,14 @@ string const getNatbibLabel(Buffer const * buffer, cite_type == "citealp" || cite_type == "citeyearpar") before_str = before + ' '; + // In CITE (jurabib), the "before" string is used to attach + // the annotator (of legal texts) to the author(s) of the + // first reference. + else if (cite_type == "cite") + before_str = '/' + before; } - string after_str; + docstring after_str; if (!after.empty()) { // The "after" key is appended only to the end of the whole. after_str = ", " + after; @@ -123,35 +165,58 @@ string const getNatbibLabel(Buffer const * buffer, // puctuation mark separating citation entries. char const * const sep = ";"; - string const op_str(string(1, ' ') + string(1, op)); - string const cp_str(string(1, cp) + string(1, ' ')); - string const sep_str(string(sep) + " "); + docstring const op_str(' ' + docstring(1, op)); + docstring const cp_str(docstring(1, cp) + ' '); + docstring const sep_str(from_ascii(sep) + ' '); - string label; + docstring label; 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 - string const author(biblio::getAbbreviatedAuthor(infomap, *it)); - string const year(biblio::getYear(infomap, *it)); + docstring const author(biblio::getAbbreviatedAuthor(infomap, *it)); + docstring const year(biblio::getYear(infomap, *it)); // Something isn't right. Fail safely. if (author.empty() || year.empty()) - return string(); + return docstring(); + + // authors1/; ... ; + // authors_last, + if (cite_type == "cite" && engine == biblio::ENGINE_JURABIB) { + if (it == keys.begin()) + label += author + before_str + sep_str; + else + label += author + sep_str; // (authors1 ( year); ... ; // authors_last ( year, ) - if (cite_type == "citet") { - string const tmp = numerical ? '#' + *it : year; - label += author + op_str + before_str + tmp + - cp + sep_str; + } else if (cite_type == "citet") { + switch (engine) { + case biblio::ENGINE_NATBIB_AUTHORYEAR: + label += author + op_str + before_str + + year + cp + sep_str; + break; + case biblio::ENGINE_NATBIB_NUMERICAL: + // FIXME UNICODE + label += author + op_str + before_str + + '#' + from_utf8(*it) + cp + sep_str; + break; + case biblio::ENGINE_JURABIB: + label += before_str + author + op_str + + year + cp + sep_str; + break; + case biblio::ENGINE_BASIC: + break; + } // author, year; author, year; ... } else if (cite_type == "citep" || cite_type == "citealp") { - if (numerical) { - label += *it + sep_str; + if (engine == biblio::ENGINE_NATBIB_NUMERICAL) { + // FIXME UNICODE + label += from_utf8(*it) + sep_str; } else { label += author + ", " + year + sep_str; } @@ -159,8 +224,23 @@ string const getNatbibLabel(Buffer const * buffer, // (authors1 year; // authors_last year, ) } else if (cite_type == "citealt") { - string const tmp = numerical ? '#' + *it : year; - label += author + ' ' + before_str + tmp + sep_str; + switch (engine) { + case biblio::ENGINE_NATBIB_AUTHORYEAR: + label += author + ' ' + before_str + + year + sep_str; + break; + case biblio::ENGINE_NATBIB_NUMERICAL: + // FIXME UNICODE + label += author + ' ' + before_str + + '#' + from_utf8(*it) + sep_str; + break; + case biblio::ENGINE_JURABIB: + label += before_str + author + ' ' + + year + sep_str; + break; + case biblio::ENGINE_BASIC: + break; + } // author; author; ... } else if (cite_type == "citeauthor") { @@ -179,9 +259,10 @@ string const getNatbibLabel(Buffer const * buffer, // insert "after" before last ')' label.insert(label.size() - 1, after_str); } else { - bool const add = !(numerical && - (cite_type == "citeauthor" || - cite_type == "citeyear")); + bool const add = + !(engine == biblio::ENGINE_NATBIB_NUMERICAL && + (cite_type == "citeauthor" || + cite_type == "citeyear")); if (add) label += after_str; } @@ -194,22 +275,22 @@ string const getNatbibLabel(Buffer const * buffer, } if (cite_type == "citep" || cite_type == "citeyearpar") - label = string(1, op) + label + string(1, cp); + label = op + label + cp; return label; } -string const getBasicLabel(string const & keyList, string const & after) +docstring const getBasicLabel(docstring const & keyList, docstring const & after) { - string keys(keyList); - string label; + docstring keys(keyList); + docstring label; - if (contains(keys, ",")) { + if (contains(keys, ',')) { // Final comma allows while loop to cover all keys - keys = ltrim(split(keys, label, ',')) + ","; - while (contains(keys, ",")) { - string key; + keys = ltrim(split(keys, label, ',')) + ','; + while (contains(keys, ',')) { + docstring key; keys = ltrim(split(keys, key, ',')); label += ", " + key; } @@ -219,87 +300,60 @@ string const getBasicLabel(string const & keyList, string const & after) if (!after.empty()) label += ", " + after; - return "[" + label + "]"; + return '[' + label + ']'; } } // anon namespace -InsetCitation::InsetCitation(InsetCommandParams const & p, bool) - : InsetCommand(p) +InsetCitation::InsetCitation(InsetCommandParams const & p) + : InsetCommand(p, "citation") {} -string const InsetCitation::generateLabel(Buffer const * buffer) const +docstring const InsetCitation::generateLabel(Buffer const & buffer) const { - string const before = string(); - string const after = getOptions(); - - string label; - if (buffer->params.use_natbib) { - string cmd = getCmdName(); - if (cmd == "cite") { - // We may be "upgrading" from an older LyX version. - // If, however, we use "cite" because the necessary - // author/year info is not present in the biblio - // database, then getNatbibLabel will exit gracefully - // and we'll call getBasicLabel. - if (buffer->params.use_numerical_citations) - cmd = "citep"; - else - cmd = "citet"; - } - label = getNatbibLabel(buffer, cmd, getContents(), - before, after, - buffer->params.use_numerical_citations); + docstring const before = getParam("before"); + docstring const after = getParam("after"); + + docstring label; + biblio::CiteEngine const engine = buffer.params().cite_engine; + if (engine != biblio::ENGINE_BASIC) { + // FIXME UNICODE + label = getNatbibLabel(buffer, getCmdName(), to_utf8(getParam("key")), + before, after, engine); } // Fallback to fail-safe if (label.empty()) { - label = getBasicLabel(getContents(), after); + label = getBasicLabel(getParam("key"), after); } return label; } -InsetCitation::Cache::Style InsetCitation::getStyle(Buffer const * buffer) const +docstring const InsetCitation::getScreenLabel(Buffer const & buffer) const { - Cache::Style style = Cache::BASIC; - - if (buffer->params.use_natbib) { - if (buffer->params.use_numerical_citations) { - style = Cache::NATBIB_NUM; - } else { - style = Cache::NATBIB_AY; - } - } - - return style; -} - - -string const InsetCitation::getScreenLabel(Buffer const * buffer) const -{ - Cache::Style const style = getStyle(buffer); - if (cache.params == params() && cache.style == style) + biblio::CiteEngine const engine = biblio::getEngine(buffer); + if (cache.params == params() && cache.engine == engine) return cache.screen_label; // The label has changed, so we have to re-create it. - string const before = string(); + string const before = getSecOptions(); string const after = getOptions(); - string const glabel = generateLabel(buffer); + docstring const glabel = generateLabel(buffer); unsigned int const maxLabelChars = 45; - string label = glabel; + docstring label = glabel; if (label.size() > maxLabelChars) { label.erase(maxLabelChars-3); label += "..."; } - cache.style = style; + cache.engine = engine; cache.params = params(); cache.generated_label = glabel; cache.screen_label = label; @@ -308,34 +362,53 @@ string const InsetCitation::getScreenLabel(Buffer const * buffer) const } -void InsetCitation::edit(BufferView * bv, int, int, mouse_button::state) +int InsetCitation::plaintext(Buffer const & buffer, odocstream & os, + OutputParams const &) const { - // A call to edit() indicates that we're no longer loading the - // buffer but doing some real work. - // Doesn't matter if there is no bv->buffer() entry in the map. - loading_buffer[bv->buffer()] = false; - - bv->owner()->getDialogs().showCitation(this); + if (cache.params == params() && + cache.engine == biblio::getEngine(buffer)) + os << cache.generated_label; + else + os << generateLabel(buffer); + return 0; } -void InsetCitation::edit(BufferView * bv, bool) +namespace { + +docstring const cleanupWhitespace(docstring const & citelist) { - edit(bv, 0, 0, mouse_button::none); + docstring::const_iterator it = citelist.begin(); + docstring::const_iterator end = citelist.end(); + // Paranoia check: make sure that there is no whitespace in here + // -- at least not behind commas or at the beginning + docstring result; + char_type last = ','; + for (; it != end; ++it) { + if (*it != ' ') + last = *it; + if (*it != ' ' || last != ',') + result += *it; + } + return result; } +// end anon namyspace +} -int InsetCitation::ascii(Buffer const * buffer, ostream & os, int) const +int InsetCitation::docbook(Buffer const &, odocstream & os, OutputParams const &) const { - string label; + os << "" + << cleanupWhitespace(getParam("key")) + << ""; + return 0; +} - if (cache.params == params() && cache.style == getStyle(buffer)) - label = cache.generated_label; - else - label = generateLabel(buffer); - os << label; - return 0; +int InsetCitation::textString(Buffer const & buf, odocstream & os, + OutputParams const & op) const +{ + return plaintext(buf, os, op); } @@ -343,47 +416,24 @@ int InsetCitation::ascii(Buffer const * buffer, ostream & os, int) const // the \cite command is valid. Eg, the user has natbib enabled, inputs some // citations and then changes his mind, turning natbib support off. The output // should revert to \cite[]{} -int InsetCitation::latex(Buffer const * buffer, ostream & os, - bool /*fragile*/, bool/*fs*/) const +int InsetCitation::latex(Buffer const & buffer, odocstream & os, + OutputParams const &) const { - os << "\\"; - if (buffer->params.use_natbib) - os << getCmdName(); - else - os << "cite"; + biblio::CiteEngine const cite_engine = buffer.params().cite_engine; + // FIXME UNICODE + docstring const cite_str = from_utf8( + biblio::asValidLatexCommand(getCmdName(), cite_engine)); -#warning What is this code supposed to do? (Lgb) -// my guess is that this is just waiting for when we support before, -// so it's a oneliner. But this is very silly ! - jbl + os << "\\" << cite_str; -#if 1 - // The current strange code - - string const before = string(); - string const after = getOptions(); - if (!before.empty() && buffer->params.use_natbib) - os << "[" << before << "][" << after << "]"; + docstring const & before = getParam("before"); + docstring const & after = getParam("after"); + if (!before.empty() && cite_engine != biblio::ENGINE_BASIC) + os << '[' << before << "][" << after << ']'; else if (!after.empty()) - os << "[" << after << "]"; -#else - // and the cleaned up equvalent, should it just be changed? (Lgb) - string const after = getOptions(); - if (!after.empty()) - os << "[" << after << "]"; -#endif - string::const_iterator it = getContents().begin(); - string::const_iterator end = getContents().end(); - // Paranoia check: make sure that there is no whitespace in here - string content; - char last = ','; - for (; it != end; ++it) { - if (*it != ' ') - last = *it; - if (*it != ' ' || last != ',') - content += *it; - } + os << '[' << after << ']'; - os << "{" << content << "}"; + os << '{' << cleanupWhitespace(getParam("key")) << '}'; return 0; } @@ -391,6 +441,28 @@ int InsetCitation::latex(Buffer const * buffer, ostream & os, void InsetCitation::validate(LaTeXFeatures & features) const { - if (features.bufferParams().use_natbib) + switch (features.bufferParams().cite_engine) { + case biblio::ENGINE_BASIC: + break; + case biblio::ENGINE_NATBIB_AUTHORYEAR: + case biblio::ENGINE_NATBIB_NUMERICAL: features.require("natbib"); + break; + case biblio::ENGINE_JURABIB: + features.require("jurabib"); + break; + } } + + +void InsetCitation::replaceContents(string const & from, string const & to) +{ + if (tokenPos(getContents(), ',', from) != -1) { + vector items = getVectorFromString(getContents()); + replace(items.begin(), items.end(), from, to); + setContents(getStringFromVector(items)); + } +} + + +} // namespace lyx