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;
51 int InsetBibitem::key_counter = 0;
52 docstring const key_prefix = from_ascii("key-");
55 InsetBibitem::InsetBibitem(Buffer * buf, InsetCommandParams const & p)
56 : InsetCommand(buf, p)
58 buffer().invalidateBibinfoCache();
59 if (getParam("key").empty())
60 setParam("key", key_prefix + convert<docstring>(++key_counter));
64 InsetBibitem::~InsetBibitem()
67 buffer().invalidateBibinfoCache();
71 void InsetBibitem::initView()
73 updateCommand(getParam("key"));
77 void InsetBibitem::updateCommand(docstring const & new_key, bool)
79 docstring key = new_key;
81 vector<docstring> bibkeys = buffer().masterBibInfo().getKeys();
85 if (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
86 // generate unique label
87 key = new_key + '-' + convert<docstring>(i);
88 while (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
90 key = new_key + '-' + convert<docstring>(i);
92 frontend::Alert::warning(_("Keys must be unique!"),
93 bformat(_("The key %1$s already exists,\n"
94 "it will be changed to %2$s."), new_key, key));
97 buffer().invalidateBibinfoCache();
101 ParamInfo const & InsetBibitem::findInfo(string const & /* cmdName */)
103 static ParamInfo param_info_;
104 if (param_info_.empty()) {
105 param_info_.add("label", ParamInfo::LATEX_OPTIONAL,
106 ParamInfo::HANDLING_LATEXIFY);
107 param_info_.add("key", ParamInfo::LATEX_REQUIRED,
108 ParamInfo::HANDLING_ESCAPE);
114 void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
116 switch (cmd.action()) {
118 case LFUN_INSET_MODIFY: {
119 InsetCommandParams p(BIBITEM_CODE);
120 InsetCommand::string2params(to_utf8(cmd.argument()), p);
121 if (p.getCmdName().empty()) {
122 cur.noScreenUpdate();
128 docstring const & old_key = params()["key"];
129 docstring const & old_label = params()["label"];
130 docstring label = p["label"];
132 // definitions for escaping
134 static docstring const backslash = from_ascii("\\");
135 static docstring const lbrace = from_ascii("{");
136 static docstring const rbrace = from_ascii("}");
137 static char_type const chars_escape[6] = {
138 '&', '_', '$', '%', '#', '^'};
139 static char_type const brackets_escape[2] = {'[', ']'};
141 if (!label.empty()) {
142 // The characters in chars_name[] need to be changed to a command when
143 // they are in the name field.
144 for (int k = 0; k < 6; k++)
145 for (size_t i = 0, pos;
146 (pos = label.find(chars_escape[k], i)) != string::npos;
152 // only if not already escaped
153 if (label[previous] != '\\')
154 label.replace(pos, 1, backslash + chars_escape[k] + lbrace + rbrace);
156 // The characters '[' and ']' need to be put into braces
157 for (int k = 0; k < 2; k++)
158 for (size_t i = 0, pos;
159 (pos = label.find(brackets_escape[k], i)) != string::npos;
165 // only if not already escaped
166 if (label[previous] != '{')
167 label.replace(pos, 1, lbrace + brackets_escape[k] + rbrace);
171 if (old_label != label) {
173 cur.forceBufferUpdate();
174 buffer().invalidateBibinfoCache();
177 setParam("label", p["label"]);
178 if (p["key"] != old_key) {
179 updateCommand(p["key"]);
180 cur.bv().buffer().changeRefsIfUnique(old_key, params()["key"]);
181 cur.forceBufferUpdate();
182 buffer().invalidateBibinfoCache();
188 InsetCommand::doDispatch(cur, cmd);
194 void InsetBibitem::read(Lexer & lex)
196 InsetCommand::read(lex);
198 if (prefixIs(getParam("key"), key_prefix)) {
199 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
200 key_counter = max(key_counter, key);
205 docstring InsetBibitem::bibLabel() const
207 BufferParams const & bp = buffer().masterBuffer()->params();
208 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
210 docstring const & label = getParam("label");
211 return label.empty() ? autolabel_ : label;
215 docstring InsetBibitem::screenLabel() const
217 return getParam("key") + " [" + bibLabel() + ']';
221 int InsetBibitem::plaintext(odocstringstream & os,
222 OutputParams const &, size_t) const
224 odocstringstream oss;
225 oss << '[' << bibLabel() << "] ";
227 docstring const str = oss.str();
235 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
237 BufferParams const & bp = buffer.masterBuffer()->params();
238 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
239 return from_ascii("99");
243 InsetBibitem const * bitem = 0;
245 // FIXME: this font is used unitialized for now but should be set to
246 // a proportional font. Here is what Georg Baum has to say about it:
248 bibitemWidest() is supposed to find the bibitem with the widest label in the
249 output, because that is needed as an argument of the bibliography
250 environment to determine the correct indentation. To be 100% correct we
251 would need the metrics of the font that is used in the output, but usually
252 we don't have access to these.
253 In practice, any proportional font is probably good enough, since we don't
254 need to know the final with, we only need to know the which label is the
256 Unless there is an easy way to get the metrics of the output font I suggest
257 to use a hardcoded font like "Times" or so.
259 It is very important that the result of this function is the same both with
260 and without GUI. After thinking about this it is clear that no Font
261 metrics should be used here, since these come from the gui. If we can't
262 easily get the LaTeX font metrics we should make our own poor mans font
263 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
269 ParagraphList::const_iterator it = buffer.paragraphs().begin();
270 ParagraphList::const_iterator end = buffer.paragraphs().end();
272 for (; it != end; ++it) {
273 if (it->insetList().empty())
275 Inset * inset = it->insetList().begin()->inset;
276 if (inset->lyxCode() != BIBITEM_CODE)
279 bitem = static_cast<InsetBibitem const *>(inset);
280 docstring const label = bitem->bibLabel();
282 // FIXME: we can't be sure using the following that the GUI
283 // version and the command-line version will give the same
286 //int const wx = use_gui?
287 // theFontMetrics(font).width(label): label.size();
289 // So for now we just use the label size in order to be sure
290 // that GUI and no-GUI gives the same bibitem (even if that is
291 // potentially the wrong one.
292 int const wx = label.size();
301 pair<docstring, docstring> latex_lbl =
302 runparams.encoding->latexString(lbl, runparams.dryrun);
303 return latex_lbl.first;
306 return from_ascii("99");
310 void InsetBibitem::collectBibKeys(InsetIterator const & it) const
312 docstring const key = getParam("key");
313 docstring const label = getParam("label");
314 BibTeXInfo keyvalmap(false);
316 keyvalmap.label(label);
318 BufferParams const & bp = buffer().masterBuffer()->params();
319 Counters & counters = bp.documentClass().counters();
320 docstring const bibitem = from_ascii("bibitem");
321 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
322 if (counters.hasCounter(bibitem))
323 counters.step(bibitem, InternalUpdate);
324 string const & lang = it.paragraph().getParLanguage(bp)->code();
325 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
328 DocIterator doc_it(it);
330 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
331 AS_STR_INSETS | AS_STR_SKIPDELETE);
332 buffer().addBibTeXInfo(key, keyvalmap);
336 // Update the counters of this inset and of its contents
337 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
339 BufferParams const & bp = buffer().masterBuffer()->params();
340 Counters & counters = bp.documentClass().counters();
341 docstring const bibitem = from_ascii("bibitem");
342 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
343 if (counters.hasCounter(bibitem))
344 counters.step(bibitem, utype);
345 string const & lang = it.paragraph().getParLanguage(bp)->code();
346 autolabel_ = counters.theCounter(bibitem, lang);
348 autolabel_ = from_ascii("??");
353 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
356 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
357 // the "id" atttribute to get the document to validate. Probably, we will
358 // need to use "name" anyway, eventually, because some browsers do not
359 // handle jumping to ids. If we don't do that, though, we can just put the
360 // id into the span tag.
362 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
363 xs << html::CompTag("a", attrs);
364 xs << html::StartTag("span", "class='bibitemlabel'");
366 xs << html::EndTag("span");