]> git.lyx.org Git - lyx.git/blob - src/insets/insetcite.C
Make it compile when USE_BOOST_FORMAT is unset
[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 Voss
8  *
9  * Full author contact details are available in file CREDITS
10  */
11
12 #include <config.h>
13
14 #ifdef __GNUG__
15 #pragma implementation
16 #endif
17
18 #include "insetcite.h"
19 #include "buffer.h"
20 #include "BufferView.h"
21 #include "LaTeXFeatures.h"
22 #include "frontends/LyXView.h"
23 #include "debug.h"
24 #include "gettext.h"
25
26 #include "frontends/controllers/biblio.h"
27 #include "frontends/Dialogs.h"
28
29 #include "support/filetools.h"
30 #include "support/lstrings.h"
31 #include "support/path.h"
32 #include "support/os.h"
33 #include "support/lstrings.h"
34 #include "support/LAssert.h"
35
36 #include <map>
37
38 using std::ostream;
39 using std::vector;
40 using std::map;
41
42 namespace {
43
44 // An optimisation. We assume that until the first InsetCitation::edit is
45 // called, we're loading the buffer and that, therefore, we don't need to
46 // reload the bibkey list
47 std::map<Buffer const *, bool> loading_buffer;
48
49 string const getNatbibLabel(Buffer const * buffer,
50                             string const & citeType, string const & keyList,
51                             string const & before, string const & after,
52                             bool numerical)
53 {
54         typedef std::map<Buffer const *, biblio::InfoMap> CachedMap;
55         static CachedMap cached_keys;
56
57         // Only load the bibkeys once if we're loading up the buffer,
58         // else load them afresh each time.
59         map<Buffer const *, bool>::iterator lit = loading_buffer.find(buffer);
60         if (lit == loading_buffer.end())
61                 loading_buffer[buffer] = true;
62
63         bool loadkeys = !loading_buffer[buffer];
64         if (!loadkeys) {
65                 CachedMap::iterator kit = cached_keys.find(buffer);
66                 loadkeys = kit == cached_keys.end();
67         }
68
69         if (loadkeys) {
70                 // build the keylist
71                 typedef vector<std::pair<string, string> > InfoType;
72                 InfoType bibkeys = buffer->getBibkeyList();
73
74                 InfoType::const_iterator bit  = bibkeys.begin();
75                 InfoType::const_iterator bend = bibkeys.end();
76
77                 biblio::InfoMap infomap;
78                 for (; bit != bend; ++bit) {
79                         infomap[bit->first] = bit->second;
80                 }
81                 if (infomap.empty())
82                         return string();
83
84                 cached_keys[buffer] = infomap;
85         }
86
87         biblio::InfoMap infomap = cached_keys[buffer];
88
89         // the natbib citation-styles
90         // CITET:       author (year)
91         // CITEP:       (author,year)
92         // CITEALT:     author year
93         // CITEALP:     author, year
94         // CITEAUTHOR:  author
95         // CITEYEAR:    year
96         // CITEYEARPAR: (year)
97
98         // We don't currently use the full or forceUCase fields.
99         // bool const forceUCase = citeType[0] == 'C';
100         bool const full = citeType[citeType.size() - 1] == '*';
101
102         string const cite_type = full ?
103                 ascii_lowercase(citeType.substr(0, citeType.size() - 1)) :
104                 ascii_lowercase(citeType);
105
106         string before_str;
107         if (!before.empty()) {
108                 // In CITET and CITEALT mode, the "before" string is
109                 // attached to the label associated with each and every key.
110                 // In CITEP, CITEALP and CITEYEARPAR mode, it is attached
111                 // to the front of the whole only.
112                 // In other modes, it is not used at all.
113                 if (cite_type == "citet" ||
114                     cite_type == "citealt" ||
115                     cite_type == "citep" ||
116                     cite_type == "citealp" ||
117                     cite_type == "citeyearpar")
118                         before_str = before + ' ';
119         }
120
121         string after_str;
122         if (!after.empty()) {
123                 // The "after" key is appended only to the end of the whole.
124                 after_str = ", " + after;
125         }
126
127         // One day, these might be tunable (as they are in BibTeX).
128         char const op  = '('; // opening parenthesis.
129         char const cp  = ')'; // closing parenthesis.
130         // puctuation mark separating citation entries.
131         char const * const sep = ";";
132
133         string const op_str(string(1, ' ')  + string(1, op));
134         string const cp_str(string(1, cp)   + string(1, ' '));
135         string const sep_str(string(sep) + " ");
136
137         string label;
138         vector<string> keys = getVectorFromString(keyList);
139         vector<string>::const_iterator it  = keys.begin();
140         vector<string>::const_iterator end = keys.end();
141         for (; it != end; ++it) {
142                 // get the bibdata corresponding to the key
143                 string const author(biblio::getAbbreviatedAuthor(infomap, *it));
144                 string const year(biblio::getYear(infomap, *it));
145
146                 // Something isn't right. Fail safely.
147                 if (author.empty() || year.empty())
148                         return string();
149
150                 // (authors1 (<before> year);  ... ;
151                 //  authors_last (<before> year, <after>)
152                 if (cite_type == "citet") {
153                         string const tmp = numerical ? '#' + *it : year;
154                         label += author + op_str + before_str + tmp +
155                                 cp + sep_str;
156
157                 // author, year; author, year; ...
158                 } else if (cite_type == "citep" ||
159                            cite_type == "citealp") {
160                         if (numerical) {
161                                 label += *it + sep_str;
162                         } else {
163                                 label += author + ", " + year + sep_str;
164                         }
165
166                 // (authors1 <before> year;
167                 //  authors_last <before> year, <after>)
168                 } else if (cite_type == "citealt") {
169                         string const tmp = numerical ? '#' + *it : year;
170                         label += author + ' ' + before_str + tmp + sep_str;
171
172                 // author; author; ...
173                 } else if (cite_type == "citeauthor") {
174                         label += author + sep_str;
175
176                 // year; year; ...
177                 } else if (cite_type == "citeyear" ||
178                            cite_type == "citeyearpar") {
179                         label += year + sep_str;
180                 }
181         }
182         label = rtrim(rtrim(label), sep);
183
184         if (!after_str.empty()) {
185                 if (cite_type == "citet") {
186                         // insert "after" before last ')'
187                         label.insert(label.size() - 1, after_str);
188                 } else {
189                         bool const add = !(numerical &&
190                                            (cite_type == "citeauthor" ||
191                                             cite_type == "citeyear"));
192                         if (add)
193                                 label += after_str;
194                 }
195         }
196
197         if (!before_str.empty() && (cite_type == "citep" ||
198                                     cite_type == "citealp" ||
199                                     cite_type == "citeyearpar")) {
200                 label = before_str + label;
201         }
202
203         if (cite_type == "citep" || cite_type == "citeyearpar")
204                 label = string(1, op) + label + string(1, cp);
205
206         return label;
207 }
208
209
210 string const getBasicLabel(string const & keyList, string const & after)
211 {
212         string keys(keyList);
213         string label;
214
215         if (contains(keys, ",")) {
216                 // Final comma allows while loop to cover all keys
217                 keys = ltrim(split(keys, label, ',')) + ",";
218                 while (contains(keys, ",")) {
219                         string key;
220                         keys = ltrim(split(keys, key, ','));
221                         label += ", " + key;
222                 }
223         } else
224                 label = keys;
225
226         if (!after.empty())
227                 label += ", " + after;
228
229         return "[" + label + "]";
230 }
231
232 } // anon namespace
233
234
235 InsetCitation::InsetCitation(InsetCommandParams const & p, bool)
236         : InsetCommand(p)
237 {}
238
239
240 string const InsetCitation::generateLabel(Buffer const * buffer) const
241 {
242         string const before = string();
243         string const after  = getOptions();
244
245         string label;
246         if (buffer->params.use_natbib) {
247                 string cmd = getCmdName();
248                 if (cmd == "cite") {
249                         // We may be "upgrading" from an older LyX version.
250                         // If, however, we use "cite" because the necessary
251                         // author/year info is not present in the biblio
252                         // database, then getNatbibLabel will exit gracefully
253                         // and we'll call getBasicLabel.
254                         if (buffer->params.use_numerical_citations)
255                                 cmd = "citep";
256                         else
257                                 cmd = "citet";
258                 }
259                 label = getNatbibLabel(buffer, cmd, getContents(),
260                                        before, after,
261                                        buffer->params.use_numerical_citations);
262         }
263
264         // Fallback to fail-safe
265         if (label.empty()) {
266                 label = getBasicLabel(getContents(), after);
267         }
268
269         return label;
270 }
271
272
273 InsetCitation::Cache::Style InsetCitation::getStyle(Buffer const * buffer) const
274 {
275         Cache::Style style = Cache::BASIC;
276
277         if (buffer->params.use_natbib) {
278                 if (buffer->params.use_numerical_citations) {
279                         style = Cache::NATBIB_NUM;
280                 } else {
281                         style = Cache::NATBIB_AY;
282                 }
283         }
284
285         return style;
286 }
287
288
289 string const InsetCitation::getScreenLabel(Buffer const * buffer) const
290 {
291         Cache::Style const style = getStyle(buffer);
292         if (cache.params == params() && cache.style == style)
293                 return cache.screen_label;
294
295         // The label has changed, so we have to re-create it.
296         string const before = string();
297         string const after  = getOptions();
298
299         string const glabel = generateLabel(buffer);
300
301         unsigned int const maxLabelChars = 45;
302
303         string label = glabel;
304         if (label.size() > maxLabelChars) {
305                 label.erase(maxLabelChars-3);
306                 label += "...";
307         }
308
309         cache.style  = style;
310         cache.params = params();
311         cache.generated_label = glabel;
312         cache.screen_label = label;
313
314         return label;
315 }
316
317
318 void InsetCitation::setLoadingBuffer(Buffer const * buffer, bool state) const
319 {
320         // Doesn't matter if there is no bv->buffer() entry in the map.
321         loading_buffer[buffer] = state;
322 }
323
324
325 void InsetCitation::edit(BufferView * bv, int, int, mouse_button::state)
326 {
327         // A call to edit() indicates that we're no longer loading the
328         // buffer but doing some real work.
329         setLoadingBuffer(bv->buffer(), false);
330
331         bv->owner()->getDialogs().showCitation(this);
332 }
333
334
335 void InsetCitation::edit(BufferView * bv, bool)
336 {
337         edit(bv, 0, 0, mouse_button::none);
338 }
339
340
341 int InsetCitation::ascii(Buffer const * buffer, ostream & os, int) const
342 {
343         string label;
344
345         if (cache.params == params() && cache.style == getStyle(buffer))
346                 label = cache.generated_label;
347         else
348                 label = generateLabel(buffer);
349
350         os << label;
351         return 0;
352 }
353
354
355 // Have to overwrite the default InsetCommand method in order to check that
356 // the \cite command is valid. Eg, the user has natbib enabled, inputs some
357 // citations and then changes his mind, turning natbib support off. The output
358 // should revert to \cite[]{}
359 int InsetCitation::latex(Buffer const * buffer, ostream & os,
360                         bool /*fragile*/, bool/*fs*/) const
361 {
362         os << "\\";
363         if (buffer->params.use_natbib)
364                 os << getCmdName();
365         else
366                 os << "cite";
367
368 #warning What is this code supposed to do? (Lgb)
369 // my guess is that this is just waiting for when we support before,
370 // so it's a oneliner. But this is very silly ! - jbl
371
372 #if 1
373         // The current strange code
374
375         string const before = string();
376         string const after  = getOptions();
377         if (!before.empty() && buffer->params.use_natbib)
378                 os << "[" << before << "][" << after << "]";
379         else if (!after.empty())
380                 os << "[" << after << "]";
381 #else
382         // and the cleaned up equvalent, should it just be changed? (Lgb)
383         string const after  = getOptions();
384         if (!after.empty())
385                 os << "[" << after << "]";
386 #endif
387         string::const_iterator it  = getContents().begin();
388         string::const_iterator end = getContents().end();
389         // Paranoia check: make sure that there is no whitespace in here
390         string content;
391         char last = ',';
392         for (; it != end; ++it) {
393                 if (*it != ' ')
394                         last = *it;
395                 if (*it != ' ' || last != ',')
396                         content += *it;
397         }
398
399         os << "{" << content << "}";
400
401         return 0;
402 }
403
404
405 void InsetCitation::validate(LaTeXFeatures & features) const
406 {
407         if (features.bufferParams().use_natbib)
408                 features.require("natbib");
409 }