]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiBibtex.cpp
merge GuiCommand into its users.
[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 "FuncRequest.h"
21 #include "LyXRC.h"
22 #include "qt_helpers.h"
23 #include "Validator.h"
24
25 #include "ui_BibtexAddUi.h"
26
27 #include "ButtonPolicy.h"
28
29 #include "frontends/alert.h"
30
31 #include "insets/InsetBibtex.h"
32
33 #include "support/debug.h"
34 #include "support/ExceptionMessage.h"
35 #include "support/FileFilterList.h"
36 #include "support/FileName.h"
37 #include "support/filetools.h" // changeExtension
38 #include "support/gettext.h"
39 #include "support/lstrings.h"
40
41 #include <QPushButton>
42 #include <QListWidget>
43 #include <QCheckBox>
44 #include <QLineEdit>
45
46 using namespace std;
47 using namespace lyx::support;
48
49 namespace lyx {
50 namespace frontend {
51
52
53 GuiBibtex::GuiBibtex(GuiView & lv)
54         : GuiDialog(lv, "bibtex", qt_("BibTeX Bibliography")),
55           params_(insetCode("bibtex"))
56 {
57         setupUi(this);
58
59         QDialog::setModal(true);
60
61         connect(okPB, SIGNAL(clicked()),
62                 this, SLOT(slotOK()));
63         connect(closePB, SIGNAL(clicked()),
64                 this, SLOT(slotClose()));
65         connect(stylePB, SIGNAL(clicked()),
66                 this, SLOT(browsePressed()));
67         connect(deletePB, SIGNAL(clicked()),
68                 this, SLOT(deletePressed()));
69         connect(upPB, SIGNAL(clicked()),
70                 this, SLOT(upPressed()));
71         connect(downPB, SIGNAL(clicked()),
72                 this, SLOT(downPressed()));
73         connect(styleCB, SIGNAL(editTextChanged(QString)),
74                 this, SLOT(change_adaptor()));
75         connect(databaseLW, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
76                 this, SLOT(databaseChanged()));
77         connect(bibtocCB, SIGNAL(clicked()),
78                 this, SLOT(change_adaptor()));
79         connect(btPrintCO, SIGNAL(activated(int)),
80                 this, SLOT(change_adaptor()));
81         connect(addBibPB, SIGNAL(clicked()),
82                 this, SLOT(addPressed()));
83
84         add_ = new GuiBibtexAddDialog(this);
85         add_bc_.setPolicy(ButtonPolicy::OkCancelPolicy);
86         add_bc_.setOK(add_->addPB);
87         add_bc_.setCancel(add_->closePB);
88         add_bc_.addCheckedLineEdit(add_->bibED, 0);
89
90         connect(add_->bibED, SIGNAL(textChanged(QString)),
91                 this, SLOT(bibEDChanged()));
92         connect(add_->addPB, SIGNAL(clicked()),
93                 this, SLOT(addDatabase()));
94         connect(add_->addPB, SIGNAL(clicked()),
95                 add_, SLOT(accept()));
96         connect(add_->bibLW, SIGNAL(itemActivated(QListWidgetItem *)),
97                 this, SLOT(addDatabase()));
98         connect(add_->bibLW, SIGNAL(itemActivated(QListWidgetItem *)),
99                 add_, SLOT(accept()));
100         connect(add_->bibLW, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
101                 this, SLOT(availableChanged()));
102         connect(add_->browsePB, SIGNAL(clicked()),
103                 this, SLOT(browseBibPressed()));
104         connect(add_->closePB, SIGNAL(clicked()),
105                 add_, SLOT(reject()));
106
107         bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy);
108         bc().setOK(okPB);
109         bc().setCancel(closePB);
110         bc().addReadOnly(databaseLW);
111         bc().addReadOnly(stylePB);
112         bc().addReadOnly(styleCB);
113         bc().addReadOnly(bibtocCB);
114         bc().addReadOnly(addBibPB);
115         // Delete/Up/Down are handled with more conditions in
116         // databaseChanged().
117
118         // Make sure the delete/up/down buttons are disabled if necessary.
119         databaseChanged();
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         QString const file = browseBst(QString());
141
142         if (file.isEmpty())
143                 return;
144
145         QString const filen = changeExtension(file, "");
146         bool present = false;
147         unsigned int pres = 0;
148
149         for (int i = 0; i != styleCB->count(); ++i) {
150                 if (styleCB->itemText(i) == filen) {
151                         present = true;
152                         pres = i;
153                 }
154         }
155
156         if (!present)
157                 styleCB->insertItem(0, filen);
158
159         styleCB->setCurrentIndex(pres);
160         changed();
161 }
162
163
164 void GuiBibtex::browseBibPressed()
165 {
166         QString const file = browseBib(QString()).trimmed();
167
168         if (file.isEmpty())
169                 return;
170
171         QString const f = changeExtension(file, "");
172         bool present = false;
173
174         for (int i = 0; i < add_->bibLW->count(); ++i) {
175                 if (add_->bibLW->item(i)->text() == f)
176                         present = true;
177         }
178
179         if (!present) {
180                 add_->bibLW->addItem(f);
181                 changed();
182         }
183
184         add_->bibED->setText(f);
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         QString const file = add_->bibED->text().trimmed();
199
200         if (sel < 0 && file.isEmpty())
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                                 QString label = item->text();
213                                 QListWidgetItem * db = new QListWidgetItem(label);
214                                 db->setFlags(db->flags() | Qt::ItemIsSelectable);
215                                 databaseLW->addItem(db);
216                         }
217                 }
218         }
219
220         if (!file.isEmpty()) {
221                 add_->bibED->clear();
222                 QString const f = changeExtension(file, "");
223                 QList<QListWidgetItem *> matches =
224                         databaseLW->findItems(f, Qt::MatchExactly);
225                 if (matches.empty()) {
226                         QListWidgetItem * db = new QListWidgetItem(f);
227                         db->setFlags(db->flags() | Qt::ItemIsSelectable);
228                         databaseLW->addItem(db);
229                 }
230         }
231
232         databaseChanged();
233         changed();
234 }
235
236
237 void GuiBibtex::deletePressed()
238 {
239         QListWidgetItem *cur = databaseLW->takeItem(databaseLW->currentRow());
240         if (cur) {
241                 delete cur;
242                 databaseChanged();
243                 changed();
244         }
245 }
246
247
248 void GuiBibtex::upPressed()
249 {
250         int row = databaseLW->currentRow();
251         QListWidgetItem *cur = databaseLW->takeItem(row);
252         databaseLW->insertItem(row - 1, cur);
253         databaseLW->setCurrentItem(cur);
254         changed();
255 }
256
257
258 void GuiBibtex::downPressed()
259 {
260         int row = databaseLW->currentRow();
261         QListWidgetItem *cur = databaseLW->takeItem(row);
262         databaseLW->insertItem(row + 1, cur);
263         databaseLW->setCurrentItem(cur);
264         changed();
265 }
266
267
268 void GuiBibtex::databaseChanged()
269 {
270         bool readOnly = isBufferReadonly();
271         int count = databaseLW->count();
272         int row = databaseLW->currentRow();
273         deletePB->setEnabled(!readOnly && row != -1);
274         upPB->setEnabled(!readOnly && count > 1 && row > 0);
275         downPB->setEnabled(!readOnly && count > 1 && row < count - 1);
276 }
277
278
279 void GuiBibtex::availableChanged()
280 {
281         add_bc_.setValid(true);
282 }
283
284
285 void GuiBibtex::updateContents()
286 {
287         bool bibtopic = usingBibtopic();
288
289         databaseLW->clear();
290
291         docstring bibs = params_["bibfiles"];
292         docstring bib;
293
294         while (!bibs.empty()) {
295                 bibs = split(bibs, bib, ',');
296                 bib = trim(bib);
297                 if (!bib.empty()) {
298                         QListWidgetItem * db = new QListWidgetItem(toqstr(bib));
299                         db->setFlags(db->flags() | Qt::ItemIsSelectable);
300                         databaseLW->addItem(db);
301                 }
302         }
303
304         add_->bibLW->clear();
305
306         QStringList bibfiles = bibFiles();
307         for (int i = 0; i != bibfiles.count(); ++i)
308                 add_->bibLW->addItem(changeExtension(bibfiles[i], ""));
309
310         QString bibstyle = styleFile();
311
312         bibtocCB->setChecked(bibtotoc() && !bibtopic);
313         bibtocCB->setEnabled(!bibtopic);
314
315         if (!bibtopic && btPrintCO->count() == 3)
316                 btPrintCO->removeItem(1);
317         else if (bibtopic && btPrintCO->count() < 3)
318                 btPrintCO->insertItem(1, qt_("all uncited references", 0));
319
320         docstring btprint = params_["btprint"];
321         int btp = 0;
322         if ((bibtopic && btprint == "btPrintNotCited") ||
323            (!bibtopic && btprint == "btPrintAll"))
324                 btp = 1;
325         else if (bibtopic && btprint == "btPrintAll")
326                 btp = 2;
327
328         btPrintCO->setCurrentIndex(btp);
329
330         styleCB->clear();
331
332         int item_nr = -1;
333
334         QStringList str = bibStyles();
335         for (int i = 0; i != str.count(); ++i) {
336                 QString item = changeExtension(str[i], "");
337                 if (item == bibstyle)
338                         item_nr = i;
339                 styleCB->addItem(item);
340         }
341
342         if (item_nr == -1 && !bibstyle.isEmpty()) {
343                 styleCB->addItem(bibstyle);
344                 item_nr = styleCB->count() - 1;
345         }
346
347         if (item_nr != -1)
348                 styleCB->setCurrentIndex(item_nr);
349         else
350                 styleCB->clearEditText();
351 }
352
353
354 void GuiBibtex::applyView()
355 {
356         docstring dbs;
357
358         unsigned int maxCount = databaseLW->count();
359         for (unsigned int i = 0; i < maxCount; i++) {
360                 if (i != 0)
361                         dbs += ',';
362                 QString item = databaseLW->item(i)->text();
363                 docstring bibfile = qstring_to_ucs4(item);
364                 dbs += bibfile;
365         }
366
367         params_["bibfiles"] = dbs;
368
369         docstring const bibstyle = qstring_to_ucs4(styleCB->currentText());
370         bool const bibtotoc = bibtocCB->isChecked();
371
372         if (bibtotoc && !bibstyle.empty()) {
373                 // both bibtotoc and style
374                 params_["options"] = "bibtotoc," + bibstyle;
375         } else if (bibtotoc) {
376                 // bibtotoc and no style
377                 params_["options"] = from_ascii("bibtotoc");
378         } else {
379                 // only style. An empty one is valid, because some
380                 // documentclasses have an own \bibliographystyle{}
381                 // command!
382                 params_["options"] = bibstyle;
383         }
384
385         int btp = btPrintCO->currentIndex();
386
387         if (usingBibtopic()) {
388                 // bibtopic allows three kinds of sections:
389                 // 1. sections that include all cited references of the database(s)
390                 // 2. sections that include all uncited references of the database(s)
391                 // 3. sections that include all references of the database(s), cited or not
392                 switch (btp) {
393                 case 0:
394                         params_["btprint"] = from_ascii("btPrintCited");
395                         break;
396                 case 1:
397                         params_["btprint"] = from_ascii("btPrintNotCited");
398                         break;
399                 case 2:
400                         params_["btprint"] = from_ascii("btPrintAll");
401                         break;
402                 }
403         } else {
404                 switch (btp) {
405                 case 0:
406                         params_["btprint"] = docstring();
407                         break;
408                 case 1:
409                         // use \nocite{*}
410                         params_["btprint"] = from_ascii("btPrintAll");
411                         break;
412                 }               
413         }
414 }
415
416
417 bool GuiBibtex::isValid()
418 {
419         return databaseLW->count() != 0;
420 }
421
422
423 QString GuiBibtex::browseBib(QString const & in_name) const
424 {
425         QString const label1 = qt_("Documents|#o#O");
426         QString const dir1 = toqstr(lyxrc.document_path);
427         FileFilterList const filter(_("BibTeX Databases (*.bib)"));
428         return browseRelFile(in_name, bufferFilepath(),
429                 qt_("Select a BibTeX database to add"), filter, false, label1, dir1);
430 }
431
432
433 QString GuiBibtex::browseBst(QString const & in_name) const
434 {
435         QString const label1 = qt_("Documents|#o#O");
436         QString const dir1 = toqstr(lyxrc.document_path);
437         FileFilterList const filter(_("BibTeX Styles (*.bst)"));
438         return browseRelFile(in_name, bufferFilepath(),
439                 qt_("Select a BibTeX style"), filter, false, label1, dir1);
440 }
441
442
443 QStringList GuiBibtex::bibStyles() const
444 {
445         QStringList data = texFileList("bstFiles.lst");
446         // test whether we have a valid list, otherwise run rescan
447         if (data.isEmpty()) {
448                 rescanBibStyles();
449                 data = texFileList("bstFiles.lst");
450         }
451         for (int i = 0; i != data.size(); ++i)
452                 data[i] = onlyFilename(data[i]);
453         // sort on filename only (no path)
454         data.sort();
455         return data;
456 }
457
458
459 QStringList GuiBibtex::bibFiles() const
460 {
461         QStringList data = texFileList("bibFiles.lst");
462         // test whether we have a valid list, otherwise run rescan
463         if (data.isEmpty()) {
464                 rescanBibStyles();
465                 data = texFileList("bibFiles.lst");
466         }
467         for (int i = 0; i != data.size(); ++i)
468                 data[i] = onlyFilename(data[i]);
469         // sort on filename only (no path)
470         data.sort();
471         return data;
472 }
473
474
475 void GuiBibtex::rescanBibStyles() const
476 {
477         rescanTexStyles();
478 }
479
480
481 bool GuiBibtex::usingBibtopic() const
482 {
483         return buffer().params().use_bibtopic;
484 }
485
486
487 bool GuiBibtex::bibtotoc() const
488 {
489         return prefixIs(to_utf8(params_["options"]), "bibtotoc");
490 }
491
492
493 QString GuiBibtex::styleFile() const
494 {
495         // the different bibtex packages have (and need) their
496         // own "plain" stylefiles
497         biblio::CiteEngine const engine = buffer().params().citeEngine();
498         QString defaultstyle;
499         switch (engine) {
500         case biblio::ENGINE_BASIC:
501                 defaultstyle = "plain";
502                 break;
503         case biblio::ENGINE_NATBIB_AUTHORYEAR:
504                 defaultstyle = "plainnat";
505                 break;
506         case biblio::ENGINE_NATBIB_NUMERICAL:
507                 defaultstyle = "plainnat";
508                 break;
509         case biblio::ENGINE_JURABIB:
510                 defaultstyle = "jurabib";
511                 break;
512         }
513
514         QString bst = toqstr(params_["options"]);
515         if (bibtotoc()){
516                 // bibstyle exists?
517                 int pos = bst.indexOf(',');
518                 if (pos != -1) {
519                         // FIXME: check
520                         // docstring bibtotoc = from_ascii("bibtotoc");
521                         // bst = split(bst, bibtotoc, ',');
522                         bst = bst.mid(pos);     
523                 } else {
524                         bst.clear();
525                 }
526         }
527
528         // propose default style file for new insets
529         // existing insets might have (legally) no bst files
530         // (if the class already provides a style)
531         if (bst.isEmpty() && params_["bibfiles"].empty())
532                 bst = defaultstyle;
533
534         return bst;
535 }
536
537
538 bool GuiBibtex::initialiseParams(std::string const & data)
539 {
540         InsetCommand::string2params("bibtex", data, params_);
541         return true;
542 }
543
544
545 void GuiBibtex::dispatchParams()
546 {
547         std::string const lfun = InsetCommand::params2string("bibtex", params_);
548         dispatch(FuncRequest(getLfun(), lfun));
549 }
550
551
552
553 Dialog * createGuiBibtex(GuiView & lv) { return new GuiBibtex(lv); }
554
555
556 } // namespace frontend
557 } // namespace lyx
558
559 #include "GuiBibtex_moc.cpp"