]> git.lyx.org Git - lyx.git/blob - src/insets/insetbib.C
3c7f03a762ede717ab4c3de7944408746cee97c4
[lyx.git] / src / insets / insetbib.C
1 #include <config.h>
2
3 #include <fstream>
4 #include <cstdlib>
5
6 #ifdef __GNUG__
7 #pragma implementation
8 #endif
9
10 #include FORMS_H_LOCATION  
11 #include "insetbib.h"
12 #include "buffer.h"
13 #include "debug.h"
14 #include "lyx_gui_misc.h"
15 #include "BufferView.h"
16 #include "gettext.h"
17 #include "bibforms.h"
18 #include "lyxtext.h"
19 #include "support/filetools.h"
20 #include "support/path.h"
21 #include "lyxrc.h"
22 #include "font.h"
23
24 using std::ostream;
25 using std::ifstream;
26 using std::getline;
27 using std::endl;
28 using std::vector;
29 using std::pair;
30
31 FD_bibitem_form * bibitem_form = 0;
32
33 FD_bibitem_form * create_form_bibitem_form(void);
34
35 extern BufferView * current_view;
36
37 // This is foul!
38 // called from both InsetBibKey and InsetBibtex dialogs yet cast off
39 // only to InsetBibKey holder. Real problems can ensue.
40 extern "C"
41 void bibitem_cb(FL_OBJECT *, long data)
42 {
43         InsetBibKey::Holder * holder =
44                 static_cast<InsetBibKey::Holder*>
45                 (bibitem_form->bibitem_form->u_vdata);
46
47         holder->inset->callback( bibitem_form, data );
48 }
49
50
51 FD_bibitem_form * create_form_bibitem_form(void)
52 {
53         FL_OBJECT * obj;
54         FD_bibitem_form * fdui = (FD_bibitem_form *) fl_calloc(1, sizeof(FD_bibitem_form));
55
56         fdui->bibitem_form = fl_bgn_form(FL_NO_BOX, 220, 130);
57         obj = fl_add_box(FL_UP_BOX, 0, 0, 220, 130, "");
58         fdui->key = obj = fl_add_input(FL_NORMAL_INPUT, 80, 10, 130, 30, idex(_("Key:|#K")));
59         fl_set_input_shortcut(obj, scex(_("Key:|#K")), 1);
60         fl_set_object_lsize(obj, FL_NORMAL_SIZE);
61         obj = fl_add_button(FL_RETURN_BUTTON, 20, 90, 90, 30, _("OK"));
62         fl_set_object_lsize(obj, FL_NORMAL_SIZE);
63         fl_set_object_callback(obj, bibitem_cb, 1);
64         obj = fl_add_button(FL_NORMAL_BUTTON, 120, 90, 90, 30, idex(_("Cancel|^[")));
65         fl_set_button_shortcut(obj, scex(_("Cancel|^[")), 1);
66         fl_set_object_lsize(obj, FL_NORMAL_SIZE);
67         fl_set_object_callback(obj, bibitem_cb, 0);
68         fdui->label = obj = fl_add_input(FL_NORMAL_INPUT, 80, 50, 130, 30, idex(_("Label:|#L")));
69         fl_set_input_shortcut(obj, scex(_("Label:|#L")), 1);
70         fl_set_object_lsize(obj, FL_NORMAL_SIZE);
71         fl_end_form();
72
73         //fdui->bibitem_form->fdui = fdui;
74
75         return fdui;
76 }
77
78
79 InsetBibKey::InsetBibKey(InsetCommandParams const & p)
80         : InsetCommand(p)
81 {
82         counter = 1;
83 }
84
85
86 InsetBibKey::~InsetBibKey()
87 {
88         if (bibitem_form && bibitem_form->bibitem_form
89            && bibitem_form->bibitem_form->visible
90            && bibitem_form->bibitem_form->u_vdata == &holder)
91                 fl_hide_form(bibitem_form->bibitem_form);
92 }
93
94
95 Inset * InsetBibKey::Clone(Buffer const &) const
96 {
97         InsetBibKey * b = new InsetBibKey(params());
98         b->setCounter(counter);
99         return b;
100 }
101
102
103 void InsetBibKey::callback( FD_bibitem_form * form, long data )
104 {
105         switch (data) {
106         case 1:
107                 // Do NOT change this to
108                 // holder.view->buffer() as this code is used by both
109                 // InsetBibKey and InsetBibtex! Ughhhhhhh!!!!
110                 if (!current_view->buffer()->isReadonly()) {
111                         setContents(fl_get_input(form->key));
112                         setOptions(fl_get_input(form->label));
113                         // shouldn't mark the buffer dirty unless
114                         // something was actually altered
115                         current_view->updateInset( this, true );
116                 } // fall through to Cancel
117         case 0:
118                 fl_hide_form(form->bibitem_form);
119                 break;
120         }
121 }
122
123
124 void InsetBibKey::setCounter(int c) 
125
126         counter = c; 
127     
128         if (getCmdName().empty())
129                 setCmdName( tostr(counter) );
130 }
131
132
133 // I'm sorry but this is still necessary because \bibitem is used also
134 // as a LyX 2.x command, and lyxlex is not enough smart to understand
135 // real LaTeX commands. Yes, that could be fixed, but would be a waste 
136 // of time cause LyX3 won't use lyxlex anyway.  (ale)
137 void InsetBibKey::Write(Buffer const *, ostream & os) const
138 {
139         os << "\\bibitem ";
140         if (! getOptions().empty()) {
141                 os << '['
142                    << getOptions() << ']';
143         }
144         os << '{'
145            << getContents() << "}\n";
146 }
147
148
149 // This is necessary here because this is written without begin_inset
150 // This should be changed!!! (Jug)
151 void InsetBibKey::Read(Buffer const *, LyXLex & lex)
152 {    
153         string token;
154
155         if (lex.EatLine()) {
156                 token = lex.GetString();
157                 scanCommand(token);
158         } else
159                 lex.printError("InsetCommand: Parse error: `$$Token'");
160 }
161
162
163 string const InsetBibKey::getScreenLabel() const
164 {
165         if (! getOptions().empty())
166                 return getOptions();
167     
168         return tostr(counter);
169 }
170
171
172 /**
173   The value in "Key:" isn't allways set right after a few bibkey insets have
174   been added/removed.  Perhaps the wrong object is deleted/used somewhere
175   upwards?
176   (Joacim 1998-03-04)
177 */
178 void InsetBibKey::Edit(BufferView * bv, int, int, unsigned int)
179 {
180         if (bv->buffer()->isReadonly())
181                 WarnReadonly(bv->buffer()->fileName());
182         
183         if (!bibitem_form) {
184                 bibitem_form = create_form_bibitem_form();
185                 fl_set_form_atclose(bibitem_form->bibitem_form, 
186                                     CancelCloseBoxCB, 0);
187         }
188
189         holder.inset = this;
190         holder.view = bv;
191         
192         bibitem_form->bibitem_form->u_vdata = &holder;
193         // InsetBibtex uses the same form, with different labels
194         fl_set_object_label(bibitem_form->key, idex(_("Key:|#K")));
195         fl_set_button_shortcut(bibitem_form->key, scex(_("Key:|#K")), 1);
196         fl_set_object_label(bibitem_form->label, idex(_("Label:|#L")));
197         fl_set_button_shortcut(bibitem_form->label, scex(_("Label:|#L")), 1);
198         fl_set_input(bibitem_form->key, getContents().c_str());
199         fl_set_input(bibitem_form->label, getOptions().c_str());
200         if (bibitem_form->bibitem_form->visible) {
201                 fl_raise_form(bibitem_form->bibitem_form);
202         } else {
203                 fl_show_form(bibitem_form->bibitem_form,
204                              FL_PLACE_MOUSE | FL_FREE_SIZE, FL_TRANSIENT,
205                              _("Bibliography item"));
206         }   
207 }
208
209
210 InsetBibtex::InsetBibtex(InsetCommandParams const & p)
211         : InsetCommand(p)
212 {}
213
214
215 InsetBibtex::~InsetBibtex()
216 {
217         if (bibitem_form && bibitem_form->bibitem_form
218            && bibitem_form->bibitem_form->visible
219            && bibitem_form->bibitem_form->u_vdata == &holder)
220                 fl_hide_form(bibitem_form->bibitem_form);
221 }
222
223
224 string const InsetBibtex::getScreenLabel() const
225 {
226         return _("BibTeX Generated References");
227 }
228
229
230 int InsetBibtex::Latex(Buffer const * buffer, ostream & os,
231                        bool /*fragile*/, bool/*fs*/) const
232 {
233         // If we generate in a temp dir, we might need to give an
234         // absolute path there. This is a bit complicated since we can
235         // have a comma-separated list of bibliographies
236         string adb, db_out;
237         string db_in = getContents();
238         db_in = split(db_in, adb, ',');
239         while(!adb.empty()) {
240                 if (!buffer->niceFile &&
241                     IsFileReadable(MakeAbsPath(adb, buffer->filepath)+".bib")) 
242                          adb = MakeAbsPath(adb, buffer->filepath);
243
244                 db_out += adb;
245                 db_out += ',';
246                 db_in= split(db_in, adb,',');
247         }
248         db_out = strip(db_out, ',');
249         // Idem, but simpler
250         string style;
251         if (!buffer->niceFile 
252             && IsFileReadable(MakeAbsPath(getOptions(), buffer->filepath)
253                               + ".bst")) 
254                 style = MakeAbsPath(getOptions(), buffer->filepath);
255         else
256                 style = getOptions();
257
258         os << "\\bibliographystyle{" << style << "}\n"
259            << "\\bibliography{" << db_out << "}\n";
260         return 2;
261 }
262
263
264 // This method returns a comma separated list of Bibtex entries
265 vector<pair<string, string> > const InsetBibtex::getKeys(Buffer const * buffer) const
266 {
267         Path p(buffer->filepath);
268
269         vector<pair<string,string> > keys;
270         string tmp;
271         string bibfiles = getContents();
272         bibfiles = split(bibfiles, tmp, ',');
273         while(!tmp.empty()) {
274                 string fil = findtexfile(ChangeExtension(tmp, "bib"),
275                                          "bib");
276                 lyxerr[Debug::LATEX] << "Bibfile: " << fil << endl;
277                 // If we didn't find a matching file name just fail silently
278                 if (!fil.empty()) {
279                         // This is a _very_ simple parser for Bibtex database
280                         // files. All it does is to look for lines starting
281                         // in @ and not being @preamble and @string entries.
282                         // It does NOT do any syntax checking!
283                         ifstream ifs(fil.c_str());
284                         string linebuf0;
285                         while (getline(ifs, linebuf0)) {
286                                 string linebuf = frontStrip(strip(linebuf0));
287                                 if (linebuf.empty() ) continue;
288                                 if (prefixIs(linebuf, "@")) {
289                                         linebuf = subst(linebuf, '{', '(');
290                                         linebuf = split(linebuf, tmp, '(');
291                                         tmp = lowercase(tmp);
292                                         if (!prefixIs(tmp, "@string")
293                                             && !prefixIs(tmp, "@preamble")) {
294                                                 linebuf = split(linebuf, tmp, ',');
295                                                 tmp = frontStrip(tmp);
296                                                 if (!tmp.empty()) {
297                                                         keys.push_back(pair<string,string>(tmp,string()));
298                                                 }
299                                         }
300                                 } else if (!keys.empty()) {
301                                         keys.back().second += linebuf + "\n";
302                                 }
303                         }
304                 }
305                 // Get next file name
306                 bibfiles = split(bibfiles, tmp, ',');
307         }
308         return keys;
309 }
310
311
312 // BibTeX should have its own dialog. This is provisional.
313 void InsetBibtex::Edit(BufferView * bv, int, int, unsigned int)
314 {
315         if (!bibitem_form) {
316                 bibitem_form = create_form_bibitem_form();
317                 fl_set_form_atclose(bibitem_form->bibitem_form, 
318                                     CancelCloseBoxCB, 0);
319         }
320
321         holder.inset = this;
322         holder.view = bv;
323         bibitem_form->bibitem_form->u_vdata = &holder;
324
325         fl_set_object_label(bibitem_form->key, _("Database:"));
326         fl_set_object_label(bibitem_form->label, _("Style:  "));
327         fl_set_input(bibitem_form->key, getContents().c_str());
328         fl_set_input(bibitem_form->label, getOptions().c_str());
329         if (bibitem_form->bibitem_form->visible) {
330                 fl_raise_form(bibitem_form->bibitem_form);
331         } else {
332                 fl_show_form(bibitem_form->bibitem_form,
333                              FL_PLACE_MOUSE | FL_FREE_SIZE, FL_TRANSIENT,
334                              _("BibTeX"));
335         }   
336 }
337
338
339 bool InsetBibtex::addDatabase(string const & db)
340 {
341         string contents(getContents());
342         if (!contains(contents, db)) {
343                 if (!contents.empty()) 
344                         contents += ",";
345                 setContents(contents + db);
346                 return true;
347         }
348         return false;
349 }
350
351
352 bool InsetBibtex::delDatabase(string const & db)
353 {
354         if (contains(getContents(), db)) {
355                 string bd = db;
356                 int const n = tokenPos(getContents(), ',', bd);
357                 if (n > 0) {
358                         // Weird code, would someone care to explain this?(Lgb)
359                         string tmp(", ");
360                         tmp += bd;
361                         setContents(subst(getContents(), tmp, ", "));
362                 } else if (n == 0)
363                         setContents(split(getContents(), bd, ','));
364                 else 
365                         return false;
366         }
367         return true;
368 }
369
370
371 // ale070405 This function maybe shouldn't be here. We'll fix this at 0.13.
372 int bibitemMaxWidth(BufferView * bv, LyXFont const & font)
373 {
374         int w = 0;
375         // Does look like a hack? It is! (but will change at 0.13)
376         LyXParagraph * par = bv->buffer()->paragraph;
377     
378         while (par) {
379                 if (par->bibkey) {
380                         int const wx = par->bibkey->width(bv, font);
381                         if (wx > w) w = wx;
382                 }
383                 par = par->next;
384         }
385         return w;
386 }
387
388
389 // ale070405
390 string const bibitemWidest(Buffer const * buffer)
391 {
392         int w = 0;
393         // Does look like a hack? It is! (but will change at 0.13)
394         LyXParagraph * par = buffer->paragraph;
395         BufferView * bv = buffer->getUser();
396         InsetBibKey * bkey = 0;
397         LyXFont font;
398       
399         while (par) {
400                 if (par->bibkey) {
401                         int const wx =
402                                 lyxfont::width(par->bibkey->getScreenLabel(),
403                                                font);
404                         if (wx > w) {
405                                 w = wx;
406                                 bkey = par->bibkey;
407                         }
408                 }
409                 par = par->next;
410         }
411     
412         if (bkey && !bkey->getScreenLabel().empty())
413                 return bkey->getScreenLabel();
414     
415         return "99";
416 }