Support for multiple bibliographies

Using refsection=unit in biblatex, or bibtopic's btUnit environments
in BibTeX.

File format change.
This commit is contained in:
Juergen Spitzmueller 2017-02-04 12:02:00 +01:00
parent ad79ac406f
commit 7ca20b292c
14 changed files with 280 additions and 60 deletions

View File

@ -7,6 +7,14 @@ changes happened in particular if possible. A good example would be
-----------------------
2017-02-04 Jürgen Spitzmüller <spitz@lyx.org>
* 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 <spitz@lyx.org>
* Format incremented to 532: literal command inset parameter.
With this, inset command params with ParamInfo::HANDLING_LATEXIFY

View File

@ -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]],

View File

@ -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];

View File

@ -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<string>(use_bibtopic)
<< "\n\\use_indices " << convert<string>(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";

View File

@ -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<std::string, std::string> 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?

View File

@ -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),

View File

@ -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.
*/

View File

@ -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());
}

View File

@ -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();

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>545</width>
<height>481</height>
<width>534</width>
<height>482</height>
</rect>
</property>
<property name="windowTitle">
@ -305,7 +305,7 @@
<string>Select this if you want to split your bibliography into sections</string>
</property>
<property name="text">
<string>Secti&amp;oned bibliography</string>
<string>Subdivided bibli&amp;ography</string>
</property>
</widget>
</item>
@ -334,6 +334,33 @@
</item>
</layout>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="bibunitsLA">
<property name="text">
<string>&amp;Multiple bibliographies:</string>
</property>
<property name="buddy">
<cstring>bibunitsCO</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="bibunitsCO">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Generate a bibliography per defined unit.</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>

View File

@ -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 += "<ul><li>" + (style.empty() ? _("none") : style) + "</li></ul>";
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 += "<br />";
@ -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";

View File

@ -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 ?

View File

@ -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=<val> \multibib <val>
\bibbysection[<opts>] \begin_inset CommandInset bibtex
biblatexopts "<opts>"
btprint "bibbysection"

View File

@ -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