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