2 * \file InsetCitation.cpp
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 "InsetCitation.h"
17 #include "buffer_funcs.h"
18 #include "BufferParams.h"
19 #include "BufferView.h"
20 #include "DispatchResult.h"
21 #include "FuncRequest.h"
22 #include "LaTeXFeatures.h"
23 #include "output_xhtml.h"
24 #include "ParIterator.h"
25 #include "TocBackend.h"
27 #include "support/debug.h"
28 #include "support/docstream.h"
29 #include "support/FileNameList.h"
30 #include "support/gettext.h"
31 #include "support/lstrings.h"
36 using namespace lyx::support;
42 vector<string> const init_possible_cite_commands()
44 char const * const possible[] = {
45 "cite", "nocite", "citet", "citep", "citealt", "citealp",
46 "citeauthor", "citeyear", "citeyearpar",
47 "citet*", "citep*", "citealt*", "citealp*", "citeauthor*",
48 "Citet", "Citep", "Citealt", "Citealp", "Citeauthor",
49 "Citet*", "Citep*", "Citealt*", "Citealp*", "Citeauthor*",
51 "footcite", "footcitet", "footcitep", "footcitealt",
52 "footcitealp", "footciteauthor", "footciteyear", "footciteyearpar",
53 "citefield", "citetitle", "cite*"
55 size_t const size_possible = sizeof(possible) / sizeof(possible[0]);
57 return vector<string>(possible, possible + size_possible);
61 vector<string> const & possibleCiteCommands()
63 static vector<string> const possible = init_possible_cite_commands();
68 // FIXME See the header for the issue.
69 string defaultCiteCommand(CiteEngine engine)
76 case ENGINE_NATBIB_AUTHORYEAR:
79 case ENGINE_NATBIB_NUMERICAL:
90 string asValidLatexCommand(string const & input, CiteEngine const engine)
92 string const default_str = defaultCiteCommand(engine);
93 if (!InsetCitation::isCompatibleCommand(input))
99 if (input == "nocite")
102 output = default_str;
105 case ENGINE_NATBIB_AUTHORYEAR:
106 case ENGINE_NATBIB_NUMERICAL:
107 if (input == "cite" || input == "citefield"
108 || input == "citetitle" || input == "cite*")
109 output = default_str;
110 else if (prefixIs(input, "foot"))
111 output = input.substr(4);
116 case ENGINE_JURABIB: {
117 // Jurabib does not support the 'uppercase' natbib style.
119 output = string(1, 'c') + input.substr(1);
123 // Jurabib does not support the 'full' natbib style.
124 string::size_type const n = output.size() - 1;
125 if (output != "cite*" && output[n] == '*')
126 output = output.substr(0, n);
136 docstring complexLabel(Buffer const & buffer,
137 string const & citetype, docstring const & keylist,
138 docstring const & before, docstring const & after,
141 // Only start the process off after the buffer is loaded from file.
142 if (!buffer.isFullyLoaded())
145 BiblioInfo const & biblist = buffer.masterBibInfo();
149 // the natbib citation-styles
150 // CITET: author (year)
151 // CITEP: (author,year)
152 // CITEALT: author year
153 // CITEALP: author, year
154 // CITEAUTHOR: author
156 // CITEYEARPAR: (year)
157 // jurabib supports these plus
158 // CITE: author/<before field>
160 // We don't currently use the full or forceUCase fields.
161 string cite_type = asValidLatexCommand(citetype, engine);
162 if (cite_type[0] == 'C')
163 // If we were going to use them, this would mean ForceUCase
164 cite_type = string(1, 'c') + cite_type.substr(1);
165 if (cite_type[cite_type.size() - 1] == '*')
166 // and this would mean FULL
167 cite_type = cite_type.substr(0, cite_type.size() - 1);
169 docstring before_str;
170 if (!before.empty()) {
171 // In CITET and CITEALT mode, the "before" string is
172 // attached to the label associated with each and every key.
173 // In CITEP, CITEALP and CITEYEARPAR mode, it is attached
174 // to the front of the whole only.
175 // In other modes, it is not used at all.
176 if (cite_type == "citet" ||
177 cite_type == "citealt" ||
178 cite_type == "citep" ||
179 cite_type == "citealp" ||
180 cite_type == "citeyearpar")
181 before_str = before + ' ';
182 // In CITE (jurabib), the "before" string is used to attach
183 // the annotator (of legal texts) to the author(s) of the
185 else if (cite_type == "cite")
186 before_str = '/' + before;
190 // The "after" key is appended only to the end of the whole.
191 if (cite_type == "nocite")
192 after_str = " (" + _("not cited") + ')';
193 else if (!after.empty()) {
194 after_str = ", " + after;
197 // One day, these might be tunable (as they are in BibTeX).
198 char op, cp; // opening and closing parenthesis.
199 const char * sep; // punctuation mark separating citation entries.
200 if (engine == ENGINE_BASIC) {
210 docstring const op_str = ' ' + docstring(1, op);
211 docstring const cp_str = docstring(1, cp) + ' ';
212 docstring const sep_str = from_ascii(sep) + ' ';
215 vector<docstring> keys = getVectorFromString(keylist);
216 vector<docstring>::const_iterator it = keys.begin();
217 vector<docstring>::const_iterator end = keys.end();
218 for (; it != end; ++it) {
219 // get the bibdata corresponding to the key
220 docstring const author = biblist.getAbbreviatedAuthor(*it);
221 docstring const year = biblist.getYear(*it);
223 // Something isn't right. Fail safely.
224 if (author.empty() || year.empty())
227 // authors1/<before>; ... ;
228 // authors_last, <after>
229 if (cite_type == "cite") {
230 if (engine == ENGINE_BASIC) {
231 label += *it + sep_str;
232 } else if (engine == ENGINE_JURABIB) {
233 if (it == keys.begin())
234 label += author + before_str + sep_str;
236 label += author + sep_str;
240 } else if (cite_type == "nocite") {
241 label += *it + sep_str;
243 // (authors1 (<before> year); ... ;
244 // authors_last (<before> year, <after>)
245 } else if (cite_type == "citet") {
247 case ENGINE_NATBIB_AUTHORYEAR:
248 label += author + op_str + before_str +
251 case ENGINE_NATBIB_NUMERICAL:
252 label += author + op_str + before_str + '#' + *it + cp + sep_str;
255 label += before_str + author + op_str +
262 // author, year; author, year; ...
263 } else if (cite_type == "citep" ||
264 cite_type == "citealp") {
265 if (engine == ENGINE_NATBIB_NUMERICAL) {
266 label += *it + sep_str;
268 label += author + ", " + year + sep_str;
271 // (authors1 <before> year;
272 // authors_last <before> year, <after>)
273 } else if (cite_type == "citealt") {
275 case ENGINE_NATBIB_AUTHORYEAR:
276 label += author + ' ' + before_str +
279 case ENGINE_NATBIB_NUMERICAL:
280 label += author + ' ' + before_str + '#' + *it + sep_str;
283 label += before_str + author + ' ' +
290 // author; author; ...
291 } else if (cite_type == "citeauthor") {
292 label += author + sep_str;
295 } else if (cite_type == "citeyear" ||
296 cite_type == "citeyearpar") {
297 label += year + sep_str;
300 label = rtrim(rtrim(label), sep);
302 if (!after_str.empty()) {
303 if (cite_type == "citet") {
304 // insert "after" before last ')'
305 label.insert(label.size() - 1, after_str);
308 !(engine == ENGINE_NATBIB_NUMERICAL &&
309 (cite_type == "citeauthor" ||
310 cite_type == "citeyear"));
316 if (!before_str.empty() && (cite_type == "citep" ||
317 cite_type == "citealp" ||
318 cite_type == "citeyearpar")) {
319 label = before_str + label;
322 if (cite_type == "citep" || cite_type == "citeyearpar" ||
323 (cite_type == "cite" && engine == ENGINE_BASIC) )
324 label = op + label + cp;
330 docstring basicLabel(docstring const & keyList, docstring const & after)
332 docstring keys = keyList;
335 if (contains(keys, ',')) {
336 // Final comma allows while loop to cover all keys
337 keys = ltrim(split(keys, label, ',')) + ',';
338 while (contains(keys, ',')) {
340 keys = ltrim(split(keys, key, ','));
348 label += ", " + after;
350 return '[' + label + ']';
356 ParamInfo InsetCitation::param_info_;
359 InsetCitation::InsetCitation(Buffer * buf, InsetCommandParams const & p)
360 : InsetCommand(buf, p, "citation")
364 ParamInfo const & InsetCitation::findInfo(string const & /* cmdName */)
366 // standard cite does only take one argument if jurabib is
367 // not used, but jurabib extends this to two arguments, so
368 // we have to allow both here. InsetCitation takes care that
369 // LaTeX output is nevertheless correct.
370 if (param_info_.empty()) {
371 param_info_.add("after", ParamInfo::LATEX_OPTIONAL);
372 param_info_.add("before", ParamInfo::LATEX_OPTIONAL);
373 param_info_.add("key", ParamInfo::LATEX_REQUIRED);
379 bool InsetCitation::isCompatibleCommand(string const & cmd)
381 vector<string> const & possibles = possibleCiteCommands();
382 vector<string>::const_iterator const end = possibles.end();
383 return find(possibles.begin(), end, cmd) != end;
387 docstring InsetCitation::toolTip(BufferView const & bv, int, int) const
389 Buffer const & buf = bv.buffer();
390 // Only after the buffer is loaded from file...
391 if (!buf.isFullyLoaded())
394 BiblioInfo const & bi = buf.masterBibInfo();
396 return _("No bibliography defined!");
398 docstring const & key = getParam("key");
400 return _("No citations selected!");
402 vector<docstring> keys = getVectorFromString(key);
403 vector<docstring>::const_iterator it = keys.begin();
404 vector<docstring>::const_iterator en = keys.end();
406 for (; it != en; ++it) {
407 docstring const key_info = bi.getInfo(*it);
408 if (key_info.empty())
412 tip += wrap(key_info, -4);
419 docstring InsetCitation::generateLabel() const
421 docstring const & before = getParam("before");
422 docstring const & after = getParam("after");
425 CiteEngine const engine = buffer().params().citeEngine();
426 label = complexLabel(buffer(), getCmdName(), getParam("key"),
427 before, after, engine);
429 // Fallback to fail-safe
431 label = basicLabel(getParam("key"), after);
437 docstring InsetCitation::screenLabel() const
439 return cache.screen_label;
443 void InsetCitation::updateLabels(ParIterator const &, bool)
445 CiteEngine const engine = buffer().params().citeEngine();
446 if (cache.params == params() && cache.engine == engine)
449 // The label has changed, so we have to re-create it.
450 docstring const glabel = generateLabel();
452 unsigned int const maxLabelChars = 45;
454 docstring label = glabel;
455 if (label.size() > maxLabelChars) {
456 label.erase(maxLabelChars-3);
460 cache.engine = engine;
461 cache.params = params();
462 cache.generated_label = glabel;
463 cache.screen_label = label;
467 void InsetCitation::addToToc(DocIterator const & cpit)
469 Toc & toc = buffer().tocBackend().toc("citation");
470 toc.push_back(TocItem(cpit, 0, getParam("key")));
474 int InsetCitation::plaintext(odocstream & os, OutputParams const &) const
476 os << cache.generated_label;
477 return cache.generated_label.size();
481 static docstring const cleanupWhitespace(docstring const & citelist)
483 docstring::const_iterator it = citelist.begin();
484 docstring::const_iterator end = citelist.end();
485 // Paranoia check: make sure that there is no whitespace in here
486 // -- at least not behind commas or at the beginning
488 char_type last = ',';
489 for (; it != end; ++it) {
492 if (*it != ' ' || last != ',')
499 int InsetCitation::docbook(odocstream & os, OutputParams const &) const
501 os << from_ascii("<citation>")
502 << cleanupWhitespace(getParam("key"))
503 << from_ascii("</citation>");
508 docstring InsetCitation::xhtml(XHTMLStream & xs, OutputParams const &) const
510 string const & cmd = getCmdName();
514 BiblioInfo const & bi = buffer().masterBibInfo();
515 docstring const & key_list = getParam("key");
516 if (key_list.empty())
519 // FIXME We should do a better job outputing different things for the
520 // different citation styles. For now, we use square brackets for every
523 docstring const & before = getParam("before");
527 vector<docstring> const keys = getVectorFromString(key_list);
528 vector<docstring>::const_iterator it = keys.begin();
529 vector<docstring>::const_iterator const en = keys.end();
531 for (; it != en; ++it) {
532 BiblioInfo::const_iterator const bt = bi.find(*it);
535 BibTeXInfo const & bibinfo = bt->second;
540 docstring citekey = bibinfo.citeKey();
541 if (citekey.empty()) {
542 citekey = bibinfo.label();
546 string const attr = "href='#" + to_utf8(*it) + "'";
547 xs << StartTag("a", attr) << citekey << EndTag("a");
550 docstring const & after = getParam("after");
558 void InsetCitation::tocString(odocstream & os) const
560 plaintext(os, OutputParams(0));
564 // Have to overwrite the default InsetCommand method in order to check that
565 // the \cite command is valid. Eg, the user has natbib enabled, inputs some
566 // citations and then changes his mind, turning natbib support off. The output
567 // should revert to \cite[]{}
568 int InsetCitation::latex(odocstream & os, OutputParams const & runparams) const
570 CiteEngine cite_engine = buffer().params().citeEngine();
572 docstring const cite_str = from_utf8(
573 asValidLatexCommand(getCmdName(), cite_engine));
575 if (runparams.inulemcmd)
578 os << "\\" << cite_str;
580 docstring const & before = getParam("before");
581 docstring const & after = getParam("after");
582 if (!before.empty() && cite_engine != ENGINE_BASIC)
583 os << '[' << before << "][" << after << ']';
584 else if (!after.empty())
585 os << '[' << after << ']';
587 os << '{' << cleanupWhitespace(getParam("key")) << '}';
589 if (runparams.inulemcmd)
596 void InsetCitation::validate(LaTeXFeatures & features) const
598 switch (features.bufferParams().citeEngine()) {
601 case ENGINE_NATBIB_AUTHORYEAR:
602 case ENGINE_NATBIB_NUMERICAL:
603 features.require("natbib");
606 features.require("jurabib");
612 docstring InsetCitation::contextMenu(BufferView const &, int, int) const
614 return from_ascii("context-citation");