]> git.lyx.org Git - lyx.git/blob - src/insets/insetbibtex.C
503e66846307c6cdbd1a79bc4672ff806d5652b0
[lyx.git] / src / insets / insetbibtex.C
1 /**
2  * \file insetbibtex.C
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  * \author Angus Leeming
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "insetbibtex.h"
15 #include "metricsinfo.h"
16 #include "buffer.h"
17 #include "BufferView.h"
18 #include "debug.h"
19 #include "funcrequest.h"
20 #include "gettext.h"
21 #include "latexrunparams.h"
22 #include "lyxlex.h"
23 #include "Lsstream.h"
24 #include "metricsinfo.h"
25
26 #include "support/filetools.h"
27 #include "support/path.h"
28 #include "support/os.h"
29 #include "support/lstrings.h"
30 #include "support/lyxalgo.h"
31 #include "support/LAssert.h"
32 #include "support/tostr.h"
33
34 #include <fstream>
35
36 using namespace lyx;
37 using support::FileName;
38
39 using std::ostream;
40 using std::ifstream;
41 using std::endl;
42 using std::vector;
43 using std::pair;
44
45
46 InsetBibtexParams::InsetBibtexParams()
47         : bibtotoc(false)
48 {}
49
50
51 bool InsetBibtexParams::empty() const
52 {
53         return databases.empty();
54 }
55
56
57 void InsetBibtexParams::erase()
58 {
59         databases.clear();
60         style.erase();
61         bibtotoc = false;
62 }
63
64
65 void InsetBibtexParams::write(Buffer const & buffer, std::ostream & os) const
66 {
67         os << "Bibtex\n";
68         
69         vector<FileName>::const_iterator it  = databases.begin();
70         vector<FileName>::const_iterator end = databases.end();
71         for (; it != end; ++it) {
72                 os << "\tfilename "
73                    << it->outputFilename(buffer.filePath())
74                    << '\n';
75         }
76         if (!style.empty())
77                 os << "\tstyle " << style << '\n';
78         if (bibtotoc)
79                 os << "\tbibtotoc " << tostr(bibtotoc) << '\n';
80 }
81
82
83 void InsetBibtexParams::read(Buffer const & buffer, LyXLex & lex)
84 {
85         enum BibtexTags {
86                 BIB_FILENAME = 1,
87                 BIB_STYLE,
88                 BIB_BIBTOTOC,
89                 BIB_END
90         };
91
92         keyword_item bibtex_tags[] = {
93                 { "\\end_inset", BIB_END },
94                 { "bibtotoc",    BIB_BIBTOTOC },
95                 { "filename",    BIB_FILENAME},
96                 { "style",       BIB_STYLE}
97         };
98
99         pushpophelper pph(lex, bibtex_tags, BIB_END);
100
101         bool found_end  = false;
102         bool read_error = false;
103
104         while (lex.isOK()) {
105                 switch (lex.lex()) {
106                 case BIB_FILENAME: {
107                         lex.next();
108                         FileName filename;
109                         filename.set(lex.getString(), buffer.filePath());
110                         databases.push_back(filename);
111                         break;
112                 }
113
114                 case BIB_STYLE: {
115                         lex.next();
116                         style = lex.getString();
117                         break;
118                 }
119
120                 case BIB_BIBTOTOC: {
121                         lex.next();
122                         bibtotoc = lex.getBool();
123                         break;
124                 }
125
126                 case BIB_END:
127                         found_end = true;
128                         break;
129
130                 default:
131                         lex.printError("BibtexInset::read: "
132                                        "Wrong tag: $$Token");
133                         read_error = true;
134                         break;
135                 }
136
137                 if (found_end || read_error)
138                         break;
139         }
140
141         if (!found_end) {
142                 lex.printError("BibtexInset::read: "
143                                "Missing \\end_inset.");
144         }
145 }
146
147
148 InsetBibtex::InsetBibtex()
149         : set_label_(false), center_indent_(0)
150 {}
151
152
153
154 InsetBibtex::InsetBibtex(InsetBibtexParams const & p)
155         : params_(p), set_label_(false), center_indent_(0)
156 {}
157
158
159 InsetBibtex::~InsetBibtex()
160 {
161         InsetBibtexMailer(*this).hideDialog();
162 }
163
164
165 std::auto_ptr<InsetBase> InsetBibtex::clone() const
166 {
167         return std::auto_ptr<InsetBase>(new InsetBibtex(*this));
168 }
169
170
171 dispatch_result InsetBibtex::localDispatch(FuncRequest const & cmd)
172 {
173         switch (cmd.action) {
174
175         case LFUN_INSET_EDIT:
176                 InsetBibtexMailer(*this).showDialog(cmd.view());
177                 return DISPATCHED;
178
179         case LFUN_INSET_MODIFY: {
180                 Buffer const * buffer = cmd.view()->buffer();
181                 InsetBibtexParams p;
182                 InsetBibtexMailer::string2params(cmd.argument, *buffer, p);
183                 setParams(p);
184                 return  DISPATCHED;
185         }
186
187         case LFUN_INSET_DIALOG_UPDATE:
188                 InsetBibtexMailer(*this).updateDialog(cmd.view());
189                 return DISPATCHED;
190
191         case LFUN_MOUSE_RELEASE:
192                 return localDispatch(FuncRequest(cmd.view(), LFUN_INSET_EDIT));
193
194         default:
195                 return InsetOld::localDispatch(cmd);
196         }
197
198 }
199
200 string const InsetBibtex::getScreenLabel(Buffer const &) const
201 {
202         return _("BibTeX Generated References");
203 }
204
205
206 void InsetBibtex::metrics(MetricsInfo & mi, Dimension & dim) const
207 {
208         if (!set_label_) {
209                 set_label_ = true;
210                 button_.update(getScreenLabel(*mi.base.bv->buffer()),
211                                editable() != NOT_EDITABLE);
212         }
213         button_.metrics(mi, dim);
214         center_indent_ = (mi.base.textwidth - dim.wid) / 2;
215         dim.wid = mi.base.textwidth;
216         dim_ = dim;
217 }
218
219
220 void InsetBibtex::draw(PainterInfo & pi, int x, int y) const
221 {
222         button_.draw(pi, x + center_indent_, y);
223 }
224
225
226 void InsetBibtex::write(Buffer const & buffer, std::ostream & os) const
227 {
228         params().write(buffer, os);
229 }
230
231
232 void InsetBibtex::read(Buffer const & buffer, LyXLex & lex)
233 {
234         InsetBibtexParams p;
235         p.read(buffer, lex);
236
237         // Replace the inset's store
238         setParams(p);
239 }
240
241
242 int InsetBibtex::latex(Buffer const & buffer, ostream & os,
243                        LatexRunParams const & runparams) const
244 {
245         ostringstream ss;
246         // 1. \bibliographystyle{style}
247         if (!params().style.empty()) { // we want no \biblio...{}
248                 string style = params().style;
249
250                 string const abs_style = 
251                         support::MakeAbsPath(style, buffer.filePath());
252                 if (!runparams.nice && support::IsFileReadable(abs_style + ".bst"))
253                         style = abs_style;
254
255                 ss << "\\bibliographystyle{" << style << "}\n";
256         }
257
258         // 2. \addcontentsline{...} - if option bibtotoc set
259         if (params().bibtotoc) {
260                 // Assumption: if the textclass name does not contain "art",
261                 // then it's a book.
262                 BufferParams const & bp = buffer.params;
263                 if (!support::contains(bp.getLyXTextClass().name(), "art")) {
264                         if (bp.sides == LyXTextClass::OneSide) {
265                                 ss << "\\clearpage";
266                         } else {
267                                 ss << "\\cleardoublepage";
268                         }
269
270                         // book class
271                         ss << "\\addcontentsline{toc}{chapter}{\\bibname}\n";
272                 } else {
273                         // article class
274                         ss << "\\addcontentsline{toc}{section}{\\refname}\n";
275                 }
276         }
277
278         // 3. \bibliography{database}
279         // If we generate in a temp dir, we _need_ to use the absolute path,
280         // else rely on the user.
281         ss << "\\bibliography{";
282         vector<FileName>::const_iterator begin = params().databases.begin();
283         vector<FileName>::const_iterator end   = params().databases.end();
284         vector<FileName>::const_iterator it    = begin;
285         for (; it != end; ++it) {
286                 if (it != begin)
287                         ss << ',';
288                 string db = it->outputFilename(buffer.filePath());
289                 if (!runparams.nice &&
290                     support::IsFileReadable(it->absFilename())+".bib")
291                         db = support::os::external_path(it->absFilename());
292
293                 ss << db;
294         }
295         ss << '}';
296
297         string const output = STRCONV(ss.str());
298         os << output;
299         return int(lyx::count(output.begin(), output.end(),'\n') + 1);
300 }
301
302
303 int InsetBibtex::ascii(Buffer const &, std::ostream &, int) const
304 {
305         return 0;
306 }
307
308
309 int InsetBibtex::linuxdoc(Buffer const &, std::ostream &) const
310 {
311         return 0;
312 }
313
314
315 int InsetBibtex::docbook(Buffer const &, std::ostream &, bool) const
316 {
317         return 0;
318 }
319
320
321 vector<string> const InsetBibtex::getFiles(Buffer const & buffer) const
322 {
323         support::Path p(buffer.filePath());
324
325         vector<string> files;
326         vector<FileName>::const_iterator it  = params().databases.begin();
327         vector<FileName>::const_iterator end = params().databases.end();
328         for (; it != end; ++it) {
329                 // I really do need to pass the buffer path here...
330                 // FileName needs extending it would seem.
331                 string file_in = it->relFilename(buffer.filePath());
332                 string file_out = support::findtexfile(
333                         support::ChangeExtension(file_in, "bib"), "bib");
334                 lyxerr[Debug::LATEX] << "Bibfile: " << file_in
335                                      << ' ' << file_out << endl;
336
337                 // If we don't find a matching file name just fail silently
338                 if (!file_out.empty())
339                         files.push_back(file_out);
340         }
341
342         return files;
343 }
344
345
346 // This method returns a comma separated list of Bibtex entries
347 void InsetBibtex::fillWithBibKeys(Buffer const & buffer,
348                                   std::vector<std::pair<string, string> > & keys) const
349 {
350         vector<string> const files = getFiles(buffer);
351         for (vector<string>::const_iterator it = files.begin();
352              it != files.end(); ++ it) {
353                 // This is a _very_ simple parser for Bibtex database
354                 // files. All it does is to look for lines starting
355                 // in @ and not being @preamble and @string entries.
356                 // It does NOT do any syntax checking!
357                 ifstream ifs(it->c_str());
358                 string linebuf0;
359                 while (getline(ifs, linebuf0)) {
360                         string linebuf = support::trim(linebuf0);
361                         if (linebuf.empty()) continue;
362                         if (support::prefixIs(linebuf, "@")) {
363                                 linebuf = support::subst(linebuf, '{', '(');
364                                 string tmp;
365                                 linebuf = support::split(linebuf, tmp, '(');
366                                 tmp = support::ascii_lowercase(tmp);
367                                 if (!support::prefixIs(tmp, "@string")
368                                     && !support::prefixIs(tmp, "@preamble")) {
369                                         linebuf = support::split(linebuf, tmp, ',');
370                                         tmp = support::ltrim(tmp, " \t");
371                                         if (!tmp.empty()) {
372                                                 keys.push_back(pair<string,string>(tmp,string()));
373                                         }
374                                 }
375                         } else if (!keys.empty()) {
376                                 keys.back().second += linebuf + "\n";
377                         }
378                 }
379         }
380 }
381
382
383 bool InsetBibtex::addDatabase(string const & /* db */)
384 {
385 #ifdef WITH_WARNINGS
386 #warning addDatabase is currently disabled (no LFUN).
387 #endif
388 #if 0
389         vector<string>
390         string contents(getContents());
391         if (!support::contains(contents, db)) {
392                 if (!contents.empty())
393                         contents += ',';
394                 setContents(contents + db);
395                 return true;
396         }
397 #endif
398         return false;
399 }
400
401
402 bool InsetBibtex::delDatabase(string const & /* db */)
403 {
404 #ifdef WITH_WARNINGS
405 #warning delDatabase is currently disabled (no LFUN).
406 #endif
407 #if 0
408         if (support::contains(getContents(), db)) {
409                 string bd = db;
410                 int const n = tokenPos(getContents(), ',', bd);
411                 if (n > 0) {
412                         // Weird code, would someone care to explain this?(Lgb)
413                         string tmp(", ");
414                         tmp += bd;
415                         setContents(support::subst(getContents(), tmp, ", "));
416                 } else if (n == 0)
417                         setContents(support::split(getContents(), bd, ','));
418                 else
419                         return false;
420         }
421 #endif
422         return true;
423 }
424
425
426 void InsetBibtex::setParams(InsetBibtexParams const & params)
427 {
428         params_ = params;
429 }
430
431
432 string const InsetBibtexMailer::name_ = "bibtex";
433
434
435 InsetBibtexMailer::InsetBibtexMailer(InsetBibtex & inset)
436         : inset_(inset)
437 {}
438
439
440 string const InsetBibtexMailer::inset2string(Buffer const & buffer) const
441 {
442         return params2string(inset_.params(), buffer);
443 }
444
445
446 void InsetBibtexMailer::string2params(string const & in,
447                                       Buffer const & buffer,
448                                       InsetBibtexParams & params)
449 {
450         params = InsetBibtexParams();
451
452         if (in.empty())
453                 return;
454
455         istringstream data(STRCONV(in));
456         LyXLex lex(0,0);
457         lex.setStream(data);
458
459         if (lex.isOK()) {
460                 lex.next();
461                 string const token = lex.getString();
462                 if (token != name_)
463                         return;
464         }
465
466         // This is part of the inset proper that is usually swallowed
467         // by Buffer::readInset
468         if (lex.isOK()) {
469                 lex.next();
470                 string const token = lex.getString();
471                 if (token != "Bibtex")
472                         return;
473         }
474
475         if (lex.isOK()) {
476                 params.read(buffer, lex);
477         }
478 }
479
480
481 string const InsetBibtexMailer::params2string(InsetBibtexParams const & params,
482                                               Buffer const & buffer)
483 {
484         ostringstream data;
485         data << name_ << ' ';
486         params.write(buffer, data);
487         data << "\\end_inset\n";
488         return STRCONV(data.str());
489 }