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,
181 params()["key"], CITE_CODE);
182 cur.forceBufferUpdate();
183 buffer().invalidateBibinfoCache();
189 InsetCommand::doDispatch(cur, cmd);
195 void InsetBibitem::read(Lexer & lex)
197 InsetCommand::read(lex);
199 if (prefixIs(getParam("key"), key_prefix)) {
200 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
201 key_counter = max(key_counter, key);
206 docstring InsetBibitem::bibLabel() const
208 docstring const & label = getParam("label");
209 return label.empty() ? autolabel_ : label;
213 docstring InsetBibitem::screenLabel() const
215 return getParam("key") + " [" + bibLabel() + ']';
219 int InsetBibitem::plaintext(odocstream & os, OutputParams const &) const
221 odocstringstream oss;
222 oss << '[' << bibLabel() << "] ";
224 docstring const str = oss.str();
232 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
236 InsetBibitem const * bitem = 0;
238 // FIXME: this font is used unitialized for now but should be set to
239 // a proportional font. Here is what Georg Baum has to say about it:
241 bibitemWidest() is supposed to find the bibitem with the widest label in the
242 output, because that is needed as an argument of the bibliography
243 environment to determine the correct indentation. To be 100% correct we
244 would need the metrics of the font that is used in the output, but usually
245 we don't have access to these.
246 In practice, any proportional font is probably good enough, since we don't
247 need to know the final with, we only need to know the which label is the
249 Unless there is an easy way to get the metrics of the output font I suggest
250 to use a hardcoded font like "Times" or so.
252 It is very important that the result of this function is the same both with
253 and without GUI. After thinking about this it is clear that no Font
254 metrics should be used here, since these come from the gui. If we can't
255 easily get the LaTeX font metrics we should make our own poor mans font
256 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
262 ParagraphList::const_iterator it = buffer.paragraphs().begin();
263 ParagraphList::const_iterator end = buffer.paragraphs().end();
265 for (; it != end; ++it) {
266 if (it->insetList().empty())
268 Inset * inset = it->insetList().begin()->inset;
269 if (inset->lyxCode() != BIBITEM_CODE)
272 bitem = static_cast<InsetBibitem const *>(inset);
273 docstring const label = bitem->bibLabel();
275 // FIXME: we can't be sure using the following that the GUI
276 // version and the command-line version will give the same
279 //int const wx = use_gui?
280 // theFontMetrics(font).width(label): label.size();
282 // So for now we just use the label size in order to be sure
283 // that GUI and no-GUI gives the same bibitem (even if that is
284 // potentially the wrong one.
285 int const wx = label.size();
295 for (size_t n = 0; n < lbl.size(); ++n) {
297 latex_lbl += runparams.encoding->latexChar(lbl[n]).first;
298 } catch (EncodingException & /* e */) {
299 if (runparams.dryrun) {
300 latex_lbl += "<" + _("LyX Warning: ")
301 + _("uncodable character") + " '";
302 latex_lbl += docstring(1, lbl[n]);
310 return from_ascii("99");
314 void InsetBibitem::collectBibKeys(InsetIterator const & it) const
316 docstring const key = getParam("key");
317 docstring const label = getParam("label");
318 BibTeXInfo keyvalmap(false);
320 keyvalmap.label(label);
321 DocIterator doc_it(it);
323 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
324 AS_STR_INSETS | AS_STR_SKIPDELETE);
325 buffer().addBibTeXInfo(key, keyvalmap);
329 // Update the counters of this inset and of its contents
330 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
332 BufferParams const & bp = buffer().masterBuffer()->params();
333 Counters & counters = bp.documentClass().counters();
334 docstring const bibitem = from_ascii("bibitem");
335 if (getParam("label").empty()) {
336 if (counters.hasCounter(bibitem))
337 counters.step(bibitem, utype);
338 string const & lang = it.paragraph().getParLanguage(bp)->code();
339 autolabel_ = counters.theCounter(bibitem, lang);
341 autolabel_ = from_ascii("??");
346 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
349 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
350 // the "id" atttribute to get the document to validate. Probably, we will
351 // need to use "name" anyway, eventually, because some browsers do not
352 // handle jumping to ids. If we don't do that, though, we can just put the
353 // id into the span tag.
354 string const attrs = "id='LyXCite-" + to_utf8(getParam("key")) + "'";
355 xs << html::CompTag("a", attrs);
356 xs << html::StartTag("span", "class='bibitemlabel'");
358 xs << html::EndTag("span");