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();
125 docstring const & old_key = params()["key"];
126 docstring const & old_label = params()["label"];
127 docstring label = p["label"];
129 // definitions for escaping
131 static docstring const backslash = from_ascii("\\");
132 static docstring const lbrace = from_ascii("{");
133 static docstring const rbrace = from_ascii("}");
134 static char_type const chars_escape[6] = {
135 '&', '_', '$', '%', '#', '^'};
136 static char_type const brackets_escape[2] = {'[', ']'};
138 if (!label.empty()) {
139 // The characters in chars_name[] need to be changed to a command when
140 // they are in the name field.
141 for (int k = 0; k < 6; k++)
142 for (size_t i = 0, pos;
143 (pos = label.find(chars_escape[k], i)) != string::npos;
149 // only if not already escaped
150 if (label[previous] != '\\')
151 label.replace(pos, 1, backslash + chars_escape[k] + lbrace + rbrace);
153 // The characters '[' and ']' need to be put into braces
154 for (int k = 0; k < 2; k++)
155 for (size_t i = 0, pos;
156 (pos = label.find(brackets_escape[k], i)) != string::npos;
162 // only if not already escaped
163 if (label[previous] != '{')
164 label.replace(pos, 1, lbrace + brackets_escape[k] + rbrace);
167 if (old_label != label) {
169 cur.forceBufferUpdate();
170 buffer().invalidateBibinfoCache();
174 setParam("label", p["label"]);
175 if (p["key"] != old_key) {
176 updateCommand(p["key"]);
177 cur.bv().buffer().changeRefsIfUnique(old_key,
178 params()["key"], CITE_CODE);
179 cur.forceBufferUpdate();
180 buffer().invalidateBibinfoCache();
186 InsetCommand::doDispatch(cur, cmd);
192 void InsetBibitem::read(Lexer & lex)
194 InsetCommand::read(lex);
196 if (prefixIs(getParam("key"), key_prefix)) {
197 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
198 key_counter = max(key_counter, key);
203 docstring InsetBibitem::bibLabel() const
205 docstring const & label = getParam("label");
206 return label.empty() ? autolabel_ : label;
210 docstring InsetBibitem::screenLabel() const
212 return getParam("key") + " [" + bibLabel() + ']';
216 int InsetBibitem::plaintext(odocstream & os, OutputParams const &) const
218 odocstringstream oss;
219 oss << '[' << bibLabel() << "] ";
221 docstring const str = oss.str();
229 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
233 InsetBibitem const * bitem = 0;
235 // FIXME: this font is used unitialized for now but should be set to
236 // a proportional font. Here is what Georg Baum has to say about it:
238 bibitemWidest() is supposed to find the bibitem with the widest label in the
239 output, because that is needed as an argument of the bibliography
240 environment to determine the correct indentation. To be 100% correct we
241 would need the metrics of the font that is used in the output, but usually
242 we don't have access to these.
243 In practice, any proportional font is probably good enough, since we don't
244 need to know the final with, we only need to know the which label is the
246 Unless there is an easy way to get the metrics of the output font I suggest
247 to use a hardcoded font like "Times" or so.
249 It is very important that the result of this function is the same both with
250 and without GUI. After thinking about this it is clear that no Font
251 metrics should be used here, since these come from the gui. If we can't
252 easily get the LaTeX font metrics we should make our own poor mans font
253 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
259 ParagraphList::const_iterator it = buffer.paragraphs().begin();
260 ParagraphList::const_iterator end = buffer.paragraphs().end();
262 for (; it != end; ++it) {
263 if (it->insetList().empty())
265 Inset * inset = it->insetList().begin()->inset;
266 if (inset->lyxCode() != BIBITEM_CODE)
269 bitem = static_cast<InsetBibitem const *>(inset);
270 docstring const label = bitem->bibLabel();
272 // FIXME: we can't be sure using the following that the GUI
273 // version and the command-line version will give the same
276 //int const wx = use_gui?
277 // theFontMetrics(font).width(label): label.size();
279 // So for now we just use the label size in order to be sure
280 // that GUI and no-GUI gives the same bibitem (even if that is
281 // potentially the wrong one.
282 int const wx = label.size();
292 for (size_t n = 0; n < lbl.size(); ++n) {
294 latex_lbl += runparams.encoding->latexChar(lbl[n]);
295 } catch (EncodingException & /* e */) {
296 if (runparams.dryrun) {
297 latex_lbl += "<" + _("LyX Warning: ")
298 + _("uncodable character") + " '";
299 latex_lbl += docstring(1, lbl[n]);
307 return from_ascii("99");
311 void InsetBibitem::fillWithBibKeys(BiblioInfo & keys, InsetIterator const & it) const
313 docstring const key = getParam("key");
314 BibTeXInfo keyvalmap(false);
315 keyvalmap.label(bibLabel());
316 DocIterator doc_it(it);
318 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString();
319 keys[key] = keyvalmap;
323 // Update the counters of this inset and of its contents
324 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
326 BufferParams const & bp = buffer().masterBuffer()->params();
327 Counters & counters = bp.documentClass().counters();
328 docstring const bibitem = from_ascii("bibitem");
329 if (counters.hasCounter(bibitem) && getParam("label").empty()) {
330 counters.step(bibitem, utype);
331 string const & lang = it.paragraph().getParLanguage(bp)->code();
332 autolabel_ = counters.theCounter(bibitem, lang);
334 autolabel_ = from_ascii("??");
339 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
342 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
343 // the "id" atttribute to get the document to validate. Probably, we will
344 // need to use "name" anyway, eventually, because some browsers do not
345 // handle jumping to ids. If we don't do that, though, we can just put the
346 // id into the span tag.
347 string const attrs = "id='" + to_utf8(getParam("label")) + "'";
348 xs << html::CompTag("a", attrs);
349 xs << html::StartTag("span", "class='bibitemlabel'");
351 xs << html::EndTag("span");