]> git.lyx.org Git - lyx.git/blob - src/insets/InsetBibitem.cpp
Try another way to signal a false positive to coverity
[lyx.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/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"
45
46 using namespace std;
47 using namespace lyx::support;
48
49 namespace lyx {
50
51
52 int InsetBibitem::key_counter = 0;
53 static Mutex counter_mutex;
54 docstring const key_prefix = from_ascii("key-");
55
56
57 InsetBibitem::InsetBibitem(Buffer * buf, InsetCommandParams const & p)
58         : InsetCommand(buf, p)
59 {
60         buffer().invalidateBibinfoCache();
61         if (getParam("key").empty()) {
62                 Mutex::Locker lock(&counter_mutex);
63                 setParam("key", key_prefix + convert<docstring>(++key_counter));
64         }
65 }
66
67
68 InsetBibitem::~InsetBibitem()
69 {
70         if (isBufferLoaded())
71                 buffer().invalidateBibinfoCache();
72 }
73
74
75 void InsetBibitem::initView()
76 {
77         updateCommand(getParam("key"));
78 }
79
80
81 void InsetBibitem::updateCommand(docstring const & new_key, bool)
82 {
83         docstring key = new_key;
84         vector<docstring> bibkeys = buffer().masterBibInfo().getKeys();
85
86         if (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
87                 int i = 1;
88                 // generate unique label
89                 key = new_key + '-' + convert<docstring>(i);
90                 while (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
91                         ++i;
92                         key = new_key + '-' + convert<docstring>(i);
93                 }
94                 frontend::Alert::warning(_("Keys must be unique!"),
95                         bformat(_("The key %1$s already exists,\n"
96                         "it will be changed to %2$s."), new_key, key));
97         }
98         setParam("key", key);
99         buffer().invalidateBibinfoCache();
100 }
101
102
103 ParamInfo const & InsetBibitem::findInfo(string const & /* cmdName */)
104 {
105         static ParamInfo param_info_;
106         if (param_info_.empty()) {
107                 param_info_.add("label", ParamInfo::LATEX_OPTIONAL,
108                                 ParamInfo::HANDLING_LATEXIFY);
109                 param_info_.add("key", ParamInfo::LATEX_REQUIRED,
110                                 ParamInfo::HANDLING_ESCAPE);
111                 param_info_.add("literal", ParamInfo::LYX_INTERNAL);
112         }
113         return param_info_;
114 }
115
116
117 void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
118 {
119         switch (cmd.action()) {
120
121         case LFUN_INSET_MODIFY: {
122                 InsetCommandParams p(BIBITEM_CODE);
123                 InsetCommand::string2params(to_utf8(cmd.argument()), p);
124                 if (p.getCmdName().empty()) {
125                         cur.noScreenUpdate();
126                         break;
127                 }
128
129                 cur.recordUndo();
130
131                 docstring const & old_key = params()["key"];
132                 docstring const & old_label = params()["label"];
133                 docstring const & old_literal = params()["literal"];
134                 docstring label = p["label"];
135                 docstring literal = p["literal"];
136
137                 if (old_label != label) {
138                         p["label"] = label;
139                         cur.forceBufferUpdate();
140                         buffer().invalidateBibinfoCache();
141                 }
142                 setParam("label", p["label"]);
143
144                 if (old_literal != literal) {
145                         p["literal"] = literal;
146                         cur.forceBufferUpdate();
147                         buffer().invalidateBibinfoCache();
148                 }
149                 setParam("literal", p["literal"]);
150
151                 if (p["key"] != old_key) {
152                         updateCommand(p["key"]);
153                         cur.bv().buffer().changeRefsIfUnique(old_key, params()["key"]);
154                         cur.forceBufferUpdate();
155                         buffer().invalidateBibinfoCache();
156                 }
157                 break;
158         }
159
160         default:
161                 InsetCommand::doDispatch(cur, cmd);
162                 break;
163         }
164 }
165
166
167 void InsetBibitem::read(Lexer & lex)
168 {
169         InsetCommand::read(lex);
170
171         if (prefixIs(getParam("key"), key_prefix)) {
172                 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
173                 Mutex::Locker lock(&counter_mutex);
174                 key_counter = max(key_counter, key);
175         }
176 }
177
178
179 docstring InsetBibitem::bibLabel() const
180 {
181         BufferParams const & bp = buffer().masterBuffer()->params();
182         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
183                 return autolabel_;
184         docstring const & label = getParam("label");
185         return label.empty() ? autolabel_ : label;
186 }
187
188
189 docstring InsetBibitem::screenLabel() const
190 {
191         return getParam("key") + " [" + bibLabel() + ']';
192 }
193
194
195 int InsetBibitem::plaintext(odocstringstream & os,
196         OutputParams const &, size_t) const
197 {
198         odocstringstream oss;
199         oss << '[' << bibLabel() << "] ";
200
201         docstring const str = oss.str();
202         os << str;
203
204         return str.size();
205 }
206
207
208 // ale070405
209 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
210 {
211         BufferParams const & bp = buffer.masterBuffer()->params();
212         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
213                 return from_ascii("99");
214
215         int w = 0;
216
217         InsetBibitem const * bitem = 0;
218
219         // FIXME: this font is used unitialized for now but should  be set to
220         // a proportional font. Here is what Georg Baum has to say about it:
221         /*
222         bibitemWidest() is supposed to find the bibitem with the widest label in the
223         output, because that is needed as an argument of the bibliography
224         environment to determine the correct indentation. To be 100% correct we
225         would need the metrics of the font that is used in the output, but usually
226         we don't have access to these.
227         In practice, any proportional font is probably good enough, since we don't
228         need to know the final with, we only need to know the which label is the
229         widest.
230         Unless there is an easy way to get the metrics of the output font I suggest
231         to use a hardcoded font like "Times" or so.
232
233         It is very important that the result of this function is the same both with
234         and without GUI. After thinking about this it is clear that no Font
235         metrics should be used here, since these come from the gui. If we can't
236         easily get the LaTeX font metrics we should make our own poor mans font
237         metrics replacement, e.g. by hardcoding the metrics of the standard TeX
238         font.
239         */
240
241         docstring lbl;
242
243         ParagraphList::const_iterator it = buffer.paragraphs().begin();
244         ParagraphList::const_iterator end = buffer.paragraphs().end();
245
246         for (; it != end; ++it) {
247                 if (it->insetList().empty())
248                         continue;
249                 Inset * inset = it->insetList().begin()->inset;
250                 if (inset->lyxCode() != BIBITEM_CODE)
251                         continue;
252
253                 bitem = static_cast<InsetBibitem const *>(inset);
254                 docstring const label = bitem->bibLabel();
255
256                 // FIXME: we can't be sure using the following that the GUI
257                 // version and the command-line version will give the same
258                 // result.
259                 //
260                 //int const wx = use_gui?
261                 //      theFontMetrics(font).width(label): label.size();
262                 //
263                 // So for now we just use the label size in order to be sure
264                 // that GUI and no-GUI gives the same bibitem (even if that is
265                 // potentially the wrong one.
266                 int const wx = label.size();
267
268                 if (wx > w) {
269                         w = wx;
270                         lbl = label;
271                 }
272         }
273
274         if (!lbl.empty()) {
275                 InsetCommandParams p(BIBITEM_CODE);
276                 return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
277         }
278
279         return from_ascii("99");
280 }
281
282
283 void InsetBibitem::collectBibKeys(InsetIterator const & it) const
284 {
285         docstring const key = getParam("key");
286         docstring const label = getParam("label");
287         BibTeXInfo keyvalmap(false);
288         keyvalmap.key(key);
289         keyvalmap.label(label);
290
291         BufferParams const & bp = buffer().masterBuffer()->params();
292         Counters & counters = bp.documentClass().counters();
293         docstring const bibitem = from_ascii("bibitem");
294         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
295                 if (counters.hasCounter(bibitem))
296                         counters.step(bibitem, InternalUpdate);
297                 string const & lang = it.paragraph().getParLanguage(bp)->code();
298                 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
299         }
300
301         DocIterator doc_it(it);
302         doc_it.forwardPos();
303         keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
304                 AS_STR_INSETS | AS_STR_SKIPDELETE);
305         buffer().addBibTeXInfo(key, keyvalmap);
306 }
307
308
309 // Update the counters of this inset and of its contents
310 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
311 {
312         BufferParams const & bp = buffer().masterBuffer()->params();
313         Counters & counters = bp.documentClass().counters();
314         docstring const bibitem = from_ascii("bibitem");
315         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
316                 if (counters.hasCounter(bibitem))
317                         counters.step(bibitem, utype);
318                 string const & lang = it.paragraph().getParLanguage(bp)->code();
319                 autolabel_ = counters.theCounter(bibitem, lang);
320         } else {
321                 autolabel_ = from_ascii("??");
322         }
323 }
324
325
326 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
327 {
328         // FIXME XHTML
329         // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
330         // the "id" atttribute to get the document to validate. Probably, we will
331         // need to use "name" anyway, eventually, because some browsers do not
332         // handle jumping to ids. If we don't do that, though, we can just put the
333         // id into the span tag.
334         string const attrs =
335                 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
336         xs << html::CompTag("a", attrs);
337         xs << html::StartTag("span", "class='bibitemlabel'");
338         xs << bibLabel();
339         xs << html::EndTag("span");
340         return docstring();
341 }
342
343
344 } // namespace lyx