]> git.lyx.org Git - lyx.git/blob - src/insets/InsetBibitem.cpp
64e6a31bf5851e043e65f2a7b66ba321188bfe0f
[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                 // and strip off long author list
195                 docstring striplabel;
196                 label = rsplit(label, striplabel, ')');
197                 if (!striplabel.empty())
198                         label = striplabel + ")";
199         }
200         return label.empty() ? autolabel_ : label;
201 }
202
203
204 docstring InsetBibitem::screenLabel() const
205 {
206         return getParam("key") + " [" + bibLabel() + ']';
207 }
208
209
210 int InsetBibitem::plaintext(odocstringstream & os,
211         OutputParams const &, size_t) const
212 {
213         odocstringstream oss;
214         oss << '[' << bibLabel() << "] ";
215
216         docstring const str = oss.str();
217         os << str;
218
219         return str.size();
220 }
221
222
223 // ale070405
224 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
225 {
226         BufferParams const & bp = buffer.masterBuffer()->params();
227         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
228                 return from_ascii("99");
229
230         int w = 0;
231
232         InsetBibitem const * bitem = 0;
233
234         // FIXME: this font is used unitialized for now but should  be set to
235         // a proportional font. Here is what Georg Baum has to say about it:
236         /*
237         bibitemWidest() is supposed to find the bibitem with the widest label in the
238         output, because that is needed as an argument of the bibliography
239         environment to determine the correct indentation. To be 100% correct we
240         would need the metrics of the font that is used in the output, but usually
241         we don't have access to these.
242         In practice, any proportional font is probably good enough, since we don't
243         need to know the final with, we only need to know the which label is the
244         widest.
245         Unless there is an easy way to get the metrics of the output font I suggest
246         to use a hardcoded font like "Times" or so.
247
248         It is very important that the result of this function is the same both with
249         and without GUI. After thinking about this it is clear that no Font
250         metrics should be used here, since these come from the gui. If we can't
251         easily get the LaTeX font metrics we should make our own poor mans font
252         metrics replacement, e.g. by hardcoding the metrics of the standard TeX
253         font.
254         */
255
256         docstring lbl;
257
258         ParagraphList::const_iterator it = buffer.paragraphs().begin();
259         ParagraphList::const_iterator end = buffer.paragraphs().end();
260
261         bool is_literal = false;
262         for (; it != end; ++it) {
263                 if (it->insetList().empty())
264                         continue;
265                 Inset * inset = it->insetList().begin()->inset;
266                 if (inset->lyxCode() != BIBITEM_CODE)
267                         continue;
268
269                 bitem = static_cast<InsetBibitem const *>(inset);
270                 docstring const label = bitem->bibLabel();
271
272                 // FIXME: we can't be sure using the following that the GUI
273                 // version and the command-line version will give the same
274                 // result.
275                 //
276                 //int const wx = use_gui?
277                 //      theFontMetrics(font).width(label): label.size();
278                 //
279                 // So for now we just use the label size in order to be sure
280                 // that GUI and no-GUI gives the same bibitem (even if that is
281                 // potentially the wrong one.
282                 int const wx = label.size();
283
284                 if (wx > w) {
285                         w = wx;
286                         lbl = label;
287                         is_literal = (bitem->getParam("literal") == "true");
288                 }
289         }
290
291         if (!lbl.empty()) {
292                 InsetCommandParams p(BIBITEM_CODE);
293                 if (is_literal)
294                         p["literal"] = from_ascii("true");
295                 return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
296         }
297
298         return from_ascii("99");
299 }
300
301
302 void InsetBibitem::collectBibKeys(InsetIterator const & it, FileNameList & /*checkedFiles*/) const
303 {
304         docstring const key = getParam("key");
305         docstring const label = getParam("label");
306         BibTeXInfo keyvalmap(false);
307         keyvalmap.key(key);
308         keyvalmap.label(label);
309
310         BufferParams const & bp = buffer().masterBuffer()->params();
311         Counters & counters = bp.documentClass().counters();
312         docstring const bibitem = from_ascii("bibitem");
313         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
314                 if (counters.hasCounter(bibitem))
315                         counters.step(bibitem, InternalUpdate);
316                 string const & lang = it.paragraph().getParLanguage(bp)->code();
317                 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
318         }
319
320         DocIterator doc_it(it);
321         doc_it.forwardPos();
322         keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
323                 AS_STR_INSETS | AS_STR_SKIPDELETE);
324         buffer().addBibTeXInfo(key, keyvalmap);
325 }
326
327
328 // Update the counters of this inset and of its contents
329 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
330 {
331         BufferParams const & bp = buffer().masterBuffer()->params();
332         Counters & counters = bp.documentClass().counters();
333         docstring const bibitem = from_ascii("bibitem");
334         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
335                 if (counters.hasCounter(bibitem))
336                         counters.step(bibitem, utype);
337                 string const & lang = it.paragraph().getParLanguage(bp)->code();
338                 autolabel_ = counters.theCounter(bibitem, lang);
339         } else {
340                 autolabel_ = from_ascii("??");
341         }
342 }
343
344
345 docstring InsetBibitem::xhtml(XHTMLStream & xs, OutputParams const &) const
346 {
347         // FIXME XHTML
348         // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
349         // the "id" atttribute to get the document to validate. Probably, we will
350         // need to use "name" anyway, eventually, because some browsers do not
351         // handle jumping to ids. If we don't do that, though, we can just put the
352         // id into the span tag.
353         string const attrs =
354                 "id='LyXCite-" + to_utf8(html::cleanAttr(getParam("key"))) + "'";
355         xs << html::CompTag("a", attrs);
356         xs << html::StartTag("span", "class='bibitemlabel'");
357         xs << bibLabel();
358         xs << html::EndTag("span");
359         return docstring();
360 }
361
362
363 } // namespace lyx