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 InsetCommand::read(lex);
200 if (prefixIs(getParam("key"), key_prefix)) {
201 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
202 Mutex::Locker lock(&counter_mutex);
203 key_counter = max(key_counter, key);
208 docstring InsetBibitem::bibLabel() const
210 BufferParams const & bp = buffer().masterBuffer()->params();
211 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
213 docstring const & label = getParam("label");
214 return label.empty() ? autolabel_ : label;
218 docstring InsetBibitem::screenLabel() const
220 return getParam("key") + " [" + bibLabel() + ']';
224 int InsetBibitem::plaintext(odocstringstream & os,
225 OutputParams const &, size_t) const
227 odocstringstream oss;
228 oss << '[' << bibLabel() << "] ";
230 docstring const str = oss.str();
238 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
240 BufferParams const & bp = buffer.masterBuffer()->params();
241 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
242 return from_ascii("99");
246 InsetBibitem const * bitem = 0;
248 // FIXME: this font is used unitialized for now but should be set to
249 // a proportional font. Here is what Georg Baum has to say about it:
251 bibitemWidest() is supposed to find the bibitem with the widest label in the
252 output, because that is needed as an argument of the bibliography
253 environment to determine the correct indentation. To be 100% correct we
254 would need the metrics of the font that is used in the output, but usually
255 we don't have access to these.
256 In practice, any proportional font is probably good enough, since we don't
257 need to know the final with, we only need to know the which label is the
259 Unless there is an easy way to get the metrics of the output font I suggest
260 to use a hardcoded font like "Times" or so.
262 It is very important that the result of this function is the same both with
263 and without GUI. After thinking about this it is clear that no Font
264 metrics should be used here, since these come from the gui. If we can't
265 easily get the LaTeX font metrics we should make our own poor mans font
266 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
272 ParagraphList::const_iterator it = buffer.paragraphs().begin();
273 ParagraphList::const_iterator end = buffer.paragraphs().end();
275 for (; it != end; ++it) {
276 if (it->insetList().empty())
278 Inset * inset = it->insetList().begin()->inset;
279 if (inset->lyxCode() != BIBITEM_CODE)
282 bitem = static_cast<InsetBibitem const *>(inset);
283 docstring const label = bitem->bibLabel();
285 // FIXME: we can't be sure using the following that the GUI
286 // version and the command-line version will give the same
289 //int const wx = use_gui?
290 // theFontMetrics(font).width(label): label.size();
292 // So for now we just use the label size in order to be sure
293 // that GUI and no-GUI gives the same bibitem (even if that is
294 // potentially the wrong one.
295 int const wx = label.size();
304 pair<docstring, docstring> latex_lbl =
305 runparams.encoding->latexString(lbl, runparams.dryrun);
306 return latex_lbl.first;
309 return from_ascii("99");
313 void InsetBibitem::collectBibKeys(InsetIterator const & it) const
315 docstring const key = getParam("key");
316 docstring const label = getParam("label");
317 BibTeXInfo keyvalmap(false);
319 keyvalmap.label(label);
321 BufferParams const & bp = buffer().masterBuffer()->params();
322 Counters & counters = bp.documentClass().counters();
323 docstring const bibitem = from_ascii("bibitem");
324 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
325 if (counters.hasCounter(bibitem))
326 counters.step(bibitem, InternalUpdate);
327 string const & lang = it.paragraph().getParLanguage(bp)->code();
328 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
331 DocIterator doc_it(it);
333 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
334 AS_STR_INSETS | AS_STR_SKIPDELETE);
335 buffer().addBibTeXInfo(key, keyvalmap);
339 // Update the counters of this inset and of its contents
340 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
342 BufferParams const & bp = buffer().masterBuffer()->params();
343 Counters & counters = bp.documentClass().counters();
344 docstring const bibitem = from_ascii("bibitem");
345 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
346 if (counters.hasCounter(bibitem))
347 counters.step(bibitem, utype);
348 string const & lang = it.paragraph().getParLanguage(bp)->code();
349 autolabel_ = counters.theCounter(bibitem, lang);
351 autolabel_ = from_ascii("??");
356 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
359 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
360 // the "id" atttribute to get the document to validate. Probably, we will
361 // need to use "name" anyway, eventually, because some browsers do not
362 // handle jumping to ids. If we don't do that, though, we can just put the
363 // id into the span tag.
365 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
366 xs << html::CompTag("a", attrs);
367 xs << html::StartTag("span", "class='bibitemlabel'");
369 xs << html::EndTag("span");