3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * \author Angus Leeming
9 * \author Jürgen Spitzmüller
11 * Full author contact details are available in file CREDITS.
16 #include "GuiBibtex.h"
19 #include "BufferParams.h"
20 #include "CiteEnginesList.h"
22 #include "FuncRequest.h"
24 #include "qt_helpers.h"
25 #include "Validator.h"
27 #include "ui_BibtexAddUi.h"
29 #include "ButtonPolicy.h"
31 #include "frontends/alert.h"
33 #include "insets/InsetBibtex.h"
35 #include "support/debug.h"
36 #include "support/ExceptionMessage.h"
37 #include "support/FileName.h"
38 #include "support/filetools.h" // changeExtension
39 #include "support/gettext.h"
40 #include "support/lstrings.h"
42 #include <QPushButton>
43 #include <QListWidget>
48 using namespace lyx::support;
54 GuiBibtex::GuiBibtex(GuiView & lv)
55 : GuiDialog(lv, "bibtex", qt_("BibTeX Bibliography")),
56 params_(insetCode("bibtex"))
60 QDialog::setModal(true);
61 setWindowModality(Qt::WindowModal);
63 connect(okPB, SIGNAL(clicked()),
64 this, SLOT(slotOK()));
65 connect(closePB, SIGNAL(clicked()),
66 this, SLOT(slotClose()));
67 connect(stylePB, SIGNAL(clicked()),
68 this, SLOT(browsePressed()));
69 connect(deletePB, SIGNAL(clicked()),
70 this, SLOT(deletePressed()));
71 connect(upPB, SIGNAL(clicked()),
72 this, SLOT(upPressed()));
73 connect(downPB, SIGNAL(clicked()),
74 this, SLOT(downPressed()));
75 connect(styleCB, SIGNAL(editTextChanged(QString)),
76 this, SLOT(change_adaptor()));
77 connect(databaseLW, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
78 this, SLOT(databaseChanged()));
79 connect(bibtocCB, SIGNAL(clicked()),
80 this, SLOT(change_adaptor()));
81 connect(btPrintCO, SIGNAL(activated(int)),
82 this, SLOT(change_adaptor()));
83 connect(addBibPB, SIGNAL(clicked()),
84 this, SLOT(addPressed()));
85 connect(rescanPB, SIGNAL(clicked()),
86 this, SLOT(rescanClicked()));
87 connect(biblatexOptsLE, SIGNAL(textChanged(QString)),
88 this, SLOT(change_adaptor()));
89 connect(bibEncodingCO, SIGNAL(activated(int)),
90 this, SLOT(change_adaptor()));
92 add_ = new GuiBibtexAddDialog(this);
93 add_bc_.setPolicy(ButtonPolicy::OkCancelPolicy);
94 add_bc_.setOK(add_->addPB);
95 add_bc_.setCancel(add_->closePB);
96 add_bc_.addCheckedLineEdit(add_->bibED, 0);
98 connect(add_->bibED, SIGNAL(textChanged(QString)),
99 this, SLOT(bibEDChanged()));
100 connect(add_->addPB, SIGNAL(clicked()),
101 this, SLOT(addDatabase()));
102 connect(add_->addPB, SIGNAL(clicked()),
103 add_, SLOT(accept()));
104 connect(add_->rescanPB, SIGNAL(clicked()),
105 this, SLOT(rescanClicked()));
106 connect(add_->bibLW, SIGNAL(itemActivated(QListWidgetItem *)),
107 this, SLOT(addDatabase()));
108 connect(add_->bibLW, SIGNAL(itemActivated(QListWidgetItem *)),
109 add_, SLOT(accept()));
110 connect(add_->bibLW, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
111 this, SLOT(availableChanged()));
112 connect(add_->browsePB, SIGNAL(clicked()),
113 this, SLOT(browseBibPressed()));
114 connect(add_->closePB, SIGNAL(clicked()),
115 add_, SLOT(reject()));
117 add_->bibLW->setToolTip(formatToolTip(qt_("This list consists of all databases that are indexed by LaTeX and thus are found without a file path. "
118 "This is usually everything in the bib/ subdirectory of LaTeX's texmf tree. "
119 "If you want to reuse your own database, this is the place you should store it.")));
121 bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy);
123 bc().setCancel(closePB);
124 bc().addReadOnly(databaseLW);
125 bc().addReadOnly(stylePB);
126 bc().addReadOnly(styleCB);
127 bc().addReadOnly(bibtocCB);
128 bc().addReadOnly(addBibPB);
129 bc().addReadOnly(bibEncodingCO);
130 // Delete/Up/Down are handled with more conditions in
131 // databaseChanged().
133 // Always put the default encoding in the first position.
134 bibEncodingCO->addItem(qt_("Document Encoding"), "default");
135 QMap<QString, QString> encodinglist;
136 for (auto const & encvar : encodings) {
137 if (!encvar.unsafe() && !encvar.guiName().empty())
138 encodinglist.insert(qt_(encvar.guiName()), toqstr(encvar.name()));
140 QMap<QString, QString>::const_iterator it = encodinglist.constBegin();
141 while (it != encodinglist.constEnd()) {
142 bibEncodingCO->addItem(it.key(), it.value());
146 // Make sure the delete/up/down buttons are disabled if necessary.
151 void GuiBibtex::bibEDChanged()
153 // Indicate to the button controller that the contents have
154 // changed. The actual test of validity is carried out by
155 // the checkedLineEdit.
156 add_bc_.setValid(true);
160 void GuiBibtex::change_adaptor()
166 void GuiBibtex::browsePressed()
168 QString const file = browseBst(QString());
173 QString const filen = changeExtension(file, "");
174 bool present = false;
175 unsigned int pres = 0;
177 for (int i = 0; i != styleCB->count(); ++i) {
178 if (styleCB->itemText(i) == filen) {
185 styleCB->insertItem(0, filen);
187 styleCB->setCurrentIndex(pres);
192 void GuiBibtex::browseBibPressed()
194 QString const file = browseBib(QString()).trimmed();
199 QString const f = changeExtension(file, "");
200 bool present = false;
202 for (int i = 0; i < add_->bibLW->count(); ++i) {
203 if (add_->bibLW->item(i)->text() == f)
208 add_->bibLW->addItem(f);
212 add_->bibED->setText(f);
216 void GuiBibtex::addPressed()
218 add_bc_.setValid(false);
223 void GuiBibtex::addDatabase()
225 int const sel = add_->bibLW->currentRow();
226 QString const file = add_->bibED->text().trimmed();
228 if (sel < 0 && file.isEmpty())
231 // Add the selected browser_bib keys to browser_database
232 // multiple selections are possible
233 for (int i = 0; i != add_->bibLW->count(); ++i) {
234 QListWidgetItem * const item = add_->bibLW->item(i);
235 if (add_->bibLW->isItemSelected(item)) {
236 add_->bibLW->setItemSelected(item, false);
237 QList<QListWidgetItem *> matches =
238 databaseLW->findItems(item->text(), Qt::MatchExactly);
239 if (matches.empty()) {
240 QString label = item->text();
241 QListWidgetItem * db = new QListWidgetItem(label);
242 db->setFlags(db->flags() | Qt::ItemIsSelectable);
243 databaseLW->addItem(db);
248 if (!file.isEmpty()) {
249 add_->bibED->clear();
250 QString const f = changeExtension(file, "");
251 QList<QListWidgetItem *> matches =
252 databaseLW->findItems(f, Qt::MatchExactly);
253 if (matches.empty()) {
254 QListWidgetItem * db = new QListWidgetItem(f);
255 db->setFlags(db->flags() | Qt::ItemIsSelectable);
256 databaseLW->addItem(db);
265 void GuiBibtex::deletePressed()
267 QListWidgetItem *cur = databaseLW->takeItem(databaseLW->currentRow());
276 void GuiBibtex::upPressed()
278 int row = databaseLW->currentRow();
279 QListWidgetItem *cur = databaseLW->takeItem(row);
280 databaseLW->insertItem(row - 1, cur);
281 databaseLW->setCurrentItem(cur);
286 void GuiBibtex::downPressed()
288 int row = databaseLW->currentRow();
289 QListWidgetItem *cur = databaseLW->takeItem(row);
290 databaseLW->insertItem(row + 1, cur);
291 databaseLW->setCurrentItem(cur);
296 void GuiBibtex::rescanClicked()
303 void GuiBibtex::databaseChanged()
305 bool readOnly = isBufferReadonly();
306 int count = databaseLW->count();
307 int row = databaseLW->currentRow();
308 deletePB->setEnabled(!readOnly && row != -1);
309 upPB->setEnabled(!readOnly && count > 1 && row > 0);
310 downPB->setEnabled(!readOnly && count > 1 && row < count - 1);
314 void GuiBibtex::availableChanged()
316 add_bc_.setValid(true);
320 void GuiBibtex::updateContents()
322 bool bibtopic = usingBibtopic();
323 bool biblatex = usingBiblatex();
326 setTitle(qt_("Biblatex Bibliography"));
328 setTitle(qt_("BibTeX Bibliography"));
332 docstring bibs = params_["bibfiles"];
335 while (!bibs.empty()) {
336 bibs = split(bibs, bib, ',');
339 QListWidgetItem * db = new QListWidgetItem(toqstr(bib));
340 db->setFlags(db->flags() | Qt::ItemIsSelectable);
341 databaseLW->addItem(db);
345 add_->bibLW->clear();
347 QStringList bibfiles = bibFiles();
348 for (int i = 0; i != bibfiles.count(); ++i)
349 add_->bibLW->addItem(changeExtension(bibfiles[i], ""));
351 QString const bibstyle = styleFile();
353 bibtocCB->setChecked(bibtotoc() && !bibtopic);
354 bibtocCB->setEnabled(!bibtopic);
357 btPrintCO->addItem(qt_("all cited references"), toqstr("btPrintCited"));
359 btPrintCO->addItem(qt_("all uncited references"), toqstr("btPrintNotCited"));
360 btPrintCO->addItem(qt_("all references"), toqstr("btPrintAll"));
361 if (usingBiblatex() && !buffer().masterParams().multibib.empty())
362 btPrintCO->addItem(qt_("all reference units"), toqstr("bibbysection"));
364 docstring btprint = params_["btprint"];
367 btprint = from_ascii("btPrintCited");
368 btPrintCO->setCurrentIndex(btPrintCO->findData(toqstr(btprint)));
370 docstring encoding = params_["encoding"];
371 if (encoding.empty())
373 encoding = from_ascii("default");
374 bibEncodingCO->setCurrentIndex(bibEncodingCO->findData(toqstr(encoding)));
376 // Only useful for biblatex
377 biblatexOptsLA->setVisible(biblatex);
378 biblatexOptsLE->setVisible(biblatex);
380 // only useful for BibTeX
381 styleCB->setVisible(!biblatex);
382 styleLA->setVisible(!biblatex);
383 stylePB->setVisible(!biblatex);
390 QStringList const str = bibStyles();
391 for (int i = 0; i != str.count(); ++i) {
392 QString item = changeExtension(str[i], "");
393 if (item == bibstyle)
395 styleCB->addItem(item);
398 if (item_nr == -1 && !bibstyle.isEmpty()) {
399 styleCB->addItem(bibstyle);
400 item_nr = styleCB->count() - 1;
405 styleCB->setCurrentIndex(item_nr);
407 styleCB->clearEditText();
409 biblatexOptsLE->setText(toqstr(params_["biblatexopts"]));
413 void GuiBibtex::applyView()
417 unsigned int maxCount = databaseLW->count();
418 for (unsigned int i = 0; i < maxCount; i++) {
421 QString item = databaseLW->item(i)->text();
422 docstring bibfile = qstring_to_ucs4(item);
426 params_["bibfiles"] = dbs;
428 docstring const bibstyle = qstring_to_ucs4(styleCB->currentText());
429 bool const bibtotoc = bibtocCB->isChecked();
431 if (bibtotoc && !bibstyle.empty()) {
432 // both bibtotoc and style
433 params_["options"] = "bibtotoc," + bibstyle;
434 } else if (bibtotoc) {
435 // bibtotoc and no style
436 params_["options"] = from_ascii("bibtotoc");
438 // only style. An empty one is valid, because some
439 // documentclasses have an own \bibliographystyle{}
441 params_["options"] = bibstyle;
444 params_["biblatexopts"] = qstring_to_ucs4(biblatexOptsLE->text());
446 params_["btprint"] = qstring_to_ucs4(btPrintCO->itemData(btPrintCO->currentIndex()).toString());
448 params_["encoding"] = qstring_to_ucs4(bibEncodingCO->itemData(bibEncodingCO->currentIndex()).toString());
452 bool GuiBibtex::isValid()
454 return databaseLW->count() != 0;
458 QString GuiBibtex::browseBib(QString const & in_name) const
460 QString const label1 = qt_("D&ocuments");
461 QString const dir1 = toqstr(lyxrc.document_path);
462 QStringList const filter(qt_("BibTeX Databases (*.bib)"));
463 return browseRelToParent(in_name, bufferFilePath(),
464 qt_("Select a BibTeX database to add"), filter, false, label1, dir1);
468 QString GuiBibtex::browseBst(QString const & in_name) const
470 QString const label1 = qt_("D&ocuments");
471 QString const dir1 = toqstr(lyxrc.document_path);
472 QStringList const filter(qt_("BibTeX Styles (*.bst)"));
473 return browseRelToParent(in_name, bufferFilePath(),
474 qt_("Select a BibTeX style"), filter, false, label1, dir1);
478 QStringList GuiBibtex::bibStyles() const
480 QStringList sdata = texFileList("bstFiles.lst");
481 // test whether we have a valid list, otherwise run rescan
482 if (sdata.isEmpty()) {
484 sdata = texFileList("bstFiles.lst");
486 for (int i = 0; i != sdata.size(); ++i)
487 sdata[i] = onlyFileName(sdata[i]);
488 // sort on filename only (no path)
494 QStringList GuiBibtex::bibFiles() const
496 QStringList sdata = texFileList("bibFiles.lst");
497 // test whether we have a valid list, otherwise run rescan
498 if (sdata.isEmpty()) {
500 sdata = texFileList("bibFiles.lst");
502 for (int i = 0; i != sdata.size(); ++i)
503 sdata[i] = onlyFileName(sdata[i]);
504 // sort on filename only (no path)
510 void GuiBibtex::rescanBibStyles() const
513 rescanTexStyles("bib");
515 rescanTexStyles("bst bib");
519 bool GuiBibtex::usingBibtopic() const
521 return buffer().params().useBibtopic();
525 bool GuiBibtex::bibtotoc() const
527 return prefixIs(to_utf8(params_["options"]), "bibtotoc");
531 bool GuiBibtex::usingBiblatex() const
533 return buffer().masterBuffer()->params().useBiblatex();
537 QString GuiBibtex::styleFile() const
539 // the different bibtex packages have (and need) their
540 // own "plain" stylefiles
541 QString defaultstyle = toqstr(buffer().params().defaultBiblioStyle());
543 QString bst = toqstr(params_["options"]);
546 int pos = bst.indexOf(',');
549 // docstring bibtotoc = from_ascii("bibtotoc");
550 // bst = split(bst, bibtotoc, ',');
551 bst = bst.mid(pos + 1);
557 // propose default style file for new insets
558 // existing insets might have (legally) no bst files
559 // (if the class already provides a style)
560 if (bst.isEmpty() && params_["bibfiles"].empty())
567 bool GuiBibtex::initialiseParams(std::string const & sdata)
569 InsetCommand::string2params(sdata, params_);
574 void GuiBibtex::dispatchParams()
576 std::string const lfun = InsetCommand::params2string(params_);
577 dispatch(FuncRequest(getLfun(), lfun));
582 Dialog * createGuiBibtex(GuiView & lv) { return new GuiBibtex(lv); }
585 } // namespace frontend
588 #include "moc_GuiBibtex.cpp"