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)
239 InsetBibitem const * bitem = 0;
241 // FIXME: this font is used unitialized for now but should be set to
242 // a proportional font. Here is what Georg Baum has to say about it:
244 bibitemWidest() is supposed to find the bibitem with the widest label in the
245 output, because that is needed as an argument of the bibliography
246 environment to determine the correct indentation. To be 100% correct we
247 would need the metrics of the font that is used in the output, but usually
248 we don't have access to these.
249 In practice, any proportional font is probably good enough, since we don't
250 need to know the final with, we only need to know the which label is the
252 Unless there is an easy way to get the metrics of the output font I suggest
253 to use a hardcoded font like "Times" or so.
255 It is very important that the result of this function is the same both with
256 and without GUI. After thinking about this it is clear that no Font
257 metrics should be used here, since these come from the gui. If we can't
258 easily get the LaTeX font metrics we should make our own poor mans font
259 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
265 ParagraphList::const_iterator it = buffer.paragraphs().begin();
266 ParagraphList::const_iterator end = buffer.paragraphs().end();
268 for (; it != end; ++it) {
269 if (it->insetList().empty())
271 Inset * inset = it->insetList().begin()->inset;
272 if (inset->lyxCode() != BIBITEM_CODE)
275 bitem = static_cast<InsetBibitem const *>(inset);
276 docstring const label = bitem->bibLabel();
278 // FIXME: we can't be sure using the following that the GUI
279 // version and the command-line version will give the same
282 //int const wx = use_gui?
283 // theFontMetrics(font).width(label): label.size();
285 // So for now we just use the label size in order to be sure
286 // that GUI and no-GUI gives the same bibitem (even if that is
287 // potentially the wrong one.
288 int const wx = label.size();
297 pair<docstring, docstring> latex_lbl =
298 runparams.encoding->latexString(lbl, runparams.dryrun);
299 return latex_lbl.first;
302 return from_ascii("99");
306 void InsetBibitem::collectBibKeys(InsetIterator const & it) const
308 docstring const key = getParam("key");
309 docstring const label = getParam("label");
310 BibTeXInfo keyvalmap(false);
312 keyvalmap.label(label);
314 BufferParams const & bp = buffer().masterBuffer()->params();
315 Counters & counters = bp.documentClass().counters();
316 docstring const bibitem = from_ascii("bibitem");
317 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
318 if (counters.hasCounter(bibitem))
319 counters.step(bibitem, InternalUpdate);
320 string const & lang = it.paragraph().getParLanguage(bp)->code();
321 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
324 DocIterator doc_it(it);
326 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
327 AS_STR_INSETS | AS_STR_SKIPDELETE);
328 buffer().addBibTeXInfo(key, keyvalmap);
332 // Update the counters of this inset and of its contents
333 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
335 BufferParams const & bp = buffer().masterBuffer()->params();
336 Counters & counters = bp.documentClass().counters();
337 docstring const bibitem = from_ascii("bibitem");
338 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
339 if (counters.hasCounter(bibitem))
340 counters.step(bibitem, utype);
341 string const & lang = it.paragraph().getParLanguage(bp)->code();
342 autolabel_ = counters.theCounter(bibitem, lang);
344 autolabel_ = from_ascii("??");
349 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
352 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
353 // the "id" atttribute to get the document to validate. Probably, we will
354 // need to use "name" anyway, eventually, because some browsers do not
355 // handle jumping to ids. If we don't do that, though, we can just put the
356 // id into the span tag.
358 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
359 xs << html::CompTag("a", attrs);
360 xs << html::StartTag("span", "class='bibitemlabel'");
362 xs << html::EndTag("span");