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