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
14 #include "insetcite.h"
15 #include "funcrequest.h"
17 #include "BufferView.h"
18 #include "LaTeXFeatures.h"
20 #include "frontends/controllers/biblio.h"
22 #include "support/lstrings.h"
32 // An optimisation. We assume that until the first InsetCitation::edit is
33 // called, we're loading the buffer and that, therefore, we don't need to
34 // reload the bibkey list
35 std::map<Buffer const *, bool> loading_buffer;
37 string const getNatbibLabel(Buffer const * buffer,
38 string const & citeType, string const & keyList,
39 string const & before, string const & after,
42 typedef std::map<Buffer const *, biblio::InfoMap> CachedMap;
43 static CachedMap cached_keys;
45 // Only load the bibkeys once if we're loading up the buffer,
46 // else load them afresh each time.
47 map<Buffer const *, bool>::iterator lit = loading_buffer.find(buffer);
48 if (lit == loading_buffer.end())
49 loading_buffer[buffer] = true;
51 bool loadkeys = !loading_buffer[buffer];
53 CachedMap::iterator kit = cached_keys.find(buffer);
54 loadkeys = kit == cached_keys.end();
59 typedef vector<std::pair<string, string> > InfoType;
61 buffer->fillWithBibKeys(bibkeys);
63 InfoType::const_iterator bit = bibkeys.begin();
64 InfoType::const_iterator bend = bibkeys.end();
66 biblio::InfoMap infomap;
67 for (; bit != bend; ++bit) {
68 infomap[bit->first] = bit->second;
73 cached_keys[buffer] = infomap;
76 biblio::InfoMap infomap = cached_keys[buffer];
78 // the natbib citation-styles
79 // CITET: author (year)
80 // CITEP: (author,year)
81 // CITEALT: author year
82 // CITEALP: author, year
85 // CITEYEARPAR: (year)
87 // We don't currently use the full or forceUCase fields.
88 // bool const forceUCase = citeType[0] == 'C';
89 bool const full = citeType[citeType.size() - 1] == '*';
91 string const cite_type = full ?
92 ascii_lowercase(citeType.substr(0, citeType.size() - 1)) :
93 ascii_lowercase(citeType);
96 if (!before.empty()) {
97 // In CITET and CITEALT mode, the "before" string is
98 // attached to the label associated with each and every key.
99 // In CITEP, CITEALP and CITEYEARPAR mode, it is attached
100 // to the front of the whole only.
101 // In other modes, it is not used at all.
102 if (cite_type == "citet" ||
103 cite_type == "citealt" ||
104 cite_type == "citep" ||
105 cite_type == "citealp" ||
106 cite_type == "citeyearpar")
107 before_str = before + ' ';
111 if (!after.empty()) {
112 // The "after" key is appended only to the end of the whole.
113 after_str = ", " + after;
116 // One day, these might be tunable (as they are in BibTeX).
117 char const op = '('; // opening parenthesis.
118 char const cp = ')'; // closing parenthesis.
119 // puctuation mark separating citation entries.
120 char const * const sep = ";";
122 string const op_str(' ' + string(1, op));
123 string const cp_str(string(1, cp) + ' ');
124 string const sep_str(string(sep) + ' ');
127 vector<string> keys = getVectorFromString(keyList);
128 vector<string>::const_iterator it = keys.begin();
129 vector<string>::const_iterator end = keys.end();
130 for (; it != end; ++it) {
131 // get the bibdata corresponding to the key
132 string const author(biblio::getAbbreviatedAuthor(infomap, *it));
133 string const year(biblio::getYear(infomap, *it));
135 // Something isn't right. Fail safely.
136 if (author.empty() || year.empty())
139 // (authors1 (<before> year); ... ;
140 // authors_last (<before> year, <after>)
141 if (cite_type == "citet") {
142 string const tmp = numerical ? '#' + *it : year;
143 label += author + op_str + before_str + tmp +
146 // author, year; author, year; ...
147 } else if (cite_type == "citep" ||
148 cite_type == "citealp") {
150 label += *it + sep_str;
152 label += author + ", " + year + sep_str;
155 // (authors1 <before> year;
156 // authors_last <before> year, <after>)
157 } else if (cite_type == "citealt") {
158 string const tmp = numerical ? '#' + *it : year;
159 label += author + ' ' + before_str + tmp + sep_str;
161 // author; author; ...
162 } else if (cite_type == "citeauthor") {
163 label += author + sep_str;
166 } else if (cite_type == "citeyear" ||
167 cite_type == "citeyearpar") {
168 label += year + sep_str;
171 label = rtrim(rtrim(label), sep);
173 if (!after_str.empty()) {
174 if (cite_type == "citet") {
175 // insert "after" before last ')'
176 label.insert(label.size() - 1, after_str);
178 bool const add = !(numerical &&
179 (cite_type == "citeauthor" ||
180 cite_type == "citeyear"));
186 if (!before_str.empty() && (cite_type == "citep" ||
187 cite_type == "citealp" ||
188 cite_type == "citeyearpar")) {
189 label = before_str + label;
192 if (cite_type == "citep" || cite_type == "citeyearpar")
193 label = string(1, op) + label + string(1, cp);
199 string const getBasicLabel(string const & keyList, string const & after)
201 string keys(keyList);
204 if (contains(keys, ",")) {
205 // Final comma allows while loop to cover all keys
206 keys = ltrim(split(keys, label, ',')) + ',';
207 while (contains(keys, ",")) {
209 keys = ltrim(split(keys, key, ','));
216 label += ", " + after;
218 return '[' + label + ']';
224 InsetCitation::InsetCitation(InsetCommandParams const & p)
229 // InsetCitation::InsetCitation(InsetCommandParams const & p, bool)
230 // : InsetCommand(p, false)
234 InsetCitation::~InsetCitation()
236 InsetCommandMailer mailer("citation", *this);
241 string const InsetCitation::generateLabel(Buffer const * buffer) const
243 string const before = string();
244 string const after = getOptions();
247 if (buffer->params.use_natbib) {
248 string cmd = getCmdName();
250 // We may be "upgrading" from an older LyX version.
251 // If, however, we use "cite" because the necessary
252 // author/year info is not present in the biblio
253 // database, then getNatbibLabel will exit gracefully
254 // and we'll call getBasicLabel.
255 if (buffer->params.use_numerical_citations)
260 label = getNatbibLabel(buffer, cmd, getContents(),
262 buffer->params.use_numerical_citations);
265 // Fallback to fail-safe
267 label = getBasicLabel(getContents(), after);
274 InsetCitation::Cache::Style InsetCitation::getStyle(Buffer const * buffer) const
276 Cache::Style style = Cache::BASIC;
278 if (buffer->params.use_natbib) {
279 if (buffer->params.use_numerical_citations) {
280 style = Cache::NATBIB_NUM;
282 style = Cache::NATBIB_AY;
290 string const InsetCitation::getScreenLabel(Buffer const * buffer) const
292 Cache::Style const style = getStyle(buffer);
293 if (cache.params == params() && cache.style == style)
294 return cache.screen_label;
296 // The label has changed, so we have to re-create it.
297 string const before = string();
298 string const after = getOptions();
300 string const glabel = generateLabel(buffer);
302 unsigned int const maxLabelChars = 45;
304 string label = glabel;
305 if (label.size() > maxLabelChars) {
306 label.erase(maxLabelChars-3);
311 cache.params = params();
312 cache.generated_label = glabel;
313 cache.screen_label = label;
319 void InsetCitation::setLoadingBuffer(Buffer const * buffer, bool state) const
321 // Doesn't matter if there is no bv->buffer() entry in the map.
322 loading_buffer[buffer] = state;
326 dispatch_result InsetCitation::localDispatch(FuncRequest const & cmd)
328 switch (cmd.action) {
329 case LFUN_INSET_EDIT:
330 // A call to edit indicates that we're no longer loading the
331 // buffer but doing some real work.
332 setLoadingBuffer(cmd.view()->buffer(), false);
333 InsetCommandMailer("citation", *this).showDialog(cmd.view());
343 int InsetCitation::ascii(Buffer const * buffer, ostream & os, int) const
347 if (cache.params == params() && cache.style == getStyle(buffer))
348 label = cache.generated_label;
350 label = generateLabel(buffer);
357 // Have to overwrite the default InsetCommand method in order to check that
358 // the \cite command is valid. Eg, the user has natbib enabled, inputs some
359 // citations and then changes his mind, turning natbib support off. The output
360 // should revert to \cite[]{}
361 int InsetCitation::latex(Buffer const * buffer, ostream & os,
362 LatexRunParams const &) const
365 if (buffer->params.use_natbib)
370 #warning What is this code supposed to do? (Lgb)
371 // my guess is that this is just waiting for when we support before,
372 // so it's a oneliner. But this is very silly ! - jbl
375 // The current strange code
377 string const before = string();
378 string const after = getOptions();
379 if (!before.empty() && buffer->params.use_natbib)
380 os << '[' << before << "][" << after << ']';
381 else if (!after.empty())
382 os << '[' << after << ']';
384 // and the cleaned up equvalent, should it just be changed? (Lgb)
385 string const after = getOptions();
387 os << '[' << after << ']';
389 string::const_iterator it = getContents().begin();
390 string::const_iterator end = getContents().end();
391 // Paranoia check: make sure that there is no whitespace in here
394 for (; it != end; ++it) {
397 if (*it != ' ' || last != ',')
401 os << '{' << content << '}';
407 void InsetCitation::validate(LaTeXFeatures & features) const
409 if (features.bufferParams().use_natbib)
410 features.require("natbib");