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