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::HANDLING_ESCAPE);
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 updateCommand(p["key"]);
158 cur.bv().buffer().changeRefsIfUnique(old_key, params()["key"]);
159 cur.forceBufferUpdate();
160 buffer().invalidateBibinfoCache();
166 InsetCommand::doDispatch(cur, cmd);
172 void InsetBibitem::read(Lexer & lex)
174 InsetCommand::read(lex);
176 if (prefixIs(getParam("key"), key_prefix)) {
177 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
178 Mutex::Locker lock(&counter_mutex);
179 key_counter = max(key_counter, key);
184 docstring InsetBibitem::bibLabel() const
186 BufferParams const & bp = buffer().masterBuffer()->params();
187 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
189 docstring const & label = getParam("label");
190 return label.empty() ? autolabel_ : label;
194 docstring InsetBibitem::screenLabel() const
196 return getParam("key") + " [" + bibLabel() + ']';
200 int InsetBibitem::plaintext(odocstringstream & os,
201 OutputParams const &, size_t) const
203 odocstringstream oss;
204 oss << '[' << bibLabel() << "] ";
206 docstring const str = oss.str();
214 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
216 BufferParams const & bp = buffer.masterBuffer()->params();
217 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
218 return from_ascii("99");
222 InsetBibitem const * bitem = 0;
224 // FIXME: this font is used unitialized for now but should be set to
225 // a proportional font. Here is what Georg Baum has to say about it:
227 bibitemWidest() is supposed to find the bibitem with the widest label in the
228 output, because that is needed as an argument of the bibliography
229 environment to determine the correct indentation. To be 100% correct we
230 would need the metrics of the font that is used in the output, but usually
231 we don't have access to these.
232 In practice, any proportional font is probably good enough, since we don't
233 need to know the final with, we only need to know the which label is the
235 Unless there is an easy way to get the metrics of the output font I suggest
236 to use a hardcoded font like "Times" or so.
238 It is very important that the result of this function is the same both with
239 and without GUI. After thinking about this it is clear that no Font
240 metrics should be used here, since these come from the gui. If we can't
241 easily get the LaTeX font metrics we should make our own poor mans font
242 metrics replacement, e.g. by hardcoding the metrics of the standard TeX
248 ParagraphList::const_iterator it = buffer.paragraphs().begin();
249 ParagraphList::const_iterator end = buffer.paragraphs().end();
251 for (; it != end; ++it) {
252 if (it->insetList().empty())
254 Inset * inset = it->insetList().begin()->inset;
255 if (inset->lyxCode() != BIBITEM_CODE)
258 bitem = static_cast<InsetBibitem const *>(inset);
259 docstring const label = bitem->bibLabel();
261 // FIXME: we can't be sure using the following that the GUI
262 // version and the command-line version will give the same
265 //int const wx = use_gui?
266 // theFontMetrics(font).width(label): label.size();
268 // So for now we just use the label size in order to be sure
269 // that GUI and no-GUI gives the same bibitem (even if that is
270 // potentially the wrong one.
271 int const wx = label.size();
280 InsetCommandParams p(BIBITEM_CODE);
281 return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
284 return from_ascii("99");
288 void InsetBibitem::collectBibKeys(InsetIterator const & it) const
290 docstring const key = getParam("key");
291 docstring const label = getParam("label");
292 BibTeXInfo keyvalmap(false);
294 keyvalmap.label(label);
296 BufferParams const & bp = buffer().masterBuffer()->params();
297 Counters & counters = bp.documentClass().counters();
298 docstring const bibitem = from_ascii("bibitem");
299 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
300 if (counters.hasCounter(bibitem))
301 counters.step(bibitem, InternalUpdate);
302 string const & lang = it.paragraph().getParLanguage(bp)->code();
303 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
306 DocIterator doc_it(it);
308 keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
309 AS_STR_INSETS | AS_STR_SKIPDELETE);
310 buffer().addBibTeXInfo(key, keyvalmap);
314 // Update the counters of this inset and of its contents
315 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
317 BufferParams const & bp = buffer().masterBuffer()->params();
318 Counters & counters = bp.documentClass().counters();
319 docstring const bibitem = from_ascii("bibitem");
320 if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
321 if (counters.hasCounter(bibitem))
322 counters.step(bibitem, utype);
323 string const & lang = it.paragraph().getParLanguage(bp)->code();
324 autolabel_ = counters.theCounter(bibitem, lang);
326 autolabel_ = from_ascii("??");
331 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
334 // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
335 // the "id" atttribute to get the document to validate. Probably, we will
336 // need to use "name" anyway, eventually, because some browsers do not
337 // handle jumping to ids. If we don't do that, though, we can just put the
338 // id into the span tag.
340 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
341 xs << html::CompTag("a", attrs);
342 xs << html::StartTag("span", "class='bibitemlabel'");
344 xs << html::EndTag("span");