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;
81 vector<docstring> bibkeys = buffer().masterBibInfo().getKeys();
83 if (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
85 // generate unique label
86 key = new_key + '-' + convert<docstring>(i);
87 while (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
89 key = new_key + '-' + convert<docstring>(i);
91 frontend::Alert::warning(_("Keys must be unique!"),
92 bformat(_("The key %1$s already exists,\n"
93 "it will be changed to %2$s."), new_key, key));
96 buffer().invalidateBibinfoCache();
100 ParamInfo const & InsetBibitem::findInfo(string const & /* cmdName */)
102 static ParamInfo param_info_;
103 if (param_info_.empty()) {
104 param_info_.add("label", ParamInfo::LATEX_OPTIONAL,
105 ParamInfo::HANDLING_LATEXIFY);
106 param_info_.add("key", ParamInfo::LATEX_REQUIRED,
107 ParamInfo::HANDLING_ESCAPE);
113 void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
115 switch (cmd.action()) {
117 case LFUN_INSET_MODIFY: {
118 InsetCommandParams p(BIBITEM_CODE);
119 InsetCommand::string2params(to_utf8(cmd.argument()), p);
120 if (p.getCmdName().empty()) {
121 cur.noScreenUpdate();
127 docstring const & old_key = params()["key"];
128 docstring const & old_label = params()["label"];
129 docstring label = p["label"];
131 // definitions for escaping
132 static docstring const backslash = from_ascii("\\");
133 static docstring const lbrace = from_ascii("{");
134 static docstring const rbrace = from_ascii("}");
135 static char_type const chars_escape[6] = {
136 '&', '_', '$', '%', '#', '^'};
137 static char_type const brackets_escape[2] = {'[', ']'};
139 if (!label.empty()) {
141 // The characters in chars_name[] need to be changed to a command when
142 // they are in the name field.
143 for (int k = 0; k < 6; k++)
144 for (size_t i = 0, pos;
145 (pos = label.find(chars_escape[k], i)) != string::npos;
151 // only if not already escaped
152 if (label[previous] != '\\')
153 label.replace(pos, 1, backslash + chars_escape[k] + lbrace + rbrace);
155 // The characters '[' and ']' need to be put into braces
156 for (int k = 0; k < 2; k++)
157 for (size_t i = 0, pos;
158 (pos = label.find(brackets_escape[k], i)) != string::npos;
164 // only if not already escaped
165 if (label[previous] != '{')
166 label.replace(pos, 1, lbrace + brackets_escape[k] + rbrace);
170 if (old_label != label) {
172 cur.forceBufferUpdate();
173 buffer().invalidateBibinfoCache();
176 setParam("label", p["label"]);
177 if (p["key"] != old_key) {
178 updateCommand(p["key"]);
179 cur.bv().buffer().changeRefsIfUnique(old_key, params()["key"]);
180 cur.forceBufferUpdate();
181 buffer().invalidateBibinfoCache();
187 InsetCommand::doDispatch(cur, cmd);
193 void InsetBibitem::read(Lexer & lex)
195 InsetCommand::read(lex);
197 if (prefixIs(getParam("key"), key_prefix)) {
198 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
199 key_counter = max(key_counter, key);
204 docstring InsetBibitem::bibLabel() const
206 BufferParams const & bp = buffer().masterBuffer()->params();
207 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
209 docstring const & label = getParam("label");
210 return label.empty() ? autolabel_ : label;
214 docstring InsetBibitem::screenLabel() const
216 return getParam("key") + " [" + bibLabel() + ']';
220 int InsetBibitem::plaintext(odocstringstream & os,
221 OutputParams const &, size_t) const
223 odocstringstream oss;
224 oss << '[' << bibLabel() << "] ";
226 docstring const str = oss.str();
234 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
236 BufferParams const & bp = buffer.masterBuffer()->params();
237 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
238 return from_ascii("99");
242 InsetBibitem const * bitem = 0;
244 // FIXME: this font is used unitialized for now but should be set to
245 // a proportional font. Here is what Georg Baum has to say about it:
247 bibitemWidest() is supposed to find the bibitem with the widest label in the
248 output, because that is needed as an argument of the bibliography
249 environment to determine the correct indentation. To be 100% correct we
250 would need the metrics of the font that is used in the output, but usually
251 we don't have access to these.
252 In practice, any proportional font is probably good enough, since we don't
253 need to know the final with, we only need to know the which label is the
255 Unless there is an easy way to get the metrics of the output font I suggest
256 to use a hardcoded font like "Times" or so.
258 It is very important that the result of this function is the same both with
259 and without GUI. After thinking about this it is clear that no Font
260 metrics should be used here, since these come from the gui. If we can't
261 easily get the LaTeX font metrics we should make our own poor mans font
262 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
268 ParagraphList::const_iterator it = buffer.paragraphs().begin();
269 ParagraphList::const_iterator end = buffer.paragraphs().end();
271 for (; it != end; ++it) {
272 if (it->insetList().empty())
274 Inset * inset = it->insetList().begin()->inset;
275 if (inset->lyxCode() != BIBITEM_CODE)
278 bitem = static_cast<InsetBibitem const *>(inset);
279 docstring const label = bitem->bibLabel();
281 // FIXME: we can't be sure using the following that the GUI
282 // version and the command-line version will give the same
285 //int const wx = use_gui?
286 // theFontMetrics(font).width(label): label.size();
288 // So for now we just use the label size in order to be sure
289 // that GUI and no-GUI gives the same bibitem (even if that is
290 // potentially the wrong one.
291 int const wx = label.size();
300 pair<docstring, docstring> latex_lbl =
301 runparams.encoding->latexString(lbl, runparams.dryrun);
302 return latex_lbl.first;
305 return from_ascii("99");
309 void InsetBibitem::collectBibKeys(InsetIterator const & it) const
311 docstring const key = getParam("key");
312 docstring const label = getParam("label");
313 BibTeXInfo keyvalmap(false);
315 keyvalmap.label(label);
317 BufferParams const & bp = buffer().masterBuffer()->params();
318 Counters & counters = bp.documentClass().counters();
319 docstring const bibitem = from_ascii("bibitem");
320 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
321 if (counters.hasCounter(bibitem))
322 counters.step(bibitem, InternalUpdate);
323 string const & lang = it.paragraph().getParLanguage(bp)->code();
324 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
327 DocIterator doc_it(it);
329 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
330 AS_STR_INSETS | AS_STR_SKIPDELETE);
331 buffer().addBibTeXInfo(key, keyvalmap);
335 // Update the counters of this inset and of its contents
336 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
338 BufferParams const & bp = buffer().masterBuffer()->params();
339 Counters & counters = bp.documentClass().counters();
340 docstring const bibitem = from_ascii("bibitem");
341 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
342 if (counters.hasCounter(bibitem))
343 counters.step(bibitem, utype);
344 string const & lang = it.paragraph().getParLanguage(bp)->code();
345 autolabel_ = counters.theCounter(bibitem, lang);
347 autolabel_ = from_ascii("??");
352 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
355 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
356 // the "id" atttribute to get the document to validate. Probably, we will
357 // need to use "name" anyway, eventually, because some browsers do not
358 // handle jumping to ids. If we don't do that, though, we can just put the
359 // id into the span tag.
361 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
362 xs << html::CompTag("a", attrs);
363 xs << html::StartTag("span", "class='bibitemlabel'");
365 xs << html::EndTag("span");