]> git.lyx.org Git - lyx.git/blobdiff - src/BiblioInfo.cpp
Improve info display for biblatex databases
[lyx.git] / src / BiblioInfo.cpp
index f6f0e8ba8f5370ab53f58e839dc6bda56f63b805..c85eabd5e75ee5ddc605d41a65655d8222d3b38f 100644 (file)
@@ -255,7 +255,8 @@ BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
 {}
 
 
-docstring const BibTeXInfo::getAbbreviatedAuthor(bool jurabib_style) const
+docstring const BibTeXInfo::getAbbreviatedAuthor(
+    Buffer const * buf, bool jurabib_style) const
 {
        if (!is_bibtex_) {
                docstring const opt = label();
@@ -296,37 +297,45 @@ docstring const BibTeXInfo::getAbbreviatedAuthor(bool jurabib_style) const
 
        docstring retval = familyName(authors[0]);
 
-       if (authors.size() == 2 && authors[1] != "others")
-               retval = bformat(from_ascii("%1$s and %2$s"),
-                       familyName(authors[0]), familyName(authors[1]));
-
-       if (authors.size() >= 2)
-               retval = bformat(from_ascii("%1$s et al."),
-                       familyName(authors[0]));
+       if (authors.size() == 2 && authors[1] != "others") {
+               docstring const dformat = buf ? 
+                       buf->B_("%1$s and %2$s") : from_ascii("%1$s and %2$s");
+               retval = bformat(dformat, familyName(authors[0]), familyName(authors[1]));
+       } else if (authors.size() >= 2) {
+               // we get here either if the author list is longer than two names
+               // or if the second 'name' is "others". we do the same thing either
+               // way.
+               docstring const dformat = buf ? 
+                       buf->B_("%1$s et al.") : from_ascii("%1$s et al.");
+               retval = bformat(dformat, familyName(authors[0]));
+       }
 
        return convertLaTeXCommands(retval);
 }
 
 
-docstring const BibTeXInfo::getAbbreviatedAuthor(Buffer const & buf, bool jurabib_style) const
-{
-       docstring const author = getAbbreviatedAuthor(jurabib_style);
-       if (!is_bibtex_)
-               return author;
-       vector<docstring> const authors = getVectorFromString(author, from_ascii(" and "));
-       if (authors.size() == 2)
-               return bformat(buf.B_("%1$s and %2$s"), authors[0], authors[1]);
-       docstring::size_type const idx = author.rfind(from_ascii(" et al."));
-       if (idx != docstring::npos)
-               return bformat(buf.B_("%1$s et al."), author.substr(0, idx));
-       return author;
-}
-
-
 docstring const BibTeXInfo::getYear() const
 {
-       if (is_bibtex_)
-               return operator[]("year");
+       if (is_bibtex_) {
+               // first try legacy year field
+               docstring year = operator[]("year");
+               if (!year.empty())
+                       return year;
+               // now try biblatex's date field
+               year = operator[]("date");
+               // Format is [-]YYYY-MM-DD*/[-]YYYY-MM-DD*
+               // We only want the years.
+               static regex const yreg("[-]?([\\d]{4}).*");
+               static regex const ereg(".*/([\\d]{4}).*");
+               smatch sm;
+               string const date = to_utf8(year);
+               regex_match(date, sm, yreg);
+               year = from_ascii(sm[1]);
+               // check for an endyear
+               if (regex_match(date, sm, ereg))
+                       year += char_type(0x2013) + from_ascii(sm[1]);
+               return year;
+       }
 
        docstring const opt = label();
        if (opt.empty())
@@ -466,7 +475,15 @@ docstring parseOptions(docstring const & format, string & optkey,
 
 } // anon namespace
 
-
+/* FIXME
+Bug #9131 revealed an oddity in how we are generating citation information
+when more than one key is given. We end up building a longer and longer format 
+string as we go, which we then have to re-parse, over and over and over again,
+rather than generating the information for the individual keys and then putting
+all of that together. We do that to deal with the way separators work, from what
+I can tell, but it still feels like a hack. Fixing this would require quite a
+bit of work, however.
+*/
 docstring BibTeXInfo::expandFormat(docstring const & format,
                BibTeXInfo const * const xref, int & counter, Buffer const & buf,
                docstring before, docstring after, docstring dialog, bool next) const
@@ -488,7 +505,7 @@ docstring BibTeXInfo::expandFormat(docstring const & format,
        // we'll remove characters from the front of fmt as we
        // deal with them
        while (!fmt.empty()) {
-               if (counter++ > max_passes) {
+               if (counter > max_passes) {
                        LYXERR0("Recursion limit reached while parsing `"
                                << format << "'.");
                        return _("ERROR!");
@@ -506,6 +523,7 @@ docstring BibTeXInfo::expandFormat(docstring const & format,
                                        string const val =
                                                buf.params().documentClass().getCiteMacro(engine_type, key);
                                        fmt = from_utf8(val) + fmt.substr(1);
+                                       counter += 1;
                                        continue;
                                } else if (key[0] == '_') {
                                        // a translatable bit
@@ -550,12 +568,15 @@ docstring BibTeXInfo::expandFormat(docstring const & format,
                                                getValueForKey(optkey, buf, before, after, dialog, xref);
                                        if (optkey == "next" && next)
                                                ret << ifpart; // without expansion
-                                       else if (!val.empty())
-                                               ret << expandFormat(ifpart, xref, counter, buf,
+                                       else if (!val.empty()) {
+                                               int newcounter = 0;
+                                               ret << expandFormat(ifpart, xref, newcounter, buf,
                                                        before, after, dialog, next);
-                                       else if (!elsepart.empty())
-                                               ret << expandFormat(elsepart, xref, counter, buf,
+                                       } else if (!elsepart.empty()) {
+                                               int newcounter = 0;
+                                               ret << expandFormat(elsepart, xref, newcounter, buf,
                                                        before, after, dialog, next);
+                                       }
                                        // fmt will have been shortened for us already
                                        continue;
                                }
@@ -708,12 +729,12 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
                        ret = cite_number_;
                else if (key == "abbrvauthor")
                        // Special key to provide abbreviated author names.
-                       ret = getAbbreviatedAuthor(buf, false);
+                       ret = getAbbreviatedAuthor(&buf, false);
                else if (key == "shortauthor")
                        // When shortauthor is not defined, jurabib automatically
                        // provides jurabib-style abbreviated author names. We do
                        // this as well.
-                       ret = getAbbreviatedAuthor(buf, true);
+                       ret = getAbbreviatedAuthor(&buf, true);
                else if (key == "shorttitle") {
                        // When shorttitle is not defined, jurabib uses for `article'
                        // and `periodical' entries the form `journal volume [year]'
@@ -744,8 +765,7 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
                ret = html::cleanAttr(ret);
 
        // make sure it is not too big
-       if (ret.size() > maxsize)
-               ret = ret.substr(0, maxsize - 3) + from_ascii("...");
+       support::truncateWithEllipsis(ret, maxsize);
        return ret;
 }
 
@@ -811,7 +831,7 @@ docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key, Buffer c
        if (it == end())
                return docstring();
        BibTeXInfo const & data = it->second;
-       return data.getAbbreviatedAuthor(buf, false);
+       return data.getAbbreviatedAuthor(&buf, false);
 }
 
 
@@ -878,10 +898,19 @@ docstring const BiblioInfo::getInfo(docstring const & key,
 }
 
 
-docstring const BiblioInfo::getLabel(vector<docstring> const & keys,
-       Buffer const & buf, string const & style, bool richtext,
-       docstring const & before, docstring const & after, docstring const & dialog) const
+docstring const BiblioInfo::getLabel(vector<docstring> keys,
+       Buffer const & buf, string const & style, bool for_xhtml,
+       size_t max_size, docstring const & before, docstring const & after,
+       docstring const & dialog) const
 {
+       // shorter makes no sense
+       LASSERT(max_size >= 16, max_size = 16);
+
+       // we can't display more than 10 of these, anyway
+       bool const too_many_keys = keys.size() > 10;
+       if (too_many_keys)
+               keys.resize(10);
+
        CiteEngineType const engine_type = buf.params().citeEngineType();
        DocumentClass const & dc = buf.params().documentClass();
        docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite"));
@@ -903,16 +932,22 @@ docstring const BiblioInfo::getLabel(vector<docstring> const & keys,
                                        xrefptr = &(xrefit->second);
                        }
                }
-               ret = data.getLabel(xrefptr, buf, ret, richtext,
-                       before, after, dialog, key+1 != ken);
+               ret = data.getLabel(xrefptr, buf, ret, for_xhtml,
+                       before, after, dialog, key + 1 != ken);
        }
+
+       if (too_many_keys)
+               ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
+       support::truncateWithEllipsis(ret, max_size);
        return ret;
 }
 
 
 bool BiblioInfo::isBibtex(docstring const & key) const
 {
-       BiblioInfo::const_iterator it = find(key);
+       docstring key1;
+       split(key, key1, ',');
+       BiblioInfo::const_iterator it = find(key1);
        if (it == end())
                return false;
        return it->second.isBibTeX();
@@ -921,8 +956,8 @@ bool BiblioInfo::isBibtex(docstring const & key) const
 
 vector<docstring> const BiblioInfo::getCiteStrings(
        vector<docstring> const & keys, vector<CitationStyle> const & styles,
-       Buffer const & buf, bool richtext, docstring const & before,
-       docstring const & after, docstring const & dialog) const
+       Buffer const & buf, docstring const & before,
+       docstring const & after, docstring const & dialog, size_t max_size) const
 {
        if (empty())
                return vector<docstring>();
@@ -931,7 +966,7 @@ vector<docstring> const BiblioInfo::getCiteStrings(
        vector<docstring> vec(styles.size());
        for (size_t i = 0; i != vec.size(); ++i) {
                style = styles[i].cmd;
-               vec[i] = getLabel(keys, buf, style, richtext, before, after, dialog);
+               vec[i] = getLabel(keys, buf, style, false, max_size, before, after, dialog);
        }
 
        return vec;
@@ -973,9 +1008,9 @@ void BiblioInfo::collectCitedEntries(Buffer const & buf)
        // FIXME We may want to collect these differently, in the first case,
        // so that we might have them in order of appearance.
        set<docstring> citekeys;
-       Toc const & toc = buf.tocBackend().toc("citation");
-       Toc::const_iterator it = toc.begin();
-       Toc::const_iterator const en = toc.end();
+       shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
+       Toc::const_iterator it = toc->begin();
+       Toc::const_iterator const en = toc->end();
        for (; it != en; ++it) {
                if (it->str().empty())
                        continue;
@@ -1034,6 +1069,11 @@ void BiblioInfo::makeCitationLabels(Buffer const & buf)
                        docstring const num = convert<docstring>(++keynumber);
                        entry.setCiteNumber(num);
                } else {
+                       // coverity complains about our derefercing the iterator last,
+                       // which was not initialized above. but it does get initialized
+                       // after the first time through the loop, which is the point of
+                       // the first test.
+                       // coverity[FORWARD_NULL]
                        if (it != cited_entries_.begin()
                            && entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
                            // we access the year via getYear() so as to get it from the xref,
@@ -1067,7 +1107,7 @@ void BiblioInfo::makeCitationLabels(Buffer const & buf)
                if (numbers) {
                        entry.label(entry.citeNumber());
                } else {
-                       docstring const auth = entry.getAbbreviatedAuthor(buf, false);
+                       docstring const auth = entry.getAbbreviatedAuthor(&buf, false);
                        // we do it this way so as to access the xref, if necessary
                        // note that this also gives us the modifier
                        docstring const year = getYear(*it, buf, true);
@@ -1114,7 +1154,7 @@ string citationStyleToString(const CitationStyle & cs)
 {
        string cmd = cs.cmd;
        if (cs.forceUpperCase)
-               cmd[0] = 'C';
+               cmd[0] = uppercase(cmd[0]);
        if (cs.fullAuthorList)
                cmd += '*';
        return cmd;