X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FGuiCitation.cpp;h=5e35afca793dd75001c4aaa08f5f10c476b18493;hb=f973855bdea94f0c6edf8eace7548a440f005902;hp=c54a5f2128cc3eb1a4802f314eaed7923739d46f;hpb=559da350f085f0fea26d704fb33483f43bede909;p=lyx.git diff --git a/src/frontends/qt4/GuiCitation.cpp b/src/frontends/qt4/GuiCitation.cpp index c54a5f2128..5e35afca79 100644 --- a/src/frontends/qt4/GuiCitation.cpp +++ b/src/frontends/qt4/GuiCitation.cpp @@ -16,14 +16,18 @@ #include "GuiCitation.h" +#include "GuiApplication.h" #include "GuiSelectionManager.h" #include "qt_helpers.h" #include "Buffer.h" +#include "BufferView.h" #include "BiblioInfo.h" #include "BufferParams.h" +#include "TextClass.h" #include "FuncRequest.h" +#include "insets/InsetCitation.h" #include "insets/InsetCommand.h" #include "support/debug.h" @@ -32,8 +36,10 @@ #include "support/lstrings.h" #include +#include #include #include +#include #include #include @@ -41,7 +47,7 @@ #undef KeyPress -#include +#include "support/regex.h" #include #include @@ -53,7 +59,10 @@ using namespace lyx::support; namespace lyx { namespace frontend { -static vector citeStyles_; +// FIXME THREAD +// I am guessing that it would not hurt to make these private members. +static vector citeCmds_; +static vector citeStyles_; template @@ -84,44 +93,91 @@ static vector to_docstring_vector(QStringList const & qlist) GuiCitation::GuiCitation(GuiView & lv) : DialogView(lv, "citation", qt_("Citation")), - params_(insetCode("citation")) + style_(QString()), params_(insetCode("citation")) { setupUi(this); + // The filter bar + filter_ = new FancyLineEdit(this); + filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png")); + filter_->setButtonVisible(FancyLineEdit::Right, true); + filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text")); + filter_->setAutoHideButton(FancyLineEdit::Right, true); + filter_->setPlaceholderText(qt_("All avail. citations")); + + filterBarL->addWidget(filter_, 0); + findKeysLA->setBuddy(filter_); + + // Add search options as button menu + regexp_ = new QAction(qt_("Regular e&xpression"), this); + regexp_->setCheckable(true); + casesense_ = new QAction(qt_("Case se&nsitive"), this); + casesense_->setCheckable(true); + instant_ = new QAction(qt_("Search as you &type"), this); + instant_->setCheckable(true); + instant_->setChecked(true); + + QMenu * searchOpts = new QMenu(this); + searchOpts->addAction(regexp_); + searchOpts->addAction(casesense_); + searchOpts->addAction(instant_); + searchOptionsPB->setMenu(searchOpts); + connect(citationStyleCO, SIGNAL(activated(int)), this, SLOT(on_citationStyleCO_currentIndexChanged(int))); - connect(fulllistCB, SIGNAL(clicked()), + connect(starredCB, SIGNAL(clicked()), + this, SLOT(updateStyles())); + connect(literalCB, SIGNAL(clicked()), this, SLOT(changed())); connect(forceuppercaseCB, SIGNAL(clicked()), - this, SLOT(changed())); + this, SLOT(updateStyles())); connect(textBeforeED, SIGNAL(textChanged(QString)), - this, SLOT(changed())); + this, SLOT(updateStyles())); connect(textAfterED, SIGNAL(textChanged(QString)), - this, SLOT(changed())); - connect(findLE, SIGNAL(returnPressed()), - this, SLOT(on_searchPB_clicked())); + this, SLOT(updateStyles())); connect(textBeforeED, SIGNAL(returnPressed()), - this, SLOT(on_okPB_clicked())); + this, SLOT(on_buttonBox_accepted())); connect(textAfterED, SIGNAL(returnPressed()), - this, SLOT(on_okPB_clicked())); + this, SLOT(on_buttonBox_accepted())); - selectionManager = new GuiSelectionManager(availableLV, selectedLV, - addPB, deletePB, upPB, downPB, &available_model_, &selected_model_); + selectionManager = new GuiSelectionManager(this, availableLV, selectedLV, + addPB, deletePB, upPB, downPB, &available_model_, &selected_model_, 1); connect(selectionManager, SIGNAL(selectionChanged()), this, SLOT(setCitedKeys())); connect(selectionManager, SIGNAL(updateHook()), this, SLOT(updateControls())); connect(selectionManager, SIGNAL(okHook()), - this, SLOT(on_okPB_clicked())); - - // FIXME: the sizeHint() for this is _way_ too high - infoML->setFixedHeight(60); -} - - -GuiCitation::~GuiCitation() -{ - delete selectionManager; + this, SLOT(on_buttonBox_accepted())); + + connect(filter_, SIGNAL(rightButtonClicked()), + this, SLOT(resetFilter())); + connect(filter_, SIGNAL(textEdited(QString)), + this, SLOT(filterChanged(QString))); + connect(filter_, SIGNAL(returnPressed()), + this, SLOT(filterPressed())); +#if (QT_VERSION < 0x050000) + connect(filter_, SIGNAL(downPressed()), + availableLV, SLOT(setFocus())); +#else + connect(filter_, &FancyLineEdit::downPressed, + availableLV, [=](){ focusAndHighlight(availableLV); }); +#endif + connect(regexp_, SIGNAL(triggered()), + this, SLOT(regexChanged())); + connect(casesense_, SIGNAL(triggered()), + this, SLOT(caseChanged())); + connect(instant_, SIGNAL(triggered(bool)), + this, SLOT(instantChanged(bool))); + +#if (QT_VERSION < 0x050000) + selectedLV->horizontalHeader()->setResizeMode(QHeaderView::Stretch); +#else + selectedLV->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); +#endif + + selectedLV->setToolTip(qt_("Ordered list of all cited references.\n" + "You can reorder, add and remove references with the buttons on the left.")); + setFocusProxy(filter_); } @@ -135,26 +191,27 @@ void GuiCitation::closeEvent(QCloseEvent * e) void GuiCitation::applyView() { int const choice = max(0, citationStyleCO->currentIndex()); - style_ = choice; - bool const full = fulllistCB->isChecked(); + style_ = citationStyleCO->itemData(citationStyleCO->currentIndex()).toString(); + bool const full = starredCB->isChecked(); bool const force = forceuppercaseCB->isChecked(); QString const before = textBeforeED->text(); QString const after = textAfterED->text(); - apply(choice, full, force, before, after); + applyParams(choice, full, force, before, after); } void GuiCitation::showEvent(QShowEvent * e) { - findLE->clear(); + if (!filter_->text().isEmpty()) + filterPressed(); availableLV->setFocus(); DialogView::showEvent(e); } -void GuiCitation::on_okPB_clicked() +void GuiCitation::on_buttonBox_accepted() { applyView(); clearSelection(); @@ -162,22 +219,34 @@ void GuiCitation::on_okPB_clicked() } -void GuiCitation::on_cancelPB_clicked() +void GuiCitation::on_buttonBox_rejected() { clearSelection(); hide(); } -void GuiCitation::on_applyPB_clicked() +void GuiCitation::on_buttonBox_clicked(QAbstractButton * button) { - applyView(); + switch (buttonBox->standardButton(button)) { + case QDialogButtonBox::Apply: + applyView(); + break; + case QDialogButtonBox::Reset: + init(); + updateFilterHint(); + filterPressed(); + break; + default: + break; + } } -void GuiCitation::on_restorePB_clicked() +void GuiCitation::on_literalCB_clicked() { - init(); + InsetCitation::last_literal = literalCB->isChecked(); + changed(); } @@ -191,88 +260,129 @@ void GuiCitation::updateControls() // The main point of separating this out is that the fill*() methods // called in update() do not need to be called for INTERNAL updates, // such as when addPB is pressed, as the list of fields, entries, etc, -// will not have changed. At the moment, however, the division between -// fillStyles() and updateStyle() doesn't lend itself to dividing the -// two methods, though they should be divisible. +// will not have changed. void GuiCitation::updateControls(BiblioInfo const & bi) { - QModelIndex idx = selectionManager->getSelectedIndex(); + QModelIndex idx = selectionManager->getSelectedIndex(1); updateInfo(bi, idx); - setButtons(); - - textBeforeED->setText(toqstr(params_["before"])); - textAfterED->setText(toqstr(params_["after"])); - fillStyles(bi); - updateStyle(); + int i = citationStyleCO->currentIndex(); + if (i == -1 || i > int(citeStyles_.size())) + i = 0; + updateFormatting(citeStyles_[i]); + selectionManager->update(); } -void GuiCitation::updateFormatting(CiteStyle currentStyle) +void GuiCitation::updateFormatting(CitationStyle const & currentStyle) { - CiteEngine const engine = citeEngine(); - bool const natbib_engine = - engine == ENGINE_NATBIB_AUTHORYEAR || - engine == ENGINE_NATBIB_NUMERICAL; - bool const basic_engine = engine == ENGINE_BASIC; - - bool const haveSelection = - selectedLV->model()->rowCount() > 0; - - bool const isNocite = currentStyle == NOCITE; - - bool const isCiteyear = - currentStyle == CITEYEAR || - currentStyle == CITEYEARPAR; + BufferParams const bp = documentBuffer().params(); + bool const force = currentStyle.forceUpperCase; + bool const starred = currentStyle.hasStarredVersion; + bool const full = starred && bp.fullAuthorList(); + bool const textbefore = currentStyle.textBefore; + bool const textafter = currentStyle.textAfter; + + int const rows = selectedLV->model()->rowCount(); + + bool const qualified = currentStyle.hasQualifiedList + && (rows > 1 + || !params_["pretextlist"].empty() + || !params_["posttextlist"].empty()); + selectedLV->horizontalHeader()->setVisible(qualified); + selectedLV->setColumnHidden(0, !qualified); + selectedLV->setColumnHidden(2, !qualified); + bool const haveSelection = rows > 0; + if (qualified) { + textBeforeLA->setText(qt_("General text befo&re:")); + textAfterLA->setText(qt_("General &text after:")); + textBeforeED->setToolTip(qt_("Text that precedes the whole reference list. " + "For text that precedes individual items, " + "double-click on the respective entry above.")); + textAfterLA->setToolTip(qt_("General &text after:")); + textAfterED->setToolTip(qt_("Text that follows the whole reference list. " + "For text that follows individual items, " + "double-click on the respective entry above.")); + } else { + textBeforeLA->setText(qt_("Text befo&re:")); + if (textbefore && haveSelection) + textBeforeED->setToolTip(qt_("Text that precedes the reference (e.g., \"cf.\")")); + else + textBeforeED->setToolTip(qt_("Text that precedes the reference (e.g., \"cf.\"), " + "if the current citation style supports this.")); + textAfterLA->setText(qt_("&Text after:")); + if (textafter && haveSelection) + textAfterED->setToolTip(qt_("Text that follows the reference (e.g., pages)")); + else + textAfterED->setToolTip(qt_("Text that follows the reference (e.g., pages), " + "if the current citation style supports this.")); + } - fulllistCB->setEnabled(natbib_engine && haveSelection && !isNocite - && !isCiteyear); - forceuppercaseCB->setEnabled(natbib_engine && haveSelection - && !isNocite && !isCiteyear); - textBeforeED->setEnabled(!basic_engine && haveSelection && !isNocite); - textBeforeLA->setEnabled(!basic_engine && haveSelection && !isNocite); - textAfterED->setEnabled(haveSelection && !isNocite); - textAfterLA->setEnabled(haveSelection && !isNocite); + forceuppercaseCB->setEnabled(force && haveSelection); + if (force && haveSelection) + forceuppercaseCB->setToolTip(qt_("Force upper case in names (\"Del Piero\", not \"del Piero\").")); + else + forceuppercaseCB->setToolTip(qt_("Force upper case in names (\"Del Piero\", not \"del Piero\"), " + "if the current citation style supports this.")); + starredCB->setEnabled(full && haveSelection); + textBeforeED->setEnabled(textbefore && haveSelection); + textBeforeLA->setEnabled(textbefore && haveSelection); + textAfterED->setEnabled(textafter && haveSelection); + textAfterLA->setEnabled(textafter && haveSelection); + literalCB->setEnabled(textbefore || textafter); citationStyleCO->setEnabled(haveSelection); citationStyleLA->setEnabled(haveSelection); + + // Check if we have a custom string/tooltip for the starred version + if (starred && !currentStyle.stardesc.empty()) { + string val = + bp.documentClass().getCiteMacro(bp.citeEngineType(), currentStyle.stardesc); + docstring guistring; + if (!val.empty()) { + guistring = translateIfPossible(from_utf8(val)); + starredCB->setText(toqstr(guistring)); + starredCB->setEnabled(haveSelection); + } + if (!currentStyle.startooltip.empty()) { + val = bp.documentClass().getCiteMacro(bp.citeEngineType(), + currentStyle.startooltip); + if (!val.empty()) + guistring = translateIfPossible(from_utf8(val)); + } + // Tooltip might also be empty + starredCB->setToolTip(toqstr(guistring)); + } else { + // This is the default meaning of the starred commands + starredCB->setText(qt_("All aut&hors")); + if (full && haveSelection) + starredCB->setToolTip(qt_("Always list all authors (rather than using \"et al.\")")); + else + starredCB->setToolTip(qt_("Always list all authors (rather than using \"et al.\"), " + "if the current citation style supports this.")); + } + if (availableLV->selectionModel()->selectedIndexes().isEmpty()) + availableLV->setToolTip(qt_("All references available for citing.")); + else + availableLV->setToolTip(qt_("All references available for citing.\n" + "To add the selected one, hit Add, press Enter or double-click.\n" + "Hit Ctrl-Enter to add and close the dialog.")); } -void GuiCitation::updateStyle() +// Update the styles for the style combo, citationStyleCO, and mark the +// settings as changed. Called upon changing the cited keys (including +// merely reordering the keys) or editing the text before/after fields. +void GuiCitation::updateStyles() { - string const & command = params_.getCmdName(); - - // Find the style of the citekeys - vector const & styles = citeStyles_; - CitationStyle const cs = citationStyleFromString(command); - - vector::const_iterator cit = - std::find(styles.begin(), styles.end(), cs.style); - - // restore the latest natbib style - if (style_ >= 0 && style_ < citationStyleCO->count()) - citationStyleCO->setCurrentIndex(style_); - else - citationStyleCO->setCurrentIndex(0); - - if (cit != styles.end()) { - int const i = int(cit - styles.begin()); - citationStyleCO->setCurrentIndex(i); - fulllistCB->setChecked(cs.full); - forceuppercaseCB->setChecked(cs.forceUpperCase); - } else { - fulllistCB->setChecked(false); - forceuppercaseCB->setChecked(false); - } - updateFormatting(cs.style); + BiblioInfo const & bi = bibInfo(); + updateStyles(bi); + changed(); } -// This one needs to be called whenever citationStyleCO needs -// to be updated---and this would be on anything that changes the -// selection in selectedLV, or on a general update. -void GuiCitation::fillStyles(BiblioInfo const & bi) +// Update the styles for the style combo, citationStyleCO. +void GuiCitation::updateStyles(BiblioInfo const & bi) { - QStringList selected_keys = selected_model_.stringList(); + QStringList selected_keys = selectedKeys(); int curr = selectedLV->model()->rowCount() - 1; if (curr < 0 || selected_keys.empty()) { @@ -282,27 +392,36 @@ void GuiCitation::fillStyles(BiblioInfo const & bi) return; } - int const oldIndex = citationStyleCO->currentIndex(); - - if (!selectedLV->selectionModel()->selectedIndexes().empty()) - curr = selectedLV->selectionModel()->selectedIndexes()[0].row(); + static const size_t max_length = 80; + BiblioInfo::CiteStringMap sty = citationStyles(bi, max_length); - QStringList sty = citationStyles(bi, curr); - citationStyleCO->clear(); - - if (sty.isEmpty()) { + if (sty.empty()) { // some error citationStyleCO->setEnabled(false); citationStyleLA->setEnabled(false); + citationStyleCO->clear(); return; } - - citationStyleCO->insertItems(0, sty); + + citationStyleCO->blockSignals(true); + + // save old style selection + QString const curdata = + citationStyleCO->itemData(citationStyleCO->currentIndex()).toString(); + QString const olddata = (curdata.isEmpty()) ? style_ : curdata; + citationStyleCO->clear(); + BiblioInfo::CiteStringMap::const_iterator cit = sty.begin(); + BiblioInfo::CiteStringMap::const_iterator end = sty.end(); + for (int ii = 1; cit != end; ++cit, ++ii) + citationStyleCO->addItem(toqstr(cit->second), toqstr(cit->first)); citationStyleCO->setEnabled(true); citationStyleLA->setEnabled(true); + // restore old style selection + int const i = citationStyleCO->findData(olddata); + if (i != -1) + citationStyleCO->setCurrentIndex(i); - if (oldIndex != -1 && oldIndex < citationStyleCO->count()) - citationStyleCO->setCurrentIndex(oldIndex); + citationStyleCO->blockSignals(false); } @@ -338,16 +457,15 @@ void GuiCitation::fillEntries(BiblioInfo const & bi) bool GuiCitation::isSelected(QModelIndex const & idx) { QString const str = idx.data().toString(); - return selected_model_.stringList().contains(str); + return selectedKeys().contains(str); } void GuiCitation::setButtons() { - selectionManager->update(); int const srows = selectedLV->model()->rowCount(); - applyPB->setEnabled(srows > 0); - okPB->setEnabled(srows > 0); + buttonBox->button(QDialogButtonBox::Apply)->setEnabled(srows > 0); + buttonBox->button(QDialogButtonBox::Ok)->setEnabled(srows > 0); } @@ -355,112 +473,134 @@ void GuiCitation::updateInfo(BiblioInfo const & bi, QModelIndex const & idx) { if (!idx.isValid() || bi.empty()) { infoML->document()->clear(); + infoML->setToolTip(qt_("Displays a sketchy preview if a citation is selected above")); return; } + infoML->setToolTip(qt_("Sketchy preview of the selected citation")); + CiteItem ci; + ci.richtext = true; QString const keytxt = toqstr( - bi.getInfo(qstring_to_ucs4(idx.data().toString()))); - infoML->document()->setPlainText(keytxt); + bi.getInfo(qstring_to_ucs4(idx.data().toString()), documentBuffer(), ci)); + infoML->document()->setHtml(keytxt); } void GuiCitation::findText(QString const & text, bool reset) { //"All Fields" and "Keys" are the first two - int index = fieldsCO->currentIndex() - 2; + int index = fieldsCO->currentIndex() - 2; BiblioInfo const & bi = bibInfo(); vector const & fields = bi.getFields(); docstring field; - + if (index <= -1 || index >= int(fields.size())) //either "All Fields" or "Keys" or an invalid value field = from_ascii(""); else field = fields[index]; - + //Was it "Keys"? bool const onlyKeys = index == -1; - + //"All Entry Types" is first. - index = entriesCO->currentIndex() - 1; + index = entriesCO->currentIndex() - 1; vector const & entries = bi.getEntries(); docstring entry_type; if (index < 0 || index >= int(entries.size())) entry_type = from_ascii(""); - else + else entry_type = entries[index]; - - bool const case_sentitive = caseCB->checkState(); - bool const reg_exp = regexCB->checkState(); - findKey(bi, text, onlyKeys, field, entry_type, + + bool const case_sentitive = casesense_->isChecked(); + bool const reg_exp = regexp_->isChecked(); + + findKey(bi, text, onlyKeys, field, entry_type, case_sentitive, reg_exp, reset); //FIXME - //It'd be nice to save and restore the current selection in + //It'd be nice to save and restore the current selection in //availableLV. Currently, we get an automatic reset, since the //model is reset. - + updateControls(bi); } void GuiCitation::on_fieldsCO_currentIndexChanged(int /*index*/) { - findText(findLE->text(), true); + findText(filter_->text(), true); } void GuiCitation::on_entriesCO_currentIndexChanged(int /*index*/) { - findText(findLE->text(), true); + findText(filter_->text(), true); } void GuiCitation::on_citationStyleCO_currentIndexChanged(int index) { if (index >= 0 && index < citationStyleCO->count()) { - vector const & styles = citeStyles_; + vector const & styles = citeStyles_; updateFormatting(styles[index]); + changed(); } } -void GuiCitation::on_findLE_textChanged(const QString & text) +void GuiCitation::filterChanged(const QString & text) { - bool const searchAsWeGo = (asTypeCB->checkState() == Qt::Checked); - searchPB->setDisabled(text.isEmpty() || searchAsWeGo); if (!text.isEmpty()) { - if (searchAsWeGo) - findText(findLE->text()); + if (instant_->isChecked()) + findText(filter_->text()); return; } - findText(findLE->text()); - findLE->setFocus(); + findText(filter_->text()); + filter_->setFocus(); } -void GuiCitation::on_searchPB_clicked() + +void GuiCitation::filterPressed() { - findText(findLE->text(), true); + findText(filter_->text(), true); } -void GuiCitation::on_caseCB_stateChanged(int) +void GuiCitation::resetFilter() { - findText(findLE->text()); + filter_->setText(QString()); + findText(filter_->text(), true); } -void GuiCitation::on_regexCB_stateChanged(int) +void GuiCitation::caseChanged() { - findText(findLE->text()); + findText(filter_->text()); } -void GuiCitation::on_asTypeCB_stateChanged(int) +void GuiCitation::regexChanged() { - bool const searchAsWeGo = (asTypeCB->checkState() == Qt::Checked); - searchPB->setDisabled(findLE->text().isEmpty() || searchAsWeGo); - if (searchAsWeGo) - findText(findLE->text(), true); + findText(filter_->text()); +} + + +void GuiCitation::updateFilterHint() +{ + QString hint = instant_->isChecked() ? + qt_("Enter string to filter the list of available citations") : + qt_("Enter string to filter the list of available citations and press "); + hint += qt_("\nThe down arrow key will get you into the list of filtered citations."); + filter_->setToolTip(hint); +} + + +void GuiCitation::instantChanged(bool checked) +{ + if (checked) + findText(filter_->text(), true); + + updateFilterHint(); } @@ -470,30 +610,34 @@ void GuiCitation::changed() } -void GuiCitation::apply(int const choice, bool full, bool force, +void GuiCitation::applyParams(int const choice, bool full, bool force, QString before, QString after) { if (cited_keys_.isEmpty()) return; - vector const & styles = citeStyles_; - if (styles[choice] == NOCITE) { - full = false; - force = false; + vector const & styles = citeStyles_; + + CitationStyle cs = styles[choice]; + + if (!cs.textBefore) before.clear(); + if (!cs.textAfter) after.clear(); - } - - CitationStyle s; - s.style = styles[choice]; - s.full = full; - s.forceUpperCase = force; - string const command = citationStyleToString(s); + + cs.forceUpperCase &= force; + cs.hasStarredVersion &= full; + string const command = citationStyleToString(cs); params_.setCmdName(command); params_["key"] = qstring_to_ucs4(cited_keys_.join(",")); params_["before"] = qstring_to_ucs4(before); params_["after"] = qstring_to_ucs4(after); + if (cs.hasQualifiedList) { + params_["pretextlist"] = getStringFromVector(getPreTexts(), from_ascii("\t")); + params_["posttextlist"] = getStringFromVector(getPostTexts(), from_ascii("\t")); + } + params_["literal"] = literalCB->isChecked() ? from_ascii("true") : from_ascii("false"); dispatchParams(); } @@ -501,7 +645,107 @@ void GuiCitation::apply(int const choice, bool full, bool force, void GuiCitation::clearSelection() { cited_keys_.clear(); - selected_model_.setStringList(cited_keys_); + setSelectedKeys(cited_keys_); +} + + +void GuiCitation::setSelectedKeys(QStringList const sl) +{ + selected_model_.clear(); + selected_model_.setColumnCount(3); + QStringList headers; + headers << qt_("Text before") + << qt_("Cite key") + << qt_("Text after"); + selected_model_.setHorizontalHeaderLabels(headers); + selectedLV->setColumnHidden(0, true); + selectedLV->setColumnHidden(2, true); + selectedLV->verticalHeader()->setVisible(false); + selectedLV->horizontalHeader()->setVisible(false); + QStringList::const_iterator it = sl.begin(); + QStringList::const_iterator end = sl.end(); + for (int i = 0; it != end; ++it, ++i) { + QStandardItem * si = new QStandardItem(); + si->setData(*it); + si->setText(*it); + si->setToolTip(*it); + si->setEditable(false); + selected_model_.setItem(i, 1, si); + } +} + + +QStringList GuiCitation::selectedKeys() +{ + QStringList res; + for (int i = 0; i != selected_model_.rowCount(); ++i) { + QStandardItem const * item = selected_model_.item(i, 1); + if (item) + res.append(item->text()); + } + return res; +} + + +void GuiCitation::setPreTexts(vector const m) +{ + for (docstring const & s: m) { + QStandardItem * si = new QStandardItem(); + docstring key; + docstring pre = split(s, key, ' '); + si->setData(toqstr(pre)); + si->setText(toqstr(pre)); + QModelIndexList qmil = + selected_model_.match(selected_model_.index(0, 1), + Qt::DisplayRole, toqstr(key), 1, + Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap)); + if (!qmil.empty()) + selected_model_.setItem(qmil.front().row(), 0, si); + } +} + + +vector GuiCitation::getPreTexts() +{ + vector res; + for (int i = 0; i != selected_model_.rowCount(); ++i) { + QStandardItem const * key = selected_model_.item(i, 1); + QStandardItem const * pre = selected_model_.item(i, 0); + if (key && pre && !key->text().isEmpty() && !pre->text().isEmpty()) + res.push_back(qstring_to_ucs4(key->text()) + " " + qstring_to_ucs4(pre->text())); + } + return res; +} + + +void GuiCitation::setPostTexts(vector const m) +{ + for (docstring const & s: m) { + QStandardItem * si = new QStandardItem(); + docstring key; + docstring post = split(s, key, ' '); + si->setData(toqstr(post)); + si->setText(toqstr(post)); + QModelIndexList qmil = + selected_model_.match(selected_model_.index(0, 1), + Qt::DisplayRole, toqstr(key), 1, + Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap)); + if (!qmil.empty()) + selected_model_.setItem(qmil.front().row(), 2, si); + } +} + + +vector GuiCitation::getPostTexts() +{ + vector res; + for (int i = 0; i != selected_model_.rowCount(); ++i) { + QStandardItem const * key = selected_model_.item(i, 1); + QStandardItem const * post = selected_model_.item(i, 2); + if (key && post && !key->text().isEmpty() && !post->text().isEmpty()) + res.push_back(qstring_to_ucs4(key->text()) + " " + qstring_to_ucs4(post->text())); + } + return res; } @@ -510,6 +754,7 @@ void GuiCitation::init() // Make the list of all available bibliography keys BiblioInfo const & bi = bibInfo(); all_keys_ = to_qstring_list(bi.getKeys()); + available_model_.setStringList(all_keys_); // Ditto for the keys cited in this inset @@ -518,19 +763,60 @@ void GuiCitation::init() cited_keys_.clear(); else cited_keys_ = str.split(","); - selected_model_.setStringList(cited_keys_); + setSelectedKeys(cited_keys_); + + // Initialize the drop downs + fillEntries(bi); + fillFields(bi); + + // Initialize the citation formatting + string const & cmd = params_.getCmdName(); + CitationStyle const cs = + citationStyleFromString(cmd, documentBuffer().params()); + + forceuppercaseCB->setChecked(cs.forceUpperCase); + starredCB->setChecked(cs.hasStarredVersion && + documentBuffer().params().fullAuthorList()); + textBeforeED->setText(toqstr(params_["before"])); + textAfterED->setText(toqstr(params_["after"])); + + // if this is a new citation, we set the literal checkbox + // to its last set value. + if (cited_keys_.isEmpty()) + literalCB->setChecked(InsetCitation::last_literal); + else + literalCB->setChecked(params_["literal"] == "true"); + + setPreTexts(getVectorFromString(params_["pretextlist"], from_ascii("\t"))); + setPostTexts(getVectorFromString(params_["posttextlist"], from_ascii("\t"))); + + // Update the interface + updateControls(bi); + updateStyles(bi); if (selected_model_.rowCount()) { selectedLV->blockSignals(true); selectedLV->setFocus(); - QModelIndex idx = selected_model_.index(0, 0); - selectedLV->selectionModel()->select(idx, - QItemSelectionModel::ClearAndSelect); + selectedLV->selectRow(0); selectedLV->blockSignals(false); + + // Find the citation style + vector const & cmds = citeCmds_; + vector::const_iterator cit = + std::find(cmds.begin(), cmds.end(), cs.name); + int i = 0; + if (cit != cmds.end()) + i = int(cit - cmds.begin()); + + // Set the style combo appropriately + citationStyleCO->blockSignals(true); + citationStyleCO->setCurrentIndex(i); + citationStyleCO->blockSignals(false); + updateFormatting(citeStyles_[i]); } else availableLV->setFocus(); - fillFields(bi); - fillEntries(bi); - updateControls(bi); + + buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); + buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } @@ -539,6 +825,8 @@ void GuiCitation::findKey(BiblioInfo const & bi, docstring field, docstring entry_type, bool case_sensitive, bool reg_exp, bool reset) { + // FIXME THREAD + // This should be moved to a class member. // Used for optimisation: store last searched string. static QString last_searched_string; // Used to disable the above optimisation. @@ -571,40 +859,75 @@ void GuiCitation::findKey(BiblioInfo const & bi, last_searched_string = str; QStringList result; - - // First, filter by entry_type, which will be faster than + + // First, filter by entry_type, which will be faster than // what follows, so we may get to do that on less. vector keyVector = to_docstring_vector(keys); filterByEntryType(bi, keyVector, entry_type); - + if (str.isEmpty()) result = to_qstring_list(keyVector); else - result = to_qstring_list(searchKeys(bi, keyVector, only_keys, + result = to_qstring_list(searchKeys(bi, keyVector, only_keys, qstring_to_ucs4(str), field, case_sensitive, reg_exp)); - + available_model_.setStringList(result); } -QStringList GuiCitation::citationStyles(BiblioInfo const & bi, int sel) +BiblioInfo::CiteStringMap GuiCitation::citationStyles(BiblioInfo const & bi, size_t max_size) { - docstring const key = qstring_to_ucs4(cited_keys_[sel]); - return to_qstring_list(bi.getCiteStrings(key, buffer())); + vector const keys = to_docstring_vector(cited_keys_); + vector styles = citeStyles_; + int ind = citationStyleCO->currentIndex(); + if (ind == -1) + ind = 0; + CitationStyle cs = styles[ind]; + vector pretexts = getPreTexts(); + vector posttexts = getPostTexts(); + bool const qualified = cs.hasQualifiedList + && (selectedLV->model()->rowCount() > 1 + || !pretexts.empty() + || !posttexts.empty()); + std::map pres; + for (docstring const & s: pretexts) { + docstring key; + docstring val = split(s, key, ' '); + pres[key] = val; + } + std::map posts; + for (docstring const & s: posttexts) { + docstring key; + docstring val = split(s, key, ' '); + posts[key] = val; + } + CiteItem ci; + ci.textBefore = qstring_to_ucs4(textBeforeED->text()); + ci.textAfter = qstring_to_ucs4(textAfterED->text()); + ci.forceUpperCase = forceuppercaseCB->isChecked(); + ci.Starred = starredCB->isChecked(); + ci.context = CiteItem::Dialog; + ci.max_size = max_size; + ci.isQualified = qualified; + ci.pretexts = pres; + ci.posttexts = posts; + BiblioInfo::CiteStringMap ret = bi.getCiteStrings(keys, styles, documentBuffer(), ci); + return ret; } -void GuiCitation::setCitedKeys() +void GuiCitation::setCitedKeys() { - cited_keys_ = selected_model_.stringList(); + cited_keys_ = selectedKeys(); + updateStyles(); } -bool GuiCitation::initialiseParams(string const & data) +bool GuiCitation::initialiseParams(string const & sdata) { - InsetCommand::string2params("citation", data, params_); - CiteEngine const engine = buffer().params().citeEngine(); - citeStyles_ = citeStyles(engine); + InsetCommand::string2params(sdata, params_); + citeCmds_ = documentBuffer().params().citeCommands(); + citeStyles_ = documentBuffer().params().citeStyles(); init(); return true; } @@ -617,11 +940,11 @@ void GuiCitation::clearParams() void GuiCitation::filterByEntryType(BiblioInfo const & bi, - vector & keyVector, docstring entry_type) + vector & keyVector, docstring entry_type) { if (entry_type.empty()) return; - + vector::iterator it = keyVector.begin(); vector::iterator end = keyVector.end(); @@ -638,12 +961,6 @@ void GuiCitation::filterByEntryType(BiblioInfo const & bi, } -CiteEngine GuiCitation::citeEngine() const -{ - return buffer().params().citeEngine(); -} - - // Escape special chars. // All characters are literals except: '.|*?+(){}[]^$\' // These characters are literals when preceded by a "\", which is done here @@ -652,22 +969,28 @@ CiteEngine GuiCitation::citeEngine() const static docstring escape_special_chars(docstring const & expr) { // Search for all chars '.|*?+(){}[^$]\' - // Note that '[' and '\' must be escaped. - // This is a limitation of boost::regex, but all other chars in BREs - // are assumed literal. - static const boost::regex reg("[].|*?+(){}^$\\[\\\\]"); + // Note that '[', ']', and '\' must be escaped. + static const lyx::regex reg("[.|*?+(){}^$\\[\\]\\\\]"); - // $& is a perl-like expression that expands to all + // $& is an ECMAScript format expression that expands to all // of the current match - // The '$' must be prefixed with the escape character '\' for - // boost to treat it as a literal. - // Thus, to prefix a matched expression with '\', we use: +#ifdef LYX_USE_STD_REGEX + // To prefix a matched expression with a single literal backslash, we + // need to escape it for the C++ compiler and use: // FIXME: UNICODE - return from_utf8(boost::regex_replace(to_utf8(expr), reg, "\\\\$&")); + return from_utf8(lyx::regex_replace(to_utf8(expr), reg, string("\\$&"))); +#else + // A backslash in the format string starts an escape sequence in boost. + // Thus, to prefix a matched expression with a single literal backslash, + // we need to give two backslashes to the regex engine, and escape both + // for the C++ compiler and use: + // FIXME: UNICODE + return from_utf8(lyx::regex_replace(to_utf8(expr), reg, string("\\\\$&"))); +#endif } -vector GuiCitation::searchKeys(BiblioInfo const & bi, +vector GuiCitation::searchKeys(BiblioInfo const & bi, vector const & keys_to_search, bool only_keys, docstring const & search_expression, docstring field, bool case_sensitive, bool regex) @@ -680,15 +1003,15 @@ vector GuiCitation::searchKeys(BiblioInfo const & bi, if (!regex) // We must escape special chars in the search_expr so that - // it is treated as a simple string by boost::regex. + // it is treated as a simple string by lyx::regex. expr = escape_special_chars(expr); - boost::regex reg_exp; + lyx::regex reg_exp; try { reg_exp.assign(to_utf8(expr), case_sensitive ? - boost::regex_constants::normal : boost::regex_constants::icase); - } catch (boost::regex_error & e) { - // boost::regex throws an exception if the regular expression is not + lyx::regex_constants::ECMAScript : lyx::regex_constants::icase); + } catch (lyx::regex_error const & e) { + // lyx::regex throws an exception if the regular expression is not // valid. LYXERR(Debug::GUI, e.what()); return vector(); @@ -700,24 +1023,24 @@ vector GuiCitation::searchKeys(BiblioInfo const & bi, BiblioInfo::const_iterator info = bi.find(*it); if (info == bi.end()) continue; - + BibTeXInfo const & kvm = info->second; - string data; + string sdata; if (only_keys) - data = to_utf8(*it); + sdata = to_utf8(*it); else if (field.empty()) - data = to_utf8(*it) + ' ' + to_utf8(kvm.allData()); - else - data = to_utf8(kvm[field]); - - if (data.empty()) + sdata = to_utf8(*it) + ' ' + to_utf8(kvm.allData()); + else + sdata = to_utf8(kvm[field]); + + if (sdata.empty()) continue; try { - if (boost::regex_search(data, reg_exp)) + if (lyx::regex_search(sdata, reg_exp)) foundKeys.push_back(*it); } - catch (boost::regex_error & e) { + catch (lyx::regex_error const & e) { LYXERR(Debug::GUI, e.what()); return vector(); } @@ -728,28 +1051,32 @@ vector GuiCitation::searchKeys(BiblioInfo const & bi, void GuiCitation::dispatchParams() { - std::string const lfun = InsetCommand::params2string("citation", params_); + std::string const lfun = InsetCommand::params2string(params_); dispatch(FuncRequest(getLfun(), lfun)); } BiblioInfo const & GuiCitation::bibInfo() const { - buffer().checkBibInfoCache(); - return buffer().masterBibInfo(); + Buffer const & buf = documentBuffer(); + buf.reloadBibInfoCache(); + return buf.masterBibInfo(); } -void GuiCitation::saveSession() const +void GuiCitation::saveSession(QSettings & settings) const { - Dialog::saveSession(); - QSettings settings; + Dialog::saveSession(settings); + settings.setValue( + sessionKey() + "/regex", regexp_->isChecked()); + settings.setValue( + sessionKey() + "/casesensitive", casesense_->isChecked()); settings.setValue( - sessionKey() + "/regex", regexCB->isChecked()); + sessionKey() + "/autofind", instant_->isChecked()); settings.setValue( - sessionKey() + "/casesensitive", caseCB->isChecked()); + sessionKey() + "/citestyle", style_); settings.setValue( - sessionKey() + "/autofind", asTypeCB->isChecked()); + sessionKey() + "/literal", InsetCitation::last_literal); } @@ -757,12 +1084,13 @@ void GuiCitation::restoreSession() { Dialog::restoreSession(); QSettings settings; - regexCB->setChecked( - settings.value(sessionKey() + "/regex").toBool()); - caseCB->setChecked( - settings.value(sessionKey() + "/casesensitive").toBool()); - asTypeCB->setChecked( - settings.value(sessionKey() + "/autofind").toBool()); + regexp_->setChecked(settings.value(sessionKey() + "/regex").toBool()); + casesense_->setChecked(settings.value(sessionKey() + "/casesensitive").toBool()); + instant_->setChecked(settings.value(sessionKey() + "/autofind", true).toBool()); + style_ = settings.value(sessionKey() + "/citestyle").toString(); + InsetCitation::last_literal = + settings.value(sessionKey() + "/literal", false).toBool(); + updateFilterHint(); }