From: Juergen Spitzmueller Date: Sat, 4 Feb 2017 11:02:00 +0000 (+0100) Subject: Support for multiple bibliographies X-Git-Tag: 2.3.0alpha1~404 X-Git-Url: https://git.lyx.org/gitweb/?a=commitdiff_plain;h=7ca20b292c285ae8d6380292626345b78a13abf4;p=features.git Support for multiple bibliographies Using refsection=unit in biblatex, or bibtopic's btUnit environments in BibTeX. File format change. --- diff --git a/development/FORMAT b/development/FORMAT index d03a6b4547..49d377b42e 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -7,6 +7,14 @@ changes happened in particular if possible. A good example would be ----------------------- +2017-02-04 Jürgen Spitzmüller + * Format incremented to 533: Support for multiple bibliographies + - New buffer param \multibib {none|part|chapter|section|subsetion} + LaTeX support either via biblatex option "refsection" or bibtopic's + btUnit environment. + - New btprint value "bibbysection" of CommandInset bibtex: + outputs \bibbysection instead of \printbibliography. + 2017-01-28 Jürgen Spitzmüller * Format incremented to 532: literal command inset parameter. With this, inset command params with ParamInfo::HANDLING_LATEXIFY diff --git a/lib/lyx2lyx/lyx_2_3.py b/lib/lyx2lyx/lyx_2_3.py index 23bdda8e0b..3b46470b18 100644 --- a/lib/lyx2lyx/lyx_2_3.py +++ b/lib/lyx2lyx/lyx_2_3.py @@ -1191,7 +1191,7 @@ def revert_biblatex(document): biblatex = False if engine in ["biblatex", "biblatex-natbib"]: biblatex = True - document.header[i] = "\cite_engine natbib" + document.header[i] = "\\cite_engine natbib" # 3. Store and remove new document headers bibstyle = "" @@ -1595,6 +1595,148 @@ def revert_literalparam(document): del document.body[k] + +def revert_multibib(document): + " Revert multibib support " + + # 1. Get cite engine + engine = "basic" + i = find_token(document.header, "\\cite_engine", 0) + if i == -1: + document.warning("Malformed document! Missing \\cite_engine") + else: + engine = get_value(document.header, "\\cite_engine", i) + + # 2. Do we use biblatex? + biblatex = False + if engine in ["biblatex", "biblatex-natbib"]: + biblatex = True + + # 3. Store and remove multibib document header + multibib = "" + i = find_token(document.header, "\\multibib", 0) + if i != -1: + multibib = get_value(document.header, "\\multibib", i) + del document.header[i] + + if not multibib: + return + + # 4. The easy part: Biblatex + if biblatex: + i = find_token(document.header, "\\biblio_options", 0) + if i == -1: + k = find_token(document.header, "\\use_bibtopic", 0) + if k == -1: + # this should not happen + document.warning("Malformed LyX document! No \\use_bibtopic header found!") + return + document.header[k-1 : k-1] = ["\\biblio_options " + "refsection=" + multibib] + else: + biblio_options = get_value(document.header, "\\biblio_options", i) + if biblio_options: + biblio_options += "," + biblio_options += "refsection=" + multibib + document.header[i] = "\\biblio_options " + biblio_options + + # Bibtex insets + i = 0 + while (True): + i = find_token(document.body, "\\begin_inset CommandInset bibtex", i) + if i == -1: + break + j = find_end_of_inset(document.body, i) + if j == -1: + document.warning("Can't find end of bibtex inset at line %d!!" %(i)) + i += 1 + continue + btprint = get_quoted_value(document.body, "btprint", i, j) + if btprint != "bibbysection": + i += 1 + continue + opts = get_quoted_value(document.body, "biblatexopts", i, j) + # change btprint line + k = find_token(document.body, "btprint", i, j) + if k != -1: + document.body[k] = "btprint \"btPrintCited\"" + # Insert ERT \\bibbysection and wrap bibtex inset to a Note + pcmd = "bibbysection" + if opts: + pcmd += "[" + opts + "]" + repl = ["\\begin_inset ERT", "status open", "", "\\begin_layout Plain Layout",\ + "", "", "\\backslash", pcmd, "\\end_layout", "", "\\end_inset", "", "",\ + "\\end_layout", "", "\\begin_layout Standard", "\\begin_inset Note Note",\ + "status open", "", "\\begin_layout Plain Layout" ] + repl += document.body[i:j+1] + repl += ["", "\\end_layout", "", "\\end_inset", "", ""] + document.body[i:j+1] = repl + j += 27 + + i = j + 1 + return + + # 5. More tricky: Bibtex/Bibtopic + k = find_token(document.header, "\\use_bibtopic", 0) + if k == -1: + # this should not happen + document.warning("Malformed LyX document! No \\use_bibtopic header found!") + return + document.header[k] = "\\use_bibtopic true" + + # Possible units. This assumes that the LyX name follows the std, + # which might not always be the case. But it's as good as we can get. + units = { + "part" : "Part", + "chapter" : "Chapter", + "section" : "Section", + "subsection" : "Subsection", + } + + if multibib not in units.keys(): + document.warning("Unknown multibib value `%s'!" % nultibib) + return + unit = units[multibib] + btunit = False + i = 0 + while (True): + i = find_token(document.body, "\\begin_layout " + unit, i) + if i == -1: + break + if btunit: + document.body[i-1 : i-1] = ["\\begin_layout Standard", + "\\begin_inset ERT", "status open", "", + "\\begin_layout Plain Layout", "", "", + "\\backslash", + "end{btUnit}", "\\end_layout", + "\\begin_layout Plain Layout", "", + "\\backslash", + "begin{btUnit}" + "\\end_layout", "", "\\end_inset", "", "", + "\\end_layout", ""] + i += 21 + else: + document.body[i-1 : i-1] = ["\\begin_layout Standard", + "\\begin_inset ERT", "status open", "", + "\\begin_layout Plain Layout", "", "", + "\\backslash", + "begin{btUnit}" + "\\end_layout", "", "\\end_inset", "", "", + "\\end_layout", ""] + i += 16 + btunit = True + i += 1 + + if btunit: + i = find_token(document.body, "\\end_body", i) + document.body[i-1 : i-1] = ["\\begin_layout Standard", + "\\begin_inset ERT", "status open", "", + "\\begin_layout Plain Layout", "", "", + "\\backslash", + "end{btUnit}" + "\\end_layout", "", "\\end_inset", "", "", + "\\end_layout", ""] + + ## # Conversion hub # @@ -1624,10 +1766,12 @@ convert = [ [529, []], [530, []], [531, []], - [532, [convert_literalparam]] + [532, [convert_literalparam]], + [533, []], ] revert = [ + [532, [revert_multibib]], [531, [revert_literalparam]], [530, [revert_qualicites]], [529, [revert_bibpackopts]], diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 5ad548db74..7641741076 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -915,6 +915,7 @@ int Buffer::readHeader(Lexer & lex) params().biblio_opts.erase(); params().biblatex_bibstyle.erase(); params().biblatex_citestyle.erase(); + params().multibib.erase(); for (int i = 0; i < 4; ++i) { params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i]; diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index e044d4e45a..1a5b6b9e60 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -393,6 +393,7 @@ BufferParams::BufferParams() use_geometry = false; biblio_style = "plain"; use_bibtopic = false; + multibib = string(); use_indices = false; save_transient_properties = true; track_changes = false; @@ -858,6 +859,8 @@ string BufferParams::readToken(Lexer & lex, string const & token, biblatex_citestyle = trim(lex.getString()); } else if (token == "\\use_bibtopic") { lex >> use_bibtopic; + } else if (token == "\\multibib") { + lex >> multibib; } else if (token == "\\use_indices") { lex >> use_indices; } else if (token == "\\tracking_changes") { @@ -1233,6 +1236,8 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const os << "\n\\biblatex_bibstyle " << biblatex_bibstyle; if (!biblatex_citestyle.empty()) os << "\n\\biblatex_citestyle " << biblatex_citestyle; + if (!multibib.empty()) + os << "\n\\multibib " << multibib; os << "\n\\use_bibtopic " << convert(use_bibtopic) << "\n\\use_indices " << convert(use_indices) @@ -2213,6 +2218,10 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, delim = ","; } } + if (!multibib.empty()) { + opts += delim + "refsection=" + multibib; + delim = ","; + } if (bibtexCommand() == "bibtex8" || prefixIs(bibtexCommand(), "bibtex8 ")) { opts += delim + "backend=bibtex8"; diff --git a/src/BufferParams.h b/src/BufferParams.h index a09366bb3c..3914837efa 100644 --- a/src/BufferParams.h +++ b/src/BufferParams.h @@ -378,10 +378,14 @@ public: void use_package(std::string const & p, Package u); /// All packages that can be switched on or off static std::map const & auto_packages(); + /// Do we use the bibtopic package? + bool useBibtopic() const { return (use_bibtopic || !multibib.empty()) && !useBiblatex(); } /// Split bibliography? - bool useBibtopic() const { return use_bibtopic && !useBiblatex(); } + bool splitbib() const { return use_bibtopic; } /// Set split bibliography - void bibtopic(bool const b) { use_bibtopic = b; } + void splitbib(bool const b) { use_bibtopic = b; } + /// Do we have multiple bibliographies (by chapter etc.)? + std::string multibib; /// Split the index? bool use_indices; /// Save transient properties? diff --git a/src/OutputParams.cpp b/src/OutputParams.cpp index 2fdc065180..3101e32213 100644 --- a/src/OutputParams.cpp +++ b/src/OutputParams.cpp @@ -24,7 +24,7 @@ OutputParams::OutputParams(Encoding const * enc) encoding(enc), free_spacing(false), use_babel(false), use_polyglossia(false), use_indices(false), use_japanese(false), linelen(0), depth(0), exportdata(new ExportData), inDisplayMath(false), wasDisplayMath(false), - inComment(false), inTableCell(NO), inFloat(NONFLOAT), + inComment(false), openbtUnit(false), inTableCell(NO), inFloat(NONFLOAT), inIndexEntry(false), inIPA(false), inDeletedInset(0), changeOfDeletedInset(Change::UNCHANGED), par_begin(0), par_end(0), lastid(-1), lastpos(0), isLastPar(false), diff --git a/src/OutputParams.h b/src/OutputParams.h index b3359d417a..5977e2f75e 100644 --- a/src/OutputParams.h +++ b/src/OutputParams.h @@ -205,6 +205,10 @@ public: */ bool inComment; + /** Whether a btUnit (for multiple biblographies) is open. + */ + bool openbtUnit; + /** Whether we are in a table cell. * For newline, it matters whether its content is aligned or not. */ diff --git a/src/frontends/qt4/GuiBibtex.cpp b/src/frontends/qt4/GuiBibtex.cpp index b49d663b8a..1f87b6ac9d 100644 --- a/src/frontends/qt4/GuiBibtex.cpp +++ b/src/frontends/qt4/GuiBibtex.cpp @@ -330,20 +330,15 @@ void GuiBibtex::updateContents() bibtocCB->setChecked(bibtotoc() && !bibtopic); bibtocCB->setEnabled(!bibtopic); - if (!bibtopic && btPrintCO->count() == 3) - btPrintCO->removeItem(1); - else if (bibtopic && btPrintCO->count() < 3) - btPrintCO->insertItem(1, qt_("all uncited references", 0)); + btPrintCO->clear(); + btPrintCO->addItem(qt_("all cited references"), toqstr("btPrintCited")); + if (bibtopic) + btPrintCO->addItem(qt_("all uncited references"), toqstr("btPrintNotCited")); + btPrintCO->addItem(qt_("all references"), toqstr("btPrintAll")); + if (usingBiblatex() && !buffer().masterParams().multibib.empty()) + btPrintCO->addItem(qt_("all reference units"), toqstr("bibbysection")); - docstring const & btprint = params_["btprint"]; - int btp = 0; - if ((bibtopic && btprint == from_ascii("btPrintNotCited")) || - (!bibtopic && btprint == from_ascii("btPrintAll"))) - btp = 1; - else if (bibtopic && btprint == from_ascii("btPrintAll")) - btp = 2; - - btPrintCO->setCurrentIndex(btp); + btPrintCO->setCurrentIndex(btPrintCO->findData(toqstr(params_["btprint"]))); // Only useful for biblatex biblatexOptsLA->setVisible(biblatex); @@ -415,35 +410,7 @@ void GuiBibtex::applyView() params_["biblatexopts"] = qstring_to_ucs4(biblatexOptsLE->text()); - int btp = btPrintCO->currentIndex(); - - if (usingBibtopic()) { - // bibtopic allows three kinds of sections: - // 1. sections that include all cited references of the database(s) - // 2. sections that include all uncited references of the database(s) - // 3. sections that include all references of the database(s), cited or not - switch (btp) { - case 0: - params_["btprint"] = from_ascii("btPrintCited"); - break; - case 1: - params_["btprint"] = from_ascii("btPrintNotCited"); - break; - case 2: - params_["btprint"] = from_ascii("btPrintAll"); - break; - } - } else { - switch (btp) { - case 0: - params_["btprint"] = docstring(); - break; - case 1: - // use \nocite{*} - params_["btprint"] = from_ascii("btPrintAll"); - break; - } - } + params_["btprint"] = qstring_to_ucs4(btPrintCO->itemData(btPrintCO->currentIndex()).toString()); } diff --git a/src/frontends/qt4/GuiDocument.cpp b/src/frontends/qt4/GuiDocument.cpp index fa4a6e280b..daee81b952 100644 --- a/src/frontends/qt4/GuiDocument.cpp +++ b/src/frontends/qt4/GuiDocument.cpp @@ -1126,6 +1126,8 @@ GuiDocument::GuiDocument(GuiView & lv) this, SLOT(citeStyleChanged())); connect(biblioModule->bibtopicCB, SIGNAL(clicked()), this, SLOT(biblioChanged())); + connect(biblioModule->bibunitsCO, SIGNAL(activated(int)), + this, SLOT(biblioChanged())); connect(biblioModule->bibtexCO, SIGNAL(activated(int)), this, SLOT(bibtexChanged(int))); connect(biblioModule->bibtexOptionsLE, SIGNAL(textChanged(QString)), @@ -2726,7 +2728,10 @@ void GuiDocument::applyView() else bp_.setCiteEngineType(ENGINE_TYPE_DEFAULT); - bp_.bibtopic(biblioModule->bibtopicCB->isChecked()); + bp_.splitbib(biblioModule->bibtopicCB->isChecked()); + + bp_.multibib = fromqstr(biblioModule->bibunitsCO->itemData( + biblioModule->bibunitsCO->currentIndex()).toString()); bp_.setDefaultBiblioStyle(fromqstr(biblioModule->defaultBiblioCO->currentText())); @@ -3152,8 +3157,24 @@ void GuiDocument::paramsToDialog() biblioModule->citeStyleCO->setCurrentIndex( biblioModule->citeStyleCO->findData(bp_.citeEngineType())); - biblioModule->bibtopicCB->setChecked( - bp_.useBibtopic()); + biblioModule->bibtopicCB->setChecked(bp_.splitbib()); + + biblioModule->bibunitsCO->clear(); + biblioModule->bibunitsCO->addItem(qt_("No"), QString()); + if (documentClass().hasLaTeXLayout("part")) + biblioModule->bibunitsCO->addItem(qt_("per part"), toqstr("part")); + if (documentClass().hasLaTeXLayout("chapter")) + biblioModule->bibunitsCO->addItem(qt_("per chapter"), toqstr("chapter")); + if (documentClass().hasLaTeXLayout("section")) + biblioModule->bibunitsCO->addItem(qt_("per section"), toqstr("section")); + if (documentClass().hasLaTeXLayout("subsection")) + biblioModule->bibunitsCO->addItem(qt_("per subsection"), toqstr("subsection")); + + int const mbpos = biblioModule->bibunitsCO->findData(toqstr(bp_.multibib)); + if (mbpos != -1) + biblioModule->bibunitsCO->setCurrentIndex(mbpos); + else + biblioModule->bibunitsCO->setCurrentIndex(0); updateEngineDependends(); diff --git a/src/frontends/qt4/ui/BiblioUi.ui b/src/frontends/qt4/ui/BiblioUi.ui index 2d12fec119..e084c5e6d4 100644 --- a/src/frontends/qt4/ui/BiblioUi.ui +++ b/src/frontends/qt4/ui/BiblioUi.ui @@ -6,8 +6,8 @@ 0 0 - 545 - 481 + 534 + 482 @@ -305,7 +305,7 @@ Select this if you want to split your bibliography into sections - Secti&oned bibliography + Subdivided bibli&ography @@ -334,6 +334,33 @@ + + + + + + &Multiple bibliographies: + + + bibunitsCO + + + + + + + + 0 + 0 + + + + Generate a bibliography per defined unit. + + + + + diff --git a/src/insets/InsetBibtex.cpp b/src/insets/InsetBibtex.cpp index afbbc9e74f..05a154466a 100644 --- a/src/insets/InsetBibtex.cpp +++ b/src/insets/InsetBibtex.cpp @@ -206,12 +206,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") @@ -223,8 +223,17 @@ docstring InsetBibtex::toolTip(BufferView const & /*bv*/, int /*x*/, int /*y*/) tip += _("included in TOC"); } } 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 += "
"; @@ -250,6 +259,8 @@ void InsetBibtex::latex(otexstream & os, OutputParams const & runparams) const // 4. \end{btSect} // With Biblatex: // \printbibliography[biblatexopts] + // or + // \bibbysection[biblatexopts] - if btprint is "bibbysection" string style = to_utf8(getParam("options")); // maybe empty! and with bibtotoc string bibtotoc; @@ -269,7 +280,10 @@ 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"; diff --git a/src/output_latex.cpp b/src/output_latex.cpp index b6ed0cec18..0e2abadbc0 100644 --- a/src/output_latex.cpp +++ b/src/output_latex.cpp @@ -1259,9 +1259,10 @@ void TeXOnePar(Buffer const & buf, void latexParagraphs(Buffer const & buf, Text const & text, otexstream & os, - OutputParams const & runparams, + OutputParams const & runparams_in, string const & everypar) { + OutputParams runparams = runparams_in; LASSERT(runparams.par_begin <= runparams.par_end, { os << "% LaTeX Output Error\n"; return; } ); @@ -1361,6 +1362,15 @@ void latexParagraphs(Buffer const & buf, was_title = false; } + if (layout.isCommand() && !layout.latexname().empty() + && layout.latexname() == bparams.multibib) { + if (runparams.openbtUnit) + os << "\\end{btUnit}\n"; + if (!bparams.useBiblatex()) { + os << '\n' << "\\begin{btUnit}\n"; + runparams.openbtUnit = true; + } + } if (!layout.isEnvironment() && par->params().leftIndent().zero()) { // This is a standard top level paragraph, TeX it and continue. @@ -1393,6 +1403,9 @@ void latexParagraphs(Buffer const & buf, } } + if (runparams.openbtUnit) + os << "\\end{btUnit}\n"; + // if "auto end" is switched off, explicitly close the language at the end // but only if the last par is in a babel or polyglossia language string const lang_end_command = runparams.use_polyglossia ? diff --git a/src/tex2lyx/TODO.txt b/src/tex2lyx/TODO.txt index d7178d3d0b..e92739ecd0 100644 --- a/src/tex2lyx/TODO.txt +++ b/src/tex2lyx/TODO.txt @@ -146,6 +146,14 @@ Format LaTeX feature LyX feature posttextlist "key1 post1\tab key2 post2..." Same for: \Cites, \textcites, \Textcites, \parencites, \Parencites, \smartcites, \Smartcites, \autocites, Autocites +533 Multibib support + \begin{btUnit}...\end{btUnit} \multibib {none|part|chapter|section|subsetion} + (if a part, chapter, section etc. + follows the \begin...) + \usepackage[refsection= \multibib + \bibbysection[] \begin_inset CommandInset bibtex + biblatexopts "" + btprint "bibbysection" diff --git a/src/version.h b/src/version.h index 4056e9ddee..0ec2faf92d 100644 --- a/src/version.h +++ b/src/version.h @@ -32,8 +32,8 @@ extern char const * const lyx_version_info; // Do not remove the comment below, so we get merge conflict in // independent branches. Instead add your own. -#define LYX_FORMAT_LYX 532 // gb/spitz add literal command inset parameter -#define LYX_FORMAT_TEX2LYX 532 +#define LYX_FORMAT_LYX 533 // spitz: multibib +#define LYX_FORMAT_TEX2LYX 533 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX #ifndef _MSC_VER