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