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);
139 ParamInfo InsetCitation::param_info_;
142 InsetCitation::InsetCitation(Buffer * buf, InsetCommandParams const & p)
143 : InsetCommand(buf, p, "citation")
147 ParamInfo const & InsetCitation::findInfo(string const & /* cmdName */)
149 // standard cite does only take one argument if jurabib is
150 // not used, but jurabib extends this to two arguments, so
151 // we have to allow both here. InsetCitation takes care that
152 // LaTeX output is nevertheless correct.
153 if (param_info_.empty()) {
154 param_info_.add("after", ParamInfo::LATEX_OPTIONAL);
155 param_info_.add("before", ParamInfo::LATEX_OPTIONAL);
156 param_info_.add("key", ParamInfo::LATEX_REQUIRED);
162 bool InsetCitation::isCompatibleCommand(string const & cmd)
164 vector<string> const & possibles = possibleCiteCommands();
165 vector<string>::const_iterator const end = possibles.end();
166 return find(possibles.begin(), end, cmd) != end;
170 docstring InsetCitation::toolTip(BufferView const & bv, int, int) const
172 Buffer const & buf = bv.buffer();
173 // Only after the buffer is loaded from file...
174 if (!buf.isFullyLoaded())
177 BiblioInfo const & bi = buf.masterBibInfo();
179 return _("No bibliography defined!");
181 docstring const & key = getParam("key");
183 return _("No citations selected!");
185 vector<docstring> keys = getVectorFromString(key);
186 vector<docstring>::const_iterator it = keys.begin();
187 vector<docstring>::const_iterator en = keys.end();
189 for (; it != en; ++it) {
190 docstring const key_info = bi.getInfo(*it);
191 if (key_info.empty())
195 tip += wrap(key_info, -4);
202 docstring InsetCitation::generateLabel() const
205 label = complexLabel();
207 // Fallback to fail-safe
209 label = basicLabel();
215 docstring InsetCitation::complexLabel() const
217 Buffer const & buf = buffer();
218 // Only start the process off after the buffer is loaded from file.
219 if (!buf.isFullyLoaded())
222 BiblioInfo const & biblist = buf.masterBibInfo();
226 // the natbib citation-styles
227 // CITET: author (year)
228 // CITEP: (author,year)
229 // CITEALT: author year
230 // CITEALP: author, year
231 // CITEAUTHOR: author
233 // CITEYEARPAR: (year)
234 // jurabib supports these plus
235 // CITE: author/<before field>
237 CiteEngine const engine = buffer().params().citeEngine();
238 // We don't currently use the full or forceUCase fields.
239 string cite_type = asValidLatexCommand(getCmdName(), engine);
240 if (cite_type[0] == 'C')
241 // If we were going to use them, this would mean ForceUCase
242 cite_type = string(1, 'c') + cite_type.substr(1);
243 if (cite_type[cite_type.size() - 1] == '*')
244 // and this would mean FULL
245 cite_type = cite_type.substr(0, cite_type.size() - 1);
247 docstring const & before = getParam("before");
248 docstring before_str;
249 if (!before.empty()) {
250 // In CITET and CITEALT mode, the "before" string is
251 // attached to the label associated with each and every key.
252 // In CITEP, CITEALP and CITEYEARPAR mode, it is attached
253 // to the front of the whole only.
254 // In other modes, it is not used at all.
255 if (cite_type == "citet" ||
256 cite_type == "citealt" ||
257 cite_type == "citep" ||
258 cite_type == "citealp" ||
259 cite_type == "citeyearpar")
260 before_str = before + ' ';
261 // In CITE (jurabib), the "before" string is used to attach
262 // the annotator (of legal texts) to the author(s) of the
264 else if (cite_type == "cite")
265 before_str = '/' + before;
268 docstring const & after = getParam("after");
270 // The "after" key is appended only to the end of the whole.
271 if (cite_type == "nocite")
272 after_str = " (" + _("not cited") + ')';
273 else if (!after.empty()) {
274 after_str = ", " + after;
277 // One day, these might be tunable (as they are in BibTeX).
278 char op, cp; // opening and closing parenthesis.
279 const char * sep; // punctuation mark separating citation entries.
280 if (engine == ENGINE_BASIC) {
290 docstring const op_str = ' ' + docstring(1, op);
291 docstring const cp_str = docstring(1, cp) + ' ';
292 docstring const sep_str = from_ascii(sep) + ' ';
295 vector<docstring> keys = getVectorFromString(getParam("key"));
296 vector<docstring>::const_iterator it = keys.begin();
297 vector<docstring>::const_iterator end = keys.end();
298 for (; it != end; ++it) {
299 // get the bibdata corresponding to the key
300 docstring const author = biblist.getAbbreviatedAuthor(*it);
301 docstring const year = biblist.getYear(*it);
303 // Something isn't right. Fail safely.
304 if (author.empty() || year.empty())
307 // authors1/<before>; ... ;
308 // authors_last, <after>
309 if (cite_type == "cite") {
310 if (engine == ENGINE_BASIC) {
311 label += *it + sep_str;
312 } else if (engine == ENGINE_JURABIB) {
313 if (it == keys.begin())
314 label += author + before_str + sep_str;
316 label += author + sep_str;
320 } else if (cite_type == "nocite") {
321 label += *it + sep_str;
323 // (authors1 (<before> year); ... ;
324 // authors_last (<before> year, <after>)
325 } else if (cite_type == "citet") {
327 case ENGINE_NATBIB_AUTHORYEAR:
328 label += author + op_str + before_str +
331 case ENGINE_NATBIB_NUMERICAL:
332 label += author + op_str + before_str + '#' + *it + cp + sep_str;
335 label += before_str + author + op_str +
342 // author, year; author, year; ...
343 } else if (cite_type == "citep" ||
344 cite_type == "citealp") {
345 if (engine == ENGINE_NATBIB_NUMERICAL) {
346 label += *it + sep_str;
348 label += author + ", " + year + sep_str;
351 // (authors1 <before> year;
352 // authors_last <before> year, <after>)
353 } else if (cite_type == "citealt") {
355 case ENGINE_NATBIB_AUTHORYEAR:
356 label += author + ' ' + before_str +
359 case ENGINE_NATBIB_NUMERICAL:
360 label += author + ' ' + before_str + '#' + *it + sep_str;
363 label += before_str + author + ' ' +
370 // author; author; ...
371 } else if (cite_type == "citeauthor") {
372 label += author + sep_str;
375 } else if (cite_type == "citeyear" ||
376 cite_type == "citeyearpar") {
377 label += year + sep_str;
380 label = rtrim(rtrim(label), sep);
382 if (!after_str.empty()) {
383 if (cite_type == "citet") {
384 // insert "after" before last ')'
385 label.insert(label.size() - 1, after_str);
388 !(engine == ENGINE_NATBIB_NUMERICAL &&
389 (cite_type == "citeauthor" ||
390 cite_type == "citeyear"));
396 if (!before_str.empty() && (cite_type == "citep" ||
397 cite_type == "citealp" ||
398 cite_type == "citeyearpar")) {
399 label = before_str + label;
402 if (cite_type == "citep" || cite_type == "citeyearpar" ||
403 (cite_type == "cite" && engine == ENGINE_BASIC) )
404 label = op + label + cp;
410 docstring InsetCitation::basicLabel() const
412 docstring keys = getParam("key");
415 if (contains(keys, ',')) {
416 // Final comma allows while loop to cover all keys
417 keys = ltrim(split(keys, label, ',')) + ',';
418 while (contains(keys, ',')) {
420 keys = ltrim(split(keys, key, ','));
427 docstring const & after = getParam("after");
429 label += ", " + after;
431 return '[' + label + ']';
434 docstring InsetCitation::screenLabel() const
436 return cache.screen_label;
440 void InsetCitation::updateLabels(ParIterator const &, bool)
442 CiteEngine const engine = buffer().params().citeEngine();
443 if (cache.params == params() && cache.engine == engine)
446 // The label has changed, so we have to re-create it.
447 docstring const glabel = generateLabel();
449 unsigned int const maxLabelChars = 45;
451 docstring label = glabel;
452 if (label.size() > maxLabelChars) {
453 label.erase(maxLabelChars-3);
457 cache.engine = engine;
458 cache.params = params();
459 cache.generated_label = glabel;
460 cache.screen_label = label;
464 void InsetCitation::addToToc(DocIterator const & cpit)
466 Toc & toc = buffer().tocBackend().toc("citation");
467 toc.push_back(TocItem(cpit, 0, getParam("key")));
471 int InsetCitation::plaintext(odocstream & os, OutputParams const &) const
473 os << cache.generated_label;
474 return cache.generated_label.size();
478 static docstring const cleanupWhitespace(docstring const & citelist)
480 docstring::const_iterator it = citelist.begin();
481 docstring::const_iterator end = citelist.end();
482 // Paranoia check: make sure that there is no whitespace in here
483 // -- at least not behind commas or at the beginning
485 char_type last = ',';
486 for (; it != end; ++it) {
489 if (*it != ' ' || last != ',')
496 int InsetCitation::docbook(odocstream & os, OutputParams const &) const
498 os << from_ascii("<citation>")
499 << cleanupWhitespace(getParam("key"))
500 << from_ascii("</citation>");
505 docstring InsetCitation::xhtml(XHTMLStream & xs, OutputParams const &) const
507 string const & cmd = getCmdName();
511 BiblioInfo const & bi = buffer().masterBibInfo();
512 docstring const & key_list = getParam("key");
513 if (key_list.empty())
516 // FIXME We should do a better job outputing different things for the
517 // different citation styles. For now, we use square brackets for every
520 docstring const & before = getParam("before");
524 vector<docstring> const keys = getVectorFromString(key_list);
525 vector<docstring>::const_iterator it = keys.begin();
526 vector<docstring>::const_iterator const en = keys.end();
528 for (; it != en; ++it) {
529 BiblioInfo::const_iterator const bt = bi.find(*it);
532 BibTeXInfo const & bibinfo = bt->second;
537 docstring citekey = bibinfo.citeKey();
538 if (citekey.empty()) {
539 citekey = bibinfo.label();
543 string const attr = "href='#" + to_utf8(*it) + "'";
544 xs << StartTag("a", attr) << citekey << EndTag("a");
547 docstring const & after = getParam("after");
555 void InsetCitation::tocString(odocstream & os) const
557 plaintext(os, OutputParams(0));
561 // Have to overwrite the default InsetCommand method in order to check that
562 // the \cite command is valid. Eg, the user has natbib enabled, inputs some
563 // citations and then changes his mind, turning natbib support off. The output
564 // should revert to \cite[]{}
565 int InsetCitation::latex(odocstream & os, OutputParams const & runparams) const
567 CiteEngine cite_engine = buffer().params().citeEngine();
569 docstring const cite_str = from_utf8(
570 asValidLatexCommand(getCmdName(), cite_engine));
572 if (runparams.inulemcmd)
575 os << "\\" << cite_str;
577 docstring const & before = getParam("before");
578 docstring const & after = getParam("after");
579 if (!before.empty() && cite_engine != ENGINE_BASIC)
580 os << '[' << before << "][" << after << ']';
581 else if (!after.empty())
582 os << '[' << after << ']';
584 os << '{' << cleanupWhitespace(getParam("key")) << '}';
586 if (runparams.inulemcmd)
593 void InsetCitation::validate(LaTeXFeatures & features) const
595 switch (features.bufferParams().citeEngine()) {
598 case ENGINE_NATBIB_AUTHORYEAR:
599 case ENGINE_NATBIB_NUMERICAL:
600 features.require("natbib");
603 features.require("jurabib");
609 docstring InsetCitation::contextMenu(BufferView const &, int, int) const
611 return from_ascii("context-citation");