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/lassert.h"
40 #include "support/convert.h"
41 #include "support/debug.h"
42 #include "support/docstream.h"
43 #include "support/gettext.h"
44 #include "support/lstrings.h"
45 #include "support/mutex.h"
48 using namespace lyx::support;
53 int InsetBibitem::key_counter = 0;
54 static Mutex counter_mutex;
55 docstring const key_prefix = from_ascii("key-");
58 InsetBibitem::InsetBibitem(Buffer * buf, InsetCommandParams const & p)
59 : InsetCommand(buf, p)
61 buffer().invalidateBibinfoCache();
62 if (getParam("key").empty()) {
63 Mutex::Locker lock(&counter_mutex);
64 setParam("key", key_prefix + convert<docstring>(++key_counter));
69 InsetBibitem::~InsetBibitem()
71 if (isBufferLoaded()) {
72 /* We do not use buffer() because Coverity believes that this
73 * may throw an exception. Actually this code path is not
74 * taken when buffer_ == 0 */
75 buffer_->invalidateBibinfoCache();
80 void InsetBibitem::initView()
82 updateCommand(getParam("key"));
86 void InsetBibitem::updateCommand(docstring const & new_key, bool)
88 docstring key = new_key;
89 vector<docstring> bibkeys = buffer().masterBibInfo().getKeys();
91 if (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
93 // generate unique label
94 key = new_key + '-' + convert<docstring>(i);
95 while (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
97 key = new_key + '-' + convert<docstring>(i);
99 frontend::Alert::warning(_("Keys must be unique!"),
100 bformat(_("The key %1$s already exists,\n"
101 "it will be changed to %2$s."), new_key, key));
103 setParam("key", key);
104 buffer().invalidateBibinfoCache();
108 ParamInfo const & InsetBibitem::findInfo(string const & /* cmdName */)
110 static ParamInfo param_info_;
111 if (param_info_.empty()) {
112 param_info_.add("label", ParamInfo::LATEX_OPTIONAL,
113 ParamInfo::HANDLING_LATEXIFY);
114 param_info_.add("key", ParamInfo::LATEX_REQUIRED,
115 ParamInfo::ParamHandling(ParamInfo::HANDLING_ESCAPE
116 | ParamInfo::HANDLING_LTRIM));
117 param_info_.add("literal", ParamInfo::LYX_INTERNAL);
123 void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
125 switch (cmd.action()) {
127 case LFUN_INSET_MODIFY: {
128 InsetCommandParams p(BIBITEM_CODE);
129 InsetCommand::string2params(to_utf8(cmd.argument()), p);
130 if (p.getCmdName().empty()) {
131 cur.noScreenUpdate();
137 docstring const & old_key = params()["key"];
138 docstring const & old_label = params()["label"];
139 docstring const & old_literal = params()["literal"];
140 docstring label = p["label"];
141 docstring literal = p["literal"];
143 if (old_label != label) {
145 cur.forceBufferUpdate();
146 buffer().invalidateBibinfoCache();
148 setParam("label", p["label"]);
150 if (old_literal != literal) {
151 p["literal"] = literal;
152 cur.forceBufferUpdate();
153 buffer().invalidateBibinfoCache();
155 setParam("literal", p["literal"]);
157 if (p["key"] != old_key) {
158 updateCommand(p["key"]);
159 cur.bv().buffer().changeRefsIfUnique(old_key, params()["key"]);
160 cur.forceBufferUpdate();
161 buffer().invalidateBibinfoCache();
167 InsetCommand::doDispatch(cur, cmd);
173 void InsetBibitem::read(Lexer & lex)
175 InsetCommand::read(lex);
177 if (prefixIs(getParam("key"), key_prefix)) {
178 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
179 Mutex::Locker lock(&counter_mutex);
180 key_counter = max(key_counter, key);
185 docstring InsetBibitem::bibLabel() const
187 BufferParams const & bp = buffer().masterBuffer()->params();
188 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
190 docstring label = getParam("label");
191 if (!label.empty() && bp.citeEngine() == "natbib")
192 // Add a space before opening paren
193 label = subst(label, from_ascii("("), from_ascii(" ("));
194 return label.empty() ? autolabel_ : label;
198 docstring InsetBibitem::screenLabel() const
200 return getParam("key") + " [" + bibLabel() + ']';
204 int InsetBibitem::plaintext(odocstringstream & os,
205 OutputParams const &, size_t) const
207 odocstringstream oss;
208 oss << '[' << bibLabel() << "] ";
210 docstring const str = oss.str();
218 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
220 BufferParams const & bp = buffer.masterBuffer()->params();
221 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
222 return from_ascii("99");
226 InsetBibitem const * bitem = 0;
228 // FIXME: this font is used unitialized for now but should be set to
229 // a proportional font. Here is what Georg Baum has to say about it:
231 bibitemWidest() is supposed to find the bibitem with the widest label in the
232 output, because that is needed as an argument of the bibliography
233 environment to determine the correct indentation. To be 100% correct we
234 would need the metrics of the font that is used in the output, but usually
235 we don't have access to these.
236 In practice, any proportional font is probably good enough, since we don't
237 need to know the final with, we only need to know the which label is the
239 Unless there is an easy way to get the metrics of the output font I suggest
240 to use a hardcoded font like "Times" or so.
242 It is very important that the result of this function is the same both with
243 and without GUI. After thinking about this it is clear that no Font
244 metrics should be used here, since these come from the gui. If we can't
245 easily get the LaTeX font metrics we should make our own poor mans font
246 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
252 ParagraphList::const_iterator it = buffer.paragraphs().begin();
253 ParagraphList::const_iterator end = buffer.paragraphs().end();
255 bool is_literal = false;
256 for (; it != end; ++it) {
257 if (it->insetList().empty())
259 Inset * inset = it->insetList().begin()->inset;
260 if (inset->lyxCode() != BIBITEM_CODE)
263 bitem = static_cast<InsetBibitem const *>(inset);
264 docstring const label = bitem->bibLabel();
266 // FIXME: we can't be sure using the following that the GUI
267 // version and the command-line version will give the same
270 //int const wx = use_gui?
271 // theFontMetrics(font).width(label): label.size();
273 // So for now we just use the label size in order to be sure
274 // that GUI and no-GUI gives the same bibitem (even if that is
275 // potentially the wrong one.
276 int const wx = label.size();
281 is_literal = (bitem->getParam("literal") == "true");
286 InsetCommandParams p(BIBITEM_CODE);
288 p["literal"] = from_ascii("true");
289 return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
292 return from_ascii("99");
296 void InsetBibitem::collectBibKeys(InsetIterator const & it, FileNameList & /*checkedFiles*/) const
298 docstring const key = getParam("key");
299 docstring const label = getParam("label");
300 BibTeXInfo keyvalmap(false);
302 keyvalmap.label(label);
304 BufferParams const & bp = buffer().masterBuffer()->params();
305 Counters & counters = bp.documentClass().counters();
306 docstring const bibitem = from_ascii("bibitem");
307 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
308 if (counters.hasCounter(bibitem))
309 counters.step(bibitem, InternalUpdate);
310 string const & lang = it.paragraph().getParLanguage(bp)->code();
311 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
314 DocIterator doc_it(it);
316 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
317 AS_STR_INSETS | AS_STR_SKIPDELETE);
318 buffer().addBibTeXInfo(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 (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
329 if (counters.hasCounter(bibitem))
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.
348 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
349 xs << html::CompTag("a", attrs);
350 xs << html::StartTag("span", "class='bibitemlabel'");
352 xs << html::EndTag("span");