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