2 * \file InsetBibitem.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alejandro Aguilar Sierra
8 * Full author contact details are available in file CREDITS.
14 #include "InsetBibitem.h"
16 #include "BiblioInfo.h"
19 #include "buffer_funcs.h"
20 #include "BufferParams.h"
21 #include "BufferView.h"
23 #include "DispatchResult.h"
25 #include "FuncRequest.h"
26 #include "InsetIterator.h"
27 #include "InsetList.h"
30 #include "output_xhtml.h"
31 #include "OutputParams.h"
32 #include "Paragraph.h"
33 #include "ParagraphList.h"
34 #include "ParIterator.h"
35 #include "TextClass.h"
37 #include "frontends/alert.h"
39 #include "support/convert.h"
40 #include "support/debug.h"
41 #include "support/docstream.h"
42 #include "support/gettext.h"
43 #include "support/lstrings.h"
46 using namespace lyx::support;
52 int InsetBibitem::key_counter = 0;
53 docstring const key_prefix = from_ascii("key-");
56 InsetBibitem::InsetBibitem(Buffer * buf, InsetCommandParams const & p)
57 : InsetCommand(buf, p)
59 buffer().invalidateBibinfoCache();
60 if (getParam("key").empty())
61 setParam("key", key_prefix + convert<docstring>(++key_counter));
65 InsetBibitem::~InsetBibitem()
68 buffer().invalidateBibinfoCache();
72 void InsetBibitem::initView()
74 updateCommand(getParam("key"));
78 void InsetBibitem::updateCommand(docstring const & new_key, bool)
80 docstring key = new_key;
82 vector<docstring> bibkeys = buffer().masterBibInfo().getKeys();
86 if (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
87 // generate unique label
88 key = new_key + '-' + convert<docstring>(i);
89 while (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
91 key = new_key + '-' + convert<docstring>(i);
93 frontend::Alert::warning(_("Keys must be unique!"),
94 bformat(_("The key %1$s already exists,\n"
95 "it will be changed to %2$s."), new_key, key));
98 buffer().invalidateBibinfoCache();
102 ParamInfo const & InsetBibitem::findInfo(string const & /* cmdName */)
104 static ParamInfo param_info_;
105 if (param_info_.empty()) {
106 param_info_.add("label", ParamInfo::LATEX_OPTIONAL,
107 ParamInfo::HANDLING_LATEXIFY);
108 param_info_.add("key", ParamInfo::LATEX_REQUIRED,
109 ParamInfo::HANDLING_ESCAPE);
115 void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
117 switch (cmd.action()) {
119 case LFUN_INSET_MODIFY: {
120 InsetCommandParams p(BIBITEM_CODE);
121 InsetCommand::string2params(to_utf8(cmd.argument()), p);
122 if (p.getCmdName().empty()) {
123 cur.noScreenUpdate();
129 docstring const & old_key = params()["key"];
130 docstring const & old_label = params()["label"];
131 docstring label = p["label"];
133 // definitions for escaping
135 static docstring const backslash = from_ascii("\\");
136 static docstring const lbrace = from_ascii("{");
137 static docstring const rbrace = from_ascii("}");
138 static char_type const chars_escape[6] = {
139 '&', '_', '$', '%', '#', '^'};
140 static char_type const brackets_escape[2] = {'[', ']'};
142 if (!label.empty()) {
143 // The characters in chars_name[] need to be changed to a command when
144 // they are in the name field.
145 for (int k = 0; k < 6; k++)
146 for (size_t i = 0, pos;
147 (pos = label.find(chars_escape[k], i)) != string::npos;
153 // only if not already escaped
154 if (label[previous] != '\\')
155 label.replace(pos, 1, backslash + chars_escape[k] + lbrace + rbrace);
157 // The characters '[' and ']' need to be put into braces
158 for (int k = 0; k < 2; k++)
159 for (size_t i = 0, pos;
160 (pos = label.find(brackets_escape[k], i)) != string::npos;
166 // only if not already escaped
167 if (label[previous] != '{')
168 label.replace(pos, 1, lbrace + brackets_escape[k] + rbrace);
172 if (old_label != label) {
174 cur.forceBufferUpdate();
175 buffer().invalidateBibinfoCache();
178 setParam("label", p["label"]);
179 if (p["key"] != old_key) {
180 updateCommand(p["key"]);
181 cur.bv().buffer().changeRefsIfUnique(old_key, params()["key"]);
182 cur.forceBufferUpdate();
183 buffer().invalidateBibinfoCache();
189 InsetCommand::doDispatch(cur, cmd);
195 void InsetBibitem::read(Lexer & lex)
197 InsetCommand::read(lex);
199 if (prefixIs(getParam("key"), key_prefix)) {
200 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
201 key_counter = max(key_counter, key);
206 docstring InsetBibitem::bibLabel() const
208 BufferParams const & bp = buffer().masterBuffer()->params();
209 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
211 docstring const & label = getParam("label");
212 return label.empty() ? autolabel_ : label;
216 docstring InsetBibitem::screenLabel() const
218 return getParam("key") + " [" + bibLabel() + ']';
222 int InsetBibitem::plaintext(odocstringstream & os,
223 OutputParams const &, size_t) const
225 odocstringstream oss;
226 oss << '[' << bibLabel() << "] ";
228 docstring const str = oss.str();
236 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
238 BufferParams const & bp = buffer.masterBuffer()->params();
239 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
240 return from_ascii("99");
244 InsetBibitem const * bitem = 0;
246 // FIXME: this font is used unitialized for now but should be set to
247 // a proportional font. Here is what Georg Baum has to say about it:
249 bibitemWidest() is supposed to find the bibitem with the widest label in the
250 output, because that is needed as an argument of the bibliography
251 environment to determine the correct indentation. To be 100% correct we
252 would need the metrics of the font that is used in the output, but usually
253 we don't have access to these.
254 In practice, any proportional font is probably good enough, since we don't
255 need to know the final with, we only need to know the which label is the
257 Unless there is an easy way to get the metrics of the output font I suggest
258 to use a hardcoded font like "Times" or so.
260 It is very important that the result of this function is the same both with
261 and without GUI. After thinking about this it is clear that no Font
262 metrics should be used here, since these come from the gui. If we can't
263 easily get the LaTeX font metrics we should make our own poor mans font
264 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
270 ParagraphList::const_iterator it = buffer.paragraphs().begin();
271 ParagraphList::const_iterator end = buffer.paragraphs().end();
273 for (; it != end; ++it) {
274 if (it->insetList().empty())
276 Inset * inset = it->insetList().begin()->inset;
277 if (inset->lyxCode() != BIBITEM_CODE)
280 bitem = static_cast<InsetBibitem const *>(inset);
281 docstring const label = bitem->bibLabel();
283 // FIXME: we can't be sure using the following that the GUI
284 // version and the command-line version will give the same
287 //int const wx = use_gui?
288 // theFontMetrics(font).width(label): label.size();
290 // So for now we just use the label size in order to be sure
291 // that GUI and no-GUI gives the same bibitem (even if that is
292 // potentially the wrong one.
293 int const wx = label.size();
302 pair<docstring, docstring> latex_lbl =
303 runparams.encoding->latexString(lbl, runparams.dryrun);
304 return latex_lbl.first;
307 return from_ascii("99");
311 void InsetBibitem::collectBibKeys(InsetIterator const & it) const
313 docstring const key = getParam("key");
314 docstring const label = getParam("label");
315 BibTeXInfo keyvalmap(false);
317 keyvalmap.label(label);
319 BufferParams const & bp = buffer().masterBuffer()->params();
320 Counters & counters = bp.documentClass().counters();
321 docstring const bibitem = from_ascii("bibitem");
322 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
323 if (counters.hasCounter(bibitem))
324 counters.step(bibitem, InternalUpdate);
325 string const & lang = it.paragraph().getParLanguage(bp)->code();
326 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
329 DocIterator doc_it(it);
331 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
332 AS_STR_INSETS | AS_STR_SKIPDELETE);
333 buffer().addBibTeXInfo(key, keyvalmap);
337 // Update the counters of this inset and of its contents
338 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
340 BufferParams const & bp = buffer().masterBuffer()->params();
341 Counters & counters = bp.documentClass().counters();
342 docstring const bibitem = from_ascii("bibitem");
343 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
344 if (counters.hasCounter(bibitem))
345 counters.step(bibitem, utype);
346 string const & lang = it.paragraph().getParLanguage(bp)->code();
347 autolabel_ = counters.theCounter(bibitem, lang);
349 autolabel_ = from_ascii("??");
354 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
357 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
358 // the "id" atttribute to get the document to validate. Probably, we will
359 // need to use "name" anyway, eventually, because some browsers do not
360 // handle jumping to ids. If we don't do that, though, we can just put the
361 // id into the span tag.
363 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
364 xs << html::CompTag("a", attrs);
365 xs << html::StartTag("span", "class='bibitemlabel'");
367 xs << html::EndTag("span");