3 * Copyright 2001 the LyX Team
4 * Read the file COPYING
6 * \author Angus Leeming, a.leeming@ic.ac.uk
7 * \author Herbert Voss, voss@lyx.org 2002-03-17
13 #pragma implementation
16 #include "insetcite.h"
18 #include "BufferView.h"
19 #include "LaTeXFeatures.h"
20 #include "frontends/LyXView.h"
24 #include "frontends/controllers/biblio.h"
25 #include "frontends/Dialogs.h"
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"
42 // An optimisation. We assume that until the first InsetCitation::edit is
43 // called, we're loading the buffer and that, therefore, we don't need to
44 // reload the bibkey list
45 std::map<Buffer const *, bool> loading_buffer;
47 string const getNatbibLabel(Buffer const * buffer,
48 string const & citeType, string const & keyList,
49 string const & before, string const & after,
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;
57 typedef std::map<Buffer const *, biblio::InfoMap> CachedMap;
58 static CachedMap cached_keys;
60 CachedMap::iterator kit = cached_keys.find(buffer);
62 if (!loading_buffer[buffer] || kit == cached_keys.end()) {
64 typedef vector<std::pair<string, string> > InfoType;
65 InfoType bibkeys = buffer->getBibkeyList();
67 InfoType::const_iterator bit = bibkeys.begin();
68 InfoType::const_iterator bend = bibkeys.end();
70 biblio::InfoMap infomap;
71 for (; bit != bend; ++bit) {
72 infomap[bit->first] = bit->second;
77 cached_keys[buffer] = infomap;
80 biblio::InfoMap infomap = cached_keys[buffer];
82 // the natbib citation-styles
83 // CITET: author (year)
84 // CITEP: (author,year)
85 // CITEALT: author year
86 // CITEALP: author, year
89 // CITEYEARPAR: (year)
91 // We don't currently use the full or forceUCase fields.
92 // bool const forceUCase = citeType[0] == 'C';
93 bool const full = citeType[citeType.size() - 1] == '*';
95 string const cite_type = full ?
96 ascii_lowercase(citeType.substr(0, citeType.size() - 1)) :
97 ascii_lowercase(citeType);
100 if (!before.empty()) {
101 // In CITET and CITEALT mode, the "before" string is
102 // attached to the label associated with each and every key.
103 // In CITEP, CITEALP and CITEYEARPAR mode, it is attached
104 // to the front of the whole only.
105 // In other modes, it is not used at all.
106 if (cite_type == "citet" ||
107 cite_type == "citealt" ||
108 cite_type == "citep" ||
109 cite_type == "citealp" ||
110 cite_type == "citeyearpar")
111 before_str = before + ' ';
115 if (!after.empty()) {
116 // The "after" key is appended only to the end of the whole.
117 after_str = ", " + after;
120 // One day, these might be tunable (as they are in BibTeX).
121 char const op = '('; // opening parenthesis.
122 char const cp = ')'; // closing parenthesis.
123 // puctuation mark separating citation entries.
124 char const * const sep = ";";
126 string const op_str(string(1, ' ') + string(1, op));
127 string const cp_str(string(1, cp) + string(1, ' '));
128 string const sep_str(string(sep) + " ");
131 vector<string> keys = getVectorFromString(keyList);
132 vector<string>::const_iterator it = keys.begin();
133 vector<string>::const_iterator end = keys.end();
134 for (; it != end; ++it) {
135 // get the bibdata corresponding to the key
136 string const author(biblio::getAbbreviatedAuthor(infomap, *it));
137 string const year(biblio::getYear(infomap, *it));
139 // Something isn't right. Fail safely.
140 if (author.empty() || year.empty())
143 // (authors1 (<before> year); ... ;
144 // authors_last (<before> year, <after>)
145 if (cite_type == "citet") {
146 string const tmp = numerical ? '#' + *it : year;
147 label += author + op_str + before_str + tmp +
150 // author, year; author, year; ...
151 } else if (cite_type == "citep" ||
152 cite_type == "citealp") {
154 label += *it + sep_str;
156 label += author + ", " + year + sep_str;
159 // (authors1 <before> year;
160 // authors_last <before> year, <after>)
161 } else if (cite_type == "citealt") {
162 string const tmp = numerical ? '#' + *it : year;
163 label += author + ' ' + before_str + tmp + sep_str;
165 // author; author; ...
166 } else if (cite_type == "citeauthor") {
167 label += author + sep_str;
170 } else if (cite_type == "citeyear" ||
171 cite_type == "citeyearpar") {
172 label += year + sep_str;
175 label = rtrim(rtrim(label), sep);
177 if (!after_str.empty()) {
178 if (cite_type == "citet") {
179 // insert "after" before last ')'
180 label.insert(label.size() - 1, after_str);
182 bool const add = !(numerical &&
183 (cite_type == "citeauthor" ||
184 cite_type == "citeyear"));
190 if (!before_str.empty() && (cite_type == "citep" ||
191 cite_type == "citealp" ||
192 cite_type == "citeyearpar")) {
193 label = before_str + label;
196 if (cite_type == "citep" || cite_type == "citeyearpar")
197 label = string(1, op) + label + string(1, cp);
203 string const getBasicLabel(string const & keyList, string const & after)
205 string keys(keyList);
208 if (contains(keys, ",")) {
209 // Final comma allows while loop to cover all keys
210 keys = ltrim(split(keys, label, ',')) + ",";
211 while (contains(keys, ",")) {
213 keys = ltrim(split(keys, key, ','));
220 label += ", " + after;
222 return "[" + label + "]";
228 InsetCitation::InsetCitation(InsetCommandParams const & p, bool)
233 string const InsetCitation::generateLabel(Buffer const * buffer) const
235 string const before = string();
236 string const after = getOptions();
239 if (buffer->params.use_natbib) {
240 string cmd = getCmdName();
242 // We may be "upgrading" from an older LyX version.
243 // If, however, we use "cite" because the necessary
244 // author/year info is not present in the biblio
245 // database, then getNatbibLabel will exit gracefully
246 // and we'll call getBasicLabel.
247 if (buffer->params.use_numerical_citations)
252 label = getNatbibLabel(buffer, cmd, getContents(),
254 buffer->params.use_numerical_citations);
257 // Fallback to fail-safe
259 label = getBasicLabel(getContents(), after);
266 InsetCitation::Cache::Style InsetCitation::getStyle(Buffer const * buffer) const
268 Cache::Style style = Cache::BASIC;
270 if (buffer->params.use_natbib) {
271 if (buffer->params.use_numerical_citations) {
272 style = Cache::NATBIB_NUM;
274 style = Cache::NATBIB_AY;
282 string const InsetCitation::getScreenLabel(Buffer const * buffer) const
284 Cache::Style const style = getStyle(buffer);
285 if (cache.params == params() && cache.style == style)
286 return cache.screen_label;
288 // The label has changed, so we have to re-create it.
289 string const before = string();
290 string const after = getOptions();
292 string const glabel = generateLabel(buffer);
294 unsigned int const maxLabelChars = 45;
296 string label = glabel;
297 if (label.size() > maxLabelChars) {
298 label.erase(maxLabelChars-3);
303 cache.params = params();
304 cache.generated_label = glabel;
305 cache.screen_label = label;
311 void InsetCitation::edit(BufferView * bv, int, int, mouse_button::state)
313 // A call to edit() indicates that we're no longer loading the
314 // buffer but doing some real work.
315 // Doesn't matter if there is no bv->buffer() entry in the map.
316 loading_buffer[bv->buffer()] = false;
318 bv->owner()->getDialogs().showCitation(this);
322 void InsetCitation::edit(BufferView * bv, bool)
324 edit(bv, 0, 0, mouse_button::none);
328 int InsetCitation::ascii(Buffer const * buffer, ostream & os, int) const
332 if (cache.params == params() && cache.style == getStyle(buffer))
333 label = cache.generated_label;
335 label = generateLabel(buffer);
342 // Have to overwrite the default InsetCommand method in order to check that
343 // the \cite command is valid. Eg, the user has natbib enabled, inputs some
344 // citations and then changes his mind, turning natbib support off. The output
345 // should revert to \cite[]{}
346 int InsetCitation::latex(Buffer const * buffer, ostream & os,
347 bool /*fragile*/, bool/*fs*/) const
350 if (buffer->params.use_natbib)
355 #warning What is this code supposed to do? (Lgb)
356 // my guess is that this is just waiting for when we support before,
357 // so it's a oneliner. But this is very silly ! - jbl
360 // The current strange code
362 string const before = string();
363 string const after = getOptions();
364 if (!before.empty() && buffer->params.use_natbib)
365 os << "[" << before << "][" << after << "]";
366 else if (!after.empty())
367 os << "[" << after << "]";
369 // and the cleaned up equvalent, should it just be changed? (Lgb)
370 string const after = getOptions();
372 os << "[" << after << "]";
374 string::const_iterator it = getContents().begin();
375 string::const_iterator end = getContents().end();
376 // Paranoia check: make sure that there is no whitespace in here
379 for (; it != end; ++it) {
382 if (*it != ' ' || last != ',')
386 os << "{" << content << "}";
392 void InsetCitation::validate(LaTeXFeatures & features) const
394 if (features.bufferParams().use_natbib)
395 features.require("natbib");