]> git.lyx.org Git - lyx.git/blob - src/insets/insetbibtex.C
Rename ascii to plaintext and LatexRunParams to OutputParams.
[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  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "insetbibtex.h"
14
15 #include "buffer.h"
16 #include "bufferparams.h"
17 #include "dispatchresult.h"
18 #include "debug.h"
19 #include "funcrequest.h"
20 #include "gettext.h"
21 #include "metricsinfo.h"
22 #include "outputparams.h"
23
24 #include "support/filetools.h"
25 #include "support/lstrings.h"
26 #include "support/os.h"
27 #include "support/path.h"
28
29 #include <fstream>
30
31 using lyx::support::ascii_lowercase;
32 using lyx::support::ChangeExtension;
33 using lyx::support::contains;
34 using lyx::support::findtexfile;
35 using lyx::support::IsFileReadable;
36 using lyx::support::ltrim;
37 using lyx::support::MakeAbsPath;
38 using lyx::support::Path;
39 using lyx::support::prefixIs;
40 using lyx::support::rtrim;
41 using lyx::support::split;
42 using lyx::support::subst;
43 using lyx::support::tokenPos;
44 using lyx::support::trim;
45
46 namespace os = lyx::support::os;
47
48 using std::endl;
49 using std::getline;
50 using std::string;
51 using std::ifstream;
52 using std::ostream;
53 using std::pair;
54 using std::vector;
55
56
57 InsetBibtex::InsetBibtex(InsetCommandParams const & p)
58         : InsetCommand(p)
59 {}
60
61
62 InsetBibtex::~InsetBibtex()
63 {
64         InsetCommandMailer("bibtex", *this).hideDialog();
65 }
66
67
68 std::auto_ptr<InsetBase> InsetBibtex::clone() const
69 {
70         return std::auto_ptr<InsetBase>(new InsetBibtex(*this));
71 }
72
73
74 void InsetBibtex::metrics(MetricsInfo & mi, Dimension & dim) const
75 {
76         InsetCommand::metrics(mi, dim);
77         int center_indent = (mi.base.textwidth - dim.wid) / 2;
78         Box b(center_indent, center_indent + dim.wid, -dim.asc, dim.des);
79         button().setBox(b);
80         dim.wid = mi.base.textwidth;
81         dim_ = dim;
82 }
83
84
85 void InsetBibtex::draw(PainterInfo & pi, int x, int y) const
86 {
87         InsetCommand::draw(pi, x + button().box().x1, y);
88 }
89
90
91 DispatchResult
92 InsetBibtex::priv_dispatch(FuncRequest const & cmd,
93                            idx_type & idx, pos_type & pos)
94 {
95         DispatchResult result(false);
96         
97         switch (cmd.action) {
98
99         case LFUN_INSET_DIALOG_SHOW:
100                 InsetCommandMailer("bibtex", *this).showDialog(cmd.view());
101                 result.dispatched(true);
102                 break;
103                 
104         case LFUN_MOUSE_RELEASE:
105                 if (button().box().contains(cmd.x, cmd.y))
106                         InsetCommandMailer("bibtex", *this).showDialog(cmd.view());
107                 result.dispatched(true);
108                 break;
109                 
110         case LFUN_INSET_MODIFY: {
111                 InsetCommandParams p;
112                 InsetCommandMailer::string2params(cmd.argument, p);
113                 if (!p.getCmdName().empty())
114                         setParams(p);
115                 result.dispatched(true);
116                 result.update(true);
117         }
118                 break;
119                 
120         default:
121                 result = InsetCommand::priv_dispatch(cmd, idx, pos);
122                 break;
123         }
124
125         return result;
126 }
127
128
129 string const InsetBibtex::getScreenLabel(Buffer const &) const
130 {
131         return _("BibTeX Generated References");
132 }
133
134
135 int InsetBibtex::latex(Buffer const & buffer, ostream & os,
136                        OutputParams const & runparams) const
137 {
138         // changing the sequence of the commands
139         // 1. \bibliographystyle{style}
140         // 2. \addcontentsline{...} - if option bibtotoc set
141         // 3. \bibliography{database}
142         string adb;
143         string db_in = getContents();
144         db_in = split(db_in, adb, ',');
145
146         // Style-Options
147         string style = getOptions(); // maybe empty! and with bibtotoc
148         string bibtotoc;
149         if (prefixIs(style, "bibtotoc")) {
150                 bibtotoc = "bibtotoc";
151                 if (contains(style, ',')) {
152                         style = split(style, bibtotoc, ',');
153                 }
154         }
155
156         if (!runparams.nice
157             && IsFileReadable(MakeAbsPath(style, buffer.filePath()) + ".bst")) {
158                 style = MakeAbsPath(style, buffer.filePath());
159         }
160
161         if (!style.empty()) { // we want no \biblio...{}
162                 os << "\\bibliographystyle{" << style << "}\n";
163         }
164
165         // bibtotoc-Option
166         if (!bibtotoc.empty()) {
167                 // maybe a problem when a textclass has no "art" as
168                 // part of its name, because it's than book.
169                 // For the "official" lyx-layouts it's no problem to support
170                 // all well
171                 if (!contains(buffer.params().getLyXTextClass().name(),
172                               "art")) {
173                         if (buffer.params().sides == LyXTextClass::OneSide) {
174                                 // oneside
175                                 os << "\\clearpage";
176                         } else {
177                                 // twoside
178                                 os << "\\cleardoublepage";
179                         }
180
181                         // bookclass
182                         os << "\\addcontentsline{toc}{chapter}{\\bibname}";
183
184                 } else {
185                         // article class
186                         os << "\\addcontentsline{toc}{section}{\\refname}";
187                 }
188         }
189
190         // database
191         // If we generate in a temp dir, we might need to give an
192         // absolute path there. This is a bit complicated since we can
193         // have a comma-separated list of bibliographies
194         string db_out;
195         while (!adb.empty()) {
196                 if (!runparams.nice &&
197                     IsFileReadable(MakeAbsPath(adb, buffer.filePath())+".bib"))
198                          adb = os::external_path(MakeAbsPath(adb, buffer.filePath()));
199                 db_out += adb;
200                 db_out += ',';
201                 db_in = split(db_in, adb,',');
202         }
203         db_out = rtrim(db_out, ",");
204         os   << "\\bibliography{" << db_out << "}\n";
205         return 2;
206 }
207
208
209 vector<string> const InsetBibtex::getFiles(Buffer const & buffer) const
210 {
211         Path p(buffer.filePath());
212
213         vector<string> vec;
214
215         string tmp;
216         string bibfiles = getContents();
217         bibfiles = split(bibfiles, tmp, ',');
218         while (!tmp.empty()) {
219                 string file = findtexfile(ChangeExtension(tmp, "bib"), "bib");
220                 lyxerr[Debug::LATEX] << "Bibfile: " << file << endl;
221
222                 // If we didn't find a matching file name just fail silently
223                 if (!file.empty())
224                         vec.push_back(file);
225
226                 // Get next file name
227                 bibfiles = split(bibfiles, tmp, ',');
228         }
229
230         return vec;
231 }
232
233
234 // This method returns a comma separated list of Bibtex entries
235 void InsetBibtex::fillWithBibKeys(Buffer const & buffer,
236                                   std::vector<std::pair<string, string> > & keys) const
237 {
238         vector<string> const files = getFiles(buffer);
239         for (vector<string>::const_iterator it = files.begin();
240              it != files.end(); ++ it) {
241                 // This is a _very_ simple parser for Bibtex database
242                 // files. All it does is to look for lines starting
243                 // in @ and not being @preamble and @string entries.
244                 // It does NOT do any syntax checking!
245                 ifstream ifs(it->c_str());
246                 string linebuf0;
247                 while (getline(ifs, linebuf0)) {
248                         string linebuf = trim(linebuf0);
249                         if (linebuf.empty()) continue;
250                         if (prefixIs(linebuf, "@")) {
251                                 linebuf = subst(linebuf, '{', '(');
252                                 string tmp;
253                                 linebuf = split(linebuf, tmp, '(');
254                                 tmp = ascii_lowercase(tmp);
255                                 if (!prefixIs(tmp, "@string")
256                                     && !prefixIs(tmp, "@preamble")) {
257                                         linebuf = split(linebuf, tmp, ',');
258                                         tmp = ltrim(tmp, " \t");
259                                         if (!tmp.empty()) {
260                                                 keys.push_back(pair<string,string>(tmp,string()));
261                                         }
262                                 }
263                         } else if (!keys.empty()) {
264                                 keys.back().second += linebuf + "\n";
265                         }
266                 }
267         }
268 }
269
270
271 bool InsetBibtex::addDatabase(string const & db)
272 {
273         string contents(getContents());
274         if (!contains(contents, db)) {
275                 if (!contents.empty())
276                         contents += ',';
277                 setContents(contents + db);
278                 return true;
279         }
280         return false;
281 }
282
283
284 bool InsetBibtex::delDatabase(string const & db)
285 {
286         if (contains(getContents(), db)) {
287                 string bd = db;
288                 int const n = tokenPos(getContents(), ',', bd);
289                 if (n > 0) {
290                         // Weird code, would someone care to explain this?(Lgb)
291                         string tmp(", ");
292                         tmp += bd;
293                         setContents(subst(getContents(), tmp, ", "));
294                 } else if (n == 0)
295                         setContents(split(getContents(), bd, ','));
296                 else
297                         return false;
298         }
299         return true;
300 }