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