commit 68ab4023ccdf26022a053adc1bfaecef9fef58f4
Author: Juergen Spitzmueller <sp...@lyx.org>
Date:   Sat Jan 21 14:25:17 2017 +0100

    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 3c4f115..3c464f3 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 <sp...@lyx.org>
+       * 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 <sp...@lyx.org>
        * Format incremented to 530: Support natbib & jurabib package options.
 
diff --git a/lib/citeengines/biblatex-natbib.citeengine 
b/lib/citeengines/biblatex-natbib.citeengine
index ffcd9e6..25b76f6 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*<!_citetitlestar!_citetitlestartooltip>[][]
        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 657547e..c58e2be 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*<!_citeauthorstar!_citeauthorstartooltip>[][]
        citeyearpar[][]=parencite*
        citeyear[][]=cite*
        citebyear[][]=citeyear
-       Footcite[][]=smartcite
-       Autocite[][]
+       Footcite$[][]=smartcite
+       Autocite$[][]
        citetitle*<!_citetitlestar!_citetitlestartooltip>[][]
        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*<!_citeauthorstar!_citeauthorstartooltip>[][]
        citeyear|parencite*,citebyear[][]=citeyear*
        citetitle*<!_citetitlestar!_citetitlestartooltip>[][]
@@ -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 3ecd1ee..a9a23de 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*<!_stardesc!_stardesctooltip>[][]=latexcmd
+\change_inserted -712698321 1484997816
+LyXName|alias$*<!_stardesc!_stardesctooltip>[][]=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 d1fd4e8..518db5d 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 5b647db..044bc75 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 ab06f37..4220fe0 100644
--- a/src/BiblioInfo.cpp
+++ b/src/BiblioInfo.cpp
@@ -36,6 +36,7 @@
 #include "support/regex.h"
 #include "support/textutils.h"
 
+#include <map>
 #include <set>
 
 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 c48e3c4..600ba6c 100644
--- a/src/Citation.h
+++ b/src/Citation.h
@@ -13,6 +13,7 @@
 #define CITATION_H
 
 #include "support/docstring.h"
+#include <map>
 #include <string>
 
 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<docstring, docstring> pretexts;
+       ///
+       std::map<docstring, docstring> getPretexts() const { return pretexts; }
+       /// Qualified lists's post texts
+       std::map<docstring, docstring> posttexts;
+       ///
+       std::map<docstring, docstring> 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 9b25a67..34b55fa 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 d73f3d3..2178dd1 100644
--- a/src/frontends/qt4/GuiCitation.cpp
+++ b/src/frontends/qt4/GuiCitation.cpp
@@ -39,6 +39,7 @@
 #include <QMenu>
 #include <QSettings>
 #include <QShowEvent>
+#include <QStandardItemModel>
 #include <QVariant>
 
 #include <vector>
@@ -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<docstring> 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<docstring> GuiCitation::getPreTexts()
+{
+       vector<docstring> 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<docstring> 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<docstring> GuiCitation::getPostTexts()
+{
+       vector<docstring> 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<docstring> const keys = to_docstring_vector(cited_keys_);
        vector<CitationStyle> styles = citeStyles_;
+       int ind = citationStyleCO->currentIndex();
+       if (ind == -1)
+               ind = 0;
+       CitationStyle cs = styles[ind];
+       vector<docstring> pretexts = getPreTexts();
+       vector<docstring> posttexts = getPostTexts();
+       bool const qualified = cs.hasQualifiedList
+               && (selectedLV->model()->rowCount() > 1
+                   || !pretexts.empty()
+                   || !posttexts.empty());
+       std::map<docstring, docstring> pres;
+       for (docstring const & s: pretexts) {
+               docstring key;
+               docstring val = split(s, key, ' ');
+               pres[key] = val;
+       }
+       std::map<docstring, docstring> 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<docstring> 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 035b2d2..2fa4369 100644
--- a/src/frontends/qt4/GuiCitation.h
+++ b/src/frontends/qt4/GuiCitation.h
@@ -23,6 +23,8 @@
 
 #include "Citation.h"
 
+#include <QAbstractListModel>
+#include <QStandardItemModel>
 #include <QStringList>
 #include <QStringListModel>
 
@@ -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<docstring> const m);
+       /// Get pre texts of qualified lists
+       std::vector<docstring> getPreTexts();
+       /// Set post texts of qualified lists
+       void setPostTexts(std::vector<docstring> const m);
+       /// Get post texts of qualified lists
+       std::vector<docstring> 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 d3d2b01..b90d0ea 100644
--- a/src/frontends/qt4/GuiSelectionManager.cpp
+++ b/src/frontends/qt4/GuiSelectionManager.cpp
@@ -18,6 +18,7 @@
 
 #include "support/debug.h"
 
+#include <QAbstractItemModel>
 #include <QAbstractListModel>
 #include <QItemSelection>
 #include <QListView>
@@ -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<int, QVariant> 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<int, QMap<int, 
QVariant>> & qms)
+{
+       if (i <= -1)
+               i = 0;
+       if (i > selectedModel->rowCount())
+               i = selectedModel->rowCount();
+       if (!selectedModel->insertRow(i))
+               return false;
+       bool res = true;
+       QMap<int, QMap<int, QVariant>>::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<int, QVariant> qm = selectedModel->itemData(idx);
+
+       QMap<int, QMap<int, QVariant>> qms;
+       QList<QModelIndex>::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<int, QVariant> qm = selectedModel->itemData(idx);
+       QMap<int, QMap<int, QVariant>> qms;
+       QList<QModelIndex>::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 4b83e23..dae3f90 100644
--- a/src/frontends/qt4/GuiSelectionManager.h
+++ b/src/frontends/qt4/GuiSelectionManager.h
@@ -14,6 +14,7 @@
 
 #include <QObject>
 
+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<int, QVariant> const & itemData);
+       bool insertRowToSelected(int i, QMap<int, QVariant> const & itemData);
+       ///
+       bool insertRowToSelected(int i, QMap<int, QMap<int, QVariant>> &);
        ///
        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 c8682bb..535ee31 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<InsetCitation const *>(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<docstring> const keys = getVectorFromString(key);
 
-       vector<CitationStyle> const citeStyleList = buf->params().citeStyles();
+       vector<CitationStyle> 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<docstring, docstring> pres =
+               citinset->getQualifiedLists(citinset->getParam("pretextlist"));
+       std::map<docstring, docstring> 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<docstring> 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 a824212..6b43968 100644
--- a/src/frontends/qt4/ui/CitationUi.ui
+++ b/src/frontends/qt4/ui/CitationUi.ui
@@ -234,11 +234,7 @@
             </widget>
            </item>
            <item>
-            <widget class="QListView" name="selectedLV">
-             <property name="editTriggers">
-              <set>QAbstractItemView::NoEditTriggers</set>
-             </property>
-            </widget>
+            <widget class="QTableView" name="selectedLV"/>
            </item>
           </layout>
          </item>
@@ -290,7 +286,7 @@
           <item>
            <widget class="QLabel" name="textBeforeLA">
             <property name="text">
-             <string>Text &amp;before:</string>
+             <string>Text befo&amp;re:</string>
             </property>
             <property name="buddy">
              <cstring>textBeforeED</cstring>
@@ -483,7 +479,6 @@
   <tabstop>entriesCO</tabstop>
   <tabstop>searchOptionsPB</tabstop>
   <tabstop>availableLV</tabstop>
-  <tabstop>selectedLV</tabstop>
   <tabstop>addPB</tabstop>
   <tabstop>deletePB</tabstop>
   <tabstop>upPB</tabstop>
diff --git a/src/insets/InsetCitation.cpp b/src/insets/InsetCitation.cpp
index 0a5da5b..9004c98 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<docstring, docstring> InsetCitation::getQualifiedLists(docstring const p) 
const
+{
+       vector<docstring> ps =
+               getVectorFromString(p, from_ascii("\t"));
+       std::map<docstring, docstring> 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<docstring> 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<docstring, docstring> pres = 
getQualifiedLists(getParam("pretextlist"));
+       map<docstring, docstring> 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<CitationStyle> 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<docstring> 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";
+
+       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(getParam("key")))
+       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<docstring, docstring> pres = 
getQualifiedLists(getParam("pretextlist"));
+                       map<docstring, docstring> 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 38624ed..57b0b3d 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<CitationStyle> const & 
valid_styles) const;
+       ///
+       std::map<docstring, docstring> 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 c6866ed..d7178d3 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 a0b5c97..a0a611d 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

Reply via email to