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