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