]> git.lyx.org Git - lyx.git/blob - src/insets/InsetBibitem.cpp
eb8e1c7c7751c881ebd8f9dfb213a76f87b68d4c
[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                 docstring const & old_key = params()["key"];
126                 docstring const & old_label = params()["label"];
127                 docstring label = p["label"];
128
129                 // definitions for escaping
130                 int previous;
131                 static docstring const backslash = from_ascii("\\");
132                 static docstring const lbrace = from_ascii("{");
133                 static docstring const rbrace = from_ascii("}");
134                 static char_type const chars_escape[6] = {
135                         '&', '_', '$', '%', '#', '^'};
136                 static char_type const brackets_escape[2] = {'[', ']'};
137
138                 if (!label.empty()) {
139                         // The characters in chars_name[] need to be changed to a command when
140                         // they are in the name field.
141                         for (int k = 0; k < 6; k++)
142                                 for (size_t i = 0, pos;
143                                         (pos = label.find(chars_escape[k], i)) != string::npos;
144                                         i = pos + 2) {
145                                                 if (pos == 0)
146                                                         previous = 0;
147                                                 else
148                                                         previous = pos - 1;
149                                                 // only if not already escaped
150                                                 if (label[previous] != '\\')
151                                                         label.replace(pos, 1, backslash + chars_escape[k] + lbrace + rbrace);
152                                 }
153                         // The characters '[' and ']' need to be put into braces
154                         for (int k = 0; k < 2; k++)
155                                 for (size_t i = 0, pos;
156                                         (pos = label.find(brackets_escape[k], i)) != string::npos;
157                                         i = pos + 2) {
158                                                 if (pos == 0)
159                                                         previous = 0;
160                                                 else
161                                                         previous = pos - 1;
162                                                 // only if not already escaped
163                                                 if (label[previous] != '{')
164                                                         label.replace(pos, 1, lbrace + brackets_escape[k] + rbrace);
165                                 }
166
167                         if (old_label != label) {
168                                 p["label"] = label;
169                                 cur.forceBufferUpdate();
170                                 buffer().invalidateBibinfoCache();
171                         }
172                 }
173
174                 setParam("label", p["label"]);
175                 if (p["key"] != old_key) {
176                         updateCommand(p["key"]);
177                         cur.bv().buffer().changeRefsIfUnique(old_key,
178                                 params()["key"], CITE_CODE);
179                         cur.forceBufferUpdate();
180                         buffer().invalidateBibinfoCache();
181                 }
182                 break;
183         }
184
185         default:
186                 InsetCommand::doDispatch(cur, cmd);
187                 break;
188         }
189 }
190
191
192 void InsetBibitem::read(Lexer & lex)
193 {
194         InsetCommand::read(lex);
195
196         if (prefixIs(getParam("key"), key_prefix)) {
197                 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
198                 key_counter = max(key_counter, key);
199         }
200 }
201
202
203 docstring InsetBibitem::bibLabel() const
204 {
205         docstring const & label = getParam("label");
206         return label.empty() ? autolabel_ : label;
207 }
208
209
210 docstring InsetBibitem::screenLabel() const
211 {
212         return getParam("key") + " [" + bibLabel() + ']';
213 }
214
215
216 int InsetBibitem::plaintext(odocstream & os, OutputParams const &) const
217 {
218         odocstringstream oss;
219         oss << '[' << bibLabel() << "] ";
220
221         docstring const str = oss.str();
222         os << str;
223
224         return str.size();
225 }
226
227
228 // ale070405
229 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
230 {
231         int w = 0;
232
233         InsetBibitem const * bitem = 0;
234
235         // FIXME: this font is used unitialized for now but should  be set to
236         // a proportional font. Here is what Georg Baum has to say about it:
237         /*
238         bibitemWidest() is supposed to find the bibitem with the widest label in the
239         output, because that is needed as an argument of the bibliography
240         environment to determine the correct indentation. To be 100% correct we
241         would need the metrics of the font that is used in the output, but usually
242         we don't have access to these.
243         In practice, any proportional font is probably good enough, since we don't
244         need to know the final with, we only need to know the which label is the
245         widest.
246         Unless there is an easy way to get the metrics of the output font I suggest
247         to use a hardcoded font like "Times" or so.
248
249         It is very important that the result of this function is the same both with
250         and without GUI. After thinking about this it is clear that no Font
251         metrics should be used here, since these come from the gui. If we can't
252         easily get the LaTeX font metrics we should make our own poor mans font
253         metrics replacement, e.g. by hardcoding the metrics of the standard TeX
254         font.
255         */
256
257         docstring lbl;
258
259         ParagraphList::const_iterator it = buffer.paragraphs().begin();
260         ParagraphList::const_iterator end = buffer.paragraphs().end();
261
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                 }
288         }
289
290         if (!lbl.empty()) {
291                 docstring latex_lbl;
292                 for (size_t n = 0; n < lbl.size(); ++n) {
293                         try {
294                                 latex_lbl += runparams.encoding->latexChar(lbl[n]);
295                         } catch (EncodingException & /* e */) {
296                                 if (runparams.dryrun) {
297                                         latex_lbl += "<" + _("LyX Warning: ")
298                                                   + _("uncodable character") + " '";
299                                         latex_lbl += docstring(1, lbl[n]);
300                                         latex_lbl += "'>";
301                                 }
302                         }
303                 }
304                 return latex_lbl;
305         }
306
307         return from_ascii("99");
308 }
309
310
311 void InsetBibitem::fillWithBibKeys(BiblioInfo & keys, InsetIterator const & it) const
312 {
313         docstring const key = getParam("key");
314         BibTeXInfo keyvalmap(false);
315         keyvalmap.label(bibLabel());
316         DocIterator doc_it(it); 
317         doc_it.forwardPos();
318         keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString();
319         keys[key] = keyvalmap;
320 }
321
322
323 // Update the counters of this inset and of its contents
324 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype)
325 {
326         BufferParams const & bp = buffer().masterBuffer()->params();
327         Counters & counters = bp.documentClass().counters();
328         docstring const bibitem = from_ascii("bibitem");
329         if (counters.hasCounter(bibitem) && getParam("label").empty()) {
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 = "id='" + to_utf8(getParam("label")) + "'";
348         xs << html::CompTag("a", attrs);
349         xs << html::StartTag("span", "class='bibitemlabel'");
350         xs << bibLabel();
351         xs << html::EndTag("span");
352         return docstring();
353 }
354
355
356 } // namespace lyx