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