From 68ab4023ccdf26022a053adc1bfaecef9fef58f4 Mon Sep 17 00:00:00 2001 From: Juergen Spitzmueller Date: Sat, 21 Jan 2017 14:25:17 +0100 Subject: [PATCH] Support for "qualified citation lists" These are biblatex-specific multicite commands that allow for multiple pre- and postnotes, as in: \cites(pre)(post)[pre1][post1]{key1}[pre2][post2]{key2}... with an optional general pre- and postnote, which applies to the whole list (like [][] in normal cite commands) and an optional pre- and postnotes for each item, so that pagination can actually be specified in multi-cite references, as in: (cf. Miller 2015, 2; furthermore Smith 2013, 23-23; Jenkins 2012, 103, also refer to chapter 6 in this book) See the biblatex manual, sec. 3.8.3., for details. File format change. --- development/FORMAT | 7 + lib/citeengines/biblatex-natbib.citeengine | 26 ++- lib/citeengines/biblatex.citeengine | 36 ++-- lib/doc/Customization.lyx | 79 ++++++++- lib/doc/UserGuide.lyx | 95 ++++++++++- lib/lyx2lyx/lyx_2_3.py | 105 +++++++++++- src/BiblioInfo.cpp | 7 + src/Citation.h | 18 +- src/TextClass.cpp | 2 + src/frontends/qt4/GuiCitation.cpp | 186 +++++++++++++++++++-- src/frontends/qt4/GuiCitation.h | 18 +- src/frontends/qt4/GuiSelectionManager.cpp | 66 ++++++-- src/frontends/qt4/GuiSelectionManager.h | 20 ++- src/frontends/qt4/Menus.cpp | 21 ++- src/frontends/qt4/ui/CitationUi.ui | 9 +- src/insets/InsetCitation.cpp | 93 +++++++++-- src/insets/InsetCitation.h | 2 + src/tex2lyx/TODO.txt | 15 +- src/version.h | 4 +- 19 files changed, 715 insertions(+), 94 deletions(-) diff --git a/development/FORMAT b/development/FORMAT index 3c4f11553f..3c464f339f 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -7,6 +7,13 @@ changes happened in particular if possible. A good example would be ----------------------- +2017-01-21 Jürgen Spitzmüller + * Format incremented to 531: Support for qualified citation lists. + \begin_inset CommandInset citation + New params: pretextlist, posttextlist + A tab-separated list consisting of a cite key, a space and the the + pre- or postnote associated with that specific key. + 2017-01-13 Jürgen Spitzmüller * Format incremented to 530: Support natbib & jurabib package options. diff --git a/lib/citeengines/biblatex-natbib.citeengine b/lib/citeengines/biblatex-natbib.citeengine index ffcd9e60af..25b76f6a2a 100644 --- a/lib/citeengines/biblatex-natbib.citeengine +++ b/lib/citeengines/biblatex-natbib.citeengine @@ -68,6 +68,8 @@ MaxCiteNames 3 # dropping the '!' from the prefix (see below), e.g.: # _stardesc Starred command label # _stardesctooltip Tooltip for the starred command checkbox. +# * A trailing $ indicates that a command features "qualified citation +# lists" (a specific Biblatex feature) # # CITE COMMAND DEFINITIONS for either engine type @@ -84,8 +86,8 @@ CiteEngine authoryear citeyearpar[][] citeyear=cite* citebyear[][]=citeyear - Footcite[][]=smartcite - Autocite[][] + Footcite$[][]=smartcite + Autocite$[][] citetitle*[][] fullcite[][] footfullcite[][] @@ -97,8 +99,8 @@ CiteEngine numerical Citep|citealp,citealt*[][] Citet|textcite*[][] supercite - Footcite[][]=smartcite - Autocite[][] + Footcite$[][]=smartcite + Autocite$[][] Citeauthor[][]* citeyearpar[][] citeyear|citebyear[][] @@ -177,6 +179,10 @@ CiteFormat default !textbefore {%textbefore%[[%textbefore% ]]} # ", postnote" !textafter {%textafter%[[, %textafter%]]} + # "prenote " (for qualified lists) + !ctextbefore {%curpretext%[[%curpretext% ]]} + # ", postnote" (for qualified lists) + !ctextafter {%curposttext%[[, %curposttext%]]} # Add a year if it exists (else "??") and possibly a modifier (as in 2017a) !makeyear {%year%[[%year%]][[??]]}{%modifier%[[%modifier%]]} # Add a year if it exists (else "??") and indicate a possible modifier (as in 2017[a]) @@ -239,6 +245,8 @@ CiteFormat authoryear !sep ; !close ) + # "cf. Author et. al Year..." + !makecite %!ctextbefore%%!startlink%%!abbrvciteauthor% %!makeyear%%!endlink%%!ctextafter%%!nextcite% # "Author et al. (cf. Year..." !makecitet %!startlink%%!makeauthor%%!endlink% %!open%%!textbefore%%!makeyear%%!nextcitet% # "cf. Author et al. Year..." @@ -246,6 +254,8 @@ CiteFormat authoryear # "Author et al., Year..." !makecitealp %!startlink%%!makeauthor%, %!makeyear%%!endlink%%!nextcitealp% + # "...; Nextauthor Year..." + !nextcite {%next%[[%!sep% %!makecite%]]} # "...), [and] Nextauthor (Year..." !nextcitet {%next%[[%!close%%!smartsep%%!startlink%%!makeauthor%%!endlink% %!open%%!makeyear%%!nextcitet%]]} # "...; NextAuthor et al. Year..." @@ -268,9 +278,9 @@ CiteFormat authoryear # "cf. Author Year; NextAuthor Year, p. xx" [NB: textbefore position differs from real natbib!] citealt %!makecitealt%%!textafter% # "Footnote: cf. Author A Year; Author B Year, p. xx." - footcite {%dialog%[[%_footnote%]][[%_foot%]]}: %!textbefore%%!makecitealp%%!textafter%. + footcite {%dialog%[[%_footnote%]][[%_foot%]]}: %!textbefore%%!makecite%%!textafter%. # "Auto: (cf. Author A Year; Author B Year, p. xx)" - autocite {%dialog%[[%_autocite%]][[%_auto%]]}: %!open%%!textbefore%%!makecitealp%%!textafter%%!close% + autocite {%dialog%[[%_autocite%]][[%_auto%]]}: %!open%%!textbefore%%!makecite%%!textafter%%!close% # Fallback style: "Author A (cf. Year),[ and] Author B (Year, p. xx)" cite %!makecitet%%!textafter%%!close% @@ -298,6 +308,8 @@ CiteFormat numerical !makecitealt {%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%]]}%!nextcitealt% # "ID..." !hashkey {%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%%!nexthashkey%]]} + # "ID" + !makekey %!ctextbefore%{%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%]]}%!ctextafter%%!nextkey% # "...], [and] NextAuthor [ID..." !nextcitet {%next%[[%!close%%!smartsep%%!makeauthor% %!open%%!textbefore%{%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%]]}%!nextcitet%]]} @@ -309,6 +321,8 @@ CiteFormat numerical !nexthashkey {%next%[[%!sep% %!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%%!nexthashkey%]]} # "...); Nextauthor [ID..." !nextcitet {%next%[[%!close%%!smartsep%%!makeauthor% %!open%{%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%]]}%!nextcitet%]]} + # "..., NextID..." + !nextkey {%next%[[%!sep% %!ctextbefore%{%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%]]}%!ctextafter%%!nextkey%]]} # # ACTUAL STYLE DEFINITIONS diff --git a/lib/citeengines/biblatex.citeengine b/lib/citeengines/biblatex.citeengine index 657547e403..c58e2be763 100644 --- a/lib/citeengines/biblatex.citeengine +++ b/lib/citeengines/biblatex.citeengine @@ -64,20 +64,22 @@ MaxCiteNames 3 # the '!' from the prefix (see below), e.g.: # _stardesc Starred command label # _stardesctooltip Tooltip for the starred command checkbox. +# * A trailing $ indicates that a command features "qualified citation +# lists" (a specific Biblatex feature) # # CITE COMMAND DEFINITIONS for either engine type # CiteEngine authoryear - Cite|citealt,citealp[][] - Citet[][]=textcite - Citep[][]=parencite + Cite$|citealt,citealp[][] + Citet$[][]=textcite + Citep$[][]=parencite Citeauthor*[][] citeyearpar[][]=parencite* citeyear[][]=cite* citebyear[][]=citeyear - Footcite[][]=smartcite - Autocite[][] + Footcite$[][]=smartcite + Autocite$[][] citetitle*[][] fullcite[][] footfullcite[][] @@ -86,11 +88,11 @@ CiteEngine authoryear End CiteEngine numerical - cite|parencite,citep,citealt,citealp[][] - Citet[][]=textcite + cite$|parencite,citep,citealt,citealp[][] + Citet$[][]=textcite supercite - Footcite[][]=smartcite - Autocite[][] + Footcite$[][]=smartcite + Autocite$[][] Citeauthor*[][] citeyear|parencite*,citebyear[][]=citeyear* citetitle*[][] @@ -182,6 +184,10 @@ CiteFormat default !textbefore {%textbefore%[[%textbefore% ]]} # ", postnote" !textafter {%textafter%[[, %textafter%]]} + # "prenote " (for qualified lists) + !ctextbefore {%curpretext%[[%curpretext% ]]} + # ", postnote" (for qualified lists) + !ctextafter {%curposttext%[[, %curposttext%]]} # Add a year if it exists (else "??") and possibly a modifier (as in 2017a) !year {%year%[[%year%]][[??]]}{%modifier%[[%modifier%]]} # Add a year if it exists (else "??") and indicate a possible modifier (as in 2017[a]) @@ -228,14 +234,14 @@ CiteFormat authoryear !close ) # "cf. Author et. al Year..." - !makecite %!startlink%%!abbrvciteauthor% %!year%%!endlink%%!nextcite% + !makecite %!ctextbefore%%!startlink%%!abbrvciteauthor% %!year%%!endlink%%!ctextafter%%!nextcite% # Author et al. (cf. Year... - !maketextcite %!startlink%%!abbrvciteauthor%%!endlink% %!open%%!textbefore%%!year%%!nexttextcite% + !maketextcite {%ifqualified%[[%!textbefore%]]}%!startlink%%!abbrvciteauthor%%!endlink% %!open%{%ifqualified%[[%!ctextbefore%]][[%!textbefore%]]}%!year%%!ctextafter%%!nexttextcite% # "...; Nextauthor Year..." !nextcite {%next%[[%!sep% %!makecite%]]} # "...); Nextauthor (Year..." - !nexttextcite {%next%[[%!close%%!smartsep%%!startlink%%!abbrvciteauthor%%!endlink% %!open%%!year%%!nexttextcite%]]} + !nexttextcite {%next%[[%!close%%!smartsep%%!startlink%%!abbrvciteauthor%%!endlink% %!open%%!ctextbefore%%!year%%!ctextafter%%!nexttextcite%]]} # Add a year if it exists (else title, else "??") and possibly a modifier (as in 2017a) !yeartitle {%year%[[%year%{%modifier%[[%modifier%]][[{%export%[[]][[%!dummymod%]]}]]}]][[{%title%[[%title%]][[??]]}]]} @@ -283,12 +289,12 @@ CiteFormat numerical # "Author [cf. ID..." !maketextcite %!abbrvciteauthor% %!open%%!textbefore%{%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%]]}%!nexttextcite% # "ID" - !makekey {%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%%!nextkey%]]} + !makekey %!ctextbefore%{%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%]]}%!ctextafter%%!nextkey% # "...); Nextauthor [ID..." - !nexttextcite {%next%[[%!close%%!smartsep%%!abbrvciteauthor% %!open%{%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%]]}%!nexttextcite%]]} + !nexttextcite {%next%[[%!close%%!smartsep%%!abbrvciteauthor% %!open%%!ctextbefore%{%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%]]}%!nexttextcite%]]} # "..., NextID..." - !nextkey {%next%[[%!sep% %!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%%!nextkey%]]} + !nextkey {%next%[[%!sep% %!ctextbefore%{%dialog%[[#ID]][[%!startlink%{%numericallabel%[[%numericallabel%]][[#%key%]]}%!endlink%]]}%!ctextafter%%!nextkey%]]} # # ACTUAL STYLE DEFINITIONS diff --git a/lib/doc/Customization.lyx b/lib/doc/Customization.lyx index 3ecd1eedc4..a9a23de2d9 100644 --- a/lib/doc/Customization.lyx +++ b/lib/doc/Customization.lyx @@ -1,5 +1,5 @@ #LyX 2.3 created this file. For more info see http://www.lyx.org/ -\lyxformat 528 +\lyxformat 530 \begin_document \begin_header \save_transient_properties true @@ -21598,8 +21598,8 @@ The full syntax is: \begin_layout LyX-Code -\change_inserted -712698321 1483870927 -LyXName|alias*[][]=latexcmd +\change_inserted -712698321 1484997816 +LyXName|alias$*[][]=latexcmd \end_layout \begin_layout Itemize @@ -22048,11 +22048,50 @@ _stardesc Sta&rred command label \begin_layout LyX-Code -\change_inserted -712698321 1483872184 +\change_inserted -712698321 1484997832 _stardesctooltip Tooltip for the starred command checkbox. \end_layout \end_deeper +\begin_layout Itemize + +\change_inserted -712698321 1484997948 +A dollar sign +\begin_inset Flex Code +status collapsed + +\begin_layout Plain Layout + +\change_inserted -712698321 1484997871 +$ +\change_unchanged + +\end_layout + +\end_inset + + indicates that this command features +\begin_inset Quotes eld +\end_inset + +qualified citation lists +\begin_inset Quotes erd +\end_inset + +. + This is a +\family sans +Biblatex +\family default +-specific feature for multi-reference citations where an individual pre- + and postnote can be given to each reference in the list. + Please refer to the +\family sans +Biblatex +\family default + manual for details. +\end_layout + \begin_layout Subsection \begin_inset CommandInset label LatexCommand label @@ -22670,7 +22709,7 @@ status collapsed \begin_layout Itemize -\change_inserted -712698321 1483978548 +\change_inserted -712698321 1484997600 \begin_inset Flex Code status collapsed @@ -22704,6 +22743,36 @@ status collapsed \end_inset ) +\end_layout + +\begin_layout Itemize + +\change_inserted -712698321 1484997681 +\begin_inset Flex Code +status collapsed + +\begin_layout Plain Layout + +\change_inserted -712698321 1484997608 +{%ifqualified%[[true]][[false]]} +\end_layout + +\end_inset + +: process the +\begin_inset Quotes eld +\end_inset + +true +\begin_inset Quotes erd +\end_inset + + part if the current citation is a qualified citation list (a specific +\family sans +Biblatex +\family default + format for multi-reference citations), the false part if this is not the + case. \change_unchanged \end_layout diff --git a/lib/doc/UserGuide.lyx b/lib/doc/UserGuide.lyx index d1fd4e8de7..518db5db54 100644 --- a/lib/doc/UserGuide.lyx +++ b/lib/doc/UserGuide.lyx @@ -30040,7 +30040,7 @@ key "latexcompanion" \begin_layout Standard -\change_inserted -712698321 1483891777 +\change_inserted -712698321 1484998090 All styles except for \family sans Basic @@ -30056,6 +30056,99 @@ cf. ). This text is then also included in the parentheses, if the style requires this. +\end_layout + +\begin_layout Standard + +\change_inserted -712698321 1484998495 +Note that these pre- and postnotes apply to the whole citation. + That is to say, if you refer to multiple references at one, the prenote + will precede the first citation in the list, the postnote will follow the + last. + Some +\family sans +Biblatex +\family default + styles allow for adding pre- and postnotes to any individual reference + in a multi-citation (so-called +\begin_inset Quotes eld +\end_inset + +qualified citation lists +\begin_inset Quotes erd +\end_inset + +). + \SpecialChar LyX + supports this. + If you use such a style, and if the current reference includes multiple + items, the +\begin_inset Quotes eld +\end_inset + +Selected Citations +\begin_inset Quotes erd +\end_inset + + window will display three columns: +\begin_inset Quotes eld +\end_inset + +Text before +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +Cite key +\begin_inset Quotes erd +\end_inset + +, and +\begin_inset Quotes eld +\end_inset + +Text after +\begin_inset Quotes erd +\end_inset + +. + If you double-click on an item's +\begin_inset Quotes eld +\end_inset + +Text before +\begin_inset Quotes erd +\end_inset + + or +\begin_inset Quotes eld +\end_inset + +Text after +\begin_inset Quotes erd +\end_inset + + field, you can add such individual pre- and postnotes. + In the +\begin_inset Quotes eld +\end_inset + +General text before +\begin_inset Quotes erd +\end_inset + + and +\begin_inset Quotes eld +\end_inset + +General text after +\begin_inset Quotes erd +\end_inset + + input widgets, you can add pre- and postnotes that apply to the whole list. \change_unchanged \end_layout diff --git a/lib/lyx2lyx/lyx_2_3.py b/lib/lyx2lyx/lyx_2_3.py index 5b647db03a..044bc755f1 100644 --- a/lib/lyx2lyx/lyx_2_3.py +++ b/lib/lyx2lyx/lyx_2_3.py @@ -1331,10 +1331,10 @@ def revert_biblatex(document): res = "\\" + new_citations[cmd] if pre: res += "[" + pre + "]" - elif post: - res += "[]" if post: res += "[" + post + "]" + elif pre: + res += "[]" res += "{" + key + "}" document.body[i:j+1] = put_cmd_in_ert([res]) elif cmd not in old_citations: @@ -1450,6 +1450,103 @@ def revert_bibpackopts(document): ] +def revert_qualicites(document): + " Revert qualified citation list commands to ERT " + + # Citation insets that support qualified lists, with their LaTeX code + ql_citations = { + "cite" : "cites", + "Cite" : "Cites", + "citet" : "textcites", + "Citet" : "Textcites", + "citep" : "parencites", + "Citep" : "Parencites", + "Footcite" : "Smartcites", + "footcite" : "smartcites", + "Autocite" : "Autocites", + "autocite" : "autocites", + } + + # 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) + + biblatex = engine in ["biblatex", "biblatex-natbib"] + + i = 0 + while (True): + i = find_token(document.body, "\\begin_inset CommandInset citation", i) + if i == -1: + break + j = find_end_of_inset(document.body, i) + if j == -1: + document.warning("Can't find end of citation inset at line %d!!" %(i)) + i += 1 + continue + pres = find_token(document.body, "pretextlist", i, j) + posts = find_token(document.body, "posttextlist", i, j) + if pres == -1 and posts == -1: + # nothing to do. + i = j + 1 + continue + pretexts = get_quoted_value(document.body, "pretextlist", pres) + posttexts = get_quoted_value(document.body, "posttextlist", posts) + k = find_token(document.body, "LatexCommand", i, j) + if k == -1: + document.warning("Can't find LatexCommand for citation inset at line %d!" %(i)) + i = j + 1 + continue + cmd = get_value(document.body, "LatexCommand", k) + if biblatex and cmd in list(ql_citations.keys()): + pre = get_quoted_value(document.body, "before", i, j) + post = get_quoted_value(document.body, "after", i, j) + key = get_quoted_value(document.body, "key", i, j) + if not key: + document.warning("Citation inset at line %d does not have a key!" %(i)) + key = "???" + keys = key.split(",") + prelist = pretexts.split("\t") + premap = dict() + for pp in prelist: + ppp = pp.split(" ", 1) + premap[ppp[0]] = ppp[1] + postlist = posttexts.split("\t") + postmap = dict() + for pp in postlist: + ppp = pp.split(" ", 1) + postmap[ppp[0]] = ppp[1] + # Replace known new commands with ERT + if "(" in pre or ")" in pre: + pre = "{" + pre + "}" + if "(" in post or ")" in post: + post = "{" + post + "}" + res = "\\" + ql_citations[cmd] + if pre: + res += "(" + pre + ")" + if post: + res += "(" + post + ")" + elif pre: + res += "()" + for kk in keys: + if premap.get(kk, "") != "": + res += "[" + premap[kk] + "]" + if postmap.get(kk, "") != "": + res += "[" + postmap[kk] + "]" + elif premap.get(kk, "") != "": + res += "[]" + res += "{" + kk + "}" + document.body[i:j+1] = put_cmd_in_ert([res]) + else: + # just remove the params + del document.body[posttexts] + del document.body[pretexts] + i += 1 + + ## # Conversion hub # @@ -1477,10 +1574,12 @@ convert = [ [527, []], [528, []], [529, []], - [530, []] + [530, []], + [531, []] ] revert = [ + [530, [revert_qualicites]], [529, [revert_bibpackopts]], [528, [revert_citekeyonly]], [527, [revert_biblatex]], diff --git a/src/BiblioInfo.cpp b/src/BiblioInfo.cpp index ab06f37bf8..4220fe0021 100644 --- a/src/BiblioInfo.cpp +++ b/src/BiblioInfo.cpp @@ -36,6 +36,7 @@ #include "support/regex.h" #include "support/textutils.h" +#include #include using namespace std; @@ -812,6 +813,8 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, ret = from_ascii("x"); // any non-empty string will do else if (key == "ifstar" && ci.Starred) ret = from_ascii("x"); // any non-empty string will do + else if (key == "ifqualified" && ci.isQualified) + ret = from_ascii("x"); // any non-empty string will do else if (key == "entrytype") ret = entry_type_; else if (prefixIs(key, "ifentrytype:") @@ -908,6 +911,10 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, ret = ci.textBefore; else if (key == "textafter") ret = ci.textAfter; + else if (key == "curpretext") + ret = ci.getPretexts()[bib_key_]; + else if (key == "curposttext") + ret = ci.getPosttexts()[bib_key_]; else if (key == "year") ret = getYear(); } diff --git a/src/Citation.h b/src/Citation.h index c48e3c432b..600ba6ca97 100644 --- a/src/Citation.h +++ b/src/Citation.h @@ -13,6 +13,7 @@ #define CITATION_H #include "support/docstring.h" +#include #include namespace lyx { @@ -32,7 +33,8 @@ class CitationStyle public: /// CitationStyle() : name("cite"), cmd("cite"), forceUpperCase(false), - hasStarredVersion(false), textAfter(false), textBefore(false) {} + hasStarredVersion(false), hasQualifiedList(false), + textAfter(false), textBefore(false) {} /// the LyX name std::string name; @@ -46,6 +48,8 @@ public: bool forceUpperCase; /// starred version (full author list by default) bool hasStarredVersion; + /// allows for qualified citation lists (a Biblatex feature) + bool hasQualifiedList; /// supports text after the citation bool textAfter; /// supports text before the citation @@ -67,7 +71,7 @@ public: Export }; /// - CiteItem() : forceUpperCase(false), Starred(false), + CiteItem() : forceUpperCase(false), Starred(false), isQualified(false), context(CiteItem::Everywhere), textAfter(docstring()), textBefore(docstring()), max_size(128), max_key_size(128), richtext(false) {} @@ -75,12 +79,22 @@ public: bool forceUpperCase; /// is starred version (full author list by default) bool Starred; + /// is a real qualified list + bool isQualified; /// where this to be displayed? CiteItem::CiteContext context; /// text after the citation docstring textAfter; /// text before the citation docstring textBefore; + /// Qualified lists's pre texts + std::map pretexts; + /// + std::map getPretexts() const { return pretexts; } + /// Qualified lists's post texts + std::map posttexts; + /// + std::map getPosttexts() const { return posttexts; } /// the maximum display size as a label size_t max_size; /// the maximum size of the processed keys diff --git a/src/TextClass.cpp b/src/TextClass.cpp index 9b25a67b18..34b55fa97f 100644 --- a/src/TextClass.cpp +++ b/src/TextClass.cpp @@ -1097,6 +1097,8 @@ bool TextClass::readCiteEngine(Lexer & lexrc) latex_cmd += ichar; else if (mode == StarDesc) stardesc += ichar; + else if (ichar == '$') + cs.hasQualifiedList = true; else if (ichar == '*') cs.hasStarredVersion = true; else if (ichar == '[' && cs.textAfter) diff --git a/src/frontends/qt4/GuiCitation.cpp b/src/frontends/qt4/GuiCitation.cpp index d73f3d3542..2178dd1101 100644 --- a/src/frontends/qt4/GuiCitation.cpp +++ b/src/frontends/qt4/GuiCitation.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -138,7 +139,7 @@ GuiCitation::GuiCitation(GuiView & lv) this, SLOT(on_okPB_clicked())); selectionManager = new GuiSelectionManager(availableLV, selectedLV, - addPB, deletePB, upPB, downPB, &available_model_, &selected_model_); + addPB, deletePB, upPB, downPB, &available_model_, &selected_model_, 1); connect(selectionManager, SIGNAL(selectionChanged()), this, SLOT(setCitedKeys())); connect(selectionManager, SIGNAL(updateHook()), @@ -159,6 +160,12 @@ GuiCitation::GuiCitation(GuiView & lv) connect(instant_, SIGNAL(triggered(bool)), this, SLOT(instantChanged(bool))); +#if (QT_VERSION < 0x050000) + selectedLV->horizontalHeader()->setResizeMode(QHeaderView::Stretch); +#else + selectedLV->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); +#endif + setFocusProxy(filter_); } @@ -239,7 +246,7 @@ void GuiCitation::updateControls() // will not have changed. void GuiCitation::updateControls(BiblioInfo const & bi) { - QModelIndex idx = selectionManager->getSelectedIndex(); + QModelIndex idx = selectionManager->getSelectedIndex(1); updateInfo(bi, idx); selectionManager->update(); } @@ -254,8 +261,31 @@ void GuiCitation::updateFormatting(CitationStyle currentStyle) bool const textbefore = currentStyle.textBefore; bool const textafter = currentStyle.textAfter; - bool const haveSelection = - selectedLV->model()->rowCount() > 0; + int const rows = selectedLV->model()->rowCount(); + + bool const qualified = currentStyle.hasQualifiedList + && (rows > 1 + || !params_["pretextlist"].empty() + || !params_["posttextlist"].empty()); + selectedLV->horizontalHeader()->setVisible(qualified); + selectedLV->setColumnHidden(0, !qualified); + selectedLV->setColumnHidden(2, !qualified); + if (qualified) { + textBeforeLA->setText(qt_("General text befo&re:")); + textAfterLA->setText(qt_("General &text after:")); + textBeforeED->setToolTip(qt_("Text that precedes the whole reference list. " + "For text that precedes individual items, double-click on the respective entry above.")); + textAfterLA->setToolTip(qt_("General &text after:")); + textAfterED->setToolTip(qt_("Text that follows the whole reference list. " + "For text that follows individual items, double-click on the respective entry above.")); + } else { + textBeforeLA->setText(qt_("Text befo&re:")); + textBeforeED->setToolTip(qt_("Text that precedes the reference (e.g., \"cf.\")")); + textAfterLA->setText(qt_("&Text after:")); + textAfterED->setToolTip(qt_("Text that follows the reference (e.g., pages)")); + } + + bool const haveSelection = rows > 0; forceuppercaseCB->setEnabled(force && haveSelection); starredCB->setEnabled(full && haveSelection); @@ -306,7 +336,7 @@ void GuiCitation::updateStyles() // Update the styles for the style combo, citationStyleCO. void GuiCitation::updateStyles(BiblioInfo const & bi) { - QStringList selected_keys = selected_model_.stringList(); + QStringList selected_keys = selectedKeys(); int curr = selectedLV->model()->rowCount() - 1; if (curr < 0 || selected_keys.empty()) { @@ -376,7 +406,7 @@ void GuiCitation::fillEntries(BiblioInfo const & bi) bool GuiCitation::isSelected(QModelIndex const & idx) { QString const str = idx.data().toString(); - return selected_model_.stringList().contains(str); + return selectedKeys().contains(str); } @@ -551,6 +581,10 @@ void GuiCitation::applyParams(int const choice, bool full, bool force, params_["key"] = qstring_to_ucs4(cited_keys_.join(",")); params_["before"] = qstring_to_ucs4(before); params_["after"] = qstring_to_ucs4(after); + if (cs.hasQualifiedList) { + params_["pretextlist"] = getStringFromVector(getPreTexts(), from_ascii("\t")); + params_["posttextlist"] = getStringFromVector(getPostTexts(), from_ascii("\t")); + } dispatchParams(); } @@ -558,7 +592,107 @@ void GuiCitation::applyParams(int const choice, bool full, bool force, void GuiCitation::clearSelection() { cited_keys_.clear(); - selected_model_.setStringList(cited_keys_); + setSelectedKeys(cited_keys_); +} + + +void GuiCitation::setSelectedKeys(QStringList const sl) +{ + selected_model_.clear(); + selected_model_.setColumnCount(3); + QStringList headers; + headers << qt_("Text before") + << qt_("Cite key") + << qt_("Text after"); + selected_model_.setHorizontalHeaderLabels(headers); + selectedLV->setColumnHidden(0, true); + selectedLV->setColumnHidden(2, true); + selectedLV->verticalHeader()->setVisible(false); + selectedLV->horizontalHeader()->setVisible(false); + QStringList::const_iterator it = sl.begin(); + QStringList::const_iterator end = sl.end(); + for (int i = 0; it != end; ++it, ++i) { + QStandardItem * si = new QStandardItem(); + si->setData(*it); + si->setText(*it); + si->setToolTip(*it); + si->setEditable(false); + selected_model_.setItem(i, 1, si); + } +} + + +QStringList GuiCitation::selectedKeys() +{ + QStringList res; + for (int i = 0; i != selected_model_.rowCount(); ++i) { + QStandardItem const * item = selected_model_.item(i, 1); + if (item) + res.append(item->text()); + } + return res; +} + + +void GuiCitation::setPreTexts(vector const m) +{ + for (docstring const & s: m) { + QStandardItem * si = new QStandardItem(); + docstring key; + docstring pre = split(s, key, ' '); + si->setData(toqstr(pre)); + si->setText(toqstr(pre)); + QModelIndexList qmil = + selected_model_.match(selected_model_.index(0, 1), + Qt::DisplayRole, toqstr(key), 1, + Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap)); + if (!qmil.empty()) + selected_model_.setItem(qmil.front().row(), 0, si); + } +} + + +vector GuiCitation::getPreTexts() +{ + vector res; + for (int i = 0; i != selected_model_.rowCount(); ++i) { + QStandardItem const * key = selected_model_.item(i, 1); + QStandardItem const * pre = selected_model_.item(i, 0); + if (key && pre && !key->text().isEmpty() && !pre->text().isEmpty()) + res.push_back(qstring_to_ucs4(key->text()) + " " + qstring_to_ucs4(pre->text())); + } + return res; +} + + +void GuiCitation::setPostTexts(vector const m) +{ + for (docstring const & s: m) { + QStandardItem * si = new QStandardItem(); + docstring key; + docstring post = split(s, key, ' '); + si->setData(toqstr(post)); + si->setText(toqstr(post)); + QModelIndexList qmil = + selected_model_.match(selected_model_.index(0, 1), + Qt::DisplayRole, toqstr(key), 1, + Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap)); + if (!qmil.empty()) + selected_model_.setItem(qmil.front().row(), 2, si); + } +} + + +vector GuiCitation::getPostTexts() +{ + vector res; + for (int i = 0; i != selected_model_.rowCount(); ++i) { + QStandardItem const * key = selected_model_.item(i, 1); + QStandardItem const * post = selected_model_.item(i, 2); + if (key && post) + res.push_back(qstring_to_ucs4(key->text()) + " " + qstring_to_ucs4(post->text())); + } + return res; } @@ -567,6 +701,7 @@ void GuiCitation::init() // Make the list of all available bibliography keys BiblioInfo const & bi = bibInfo(); all_keys_ = to_qstring_list(bi.getKeys()); + available_model_.setStringList(all_keys_); // Ditto for the keys cited in this inset @@ -575,7 +710,7 @@ void GuiCitation::init() cited_keys_.clear(); else cited_keys_ = str.split(","); - selected_model_.setStringList(cited_keys_); + setSelectedKeys(cited_keys_); // Initialize the drop downs fillEntries(bi); @@ -585,21 +720,23 @@ void GuiCitation::init() string const & cmd = params_.getCmdName(); CitationStyle const cs = citationStyleFromString(cmd, documentBuffer().params()); + forceuppercaseCB->setChecked(cs.forceUpperCase); starredCB->setChecked(cs.hasStarredVersion && documentBuffer().params().fullAuthorList()); textBeforeED->setText(toqstr(params_["before"])); textAfterED->setText(toqstr(params_["after"])); + setPreTexts(getVectorFromString(params_["pretextlist"], from_ascii("\t"))); + setPostTexts(getVectorFromString(params_["posttextlist"], from_ascii("\t"))); + // Update the interface updateControls(bi); updateStyles(bi); if (selected_model_.rowCount()) { selectedLV->blockSignals(true); selectedLV->setFocus(); - QModelIndex idx = selected_model_.index(0, 0); - selectedLV->selectionModel()->select(idx, - QItemSelectionModel::ClearAndSelect); + selectedLV->selectRow(0); selectedLV->blockSignals(false); // Find the citation style @@ -682,6 +819,28 @@ QStringList GuiCitation::citationStyles(BiblioInfo const & bi, size_t max_size) { vector const keys = to_docstring_vector(cited_keys_); vector styles = citeStyles_; + int ind = citationStyleCO->currentIndex(); + if (ind == -1) + ind = 0; + CitationStyle cs = styles[ind]; + vector pretexts = getPreTexts(); + vector posttexts = getPostTexts(); + bool const qualified = cs.hasQualifiedList + && (selectedLV->model()->rowCount() > 1 + || !pretexts.empty() + || !posttexts.empty()); + std::map pres; + for (docstring const & s: pretexts) { + docstring key; + docstring val = split(s, key, ' '); + pres[key] = val; + } + std::map posts; + for (docstring const & s: posttexts) { + docstring key; + docstring val = split(s, key, ' '); + posts[key] = val; + } CiteItem ci; ci.textBefore = qstring_to_ucs4(textBeforeED->text()); ci.textAfter = qstring_to_ucs4(textAfterED->text()); @@ -689,6 +848,9 @@ QStringList GuiCitation::citationStyles(BiblioInfo const & bi, size_t max_size) ci.Starred = starredCB->isChecked(); ci.context = CiteItem::Dialog; ci.max_size = max_size; + ci.isQualified = qualified; + ci.pretexts = pres; + ci.posttexts = posts; vector ret = bi.getCiteStrings(keys, styles, documentBuffer(), ci); return to_qstring_list(ret); } @@ -696,7 +858,7 @@ QStringList GuiCitation::citationStyles(BiblioInfo const & bi, size_t max_size) void GuiCitation::setCitedKeys() { - cited_keys_ = selected_model_.stringList(); + cited_keys_ = selectedKeys(); updateStyles(); } diff --git a/src/frontends/qt4/GuiCitation.h b/src/frontends/qt4/GuiCitation.h index 035b2d2e2a..2fa4369105 100644 --- a/src/frontends/qt4/GuiCitation.h +++ b/src/frontends/qt4/GuiCitation.h @@ -23,6 +23,8 @@ #include "Citation.h" +#include +#include #include #include @@ -34,6 +36,7 @@ namespace frontend { class GuiSelectionManager; + class GuiCitation : public DialogView, public Ui::CitationUi { Q_OBJECT @@ -114,6 +117,19 @@ private: /// Clear selected keys void clearSelection(); + /// Set selected keys + void setSelectedKeys(QStringList const); + /// Get selected keys + QStringList selectedKeys(); + /// Set pre texts of qualified lists + void setPreTexts(std::vector const m); + /// Get pre texts of qualified lists + std::vector getPreTexts(); + /// Set post texts of qualified lists + void setPostTexts(std::vector const m); + /// Get post texts of qualified lists + std::vector getPostTexts(); + /// Find keys containing a string. void findKey( BiblioInfo const & bi, //< optimize by passing this @@ -171,7 +187,7 @@ private: /// available keys. QStringListModel available_model_; /// selected keys. - QStringListModel selected_model_; + QStandardItemModel selected_model_; /// All keys. QStringList all_keys_; /// Cited keys. diff --git a/src/frontends/qt4/GuiSelectionManager.cpp b/src/frontends/qt4/GuiSelectionManager.cpp index d3d2b01f41..b90d0ea135 100644 --- a/src/frontends/qt4/GuiSelectionManager.cpp +++ b/src/frontends/qt4/GuiSelectionManager.cpp @@ -18,6 +18,7 @@ #include "support/debug.h" +#include #include #include #include @@ -42,21 +43,24 @@ namespace frontend { GuiSelectionManager::GuiSelectionManager( QAbstractItemView * avail, - QListView * sel, + QAbstractItemView * sel, QPushButton * add, QPushButton * del, QPushButton * up, QPushButton * down, QAbstractListModel * amod, - QAbstractListModel * smod) + QAbstractItemModel * smod, + int const main_sel_col) : availableLV(avail), selectedLV(sel), addPB(add), deletePB(del), upPB(up), downPB(down), availableModel(amod), selectedModel(smod), - selectedHasFocus_(false) + selectedHasFocus_(false), main_sel_col_(main_sel_col) { selectedLV->setModel(smod); availableLV->setModel(amod); - + selectedLV->setSelectionBehavior(QAbstractItemView::SelectRows); + selectedLV->setSelectionMode(QAbstractItemView::SingleSelection); + connect(availableLV->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(availableChanged(QModelIndex, QModelIndex))); @@ -69,6 +73,8 @@ GuiSelectionManager::GuiSelectionManager( connect(selectedLV->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(selectedChanged(QItemSelection, QItemSelection))); + connect(selectedLV->itemDelegate(), SIGNAL(commitData(QWidget*)), + this, SLOT(selectedEdited())); connect(addPB, SIGNAL(clicked()), this, SLOT(addPB_clicked())); connect(deletePB, SIGNAL(clicked()), @@ -94,10 +100,10 @@ void GuiSelectionManager::update() } -QModelIndex GuiSelectionManager::getSelectedIndex() const +QModelIndex GuiSelectionManager::getSelectedIndex(int const c) const { QModelIndexList avail = availableLV->selectionModel()->selectedIndexes(); - QModelIndexList sel = selectedLV->selectionModel()->selectedIndexes(); + QModelIndexList sel = selectedLV->selectionModel()->selectedRows(c); bool const have_avl = !avail.isEmpty(); bool const have_sel = !sel.isEmpty(); @@ -137,7 +143,7 @@ void GuiSelectionManager::updateDelPB() } QModelIndexList const selSels = selectedLV->selectionModel()->selectedIndexes(); - int const sel_nr = selSels.empty() ? -1 : selSels.first().row(); + int const sel_nr = selSels.empty() ? -1 : selSels.first().row(); deletePB->setEnabled(sel_nr >= 0); } @@ -151,7 +157,7 @@ void GuiSelectionManager::updateUpPB() } QModelIndexList const selSels = selectedLV->selectionModel()->selectedIndexes(); - int const sel_nr = selSels.empty() ? -1 : selSels.first().row(); + int const sel_nr = selSels.empty() ? -1 : selSels.first().row(); upPB->setEnabled(sel_nr > 0); } @@ -165,7 +171,7 @@ void GuiSelectionManager::updateDownPB() } QModelIndexList const selSels = selectedLV->selectionModel()->selectedIndexes(); - int const sel_nr = selSels.empty() ? -1 : selSels.first().row(); + int const sel_nr = selSels.empty() ? -1 : selSels.first().row(); downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1); } @@ -176,7 +182,7 @@ bool GuiSelectionManager::isSelected(const QModelIndex & idx) return false; QVariant const & str = availableModel->data(idx, Qt::DisplayRole); QModelIndexList qmil = - selectedModel->match(selectedModel->index(0), + selectedModel->match(selectedModel->index(0, main_sel_col_), Qt::DisplayRole, str, 1, Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap)); return !qmil.empty(); @@ -221,6 +227,12 @@ void GuiSelectionManager::selectedChanged(const QModelIndex & idx, const QModelI } +void GuiSelectionManager::selectedEdited() +{ + selectionChanged(); +} + + bool GuiSelectionManager::insertRowToSelected(int i, QMap const & itemData) { @@ -230,7 +242,23 @@ bool GuiSelectionManager::insertRowToSelected(int i, i = selectedModel->rowCount(); if (!selectedModel->insertRow(i)) return false; - return selectedModel->setItemData(selectedModel->index(i), itemData); + return selectedModel->setItemData(selectedModel->index(i, main_sel_col_), itemData); +} + + +bool GuiSelectionManager::insertRowToSelected(int i, QMap> & qms) +{ + if (i <= -1) + i = 0; + if (i > selectedModel->rowCount()) + i = selectedModel->rowCount(); + if (!selectedModel->insertRow(i)) + return false; + bool res = true; + QMap>::const_iterator it = qms.constBegin(); + for (; it != qms.constEnd(); ++it) + res &= selectedModel->setItemData(selectedModel->index(i, it.key()), it.value()); + return res; } @@ -291,11 +319,14 @@ void GuiSelectionManager::upPB_clicked() int const pos = idx.row(); if (pos <= 0) return; - - QMap qm = selectedModel->itemData(idx); + + QMap> qms; + QList::const_iterator it = selIdx.constBegin(); + for (; it != selIdx.constEnd(); ++it) + qms[it->column()] = selectedModel->itemData(*it); selectedModel->removeRow(pos); - insertRowToSelected(pos - 1, qm); + insertRowToSelected(pos - 1, qms); selectionChanged(); //signal @@ -317,10 +348,13 @@ void GuiSelectionManager::downPB_clicked() if (pos >= selectedModel->rowCount() - 1) return; - QMap qm = selectedModel->itemData(idx); + QMap> qms; + QList::const_iterator it = selIdx.constBegin(); + for (; it != selIdx.constEnd(); ++it) + qms[it->column()] = selectedModel->itemData(*it); selectedModel->removeRow(pos); - insertRowToSelected(pos + 1, qm); + insertRowToSelected(pos + 1, qms); selectionChanged(); //signal diff --git a/src/frontends/qt4/GuiSelectionManager.h b/src/frontends/qt4/GuiSelectionManager.h index 4b83e23e44..dae3f90d8d 100644 --- a/src/frontends/qt4/GuiSelectionManager.h +++ b/src/frontends/qt4/GuiSelectionManager.h @@ -14,6 +14,7 @@ #include +class QAbstractItemModel; class QAbstractListModel; class QModelIndex; class QListView; @@ -42,13 +43,14 @@ public: /// GuiSelectionManager( QAbstractItemView * availableLV, - QListView * selectedLV, + QAbstractItemView * selectedLV, QPushButton * addPB, QPushButton * delPB, QPushButton * upPB, QPushButton * downPB, QAbstractListModel * availableModel, - QAbstractListModel * selectedModel); + QAbstractItemModel * selectedModel, + int const main_sel_col = 0); /// Sets the state of the various push buttons, depending upon the /// state of the widgets. (E.g., "delete" is enabled only if the /// selection is non-empty.) @@ -64,7 +66,7 @@ public: bool selectedFocused() const { return selectedHasFocus_; } /// Returns the selected index. Note that this will depend upon /// selectedFocused(). - QModelIndex getSelectedIndex() const; + QModelIndex getSelectedIndex(int const c = 0) const; Q_SIGNALS: /// Emitted when the list of selected items has changed. @@ -87,11 +89,13 @@ protected: /// been selected (i.e., is also in selectedLV). bool isSelected(const QModelIndex & idx); /// - bool insertRowToSelected(int i, QMap const & itemData); + bool insertRowToSelected(int i, QMap const & itemData); + /// + bool insertRowToSelected(int i, QMap> &); /// QAbstractItemView * availableLV; /// - QListView * selectedLV; + QAbstractItemView * selectedLV; /// QPushButton * addPB; /// @@ -103,7 +107,7 @@ protected: /// QAbstractListModel * availableModel; /// - QAbstractListModel * selectedModel; + QAbstractItemModel * selectedModel; protected Q_SLOTS: /// @@ -115,6 +119,8 @@ protected Q_SLOTS: /// void selectedChanged(QItemSelection const & qis, QItemSelection const &); /// + void selectedEdited(); + /// virtual void addPB_clicked(); /// virtual void deletePB_clicked(); @@ -138,6 +144,8 @@ private: virtual void updateUpPB(); /// bool selectedHasFocus_; + /// + int main_sel_col_; }; } // namespace frontend diff --git a/src/frontends/qt4/Menus.cpp b/src/frontends/qt4/Menus.cpp index c8682bb081..535ee31d86 100644 --- a/src/frontends/qt4/Menus.cpp +++ b/src/frontends/qt4/Menus.cpp @@ -1540,7 +1540,7 @@ void MenuDefinition::expandCiteStyles(BufferView const * bv) static_cast(inset); Buffer const * buf = &bv->buffer(); - BufferParams const & bp = buf->params(); + BufferParams const & bp = buf->masterParams(); string const cmd = citinset->params().getCmdName(); docstring const & key = citinset->getParam("key"); @@ -1557,7 +1557,18 @@ void MenuDefinition::expandCiteStyles(BufferView const * bv) vector const keys = getVectorFromString(key); - vector const citeStyleList = buf->params().citeStyles(); + vector const citeStyleList = bp.citeStyles(); + + CitationStyle cs = citinset->getCitationStyle(bp, cmd, citeStyleList); + bool const qualified = cs.hasQualifiedList + && (keys.size() > 1 + || !citinset->getParam("pretextlist").empty() + || !citinset->getParam("posttextlist").empty()); + std::map pres = + citinset->getQualifiedLists(citinset->getParam("pretextlist")); + std::map posts = + citinset->getQualifiedLists(citinset->getParam("posttextlist")); + CiteItem ci; ci.textBefore = citinset->getParam("before"); ci.textAfter = citinset->getParam("after"); @@ -1565,6 +1576,9 @@ void MenuDefinition::expandCiteStyles(BufferView const * bv) ci.Starred = star; ci.context = CiteItem::Dialog; ci.max_size = 40; + ci.isQualified = qualified; + ci.pretexts = pres; + ci.posttexts = posts; vector citeStrings = buf->masterBibInfo().getCiteStrings(keys, citeStyleList, bv->buffer(), ci); @@ -1581,9 +1595,6 @@ void MenuDefinition::expandCiteStyles(BufferView const * bv) "changetype " + from_utf8(citationStyleToString(cs))))); } - // Extra features of the citation styles - CitationStyle cs = citinset->getCitationStyle(bp, cmd, citeStyleList); - if (cs.hasStarredVersion) { docstring starred = _("All authors|h"); // Check if we have a custom string/tooltip for the starred version diff --git a/src/frontends/qt4/ui/CitationUi.ui b/src/frontends/qt4/ui/CitationUi.ui index a824212926..6b43968a1a 100644 --- a/src/frontends/qt4/ui/CitationUi.ui +++ b/src/frontends/qt4/ui/CitationUi.ui @@ -234,11 +234,7 @@ - - - QAbstractItemView::NoEditTriggers - - + @@ -290,7 +286,7 @@ - Text &before: + Text befo&re: textBeforeED @@ -483,7 +479,6 @@ entriesCO searchOptionsPB availableLV - selectedLV addPB deletePB upPB diff --git a/src/insets/InsetCitation.cpp b/src/insets/InsetCitation.cpp index 0a5da5b119..9004c9819a 100644 --- a/src/insets/InsetCitation.cpp +++ b/src/insets/InsetCitation.cpp @@ -69,6 +69,8 @@ ParamInfo const & InsetCitation::findInfo(string const & /* cmdName */) param_info_.add("after", ParamInfo::LATEX_OPTIONAL); param_info_.add("before", ParamInfo::LATEX_OPTIONAL); param_info_.add("key", ParamInfo::LATEX_REQUIRED); + param_info_.add("pretextlist", ParamInfo::LATEX_OPTIONAL); + param_info_.add("posttextlist", ParamInfo::LATEX_OPTIONAL); } return param_info_; } @@ -312,6 +314,20 @@ inline docstring wrapCitation(docstring const & key, } // anonymous namespace + +map InsetCitation::getQualifiedLists(docstring const p) const +{ + vector ps = + getVectorFromString(p, from_ascii("\t")); + std::map res; + for (docstring const & s: ps) { + docstring key; + docstring val = split(s, key, ' '); + res[key] = val; + } + return res; +} + docstring InsetCitation::generateLabel(bool for_xhtml) const { docstring label; @@ -360,12 +376,24 @@ docstring InsetCitation::complexLabel(bool for_xhtml) const */ docstring label; vector keys = getVectorFromString(key); + CitationStyle cs = getCitationStyle(buffer().masterParams(), + cite_type, buffer().masterParams().citeStyles()); + bool const qualified = cs.hasQualifiedList + && (keys.size() > 1 + || !getParam("pretextlist").empty() + || !getParam("posttextlist").empty()); + map pres = getQualifiedLists(getParam("pretextlist")); + map posts = getQualifiedLists(getParam("posttextlist")); + CiteItem ci; ci.textBefore = getParam("before"); ci.textAfter = getParam("after"); ci.forceUpperCase = uppercase; ci.Starred = starred; ci.max_size = UINT_MAX; + ci.isQualified = qualified; + ci.pretexts = pres; + ci.posttexts = posts; if (for_xhtml) { ci.max_key_size = UINT_MAX; ci.context = CiteItem::Export; @@ -509,13 +537,15 @@ void InsetCitation::forOutliner(docstring & os, size_t const, bool const) const void InsetCitation::latex(otexstream & os, OutputParams const & runparams) const { BiblioInfo const & bi = buffer().masterBibInfo(); + docstring const key = getParam("key"); + // "keyonly" command: output the plain key and stop. if (getCmdName() == "keyonly") { // Special command to only return the key if (!bi.isBibtex(getParam("key"))) // escape chars with bibitems - os << escape(cleanupWhitespace(getParam("key"))); + os << escape(cleanupWhitespace(key)); else - os << cleanupWhitespace(getParam("key")); + os << cleanupWhitespace(key); return; } vector citation_styles = buffer().masterParams().citeStyles(); @@ -524,23 +554,62 @@ void InsetCitation::latex(otexstream & os, OutputParams const & runparams) const // FIXME UNICODE docstring const cite_str = from_utf8(citationStyleToString(cs, true)); + // check if we have to do a qualified list + vector keys = getVectorFromString(cleanupWhitespace(key)); + bool const qualified = cs.hasQualifiedList + && (!getParam("pretextlist").empty() + || !getParam("posttextlist").empty()); + if (runparams.inulemcmd > 0) os << "\\mbox{"; os << "\\" << cite_str; - docstring const & before = getParam("before"); - docstring const & after = getParam("after"); - if (!before.empty() && cs.textBefore) - os << '[' << before << "][" << after << ']'; - else if (!after.empty() && cs.textAfter) - os << '[' << after << ']'; + if (qualified) + os << "s"; - if (!bi.isBibtex(getParam("key"))) + docstring before = getParam("before"); + docstring after = getParam("after"); + if (!before.empty() && cs.textBefore) { + if (qualified) { + if (contains(before, '(') || contains(before, ')')) + // protect parens + before = '{' + before + '}'; + if (contains(after, '(') || contains(after, ')')) + // protect parens + after = '{' + after + '}'; + os << '(' << before << ")(" << after << ')'; + } else + os << '[' << before << "][" << after << ']'; + } else if (!after.empty() && cs.textAfter) { + if (qualified) { + if (contains(after, '(') || contains(after, ')')) + // protect parens + after = '{' + after + '}'; + os << '(' << after << ')'; + } else + os << '[' << after << ']'; + } + + if (!bi.isBibtex(key)) // escape chars with bibitems - os << '{' << escape(cleanupWhitespace(getParam("key"))) << '}'; - else - os << '{' << cleanupWhitespace(getParam("key")) << '}'; + os << '{' << escape(cleanupWhitespace(key)) << '}'; + else { + if (qualified) { + map pres = getQualifiedLists(getParam("pretextlist")); + map posts = getQualifiedLists(getParam("posttextlist")); + for (docstring const & k: keys) { + docstring const bef = pres[k]; + docstring const aft = posts[k]; + if (!bef.empty()) + os << '[' << bef << "][" << aft << ']'; + else if (!aft.empty()) + os << '[' << aft << ']'; + os << '{' << k << '}'; + } + } else + os << '{' << cleanupWhitespace(key) << '}'; + } if (runparams.inulemcmd) os << "}"; diff --git a/src/insets/InsetCitation.h b/src/insets/InsetCitation.h index 38624ed6f5..57b0b3da9e 100644 --- a/src/insets/InsetCitation.h +++ b/src/insets/InsetCitation.h @@ -86,6 +86,8 @@ public: /// CitationStyle getCitationStyle(BufferParams const & bp, std::string const & input, std::vector const & valid_styles) const; + /// + std::map getQualifiedLists(docstring const p) const; private: /// tries to make a pretty label and makes a basic one if not diff --git a/src/tex2lyx/TODO.txt b/src/tex2lyx/TODO.txt index c6866ed92a..d7178d3d0b 100644 --- a/src/tex2lyx/TODO.txt +++ b/src/tex2lyx/TODO.txt @@ -134,7 +134,20 @@ Format LaTeX feature LyX feature \citecite[*] LatexCmd citecite[*] \fullcite LatexCmd fullcite \footfullcite LatexCmd footfullcite - \supercite LatexCmd supercite + \supercite LatexCmd supercite +531 Biblatex "qualified citation lists" + \cites(pre)(post)[pre1][post1]{key1}[pre2][post2]{key2}... + \begin_inset CommandInset citation + LatexCmd cite + after "post" + before "pre" + key "key1,key2..." + pretextlist "key1 pre1\tab key2 pre2..." + posttextlist "key1 post1\tab key2 post2..." + Same for: + \Cites, \textcites, \Textcites, \parencites, \Parencites, \smartcites, \Smartcites, \autocites, Autocites + + General diff --git a/src/version.h b/src/version.h index a0b5c973b6..a0a611d20a 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 530 // spitz: natbib/jurabib package options -#define LYX_FORMAT_TEX2LYX 530 +#define LYX_FORMAT_LYX 531 // spitz: qualified citation lists +#define LYX_FORMAT_TEX2LYX 531 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX #ifndef _MSC_VER