From 97aea7e5b1707d662d6d7146d53adc97045c44c9 Mon Sep 17 00:00:00 2001 From: Juergen Spitzmueller Date: Fri, 30 Dec 2016 16:22:08 +0100 Subject: [PATCH] Disentangle CiteEngines and Modules These two are different beasts and thus should be handled differently. --- lib/Makefile.am | 9 +- lib/chkconfig.ltx | 1 + .../basic.citeengine} | 3 +- .../jurabib.citeengine} | 3 +- .../natbib.citeengine} | 3 +- lib/configure.py | 131 +++++++- po/Rules-lyx | 2 +- po/lyx_pot.py | 7 + src/BufferParams.cpp | 5 +- src/CiteEnginesList.cpp | 280 ++++++++++++++++++ src/CiteEnginesList.h | 156 ++++++++++ src/LyX.cpp | 8 +- src/Makefile.am | 2 + src/TextClass.cpp | 40 +++ src/TextClass.h | 3 + src/tex2lyx/Makefile.am | 1 + src/tex2lyx/tex2lyx.cpp | 3 +- 17 files changed, 640 insertions(+), 17 deletions(-) rename lib/{layouts/basic.module => citeengines/basic.citeengine} (93%) rename lib/{layouts/jurabib.module => citeengines/jurabib.citeengine} (98%) rename lib/{layouts/natbib.module => citeengines/natbib.citeengine} (97%) create mode 100644 src/CiteEnginesList.cpp create mode 100644 src/CiteEnginesList.h diff --git a/lib/Makefile.am b/lib/Makefile.am index ee7e2b141c..36c9c0c297 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -2107,7 +2107,6 @@ dist_layouts_DATA =\ layouts/arab-article.layout \ layouts/article.layout \ layouts/article-beamer.layout \ - layouts/basic.module \ layouts/beamer.layout \ layouts/beamerposter.layout \ layouts/bicaption.module \ @@ -2185,7 +2184,6 @@ dist_layouts_DATA =\ layouts/jsarticle.layout \ layouts/jsbook.layout \ layouts/jss.layout \ - layouts/jurabib.module \ layouts/kluwer.layout \ layouts/knitr.module \ layouts/latex8.layout \ @@ -2205,7 +2203,6 @@ dist_layouts_DATA =\ layouts/mwart.layout \ layouts/mwbk.layout \ layouts/mwrep.layout \ - layouts/natbib.module \ layouts/natbibapa.module \ layouts/noweb.module \ layouts/numarticle.inc \ @@ -2302,6 +2299,12 @@ dist_layouts_DATA =\ layouts/tufte-handout.layout \ layouts/varwidth.module +citeenginesdir = $(pkgdatadir)/citeengines +dist_citeengines_DATA = \ + citeengines/basic.citeengine \ + citeengines/jurabib.citeengine \ + citeengines/natbib.citeengine + scriptsdir = $(pkgdatadir)/scripts dist_scripts_DATA = \ scripts/bash_completion \ diff --git a/lib/chkconfig.ltx b/lib/chkconfig.ltx index 140014af72..9daca8fc97 100644 --- a/lib/chkconfig.ltx +++ b/lib/chkconfig.ltx @@ -492,6 +492,7 @@ % configure script. \input{chklayouts} \input{chkmodules} +\input{chkciteengines} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%% END ACTUAL CONFIGURATION INSPECTION CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/layouts/basic.module b/lib/citeengines/basic.citeengine similarity index 93% rename from lib/layouts/basic.module rename to lib/citeengines/basic.citeengine index 8e0ed1c952..dc4941b42d 100644 --- a/lib/layouts/basic.module +++ b/lib/citeengines/basic.citeengine @@ -1,9 +1,8 @@ -# \DeclareLyXModule{Default (basic)} +# \DeclareLyXCiteEngine{BibTeX (basic)} # DescriptionBegin # Use the basic citation capabilities provided by plain LaTeX. # DescriptionEnd # Excludes: jurabib | natbib -# Category: Citation engine # Author: Julien Rioux diff --git a/lib/layouts/jurabib.module b/lib/citeengines/jurabib.citeengine similarity index 98% rename from lib/layouts/jurabib.module rename to lib/citeengines/jurabib.citeengine index 63bae5a798..13bbc6f9d3 100644 --- a/lib/layouts/jurabib.module +++ b/lib/citeengines/jurabib.citeengine @@ -1,11 +1,10 @@ -# \DeclareLyXModule[jurabib.sty]{Jurabib} +# \DeclareLyXCiteEngine[jurabib.sty]{Jurabib} # DescriptionBegin # Loads the LaTeX package jurabib, a citation engine. Jurabib supports annotations, # author-year style citations and hyphenation patterns for bibliography entries in # English, German, French, Dutch, Spanish and Italian. # DescriptionEnd # Excludes: basic | natbib -# Category: Citation engine # Author: Julien Rioux diff --git a/lib/layouts/natbib.module b/lib/citeengines/natbib.citeengine similarity index 97% rename from lib/layouts/natbib.module rename to lib/citeengines/natbib.citeengine index d4d250effb..63bf1cb2c8 100644 --- a/lib/layouts/natbib.module +++ b/lib/citeengines/natbib.citeengine @@ -1,4 +1,4 @@ -# \DeclareLyXModule[natbib.sty]{Natbib} +# \DeclareLyXCiteEngine[natbib.sty]{Natbib} # DescriptionBegin # Loads the LaTeX package natbib, a citation engine. Natbib supports # both author-year and numerical styles for citations, automatic sorting @@ -6,7 +6,6 @@ # `van' part of author names, shortened and full author lists, and more. # DescriptionEnd # Excludes: basic | jurabib -# Category: Citation engine # Author: Julien Rioux diff --git a/lib/configure.py b/lib/configure.py index fb1abd3503..680e32a26f 100644 --- a/lib/configure.py +++ b/lib/configure.py @@ -1502,6 +1502,132 @@ def processModuleFile(file, filename, bool_docbook): return '"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, desc, pkgs, req, excl, catgy) +def checkCiteEnginesConfig(): + removeFiles(['lyxciteengines.lst', 'chkciteengines.tex']) + + logger.info('+checking list of cite engines... ') + tx = open('lyxciteengines.lst', 'w') + tx.write('''## This file declares cite engines and their associated definition files. +## It has been automatically generated by configure +## Use "Options/Reconfigure" if you need to update it after a +## configuration change. +## "CiteEngineName" "filename" "CiteEngineType" "Description" "Packages" "Requires" "Excludes" +''') + + # build the list of available modules + seen = [] + # note that this searches the local directory first, then the + # system directory. that way, we pick up the user's version first. + for file in glob.glob( os.path.join('citeengines', '*.citeengine') ) + \ + glob.glob( os.path.join(srcdir, 'citeengines', '*.citeengine' ) ) : + # valid file? + logger.info(file) + if not os.path.isfile(file): + continue + + filename = file.split(os.sep)[-1] + filename = filename[:-11] + if seen.count(filename): + continue + + seen.append(filename) + retval = processCiteEngineFile(file, filename, bool_docbook) + if retval != "": + tx.write(retval) + tx.close() + logger.info('\tdone') + + +def processCiteEngineFile(file, filename, bool_docbook): + ''' process cite engines file and get a line of result + + The top of a cite engine file should look like this: + #\DeclareLyXCiteEngine[LaTeX Packages]{CiteEngineName} + #DescriptionBegin + #...body of description... + #DescriptionEnd + #Requires: [list of required engines] + #Excludes: [list of excluded engines] + The last two lines are optional. + We expect output: + "CiteEngineName" "filename" "CiteEngineType" "Description" "Packages" "Requires" "Excludes" + ''' + remods = re.compile(r'\DeclareLyXCiteEngine\s*(?:\[([^]]*?)\])?{(.*)}') + rereqs = re.compile(r'#+\s*Requires: (.*)') + reexcs = re.compile(r'#+\s*Excludes: (.*)') + redbeg = re.compile(r'#+\s*DescriptionBegin\s*$') + redend = re.compile(r'#+\s*DescriptionEnd\s*$') + recet = re.compile(r'\s*CiteEngineType (.*)') + + modname = desc = pkgs = req = excl = cet = "" + readingDescription = False + descLines = [] + + for line in open(file).readlines(): + if readingDescription: + res = redend.search(line) + if res != None: + readingDescription = False + desc = " ".join(descLines) + # Escape quotes. + desc = desc.replace('"', '\\"') + continue + descLines.append(line[1:].strip()) + continue + res = redbeg.search(line) + if res != None: + readingDescription = True + continue + res = remods.search(line) + if res != None: + (pkgs, modname) = res.groups() + if pkgs == None: + pkgs = "" + else: + tmp = [s.strip() for s in pkgs.split(",")] + pkgs = ",".join(tmp) + continue + res = rereqs.search(line) + if res != None: + req = res.group(1) + tmp = [s.strip() for s in req.split("|")] + req = "|".join(tmp) + continue + res = reexcs.search(line) + if res != None: + excl = res.group(1) + tmp = [s.strip() for s in excl.split("|")] + excl = "|".join(tmp) + continue + res = recet.search(line) + if res != None: + cet = res.group(1) + continue + + if modname == "": + logger.warning("Cite Engine File file without \DeclareLyXCiteEngine line. ") + return "" + + if pkgs != "": + # this cite engine has some latex dependencies: + # append the dependencies to chkciteengines.tex, + # which is \input'ed by chkconfig.ltx + testpackages = list() + for pkg in pkgs.split(","): + if "->" in pkg: + # this is a converter dependency: skip + continue + if pkg.endswith(".sty"): + pkg = pkg[:-4] + testpackages.append("\\TestPackage{%s}" % (pkg,)) + cm = open('chkciteengines.tex', 'a') + for line in testpackages: + cm.write(line + '\n') + cm.close() + + return '"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, cet, desc, pkgs, req, excl) + + def checkTeXAllowSpaces(): ''' Let's check whether spaces are allowed in TeX file names ''' tex_allows_spaces = 'false' @@ -1543,8 +1669,8 @@ def removeTempFiles(): # Final clean-up if not lyx_keep_temps: removeFiles(['chkconfig.vars', 'chklatex.ltx', 'chklatex.log', - 'chklayouts.tex', 'chkmodules.tex', 'missfont.log', - 'wrap_chkconfig.ltx', 'wrap_chkconfig.log']) + 'chklayouts.tex', 'chkmodules.tex', 'chkciteengines.tex', + 'missfont.log', 'wrap_chkconfig.ltx', 'wrap_chkconfig.log']) if __name__ == '__main__': @@ -1623,6 +1749,7 @@ Format %i if lyx_kpsewhich: rescanTeXFiles() checkModulesConfig() + checkCiteEnginesConfig() # --without-latex-config can disable lyx_check_config ret = checkLatexConfig(lyx_check_config and LATEX != '', bool_docbook) removeTempFiles() diff --git a/po/Rules-lyx b/po/Rules-lyx index 42998f2ca5..23d4c061af 100644 --- a/po/Rules-lyx +++ b/po/Rules-lyx @@ -34,7 +34,7 @@ layouts_l10n.pot: $(top_srcdir)/lib/layouts/*.layout \ # Read translatable strings from layouts and translations from the po files and # create the layouttranslations file containing all LaTeX relevant translations $(top_srcdir)/lib/layouttranslations: $(POFILES) $(top_srcdir)/lib/layouts/*.layout \ - $(top_srcdir)/lib/layouts/*.inc $(top_srcdir)/lib/layouts/*.module + $(top_srcdir)/lib/layouts/*.inc $(top_srcdir)/lib/layouts/*.module $(top_srcdir)/lib/citeengines/*.citeengines $(LYX_POT) -o $@ -t layouttranslations ${top_srcdir}/lib/layouts/*.layout ${top_srcdir}/lib/layouts/*.inc ${top_srcdir}/lib/layouts/*.module languages_l10n.pot: $(top_srcdir)/lib/languages diff --git a/po/lyx_pot.py b/po/lyx_pot.py index 761e3dd0e6..0fc7ee481c 100755 --- a/po/lyx_pot.py +++ b/po/lyx_pot.py @@ -91,6 +91,7 @@ def layouts_l10n(input_files, output, base, layouttranslations): ListName = re.compile(r'^\s*ListName\s+(.*\S)\s*$', re.IGNORECASE) CategoryName = re.compile(r'^\s*Category\s+(.*\S)\s*$', re.IGNORECASE) NameRE = re.compile(r'^\s*#\s*\\DeclareLyXModule.*{(.*)}$', re.IGNORECASE) + CiteNameRE = re.compile(r'^\s*#\s*\\DeclareLyXCiteEngine.*{(.*)}$', re.IGNORECASE) InsetLayout = re.compile(r'^InsetLayout\s+\"?(.*)\"?\s*$', re.IGNORECASE) FlexCheck = re.compile(r'^Flex:(.*)', re.IGNORECASE) CaptionCheck = re.compile(r'^Caption:(.*)', re.IGNORECASE) @@ -229,6 +230,12 @@ def layouts_l10n(input_files, output, base, layouttranslations): readingI18nPreamble = True continue res = NameRE.search(line) + if res != None: + string = res.group(1) + if not layouttranslations: + writeString(out, src, base, lineno + 1, string) + continue + res = CiteNameRE.search(line) if res != None: string = res.group(1) if not layouttranslations: diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index fa0b7c3b92..326d487645 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -2333,6 +2333,7 @@ void BufferParams::makeDocumentClass(bool const clone) invalidateConverterCache(); LayoutModuleList mods; + LayoutModuleList ces; LayoutModuleList::iterator it = layout_modules_.begin(); LayoutModuleList::iterator en = layout_modules_.end(); for (; it != en; ++it) @@ -2341,9 +2342,9 @@ void BufferParams::makeDocumentClass(bool const clone) it = cite_engine_.begin(); en = cite_engine_.end(); for (; it != en; ++it) - mods.push_back(*it); + ces.push_back(*it); - doc_class_ = getDocumentClass(*baseClass(), mods, clone); + doc_class_ = getDocumentClass(*baseClass(), mods, ces, clone); TextClass::ReturnValues success = TextClass::OK; if (!forced_local_layout_.empty()) diff --git a/src/CiteEnginesList.cpp b/src/CiteEnginesList.cpp new file mode 100644 index 0000000000..48222cbb23 --- /dev/null +++ b/src/CiteEnginesList.cpp @@ -0,0 +1,280 @@ +// -*- C++ -*- +/** + * \file CiteEnginesList.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Richard Heck + * \author Jürgen Spitzmüller + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include "CiteEnginesList.h" + +#include "LaTeXFeatures.h" +#include "Lexer.h" + +#include "support/debug.h" +#include "support/FileName.h" +#include "support/gettext.h" +#include "support/filetools.h" +#include "support/lstrings.h" + +#include + +using namespace std; +using namespace lyx::support; + +namespace lyx { + + +//global variable: cite engine list +CiteEnginesList theCiteEnginesList; + + +LyXCiteEngine::LyXCiteEngine(string const & n, string const & i, + vector const & cet, string const & d, + vector const & p, + vector const & r, vector const & e): + name_(n), id_(i), engine_types_(cet), description_(d), package_list_(p), + required_engines_(r), excluded_engines_(e), + checked_(false), available_(false) +{ + filename_ = id_ + ".citeengine"; +} + + +vector LyXCiteEngine::prerequisites() const +{ + if (!checked_) + isAvailable(); + return prerequisites_; +} + + +bool LyXCiteEngine::isAvailable() const +{ + if (package_list_.empty()) + return true; + if (checked_) + return available_; + checked_ = true; + available_ = true; + //check whether all of the required packages are available + vector::const_iterator it = package_list_.begin(); + vector::const_iterator end = package_list_.end(); + for (; it != end; ++it) { + if (!LaTeXFeatures::isAvailable(*it)) { + available_ = false; + prerequisites_.push_back(*it); + } + } + return available_; +} + + +bool LyXCiteEngine::isCompatible(string const & cename) const +{ + // do we exclude it? + if (find(excluded_engines_.begin(), excluded_engines_.end(), cename) != + excluded_engines_.end()) + return false; + + LyXCiteEngine const * const lm = theCiteEnginesList[cename]; + if (!lm) + return true; + + // does it exclude us? + vector const excengs = lm->getExcludedEngines(); + if (find(excengs.begin(), excengs.end(), id_) != excengs.end()) + return false; + + return true; +} + + +bool LyXCiteEngine::areCompatible(string const & eng1, string const & eng2) +{ + LyXCiteEngine const * const lm1 = theCiteEnginesList[eng1]; + if (lm1) + return lm1->isCompatible(eng2); + LyXCiteEngine const * const lm2 = theCiteEnginesList[eng2]; + if (lm2) + return lm2->isCompatible(eng1); + // Can't check it either way. + return true; +} + + +// used when sorting the cite engine list. +class EngineSorter { +public: + int operator()(LyXCiteEngine const & ce1, LyXCiteEngine const & ce2) const + { + return _(ce1.getName()) < _(ce2.getName()); + } +}; + + +// Much of this is borrowed from LayoutFileList::read() +bool CiteEnginesList::read() +{ + FileName const real_file = libFileSearch("", "lyxciteengines.lst"); + LYXERR(Debug::TCLASS, "Reading cite engines from `" << real_file << '\''); + + if (real_file.empty()) { + LYXERR0("unable to find cite engines file `citeengines.lst'.\n" + << "No cite engines will be available."); + return false; + } + + Lexer lex; + if (!lex.setFile(real_file)) { + LYXERR0("lyxlex was not able to set file: " + << real_file << ".\nNo cite engines will be available."); + return false; + } + + if (!lex.isOK()) { + LYXERR0("unable to open cite engines file `" + << to_utf8(makeDisplayPath(real_file.absFileName(), 1000)) + << "'\nNo cite engines will be available."); + return false; + } + + bool finished = false; + // Parse cite engines files + LYXERR(Debug::TCLASS, "Starting parsing of lyxciteengines.lst"); + while (lex.isOK() && !finished) { + LYXERR(Debug::TCLASS, "\tline by line"); + switch (lex.lex()) { + case Lexer::LEX_FEOF: + finished = true; + break; + default: + string const cename = lex.getString(); + LYXERR(Debug::TCLASS, "Engine name: " << cename); + if (!lex.next()) + break; + string const fname = lex.getString(); + LYXERR(Debug::TCLASS, "Filename: " << fname); + if (!lex.next(true)) + break; + string cet = lex.getString(); + LYXERR(Debug::TCLASS, "Engine Type: " << cet); + vector cets; + while (!cet.empty()) { + string p; + cet = split(cet, p, '|'); + cets.push_back(p); + } + if (!lex.next(true)) + break; + string const desc = lex.getString(); + LYXERR(Debug::TCLASS, "Description: " << desc); + //FIXME Add packages + if (!lex.next()) + break; + string str = lex.getString(); + LYXERR(Debug::TCLASS, "Packages: " << str); + vector pkgs; + while (!str.empty()) { + string p; + str = split(str, p, ','); + pkgs.push_back(p); + } + if (!lex.next()) + break; + str = lex.getString(); + LYXERR(Debug::TCLASS, "Required: " << str); + vector req; + while (!str.empty()) { + string p; + str = split(str, p, '|'); + req.push_back(p); + } + if (!lex.next()) + break; + str = lex.getString(); + LYXERR(Debug::TCLASS, "Excluded: " << str); + vector exc; + while (!str.empty()) { + string p; + str = split(str, p, '|'); + exc.push_back(p); + } + // This code is run when we have + // cename, fname, desc, pkgs, req and exc + addCiteEngine(cename, fname, cets, desc, pkgs, req, exc); + } // end switch + } //end while + + LYXERR(Debug::TCLASS, "End of parsing of lyxciteengines.lst"); + + if (!theCiteEnginesList.empty()) + sort(theCiteEnginesList.begin(), theCiteEnginesList.end(), EngineSorter()); + return true; +} + + +void CiteEnginesList::addCiteEngine(string const & cename, + string const & filename, vector const & cets, string const & description, + vector const & pkgs, vector const & req, + vector const & exc) +{ + LyXCiteEngine ce(cename, filename, cets, description, pkgs, req, exc); + englist_.push_back(ce); +} + + +LyXCiteEnginesList::const_iterator CiteEnginesList::begin() const +{ + return englist_.begin(); +} + + +LyXCiteEnginesList::iterator CiteEnginesList::begin() +{ + return englist_.begin(); +} + + +LyXCiteEnginesList::const_iterator CiteEnginesList::end() const +{ + return englist_.end(); +} + + +LyXCiteEnginesList::iterator CiteEnginesList::end() +{ + return englist_.end(); +} + + +LyXCiteEngine const * CiteEnginesList::operator[](string const & str) const +{ + LyXCiteEnginesList::const_iterator it = englist_.begin(); + for (; it != englist_.end(); ++it) + if (it->getID() == str) { + LyXCiteEngine const & eng = *it; + return ŋ + } + return 0; +} + + +LyXCiteEngine * CiteEnginesList::operator[](string const & str) +{ + LyXCiteEnginesList::iterator it = englist_.begin(); + for (; it != englist_.end(); ++it) + if (it->getID() == str) { + LyXCiteEngine & eng = *it; + return ŋ + } + return 0; +} + +} // namespace lyx diff --git a/src/CiteEnginesList.h b/src/CiteEnginesList.h new file mode 100644 index 0000000000..0e6d119128 --- /dev/null +++ b/src/CiteEnginesList.h @@ -0,0 +1,156 @@ +// -*- C++ -*- +/** + * \file CiteEnginesList.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Richard Heck + * \author Jürgen Spitzmüller + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef CITEENGINESLIST_H +#define CITEENGINESLIST_H + +#include +#include + +namespace lyx { + +/** + * This class represents a particular LyX "cite engine", which defines the features + * of a particular citation backend such as natbib or biblatex. In that sense, it is more like + * a LaTeX package, where a layout file corresponds to a LaTeX class. + * + * In general, a given cite engine can be used with any document class. That said, + * one cite engine may `require' another, or it may `exclude' some other cite engine. + * The requires and excludes are given in comments within the cite engine file, + * which must begin roughly so: + * # \DeclareLyXCiteEngine[natbib.sty]{Natbib} + * # DescriptionBegin + * # Loads the LaTeX package natbib, a citation engine. Natbib supports + * # both author-year and numerical styles for citations, automatic sorting + * # and merging of numerical citations, annotations, capitalization of the + * # `van' part of author names, shortened and full author lists, and more. + * # DescriptionEnd + * # Excludes: basic | jurabib + * The description might be used in the gui to give information to the user. The + * Requires and Excludes lines are read by the configuration script + * and written to a file citeengines.lst in the user configuration directory. + * That file is then read on startup to populate the CiteEnginesList, below. + * + * Engines can also be "provided" or "excluded" by document classes, using + * the ProvidesEngine and ExcludesEngine tags. + */ + +class LyXCiteEngine { +public: + /// + LyXCiteEngine(std::string const & name, std::string const & id, + std::vector const & enginetypes, + std::string const & description, + std::vector const & packagelist, + std::vector const & requires, + std::vector const & excludes); + /// whether the required packages are available + bool isAvailable() const; + /// the missing prerequisites, if any + std::vector prerequisites() const; + /// + std::string const & getName() const { return name_; } + /// + std::string const & getID() const { return id_; } + /// + std::string const & getFilename() const { return filename_; } + /// + std::vector const & getEngineType() const { return engine_types_; } + /// + std::string const & getDescription() const { return description_; } + /// + std::vector const & getPackageList() const + { return package_list_; } + /// + std::vector const & getRequiredEngines() const + { return required_engines_; } + /// Engines this one excludes: the list should be treated disjunctively + std::vector const & getExcludedEngines() const + { return excluded_engines_; } + /// \return true if the engine is compatible with this one, i.e., + /// it does not exclude us and we do not exclude it. + /// this will also return true if cename is unknown and we do not + /// exclude it, since in that case we cannot check its exclusions. + bool isCompatible(std::string const & cename) const; + /// + static bool areCompatible(std::string const & eng1, std::string const & eng2); +private: + /// what appears in the ui + std::string name_; + /// the engine's unique identifier + /// at present, this is the filename, without the extension + std::string id_; + /// the filename + std::string filename_; + /// the engine type(s) + std::vector engine_types_; + /// a short description for use in the ui + std::string description_; + /// the LaTeX packages on which this depends, if any + std::vector package_list_; + /// Engines this one requires: at least one + std::vector required_engines_; + /// Engines this one excludes: none of these + std::vector excluded_engines_; + // these are mutable because they are used to cache the results + // or an otherwise const operation. + /// + mutable bool checked_; + /// + mutable bool available_; + /// + mutable std::vector prerequisites_; +}; + +typedef std::vector LyXCiteEnginesList; + +/** + * The CiteEnginesList represents the various LyXCiteEngine's that are available at + * present. + */ +class CiteEnginesList { +public: + /// + CiteEnginesList() {} + /// reads the engines from a file generated by configure.py + bool read(); + /// + LyXCiteEnginesList::const_iterator begin() const; + /// + LyXCiteEnginesList::iterator begin(); + /// + LyXCiteEnginesList::const_iterator end() const; + /// + LyXCiteEnginesList::iterator end(); + /// + bool empty() const { return englist_.empty(); } + /// Returns a pointer to the LyXCiteEngine with filename str. + /// Returns a null pointer if no such engine is found. + LyXCiteEngine const * operator[](std::string const & str) const; + /// + LyXCiteEngine * operator[](std::string const & str); + private: + /// noncopyable + CiteEnginesList(CiteEnginesList const &); + /// + void operator=(CiteEnginesList const &); + /// add an engine to the list + void addCiteEngine(std::string const &, std::string const &, + std::vector const &, std::string const &, std::vector const &, + std::vector const &, std::vector const &); + /// + std::vector englist_; +}; + +extern CiteEnginesList theCiteEnginesList; +} +#endif diff --git a/src/LyX.cpp b/src/LyX.cpp index b10b60b122..2ba217cac9 100644 --- a/src/LyX.cpp +++ b/src/LyX.cpp @@ -22,6 +22,7 @@ #include "Buffer.h" #include "BufferList.h" #include "CmdDef.h" +#include "CiteEnginesList.h" #include "ColorSet.h" #include "ConverterCache.h" #include "Converter.h" @@ -934,8 +935,10 @@ bool LyX::init() LYXERR(Debug::INIT, "Reading layouts..."); // Load the layouts LayoutFileList::get().read(); - //...and the modules + //... the modules theModuleList.read(); + //... and the cite engines + theCiteEnginesList.read(); // read keymap and ui files in batch mode as well // because InsetInfo needs to know these to produce @@ -1019,7 +1022,8 @@ bool LyX::queryUserLyXDir(bool explicit_userdir) return configFileNeedsUpdate("lyxrc.defaults") || configFileNeedsUpdate("lyxmodules.lst") || configFileNeedsUpdate("textclass.lst") - || configFileNeedsUpdate("packages.lst"); + || configFileNeedsUpdate("packages.lst") + || configFileNeedsUpdate("lyxciteengines.lst"); } first_start = !explicit_userdir; diff --git a/src/Makefile.am b/src/Makefile.am index f87d0290b3..4cb38b5736 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -108,6 +108,7 @@ SOURCEFILESCORE = \ Bullet.cpp \ Changes.cpp \ Chktex.cpp \ + CiteEnginesList.cpp \ CmdDef.cpp \ Color.cpp \ ConverterCache.cpp \ @@ -201,6 +202,7 @@ HEADERFILESCORE = \ BufferView.h \ Bullet.h \ Citation.h \ + CiteEnginesList.h \ Changes.h \ Chktex.h \ CmdDef.h \ diff --git a/src/TextClass.cpp b/src/TextClass.cpp index 46a96e4815..d14ef2994a 100644 --- a/src/TextClass.cpp +++ b/src/TextClass.cpp @@ -17,6 +17,7 @@ #include "TextClass.h" #include "LayoutFile.h" +#include "CiteEnginesList.h" #include "Color.h" #include "Counters.h" #include "Floating.h" @@ -123,6 +124,8 @@ string translateReadType(TextClass::ReadType rt) return "input file"; case TextClass::MODULE: return "module file"; + case TextClass::CITE_ENGINE: + return "cite engine"; case TextClass::VALIDATION: return "validation"; } @@ -1585,6 +1588,7 @@ Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const DocumentClassPtr getDocumentClass( LayoutFile const & baseClass, LayoutModuleList const & modlist, + LayoutModuleList const & celist, bool const clone) { DocumentClassPtr doc_class = @@ -1623,6 +1627,42 @@ DocumentClassPtr getDocumentClass( frontend::Alert::warning(_("Read Error"), msg); } } + + LayoutModuleList::const_iterator cit = celist.begin(); + LayoutModuleList::const_iterator cen = celist.end(); + for (; cit != cen; ++cit) { + string const ceName = *cit; + LyXCiteEngine * ce = theCiteEnginesList[ceName]; + if (!ce) { + docstring const msg = + bformat(_("The cite engine %1$s has been requested by\n" + "this document but has not been found in the list of\n" + "available engines. If you recently installed it, you\n" + "probably need to reconfigure LyX.\n"), from_utf8(ceName)); + if (!clone) + frontend::Alert::warning(_("Cite Engine not available"), msg); + continue; + } + if (!ce->isAvailable() && !clone) { + docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t")); + docstring const msg = + bformat(_("The cite engine %1$s requires a package that is not\n" + "available in your LaTeX installation, or a converter that\n" + "you have not installed. LaTeX output may not be possible.\n" + "Missing prerequisites:\n" + "\t%2$s\n" + "See section 3.1.2.3 (Modules) of the User's Guide for more information."), + from_utf8(ceName), prereqs); + frontend::Alert::warning(_("Package not available"), msg, true); + } + FileName layout_file = libFileSearch("citeengines", ce->getFilename()); + if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) { + docstring const msg = + bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName)); + frontend::Alert::warning(_("Read Error"), msg); + } + } + return doc_class; } diff --git a/src/TextClass.h b/src/TextClass.h index 22d426c86d..d082e10258 100644 --- a/src/TextClass.h +++ b/src/TextClass.h @@ -151,6 +151,7 @@ public: BASECLASS, //>This is a base class, i.e., top-level layout file MERGE, //>This is a file included in a layout file MODULE, //>This is a layout module + CITE_ENGINE, //>This is a cite engine VALIDATION //>We're just validating }; /// return values for read() @@ -498,6 +499,7 @@ private: /// The only way to make a DocumentClass is to call this function. friend DocumentClassPtr getDocumentClass(LayoutFile const &, LayoutModuleList const &, + LayoutModuleList const &, bool const clone); }; @@ -508,6 +510,7 @@ private: /// on the CutStack. DocumentClassPtr getDocumentClass(LayoutFile const & baseClass, LayoutModuleList const & modlist, + LayoutModuleList const & celist, bool const clone = false); /// convert page sides option to text 1 or 2 diff --git a/src/tex2lyx/Makefile.am b/src/tex2lyx/Makefile.am index bf2d66d67f..a4a5fdc65f 100644 --- a/src/tex2lyx/Makefile.am +++ b/src/tex2lyx/Makefile.am @@ -83,6 +83,7 @@ updatetests: tex2lyx LINKED_FILES = \ ../Author.cpp \ + ../CiteEnginesList.cpp \ ../Color.cpp \ ../Counters.cpp \ ../Encoding.cpp \ diff --git a/src/tex2lyx/tex2lyx.cpp b/src/tex2lyx/tex2lyx.cpp index 7a6bbb0412..3f6fe73821 100644 --- a/src/tex2lyx/tex2lyx.cpp +++ b/src/tex2lyx/tex2lyx.cpp @@ -282,10 +282,11 @@ void initModules() for (; it != end; ++it) { string const module = it->getID(); LayoutModuleList m; + LayoutModuleList c; vector v; if (!addModule(module, baseClass, m, v)) continue; - modules[module] = getDocumentClass(baseClass, m); + modules[module] = getDocumentClass(baseClass, m, c); } init = false; }