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