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 // and strip off long author list
195 docstring striplabel;
196 label = rsplit(label, striplabel, ')');
197 if (!striplabel.empty())
198 label = striplabel + ")";
200 return label.empty() ? autolabel_ : label;
204 docstring InsetBibitem::screenLabel() const
206 return getParam("key") + " [" + bibLabel() + ']';
210 int InsetBibitem::plaintext(odocstringstream & os,
211 OutputParams const &, size_t) const
213 odocstringstream oss;
214 oss << '[' << bibLabel() << "] ";
216 docstring const str = oss.str();
224 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
226 BufferParams const & bp = buffer.masterBuffer()->params();
227 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
228 return from_ascii("99");
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 bool is_literal = false;
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();
287 is_literal = (bitem->getParam("literal") == "true");
292 InsetCommandParams p(BIBITEM_CODE);
294 p["literal"] = from_ascii("true");
295 return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
298 return from_ascii("99");
302 void InsetBibitem::collectBibKeys(InsetIterator const & it, FileNameList & /*checkedFiles*/) const
304 docstring const key = getParam("key");
305 docstring const label = getParam("label");
306 BibTeXInfo keyvalmap(false);
308 keyvalmap.label(label);
310 BufferParams const & bp = buffer().masterBuffer()->params();
311 Counters & counters = bp.documentClass().counters();
312 docstring const bibitem = from_ascii("bibitem");
313 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
314 if (counters.hasCounter(bibitem))
315 counters.step(bibitem, InternalUpdate);
316 string const & lang = it.paragraph().getParLanguage(bp)->code();
317 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
320 DocIterator doc_it(it);
322 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
323 AS_STR_INSETS | AS_STR_SKIPDELETE);
324 buffer().addBibTeXInfo(key, keyvalmap);
328 // Update the counters of this inset and of its contents
329 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
331 BufferParams const & bp = buffer().masterBuffer()->params();
332 Counters & counters = bp.documentClass().counters();
333 docstring const bibitem = from_ascii("bibitem");
334 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
335 if (counters.hasCounter(bibitem))
336 counters.step(bibitem, utype);
337 string const & lang = it.paragraph().getParLanguage(bp)->code();
338 autolabel_ = counters.theCounter(bibitem, lang);
340 autolabel_ = from_ascii("??");
345 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
348 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
349 // the "id" atttribute to get the document to validate. Probably, we will
350 // need to use "name" anyway, eventually, because some browsers do not
351 // handle jumping to ids. If we don't do that, though, we can just put the
352 // id into the span tag.
354 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
355 xs << html::CompTag("a", attrs);
356 xs << html::StartTag("span", "class='bibitemlabel'");
358 xs << html::EndTag("span");