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