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"
18 #include "buffer_funcs.h"
19 #include "BufferParams.h"
20 #include "BufferView.h"
22 #include "DispatchResult.h"
24 #include "FuncRequest.h"
25 #include "InsetIterator.h"
26 #include "InsetList.h"
29 #include "output_xhtml.h"
30 #include "OutputParams.h"
31 #include "Paragraph.h"
32 #include "ParagraphList.h"
33 #include "ParIterator.h"
34 #include "TextClass.h"
36 #include "frontends/alert.h"
38 #include "support/convert.h"
39 #include "support/debug.h"
40 #include "support/docstream.h"
41 #include "support/gettext.h"
42 #include "support/lstrings.h"
45 using namespace lyx::support;
50 int InsetBibitem::key_counter = 0;
51 docstring const key_prefix = from_ascii("key-");
54 InsetBibitem::InsetBibitem(Buffer * buf, InsetCommandParams const & p)
55 : InsetCommand(buf, p, "bibitem")
57 buffer().invalidateBibinfoCache();
58 if (getParam("key").empty())
59 setParam("key", key_prefix + convert<docstring>(++key_counter));
63 InsetBibitem::~InsetBibitem()
66 buffer().invalidateBibinfoCache();
70 void InsetBibitem::initView()
72 updateCommand(getParam("key"));
76 void InsetBibitem::updateCommand(docstring const & new_key, bool)
78 docstring key = new_key;
80 vector<docstring> bibkeys = buffer().masterBibInfo().getKeys();
84 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("bibitem", to_utf8(cmd.argument()), p);
120 if (p.getCmdName().empty()) {
121 cur.noScreenUpdate();
124 docstring const & old_key = params()["key"];
125 docstring const & old_label = params()["label"];
126 docstring label = p["label"];
128 // definitions for escaping
130 static docstring const backslash = from_ascii("\\");
131 static docstring const lbrace = from_ascii("{");
132 static docstring const rbrace = from_ascii("}");
133 static char_type const chars_escape[6] = {
134 '&', '_', '$', '%', '#', '^'};
135 static char_type const brackets_escape[2] = {'[', ']'};
137 if (!label.empty()) {
138 // The characters in chars_name[] need to be changed to a command when
139 // they are in the name field.
140 for (int k = 0; k < 6; k++)
141 for (size_t i = 0, pos;
142 (pos = label.find(chars_escape[k], i)) != string::npos;
148 // only if not already escaped
149 if (label[previous] != '\\')
150 label.replace(pos, 1, backslash + chars_escape[k] + lbrace + rbrace);
152 // The characters '[' and ']' need to be put into braces
153 for (int k = 0; k < 2; k++)
154 for (size_t i = 0, pos;
155 (pos = label.find(brackets_escape[k], i)) != string::npos;
161 // only if not already escaped
162 if (label[previous] != '{')
163 label.replace(pos, 1, lbrace + brackets_escape[k] + rbrace);
166 if (old_label != label) {
168 cur.forceBufferUpdate();
169 buffer().invalidateBibinfoCache();
173 setParam("label", p["label"]);
174 if (p["key"] != old_key) {
175 updateCommand(p["key"]);
176 cur.bv().buffer().changeRefsIfUnique(old_key,
177 params()["key"], CITE_CODE);
178 cur.forceBufferUpdate();
179 buffer().invalidateBibinfoCache();
185 InsetCommand::doDispatch(cur, cmd);
191 void InsetBibitem::read(Lexer & lex)
193 InsetCommand::read(lex);
195 if (prefixIs(getParam("key"), key_prefix)) {
196 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
197 key_counter = max(key_counter, key);
202 docstring InsetBibitem::bibLabel() const
204 docstring const & label = getParam("label");
205 return label.empty() ? autolabel_ : label;
209 docstring InsetBibitem::screenLabel() const
211 return getParam("key") + " [" + bibLabel() + ']';
215 int InsetBibitem::plaintext(odocstream & os, OutputParams const &) const
217 odocstringstream oss;
218 oss << '[' << bibLabel() << "] ";
220 docstring const str = oss.str();
228 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
232 InsetBibitem const * bitem = 0;
234 // FIXME: this font is used unitialized for now but should be set to
235 // a proportional font. Here is what Georg Baum has to say about it:
237 bibitemWidest() is supposed to find the bibitem with the widest label in the
238 output, because that is needed as an argument of the bibliography
239 environment to determine the correct indentation. To be 100% correct we
240 would need the metrics of the font that is used in the output, but usually
241 we don't have access to these.
242 In practice, any proportional font is probably good enough, since we don't
243 need to know the final with, we only need to know the which label is the
245 Unless there is an easy way to get the metrics of the output font I suggest
246 to use a hardcoded font like "Times" or so.
248 It is very important that the result of this function is the same both with
249 and without GUI. After thinking about this it is clear that no Font
250 metrics should be used here, since these come from the gui. If we can't
251 easily get the LaTeX font metrics we should make our own poor mans font
252 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
258 ParagraphList::const_iterator it = buffer.paragraphs().begin();
259 ParagraphList::const_iterator end = buffer.paragraphs().end();
261 for (; it != end; ++it) {
262 if (it->insetList().empty())
264 Inset * inset = it->insetList().begin()->inset;
265 if (inset->lyxCode() != BIBITEM_CODE)
268 bitem = static_cast<InsetBibitem const *>(inset);
269 docstring const label = bitem->bibLabel();
271 // FIXME: we can't be sure using the following that the GUI
272 // version and the command-line version will give the same
275 //int const wx = use_gui?
276 // theFontMetrics(font).width(label): label.size();
278 // So for now we just use the label size in order to be sure
279 // that GUI and no-GUI gives the same bibitem (even if that is
280 // potentially the wrong one.
281 int const wx = label.size();
291 for (size_t n = 0; n < lbl.size(); ++n) {
293 latex_lbl += runparams.encoding->latexChar(lbl[n]);
294 } catch (EncodingException & /* e */) {
295 if (runparams.dryrun) {
296 latex_lbl += "<" + _("LyX Warning: ")
297 + _("uncodable character") + " '";
298 latex_lbl += docstring(1, lbl[n]);
306 return from_ascii("99");
310 void InsetBibitem::fillWithBibKeys(BiblioInfo & keys, InsetIterator const & it) const
312 docstring const key = getParam("key");
313 BibTeXInfo keyvalmap(false);
314 keyvalmap.label(bibLabel());
315 DocIterator doc_it(it);
317 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString();
318 keys[key] = keyvalmap;
322 // Update the counters of this inset and of its contents
323 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
325 BufferParams const & bp = buffer().masterBuffer()->params();
326 Counters & counters = bp.documentClass().counters();
327 docstring const bibitem = from_ascii("bibitem");
328 if (counters.hasCounter(bibitem) && getParam("label").empty()) {
329 counters.step(bibitem, utype);
330 string const & lang = it.paragraph().getParLanguage(bp)->code();
331 autolabel_ = counters.theCounter(bibitem, lang);
333 autolabel_ = from_ascii("??");
338 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
341 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
342 // the "id" atttribute to get the document to validate. Probably, we will
343 // need to use "name" anyway, eventually, because some browsers do not
344 // handle jumping to ids. If we don't do that, though, we can just put the
345 // id into the span tag.
346 string const attrs = "id='" + to_utf8(getParam("label")) + "'";
347 xs << html::CompTag("a", attrs);
348 xs << html::StartTag("span", "class='bibitemlabel'");
350 xs << html::EndTag("span");