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 #include "insetcite.h"
17 #include "BufferView.h"
18 #include "LaTeXFeatures.h"
19 #include "frontends/LyXView.h"
23 #include "frontends/controllers/biblio.h"
24 #include "frontends/Dialogs.h"
26 #include "support/filetools.h"
27 #include "support/lstrings.h"
28 #include "support/path.h"
29 #include "support/os.h"
30 #include "support/lstrings.h"
31 #include "support/LAssert.h"
41 // An optimisation. We assume that until the first InsetCitation::edit is
42 // called, we're loading the buffer and that, therefore, we don't need to
43 // reload the bibkey list
44 std::map<Buffer const *, bool> loading_buffer;
46 string const getNatbibLabel(Buffer const * buffer,
47 string const & citeType, string const & keyList,
48 string const & before, string const & after,
51 typedef std::map<Buffer const *, biblio::InfoMap> CachedMap;
52 static CachedMap cached_keys;
54 // Only load the bibkeys once if we're loading up the buffer,
55 // else load them afresh each time.
56 map<Buffer const *, bool>::iterator lit = loading_buffer.find(buffer);
57 if (lit == loading_buffer.end())
58 loading_buffer[buffer] = true;
60 bool loadkeys = !loading_buffer[buffer];
62 CachedMap::iterator kit = cached_keys.find(buffer);
63 loadkeys = kit == cached_keys.end();
68 typedef vector<std::pair<string, string> > InfoType;
69 InfoType bibkeys = buffer->getBibkeyList();
71 InfoType::const_iterator bit = bibkeys.begin();
72 InfoType::const_iterator bend = bibkeys.end();
74 biblio::InfoMap infomap;
75 for (; bit != bend; ++bit) {
76 infomap[bit->first] = bit->second;
81 cached_keys[buffer] = infomap;
84 biblio::InfoMap infomap = cached_keys[buffer];
86 // the natbib citation-styles
87 // CITET: author (year)
88 // CITEP: (author,year)
89 // CITEALT: author year
90 // CITEALP: author, year
93 // CITEYEARPAR: (year)
95 // We don't currently use the full or forceUCase fields.
96 // bool const forceUCase = citeType[0] == 'C';
97 bool const full = citeType[citeType.size() - 1] == '*';
99 string const cite_type = full ?
100 ascii_lowercase(citeType.substr(0, citeType.size() - 1)) :
101 ascii_lowercase(citeType);
104 if (!before.empty()) {
105 // In CITET and CITEALT mode, the "before" string is
106 // attached to the label associated with each and every key.
107 // In CITEP, CITEALP and CITEYEARPAR mode, it is attached
108 // to the front of the whole only.
109 // In other modes, it is not used at all.
110 if (cite_type == "citet" ||
111 cite_type == "citealt" ||
112 cite_type == "citep" ||
113 cite_type == "citealp" ||
114 cite_type == "citeyearpar")
115 before_str = before + ' ';
119 if (!after.empty()) {
120 // The "after" key is appended only to the end of the whole.
121 after_str = ", " + after;
124 // One day, these might be tunable (as they are in BibTeX).
125 char const op = '('; // opening parenthesis.
126 char const cp = ')'; // closing parenthesis.
127 // puctuation mark separating citation entries.
128 char const * const sep = ";";
130 string const op_str(' ' + string(1, op));
131 string const cp_str(string(1, cp) + ' ');
132 string const sep_str(string(sep) + ' ');
135 vector<string> keys = getVectorFromString(keyList);
136 vector<string>::const_iterator it = keys.begin();
137 vector<string>::const_iterator end = keys.end();
138 for (; it != end; ++it) {
139 // get the bibdata corresponding to the key
140 string const author(biblio::getAbbreviatedAuthor(infomap, *it));
141 string const year(biblio::getYear(infomap, *it));
143 // Something isn't right. Fail safely.
144 if (author.empty() || year.empty())
147 // (authors1 (<before> year); ... ;
148 // authors_last (<before> year, <after>)
149 if (cite_type == "citet") {
150 string const tmp = numerical ? '#' + *it : year;
151 label += author + op_str + before_str + tmp +
154 // author, year; author, year; ...
155 } else if (cite_type == "citep" ||
156 cite_type == "citealp") {
158 label += *it + sep_str;
160 label += author + ", " + year + sep_str;
163 // (authors1 <before> year;
164 // authors_last <before> year, <after>)
165 } else if (cite_type == "citealt") {
166 string const tmp = numerical ? '#' + *it : year;
167 label += author + ' ' + before_str + tmp + sep_str;
169 // author; author; ...
170 } else if (cite_type == "citeauthor") {
171 label += author + sep_str;
174 } else if (cite_type == "citeyear" ||
175 cite_type == "citeyearpar") {
176 label += year + sep_str;
179 label = rtrim(rtrim(label), sep);
181 if (!after_str.empty()) {
182 if (cite_type == "citet") {
183 // insert "after" before last ')'
184 label.insert(label.size() - 1, after_str);
186 bool const add = !(numerical &&
187 (cite_type == "citeauthor" ||
188 cite_type == "citeyear"));
194 if (!before_str.empty() && (cite_type == "citep" ||
195 cite_type == "citealp" ||
196 cite_type == "citeyearpar")) {
197 label = before_str + label;
200 if (cite_type == "citep" || cite_type == "citeyearpar")
201 label = string(1, op) + label + string(1, cp);
207 string const getBasicLabel(string const & keyList, string const & after)
209 string keys(keyList);
212 if (contains(keys, ",")) {
213 // Final comma allows while loop to cover all keys
214 keys = ltrim(split(keys, label, ',')) + ',';
215 while (contains(keys, ",")) {
217 keys = ltrim(split(keys, key, ','));
224 label += ", " + after;
226 return '[' + label + ']';
232 InsetCitation::InsetCitation(InsetCommandParams const & p, bool)
237 string const InsetCitation::generateLabel(Buffer const * buffer) const
239 string const before = string();
240 string const after = getOptions();
243 if (buffer->params.use_natbib) {
244 string cmd = getCmdName();
246 // We may be "upgrading" from an older LyX version.
247 // If, however, we use "cite" because the necessary
248 // author/year info is not present in the biblio
249 // database, then getNatbibLabel will exit gracefully
250 // and we'll call getBasicLabel.
251 if (buffer->params.use_numerical_citations)
256 label = getNatbibLabel(buffer, cmd, getContents(),
258 buffer->params.use_numerical_citations);
261 // Fallback to fail-safe
263 label = getBasicLabel(getContents(), after);
270 InsetCitation::Cache::Style InsetCitation::getStyle(Buffer const * buffer) const
272 Cache::Style style = Cache::BASIC;
274 if (buffer->params.use_natbib) {
275 if (buffer->params.use_numerical_citations) {
276 style = Cache::NATBIB_NUM;
278 style = Cache::NATBIB_AY;
286 string const InsetCitation::getScreenLabel(Buffer const * buffer) const
288 Cache::Style const style = getStyle(buffer);
289 if (cache.params == params() && cache.style == style)
290 return cache.screen_label;
292 // The label has changed, so we have to re-create it.
293 string const before = string();
294 string const after = getOptions();
296 string const glabel = generateLabel(buffer);
298 unsigned int const maxLabelChars = 45;
300 string label = glabel;
301 if (label.size() > maxLabelChars) {
302 label.erase(maxLabelChars-3);
307 cache.params = params();
308 cache.generated_label = glabel;
309 cache.screen_label = label;
315 void InsetCitation::setLoadingBuffer(Buffer const * buffer, bool state) const
317 // Doesn't matter if there is no bv->buffer() entry in the map.
318 loading_buffer[buffer] = state;
322 void InsetCitation::edit(BufferView * bv, int, int, mouse_button::state)
324 // A call to edit() indicates that we're no longer loading the
325 // buffer but doing some real work.
326 setLoadingBuffer(bv->buffer(), false);
328 bv->owner()->getDialogs().showCitation(this);
332 void InsetCitation::edit(BufferView * bv, bool)
334 edit(bv, 0, 0, mouse_button::none);
338 int InsetCitation::ascii(Buffer const * buffer, ostream & os, int) const
342 if (cache.params == params() && cache.style == getStyle(buffer))
343 label = cache.generated_label;
345 label = generateLabel(buffer);
352 // Have to overwrite the default InsetCommand method in order to check that
353 // the \cite command is valid. Eg, the user has natbib enabled, inputs some
354 // citations and then changes his mind, turning natbib support off. The output
355 // should revert to \cite[]{}
356 int InsetCitation::latex(Buffer const * buffer, ostream & os,
357 bool /*fragile*/, bool/*fs*/) const
360 if (buffer->params.use_natbib)
365 #warning What is this code supposed to do? (Lgb)
366 // my guess is that this is just waiting for when we support before,
367 // so it's a oneliner. But this is very silly ! - jbl
370 // The current strange code
372 string const before = string();
373 string const after = getOptions();
374 if (!before.empty() && buffer->params.use_natbib)
375 os << '[' << before << "][" << after << ']';
376 else if (!after.empty())
377 os << '[' << after << ']';
379 // and the cleaned up equvalent, should it just be changed? (Lgb)
380 string const after = getOptions();
382 os << '[' << after << ']';
384 string::const_iterator it = getContents().begin();
385 string::const_iterator end = getContents().end();
386 // Paranoia check: make sure that there is no whitespace in here
389 for (; it != end; ++it) {
392 if (*it != ' ' || last != ',')
396 os << '{' << content << '}';
402 void InsetCitation::validate(LaTeXFeatures & features) const
404 if (features.bufferParams().use_natbib)
405 features.require("natbib");