X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2FInsetBibtex.cpp;h=d20f67187cdaf652f9ecdda1d28590106eed60ef;hb=27ba6f282dc36b5cb500bbd51c2f87fd697d0626;hp=99a7d55f82fb2a1b26c471c37044f8bca44d9a6d;hpb=ba39d2de04ebe3754f944692a454b91eef4e6177;p=features.git diff --git a/src/insets/InsetBibtex.cpp b/src/insets/InsetBibtex.cpp index 99a7d55f82..d20f67187c 100644 --- a/src/insets/InsetBibtex.cpp +++ b/src/insets/InsetBibtex.cpp @@ -26,17 +26,20 @@ #include "FuncRequest.h" #include "FuncStatus.h" #include "LaTeXFeatures.h" +#include "output_latex.h" #include "output_xhtml.h" #include "OutputParams.h" #include "PDFOptions.h" #include "texstream.h" #include "TextClass.h" +#include "TocBackend.h" #include "frontends/alert.h" #include "support/convert.h" #include "support/debug.h" #include "support/docstream.h" +#include "support/docstring_list.h" #include "support/ExceptionMessage.h" #include "support/FileNameList.h" #include "support/filetools.h" @@ -59,23 +62,7 @@ namespace os = support::os; InsetBibtex::InsetBibtex(Buffer * buf, InsetCommandParams const & p) : InsetCommand(buf, p) -{ - buffer().invalidateBibfileCache(); - buffer().removeBiblioTempFiles(); -} - - -InsetBibtex::~InsetBibtex() -{ - if (isBufferLoaded()) { - /* Coverity believes that this may throw an exception, but - * actually this code path is not taken when buffer_ == 0 */ - // coverity[fun_call_w_exception] - buffer().invalidateBibfileCache(); - // coverity[fun_call_w_exception] - buffer().removeBiblioTempFiles(); - } -} +{} ParamInfo const & InsetBibtex::findInfo(string const & /* cmdName */) @@ -85,6 +72,8 @@ ParamInfo const & InsetBibtex::findInfo(string const & /* cmdName */) param_info_.add("btprint", ParamInfo::LATEX_OPTIONAL); param_info_.add("bibfiles", ParamInfo::LATEX_REQUIRED); param_info_.add("options", ParamInfo::LYX_INTERNAL); + param_info_.add("encoding", ParamInfo::LYX_INTERNAL); + param_info_.add("file_encodings", ParamInfo::LYX_INTERNAL); param_info_.add("biblatexopts", ParamInfo::LATEX_OPTIONAL); } return param_info_; @@ -96,7 +85,7 @@ void InsetBibtex::doDispatch(Cursor & cur, FuncRequest & cmd) switch (cmd.action()) { case LFUN_INSET_EDIT: - editDatabases(); + editDatabases(cmd.argument()); break; case LFUN_INSET_MODIFY: { @@ -117,8 +106,7 @@ void InsetBibtex::doDispatch(Cursor & cur, FuncRequest & cmd) cur.recordUndo(); setParams(p); - buffer().invalidateBibfileCache(); - buffer().removeBiblioTempFiles(); + cur.buffer()->clearBibFileCache(); cur.forceBufferUpdate(); break; } @@ -144,15 +132,15 @@ bool InsetBibtex::getStatus(Cursor & cur, FuncRequest const & cmd, } -void InsetBibtex::editDatabases() const +void InsetBibtex::editDatabases(docstring const db) const { vector bibfilelist = getVectorFromString(getParam("bibfiles")); if (bibfilelist.empty()) return; - int nr_databases = bibfilelist.size(); - if (nr_databases > 1) { + size_t nr_databases = bibfilelist.size(); + if (nr_databases > 1 && db.empty()) { docstring const engine = usingBiblatex() ? _("Biblatex") : _("BibTeX"); docstring message = bformat(_("The %1$s[[BibTeX/Biblatex]] inset includes %2$s databases.\n" "If you proceed, all of them will be opened."), @@ -167,7 +155,9 @@ void InsetBibtex::editDatabases() const vector::const_iterator it = bibfilelist.begin(); vector::const_iterator en = bibfilelist.end(); for (; it != en; ++it) { - FileName const bibfile = getBibTeXPath(*it, buffer()); + if (!db.empty() && db != *it) + continue; + FileName const bibfile = buffer().getBibfilePath(*it); theFormats().edit(buffer(), bibfile, theFormats().getFormatFromFile(bibfile)); } @@ -245,8 +235,7 @@ docstring InsetBibtex::toolTip(BufferView const & /*bv*/, int /*x*/, int /*y*/) tip += _("included in TOC"); } if (!getParam("biblatexopts").empty()) { - if (toc) - tip += "
"; + tip += "
"; tip += _("Options: ") + getParam("biblatexopts"); } } @@ -304,8 +293,11 @@ void InsetBibtex::latex(otexstream & os, OutputParams const & runparams) const os << "\n"; } else {// using BibTeX // Database(s) - vector const db_out = + vector> const dbs = buffer().prepareBibFilePaths(runparams, getBibFiles(), false); + vector db_out; + for (pair const & db : dbs) + db_out.push_back(db.first); // Style options if (style == "default") style = buffer().masterParams().defaultBiblioStyle(); @@ -344,6 +336,19 @@ void InsetBibtex::latex(otexstream & os, OutputParams const & runparams) const _("There are spaces in the path to your BibTeX style file.\n" "BibTeX will be unable to find it.")); } + // Encoding + bool encoding_switched = false; + Encoding const * const save_enc = runparams.encoding; + docstring const encoding = getParam("encoding"); + if (!encoding.empty() && encoding != from_ascii("default")) { + Encoding const * const enc = encodings.fromLyXName(to_ascii(encoding)); + if (enc != runparams.encoding) { + os << "\\bgroup"; + switchEncoding(os.os(), buffer().params(), runparams, *enc, true); + runparams.encoding = enc; + encoding_switched = true; + } + } // Handle the bibtopic case if (!db_out.empty() && buffer().masterParams().useBibtopic()) { os << "\\begin{btSect}"; @@ -375,34 +380,20 @@ void InsetBibtex::latex(otexstream & os, OutputParams const & runparams) const } os << "\\bibliography{" << getStringFromVector(db_out) << "}\n"; } + if (encoding_switched){ + // Switch back + switchEncoding(os.os(), buffer().params(), + runparams, *save_enc, true, true); + os << "\\egroup" << breakln; + runparams.encoding = save_enc; + } } } -support::FileNamePairList InsetBibtex::getBibFiles() const +docstring_list InsetBibtex::getBibFiles() const { - FileName path(buffer().filePath()); - support::PathChanger p(path); - - // We need to store both the real FileName and the way it is entered - // (with full path, rel path or as a single file name). - // The latter is needed for biblatex's central bibfile macro. - support::FileNamePairList vec; - - vector bibfilelist = getVectorFromString(getParam("bibfiles")); - vector::const_iterator it = bibfilelist.begin(); - vector::const_iterator en = bibfilelist.end(); - for (; it != en; ++it) { - FileName const file = getBibTeXPath(*it, buffer()); - - if (!file.empty()) - vec.push_back(make_pair(*it, file)); - else - LYXERR0("Couldn't find " + to_utf8(*it) + " in InsetBibtex::getBibFiles()!"); - } - - return vec; - + return getVectorFromString(getParam("bibfiles")); } namespace { @@ -478,11 +469,9 @@ namespace { return false; // read value - bool legalChar = true; while (ifs && !isSpace(ch) && - delimChars.find(ch) == docstring::npos && - (legalChar = (illegalChars.find(ch) == docstring::npos)) - ) + delimChars.find(ch) == docstring::npos && + illegalChars.find(ch) == docstring::npos) { if (chCase == makeLowerCase) val += lowercase(ch); @@ -491,7 +480,7 @@ namespace { ifs.get(ch); } - if (!legalChar) { + if (illegalChars.find(ch) != docstring::npos) { ifs.putback(ch); return false; } @@ -643,16 +632,16 @@ namespace { return true; } -} +} // namespace -void InsetBibtex::collectBibKeys(InsetIterator const & /*di*/) const +void InsetBibtex::collectBibKeys(InsetIterator const & /*di*/, FileNameList & checkedFiles) const { - parseBibTeXFiles(); + parseBibTeXFiles(checkedFiles); } -void InsetBibtex::parseBibTeXFiles() const +void InsetBibtex::parseBibTeXFiles(FileNameList & checkedFiles) const { // This bibtex parser is a first step to parse bibtex files // more precisely. @@ -666,27 +655,42 @@ void InsetBibtex::parseBibTeXFiles() const // bibtex does. // // Officially bibtex does only support ASCII, but in practice - // you can use the encoding of the main document as long as - // some elements like keys and names are pure ASCII. Therefore - // we convert the file from the buffer encoding. + // you can use any encoding as long as some elements like keys + // and names are pure ASCII. We support specifying an encoding, + // and we convert the file from that (default is buffer encoding). // We don't restrict keys to ASCII in LyX, since our own // InsetBibitem can generate non-ASCII keys, and nonstandard // 8bit clean bibtex forks exist. BiblioInfo keylist; - support::FileNamePairList const files = getBibFiles(); - support::FileNamePairList::const_iterator it = files.begin(); - support::FileNamePairList::const_iterator en = files.end(); - for (; it != en; ++ it) { - ifdocstream ifs(it->second.toFilesystemEncoding().c_str(), - ios_base::in, buffer().masterParams().encoding().iconvName()); + docstring_list const files = getBibFiles(); + for (auto const & bf : files) { + FileName const bibfile = buffer().getBibfilePath(bf); + if (bibfile.empty()) { + LYXERR0("Unable to find path for " << bf << "!"); + continue; + } + if (find(checkedFiles.begin(), checkedFiles.end(), bibfile) != checkedFiles.end()) + // already checked this one. Skip. + continue; + else + // record that we check this. + checkedFiles.push_back(bibfile); + string encoding = buffer().masterParams().encoding().iconvName(); + string ienc = buffer().masterParams().bibFileEncoding(to_utf8(bf)); + if (ienc.empty() || ienc == "general") + ienc = to_ascii(params()["encoding"]); + + if (!ienc.empty() && ienc != "auto-legacy-plain" && ienc != "auto-legacy" && encodings.fromLyXName(ienc)) + encoding = encodings.fromLyXName(ienc)->iconvName(); + ifdocstream ifs(bibfile.toFilesystemEncoding().c_str(), + ios_base::in, encoding); char_type ch; VarMap strings; while (ifs) { - ifs.get(ch); if (!ifs) break; @@ -845,18 +849,6 @@ void InsetBibtex::parseBibTeXFiles() const } -FileName InsetBibtex::getBibTeXPath(docstring const & filename, Buffer const & buf) -{ - string texfile = changeExtension(to_utf8(filename), "bib"); - // note that, if the filename can be found directly from the path, - // findtexfile will just return a FileName object for that path. - FileName file(findtexfile(texfile, "bib")); - if (file.empty()) - file = FileName(makeAbsPath(texfile, buf.filePath())); - return file; -} - - bool InsetBibtex::addDatabase(docstring const & db) { docstring bibfiles = getParam("bibfiles"); @@ -906,10 +898,69 @@ void InsetBibtex::validate(LaTeXFeatures & features) const } +void InsetBibtex::updateBuffer(ParIterator const &, UpdateType) +{ + buffer().registerBibfiles(getBibFiles()); + // record encoding of bib files for biblatex + string const enc = (params()["encoding"] == from_ascii("default")) ? + string() : to_ascii(params()["encoding"]); + bool invalidate = false; + if (buffer().params().bibEncoding() != enc) { + buffer().params().setBibEncoding(enc); + invalidate = true; + } + map encs = getFileEncodings(); + map::const_iterator it = encs.begin(); + for (; it != encs.end(); ++it) { + if (buffer().params().bibFileEncoding(it->first) != it->second) { + buffer().params().setBibFileEncoding(it->first, it->second); + invalidate = true; + } + } + if (invalidate) + buffer().invalidateBibinfoCache(); +} + + +map InsetBibtex::getFileEncodings() const +{ + vector ps = + getVectorFromString(to_utf8(getParam("file_encodings")), "\t"); + std::map res; + for (string const & s: ps) { + string key; + string val = split(s, key, ' '); + res[key] = val; + } + return res; +} + + +docstring InsetBibtex::getRefLabel() const +{ + if (buffer().masterParams().documentClass().hasLaTeXLayout("chapter")) + return buffer().B_("Bibliography"); + return buffer().B_("References"); +} + + +void InsetBibtex::addToToc(DocIterator const & cpit, bool output_active, + UpdateType, TocBackend & backend) const +{ + if (!prefixIs(to_utf8(getParam("options")), "bibtotoc")) + return; + + docstring const str = getRefLabel(); + TocBuilder & b = backend.builder("tableofcontents"); + b.pushItem(cpit, str, output_active); + b.pop(); +} + + int InsetBibtex::plaintext(odocstringstream & os, OutputParams const & op, size_t max_length) const { - docstring const reflabel = buffer().B_("References"); + docstring const reflabel = getRefLabel(); // We could output more information here, e.g., what databases are included // and information about options. But I don't necessarily see any reason to @@ -948,7 +999,7 @@ int InsetBibtex::plaintext(odocstringstream & os, refoutput += bibinfo.getInfo(entry.key(), buffer(), ci) + "\n\n"; } os << refoutput; - return refoutput.size(); + return int(refoutput.size()); } @@ -959,7 +1010,7 @@ docstring InsetBibtex::xhtml(XHTMLStream & xs, OutputParams const &) const { BiblioInfo const & bibinfo = buffer().masterBibInfo(); bool const all_entries = getParam("btprint") == "btPrintAll"; - vector const & cites = + vector const & cites = all_entries ? bibinfo.getKeys() : bibinfo.citedEntries(); docstring const reflabel = buffer().B_("References"); @@ -984,17 +1035,17 @@ docstring InsetBibtex::xhtml(XHTMLStream & xs, OutputParams const &) const continue; BibTeXInfo const & entry = biit->second; - string const attr = "class='bibtexentry' id='LyXCite-" + string const attr = "class='bibtexentry' id='LyXCite-" + to_utf8(html::cleanAttr(entry.key())) + "'"; xs << html::StartTag("div", attr); - + // don't print labels if we're outputting all entries if (!all_entries) { xs << html::StartTag("span", "class='bibtexlabel'") << entry.label() << html::EndTag("span"); } - + // FIXME Right now, we are calling BibInfo::getInfo on the key, // which will give us all the cross-referenced info. But for every // entry, so there's a lot of repitition. This should be fixed.