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