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