]> git.lyx.org Git - features.git/blob - src/insets/InsetBibitem.cpp
Try another way of convincing coverity that everything is fine
[features.git] / src / insets / InsetBibitem.cpp
1 /**
2  * \file InsetBibitem.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12 #include <algorithm>
13
14 #include "InsetBibitem.h"
15
16 #include "BiblioInfo.h"
17 #include "Buffer.h"
18 #include "Cursor.h"
19 #include "buffer_funcs.h"
20 #include "BufferParams.h"
21 #include "BufferView.h"
22 #include "Counters.h"
23 #include "DispatchResult.h"
24 #include "Encoding.h"
25 #include "FuncRequest.h"
26 #include "InsetIterator.h"
27 #include "InsetList.h"
28 #include "Language.h"
29 #include "Lexer.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"
36
37 #include "frontends/alert.h"
38
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"
46
47 using namespace std;
48 using namespace lyx::support;
49
50 namespace lyx {
51
52
53 int InsetBibitem::key_counter = 0;
54 static Mutex counter_mutex;
55 docstring const key_prefix = from_ascii("key-");
56
57
58 InsetBibitem::InsetBibitem(Buffer * buf, InsetCommandParams const & p)
59         : InsetCommand(buf, p)
60 {
61         buffer().invalidateBibinfoCache();
62         if (getParam("key").empty()) {
63                 Mutex::Locker lock(&counter_mutex);
64                 setParam("key", key_prefix + convert<docstring>(++key_counter));
65         }
66 }
67
68
69 InsetBibitem::~InsetBibitem()
70 {
71         if (isBufferLoaded()) {
72                 /* Coverity believes that this may throw an exception, but
73                  * actually this code path is not taken when buffer_ == 0 */
74                 LATTEST(buffer_);
75                 buffer().invalidateBibinfoCache();
76         }
77 }
78
79
80 void InsetBibitem::initView()
81 {
82         updateCommand(getParam("key"));
83 }
84
85
86 void InsetBibitem::updateCommand(docstring const & new_key, bool)
87 {
88         docstring key = new_key;
89         vector<docstring> bibkeys = buffer().masterBibInfo().getKeys();
90
91         if (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
92                 int i = 1;
93                 // generate unique label
94                 key = new_key + '-' + convert<docstring>(i);
95                 while (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
96                         ++i;
97                         key = new_key + '-' + convert<docstring>(i);
98                 }
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));
102         }
103         setParam("key", key);
104         buffer().invalidateBibinfoCache();
105 }
106
107
108 ParamInfo const & InsetBibitem::findInfo(string const & /* cmdName */)
109 {
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);
117         }
118         return param_info_;
119 }
120
121
122 void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
123 {
124         switch (cmd.action()) {
125
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();
131                         break;
132                 }
133
134                 cur.recordUndo();
135
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"];
141
142                 if (old_label != label) {
143                         p["label"] = label;
144                         cur.forceBufferUpdate();
145                         buffer().invalidateBibinfoCache();
146                 }
147                 setParam("label", p["label"]);
148
149                 if (old_literal != literal) {
150                         p["literal"] = literal;
151                         cur.forceBufferUpdate();
152                         buffer().invalidateBibinfoCache();
153                 }
154                 setParam("literal", p["literal"]);
155
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();
161                 }
162                 break;
163         }
164
165         default:
166                 InsetCommand::doDispatch(cur, cmd);
167                 break;
168         }
169 }
170
171
172 void InsetBibitem::read(Lexer & lex)
173 {
174         InsetCommand::read(lex);
175
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);
180         }
181 }
182
183
184 docstring InsetBibitem::bibLabel() const
185 {
186         BufferParams const & bp = buffer().masterBuffer()->params();
187         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
188                 return autolabel_;
189         docstring const & label = getParam("label");
190         return label.empty() ? autolabel_ : label;
191 }
192
193
194 docstring InsetBibitem::screenLabel() const
195 {
196         return getParam("key") + " [" + bibLabel() + ']';
197 }
198
199
200 int InsetBibitem::plaintext(odocstringstream & os,
201         OutputParams const &, size_t) const
202 {
203         odocstringstream oss;
204         oss << '[' << bibLabel() << "] ";
205
206         docstring const str = oss.str();
207         os << str;
208
209         return str.size();
210 }
211
212
213 // ale070405
214 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
215 {
216         BufferParams const & bp = buffer.masterBuffer()->params();
217         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
218                 return from_ascii("99");
219
220         int w = 0;
221
222         InsetBibitem const * bitem = 0;
223
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:
226         /*
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
234         widest.
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.
237
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
243         font.
244         */
245
246         docstring lbl;
247
248         ParagraphList::const_iterator it = buffer.paragraphs().begin();
249         ParagraphList::const_iterator end = buffer.paragraphs().end();
250
251         for (; it != end; ++it) {
252                 if (it->insetList().empty())
253                         continue;
254                 Inset * inset = it->insetList().begin()->inset;
255                 if (inset->lyxCode() != BIBITEM_CODE)
256                         continue;
257
258                 bitem = static_cast<InsetBibitem const *>(inset);
259                 docstring const label = bitem->bibLabel();
260
261                 // FIXME: we can't be sure using the following that the GUI
262                 // version and the command-line version will give the same
263                 // result.
264                 //
265                 //int const wx = use_gui?
266                 //      theFontMetrics(font).width(label): label.size();
267                 //
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();
272
273                 if (wx > w) {
274                         w = wx;
275                         lbl = label;
276                 }
277         }
278
279         if (!lbl.empty()) {
280                 InsetCommandParams p(BIBITEM_CODE);
281                 return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
282         }
283
284         return from_ascii("99");
285 }
286
287
288 void InsetBibitem::collectBibKeys(InsetIterator const & it) const
289 {
290         docstring const key = getParam("key");
291         docstring const label = getParam("label");
292         BibTeXInfo keyvalmap(false);
293         keyvalmap.key(key);
294         keyvalmap.label(label);
295
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));
304         }
305
306         DocIterator doc_it(it);
307         doc_it.forwardPos();
308         keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
309                 AS_STR_INSETS | AS_STR_SKIPDELETE);
310         buffer().addBibTeXInfo(key, keyvalmap);
311 }
312
313
314 // Update the counters of this inset and of its contents
315 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
316 {
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);
325         } else {
326                 autolabel_ = from_ascii("??");
327         }
328 }
329
330
331 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
332 {
333         // FIXME XHTML
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.
339         string const attrs =
340                 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
341         xs << html::CompTag("a", attrs);
342         xs << html::StartTag("span", "class='bibitemlabel'");
343         xs << bibLabel();
344         xs << html::EndTag("span");
345         return docstring();
346 }
347
348
349 } // namespace lyx