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"
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()
71 /* Coverity believes that this may throw an exception, but
72 * actually this code path is not taken when buffer_ == 0 */
73 // coverity[fun_call_w_exception]
74 buffer().invalidateBibinfoCache();
78 void InsetBibitem::initView()
80 updateCommand(getParam("key"));
84 void InsetBibitem::updateCommand(docstring const & new_key, bool)
86 docstring key = new_key;
87 vector<docstring> bibkeys = buffer().masterBibInfo().getKeys();
89 if (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
91 // generate unique label
92 key = new_key + '-' + convert<docstring>(i);
93 while (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
95 key = new_key + '-' + convert<docstring>(i);
97 frontend::Alert::warning(_("Keys must be unique!"),
98 bformat(_("The key %1$s already exists,\n"
99 "it will be changed to %2$s."), new_key, key));
101 setParam("key", key);
102 buffer().invalidateBibinfoCache();
106 ParamInfo const & InsetBibitem::findInfo(string const & /* cmdName */)
108 static ParamInfo param_info_;
109 if (param_info_.empty()) {
110 param_info_.add("label", ParamInfo::LATEX_OPTIONAL,
111 ParamInfo::HANDLING_LATEXIFY);
112 param_info_.add("key", ParamInfo::LATEX_REQUIRED,
113 ParamInfo::HANDLING_ESCAPE);
114 param_info_.add("literal", ParamInfo::LYX_INTERNAL);
120 void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
122 switch (cmd.action()) {
124 case LFUN_INSET_MODIFY: {
125 InsetCommandParams p(BIBITEM_CODE);
126 InsetCommand::string2params(to_utf8(cmd.argument()), p);
127 if (p.getCmdName().empty()) {
128 cur.noScreenUpdate();
134 docstring const & old_key = params()["key"];
135 docstring const & old_label = params()["label"];
136 docstring const & old_literal = params()["literal"];
137 docstring label = p["label"];
138 docstring literal = p["literal"];
140 if (old_label != label) {
142 cur.forceBufferUpdate();
143 buffer().invalidateBibinfoCache();
145 setParam("label", p["label"]);
147 if (old_literal != literal) {
148 p["literal"] = literal;
149 cur.forceBufferUpdate();
150 buffer().invalidateBibinfoCache();
152 setParam("literal", p["literal"]);
154 if (p["key"] != old_key) {
155 updateCommand(p["key"]);
156 cur.bv().buffer().changeRefsIfUnique(old_key, params()["key"]);
157 cur.forceBufferUpdate();
158 buffer().invalidateBibinfoCache();
164 InsetCommand::doDispatch(cur, cmd);
170 void InsetBibitem::read(Lexer & lex)
172 InsetCommand::read(lex);
174 if (prefixIs(getParam("key"), key_prefix)) {
175 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
176 Mutex::Locker lock(&counter_mutex);
177 key_counter = max(key_counter, key);
182 docstring InsetBibitem::bibLabel() const
184 BufferParams const & bp = buffer().masterBuffer()->params();
185 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
187 docstring const & label = getParam("label");
188 return label.empty() ? autolabel_ : label;
192 docstring InsetBibitem::screenLabel() const
194 return getParam("key") + " [" + bibLabel() + ']';
198 int InsetBibitem::plaintext(odocstringstream & os,
199 OutputParams const &, size_t) const
201 odocstringstream oss;
202 oss << '[' << bibLabel() << "] ";
204 docstring const str = oss.str();
212 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
214 BufferParams const & bp = buffer.masterBuffer()->params();
215 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
216 return from_ascii("99");
220 InsetBibitem const * bitem = 0;
222 // FIXME: this font is used unitialized for now but should be set to
223 // a proportional font. Here is what Georg Baum has to say about it:
225 bibitemWidest() is supposed to find the bibitem with the widest label in the
226 output, because that is needed as an argument of the bibliography
227 environment to determine the correct indentation. To be 100% correct we
228 would need the metrics of the font that is used in the output, but usually
229 we don't have access to these.
230 In practice, any proportional font is probably good enough, since we don't
231 need to know the final with, we only need to know the which label is the
233 Unless there is an easy way to get the metrics of the output font I suggest
234 to use a hardcoded font like "Times" or so.
236 It is very important that the result of this function is the same both with
237 and without GUI. After thinking about this it is clear that no Font
238 metrics should be used here, since these come from the gui. If we can't
239 easily get the LaTeX font metrics we should make our own poor mans font
240 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
246 ParagraphList::const_iterator it = buffer.paragraphs().begin();
247 ParagraphList::const_iterator end = buffer.paragraphs().end();
249 for (; it != end; ++it) {
250 if (it->insetList().empty())
252 Inset * inset = it->insetList().begin()->inset;
253 if (inset->lyxCode() != BIBITEM_CODE)
256 bitem = static_cast<InsetBibitem const *>(inset);
257 docstring const label = bitem->bibLabel();
259 // FIXME: we can't be sure using the following that the GUI
260 // version and the command-line version will give the same
263 //int const wx = use_gui?
264 // theFontMetrics(font).width(label): label.size();
266 // So for now we just use the label size in order to be sure
267 // that GUI and no-GUI gives the same bibitem (even if that is
268 // potentially the wrong one.
269 int const wx = label.size();
278 InsetCommandParams p(BIBITEM_CODE);
279 return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
282 return from_ascii("99");
286 void InsetBibitem::collectBibKeys(InsetIterator const & it) const
288 docstring const key = getParam("key");
289 docstring const label = getParam("label");
290 BibTeXInfo keyvalmap(false);
292 keyvalmap.label(label);
294 BufferParams const & bp = buffer().masterBuffer()->params();
295 Counters & counters = bp.documentClass().counters();
296 docstring const bibitem = from_ascii("bibitem");
297 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
298 if (counters.hasCounter(bibitem))
299 counters.step(bibitem, InternalUpdate);
300 string const & lang = it.paragraph().getParLanguage(bp)->code();
301 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
304 DocIterator doc_it(it);
306 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
307 AS_STR_INSETS | AS_STR_SKIPDELETE);
308 buffer().addBibTeXInfo(key, keyvalmap);
312 // Update the counters of this inset and of its contents
313 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
315 BufferParams const & bp = buffer().masterBuffer()->params();
316 Counters & counters = bp.documentClass().counters();
317 docstring const bibitem = from_ascii("bibitem");
318 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
319 if (counters.hasCounter(bibitem))
320 counters.step(bibitem, utype);
321 string const & lang = it.paragraph().getParLanguage(bp)->code();
322 autolabel_ = counters.theCounter(bibitem, lang);
324 autolabel_ = from_ascii("??");
329 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
332 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
333 // the "id" atttribute to get the document to validate. Probably, we will
334 // need to use "name" anyway, eventually, because some browsers do not
335 // handle jumping to ids. If we don't do that, though, we can just put the
336 // id into the span tag.
338 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
339 xs << html::CompTag("a", attrs);
340 xs << html::StartTag("span", "class='bibitemlabel'");
342 xs << html::EndTag("span");