]> git.lyx.org Git - features.git/blob - src/Biblio.cpp
c3c53998ee654ffe9715dbb280ee4b4f840ddff0
[features.git] / src / Biblio.cpp
1 /**
2  * \file Biblio.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  * \author Herbert Voß
8  * \author Richard Heck (re-write of BibTeX representation)
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "Biblio.h"
16
17 #include "buffer_funcs.h"
18 #include "gettext.h"
19 #include "InsetIterator.h"
20 #include "Paragraph.h"
21
22 #include "insets/Inset.h"
23 #include "insets/InsetBibitem.h"
24 #include "insets/InsetBibtex.h"
25 #include "insets/InsetInclude.h"
26
27 #include "support/lstrings.h"
28
29 #include "boost/regex.hpp"
30
31 using std::string;
32 using std::vector;
33 using std::pair;
34 using std::endl;
35
36 namespace lyx {
37 using support::ascii_lowercase;
38 using support::bformat;
39 using support::compare_ascii_no_case;
40 using support::contains;
41 using support::getVectorFromString;
42 using support::ltrim;
43 using support::prefixIs;
44 using support::rtrim;
45 using support::split;
46 using support::subst;
47 using support::token;
48 using support::trim;
49
50 namespace biblio {
51
52 namespace {
53
54         vector<string> const init_possible_cite_commands()
55         {
56                 char const * const pos[] = {
57                         "cite", "citet", "citep", "citealt", "citealp",
58                         "citeauthor", "citeyear", "citeyearpar",
59                         "citet*", "citep*", "citealt*", "citealp*", "citeauthor*",
60                         "Citet",  "Citep",  "Citealt",  "Citealp",  "Citeauthor",
61                         "Citet*", "Citep*", "Citealt*", "Citealp*", "Citeauthor*",
62                         "fullcite",
63                         "footcite", "footcitet", "footcitep", "footcitealt",
64                         "footcitealp", "footciteauthor", "footciteyear", "footciteyearpar",
65                         "citefield", "citetitle", "cite*"
66                 };
67                 size_t const size_pos = sizeof(pos) / sizeof(pos[0]);
68
69                 return vector<string>(pos, pos + size_pos);
70         }
71
72
73         vector<string> const & possible_cite_commands()
74         {
75                 static vector<string> const pos = init_possible_cite_commands();
76                 return pos;
77         }
78
79
80         bool is_possible_cite_command(string const & input)
81         {
82                 vector<string> const & possibles = possible_cite_commands();
83                 vector<string>::const_iterator const end = possibles.end();
84                 return std::find(possibles.begin(), end, input) != end;
85         }
86
87
88         string const default_cite_command(CiteEngine engine)
89         {
90                 string str;
91                 switch (engine) {
92                         case ENGINE_BASIC:
93                                 str = "cite";
94                                 break;
95                         case ENGINE_NATBIB_AUTHORYEAR:
96                                 str = "citet";
97                                 break;
98                         case ENGINE_NATBIB_NUMERICAL:
99                                 str = "citep";
100                                 break;
101                         case ENGINE_JURABIB:
102                                 str = "cite";
103                                 break;
104                 }
105                 return str;
106         }
107
108
109 } // namespace anon
110
111
112 const docstring TheBibliographyRef(from_ascii("TheBibliographyRef"));
113
114 string const asValidLatexCommand(string const & input,
115                                  CiteEngine const engine)
116 {
117         string const default_str = default_cite_command(engine);
118         if (!is_possible_cite_command(input))
119                 return default_str;
120
121         string output;
122         switch (engine) {
123                 case ENGINE_BASIC:
124                         output = default_str;
125                         break;
126
127                 case ENGINE_NATBIB_AUTHORYEAR:
128                 case ENGINE_NATBIB_NUMERICAL:
129                         if (input == "cite" || input == "citefield" ||
130                                                         input == "citetitle" || input == "cite*")
131                                 output = default_str;
132                         else if (prefixIs(input, "foot"))
133                                 output = input.substr(4);
134                         else
135                                 output = input;
136                         break;
137
138                         case ENGINE_JURABIB: {
139         // Jurabib does not support the 'uppercase' natbib style.
140                                 if (input[0] == 'C')
141                                         output = string(1, 'c') + input.substr(1);
142                                 else
143                                         output = input;
144
145         // Jurabib does not support the 'full' natbib style.
146                                 string::size_type const n = output.size() - 1;
147                                 if (output != "cite*" && output[n] == '*')
148                                         output = output.substr(0, n);
149
150                                 break;
151                         }
152         }
153
154         return output;
155 }
156
157
158 docstring const familyName(docstring const & name)
159 {
160         if (name.empty())
161                 return docstring();
162
163 // Very simple parser
164         docstring fname = name;
165
166 // possible authorname combinations are:
167 // "Surname, FirstName"
168 // "Surname, F."
169 // "FirstName Surname"
170 // "F. Surname"
171         docstring::size_type idx = fname.find(',');
172         if (idx != docstring::npos)
173                 return ltrim(fname.substr(0, idx));
174         idx = fname.rfind('.');
175         if (idx != docstring::npos && idx + 1 < fname.size())
176                 fname = ltrim(fname.substr(idx + 1));
177 // test if we have a LaTeX Space in front
178         if (fname[0] == '\\')
179                 return fname.substr(2);
180
181         return rtrim(fname);
182 }
183
184
185 docstring const getAbbreviatedAuthor(InfoMap const & map, string const & key)
186 {
187         BOOST_ASSERT(!map.empty());
188
189         InfoMap::const_iterator it = map.find(key);
190         if (it == map.end())
191                 return docstring();
192         docstring const & data = it->second;
193
194 // Is the entry a BibTeX one or one from lyx-layout "bibliography"?
195         docstring::size_type const pos = data.find(TheBibliographyRef);
196         if (pos != docstring::npos) {
197                 if (pos <= 2) {
198                         return docstring();
199                 }
200
201                 docstring const opt = trim(data.substr(0, pos - 1));
202                 if (opt.empty())
203                         return docstring();
204
205                 docstring authors;
206                 split(opt, authors, '(');
207                 return authors;
208         }
209
210         docstring author = parseBibTeX(data, "author");
211
212         if (author.empty())
213                 author = parseBibTeX(data, "editor");
214
215         if (author.empty()) {
216                 author = parseBibTeX(data, "key");
217                 if (author.empty())
218                 // FIXME UNICODE
219                         return from_utf8(key);
220                 return author;
221         }
222
223         vector<docstring> const authors = getVectorFromString(author, from_ascii(" and "));
224         if (authors.empty())
225                 return author;
226
227         if (authors.size() == 2)
228                 return bformat(_("%1$s and %2$s"),
229                                                                         familyName(authors[0]), familyName(authors[1]));
230
231         if (authors.size() > 2)
232                 return bformat(_("%1$s et al."), familyName(authors[0]));
233
234         return familyName(authors[0]);
235 }
236
237
238 docstring const getYear(InfoMap const & map, string const & key)
239 {
240         BOOST_ASSERT(!map.empty());
241
242         InfoMap::const_iterator it = map.find(key);
243         if (it == map.end())
244                 return docstring();
245         docstring const & data = it->second;
246
247 // Is the entry a BibTeX one or one from lyx-layout "bibliography"?
248         docstring::size_type const pos = data.find(TheBibliographyRef);
249         if (pos != docstring::npos) {
250                 if (pos <= 2) {
251                         return docstring();
252                 }
253
254                 docstring const opt =
255                                         trim(data.substr(0, pos - 1));
256                 if (opt.empty())
257                         return docstring();
258
259                 docstring authors;
260                 docstring const tmp = split(opt, authors, '(');
261                 docstring year;
262                 split(tmp, year, ')');
263                 return year;
264
265         }
266
267         docstring year = parseBibTeX(data, "year");
268         if (year.empty())
269                 year = _("No year");
270
271         return year;
272 }
273
274
275 namespace {
276 // A functor for use with std::sort, leading to case insensitive sorting
277 class compareNoCase: public std::binary_function<string, string, bool>
278 {
279         public:
280                 bool operator()(string const & s1, string const & s2) const {
281                         return compare_ascii_no_case(s1, s2) < 0;
282                 }
283 };
284 } // namespace anon
285
286
287 vector<string> const getKeys(InfoMap const & map)
288 {
289         vector<string> bibkeys;
290         InfoMap::const_iterator it  = map.begin();
291         InfoMap::const_iterator end = map.end();
292         for (; it != end; ++it) {
293                 bibkeys.push_back(it->first);
294         }
295
296         std::sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
297         return bibkeys;
298 }
299
300
301 docstring const getInfo(InfoMap const & map, string const & key)
302 {
303         BOOST_ASSERT(!map.empty());
304
305         InfoMap::const_iterator it = map.find(key);
306         if (it == map.end())
307                 return docstring();
308         docstring const & data = it->second;
309
310 // is the entry a BibTeX one or one from lyx-layout "bibliography"?
311         docstring::size_type const pos = data.find(TheBibliographyRef);
312         if (pos != docstring::npos) {
313                 docstring::size_type const pos2 = pos + TheBibliographyRef.size();
314                 docstring const info = trim(data.substr(pos2));
315                 return info;
316         }
317
318 // Search for all possible "required" keys
319         docstring author = parseBibTeX(data, "author");
320         if (author.empty())
321                 author = parseBibTeX(data, "editor");
322
323         docstring year      = parseBibTeX(data, "year");
324         docstring title     = parseBibTeX(data, "title");
325         docstring booktitle = parseBibTeX(data, "booktitle");
326         docstring chapter   = parseBibTeX(data, "chapter");
327         docstring number    = parseBibTeX(data, "number");
328         docstring volume    = parseBibTeX(data, "volume");
329         docstring pages     = parseBibTeX(data, "pages");
330         docstring annote    = parseBibTeX(data, "annote");
331         docstring media     = parseBibTeX(data, "journal");
332         if (media.empty())
333                 media = parseBibTeX(data, "publisher");
334         if (media.empty())
335                 media = parseBibTeX(data, "school");
336         if (media.empty())
337                 media = parseBibTeX(data, "institution");
338
339         odocstringstream result;
340         if (!author.empty())
341                 result << author << ", ";
342         if (!title.empty())
343                 result << title;
344         if (!booktitle.empty())
345                 result << ", in " << booktitle;
346         if (!chapter.empty())
347                 result << ", Ch. " << chapter;
348         if (!media.empty())
349                 result << ", " << media;
350         if (!volume.empty())
351                 result << ", vol. " << volume;
352         if (!number.empty())
353                 result << ", no. " << number;
354         if (!pages.empty())
355                 result << ", pp. " << pages;
356         if (!year.empty())
357                 result << ", " << year;
358         if (!annote.empty())
359                 result << "\n\n" << annote;
360
361         docstring const result_str = rtrim(result.str());
362         if (!result_str.empty())
363                 return result_str;
364
365 // This should never happen (or at least be very unusual!)
366         return data;
367 }
368
369
370 namespace {
371
372 // Escape special chars.
373 // All characters are literals except: '.|*?+(){}[]^$\'
374 // These characters are literals when preceded by a "\", which is done here
375 // @todo: This function should be moved to support, and then the test in tests
376 //        should be moved there as well.
377 string const escape_special_chars(string const & expr)
378 {
379         // Search for all chars '.|*?+(){}[^$]\'
380         // Note that '[' and '\' must be escaped.
381         // This is a limitation of boost::regex, but all other chars in BREs
382         // are assumed literal.
383                 boost::regex reg("[].|*?+(){}^$\\[\\\\]");
384
385         // $& is a perl-like expression that expands to all
386         // of the current match
387         // The '$' must be prefixed with the escape character '\' for
388         // boost to treat it as a literal.
389         // Thus, to prefix a matched expression with '\', we use:
390                 return boost::regex_replace(expr, reg, "\\\\$&");
391 }
392
393
394 // A functor for use with std::find_if, used to ascertain whether a
395 // data entry matches the required regex_
396 // @throws: boost::regex_error if the supplied regex pattern is not valid
397 // @todo: This function should be moved to support.
398 class RegexMatch : public std::unary_function<string, bool>
399 {
400         public:
401 // re and icase are used to construct an instance of boost::RegEx.
402 // if icase is true, then matching is insensitive to case
403                 RegexMatch(InfoMap const & m, string const & re, bool icase)
404                 : map_(m), regex_(re, icase) {}
405
406                 bool operator()(string const & key) const {
407 // the data searched is the key + its associated BibTeX/biblio
408 // fields
409                         string data = key;
410                         InfoMap::const_iterator info = map_.find(key);
411                         if (info != map_.end())
412         // FIXME UNICODE
413                                 data += ' ' + to_utf8(info->second);
414
415 // Attempts to find a match for the current RE
416 // somewhere in data.
417                         return boost::regex_search(data, regex_);
418                 }
419         private:
420                 InfoMap const map_;
421                 mutable boost::regex regex_;
422 };
423
424 } // namespace anon
425
426
427 vector<string>::const_iterator searchKeys(InfoMap const & theMap,
428                 vector<string> const & keys,
429                 string const & search_expr,
430                 vector<string>::const_iterator start,
431                 Search type,
432                 Direction dir,
433         bool caseSensitive)
434 {
435         // Preliminary checks
436         if (start < keys.begin() || start >= keys.end())
437                 return keys.end();
438
439         string expr = trim(search_expr);
440         if (expr.empty())
441                 return keys.end();
442
443         if (type == SIMPLE)
444         // We must escape special chars in the search_expr so that
445         // it is treated as a simple string by boost::regex.
446         expr = escape_special_chars(expr);
447
448         try {
449                 // Build the functor that will be passed to find_if.
450                 RegexMatch const match(theMap, expr, !caseSensitive);
451
452                 // Search the vector of 'keys' from 'start' for one
453                 // that matches the predicate 'match'. Searching can
454                 // be forward or backward from start.
455                 if (dir == FORWARD)
456                         return std::find_if(start, keys.end(), match);
457
458                 vector<string>::const_reverse_iterator rit(start);
459                 vector<string>::const_reverse_iterator rend = keys.rend();
460                 rit = std::find_if(rit, rend, match);
461
462                 if (rit == rend)
463                         return keys.end();
464                 // This is correct and always safe.
465                 // (See Meyer's Effective STL, Item 28.)
466                 return (++rit).base();
467         }
468         catch (boost::regex_error &) {
469                 return keys.end();
470         }
471 }
472
473
474 docstring const parseBibTeX(docstring data, string const & findkey)
475 {
476         // at first we delete all characters right of '%' and
477         // replace tabs through a space and remove leading spaces
478         // we read the data line by line so that the \n are
479         // ignored, too.
480         docstring data_;
481         int Entries = 0;
482         docstring dummy = token(data,'\n', Entries);
483         while (!dummy.empty()) {
484                 // no tabs
485                 dummy = subst(dummy, '\t', ' ');
486                 // no leading spaces
487                 dummy = ltrim(dummy);
488                 // ignore lines with a beginning '%' or ignore all right of %
489                 docstring::size_type const idx =
490                                         dummy.empty() ? docstring::npos : dummy.find('%');
491                 if (idx != docstring::npos)
492                         // Check if this is really a comment or just "\%"
493                         if (idx == 0 || dummy[idx - 1] != '\\')
494                                 dummy.erase(idx, docstring::npos);
495                         else
496                                 //  This is "\%", so just erase the '\'
497                                 dummy.erase(idx - 1, 1);
498                 // do we have a new token or a new line of
499                 // the same one? In the first case we ignore
500                 // the \n and in the second we replace it
501                 // with a space
502                 if (!dummy.empty()) {
503                         if (!contains(dummy, '='))
504                                 data_ += ' ' + dummy;
505                         else
506                                 data_ += dummy;
507                 }
508                 dummy = token(data, '\n', ++Entries);
509         } //end while
510
511         // replace double commas with "" for easy scanning
512         data = subst(data_, from_ascii(",,"), from_ascii("\"\""));
513
514         // unlikely!
515         if (data.empty())
516                 return docstring();
517
518         // now get only the important line of the bibtex entry.
519         // all entries are devided by ',' except the last one.
520         data += ',';
521         // now we have same behaviour for all entries because the last one
522         // is "blah ... }"
523         Entries = 0;
524         bool found = false;
525         // parsing of title and booktitle is different from the
526         // others, because booktitle contains title
527         do {
528                 dummy = token(data, ',', Entries++);
529                 if (!dummy.empty()) {
530                         found = contains(ascii_lowercase(dummy), from_ascii(findkey));
531                         if (findkey == "title" &&
532                                                                 contains(ascii_lowercase(dummy), from_ascii("booktitle")))
533                                 found = false;
534                 }
535         } while (!found && !dummy.empty());
536         if (dummy.empty())
537                 // no such keyword
538                 return docstring();
539
540         // we are not sure, if we get all, because "key= "blah, blah" is
541         // allowed.
542         // Therefore we read all until the next "=" character, which follows a
543         // new keyword
544         docstring keyvalue = dummy;
545         dummy = token(data, ',', Entries++);
546         while (!contains(dummy, '=') && !dummy.empty()) {
547                 keyvalue += ',' + dummy;
548                 dummy = token(data, ',', Entries++);
549         }
550
551         // replace double "" with originals ,, (two commas)
552         // leaving us with the all-important line
553         data = subst(keyvalue, from_ascii("\"\""), from_ascii(",,"));
554
555         // Clean-up.
556         // 1. Spaces
557         data = rtrim(data);
558         // 2. if there is no opening '{' then a closing '{' is probably cruft.
559         if (!contains(data, '{'))
560                 data = rtrim(data, "}");
561         // happens, when last keyword
562                 docstring::size_type const idx =
563                         !data.empty() ? data.find('=') : docstring::npos;
564
565                 if (idx == docstring::npos)
566                         return docstring();
567
568                 data = trim(data.substr(idx));
569
570         // a valid entry?
571         if (data.length() < 2 || data[0] != '=')
572                 return docstring();
573         else {
574                 // delete '=' and the following spaces
575                 data = ltrim(data, " =");
576                 if (data.length() < 2) {
577                         // not long enough to find delimiters
578                         return data;
579                 } else {
580                         docstring::size_type keypos = 1;
581                         char_type enclosing;
582                         if (data[0] == '{') {
583                                 enclosing = '}';
584                         } else if (data[0] == '"') {
585                                 enclosing = '"';
586                         } else {
587                                 // no {} and no "", pure data but with a
588                                 // possible ',' at the end
589                                 return rtrim(data, ",");
590                         }
591                         docstring tmp = data.substr(keypos);
592                         while (tmp.find('{') != docstring::npos &&
593                                         tmp.find('}') != docstring::npos &&
594                                         tmp.find('{') < tmp.find('}') &&
595                                         tmp.find('{') < tmp.find(enclosing)) {
596                                 keypos += tmp.find('{') + 1;
597                                 tmp = data.substr(keypos);
598                                 keypos += tmp.find('}') + 1;
599                                 tmp = data.substr(keypos);
600                         }
601                         if (tmp.find(enclosing) == docstring::npos)
602                                 return data;
603                         else {
604                                 keypos += tmp.find(enclosing);
605                                 return data.substr(1, keypos - 1);
606                         }
607                 }
608         }
609 }
610
611
612 namespace {
613
614
615 char const * const citeCommands[] = {
616         "cite", "citet", "citep", "citealt", "citealp", "citeauthor",
617         "citeyear", "citeyearpar" };
618
619 unsigned int const nCiteCommands =
620                 sizeof(citeCommands) / sizeof(char *);
621
622 CiteStyle const citeStyles[] = {
623         CITE, CITET, CITEP, CITEALT, CITEALP,
624 CITEAUTHOR, CITEYEAR, CITEYEARPAR };
625
626 unsigned int const nCiteStyles =
627                 sizeof(citeStyles) / sizeof(CiteStyle);
628
629 CiteStyle const citeStylesFull[] = {
630         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
631
632 unsigned int const nCiteStylesFull =
633                 sizeof(citeStylesFull) / sizeof(CiteStyle);
634
635 CiteStyle const citeStylesUCase[] = {
636         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
637
638 unsigned int const nCiteStylesUCase =
639         sizeof(citeStylesUCase) / sizeof(CiteStyle);
640
641 } // namespace anon
642
643
644 CitationStyle::CitationStyle(string const & command)
645         : style(CITE), full(false), forceUCase(false)
646 {
647         if (command.empty())
648                 return;
649
650         string cmd = command;
651         if (cmd[0] == 'C') {
652                 forceUCase = true;
653                 cmd[0] = 'c';
654         }
655
656         string::size_type const n = cmd.size() - 1;
657         if (cmd != "cite" && cmd[n] == '*') {
658                 full = true;
659                 cmd = cmd.substr(0,n);
660         }
661
662         char const * const * const last = citeCommands + nCiteCommands;
663         char const * const * const ptr = std::find(citeCommands, last, cmd);
664
665         if (ptr != last) {
666                 size_t idx = ptr - citeCommands;
667                 style = citeStyles[idx];
668         }
669 }
670
671
672 string const CitationStyle::asLatexStr() const
673 {
674         string cite = citeCommands[style];
675         if (full) {
676                 CiteStyle const * last = citeStylesFull + nCiteStylesFull;
677                 if (std::find(citeStylesFull, last, style) != last)
678                         cite += '*';
679         }
680
681         if (forceUCase) {
682                 CiteStyle const * last = citeStylesUCase + nCiteStylesUCase;
683                 if (std::find(citeStylesUCase, last, style) != last)
684                         cite[0] = 'C';
685         }
686
687         return cite;
688 }
689
690
691 vector<CiteStyle> const getCiteStyles(CiteEngine const engine)
692 {
693         unsigned int nStyles = 0;
694         unsigned int start = 0;
695
696         switch (engine) {
697                 case ENGINE_BASIC:
698                         nStyles = 1;
699                         start = 0;
700                         break;
701                 case ENGINE_NATBIB_AUTHORYEAR:
702                 case ENGINE_NATBIB_NUMERICAL:
703                         nStyles = nCiteStyles - 1;
704                         start = 1;
705                         break;
706                 case ENGINE_JURABIB:
707                         nStyles = nCiteStyles;
708                         start = 0;
709                         break;
710         }
711
712         typedef vector<CiteStyle> cite_vec;
713
714         cite_vec styles(nStyles);
715         cite_vec::size_type i = 0;
716         int j = start;
717         for (; i != styles.size(); ++i, ++j)
718                 styles[i] = citeStyles[j];
719
720         return styles;
721 }
722
723
724 vector<docstring> const
725         getNumericalStrings(string const & key,
726         InfoMap const & map, vector<CiteStyle> const & styles)
727 {
728         if (map.empty())
729                 return vector<docstring>();
730
731         docstring const author = getAbbreviatedAuthor(map, key);
732         docstring const year   = getYear(map, key);
733         if (author.empty() || year.empty())
734                 return vector<docstring>();
735
736         vector<docstring> vec(styles.size());
737         for (vector<docstring>::size_type i = 0; i != vec.size(); ++i) {
738                 docstring str;
739
740                 switch (styles[i]) {
741                         case CITE:
742                         case CITEP:
743                                 str = from_ascii("[#ID]");
744                                 break;
745
746                         case CITET:
747                                 str = author + " [#ID]";
748                                 break;
749
750                         case CITEALT:
751                                 str = author + " #ID";
752                                 break;
753
754                         case CITEALP:
755                                 str = from_ascii("#ID");
756                                 break;
757
758                         case CITEAUTHOR:
759                                 str = author;
760                                 break;
761
762                         case CITEYEAR:
763                                 str = year;
764                                 break;
765
766                         case CITEYEARPAR:
767                                 str = '(' + year + ')';
768                                 break;
769                 }
770
771                 vec[i] = str;
772         }
773
774         return vec;
775 }
776
777
778 vector<docstring> const
779                 getAuthorYearStrings(string const & key,
780                         InfoMap const & map, vector<CiteStyle> const & styles)
781 {
782         if (map.empty())
783                 return vector<docstring>();
784
785         docstring const author = getAbbreviatedAuthor(map, key);
786         docstring const year   = getYear(map, key);
787         if (author.empty() || year.empty())
788                 return vector<docstring>();
789
790         vector<docstring> vec(styles.size());
791         for (vector<docstring>::size_type i = 0; i != vec.size(); ++i) {
792                 docstring str;
793
794                 switch (styles[i]) {
795                         case CITE:
796                 // jurabib only: Author/Annotator
797                 // (i.e. the "before" field, 2nd opt arg)
798                                 str = author + "/<" + _("before") + '>';
799                                 break;
800
801                         case CITET:
802                                 str = author + " (" + year + ')';
803                                 break;
804
805                         case CITEP:
806                                 str = '(' + author + ", " + year + ')';
807                                 break;
808
809                         case CITEALT:
810                                 str = author + ' ' + year ;
811                                 break;
812
813                         case CITEALP:
814                                 str = author + ", " + year ;
815                                 break;
816
817                         case CITEAUTHOR:
818                                 str = author;
819                                 break;
820
821                         case CITEYEAR:
822                                 str = year;
823                                 break;
824
825                         case CITEYEARPAR:
826                                 str = '(' + year + ')';
827                                 break;
828                 }
829
830                 vec[i] = str;
831         }
832
833         return vec;
834 }
835
836
837 void fillWithBibKeys(Buffer const * const buf, 
838                      vector<pair<string, docstring> > & keys)
839 {       
840         /// if this is a child document and the parent is already loaded
841         /// use the parent's list instead  [ale990412]
842         Buffer const * const tmp = buf->getMasterBuffer();
843         BOOST_ASSERT(tmp);
844         if (tmp != buf) {
845                 fillWithBibKeys(tmp, keys);
846                 return;
847         }
848
849         // Pre-load all child documents.
850         loadChildDocuments(*buf);
851
852         for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
853                         it->fillWithBibKeys(*buf, keys, it);
854 }
855 } // namespace biblio
856 } // namespace lyx
857