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