X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2FInsetBibtex.cpp;h=d20f67187cdaf652f9ecdda1d28590106eed60ef;hb=27ba6f282dc36b5cb500bbd51c2f87fd697d0626;hp=4ead8663492ef6ac631715b5997323210af1052b;hpb=322808281ff449c542295c8e07d5b6c4c950b571;p=features.git diff --git a/src/insets/InsetBibtex.cpp b/src/insets/InsetBibtex.cpp index 4ead866349..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,19 +62,7 @@ namespace os = support::os; InsetBibtex::InsetBibtex(Buffer * buf, InsetCommandParams const & p) : InsetCommand(buf, p) -{ - buffer().invalidateBibfileCache(); - buffer().removeBiblioTempFiles(); -} - - -InsetBibtex::~InsetBibtex() -{ - if (isBufferLoaded()) { - buffer().invalidateBibfileCache(); - buffer().removeBiblioTempFiles(); - } -} +{} ParamInfo const & InsetBibtex::findInfo(string const & /* cmdName */) @@ -81,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_; @@ -92,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: { @@ -113,8 +106,7 @@ void InsetBibtex::doDispatch(Cursor & cur, FuncRequest & cmd) cur.recordUndo(); setParams(p); - buffer().invalidateBibfileCache(); - buffer().removeBiblioTempFiles(); + cur.buffer()->clearBibFileCache(); cur.forceBufferUpdate(); break; } @@ -140,16 +132,16 @@ 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) { - docstring const engine = usingBiblatex() ? _("Biblatex") : _("BibTex"); + 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."), engine, convert(nr_databases)); @@ -163,16 +155,18 @@ 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()); - formats.edit(buffer(), bibfile, - formats.getFormatFromFile(bibfile)); + if (!db.empty() && db != *it) + continue; + FileName const bibfile = buffer().getBibfilePath(*it); + theFormats().edit(buffer(), bibfile, + theFormats().getFormatFromFile(bibfile)); } } bool InsetBibtex::usingBiblatex() const { - return buffer().params().useBiblatex(); + return buffer().masterParams().useBiblatex(); } @@ -206,12 +200,12 @@ docstring InsetBibtex::toolTip(BufferView const & /*bv*/, int /*x*/, int /*y*/) style = split(style, bibtotoc, char_type(',')); } + docstring const btprint = getParam("btprint"); if (!usingBiblatex()) { tip += _("Style File:"); tip += "
  • " + (style.empty() ? _("none") : style) + "
"; tip += _("Lists:") + " "; - docstring btprint = getParam("btprint"); if (btprint == "btPrintAll") tip += _("all references"); else if (btprint == "btPrintNotCited") @@ -222,12 +216,26 @@ docstring InsetBibtex::toolTip(BufferView const & /*bv*/, int /*x*/, int /*y*/) tip += ", "; tip += _("included in TOC"); } + if (!buffer().parent() + && buffer().params().multibib == "child") { + tip += "
"; + tip += _("Note: This bibliography is not output, since bibliographies in the master file " + "are not allowed with the setting 'Multiple bibliographies per child document'"); + } } else { - if (toc) - tip += _("Included in TOC"); + tip += _("Lists:") + " "; + if (btprint == "bibbysection") + tip += _("all reference units"); + else if (btprint == "btPrintAll") + tip += _("all references"); + else + tip += _("all cited references"); + if (toc) { + tip += ", "; + tip += _("included in TOC"); + } if (!getParam("biblatexopts").empty()) { - if (toc) - tip += "
"; + tip += "
"; tip += _("Options: ") + getParam("biblatexopts"); } } @@ -250,6 +258,13 @@ void InsetBibtex::latex(otexstream & os, OutputParams const & runparams) const // 4. \end{btSect} // With Biblatex: // \printbibliography[biblatexopts] + // or + // \bibbysection[biblatexopts] - if btprint is "bibbysection" + + // chapterbib does not allow bibliographies in the master + if (!usingBiblatex() && !runparams.is_child + && buffer().params().multibib == "child") + return; string style = to_utf8(getParam("options")); // maybe empty! and with bibtotoc string bibtotoc; @@ -269,19 +284,25 @@ void InsetBibtex::latex(otexstream & os, OutputParams const & runparams) const docstring btprint = getParam("btprint"); if (btprint == "btPrintAll") os << "\\nocite{*}\n"; - os << "\\printbibliography"; + if (btprint == "bibbysection" && !buffer().masterParams().multibib.empty()) + os << "\\bibbysection"; + else + os << "\\printbibliography"; if (!opts.empty()) os << "[" << opts << "]"; 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().params().defaultBiblioStyle(); - if (!style.empty() && !buffer().params().use_bibtopic) { - string base = buffer().prepareFileNameForLaTeX(style, ".bst", runparams.nice); + style = buffer().masterParams().defaultBiblioStyle(); + if (!style.empty() && !buffer().masterParams().useBibtopic()) { + string base = buffer().masterBuffer()->prepareFileNameForLaTeX(style, ".bst", runparams.nice); FileName const try_in_file = makeAbsPath(base + ".bst", buffer().filePath()); bool const not_from_texmf = try_in_file.isReadableFile(); @@ -315,8 +336,21 @@ 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().params().use_bibtopic) { + if (!db_out.empty() && buffer().masterParams().useBibtopic()) { os << "\\begin{btSect}"; if (!style.empty()) os << "[" << style << "]"; @@ -329,51 +363,37 @@ void InsetBibtex::latex(otexstream & os, OutputParams const & runparams) const << "\\end{btSect}\n"; } // bibtotoc option - if (!bibtotoc.empty() && !buffer().params().use_bibtopic) { + if (!bibtotoc.empty() && !buffer().masterParams().useBibtopic()) { // set label for hyperref, see http://www.lyx.org/trac/ticket/6470 - if (buffer().params().pdfoptions().use_hyperref) + if (buffer().masterParams().pdfoptions().use_hyperref) os << "\\phantomsection"; - if (buffer().params().documentClass().hasLaTeXLayout("chapter")) + if (buffer().masterParams().documentClass().hasLaTeXLayout("chapter")) os << "\\addcontentsline{toc}{chapter}{\\bibname}"; - else if (buffer().params().documentClass().hasLaTeXLayout("section")) + else if (buffer().masterParams().documentClass().hasLaTeXLayout("section")) os << "\\addcontentsline{toc}{section}{\\refname}"; } // The bibliography command - if (!db_out.empty() && !buffer().params().use_bibtopic) { + if (!db_out.empty() && !buffer().masterParams().useBibtopic()) { docstring btprint = getParam("btprint"); if (btprint == "btPrintAll") { os << "\\nocite{*}\n"; } 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 { @@ -449,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); @@ -462,7 +480,7 @@ namespace { ifs.get(ch); } - if (!legalChar) { + if (illegalChars.find(ch) != docstring::npos) { ifs.putback(ch); return false; } @@ -614,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. @@ -637,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().params().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; @@ -816,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"); @@ -862,8 +883,11 @@ bool InsetBibtex::delDatabase(docstring const & db) void InsetBibtex::validate(LaTeXFeatures & features) const { - if (features.bufferParams().use_bibtopic) + BufferParams const & mparams = features.buffer().masterParams(); + if (mparams.useBibtopic()) features.require("bibtopic"); + else if (!mparams.useBiblatex() && mparams.multibib == "child") + features.require("chapterbib"); // FIXME XHTML // It'd be better to be able to get this from an InsetLayout, but at present // InsetLayouts do not seem really to work for things that aren't InsetTexts. @@ -874,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 @@ -916,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()); } @@ -927,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"); @@ -952,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.