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 "Paragraph.h"
32 #include "ParagraphList.h"
33 #include "ParIterator.h"
34 #include "TextClass.h"
36 #include "frontends/alert.h"
38 #include "support/lassert.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"
44 #include "support/mutex.h"
47 using namespace lyx::support;
52 int InsetBibitem::key_counter = 0;
53 static Mutex counter_mutex;
54 docstring const key_prefix = from_ascii("key-");
57 InsetBibitem::InsetBibitem(Buffer * buf, InsetCommandParams const & p)
58 : InsetCommand(buf, p)
60 buffer().invalidateBibinfoCache();
61 if (getParam("key").empty()) {
62 Mutex::Locker lock(&counter_mutex);
63 setParam("key", key_prefix + convert<docstring>(++key_counter));
68 InsetBibitem::~InsetBibitem()
70 if (isBufferLoaded()) {
71 /* We do not use buffer() because Coverity believes that this
72 * may throw an exception. Actually this code path is not
73 * taken when buffer_ == 0 */
74 buffer_->invalidateBibinfoCache();
79 void InsetBibitem::initView()
81 updateCommand(getParam("key"));
85 void InsetBibitem::updateCommand(docstring const & new_key, bool)
87 docstring key = new_key;
88 vector<docstring> bibkeys = buffer().masterBibInfo().getKeys();
90 if (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
92 // generate unique label
93 key = new_key + '-' + convert<docstring>(i);
94 while (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
96 key = new_key + '-' + convert<docstring>(i);
98 frontend::Alert::warning(_("Keys must be unique!"),
99 bformat(_("The key %1$s already exists,\n"
100 "it will be changed to %2$s."), new_key, key));
102 setParam("key", key);
103 buffer().invalidateBibinfoCache();
107 ParamInfo const & InsetBibitem::findInfo(string const & /* cmdName */)
109 static ParamInfo param_info_;
110 if (param_info_.empty()) {
111 param_info_.add("label", ParamInfo::LATEX_OPTIONAL,
112 ParamInfo::HANDLING_LATEXIFY);
113 param_info_.add("key", ParamInfo::LATEX_REQUIRED,
114 ParamInfo::ParamHandling(ParamInfo::HANDLING_ESCAPE
115 | ParamInfo::HANDLING_LTRIM));
116 param_info_.add("literal", ParamInfo::LYX_INTERNAL);
122 void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
124 switch (cmd.action()) {
126 case LFUN_INSET_MODIFY: {
127 InsetCommandParams p(BIBITEM_CODE);
128 InsetCommand::string2params(to_utf8(cmd.argument()), p);
129 if (p.getCmdName().empty()) {
130 cur.noScreenUpdate();
136 docstring const & old_key = params()["key"];
137 docstring const & old_label = params()["label"];
138 docstring const & old_literal = params()["literal"];
139 docstring label = p["label"];
140 docstring literal = p["literal"];
142 if (old_label != label) {
144 cur.forceBufferUpdate();
145 buffer().invalidateBibinfoCache();
147 setParam("label", p["label"]);
149 if (old_literal != literal) {
150 p["literal"] = literal;
151 cur.forceBufferUpdate();
152 buffer().invalidateBibinfoCache();
154 setParam("literal", p["literal"]);
156 if (p["key"] != old_key) {
157 // changeRefsIfUnique handles undo
158 cur.bv().buffer().changeRefsIfUnique(old_key, p["key"]);
159 updateCommand(p["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();
219 return int(str.size());
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 = nullptr;
234 // FIXME: this font is used uninitialized 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 = int(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, bool const /*deleted*/)
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 void InsetBibitem::docbook(XMLStream &, OutputParams const &) const
347 // Nothing to do: everything is implemented in makeParagraphBibliography.
351 docstring InsetBibitem::xhtml(XMLStream & xs, OutputParams const &) const
354 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
355 // the "id" attribute to get the document to validate. Probably, we will
356 // need to use "name" anyway, eventually, because some browsers do not
357 // handle jumping to ids. If we don't do that, though, we can just put the
358 // id into the span tag.
360 "id='LyXCite-" + to_utf8(xml::cleanAttr(getParam("key"))) + "'";
361 xs << xml::CompTag("a", attrs);
362 xs << xml::StartTag("span", "class='bibitemlabel'");
364 xs << xml::EndTag("span");