]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiBibtex.cpp
next one
[lyx.git] / src / frontends / qt4 / GuiBibtex.cpp
1 /**
2  * \file GuiBibtex.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Herbert Voß
8  * \author Angus Leeming
9  * \author Jürgen Spitzmüller
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "GuiBibtex.h"
17
18 #include "Buffer.h"
19 #include "BufferParams.h"
20 #include "debug.h"
21 #include "ui_BibtexAddUi.h"
22 #include "qt_helpers.h"
23 #include "Validator.h"
24 #include "LyXRC.h"
25 #include "gettext.h"
26
27 #include "ButtonPolicy.h"
28
29 #include "support/filetools.h" // changeExtension
30 #include "support/lstrings.h"
31 #include "support/FileFilterList.h"
32
33 #include "frontend_helpers.h"
34
35
36 #include <QPushButton>
37 #include <QListWidget>
38 #include <QCheckBox>
39 #include <QCloseEvent>
40 #include <QLineEdit>
41
42 using std::pair;
43 using std::string;
44 using std::vector;
45
46
47 namespace lyx {
48 namespace frontend {
49
50 using support::changeExtension;
51 using support::contains;
52 using support::FileFilterList;
53 using support::onlyFilename;
54 using support::prefixIs;
55 using support::split;
56 using support::trim;
57
58
59 GuiBibtex::GuiBibtex(LyXView & lv)
60         : GuiDialog(lv, "bibtex"), ControlCommand(*this, "bibtex")
61 {
62         setupUi(this);
63
64         setViewTitle( _("BibTeX Bibliography"));
65         setController(this, false);
66
67         QDialog::setModal(true);
68
69         connect(okPB, SIGNAL(clicked()),
70                 this, SLOT(slotOK()));
71         connect(closePB, SIGNAL(clicked()),
72                 this, SLOT(slotClose()));
73         connect(stylePB, SIGNAL(clicked()),
74                 this, SLOT(browsePressed()));
75         connect(deletePB, SIGNAL(clicked()),
76                 this, SLOT(deletePressed()));
77         connect(styleCB, SIGNAL(editTextChanged(QString)),
78                 this, SLOT(change_adaptor()));
79         connect(databaseLW, SIGNAL(itemSelectionChanged()),
80                 this, SLOT(databaseChanged()));
81         connect(bibtocCB, SIGNAL(clicked()),
82                 this, SLOT(change_adaptor()));
83         connect(btPrintCO, SIGNAL(activated(int)),
84                 this, SLOT(change_adaptor()));
85         connect(addBibPB, SIGNAL(clicked()),
86                 this, SLOT(addPressed()));
87
88         add_ = new GuiBibtexAddDialog(this);
89         add_bc_.setPolicy(ButtonPolicy::OkCancelPolicy);
90         add_bc_.setOK(add_->addPB);
91         add_bc_.setCancel(add_->closePB);
92         add_bc_.addCheckedLineEdit(add_->bibED, 0);
93
94         connect(add_->bibED, SIGNAL(textChanged(const QString &)),
95                 this, SLOT(bibEDChanged()));
96         connect(add_->addPB, SIGNAL(clicked()),
97                 this, SLOT(addDatabase()));
98         connect(add_->addPB, SIGNAL(clicked()),
99                 add_, SLOT(accept()));
100         connect(add_->bibLW, SIGNAL(itemActivated(QListWidgetItem *)),
101                 this, SLOT(addDatabase()));
102         connect(add_->bibLW, SIGNAL(itemActivated(QListWidgetItem *)),
103                 add_, SLOT(accept()));
104         connect(add_->bibLW, SIGNAL(itemSelectionChanged()),
105                 this, SLOT(availableChanged()));
106         connect(add_->browsePB, SIGNAL(clicked()),
107                 this, SLOT(browseBibPressed()));
108         connect(add_->closePB, SIGNAL(clicked()),
109                 add_, SLOT(reject()));
110
111         bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy);
112         bc().setOK(okPB);
113         bc().setCancel(closePB);
114         bc().addReadOnly(databaseLW);
115         bc().addReadOnly(stylePB);
116         bc().addReadOnly(styleCB);
117         bc().addReadOnly(bibtocCB);
118         bc().addReadOnly(addBibPB);
119         bc().addReadOnly(deletePB);
120 }
121
122
123 void GuiBibtex::bibEDChanged()
124 {
125         // Indicate to the button controller that the contents have
126         // changed. The actual test of validity is carried out by
127         // the checkedLineEdit.
128         add_bc_.setValid(true);
129 }
130
131
132 void GuiBibtex::change_adaptor()
133 {
134         changed();
135 }
136
137
138 void GuiBibtex::browsePressed()
139 {
140         docstring const file = browseBst(docstring());
141
142         if (!file.empty()) {
143                 // FIXME UNICODE
144                 docstring const filen = from_utf8(changeExtension(to_utf8(file), ""));
145                 bool present = false;
146                 unsigned int pres = 0;
147
148                 for (int i = 0; i != styleCB->count(); ++i) {
149                         if (qstring_to_ucs4(styleCB->itemText(i)) == filen) {
150                                 present = true;
151                                 pres = i;
152                         }
153                 }
154
155                 if (!present)
156                         styleCB->insertItem(0, toqstr(filen));
157
158                 styleCB->setCurrentIndex(pres);
159                 changed();
160         }
161 }
162
163
164 void GuiBibtex::browseBibPressed()
165 {
166         docstring const file = trim(browseBib(docstring()));
167
168         if (!file.empty()) {
169                 // FIXME UNICODE
170                 QString const f = toqstr(changeExtension(to_utf8(file), ""));
171                 bool present = false;
172
173                 for (int i = 0; i < add_->bibLW->count(); ++i) {
174                         if (add_->bibLW->item(i)->text() == f)
175                                 present = true;
176                 }
177
178                 if (!present) {
179                         add_->bibLW->addItem(f);
180                         changed();
181                 }
182
183                 add_->bibED->setText(f);
184         }
185 }
186
187
188 void GuiBibtex::addPressed()
189 {
190         add_bc_.setValid(false);
191         add_->exec();
192 }
193
194
195 void GuiBibtex::addDatabase()
196 {
197         int const sel = add_->bibLW->currentRow();
198         docstring const file = trim(qstring_to_ucs4(add_->bibED->text()));
199
200         if (sel < 0 && file.empty())
201                 return;
202
203         // Add the selected browser_bib keys to browser_database
204         // multiple selections are possible
205         for (int i = 0; i != add_->bibLW->count(); ++i) {
206                 QListWidgetItem * const item = add_->bibLW->item(i);
207                 if (add_->bibLW->isItemSelected(item)) {
208                         add_->bibLW->setItemSelected(item, false);
209                         QList<QListWidgetItem *> matches =
210                                 databaseLW->findItems(item->text(), Qt::MatchExactly);
211                         if (matches.empty())
212                                 databaseLW->addItem(item->text());
213                 }
214         }
215
216         if (!file.empty()) {
217                 add_->bibED->clear();
218                 QString const f = toqstr(from_utf8(changeExtension(to_utf8(file), "")));
219                 QList<QListWidgetItem *> matches =
220                         databaseLW->findItems(f, Qt::MatchExactly);
221                 if (matches.empty())
222                         databaseLW->addItem(f);
223         }
224
225         changed();
226 }
227
228
229 void GuiBibtex::deletePressed()
230 {
231         databaseLW->takeItem(databaseLW->currentRow());
232         changed();
233 }
234
235
236
237 void GuiBibtex::databaseChanged()
238 {
239         deletePB->setEnabled(!isBufferReadonly() && databaseLW->currentRow() != -1);
240 }
241
242
243 void GuiBibtex::availableChanged()
244 {
245         add_bc_.setValid(true);
246 }
247
248
249 void GuiBibtex::closeEvent(QCloseEvent *e)
250 {
251         slotClose();
252         e->accept();
253 }
254
255
256 void GuiBibtex::updateContents()
257 {
258         bool bibtopic = usingBibtopic();
259
260         databaseLW->clear();
261
262         docstring bibs = params()["bibfiles"];
263         docstring bib;
264
265         while (!bibs.empty()) {
266                 bibs = split(bibs, bib, ',');
267                 bib = trim(bib);
268                 if (!bib.empty())
269                         databaseLW->addItem(toqstr(bib));
270         }
271
272         add_->bibLW->clear();
273
274         vector<string> bib_str;
275         getBibFiles(bib_str);
276         for (vector<string>::const_iterator it = bib_str.begin();
277                 it != bib_str.end(); ++it) {
278                 string bibItem(changeExtension(*it, ""));
279                 add_->bibLW->addItem(toqstr(bibItem));
280         }
281
282         string bibstyle = getStylefile();
283
284         bibtocCB->setChecked(bibtotoc() && !bibtopic);
285         bibtocCB->setEnabled(!bibtopic);
286
287         docstring btprint(params()["btprint"]);
288         int btp = 0;
289         if (btprint == "btPrintNotCited")
290                 btp = 1;
291         else if (btprint == "btPrintAll")
292                 btp = 2;
293
294         btPrintCO->setCurrentIndex(btp);
295         btPrintCO->setEnabled(bibtopic);
296
297         styleCB->clear();
298
299         int item_nr(-1);
300
301         vector<string> str;
302         getBibStyles(str);
303         for (vector<string>::const_iterator it = str.begin();
304                 it != str.end(); ++it) {
305                 string item(changeExtension(*it, ""));
306                 if (item == bibstyle)
307                         item_nr = int(it - str.begin());
308                 styleCB->addItem(toqstr(item));
309         }
310
311         if (item_nr == -1 && !bibstyle.empty()) {
312                 styleCB->addItem(toqstr(bibstyle));
313                 item_nr = styleCB->count() - 1;
314         }
315
316         if (item_nr != -1)
317                 styleCB->setCurrentIndex(item_nr);
318         else
319                 styleCB->clearEditText();
320 }
321
322
323 void GuiBibtex::applyView()
324 {
325         docstring dbs = qstring_to_ucs4(databaseLW->item(0)->text());
326
327         unsigned int maxCount = databaseLW->count();
328         for (unsigned int i = 1; i < maxCount; i++) {
329                 dbs += ',';
330                 dbs += qstring_to_ucs4(databaseLW->item(i)->text());
331         }
332
333         params()["bibfiles"] = dbs;
334
335         docstring const bibstyle(qstring_to_ucs4(styleCB->currentText()));
336         bool const bibtotoc(bibtocCB->isChecked());
337
338         if (bibtotoc && (!bibstyle.empty())) {
339                 // both bibtotoc and style
340                 params()["options"] = "bibtotoc," + bibstyle;
341         } else if (bibtotoc) {
342                 // bibtotoc and no style
343                 params()["options"] = from_ascii("bibtotoc");
344         } else {
345                 // only style. An empty one is valid, because some
346                 // documentclasses have an own \bibliographystyle{}
347                 // command!
348                 params()["options"] = bibstyle;
349         }
350
351         // bibtopic allows three kinds of sections:
352         // 1. sections that include all cited references of the database(s)
353         // 2. sections that include all uncited references of the database(s)
354         // 3. sections that include all references of the database(s), cited or not
355         int btp = btPrintCO->currentIndex();
356
357         switch (btp) {
358         case 0:
359                 params()["btprint"] = from_ascii("btPrintCited");
360                 break;
361         case 1:
362                 params()["btprint"] = from_ascii("btPrintNotCited");
363                 break;
364         case 2:
365                 params()["btprint"] = from_ascii("btPrintAll");
366                 break;
367         }
368
369         if (!usingBibtopic())
370                 params()["btprint"] = docstring();
371 }
372
373
374 bool GuiBibtex::isValid()
375 {
376         return databaseLW->count() != 0;
377 }
378
379
380 docstring const GuiBibtex::browseBib(docstring const & in_name) const
381 {
382         // FIXME UNICODE
383         pair<docstring, docstring> dir1(_("Documents|#o#O"),
384                                   from_utf8(lyxrc.document_path));
385         FileFilterList const filter(_("BibTeX Databases (*.bib)"));
386         return browseRelFile(in_name, from_utf8(bufferFilepath()),
387                              _("Select a BibTeX database to add"),
388                              filter, false, dir1);
389 }
390
391
392 docstring const GuiBibtex::browseBst(docstring const & in_name) const
393 {
394         // FIXME UNICODE
395         pair<docstring, docstring> dir1(_("Documents|#o#O"),
396                                   from_utf8(lyxrc.document_path));
397         FileFilterList const filter(_("BibTeX Styles (*.bst)"));
398         return browseRelFile(in_name, from_utf8(bufferFilepath()),
399                              _("Select a BibTeX style"), filter, false, dir1);
400 }
401
402
403 void GuiBibtex::getBibStyles(vector<string> & data) const
404 {
405         data.clear();
406
407         getTexFileList("bstFiles.lst", data);
408         // test, if we have a valid list, otherwise run rescan
409         if (data.empty()) {
410                 rescanBibStyles();
411                 getTexFileList("bstFiles.lst", data);
412         }
413         vector<string>::iterator it  = data.begin();
414         vector<string>::iterator end = data.end();
415         for (; it != end; ++it) {
416                 *it = onlyFilename(*it);
417         }
418         // sort on filename only (no path)
419         std::sort(data.begin(), data.end());
420 }
421
422
423 void GuiBibtex::getBibFiles(vector<string> & data) const
424 {
425         data.clear();
426
427         getTexFileList("bibFiles.lst", data);
428         // test, if we have a valid list, otherwise run rescan
429         if (data.empty()) {
430                 rescanBibStyles();
431                 getTexFileList("bibFiles.lst", data);
432         }
433         vector<string>::iterator it  = data.begin();
434         vector<string>::iterator end = data.end();
435         for (; it != end; ++it) {
436                 *it = onlyFilename(*it);
437         }
438         // sort on filename only (no path)
439         std::sort(data.begin(), data.end());
440 }
441
442
443 void GuiBibtex::rescanBibStyles() const
444 {
445         rescanTexStyles();
446 }
447
448
449 bool GuiBibtex::usingBibtopic() const
450 {
451         return buffer().params().use_bibtopic;
452 }
453
454
455 bool GuiBibtex::bibtotoc() const
456 {
457         return prefixIs(to_utf8(params()["options"]), "bibtotoc");
458 }
459
460
461 string const GuiBibtex::getStylefile() const
462 {
463         // the different bibtex packages have (and need) their
464         // own "plain" stylefiles
465         biblio::CiteEngine const engine = buffer().params().getEngine();
466         docstring defaultstyle;
467         switch (engine) {
468         case biblio::ENGINE_BASIC:
469                 defaultstyle = from_ascii("plain");
470                 break;
471         case biblio::ENGINE_NATBIB_AUTHORYEAR:
472                 defaultstyle = from_ascii("plainnat");
473                 break;
474         case biblio::ENGINE_NATBIB_NUMERICAL:
475                 defaultstyle = from_ascii("plainnat");
476                 break;
477         case biblio::ENGINE_JURABIB:
478                 defaultstyle = from_ascii("jurabib");
479                 break;
480         }
481
482         docstring bst = params()["options"];
483         if (bibtotoc()){
484                 // bibstyle exists?
485                 if (contains(bst, ',')) {
486                         docstring bibtotoc = from_ascii("bibtotoc");
487                         bst = split(bst, bibtotoc, ',');
488                 } else
489                         bst.erase();
490         }
491
492         // propose default style file for new insets
493         // existing insets might have (legally) no bst files
494         // (if the class already provides a style)
495         if (bst.empty() && params()["bibfiles"].empty())
496                 bst = defaultstyle;
497
498         // FIXME UNICODE
499         return to_utf8(bst);
500 }
501
502
503 Dialog * createGuiBibtex(LyXView & lv) { return new GuiBibtex(lv); }
504
505
506 } // namespace frontend
507 } // namespace lyx
508
509 #include "GuiBibtex_moc.cpp"