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