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