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