]> git.lyx.org Git - lyx.git/blob - src/insets/InsetBibitem.cpp
Fix text direction issue for InsetInfo in RTL context
[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                         updateCommand(p["key"]);
159                         cur.bv().buffer().changeRefsIfUnique(old_key, params()["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 const & label = getParam("label");
191         return label.empty() ? autolabel_ : label;
192 }
193
194
195 docstring InsetBibitem::screenLabel() const
196 {
197         return getParam("key") + " [" + bibLabel() + ']';
198 }
199
200
201 int InsetBibitem::plaintext(odocstringstream & os,
202         OutputParams const &, size_t) const
203 {
204         odocstringstream oss;
205         oss << '[' << bibLabel() << "] ";
206
207         docstring const str = oss.str();
208         os << str;
209
210         return str.size();
211 }
212
213
214 // ale070405
215 docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
216 {
217         BufferParams const & bp = buffer.masterBuffer()->params();
218         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL)
219                 return from_ascii("99");
220
221         int w = 0;
222
223         InsetBibitem const * bitem = 0;
224
225         // FIXME: this font is used unitialized for now but should  be set to
226         // a proportional font. Here is what Georg Baum has to say about it:
227         /*
228         bibitemWidest() is supposed to find the bibitem with the widest label in the
229         output, because that is needed as an argument of the bibliography
230         environment to determine the correct indentation. To be 100% correct we
231         would need the metrics of the font that is used in the output, but usually
232         we don't have access to these.
233         In practice, any proportional font is probably good enough, since we don't
234         need to know the final with, we only need to know the which label is the
235         widest.
236         Unless there is an easy way to get the metrics of the output font I suggest
237         to use a hardcoded font like "Times" or so.
238
239         It is very important that the result of this function is the same both with
240         and without GUI. After thinking about this it is clear that no Font
241         metrics should be used here, since these come from the gui. If we can't
242         easily get the LaTeX font metrics we should make our own poor mans font
243         metrics replacement, e.g. by hardcoding the metrics of the standard TeX
244         font.
245         */
246
247         docstring lbl;
248
249         ParagraphList::const_iterator it = buffer.paragraphs().begin();
250         ParagraphList::const_iterator end = buffer.paragraphs().end();
251
252         bool is_literal = false;
253         for (; it != end; ++it) {
254                 if (it->insetList().empty())
255                         continue;
256                 Inset * inset = it->insetList().begin()->inset;
257                 if (inset->lyxCode() != BIBITEM_CODE)
258                         continue;
259
260                 bitem = static_cast<InsetBibitem const *>(inset);
261                 docstring const label = bitem->bibLabel();
262
263                 // FIXME: we can't be sure using the following that the GUI
264                 // version and the command-line version will give the same
265                 // result.
266                 //
267                 //int const wx = use_gui?
268                 //      theFontMetrics(font).width(label): label.size();
269                 //
270                 // So for now we just use the label size in order to be sure
271                 // that GUI and no-GUI gives the same bibitem (even if that is
272                 // potentially the wrong one.
273                 int const wx = label.size();
274
275                 if (wx > w) {
276                         w = wx;
277                         lbl = label;
278                         is_literal = (bitem->getParam("literal") == "true");
279                 }
280         }
281
282         if (!lbl.empty()) {
283                 InsetCommandParams p(BIBITEM_CODE);
284                 if (is_literal)
285                         p["literal"] = from_ascii("true");
286                 return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
287         }
288
289         return from_ascii("99");
290 }
291
292
293 void InsetBibitem::collectBibKeys(InsetIterator const & it, FileNameList & /*checkedFiles*/) const
294 {
295         docstring const key = getParam("key");
296         docstring const label = getParam("label");
297         BibTeXInfo keyvalmap(false);
298         keyvalmap.key(key);
299         keyvalmap.label(label);
300
301         BufferParams const & bp = buffer().masterBuffer()->params();
302         Counters & counters = bp.documentClass().counters();
303         docstring const bibitem = from_ascii("bibitem");
304         if (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || getParam("label").empty()) {
305                 if (counters.hasCounter(bibitem))
306                         counters.step(bibitem, InternalUpdate);
307                 string const & lang = it.paragraph().getParLanguage(bp)->code();
308                 keyvalmap.setCiteNumber(counters.theCounter(bibitem, lang));
309         }
310
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 (bp.citeEngineType() == ENGINE_TYPE_NUMERICAL || 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