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"
44 #include "support/mutex.h"
47 using namespace lyx::support;
52 int InsetBibitem::key_counter = 0;
53 static Mutex counter_mutex;
54 docstring const key_prefix = from_ascii("key-");
57 InsetBibitem::InsetBibitem(Buffer * buf, InsetCommandParams const & p)
58 : InsetCommand(buf, p)
60 buffer().invalidateBibinfoCache();
61 if (getParam("key").empty()) {
62 Mutex::Locker lock(&counter_mutex);
63 setParam("key", key_prefix + convert<docstring>(++key_counter));
68 InsetBibitem::~InsetBibitem()
71 buffer().invalidateBibinfoCache();
75 void InsetBibitem::initView()
77 updateCommand(getParam("key"));
81 void InsetBibitem::updateCommand(docstring const & new_key, bool)
83 docstring key = new_key;
84 vector<docstring> bibkeys = buffer().masterBibInfo().getKeys();
86 if (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
88 // generate unique label
89 key = new_key + '-' + convert<docstring>(i);
90 while (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
92 key = new_key + '-' + convert<docstring>(i);
94 frontend::Alert::warning(_("Keys must be unique!"),
95 bformat(_("The key %1$s already exists,\n"
96 "it will be changed to %2$s."), new_key, key));
99 buffer().invalidateBibinfoCache();
103 ParamInfo const & InsetBibitem::findInfo(string const & /* cmdName */)
105 static ParamInfo param_info_;
106 if (param_info_.empty()) {
107 param_info_.add("label", ParamInfo::LATEX_OPTIONAL,
108 ParamInfo::HANDLING_LATEXIFY);
109 param_info_.add("key", ParamInfo::LATEX_REQUIRED,
110 ParamInfo::HANDLING_ESCAPE);
116 void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
118 switch (cmd.action()) {
120 case LFUN_INSET_MODIFY: {
121 InsetCommandParams p(BIBITEM_CODE);
122 InsetCommand::string2params(to_utf8(cmd.argument()), p);
123 if (p.getCmdName().empty()) {
124 cur.noScreenUpdate();
130 docstring const & old_key = params()["key"];
131 docstring const & old_label = params()["label"];
132 docstring label = p["label"];
134 // 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()) {
144 // The characters in chars_name[] need to be changed to a command when
145 // they are in the name field.
146 for (int k = 0; k < 6; k++)
147 for (size_t i = 0, pos;
148 (pos = label.find(chars_escape[k], i)) != string::npos;
154 // only if not already escaped
155 if (label[previous] != '\\')
156 label.replace(pos, 1, backslash + chars_escape[k] + lbrace + rbrace);
158 // The characters '[' and ']' need to be put into braces
159 for (int k = 0; k < 2; k++)
160 for (size_t i = 0, pos;
161 (pos = label.find(brackets_escape[k], i)) != string::npos;
167 // only if not already escaped
168 if (label[previous] != '{')
169 label.replace(pos, 1, lbrace + brackets_escape[k] + rbrace);
173 if (old_label != label) {
175 cur.forceBufferUpdate();
176 buffer().invalidateBibinfoCache();
179 setParam("label", p["label"]);
180 if (p["key"] != old_key) {
181 updateCommand(p["key"]);
182 cur.bv().buffer().changeRefsIfUnique(old_key, params()["key"]);
183 cur.forceBufferUpdate();
184 buffer().invalidateBibinfoCache();
190 InsetCommand::doDispatch(cur, cmd);
196 void InsetBibitem::read(Lexer & lex)
198 InsetCommandParams * icp = const_cast<InsetCommandParams *>(¶ms());
199 icp->Read(lex, &buffer());
201 if (prefixIs(getParam("key"), key_prefix)) {
202 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
203 Mutex::Locker lock(&counter_mutex);
204 key_counter = max(key_counter, key);
209 docstring InsetBibitem::bibLabel() const
211 BufferParams const & bp = buffer().masterBuffer()->params();
212 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
214 docstring const & label = getParam("label");
215 return label.empty() ? autolabel_ : label;
219 docstring InsetBibitem::screenLabel() const
221 return getParam("key") + " [" + bibLabel() + ']';
225 int InsetBibitem::plaintext(odocstringstream & os,
226 OutputParams const &, size_t) const
228 odocstringstream oss;
229 oss << '[' << bibLabel() << "] ";
231 docstring const str = oss.str();
239 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
241 BufferParams const & bp = buffer.masterBuffer()->params();
242 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
243 return from_ascii("99");
247 InsetBibitem const * bitem = 0;
249 // FIXME: this font is used unitialized for now but should be set to
250 // a proportional font. Here is what Georg Baum has to say about it:
252 bibitemWidest() is supposed to find the bibitem with the widest label in the
253 output, because that is needed as an argument of the bibliography
254 environment to determine the correct indentation. To be 100% correct we
255 would need the metrics of the font that is used in the output, but usually
256 we don't have access to these.
257 In practice, any proportional font is probably good enough, since we don't
258 need to know the final with, we only need to know the which label is the
260 Unless there is an easy way to get the metrics of the output font I suggest
261 to use a hardcoded font like "Times" or so.
263 It is very important that the result of this function is the same both with
264 and without GUI. After thinking about this it is clear that no Font
265 metrics should be used here, since these come from the gui. If we can't
266 easily get the LaTeX font metrics we should make our own poor mans font
267 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
273 ParagraphList::const_iterator it = buffer.paragraphs().begin();
274 ParagraphList::const_iterator end = buffer.paragraphs().end();
276 for (; it != end; ++it) {
277 if (it->insetList().empty())
279 Inset * inset = it->insetList().begin()->inset;
280 if (inset->lyxCode() != BIBITEM_CODE)
283 bitem = static_cast<InsetBibitem const *>(inset);
284 docstring const label = bitem->bibLabel();
286 // FIXME: we can't be sure using the following that the GUI
287 // version and the command-line version will give the same
290 //int const wx = use_gui?
291 // theFontMetrics(font).width(label): label.size();
293 // So for now we just use the label size in order to be sure
294 // that GUI and no-GUI gives the same bibitem (even if that is
295 // potentially the wrong one.
296 int const wx = label.size();
305 pair<docstring, docstring> latex_lbl =
306 runparams.encoding->latexString(lbl, runparams.dryrun);
307 return latex_lbl.first;
310 return from_ascii("99");
314 void InsetBibitem::collectBibKeys(InsetIterator const & it) const
316 docstring const key = getParam("key");
317 docstring const label = getParam("label");
318 BibTeXInfo keyvalmap(false);
320 keyvalmap.label(label);
322 BufferParams const & bp = buffer().masterBuffer()->params();
323 Counters & counters = bp.documentClass().counters();
324 docstring const bibitem = from_ascii("bibitem");
325 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
326 if (counters.hasCounter(bibitem))
327 counters.step(bibitem, InternalUpdate);
328 string const & lang = it.paragraph().getParLanguage(bp)->code();
329 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
332 DocIterator doc_it(it);
334 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
335 AS_STR_INSETS | AS_STR_SKIPDELETE);
336 buffer().addBibTeXInfo(key, keyvalmap);
340 // Update the counters of this inset and of its contents
341 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
343 BufferParams const & bp = buffer().masterBuffer()->params();
344 Counters & counters = bp.documentClass().counters();
345 docstring const bibitem = from_ascii("bibitem");
346 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
347 if (counters.hasCounter(bibitem))
348 counters.step(bibitem, utype);
349 string const & lang = it.paragraph().getParLanguage(bp)->code();
350 autolabel_ = counters.theCounter(bibitem, lang);
352 autolabel_ = from_ascii("??");
357 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
360 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
361 // the "id" atttribute to get the document to validate. Probably, we will
362 // need to use "name" anyway, eventually, because some browsers do not
363 // handle jumping to ids. If we don't do that, though, we can just put the
364 // id into the span tag.
366 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
367 xs << html::CompTag("a", attrs);
368 xs << html::StartTag("span", "class='bibitemlabel'");
370 xs << html::EndTag("span");