]> git.lyx.org Git - lyx.git/blob - src/insets/InsetBibitem.cpp
Move Lexer to support/ directory (and lyx::support namespace)
[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 "output_xhtml.h"
30 #include "Paragraph.h"
31 #include "ParagraphList.h"
32 #include "ParIterator.h"
33 #include "TextClass.h"
34
35 #include "frontends/alert.h"
36
37 #include "support/lassert.h"
38 #include "support/convert.h"
39 #include "support/debug.h"
40 #include "support/docstream.h"
41 #include "support/gettext.h"
42 #include "support/Lexer.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                 /* We do not use buffer() because Coverity believes that this
72                  * may throw an exception. Actually this code path is not
73                  * taken when buffer_ == 0 */
74                 buffer_->invalidateBibinfoCache();
75         }
76 }
77
78
79 void InsetBibitem::initView()
80 {
81         updateCommand(getParam("key"));
82 }
83
84
85 void InsetBibitem::updateCommand(docstring const & new_key, bool)
86 {
87         docstring key = new_key;
88         vector<docstring> bibkeys = buffer().masterBibInfo().getKeys();
89
90         if (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
91                 int i = 1;
92                 // generate unique label
93                 key = new_key + '-' + convert<docstring>(i);
94                 while (find(bibkeys.begin(), bibkeys.end(), key) != bibkeys.end()) {
95                         ++i;
96                         key = new_key + '-' + convert<docstring>(i);
97                 }
98                 frontend::Alert::warning(_("Keys must be unique!"),
99                         bformat(_("The key %1$s already exists,\n"
100                         "it will be changed to %2$s."), new_key, key));
101         }
102         setParam("key", key);
103         buffer().invalidateBibinfoCache();
104 }
105
106
107 ParamInfo const & InsetBibitem::findInfo(string const & /* cmdName */)
108 {
109         static ParamInfo param_info_;
110         if (param_info_.empty()) {
111                 param_info_.add("label", ParamInfo::LATEX_OPTIONAL,
112                                 ParamInfo::HANDLING_LATEXIFY);
113                 param_info_.add("key", ParamInfo::LATEX_REQUIRED,
114                                 ParamInfo::ParamHandling(ParamInfo::HANDLING_ESCAPE
115                                                          | ParamInfo::HANDLING_LTRIM));
116                 param_info_.add("literal", ParamInfo::LYX_INTERNAL);
117         }
118         return param_info_;
119 }
120
121
122 void InsetBibitem::doDispatch(Cursor & cur, FuncRequest & cmd)
123 {
124         switch (cmd.action()) {
125
126         case LFUN_INSET_MODIFY: {
127                 InsetCommandParams p(BIBITEM_CODE);
128                 InsetCommand::string2params(to_utf8(cmd.argument()), p);
129                 if (p.getCmdName().empty()) {
130                         cur.noScreenUpdate();
131                         break;
132                 }
133
134                 cur.recordUndo();
135
136                 docstring const & old_key = params()["key"];
137                 docstring const & old_label = params()["label"];
138                 docstring const & old_literal = params()["literal"];
139                 docstring label = p["label"];
140                 docstring literal = p["literal"];
141
142                 if (old_label != label) {
143                         p["label"] = label;
144                         cur.forceBufferUpdate();
145                         buffer().invalidateBibinfoCache();
146                 }
147                 setParam("label", p["label"]);
148
149                 if (old_literal != literal) {
150                         p["literal"] = literal;
151                         cur.forceBufferUpdate();
152                         buffer().invalidateBibinfoCache();
153                 }
154                 setParam("literal", p["literal"]);
155
156                 if (p["key"] != old_key) {
157                         // changeRefsIfUnique handles undo
158                         cur.bv().buffer().changeRefsIfUnique(old_key, p["key"]);
159                         updateCommand(p["key"]);
160                         cur.forceBufferUpdate();
161                         buffer().invalidateBibinfoCache();
162                 }
163                 break;
164         }
165
166         default:
167                 InsetCommand::doDispatch(cur, cmd);
168                 break;
169         }
170 }
171
172
173 void InsetBibitem::read(Lexer & lex)
174 {
175         InsetCommand::read(lex);
176
177         if (prefixIs(getParam("key"), key_prefix)) {
178                 int const key = convert<int>(getParam("key").substr(key_prefix.length()));
179                 Mutex::Locker lock(&counter_mutex);
180                 key_counter = max(key_counter, key);
181         }
182 }
183
184
185 docstring InsetBibitem::bibLabel() const
186 {
187         BufferParams const & bp = buffer().masterBuffer()->params();
188         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
189                 return autolabel_;
190         docstring label = getParam("label");
191         if (!label.empty() && bp.citeEngine() == "natbib") {
192                 // Add a space before opening paren
193                 label = subst(label, from_ascii("("), from_ascii(" ("));
194                 // and strip off long author list
195                 docstring striplabel;
196                 label = rsplit(label, striplabel, ')');
197                 if (!striplabel.empty())
198                         label = striplabel + ")";
199         }
200         return label.empty() ? autolabel_ : label;
201 }
202
203
204 docstring InsetBibitem::screenLabel() const
205 {
206         return getParam("key") + " [" + bibLabel() + ']';
207 }
208
209
210 int InsetBibitem::plaintext(odocstringstream & os,
211         OutputParams const &, size_t) const
212 {
213         odocstringstream oss;
214         oss << '[' << bibLabel() << "] ";
215
216         docstring const str = oss.str();
217         os << str;
218
219         return int(str.size());
220 }
221
222
223 // ale070405
224 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
225 {
226         BufferParams const & bp = buffer.masterBuffer()->params();
227         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
228                 return from_ascii("99");
229
230         int w = 0;
231
232         InsetBibitem const * bitem = nullptr;
233
234         // FIXME: this font is used uninitialized 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         bool is_literal = false;
262         for (; it != end; ++it) {
263                 if (it->insetList().empty())
264                         continue;
265                 Inset * inset = it->insetList().begin()->inset;
266                 if (inset->lyxCode() != BIBITEM_CODE)
267                         continue;
268
269                 bitem = static_cast<InsetBibitem const *>(inset);
270                 docstring const label = bitem->bibLabel();
271
272                 // FIXME: we can't be sure using the following that the GUI
273                 // version and the command-line version will give the same
274                 // result.
275                 //
276                 //int const wx = use_gui?
277                 //      theFontMetrics(font).width(label): label.size();
278                 //
279                 // So for now we just use the label size in order to be sure
280                 // that GUI and no-GUI gives the same bibitem (even if that is
281                 // potentially the wrong one.
282                 int const wx = int(label.size());
283
284                 if (wx > w) {
285                         w = wx;
286                         lbl = label;
287                         is_literal = (bitem->getParam("literal") == "true");
288                 }
289         }
290
291         if (!lbl.empty()) {
292                 InsetCommandParams p(BIBITEM_CODE);
293                 if (is_literal)
294                         p["literal"] = from_ascii("true");
295                 return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
296         }
297
298         return from_ascii("99");
299 }
300
301
302 void InsetBibitem::collectBibKeys(InsetIterator const & it, FileNameList & /*checkedFiles*/) const
303 {
304         docstring const key = getParam("key");
305         docstring const label = getParam("label");
306         BibTeXInfo keyvalmap(false);
307         keyvalmap.key(key);
308         keyvalmap.label(label);
309
310         BufferParams const & bp = buffer().masterBuffer()->params();
311         Counters & counters = bp.documentClass().counters();
312         docstring const bibitem = from_ascii("bibitem");
313         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
314                 if (counters.hasCounter(bibitem))
315                         counters.step(bibitem, InternalUpdate);
316                 string const & lang = it.paragraph().getParLanguage(bp)->code();
317                 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
318         }
319
320         DocIterator doc_it(it);
321         doc_it.forwardPos();
322         keyvalmap[from_ascii("ref")] = doc_it.paragraph().asString(
323                 AS_STR_INSETS | AS_STR_SKIPDELETE);
324         buffer().addBibTeXInfo(key, keyvalmap);
325 }
326
327
328 // Update the counters of this inset and of its contents
329 void InsetBibitem::updateBuffer(ParIterator const & it, UpdateType utype, bool const /*deleted*/)
330 {
331         BufferParams const & bp = buffer().masterBuffer()->params();
332         Counters & counters = bp.documentClass().counters();
333         docstring const bibitem = from_ascii("bibitem");
334         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
335                 if (counters.hasCounter(bibitem))
336                         counters.step(bibitem, utype);
337                 string const & lang = it.paragraph().getParLanguage(bp)->code();
338                 autolabel_ = counters.theCounter(bibitem, lang);
339         } else {
340                 autolabel_ = from_ascii("??");
341         }
342 }
343
344
345 void InsetBibitem::docbook(XMLStream &, OutputParams const &) const
346 {
347         // Nothing to do: everything is implemented in makeBibliography.
348 }
349
350
351 docstring InsetBibitem::xhtml(XMLStream & xs, OutputParams const &) const
352 {
353         // FIXME XHTML
354         // XHTML 1.1 doesn't have the "name" attribute for <a>, so we have to use
355         // the "id" attribute to get the document to validate. Probably, we will
356         // need to use "name" anyway, eventually, because some browsers do not
357         // handle jumping to ids. If we don't do that, though, we can just put the
358         // id into the span tag.
359         string const attrs =
360                         "id='LyXCite-" + to_utf8(xml::cleanAttr(getParam("key"))) + "'";
361         xs << xml::CompTag("a", attrs);
362         xs << xml::StartTag("span", "class='bibitemlabel'");
363         xs << bibLabel();
364         xs << xml::EndTag("span");
365         return docstring();
366 }
367
368
369 } // namespace lyx