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