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 // changeRefsIfUnique handles undo
159 cur.bv().buffer().changeRefsIfUnique(old_key, p["key"]);
160 updateCommand(p["key"]);
161 cur.forceBufferUpdate();
162 buffer().invalidateBibinfoCache();
168 InsetCommand::doDispatch(cur, cmd);
174 void InsetBibitem::read(Lexer & lex)
176 InsetCommand::read(lex);
178 if (prefixIs(getParam("key"), key_prefix)) {
179 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
180 Mutex::Locker lock(&counter_mutex);
181 key_counter = max(key_counter, key);
186 docstring InsetBibitem::bibLabel() const
188 BufferParams const & bp = buffer().masterBuffer()->params();
189 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
191 docstring label = getParam("label");
192 if (!label.empty() && bp.citeEngine() == "natbib") {
193 // Add a space before opening paren
194 label = subst(label, from_ascii("("), from_ascii(" ("));
195 // and strip off long author list
196 docstring striplabel;
197 label = rsplit(label, striplabel, ')');
198 if (!striplabel.empty())
199 label = striplabel + ")";
201 return label.empty() ? autolabel_ : label;
205 docstring InsetBibitem::screenLabel() const
207 return getParam("key") + " [" + bibLabel() + ']';
211 int InsetBibitem::plaintext(odocstringstream & os,
212 OutputParams const &, size_t) const
214 odocstringstream oss;
215 oss << '[' << bibLabel() << "] ";
217 docstring const str = oss.str();
220 return int(str.size());
225 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
227 BufferParams const & bp = buffer.masterBuffer()->params();
228 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
229 return from_ascii("99");
233 InsetBibitem const * bitem = nullptr;
235 // FIXME: this font is used uninitialized 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 bool is_literal = false;
263 for (; it != end; ++it) {
264 if (it->insetList().empty())
266 Inset * inset = it->insetList().begin()->inset;
267 if (inset->lyxCode() != BIBITEM_CODE)
270 bitem = static_cast<InsetBibitem const *>(inset);
271 docstring const label = bitem->bibLabel();
273 // FIXME: we can't be sure using the following that the GUI
274 // version and the command-line version will give the same
277 //int const wx = use_gui?
278 // theFontMetrics(font).width(label): label.size();
280 // So for now we just use the label size in order to be sure
281 // that GUI and no-GUI gives the same bibitem (even if that is
282 // potentially the wrong one.
283 int const wx = int(label.size());
288 is_literal = (bitem->getParam("literal") == "true");
293 InsetCommandParams p(BIBITEM_CODE);
295 p["literal"] = from_ascii("true");
296 return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
299 return from_ascii("99");
303 void InsetBibitem::collectBibKeys(InsetIterator const & it, FileNameList & /*checkedFiles*/) const
305 docstring const key = getParam("key");
306 docstring const label = getParam("label");
307 BibTeXInfo keyvalmap(false);
309 keyvalmap.label(label);
311 BufferParams const & bp = buffer().masterBuffer()->params();
312 Counters & counters = bp.documentClass().counters();
313 docstring const bibitem = from_ascii("bibitem");
314 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
315 if (counters.hasCounter(bibitem))
316 counters.step(bibitem, InternalUpdate);
317 string const & lang = it.paragraph().getParLanguage(bp)->code();
318 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
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, bool const /*deleted*/)
332 BufferParams const & bp = buffer().masterBuffer()->params();
333 Counters & counters = bp.documentClass().counters();
334 docstring const bibitem = from_ascii("bibitem");
335 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || 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 void InsetBibitem::docbook(XMLStream &, OutputParams const &) const
348 // Nothing to do: everything is implemented in makeParagraphBibliography.
352 docstring InsetBibitem::xhtml(XMLStream & xs, OutputParams const &) const
355 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
356 // the "id" attribute to get the document to validate. Probably, we will
357 // need to use "name" anyway, eventually, because some browsers do not
358 // handle jumping to ids. If we don't do that, though, we can just put the
359 // id into the span tag.
361 "id='LyXCite-" + to_utf8(xml::cleanAttr(getParam("key"))) + "'";
362 xs << xml::CompTag("a", attrs);
363 xs << xml::StartTag("span", "class='bibitemlabel'");
365 xs << xml::EndTag("span");