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