3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Angus Leeming
9 * Full author contact details are available in file CREDITS
15 #pragma implementation
18 #include "insetcite.h"
20 #include "BufferView.h"
21 #include "LaTeXFeatures.h"
22 #include "frontends/LyXView.h"
26 #include "frontends/controllers/biblio.h"
27 #include "frontends/Dialogs.h"
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"
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;
49 string const getNatbibLabel(Buffer const * buffer,
50 string const & citeType, string const & keyList,
51 string const & before, string const & after,
54 // Only reload the bibkeys if we have to...
55 map<Buffer const *, bool>::iterator lit = loading_buffer.find(buffer);
56 if (lit != loading_buffer.end())
57 loading_buffer[buffer] = true;
59 typedef std::map<Buffer const *, biblio::InfoMap> CachedMap;
60 static CachedMap cached_keys;
62 CachedMap::iterator kit = cached_keys.find(buffer);
64 if (!loading_buffer[buffer] || kit == cached_keys.end()) {
66 typedef vector<std::pair<string, string> > InfoType;
67 InfoType bibkeys = buffer->getBibkeyList();
69 InfoType::const_iterator bit = bibkeys.begin();
70 InfoType::const_iterator bend = bibkeys.end();
72 biblio::InfoMap infomap;
73 for (; bit != bend; ++bit) {
74 infomap[bit->first] = bit->second;
79 cached_keys[buffer] = infomap;
82 biblio::InfoMap infomap = cached_keys[buffer];
84 // the natbib citation-styles
85 // CITET: author (year)
86 // CITEP: (author,year)
87 // CITEALT: author year
88 // CITEALP: author, year
91 // CITEYEARPAR: (year)
93 // We don't currently use the full or forceUCase fields.
94 // bool const forceUCase = citeType[0] == 'C';
95 bool const full = citeType[citeType.size() - 1] == '*';
97 string const cite_type = full ?
98 ascii_lowercase(citeType.substr(0, citeType.size() - 1)) :
99 ascii_lowercase(citeType);
102 if (!before.empty()) {
103 // In CITET and CITEALT mode, the "before" string is
104 // attached to the label associated with each and every key.
105 // In CITEP, CITEALP and CITEYEARPAR mode, it is attached
106 // to the front of the whole only.
107 // In other modes, it is not used at all.
108 if (cite_type == "citet" ||
109 cite_type == "citealt" ||
110 cite_type == "citep" ||
111 cite_type == "citealp" ||
112 cite_type == "citeyearpar")
113 before_str = before + ' ';
117 if (!after.empty()) {
118 // The "after" key is appended only to the end of the whole.
119 after_str = ", " + after;
122 // One day, these might be tunable (as they are in BibTeX).
123 char const op = '('; // opening parenthesis.
124 char const cp = ')'; // closing parenthesis.
125 // puctuation mark separating citation entries.
126 char const * const sep = ";";
128 string const op_str(string(1, ' ') + string(1, op));
129 string const cp_str(string(1, cp) + string(1, ' '));
130 string const sep_str(string(sep) + " ");
133 vector<string> keys = getVectorFromString(keyList);
134 vector<string>::const_iterator it = keys.begin();
135 vector<string>::const_iterator end = keys.end();
136 for (; it != end; ++it) {
137 // get the bibdata corresponding to the key
138 string const author(biblio::getAbbreviatedAuthor(infomap, *it));
139 string const year(biblio::getYear(infomap, *it));
141 // Something isn't right. Fail safely.
142 if (author.empty() || year.empty())
145 // (authors1 (<before> year); ... ;
146 // authors_last (<before> year, <after>)
147 if (cite_type == "citet") {
148 string const tmp = numerical ? '#' + *it : year;
149 label += author + op_str + before_str + tmp +
152 // author, year; author, year; ...
153 } else if (cite_type == "citep" ||
154 cite_type == "citealp") {
156 label += *it + sep_str;
158 label += author + ", " + year + sep_str;
161 // (authors1 <before> year;
162 // authors_last <before> year, <after>)
163 } else if (cite_type == "citealt") {
164 string const tmp = numerical ? '#' + *it : year;
165 label += author + ' ' + before_str + tmp + sep_str;
167 // author; author; ...
168 } else if (cite_type == "citeauthor") {
169 label += author + sep_str;
172 } else if (cite_type == "citeyear" ||
173 cite_type == "citeyearpar") {
174 label += year + sep_str;
177 label = rtrim(rtrim(label), sep);
179 if (!after_str.empty()) {
180 if (cite_type == "citet") {
181 // insert "after" before last ')'
182 label.insert(label.size() - 1, after_str);
184 bool const add = !(numerical &&
185 (cite_type == "citeauthor" ||
186 cite_type == "citeyear"));
192 if (!before_str.empty() && (cite_type == "citep" ||
193 cite_type == "citealp" ||
194 cite_type == "citeyearpar")) {
195 label = before_str + label;
198 if (cite_type == "citep" || cite_type == "citeyearpar")
199 label = string(1, op) + label + string(1, cp);
205 string const getBasicLabel(string const & keyList, string const & after)
207 string keys(keyList);
210 if (contains(keys, ",")) {
211 // Final comma allows while loop to cover all keys
212 keys = ltrim(split(keys, label, ',')) + ",";
213 while (contains(keys, ",")) {
215 keys = ltrim(split(keys, key, ','));
222 label += ", " + after;
224 return "[" + label + "]";
230 InsetCitation::InsetCitation(InsetCommandParams const & p, bool)
235 string const InsetCitation::generateLabel(Buffer const * buffer) const
237 string const before = string();
238 string const after = getOptions();
241 if (buffer->params.use_natbib) {
242 string cmd = getCmdName();
244 // We may be "upgrading" from an older LyX version.
245 // If, however, we use "cite" because the necessary
246 // author/year info is not present in the biblio
247 // database, then getNatbibLabel will exit gracefully
248 // and we'll call getBasicLabel.
249 if (buffer->params.use_numerical_citations)
254 label = getNatbibLabel(buffer, cmd, getContents(),
256 buffer->params.use_numerical_citations);
259 // Fallback to fail-safe
261 label = getBasicLabel(getContents(), after);
268 InsetCitation::Cache::Style InsetCitation::getStyle(Buffer const * buffer) const
270 Cache::Style style = Cache::BASIC;
272 if (buffer->params.use_natbib) {
273 if (buffer->params.use_numerical_citations) {
274 style = Cache::NATBIB_NUM;
276 style = Cache::NATBIB_AY;
284 string const InsetCitation::getScreenLabel(Buffer const * buffer) const
286 Cache::Style const style = getStyle(buffer);
287 if (cache.params == params() && cache.style == style)
288 return cache.screen_label;
290 // The label has changed, so we have to re-create it.
291 string const before = string();
292 string const after = getOptions();
294 string const glabel = generateLabel(buffer);
296 unsigned int const maxLabelChars = 45;
298 string label = glabel;
299 if (label.size() > maxLabelChars) {
300 label.erase(maxLabelChars-3);
305 cache.params = params();
306 cache.generated_label = glabel;
307 cache.screen_label = label;
313 void InsetCitation::edit(BufferView * bv, int, int, mouse_button::state)
315 // A call to edit() indicates that we're no longer loading the
316 // buffer but doing some real work.
317 // Doesn't matter if there is no bv->buffer() entry in the map.
318 loading_buffer[bv->buffer()] = false;
320 bv->owner()->getDialogs().showCitation(this);
324 void InsetCitation::edit(BufferView * bv, bool)
326 edit(bv, 0, 0, mouse_button::none);
330 int InsetCitation::ascii(Buffer const * buffer, ostream & os, int) const
334 if (cache.params == params() && cache.style == getStyle(buffer))
335 label = cache.generated_label;
337 label = generateLabel(buffer);
344 // Have to overwrite the default InsetCommand method in order to check that
345 // the \cite command is valid. Eg, the user has natbib enabled, inputs some
346 // citations and then changes his mind, turning natbib support off. The output
347 // should revert to \cite[]{}
348 int InsetCitation::latex(Buffer const * buffer, ostream & os,
349 bool /*fragile*/, bool/*fs*/) const
352 if (buffer->params.use_natbib)
357 #warning What is this code supposed to do? (Lgb)
358 // my guess is that this is just waiting for when we support before,
359 // so it's a oneliner. But this is very silly ! - jbl
362 // The current strange code
364 string const before = string();
365 string const after = getOptions();
366 if (!before.empty() && buffer->params.use_natbib)
367 os << "[" << before << "][" << after << "]";
368 else if (!after.empty())
369 os << "[" << after << "]";
371 // and the cleaned up equvalent, should it just be changed? (Lgb)
372 string const after = getOptions();
374 os << "[" << after << "]";
376 string::const_iterator it = getContents().begin();
377 string::const_iterator end = getContents().end();
378 // Paranoia check: make sure that there is no whitespace in here
381 for (; it != end; ++it) {
384 if (*it != ' ' || last != ',')
388 os << "{" << content << "}";
394 void InsetCitation::validate(LaTeXFeatures & features) const
396 if (features.bufferParams().use_natbib)
397 features.require("natbib");