]> git.lyx.org Git - lyx.git/blob - src/BiblioInfo.cpp
fix a visual cursor edge-case:
[lyx.git] / src / BiblioInfo.cpp
1 /**
2  * \file BiblioInfo.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
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "BiblioInfo.h"
16 #include "Buffer.h"
17 #include "BufferParams.h"
18 #include "buffer_funcs.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/docstream.h"
28 #include "support/gettext.h"
29 #include "support/lstrings.h"
30
31 #include "boost/regex.hpp"
32
33 using namespace std;
34 using namespace lyx::support;
35
36 namespace lyx {
37
38
39 //////////////////////////////////////////////////////////////////////
40 //
41 // BibTeXInfo
42 //
43 //////////////////////////////////////////////////////////////////////
44
45 BibTeXInfo::BibTeXInfo(bool ib)
46         : is_bibtex_(ib)
47 {}
48
49         
50 BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
51         : is_bibtex_(true), bib_key_(key), entry_type_(type)
52 {}
53
54         
55 bool BibTeXInfo::hasField(docstring const & field) const
56 {
57         return count(field) == 1;
58 }
59
60
61 docstring const & BibTeXInfo::getValueForField(docstring const & field) const
62 {
63         BibTeXInfo::const_iterator it = find(field);
64         if (it != end())
65                 return it->second;
66         static docstring const empty_value = docstring();
67         return empty_value;
68 }
69         
70         
71 docstring const & BibTeXInfo::getValueForField(string const & field) const
72 {
73         return getValueForField(from_ascii(field));
74 }
75
76
77 static docstring familyName(docstring const & name)
78 {
79         if (name.empty())
80                 return docstring();
81
82         // Very simple parser
83         docstring fname = name;
84
85         // possible authorname combinations are:
86         // "Surname, FirstName"
87         // "Surname, F."
88         // "FirstName Surname"
89         // "F. Surname"
90         docstring::size_type idx = fname.find(',');
91         if (idx != docstring::npos)
92                 return ltrim(fname.substr(0, idx));
93         idx = fname.rfind('.');
94         if (idx != docstring::npos && idx + 1 < fname.size())
95                 fname = ltrim(fname.substr(idx + 1));
96         // test if we have a LaTeX Space in front
97         if (fname[0] == '\\')
98                 return fname.substr(2);
99         return rtrim(fname);
100 }
101
102
103 docstring const BibTeXInfo::getAbbreviatedAuthor() const
104 {
105         if (!is_bibtex_) 
106                 return docstring();
107  
108         docstring author = getValueForField("author");
109  
110         if (author.empty()) {
111                 author = getValueForField("editor");
112                 if (author.empty())
113                         return bib_key_;
114         }
115
116         // OK, we've got some names. Let's format them.
117         // Try to split the author list on " and "
118         vector<docstring> const authors =
119                 getVectorFromString(author, from_ascii(" and "));
120         
121         if (authors.size() == 2)
122                 return bformat(_("%1$s and %2$s"),
123                         familyName(authors[0]), familyName(authors[1]));
124
125         if (authors.size() > 2)
126                 return bformat(_("%1$s et al."), familyName(authors[0]));
127
128         return familyName(authors[0]);
129 }
130
131
132 docstring const BibTeXInfo::getYear() const
133 {
134         if (!is_bibtex_) 
135                 return docstring();
136  
137         docstring year = getValueForField("year");
138         if (year.empty())
139                 year = _("No year");
140         return year;
141 }
142
143
144 docstring const BibTeXInfo::getInfo() const
145 {
146         if (!is_bibtex_) {
147                 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
148                 return it->second;
149         }
150  
151         // FIXME
152         // This could be made a lot better using the entryType
153         // field to customize the output based upon entry type.
154         
155         // Search for all possible "required" fields
156         docstring author = getValueForField("author");
157         if (author.empty())
158                 author = getValueForField("editor");
159  
160         docstring year      = getValueForField("year");
161         docstring title     = getValueForField("title");
162         docstring docLoc    = getValueForField("pages");
163         if (docLoc.empty()) {
164                 docLoc = getValueForField("chapter");
165                 if (!docLoc.empty())
166                         docLoc = from_ascii("Ch. ") + docLoc;
167         }       else {
168                 docLoc = from_ascii("pp. ") + docLoc;
169         }
170
171         docstring media = getValueForField("journal");
172         if (media.empty()) {
173                 media = getValueForField("publisher");
174                 if (media.empty()) {
175                         media = getValueForField("school");
176                         if (media.empty())
177                                 media = getValueForField("institution");
178                 }
179         }
180         docstring volume = getValueForField("volume");
181
182         odocstringstream result;
183         if (!author.empty())
184                 result << author << ", ";
185         if (!title.empty())
186                 result << title;
187         if (!media.empty())
188                 result << ", " << media;
189         if (!year.empty())
190                 result << ", " << year;
191         if (!docLoc.empty())
192                 result << ", " << docLoc;
193
194         docstring const result_str = rtrim(result.str());
195         if (!result_str.empty())
196                 return result_str;
197
198         // This should never happen (or at least be very unusual!)
199         return docstring();
200 }
201
202
203 //////////////////////////////////////////////////////////////////////
204 //
205 // BiblioInfo
206 //
207 //////////////////////////////////////////////////////////////////////
208
209 namespace {
210 // A functor for use with sort, leading to case insensitive sorting
211         class compareNoCase: public binary_function<docstring, docstring, bool>
212         {
213                 public:
214                         bool operator()(docstring const & s1, docstring const & s2) const {
215                                 return compare_no_case(s1, s2) < 0;
216                         }
217         };
218 } // namespace anon
219
220
221 vector<docstring> const BiblioInfo::getKeys() const
222 {
223         vector<docstring> bibkeys;
224         BiblioInfo::const_iterator it  = begin();
225         for (; it != end(); ++it)
226                 bibkeys.push_back(it->first);
227         sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
228         return bibkeys;
229 }
230
231
232 vector<docstring> const BiblioInfo::getFields() const
233 {
234         vector<docstring> bibfields;
235         set<docstring>::const_iterator it = field_names_.begin();
236         set<docstring>::const_iterator end = field_names_.end();
237         for (; it != end; ++it)
238                 bibfields.push_back(*it);
239         sort(bibfields.begin(), bibfields.end());
240         return bibfields;
241 }
242
243
244 vector<docstring> const BiblioInfo::getEntries() const
245 {
246         vector<docstring> bibentries;
247         set<docstring>::const_iterator it = entry_types_.begin();
248         set<docstring>::const_iterator end = entry_types_.end();
249         for (; it != end; ++it)
250                 bibentries.push_back(*it);
251         sort(bibentries.begin(), bibentries.end());
252         return bibentries;
253 }
254
255
256 docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key) const
257 {
258         BiblioInfo::const_iterator it = find(key);
259         if (it == end())
260                 return docstring();
261         BibTeXInfo const & data = it->second;
262         return data.getAbbreviatedAuthor();
263 }
264
265
266 docstring const BiblioInfo::getYear(docstring const & key) const
267 {
268         BiblioInfo::const_iterator it = find(key);
269         if (it == end())
270                 return docstring();
271         BibTeXInfo const & data = it->second;
272         return data.getYear();
273 }
274
275
276 docstring const BiblioInfo::getInfo(docstring const & key) const
277 {
278         BiblioInfo::const_iterator it = find(key);
279         if (it == end())
280                 return docstring();
281         BibTeXInfo const & data = it->second;
282         return data.getInfo();
283 }
284
285
286 vector<docstring> const BiblioInfo::getCiteStrings(
287         docstring const & key, Buffer const & buf) const
288 {
289         biblio::CiteEngine const engine = buf.params().getEngine();
290         if (engine == biblio::ENGINE_BASIC || 
291             engine == biblio::ENGINE_NATBIB_NUMERICAL)
292                 return getNumericalStrings(key, buf);
293         else
294                 return getAuthorYearStrings(key, buf);
295 }
296
297
298 vector<docstring> const BiblioInfo::getNumericalStrings(
299         docstring const & key, Buffer const & buf) const
300 {
301         if (empty())
302                 return vector<docstring>();
303
304         docstring const author = getAbbreviatedAuthor(key);
305         docstring const year   = getYear(key);
306         if (author.empty() || year.empty())
307                 return vector<docstring>();
308
309         vector<biblio::CiteStyle> const & styles = 
310                 biblio::getCiteStyles(buf.params().getEngine());
311         
312         vector<docstring> vec(styles.size());
313         for (vector<docstring>::size_type i = 0; i != vec.size(); ++i) {
314                 docstring str;
315
316                 switch (styles[i]) {
317                         case biblio::CITE:
318                         case biblio::CITEP:
319                                 str = from_ascii("[#ID]");
320                                 break;
321
322                         case biblio::NOCITE:
323                                 str = _("Add to bibliography only.");
324                                 break;
325
326                         case biblio::CITET:
327                                 str = author + " [#ID]";
328                                 break;
329
330                         case biblio::CITEALT:
331                                 str = author + " #ID";
332                                 break;
333
334                         case biblio::CITEALP:
335                                 str = from_ascii("#ID");
336                                 break;
337
338                         case biblio::CITEAUTHOR:
339                                 str = author;
340                                 break;
341
342                         case biblio::CITEYEAR:
343                                 str = year;
344                                 break;
345
346                         case biblio::CITEYEARPAR:
347                                 str = '(' + year + ')';
348                                 break;
349                 }
350
351                 vec[i] = str;
352         }
353
354         return vec;
355 }
356
357
358 vector<docstring> const BiblioInfo::getAuthorYearStrings(
359         docstring const & key, Buffer const & buf) const
360 {
361         if (empty())
362                 return vector<docstring>();
363
364         docstring const author = getAbbreviatedAuthor(key);
365         docstring const year   = getYear(key);
366         if (author.empty() || year.empty())
367                 return vector<docstring>();
368
369         vector<biblio::CiteStyle> const & styles = 
370                 getCiteStyles(buf.params().getEngine());
371         
372         vector<docstring> vec(styles.size());
373         for (vector<docstring>::size_type i = 0; i != vec.size(); ++i) {
374                 docstring str;
375
376                 switch (styles[i]) {
377                         case biblio::CITE:
378                 // jurabib only: Author/Annotator
379                 // (i.e. the "before" field, 2nd opt arg)
380                                 str = author + "/<" + _("before") + '>';
381                                 break;
382
383                         case biblio::NOCITE:
384                                 str = _("Add to bibliography only.");
385                                 break;
386
387                         case biblio::CITET:
388                                 str = author + " (" + year + ')';
389                                 break;
390
391                         case biblio::CITEP:
392                                 str = '(' + author + ", " + year + ')';
393                                 break;
394
395                         case biblio::CITEALT:
396                                 str = author + ' ' + year ;
397                                 break;
398
399                         case biblio::CITEALP:
400                                 str = author + ", " + year ;
401                                 break;
402
403                         case biblio::CITEAUTHOR:
404                                 str = author;
405                                 break;
406
407                         case biblio::CITEYEAR:
408                                 str = year;
409                                 break;
410
411                         case biblio::CITEYEARPAR:
412                                 str = '(' + year + ')';
413                                 break;
414                 }
415                 vec[i] = str;
416         }
417         return vec;
418 }
419
420
421 void BiblioInfo::fillWithBibKeys(Buffer const * const buf)
422 {       
423         /// if this is a child document and the parent is already loaded
424         /// use the parent's list instead  [ale990412]
425         Buffer const * const tmp = buf->masterBuffer();
426         BOOST_ASSERT(tmp);
427         if (tmp != buf) {
428                 this->fillWithBibKeys(tmp);
429                 return;
430         }
431
432         // Pre-load all child documents.
433         buf->loadChildDocuments();
434
435         for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
436                 it->fillWithBibKeys(*buf, *this, it);
437 }
438
439
440 namespace biblio {
441
442 //////////////////////////////////////////////////////////////////////
443 //
444 // CitationStyle
445 //
446 //////////////////////////////////////////////////////////////////////
447
448 namespace {
449
450
451 char const * const citeCommands[] = {
452         "cite", "nocite", "citet", "citep", "citealt", "citealp",
453         "citeauthor", "citeyear", "citeyearpar" };
454
455 unsigned int const nCiteCommands =
456                 sizeof(citeCommands) / sizeof(char *);
457
458 CiteStyle const citeStyles[] = {
459         CITE, NOCITE, CITET, CITEP, CITEALT,
460 CITEALP, CITEAUTHOR, CITEYEAR, CITEYEARPAR };
461
462 unsigned int const nCiteStyles =
463                 sizeof(citeStyles) / sizeof(CiteStyle);
464
465 CiteStyle const citeStylesFull[] = {
466         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
467
468 unsigned int const nCiteStylesFull =
469                 sizeof(citeStylesFull) / sizeof(CiteStyle);
470
471 CiteStyle const citeStylesUCase[] = {
472         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
473
474 unsigned int const nCiteStylesUCase =
475         sizeof(citeStylesUCase) / sizeof(CiteStyle);
476
477 } // namespace anon
478
479
480 CitationStyle::CitationStyle(string const & command)
481         : style(CITE), full(false), forceUCase(false)
482 {
483         if (command.empty())
484                 return;
485
486         string cmd = command;
487         if (cmd[0] == 'C') {
488                 forceUCase = true;
489                 cmd[0] = 'c';
490         }
491
492         string::size_type const n = cmd.size() - 1;
493         if (cmd != "cite" && cmd[n] == '*') {
494                 full = true;
495                 cmd = cmd.substr(0,n);
496         }
497
498         char const * const * const last = citeCommands + nCiteCommands;
499         char const * const * const ptr = find(citeCommands, last, cmd);
500
501         if (ptr != last) {
502                 size_t idx = ptr - citeCommands;
503                 style = citeStyles[idx];
504         }
505 }
506
507
508 string const CitationStyle::asLatexStr() const
509 {
510         string cite = citeCommands[style];
511         if (full) {
512                 CiteStyle const * last = citeStylesFull + nCiteStylesFull;
513                 if (find(citeStylesFull, last, style) != last)
514                         cite += '*';
515         }
516
517         if (forceUCase) {
518                 CiteStyle const * last = citeStylesUCase + nCiteStylesUCase;
519                 if (find(citeStylesUCase, last, style) != last)
520                         cite[0] = 'C';
521         }
522
523         return cite;
524 }
525
526
527 vector<CiteStyle> const getCiteStyles(CiteEngine const engine)
528 {
529         unsigned int nStyles = 0;
530         unsigned int start = 0;
531
532         switch (engine) {
533                 case ENGINE_BASIC:
534                         nStyles = 2;
535                         start = 0;
536                         break;
537                 case ENGINE_NATBIB_AUTHORYEAR:
538                 case ENGINE_NATBIB_NUMERICAL:
539                         nStyles = nCiteStyles - 1;
540                         start = 1;
541                         break;
542                 case ENGINE_JURABIB:
543                         nStyles = nCiteStyles;
544                         start = 0;
545                         break;
546         }
547
548         typedef vector<CiteStyle> cite_vec;
549
550         cite_vec styles(nStyles);
551         size_t i = 0;
552         int j = start;
553         for (; i != styles.size(); ++i, ++j)
554                 styles[i] = citeStyles[j];
555
556         return styles;
557 }
558
559 } // namespace biblio
560 } // namespace lyx
561