]> git.lyx.org Git - lyx.git/blob - src/insets/insetcite.C
a new biblio::asValidLatexString helper function.
[lyx.git] / src / insets / insetcite.C
1 /**
2  * \file insetcite.C
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  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "insetcite.h"
15
16 #include "buffer.h"
17 #include "bufferparams.h"
18 #include "BufferView.h"
19 #include "dispatchresult.h"
20 #include "funcrequest.h"
21 #include "LaTeXFeatures.h"
22
23 #include "frontends/controllers/biblio.h"
24
25 #include "support/lstrings.h"
26
27 using lyx::support::ascii_lowercase;
28 using lyx::support::contains;
29 using lyx::support::getVectorFromString;
30 using lyx::support::ltrim;
31 using lyx::support::rtrim;
32 using lyx::support::split;
33
34 using std::string;
35 using std::ostream;
36 using std::vector;
37 using std::map;
38
39
40 namespace {
41
42 string const getNatbibLabel(Buffer const & buffer,
43                             string const & citeType, string const & keyList,
44                             string const & before, string const & after,
45                             biblio::CiteEngine engine)
46 {
47         // Only start the process off after the buffer is loaded from file.
48         if (!buffer.fully_loaded())
49                 return string();
50
51         typedef std::map<Buffer const *, biblio::InfoMap> CachedMap;
52         static CachedMap cached_keys;
53
54         // build the keylist
55         typedef vector<std::pair<string, string> > InfoType;
56         InfoType bibkeys;
57         buffer.fillWithBibKeys(bibkeys);
58
59         InfoType::const_iterator bit  = bibkeys.begin();
60         InfoType::const_iterator bend = bibkeys.end();
61
62         biblio::InfoMap infomap;
63         for (; bit != bend; ++bit) {
64                 infomap[bit->first] = bit->second;
65         }
66         if (infomap.empty())
67                 return string();
68
69         cached_keys[&buffer] = infomap;
70
71         // the natbib citation-styles
72         // CITET:       author (year)
73         // CITEP:       (author,year)
74         // CITEALT:     author year
75         // CITEALP:     author, year
76         // CITEAUTHOR:  author
77         // CITEYEAR:    year
78         // CITEYEARPAR: (year)
79         // jurabib supports these plus
80         // CITE:        author/<before field>
81
82         // We don't currently use the full or forceUCase fields.
83         string cite_type = biblio::asValidLatexCommand(citeType, engine);
84         if (cite_type[0] == 'C')
85                 cite_type = string(1, 'c') + cite_type.substr(1);
86         if (cite_type[cite_type.size() - 1] == '*')
87                 cite_type = cite_type.substr(0, cite_type.size() - 1);
88
89         string before_str;
90         if (!before.empty()) {
91                 // In CITET and CITEALT mode, the "before" string is
92                 // attached to the label associated with each and every key.
93                 // In CITEP, CITEALP and CITEYEARPAR mode, it is attached
94                 // to the front of the whole only.
95                 // In other modes, it is not used at all.
96                 if (cite_type == "citet" ||
97                     cite_type == "citealt" ||
98                     cite_type == "citep" ||
99                     cite_type == "citealp" ||
100                     cite_type == "citeyearpar")
101                         before_str = before + ' ';
102                 // In CITE (jurabib), the "before" string is used to attach
103                 // the annotator (of legal texts) to the author(s) of the
104                 // first reference.
105                 else if (cite_type == "cite")
106                         before_str = '/' + before;
107         }
108
109         string after_str;
110         if (!after.empty()) {
111                 // The "after" key is appended only to the end of the whole.
112                 after_str = ", " + after;
113         }
114
115         // One day, these might be tunable (as they are in BibTeX).
116         char const op  = '('; // opening parenthesis.
117         char const cp  = ')'; // closing parenthesis.
118         // puctuation mark separating citation entries.
119         char const * const sep = ";";
120
121         string const op_str(' ' + string(1, op));
122         string const cp_str(string(1, cp) + ' ');
123         string const sep_str(string(sep) + ' ');
124
125         string label;
126         vector<string> keys = getVectorFromString(keyList);
127         vector<string>::const_iterator it  = keys.begin();
128         vector<string>::const_iterator end = keys.end();
129         for (; it != end; ++it) {
130                 // get the bibdata corresponding to the key
131                 string const author(biblio::getAbbreviatedAuthor(infomap, *it));
132                 string const year(biblio::getYear(infomap, *it));
133
134                 // Something isn't right. Fail safely.
135                 if (author.empty() || year.empty())
136                         return string();
137
138                 // authors1/<before>;  ... ;
139                 //  authors_last, <after>
140                 if (cite_type == "cite" && engine == biblio::ENGINE_JURABIB) {
141                         if (it == keys.begin())
142                                 label += author + before_str + sep_str;
143                         else
144                                 label += author + sep_str;
145
146                 // (authors1 (<before> year);  ... ;
147                 //  authors_last (<before> year, <after>)
148                 } else if (cite_type == "citet") {
149                         switch (engine) {
150                         case biblio::ENGINE_NATBIB_AUTHORYEAR:
151                                 label += author + op_str + before_str +
152                                         year + cp + sep_str;
153                                 break;
154                         case biblio::ENGINE_NATBIB_NUMERICAL:
155                                 label += author + op_str + before_str +
156                                         '#' + *it + cp + sep_str;
157                                 break;
158                         case biblio::ENGINE_JURABIB:
159                                 label += before_str + author + op_str +
160                                         year + cp + sep_str;
161                                 break;
162                         case biblio::ENGINE_BASIC:
163                                 break;
164                         }
165
166                 // author, year; author, year; ...
167                 } else if (cite_type == "citep" ||
168                            cite_type == "citealp") {
169                         if (engine == biblio::ENGINE_NATBIB_NUMERICAL) {
170                                 label += *it + sep_str;
171                         } else {
172                                 label += author + ", " + year + sep_str;
173                         }
174
175                 // (authors1 <before> year;
176                 //  authors_last <before> year, <after>)
177                 } else if (cite_type == "citealt") {
178                         switch (engine) {
179                         case biblio::ENGINE_NATBIB_AUTHORYEAR:
180                                 label += author + ' ' + before_str +
181                                         year + sep_str;
182                                 break;
183                         case biblio::ENGINE_NATBIB_NUMERICAL:
184                                 label += author + ' ' + before_str +
185                                         '#' + *it + sep_str;
186                                 break;
187                         case biblio::ENGINE_JURABIB:
188                                 label += before_str + author + ' ' +
189                                         year + sep_str;
190                                 break;
191                         case biblio::ENGINE_BASIC:
192                                 break;
193                         }
194
195                 // author; author; ...
196                 } else if (cite_type == "citeauthor") {
197                         label += author + sep_str;
198
199                 // year; year; ...
200                 } else if (cite_type == "citeyear" ||
201                            cite_type == "citeyearpar") {
202                         label += year + sep_str;
203                 }
204         }
205         label = rtrim(rtrim(label), sep);
206
207         if (!after_str.empty()) {
208                 if (cite_type == "citet") {
209                         // insert "after" before last ')'
210                         label.insert(label.size() - 1, after_str);
211                 } else {
212                         bool const add =
213                                 !(engine == biblio::ENGINE_NATBIB_NUMERICAL &&
214                                   (cite_type == "citeauthor" ||
215                                    cite_type == "citeyear"));
216                         if (add)
217                                 label += after_str;
218                 }
219         }
220
221         if (!before_str.empty() && (cite_type == "citep" ||
222                                     cite_type == "citealp" ||
223                                     cite_type == "citeyearpar")) {
224                 label = before_str + label;
225         }
226
227         if (cite_type == "citep" || cite_type == "citeyearpar")
228                 label = string(1, op) + label + string(1, cp);
229
230         return label;
231 }
232
233
234 string const getBasicLabel(string const & keyList, string const & after)
235 {
236         string keys(keyList);
237         string label;
238
239         if (contains(keys, ',')) {
240                 // Final comma allows while loop to cover all keys
241                 keys = ltrim(split(keys, label, ',')) + ',';
242                 while (contains(keys, ',')) {
243                         string key;
244                         keys = ltrim(split(keys, key, ','));
245                         label += ", " + key;
246                 }
247         } else
248                 label = keys;
249
250         if (!after.empty())
251                 label += ", " + after;
252
253         return '[' + label + ']';
254 }
255
256 } // anon namespace
257
258
259 InsetCitation::InsetCitation(InsetCommandParams const & p)
260         : InsetCommand(p, "citation")
261 {}
262
263
264 string const InsetCitation::generateLabel(Buffer const & buffer) const
265 {
266         string const before = getSecOptions();
267         string const after  = getOptions();
268
269         string label;
270         biblio::CiteEngine const engine = buffer.params().cite_engine;
271         if (engine != biblio::ENGINE_BASIC) {
272                 label = getNatbibLabel(buffer, getCmdName(), getContents(),
273                                        before, after, engine);
274         }
275
276         // Fallback to fail-safe
277         if (label.empty()) {
278                 label = getBasicLabel(getContents(), after);
279         }
280
281         return label;
282 }
283
284
285 string const InsetCitation::getScreenLabel(Buffer const & buffer) const
286 {
287         biblio::CiteEngine const engine = biblio::getEngine(buffer);
288         if (cache.params == params() && cache.engine == engine)
289                 return cache.screen_label;
290
291         // The label has changed, so we have to re-create it.
292         string const before = getSecOptions();
293         string const after  = getOptions();
294
295         string const glabel = generateLabel(buffer);
296
297         unsigned int const maxLabelChars = 45;
298
299         string label = glabel;
300         if (label.size() > maxLabelChars) {
301                 label.erase(maxLabelChars-3);
302                 label += "...";
303         }
304
305         cache.engine  = engine;
306         cache.params = params();
307         cache.generated_label = glabel;
308         cache.screen_label = label;
309
310         return label;
311 }
312
313
314 int InsetCitation::plaintext(Buffer const & buffer, ostream & os, int) const
315 {
316         if (cache.params == params() &&
317             cache.engine == biblio::getEngine(buffer))
318                 os << cache.generated_label;
319         else
320                 os << generateLabel(buffer);
321         return 0;
322 }
323
324
325 // Have to overwrite the default InsetCommand method in order to check that
326 // the \cite command is valid. Eg, the user has natbib enabled, inputs some
327 // citations and then changes his mind, turning natbib support off. The output
328 // should revert to \cite[]{}
329 int InsetCitation::latex(Buffer const & buffer, ostream & os,
330                          OutputParams const &) const
331 {
332         biblio::CiteEngine const cite_engine = buffer.params().cite_engine;
333         string const cite_str =
334                 biblio::asValidLatexCommand(getCmdName(), cite_engine);
335         
336         os << "\\" << cite_str;
337         
338         string const before = getSecOptions();
339         string const after  = getOptions();
340         if (!before.empty() && cite_engine != biblio::ENGINE_BASIC)
341                 os << '[' << before << "][" << after << ']';
342         else if (!after.empty())
343                 os << '[' << after << ']';
344
345         string::const_iterator it  = getContents().begin();
346         string::const_iterator end = getContents().end();
347         // Paranoia check: make sure that there is no whitespace in here
348         string content;
349         char last = ',';
350         for (; it != end; ++it) {
351                 if (*it != ' ')
352                         last = *it;
353                 if (*it != ' ' || last != ',')
354                         content += *it;
355         }
356
357         os << '{' << content << '}';
358
359         return 0;
360 }
361
362
363 void InsetCitation::validate(LaTeXFeatures & features) const
364 {
365         switch (features.bufferParams().cite_engine) {
366         case biblio::ENGINE_BASIC:
367                 break;
368         case biblio::ENGINE_NATBIB_AUTHORYEAR:
369         case biblio::ENGINE_NATBIB_NUMERICAL:
370                 features.require("natbib");
371                 break;
372         case biblio::ENGINE_JURABIB:
373                 features.require("jurabib");
374                 break;
375         }
376 }