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