]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiCitation.cpp
Correctly enable/disable citation dialog widgets on initialization.
[lyx.git] / src / frontends / qt4 / GuiCitation.cpp
1
2 /**
3  * \file GuiCitation.cpp
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Angus Leeming
8  * \author Kalle Dalheimer
9  * \author Abdelrazak Younes
10  * \author Richard Heck
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "GuiCitation.h"
18
19 #include "GuiApplication.h"
20 #include "GuiSelectionManager.h"
21 #include "LyXToolBox.h"
22 #include "qt_helpers.h"
23
24 #include "Buffer.h"
25 #include "BufferView.h"
26 #include "BiblioInfo.h"
27 #include "BufferParams.h"
28 #include "TextClass.h"
29 #include "FuncRequest.h"
30
31 #include "insets/InsetCommand.h"
32
33 #include "support/debug.h"
34 #include "support/docstring.h"
35 #include "support/gettext.h"
36 #include "support/lstrings.h"
37
38 #include <QCloseEvent>
39 #include <QMenu>
40 #include <QSettings>
41 #include <QShowEvent>
42 #include <QStandardItemModel>
43 #include <QVariant>
44
45 #include <vector>
46 #include <string>
47
48 #undef KeyPress
49
50 #include "support/regex.h"
51
52 #include <algorithm>
53 #include <string>
54 #include <vector>
55
56 using namespace std;
57 using namespace lyx::support;
58
59 namespace lyx {
60 namespace frontend {
61
62 // FIXME THREAD
63 // I am guessing that it would not hurt to make these private members.
64 static vector<string> citeCmds_;
65 static vector<CitationStyle> citeStyles_;
66
67
68 template<typename String>
69 static QStringList to_qstring_list(vector<String> const & v)
70 {
71         QStringList qlist;
72
73         for (size_t i = 0; i != v.size(); ++i) {
74                 if (v[i].empty())
75                         continue;
76                 qlist.append(lyx::toqstr(v[i]));
77         }
78         return qlist;
79 }
80
81
82 static vector<lyx::docstring> to_docstring_vector(QStringList const & qlist)
83 {
84         vector<lyx::docstring> v;
85         for (int i = 0; i != qlist.size(); ++i) {
86                 if (qlist[i].isEmpty())
87                         continue;
88                 v.push_back(lyx::qstring_to_ucs4(qlist[i]));
89         }
90         return v;
91 }
92
93
94 GuiCitation::GuiCitation(GuiView & lv)
95         : DialogView(lv, "citation", qt_("Citation")),
96           style_(0), params_(insetCode("citation"))
97 {
98         setupUi(this);
99
100         // The filter bar
101         filter_ = new FancyLineEdit(this);
102         filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
103         filter_->setButtonVisible(FancyLineEdit::Right, true);
104         filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text"));
105         filter_->setAutoHideButton(FancyLineEdit::Right, true);
106         filter_->setPlaceholderText(qt_("All avail. citations"));
107
108         filterBarL->addWidget(filter_, 0);
109         findKeysLA->setBuddy(filter_);
110
111         // Add search options as button menu
112         regexp_ = new QAction(qt_("Regular e&xpression"), this);
113         regexp_->setCheckable(true);
114         casesense_ = new QAction(qt_("Case se&nsitive"), this);
115         casesense_->setCheckable(true);
116         instant_ = new QAction(qt_("Search as you &type"), this);
117         instant_->setCheckable(true);
118         instant_->setChecked(true);
119
120         QMenu * searchOpts = new QMenu(this);
121         searchOpts->addAction(regexp_);
122         searchOpts->addAction(casesense_);
123         searchOpts->addAction(instant_);
124         searchOptionsPB->setMenu(searchOpts);
125
126         connect(citationStyleCO, SIGNAL(activated(int)),
127                 this, SLOT(on_citationStyleCO_currentIndexChanged(int)));
128         connect(starredCB, SIGNAL(clicked()),
129                 this, SLOT(updateStyles()));
130         connect(literalCB, SIGNAL(clicked()),
131                 this, SLOT(changed()));
132         connect(forceuppercaseCB, SIGNAL(clicked()),
133                 this, SLOT(updateStyles()));
134         connect(textBeforeED, SIGNAL(textChanged(QString)),
135                 this, SLOT(updateStyles()));
136         connect(textAfterED, SIGNAL(textChanged(QString)),
137                 this, SLOT(updateStyles()));
138         connect(textBeforeED, SIGNAL(returnPressed()),
139                 this, SLOT(on_okPB_clicked()));
140         connect(textAfterED, SIGNAL(returnPressed()),
141                 this, SLOT(on_okPB_clicked()));
142
143         selectionManager = new GuiSelectionManager(availableLV, selectedLV,
144                         addPB, deletePB, upPB, downPB, &available_model_, &selected_model_, 1);
145         connect(selectionManager, SIGNAL(selectionChanged()),
146                 this, SLOT(setCitedKeys()));
147         connect(selectionManager, SIGNAL(updateHook()),
148                 this, SLOT(updateControls()));
149         connect(selectionManager, SIGNAL(okHook()),
150                 this, SLOT(on_okPB_clicked()));
151
152         connect(filter_, SIGNAL(rightButtonClicked()),
153                 this, SLOT(resetFilter()));
154         connect(filter_, SIGNAL(textEdited(QString)),
155                 this, SLOT(filterChanged(QString)));
156         connect(filter_, SIGNAL(returnPressed()),
157                 this, SLOT(filterPressed()));
158         connect(regexp_, SIGNAL(triggered()),
159                 this, SLOT(regexChanged()));
160         connect(casesense_, SIGNAL(triggered()),
161                 this, SLOT(caseChanged()));
162         connect(instant_, SIGNAL(triggered(bool)),
163                 this, SLOT(instantChanged(bool)));
164
165 #if (QT_VERSION < 0x050000)
166         selectedLV->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
167 #else
168         selectedLV->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
169 #endif
170
171         setFocusProxy(filter_);
172 }
173
174
175 GuiCitation::~GuiCitation()
176 {
177         delete selectionManager;
178 }
179
180
181 void GuiCitation::closeEvent(QCloseEvent * e)
182 {
183         clearSelection();
184         DialogView::closeEvent(e);
185 }
186
187
188 void GuiCitation::applyView()
189 {
190         int const choice = max(0, citationStyleCO->currentIndex());
191         style_ = choice;
192         bool const full  = starredCB->isChecked();
193         bool const force = forceuppercaseCB->isChecked();
194
195         QString const before = textBeforeED->text();
196         QString const after = textAfterED->text();
197
198         applyParams(choice, full, force, before, after);
199 }
200
201
202 void GuiCitation::showEvent(QShowEvent * e)
203 {
204         filter_->clear();
205         availableLV->setFocus();
206         DialogView::showEvent(e);
207 }
208
209
210 void GuiCitation::on_okPB_clicked()
211 {
212         applyView();
213         clearSelection();
214         hide();
215 }
216
217
218 void GuiCitation::on_cancelPB_clicked()
219 {
220         clearSelection();
221         hide();
222 }
223
224
225 void GuiCitation::on_applyPB_clicked()
226 {
227         applyView();
228 }
229
230
231 void GuiCitation::on_restorePB_clicked()
232 {
233         init();
234         updateFilterHint();
235 }
236
237
238 void GuiCitation::updateControls()
239 {
240         BiblioInfo const & bi = bibInfo();
241         updateControls(bi);
242 }
243
244
245 // The main point of separating this out is that the fill*() methods
246 // called in update() do not need to be called for INTERNAL updates,
247 // such as when addPB is pressed, as the list of fields, entries, etc,
248 // will not have changed.
249 void GuiCitation::updateControls(BiblioInfo const & bi)
250 {
251         QModelIndex idx = selectionManager->getSelectedIndex(1);
252         updateInfo(bi, idx);
253         int i = citationStyleCO->currentIndex();
254         if (i == -1)
255                 i = 0;
256         updateFormatting(citeStyles_[i]);
257         selectionManager->update();
258 }
259
260
261 void GuiCitation::updateFormatting(CitationStyle currentStyle)
262 {
263         BufferParams const bp = documentBuffer().params();
264         bool const force = currentStyle.forceUpperCase;
265         bool const starred = currentStyle.hasStarredVersion;
266         bool const full = starred && bp.fullAuthorList();
267         bool const textbefore = currentStyle.textBefore;
268         bool const textafter = currentStyle.textAfter;
269
270         int const rows = selectedLV->model()->rowCount();
271
272         bool const qualified = currentStyle.hasQualifiedList
273                 && (rows > 1
274                     || !params_["pretextlist"].empty()
275                     || !params_["posttextlist"].empty());
276         selectedLV->horizontalHeader()->setVisible(qualified);
277         selectedLV->setColumnHidden(0, !qualified);
278         selectedLV->setColumnHidden(2, !qualified);
279         if (qualified) {
280                 textBeforeLA->setText(qt_("General text befo&re:"));
281                 textAfterLA->setText(qt_("General &text after:"));
282                 textBeforeED->setToolTip(qt_("Text that precedes the whole reference list. "
283                                              "For text that precedes individual items, double-click on the respective entry above."));
284                 textAfterLA->setToolTip(qt_("General &text after:"));
285                 textAfterED->setToolTip(qt_("Text that follows the whole reference list. "
286                                              "For text that follows individual items, double-click on the respective entry above."));
287         } else {
288                 textBeforeLA->setText(qt_("Text befo&re:"));
289                 textBeforeED->setToolTip(qt_("Text that precedes the reference (e.g., \"cf.\")"));
290                 textAfterLA->setText(qt_("&Text after:"));
291                 textAfterED->setToolTip(qt_("Text that follows the reference (e.g., pages)"));
292         }
293
294         bool const haveSelection = rows > 0;
295
296         forceuppercaseCB->setEnabled(force && haveSelection);
297         starredCB->setEnabled(full && haveSelection);
298         textBeforeED->setEnabled(textbefore && haveSelection);
299         textBeforeLA->setEnabled(textbefore && haveSelection);
300         textAfterED->setEnabled(textafter && haveSelection);
301         textAfterLA->setEnabled(textafter && haveSelection);
302         citationStyleCO->setEnabled(haveSelection);
303         citationStyleLA->setEnabled(haveSelection);
304
305         // Check if we have a custom string/tooltip for the starred version
306         if (starred && !currentStyle.stardesc.empty()) {
307                 string val =
308                         bp.documentClass().getCiteMacro(bp.citeEngineType(), currentStyle.stardesc);
309                 docstring guistring;
310                 if (!val.empty()) {
311                         guistring = translateIfPossible(from_utf8(val));
312                         starredCB->setText(toqstr(guistring));
313                         starredCB->setEnabled(haveSelection);
314                 }
315                 if (!currentStyle.startooltip.empty()) {
316                         val = bp.documentClass().getCiteMacro(bp.citeEngineType(),
317                                                               currentStyle.startooltip);
318                         if (!val.empty())
319                                 guistring = translateIfPossible(from_utf8(val));
320                 }
321                 // Tooltip might also be empty
322                 starredCB->setToolTip(toqstr(guistring));
323         } else {
324                 // This is the default meaning of the starred commands
325                 starredCB->setText(qt_("All aut&hors"));
326                 starredCB->setToolTip(qt_("Always list all authors (rather than using \"et al.\")"));
327         }
328 }
329
330
331 // Update the styles for the style combo, citationStyleCO, and mark the
332 // settings as changed. Called upon changing the cited keys (including
333 // merely reordering the keys) or editing the text before/after fields.
334 void GuiCitation::updateStyles()
335 {
336         BiblioInfo const & bi = bibInfo();
337         updateStyles(bi);
338         changed();
339 }
340
341
342 // Update the styles for the style combo, citationStyleCO.
343 void GuiCitation::updateStyles(BiblioInfo const & bi)
344 {
345         QStringList selected_keys = selectedKeys();
346         int curr = selectedLV->model()->rowCount() - 1;
347
348         if (curr < 0 || selected_keys.empty()) {
349                 citationStyleCO->clear();
350                 citationStyleCO->setEnabled(false);
351                 citationStyleLA->setEnabled(false);
352                 return;
353         }
354
355         static const size_t max_length = 80;
356         QStringList sty = citationStyles(bi, max_length);
357
358         if (sty.isEmpty()) {
359                 // some error
360                 citationStyleCO->setEnabled(false);
361                 citationStyleLA->setEnabled(false);
362                 citationStyleCO->clear();
363                 return;
364         }
365
366         citationStyleCO->blockSignals(true);
367
368         // save old index
369         int const curindex = citationStyleCO->currentIndex();
370         int const oldIndex = (curindex < 0) ? style_ : curindex;
371         citationStyleCO->clear();
372         citationStyleCO->insertItems(0, sty);
373         citationStyleCO->setEnabled(true);
374         citationStyleLA->setEnabled(true);
375         // restore old index
376         if (oldIndex != -1 && oldIndex < citationStyleCO->count())
377                 citationStyleCO->setCurrentIndex(oldIndex);
378
379         citationStyleCO->blockSignals(false);
380 }
381
382
383 void GuiCitation::fillFields(BiblioInfo const & bi)
384 {
385         fieldsCO->blockSignals(true);
386         int const oldIndex = fieldsCO->currentIndex();
387         fieldsCO->clear();
388         QStringList const fields = to_qstring_list(bi.getFields());
389         fieldsCO->insertItem(0, qt_("All fields"));
390         fieldsCO->insertItem(1, qt_("Keys"));
391         fieldsCO->insertItems(2, fields);
392         if (oldIndex != -1 && oldIndex < fieldsCO->count())
393                 fieldsCO->setCurrentIndex(oldIndex);
394         fieldsCO->blockSignals(false);
395 }
396
397
398 void GuiCitation::fillEntries(BiblioInfo const & bi)
399 {
400         entriesCO->blockSignals(true);
401         int const oldIndex = entriesCO->currentIndex();
402         entriesCO->clear();
403         QStringList const entries = to_qstring_list(bi.getEntries());
404         entriesCO->insertItem(0, qt_("All entry types"));
405         entriesCO->insertItems(1, entries);
406         if (oldIndex != -1 && oldIndex < entriesCO->count())
407                 entriesCO->setCurrentIndex(oldIndex);
408         entriesCO->blockSignals(false);
409 }
410
411
412 bool GuiCitation::isSelected(QModelIndex const & idx)
413 {
414         QString const str = idx.data().toString();
415         return selectedKeys().contains(str);
416 }
417
418
419 void GuiCitation::setButtons()
420 {
421         int const srows = selectedLV->model()->rowCount();
422         applyPB->setEnabled(srows > 0);
423         okPB->setEnabled(srows > 0);
424 }
425
426
427 void GuiCitation::updateInfo(BiblioInfo const & bi, QModelIndex const & idx)
428 {
429         if (!idx.isValid() || bi.empty()) {
430                 infoML->document()->clear();
431                 infoML->setToolTip(qt_("Displays a sketchy preview if a citation is selected above"));
432                 return;
433         }
434
435         infoML->setToolTip(qt_("Sketchy preview of the selected citation"));
436         CiteItem ci;
437         ci.richtext = true;
438         QString const keytxt = toqstr(
439                 bi.getInfo(qstring_to_ucs4(idx.data().toString()), documentBuffer(), ci));
440         infoML->document()->setHtml(keytxt);
441 }
442
443
444 void GuiCitation::findText(QString const & text, bool reset)
445 {
446         //"All Fields" and "Keys" are the first two
447         int index = fieldsCO->currentIndex() - 2;
448         BiblioInfo const & bi = bibInfo();
449         vector<docstring> const & fields = bi.getFields();
450         docstring field;
451
452         if (index <= -1 || index >= int(fields.size()))
453                 //either "All Fields" or "Keys" or an invalid value
454                 field = from_ascii("");
455         else
456                 field = fields[index];
457
458         //Was it "Keys"?
459         bool const onlyKeys = index == -1;
460
461         //"All Entry Types" is first.
462         index = entriesCO->currentIndex() - 1;
463         vector<docstring> const & entries = bi.getEntries();
464         docstring entry_type;
465         if (index < 0 || index >= int(entries.size()))
466                 entry_type = from_ascii("");
467         else
468                 entry_type = entries[index];
469
470         bool const case_sentitive = casesense_->isChecked();
471         bool const reg_exp = regexp_->isChecked();
472
473         findKey(bi, text, onlyKeys, field, entry_type,
474                        case_sentitive, reg_exp, reset);
475         //FIXME
476         //It'd be nice to save and restore the current selection in
477         //availableLV. Currently, we get an automatic reset, since the
478         //model is reset.
479
480         updateControls(bi);
481 }
482
483
484 void GuiCitation::on_fieldsCO_currentIndexChanged(int /*index*/)
485 {
486         findText(filter_->text(), true);
487 }
488
489
490 void GuiCitation::on_entriesCO_currentIndexChanged(int /*index*/)
491 {
492         findText(filter_->text(), true);
493 }
494
495
496 void GuiCitation::on_citationStyleCO_currentIndexChanged(int index)
497 {
498         if (index >= 0 && index < citationStyleCO->count()) {
499                 vector<CitationStyle> const & styles = citeStyles_;
500                 updateFormatting(styles[index]);
501                 changed();
502         }
503 }
504
505
506 void GuiCitation::filterChanged(const QString & text)
507 {
508         if (!text.isEmpty()) {
509                 if (instant_->isChecked())
510                         findText(filter_->text());
511                 return;
512         }
513         findText(filter_->text());
514         filter_->setFocus();
515 }
516
517
518 void GuiCitation::filterPressed()
519 {
520         findText(filter_->text(), true);
521 }
522
523
524 void GuiCitation::resetFilter()
525 {
526         filter_->setText(QString());
527         findText(filter_->text(), true);
528 }
529
530
531 void GuiCitation::caseChanged()
532 {
533         findText(filter_->text());
534 }
535
536
537 void GuiCitation::regexChanged()
538 {
539         findText(filter_->text());
540 }
541
542
543 void GuiCitation::updateFilterHint()
544 {
545         QString const hint = instant_->isChecked() ?
546                 qt_("Enter string to filter the list of available citations") :
547                 qt_("Enter string to filter the list of available citations and press <Enter>");
548         filter_->setToolTip(hint);
549 }
550
551
552 void GuiCitation::instantChanged(bool checked)
553 {
554         if (checked)
555                 findText(filter_->text(), true);
556
557         updateFilterHint();
558 }
559
560
561 void GuiCitation::changed()
562 {
563         setButtons();
564 }
565
566
567 void GuiCitation::applyParams(int const choice, bool full, bool force,
568         QString before, QString after)
569 {
570         if (cited_keys_.isEmpty())
571                 return;
572
573         vector<CitationStyle> const & styles = citeStyles_;
574
575         CitationStyle cs = styles[choice];
576
577         if (!cs.textBefore)
578                 before.clear();
579         if (!cs.textAfter)
580                 after.clear();
581
582         cs.forceUpperCase &= force;
583         cs.hasStarredVersion &= full;
584         string const command = citationStyleToString(cs);
585
586         params_.setCmdName(command);
587         params_["key"] = qstring_to_ucs4(cited_keys_.join(","));
588         params_["before"] = qstring_to_ucs4(before);
589         params_["after"] = qstring_to_ucs4(after);
590         if (cs.hasQualifiedList) {
591                 params_["pretextlist"] = getStringFromVector(getPreTexts(), from_ascii("\t"));
592                 params_["posttextlist"] = getStringFromVector(getPostTexts(), from_ascii("\t"));
593         }
594         params_["literal"] = literalCB->isChecked() ? from_ascii("true") : from_ascii("false");
595         dispatchParams();
596 }
597
598
599 void GuiCitation::clearSelection()
600 {
601         cited_keys_.clear();
602         setSelectedKeys(cited_keys_);
603 }
604
605
606 void GuiCitation::setSelectedKeys(QStringList const sl)
607 {
608         selected_model_.clear();
609         selected_model_.setColumnCount(3);
610         QStringList headers;
611         headers << qt_("Text before")
612                 << qt_("Cite key")
613                 << qt_("Text after");
614         selected_model_.setHorizontalHeaderLabels(headers);
615         selectedLV->setColumnHidden(0, true);
616         selectedLV->setColumnHidden(2, true);
617         selectedLV->verticalHeader()->setVisible(false);
618         selectedLV->horizontalHeader()->setVisible(false);
619         QStringList::const_iterator it  = sl.begin();
620         QStringList::const_iterator end = sl.end();
621         for (int i = 0; it != end; ++it, ++i) {
622                 QStandardItem * si = new QStandardItem();
623                 si->setData(*it);
624                 si->setText(*it);
625                 si->setToolTip(*it);
626                 si->setEditable(false);
627                 selected_model_.setItem(i, 1, si);
628         }
629 }
630
631
632 QStringList GuiCitation::selectedKeys()
633 {
634         QStringList res;
635         for (int i = 0; i != selected_model_.rowCount(); ++i) {
636                 QStandardItem const * item = selected_model_.item(i, 1);
637                 if (item)
638                         res.append(item->text());
639         }
640         return res;
641 }
642
643
644 void GuiCitation::setPreTexts(vector<docstring> const m)
645 {
646         for (docstring const & s: m) {
647                 QStandardItem * si = new QStandardItem();
648                 docstring key;
649                 docstring pre = split(s, key, ' ');
650                 si->setData(toqstr(pre));
651                 si->setText(toqstr(pre));
652                 QModelIndexList qmil =
653                                 selected_model_.match(selected_model_.index(0, 1),
654                                                      Qt::DisplayRole, toqstr(key), 1,
655                                                      Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap));
656                 if (!qmil.empty())
657                         selected_model_.setItem(qmil.front().row(), 0, si);
658         }
659 }
660
661
662 vector<docstring> GuiCitation::getPreTexts()
663 {
664         vector<docstring> res;
665         for (int i = 0; i != selected_model_.rowCount(); ++i) {
666                 QStandardItem const * key = selected_model_.item(i, 1);
667                 QStandardItem const * pre = selected_model_.item(i, 0);
668                 if (key && pre && !key->text().isEmpty() && !pre->text().isEmpty())
669                         res.push_back(qstring_to_ucs4(key->text()) + " " + qstring_to_ucs4(pre->text()));
670         }
671         return res;
672 }
673
674
675 void GuiCitation::setPostTexts(vector<docstring> const m)
676 {
677         for (docstring const & s: m) {
678                 QStandardItem * si = new QStandardItem();
679                 docstring key;
680                 docstring post = split(s, key, ' ');
681                 si->setData(toqstr(post));
682                 si->setText(toqstr(post));
683                 QModelIndexList qmil =
684                                 selected_model_.match(selected_model_.index(0, 1),
685                                                      Qt::DisplayRole, toqstr(key), 1,
686                                                      Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap));
687                 if (!qmil.empty())
688                         selected_model_.setItem(qmil.front().row(), 2, si);
689         }
690 }
691
692
693 vector<docstring> GuiCitation::getPostTexts()
694 {
695         vector<docstring> res;
696         for (int i = 0; i != selected_model_.rowCount(); ++i) {
697                 QStandardItem const * key = selected_model_.item(i, 1);
698                 QStandardItem const * post = selected_model_.item(i, 2);
699                 if (key && post)
700                         res.push_back(qstring_to_ucs4(key->text()) + " " + qstring_to_ucs4(post->text()));
701         }
702         return res;
703 }
704
705
706 void GuiCitation::init()
707 {
708         // Make the list of all available bibliography keys
709         BiblioInfo const & bi = bibInfo();
710         all_keys_ = to_qstring_list(bi.getKeys());
711
712         available_model_.setStringList(all_keys_);
713
714         // Ditto for the keys cited in this inset
715         QString str = toqstr(params_["key"]);
716         if (str.isEmpty())
717                 cited_keys_.clear();
718         else
719                 cited_keys_ = str.split(",");
720         setSelectedKeys(cited_keys_);
721
722         // Initialize the drop downs
723         fillEntries(bi);
724         fillFields(bi);
725
726         // Initialize the citation formatting
727         string const & cmd = params_.getCmdName();
728         CitationStyle const cs =
729                 citationStyleFromString(cmd, documentBuffer().params());
730
731         forceuppercaseCB->setChecked(cs.forceUpperCase);
732         starredCB->setChecked(cs.hasStarredVersion &&
733                 documentBuffer().params().fullAuthorList());
734         textBeforeED->setText(toqstr(params_["before"]));
735         textAfterED->setText(toqstr(params_["after"]));
736         literalCB->setChecked(params_["literal"] == "true");
737
738         setPreTexts(getVectorFromString(params_["pretextlist"], from_ascii("\t")));
739         setPostTexts(getVectorFromString(params_["posttextlist"], from_ascii("\t")));
740
741         // Update the interface
742         updateControls(bi);
743         updateStyles(bi);
744         if (selected_model_.rowCount()) {
745                 selectedLV->blockSignals(true);
746                 selectedLV->setFocus();
747                 selectedLV->selectRow(0);
748                 selectedLV->blockSignals(false);
749
750                 // Find the citation style
751                 vector<string> const & cmds = citeCmds_;
752                 vector<string>::const_iterator cit =
753                         std::find(cmds.begin(), cmds.end(), cs.name);
754                 int i = 0;
755                 if (cit != cmds.end())
756                         i = int(cit - cmds.begin());
757
758                 // Set the style combo appropriately
759                 citationStyleCO->blockSignals(true);
760                 citationStyleCO->setCurrentIndex(i);
761                 citationStyleCO->blockSignals(false);
762                 updateFormatting(citeStyles_[i]);
763         } else
764                 availableLV->setFocus();
765
766         applyPB->setEnabled(false);
767         okPB->setEnabled(false);
768 }
769
770
771 void GuiCitation::findKey(BiblioInfo const & bi,
772         QString const & str, bool only_keys,
773         docstring field, docstring entry_type,
774         bool case_sensitive, bool reg_exp, bool reset)
775 {
776         // FIXME THREAD
777         // This should be moved to a class member.
778         // Used for optimisation: store last searched string.
779         static QString last_searched_string;
780         // Used to disable the above optimisation.
781         static bool last_case_sensitive;
782         static bool last_reg_exp;
783         // Reset last_searched_string in case of changed option.
784         if (last_case_sensitive != case_sensitive
785                 || last_reg_exp != reg_exp) {
786                         LYXERR(Debug::GUI, "GuiCitation::findKey: optimisation disabled!");
787                 last_searched_string.clear();
788         }
789         // save option for next search.
790         last_case_sensitive = case_sensitive;
791         last_reg_exp = reg_exp;
792
793         Qt::CaseSensitivity qtcase = case_sensitive ?
794                         Qt::CaseSensitive: Qt::CaseInsensitive;
795         QStringList keys;
796         // If new string (str) contains the last searched one...
797         if (!reset &&
798                 !last_searched_string.isEmpty() &&
799                 str.size() > 1 &&
800                 str.contains(last_searched_string, qtcase))
801                 // ... then only search within already found list.
802                 keys = available_model_.stringList();
803         else
804                 // ... else search all keys.
805                 keys = all_keys_;
806         // save searched string for next search.
807         last_searched_string = str;
808
809         QStringList result;
810
811         // First, filter by entry_type, which will be faster than
812         // what follows, so we may get to do that on less.
813         vector<docstring> keyVector = to_docstring_vector(keys);
814         filterByEntryType(bi, keyVector, entry_type);
815
816         if (str.isEmpty())
817                 result = to_qstring_list(keyVector);
818         else
819                 result = to_qstring_list(searchKeys(bi, keyVector, only_keys,
820                         qstring_to_ucs4(str), field, case_sensitive, reg_exp));
821
822         available_model_.setStringList(result);
823 }
824
825
826 QStringList GuiCitation::citationStyles(BiblioInfo const & bi, size_t max_size)
827 {
828         vector<docstring> const keys = to_docstring_vector(cited_keys_);
829         vector<CitationStyle> styles = citeStyles_;
830         int ind = citationStyleCO->currentIndex();
831         if (ind == -1)
832                 ind = 0;
833         CitationStyle cs = styles[ind];
834         vector<docstring> pretexts = getPreTexts();
835         vector<docstring> posttexts = getPostTexts();
836         bool const qualified = cs.hasQualifiedList
837                 && (selectedLV->model()->rowCount() > 1
838                     || !pretexts.empty()
839                     || !posttexts.empty());
840         std::map<docstring, docstring> pres;
841         for (docstring const & s: pretexts) {
842                 docstring key;
843                 docstring val = split(s, key, ' ');
844                 pres[key] = val;
845         }
846         std::map<docstring, docstring> posts;
847         for (docstring const & s: posttexts) {
848                 docstring key;
849                 docstring val = split(s, key, ' ');
850                 posts[key] = val;
851         }
852         CiteItem ci;
853         ci.textBefore = qstring_to_ucs4(textBeforeED->text());
854         ci.textAfter = qstring_to_ucs4(textAfterED->text());
855         ci.forceUpperCase = forceuppercaseCB->isChecked();
856         ci.Starred = starredCB->isChecked();
857         ci.context = CiteItem::Dialog;
858         ci.max_size = max_size;
859         ci.isQualified = qualified;
860         ci.pretexts = pres;
861         ci.posttexts = posts;
862         vector<docstring> ret = bi.getCiteStrings(keys, styles, documentBuffer(), ci);
863         return to_qstring_list(ret);
864 }
865
866
867 void GuiCitation::setCitedKeys()
868 {
869         cited_keys_ = selectedKeys();
870         updateStyles();
871 }
872
873
874 bool GuiCitation::initialiseParams(string const & data)
875 {
876         InsetCommand::string2params(data, params_);
877         citeCmds_ = documentBuffer().params().citeCommands();
878         citeStyles_ = documentBuffer().params().citeStyles();
879         init();
880         return true;
881 }
882
883
884 void GuiCitation::clearParams()
885 {
886         params_.clear();
887 }
888
889
890 void GuiCitation::filterByEntryType(BiblioInfo const & bi,
891         vector<docstring> & keyVector, docstring entry_type)
892 {
893         if (entry_type.empty())
894                 return;
895
896         vector<docstring>::iterator it = keyVector.begin();
897         vector<docstring>::iterator end = keyVector.end();
898
899         vector<docstring> result;
900         for (; it != end; ++it) {
901                 docstring const key = *it;
902                 BiblioInfo::const_iterator cit = bi.find(key);
903                 if (cit == bi.end())
904                         continue;
905                 if (cit->second.entryType() == entry_type)
906                         result.push_back(key);
907         }
908         keyVector = result;
909 }
910
911
912 // Escape special chars.
913 // All characters are literals except: '.|*?+(){}[]^$\'
914 // These characters are literals when preceded by a "\", which is done here
915 // @todo: This function should be moved to support, and then the test in tests
916 //        should be moved there as well.
917 static docstring escape_special_chars(docstring const & expr)
918 {
919         // Search for all chars '.|*?+(){}[^$]\'
920         // Note that '[', ']', and '\' must be escaped.
921         static const lyx::regex reg("[.|*?+(){}^$\\[\\]\\\\]");
922
923         // $& is an ECMAScript format expression that expands to all
924         // of the current match
925 #ifdef LYX_USE_STD_REGEX
926         // To prefix a matched expression with a single literal backslash, we
927         // need to escape it for the C++ compiler and use:
928         // FIXME: UNICODE
929         return from_utf8(lyx::regex_replace(to_utf8(expr), reg, string("\\$&")));
930 #else
931         // A backslash in the format string starts an escape sequence in boost.
932         // Thus, to prefix a matched expression with a single literal backslash,
933         // we need to give two backslashes to the regex engine, and escape both
934         // for the C++ compiler and use:
935         // FIXME: UNICODE
936         return from_utf8(lyx::regex_replace(to_utf8(expr), reg, string("\\\\$&")));
937 #endif
938 }
939
940
941 vector<docstring> GuiCitation::searchKeys(BiblioInfo const & bi,
942         vector<docstring> const & keys_to_search, bool only_keys,
943         docstring const & search_expression, docstring field,
944         bool case_sensitive, bool regex)
945 {
946         vector<docstring> foundKeys;
947
948         docstring expr = trim(search_expression);
949         if (expr.empty())
950                 return foundKeys;
951
952         if (!regex)
953                 // We must escape special chars in the search_expr so that
954                 // it is treated as a simple string by lyx::regex.
955                 expr = escape_special_chars(expr);
956
957         lyx::regex reg_exp;
958         try {
959                 reg_exp.assign(to_utf8(expr), case_sensitive ?
960                         lyx::regex_constants::ECMAScript : lyx::regex_constants::icase);
961         } catch (lyx::regex_error const & e) {
962                 // lyx::regex throws an exception if the regular expression is not
963                 // valid.
964                 LYXERR(Debug::GUI, e.what());
965                 return vector<docstring>();
966         }
967
968         vector<docstring>::const_iterator it = keys_to_search.begin();
969         vector<docstring>::const_iterator end = keys_to_search.end();
970         for (; it != end; ++it ) {
971                 BiblioInfo::const_iterator info = bi.find(*it);
972                 if (info == bi.end())
973                         continue;
974
975                 BibTeXInfo const & kvm = info->second;
976                 string data;
977                 if (only_keys)
978                         data = to_utf8(*it);
979                 else if (field.empty())
980                         data = to_utf8(*it) + ' ' + to_utf8(kvm.allData());
981                 else
982                         data = to_utf8(kvm[field]);
983
984                 if (data.empty())
985                         continue;
986
987                 try {
988                         if (lyx::regex_search(data, reg_exp))
989                                 foundKeys.push_back(*it);
990                 }
991                 catch (lyx::regex_error const & e) {
992                         LYXERR(Debug::GUI, e.what());
993                         return vector<docstring>();
994                 }
995         }
996         return foundKeys;
997 }
998
999
1000 void GuiCitation::dispatchParams()
1001 {
1002         std::string const lfun = InsetCommand::params2string(params_);
1003         dispatch(FuncRequest(getLfun(), lfun));
1004 }
1005
1006
1007 BiblioInfo const & GuiCitation::bibInfo() const
1008 {
1009         Buffer const & buf = documentBuffer();
1010         buf.reloadBibInfoCache();
1011         return buf.masterBibInfo();
1012 }
1013
1014
1015 void GuiCitation::saveSession() const
1016 {
1017         Dialog::saveSession();
1018         QSettings settings;
1019         settings.setValue(
1020                 sessionKey() + "/regex", regexp_->isChecked());
1021         settings.setValue(
1022                 sessionKey() + "/casesensitive", casesense_->isChecked());
1023         settings.setValue(
1024                 sessionKey() + "/autofind", instant_->isChecked());
1025         settings.setValue(
1026                 sessionKey() + "/citestyle", style_);
1027 }
1028
1029
1030 void GuiCitation::restoreSession()
1031 {
1032         Dialog::restoreSession();
1033         QSettings settings;
1034         regexp_->setChecked(settings.value(sessionKey() + "/regex").toBool());
1035         casesense_->setChecked(settings.value(sessionKey() + "/casesensitive").toBool());
1036         instant_->setChecked(settings.value(sessionKey() + "/autofind").toBool());
1037         style_ = settings.value(sessionKey() + "/citestyle").toInt();
1038         updateFilterHint();
1039 }
1040
1041
1042 Dialog * createGuiCitation(GuiView & lv) { return new GuiCitation(lv); }
1043
1044
1045 } // namespace frontend
1046 } // namespace lyx
1047
1048 #include "moc_GuiCitation.cpp"
1049