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