]> git.lyx.org Git - lyx.git/blob - src/insets/InsetBibitem.cpp
2060b1a1c9f176c9ff537f6ba6d53fb42feaf441
[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/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                 /* 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();
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::ParamHandling(ParamInfo::HANDLING_ESCAPE
116                                                          | ParamInfo::HANDLING_LTRIM));
117                 param_info_.add("literal", ParamInfo::LYX_INTERNAL);
118         }
119         return param_info_;
120 }
121
122
123 void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
124 {
125         switch (cmd.action()) {
126
127         case LFUN_INSET_MODIFY: {
128                 InsetCommandParams p(BIBITEM_CODE);
129                 InsetCommand::string2params(to_utf8(cmd.argument()), p);
130                 if (p.getCmdName().empty()) {
131                         cur.noScreenUpdate();
132                         break;
133                 }
134
135                 cur.recordUndo();
136
137                 docstring const & old_key = params()["key"];
138                 docstring const & old_label = params()["label"];
139                 docstring const & old_literal = params()["literal"];
140                 docstring label = p["label"];
141                 docstring literal = p["literal"];
142
143                 if (old_label != label) {
144                         p["label"] = label;
145                         cur.forceBufferUpdate();
146                         buffer().invalidateBibinfoCache();
147                 }
148                 setParam("label", p["label"]);
149
150                 if (old_literal != literal) {
151                         p["literal"] = literal;
152                         cur.forceBufferUpdate();
153                         buffer().invalidateBibinfoCache();
154                 }
155                 setParam("literal", p["literal"]);
156
157                 if (p["key"] != old_key) {
158                         updateCommand(p["key"]);
159                         cur.bv().buffer().changeRefsIfUnique(old_key, params()["key"]);
160                         cur.forceBufferUpdate();
161                         buffer().invalidateBibinfoCache();
162                 }
163                 break;
164         }
165
166         default:
167                 InsetCommand::doDispatch(cur, cmd);
168                 break;
169         }
170 }
171
172
173 void InsetBibitem::read(Lexer & lex)
174 {
175         InsetCommand::read(lex);
176
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);
181         }
182 }
183
184
185 docstring InsetBibitem::bibLabel() const
186 {
187         BufferParams const & bp = buffer().masterBuffer()->params();
188         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
189                 return autolabel_;
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         return label.empty() ? autolabel_ : label;
195 }
196
197
198 docstring InsetBibitem::screenLabel() const
199 {
200         return getParam("key") + " [" + bibLabel() + ']';
201 }
202
203
204 int InsetBibitem::plaintext(odocstringstream & os,
205         OutputParams const &, size_t) const
206 {
207         odocstringstream oss;
208         oss << '[' << bibLabel() << "] ";
209
210         docstring const str = oss.str();
211         os << str;
212
213         return str.size();
214 }
215
216
217 // ale070405
218 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
219 {
220         BufferParams const & bp = buffer.masterBuffer()->params();
221         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
222                 return from_ascii("99");
223
224         int w = 0;
225
226         InsetBibitem const * bitem = 0;
227
228         // FIXME: this font is used unitialized for now but should  be set to
229         // a proportional font. Here is what Georg Baum has to say about it:
230         /*
231         bibitemWidest() is supposed to find the bibitem with the widest label in the
232         output, because that is needed as an argument of the bibliography
233         environment to determine the correct indentation. To be 100% correct we
234         would need the metrics of the font that is used in the output, but usually
235         we don't have access to these.
236         In practice, any proportional font is probably good enough, since we don't
237         need to know the final with, we only need to know the which label is the
238         widest.
239         Unless there is an easy way to get the metrics of the output font I suggest
240         to use a hardcoded font like "Times" or so.
241
242         It is very important that the result of this function is the same both with
243         and without GUI. After thinking about this it is clear that no Font
244         metrics should be used here, since these come from the gui. If we can't
245         easily get the LaTeX font metrics we should make our own poor mans font
246         metrics replacement, e.g. by hardcoding the metrics of the standard TeX
247         font.
248         */
249
250         docstring lbl;
251
252         ParagraphList::const_iterator it = buffer.paragraphs().begin();
253         ParagraphList::const_iterator end = buffer.paragraphs().end();
254
255         bool is_literal = false;
256         for (; it != end; ++it) {
257                 if (it->insetList().empty())
258                         continue;
259                 Inset * inset = it->insetList().begin()->inset;
260                 if (inset->lyxCode() != BIBITEM_CODE)
261                         continue;
262
263                 bitem = static_cast<InsetBibitem const *>(inset);
264                 docstring const label = bitem->bibLabel();
265
266                 // FIXME: we can't be sure using the following that the GUI
267                 // version and the command-line version will give the same
268                 // result.
269                 //
270                 //int const wx = use_gui?
271                 //      theFontMetrics(font).width(label): label.size();
272                 //
273                 // So for now we just use the label size in order to be sure
274                 // that GUI and no-GUI gives the same bibitem (even if that is
275                 // potentially the wrong one.
276                 int const wx = label.size();
277
278                 if (wx > w) {
279                         w = wx;
280                         lbl = label;
281                         is_literal = (bitem->getParam("literal") == "true");
282                 }
283         }
284
285         if (!lbl.empty()) {
286                 InsetCommandParams p(BIBITEM_CODE);
287                 if (is_literal)
288                         p["literal"] = from_ascii("true");
289                 return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
290         }
291
292         return from_ascii("99");
293 }
294
295
296 void InsetBibitem::collectBibKeys(InsetIterator const & it, FileNameList & /*checkedFiles*/) const
297 {
298         docstring const key = getParam("key");
299         docstring const label = getParam("label");
300         BibTeXInfo keyvalmap(false);
301         keyvalmap.key(key);
302         keyvalmap.label(label);
303
304         BufferParams const & bp = buffer().masterBuffer()->params();
305         Counters & counters = bp.documentClass().counters();
306         docstring const bibitem = from_ascii("bibitem");
307         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
308                 if (counters.hasCounter(bibitem))
309                         counters.step(bibitem, InternalUpdate);
310                 string const & lang = it.paragraph().getParLanguage(bp)->code();
311                 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
312         }
313
314         DocIterator doc_it(it);
315         doc_it.forwardPos();
316         keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
317                 AS_STR_INSETS | AS_STR_SKIPDELETE);
318         buffer().addBibTeXInfo(key, keyvalmap);
319 }
320
321
322 // Update the counters of this inset and of its contents
323 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
324 {
325         BufferParams const & bp = buffer().masterBuffer()->params();
326         Counters & counters = bp.documentClass().counters();
327         docstring const bibitem = from_ascii("bibitem");
328         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
329                 if (counters.hasCounter(bibitem))
330                         counters.step(bibitem, utype);
331                 string const & lang = it.paragraph().getParLanguage(bp)->code();
332                 autolabel_ = counters.theCounter(bibitem, lang);
333         } else {
334                 autolabel_ = from_ascii("??");
335         }
336 }
337
338
339 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
340 {
341         // FIXME XHTML
342         // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
343         // the "id" atttribute to get the document to validate. Probably, we will
344         // need to use "name" anyway, eventually, because some browsers do not
345         // handle jumping to ids. If we don't do that, though, we can just put the
346         // id into the span tag.
347         string const attrs =
348                 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
349         xs << html::CompTag("a", attrs);
350         xs << html::StartTag("span", "class='bibitemlabel'");
351         xs << bibLabel();
352         xs << html::EndTag("span");
353         return docstring();
354 }
355
356
357 } // namespace lyx