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