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