From 7c28905b177b8366531915903ac903a0fff716de Mon Sep 17 00:00:00 2001 From: Richard Heck Date: Wed, 9 Jan 2008 18:51:02 +0000 Subject: [PATCH] This commit adds some new functionality to the modules stuff. In particular, it introduces the concept of "required" and "excluded" modules: A given module may require one of some list of modules, or it may be incompatible with some other modules. (Complex Boolean combinations are not supported!!) These facts can be noted in the module file, and the UI responds appropriately: Required and excluded modules are noted in the description, and the "Add" button is enabled only if at least one of the required modules has already been selected and no excluded module is selected. Getting this to work involved a fair bit of cleanup of the existing code---including ways Angus, I think, had already pointed out were required---and also involved changing the syntax of the headers of the module files, but in ways that are probably best anyway. None of the extant modules require any other modules, but the Theorem modules all exclude one another. (See the screenshot.) When I modularize the AMS classes---that is the next task---we'll have requires. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22456 a592a061-630c-0410-9148-cb99ea01b6c8 --- lib/configure.py | 76 +++++-- lib/doc/Customization.lyx | 70 ++++++- lib/layouts/braille.module | 6 +- lib/layouts/endnotes.module | 6 +- lib/layouts/foottoend.module | 6 +- lib/layouts/hanging.module | 6 +- lib/layouts/linguistics.module | 6 +- lib/layouts/logicalmkup.module | 5 +- lib/layouts/theorems-ams-withinsec.module | 8 +- lib/layouts/theorems-ams.module | 8 +- lib/layouts/theorems-std.module | 7 +- lib/layouts/theorems-withinchap.module | 7 +- lib/layouts/theorems-withinsec.module | 7 +- src/BufferParams.cpp | 2 +- src/ModuleList.cpp | 48 +++-- src/ModuleList.h | 36 +++- src/frontends/qt4/GuiDocument.cpp | 230 +++++++++++++++++----- src/frontends/qt4/GuiDocument.h | 26 ++- src/frontends/qt4/GuiSelectionManager.cpp | 38 +++- src/frontends/qt4/GuiSelectionManager.h | 35 +++- 20 files changed, 504 insertions(+), 129 deletions(-) diff --git a/lib/configure.py b/lib/configure.py index 62f32751e6..e08032af81 100644 --- a/lib/configure.py +++ b/lib/configure.py @@ -730,35 +730,73 @@ def checkModulesConfig(): tx.close() print '\tdone' + def processModuleFile(file, bool_docbook, bool_linuxdoc): ''' process module file and get a line of result - Declare lines look like this: - \DeclareLyXModule[LaTeX Packages]{Description}{ModuleName}... + The top of a module file should look like this: + #\DeclareLyXModule[LaTeX Packages]{ModuleName} + #BeginDescription + #...body of description... + #EndDescription + #Requires: [list of required modules] + #Excludes: [list of excluded modules] + The last two lines are optional We expect output: - "ModuleName" "filename" "Description" "Packages" - " + "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" ''' - p = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*)\])?{(.*)}{(.*)}') - for line in open(file).readlines(): - res = p.search(line) - if res != None: - (packages, desc, modname) = res.groups() - #check availability...need to add that - if packages == None: - packages = "" - else: - pkgs = [s.strip() for s in packages.split(",")] - packages = ",".join(pkgs) + p = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}') + r = re.compile(r'#+\s*Requires: (.*)') + x = re.compile(r'#+\s*Excludes: (.*)') + b = re.compile(r'#+\s*DescriptionBegin\s*$') + e = re.compile(r'#+\s*DescriptionEnd\s*$') - filename = file.split(os.sep)[-1] - return '"%s" "%s" "%s" "%s"\n' % (modname, filename, desc, packages) + modname = desc = pkgs = req = excl = "" + readingDescription = False + descLines = [] + + for line in open(file).readlines(): + if readingDescription: + res = e.search(line) + if res != None: + readingDescription = False + desc = " ".join(descLines) + continue + descLines.append(line[1:].strip()) + continue + res = b.search(line) + if res != None: + readingDescription = True + continue + res = p.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) + + filename = file.split(os.sep)[-1] + continue + res = r.search(line) + if res != None: + req = res.group(1) + tmp = [s.strip() for s in req.split("|")] + req = "|".join(tmp) + continue + res = x.search(line) + if res != None: + excl = res.group(1) + tmp = [s.strip() for s in excl.split("|")] + excl = "|".join(tmp) + continue + if modname != "": + return '"%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, desc, pkgs, req, excl) print "Module file without \DeclareLyXModule line. " sys.exit(2) - - def checkTeXAllowSpaces(): ''' Let's check whether spaces are allowed in TeX file names ''' tex_allows_spaces = 'false' diff --git a/lib/doc/Customization.lyx b/lib/doc/Customization.lyx index ef21ba39f7..cfa77fd0db 100644 --- a/lib/doc/Customization.lyx +++ b/lib/doc/Customization.lyx @@ -5862,22 +5862,82 @@ A module must begin with a line like the following: \begin_layout LyX-Code # \backslash -DeclareLyXModule[endnotes.sty]{Adds an endnote inset.}{Endnotes} +DeclareLyXModule[endnotes.sty]{Endnotes} \end_layout \begin_layout Standard The argument in square brackets is optional: It declares any LaTeX packages on which the module depends. - The two mandatory arguments, in curly brackets, are a short description - of the module and the name of the module, as they should appear in + The mandatory argument, in curly brackets, is the name of the module, as + it should appear in \family sans Document\SpecialChar \menuseparator Settings \family default . + LyX uses the name to identify the module, so it should be unique. +\end_layout + +\begin_layout Standard +The module declaration should then be followed by lines like the following: +\end_layout + +\begin_layout LyX-Code +#DescriptionBegin +\end_layout + +\begin_layout LyX-Code +#Adds an endnote command, in addition to footnotes. \end_layout +\begin_layout LyX-Code +#You will need to add +\backslash +theendnotes in ERT where you +\end_layout + +\begin_layout LyX-Code +#want the endnotes to appear. + +\end_layout + +\begin_layout LyX-Code +#DescriptionEnd +\end_layout + +\begin_layout LyX-Code +#Requires: Some Module | Some Other Module +\end_layout + +\begin_layout LyX-Code +#Excludes: Bad Module +\end_layout + +\begin_layout Standard +The description is used in +\family sans +Document\SpecialChar \menuseparator +Settings +\family default + to provide the user with information about what the module does. + The Requires is used to identify other modules with which this one must + be used; the Excludes line is used to identify modules with which this + one may not be used. + Both are optional, and, as shown, multiple modules should be separated + with the pipe symbol: |. + Note that the required modules are treated disjunctively: +\emph on +at least one +\emph default + of the required modules must be used. + Similarly, +\emph on +no +\emph default + excluded module may be used. +\end_layout + \begin_layout Standard After creating a new module, you will need to reconfigure and then restart LyX for it to appear in the menu. @@ -5901,8 +5961,8 @@ OK It is strongly recommended that you save your work before doing so \emph default . - In fact, it is strongly recommended that you not attempt to create or edit - modules while simultaneously working on documents. + In fact, it is strongly recommended that you not attempt to edit modules + while simultaneously working on documents. Though of course the developers strive to keep LyX stable in such situations, syntax errors and the like in your module file could cause strange behavior. \end_layout diff --git a/lib/layouts/braille.module b/lib/layouts/braille.module index 193eb96f16..6e99ec2af7 100644 --- a/lib/layouts/braille.module +++ b/lib/layouts/braille.module @@ -1,5 +1,7 @@ -#\DeclareLyXModule[braille.sty]{Defines an environment to typeset Braille.}{Braille} - +#\DeclareLyXModule[braille.sty]{Braille} +#DescriptionBegin +#Defines an environment to typeset Braille. +#DescriptionEnd # Author: Uwe Stöhr Format 6 diff --git a/lib/layouts/endnotes.module b/lib/layouts/endnotes.module index 9c00b97bd5..9f71e9613e 100644 --- a/lib/layouts/endnotes.module +++ b/lib/layouts/endnotes.module @@ -1,4 +1,8 @@ -#\DeclareLyXModule[endnotes.sty]{Adds an endnote command, in addition to footnotes. You will need to add \theendnotes in ERT where you want the endnotes to appear.}{Endnote} +#\DeclareLyXModule[endnotes.sty]{Endnote} +#DescriptionBegin +#Adds an endnote command, in addition to footnotes. You will need to add +#\theendnotes in ERT where you want the endnotes to appear. +#DescriptionEnd Format 6 diff --git a/lib/layouts/foottoend.module b/lib/layouts/foottoend.module index c775224431..28e8a1e866 100644 --- a/lib/layouts/foottoend.module +++ b/lib/layouts/foottoend.module @@ -1,4 +1,8 @@ -#\DeclareLyXModule[endnotes.sty]{Sets all footnotes as endnotes. You will need to add \theendnotes in ERT where you want the endnotes to appear.}{Foot to End} +#\DeclareLyXModule[endnotes.sty]{Foot to End} +#DescriptionBegin +#Sets all footnotes as endnotes. You will need to add \theendnotes +#in ERT where you want the endnotes to appear. +#DescriptionEnd Format 6 diff --git a/lib/layouts/hanging.module b/lib/layouts/hanging.module index 3ac84748d5..c6c6993efe 100644 --- a/lib/layouts/hanging.module +++ b/lib/layouts/hanging.module @@ -1,4 +1,8 @@ -#\DeclareLyXModule{Adds an environment for hanging paragraphs.}{Hanging} +#\DeclareLyXModule{Hanging} +#DescriptionBegin +#Adds an environment for hanging paragraphs. +#DescriptionEnd + #Author: Richard Heck #Hanging paragraph code adapted from hanging.sty, available at: diff --git a/lib/layouts/linguistics.module b/lib/layouts/linguistics.module index 9d04931d44..51b379f2b2 100644 --- a/lib/layouts/linguistics.module +++ b/lib/layouts/linguistics.module @@ -1,4 +1,8 @@ -#\DeclareLyXModule[covington.sty]{Defines some special environments useful for linguistics (numbered examples, glosses, semantic markup).}{Linguistics} +#\DeclareLyXModule[covington.sty]{Linguistics} +#DescriptionBegin +#Defines some special environments useful for linguistics (numbered examples, +#glosses, semantic markup). +#DescriptionEnd # Author: Jürgen Spitzmüller diff --git a/lib/layouts/logicalmkup.module b/lib/layouts/logicalmkup.module index 1d4511e89a..4add152b7e 100644 --- a/lib/layouts/logicalmkup.module +++ b/lib/layouts/logicalmkup.module @@ -1,4 +1,7 @@ -#\DeclareLyXModule{Defines some character styles for logical markup: noun, emph, strong, and code.}{Logical Markup} +#\DeclareLyXModule{Logical Markup} +#DescriptionBegin +#Defines some character styles for logical markup: noun, emph, strong, and code. +#DescriptionEnd # Author : Martin vermeer diff --git a/lib/layouts/theorems-ams-withinsec.module b/lib/layouts/theorems-ams-withinsec.module index f13a939201..60d42f8d0b 100644 --- a/lib/layouts/theorems-ams-withinsec.module +++ b/lib/layouts/theorems-ams-withinsec.module @@ -1,4 +1,10 @@ -#\DeclareLyXModule{Defines theorem environments and the proof environment for use with non-AMS classes, using the extended AMS machinery. The theorems are numbered within sections. NOTE: Only one of the theorem modules should be used at a time.}{Theorems (AMS, By Section)} +#\DeclareLyXModule{Theorems (AMS, By Section)} +#DescriptionBegin +#Defines theorem environments and the proof environment for use with +#non-AMS classes, using the extended AMS machinery. The theorems are +#numbered within sections. +#DescriptionEnd +#Excludes: Theorems (AMS) | Theorems | Theorems (By Chapter) | Theorems (By Section) # Author: Richard Heck # Adapted from amsdefs.inc and amsmaths.inc diff --git a/lib/layouts/theorems-ams.module b/lib/layouts/theorems-ams.module index c0b6f2d73f..8ab3621b2b 100644 --- a/lib/layouts/theorems-ams.module +++ b/lib/layouts/theorems-ams.module @@ -1,4 +1,10 @@ -#\DeclareLyXModule{Defines theorem environments and the proof environment for use with non-AMS classes, using the extended AMS machinery. The theorems are numbered consecutively throughout the document. NOTE: Only one of the theorem modules should be used at a time.}{Theorems (AMS)} +#\DeclareLyXModule{Theorems (AMS)} +#DescriptionBegin +#Defines theorem environments and the proof environment for use with +#non-AMS classes, using the extended AMS machinery. The theorems are +#numbered consecutively throughout the document. +#DescriptionEnd +#Excludes: Theorems (AMS, By Section) | Theorems | Theorems (By Chapter) | Theorems (By Section) # Author: Richard Heck # Adapted from amsdefs.inc and amsmaths.inc diff --git a/lib/layouts/theorems-std.module b/lib/layouts/theorems-std.module index 4a481ef85d..43529111a5 100644 --- a/lib/layouts/theorems-std.module +++ b/lib/layouts/theorems-std.module @@ -1,4 +1,9 @@ -#\DeclareLyXModule{Defines some theorem environments for use with non-AMS classes. The theorems are numbered consecutively throughout the document. NOTE: Only one of the theorem modules should be used at a time.}{Theorems} +#\DeclareLyXModule{Theorems} +#DescriptionBegin +#Defines some theorem environments for use with non-AMS classes. The theorems are +#numbered consecutively throughout the document. +#DescriptionEnd +#Excludes: Theorems (AMS, By Section) | Theorems (AMS) | Theorems (By Chapter) | Theorems (By Section) # Author: Richard Heck # Adapted from amsmaths.inc diff --git a/lib/layouts/theorems-withinchap.module b/lib/layouts/theorems-withinchap.module index 8a298138aa..e69eab7a03 100644 --- a/lib/layouts/theorems-withinchap.module +++ b/lib/layouts/theorems-withinchap.module @@ -1,4 +1,9 @@ -#\DeclareLyXModule{Defines some theorem environments for use with non-AMS classes. The theorems are numbered within chapters of the document. NOTES: This module should therefore be used only with document classes that define a chapter environment. Only one of the theorem modules should be used at a time.}{Theorems (By Chapter)} +#\DeclareLyXModule{Theorems (By Chapter)} +#DescriptionBegin +#Defines some theorem environments for use with non-AMS classes. The theorems are +#numbered by chapter. +#DescriptionEnd +#Excludes: Theorems (AMS, By Section) | Theorems (AMS) | Theorems | Theorems (By Section) # Author: Richard Heck # Adapted from amsmaths.inc diff --git a/lib/layouts/theorems-withinsec.module b/lib/layouts/theorems-withinsec.module index 2c8486424d..f0c757c89d 100644 --- a/lib/layouts/theorems-withinsec.module +++ b/lib/layouts/theorems-withinsec.module @@ -1,4 +1,9 @@ -#\DeclareLyXModule{Defines some theorem environments for use with non-AMS classes. The theorems are numbered within sections of the document. NOTE: Only one of the theorem modules should be used at a time.}{Theorems (By Section)} +#\DeclareLyXModule{Theorems (By Section)} +#DescriptionBegin +#Defines some theorem environments for use with non-AMS classes. The theorems are +#numbered by section. +#DescriptionEnd +#Excludes: Theorems (AMS, By Section) | Theorems (AMS) | Theorems (By Chapter) | Theorems # Author: Richard Heck # Adapted from amsmaths.inc diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index bb77a83814..9e2ff3c57e 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -1421,7 +1421,7 @@ void BufferParams::makeTextClass() "may not be possible.\n"), from_utf8(modName)); frontend::Alert::warning(_("Package not available"), msg); } - FileName layout_file = libFileSearch("layouts", lm->filename); + FileName layout_file = libFileSearch("layouts", lm->getFilename()); textClass_->read(layout_file, TextClass::MODULE); } } diff --git a/src/ModuleList.cpp b/src/ModuleList.cpp index d1ff3bee0a..bd035f4966 100644 --- a/src/ModuleList.cpp +++ b/src/ModuleList.cpp @@ -35,8 +35,11 @@ ModuleList moduleList; LyXModule::LyXModule(string const & n, string const & f, - string const & d, vector const & p) : - name(n), filename(f), description(d), packageList(p), checked(false) + string const & d, vector const & p, + vector const & r, vector const & e): + name(n), filename(f), description(d), + packageList(p), requiredModules(r), excludedModules(e), + checked(false) {} @@ -64,7 +67,7 @@ class ModuleSorter public: int operator()(LyXModule const & lm1, LyXModule const & lm2) const { - return lm1.name < lm2.name; + return lm1.getName() < lm2.getName(); } }; @@ -123,17 +126,37 @@ bool ModuleList::load() //FIXME Add packages if (!lex.next()) break; - string packages = lex.getString(); - LYXERR(Debug::TCLASS, "Packages: " << packages); + string str = lex.getString(); + LYXERR(Debug::TCLASS, "Packages: " << str); vector pkgs; - while (!packages.empty()) { + while (!str.empty()) { string p; - packages = split(packages, 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 - // modName, fname, desc, and pkgs - addLayoutModule(modName, fname, desc, pkgs); + // modName, fname, desc, pkgs, req, and exc + addLayoutModule(modName, fname, desc, pkgs, req, exc); } // end switch } //end while @@ -147,9 +170,10 @@ bool ModuleList::load() void ModuleList::addLayoutModule(string const & moduleName, string const & filename, string const & description, - vector const & pkgs) + vector const & pkgs, vector const & req, + vector const & exc) { - LyXModule lm(moduleName, filename, description, pkgs); + LyXModule lm(moduleName, filename, description, pkgs, req, exc); modlist_.push_back(lm); } @@ -182,7 +206,7 @@ LyXModule * ModuleList::operator[](string const & str) { LyXModuleList::iterator it = modlist_.begin(); for (; it != modlist_.end(); ++it) - if (it->name == str) { + if (it->getName() == str) { LyXModule & mod = *it; return &mod; } diff --git a/src/ModuleList.h b/src/ModuleList.h index 5c22db3036..e0e52f72ee 100644 --- a/src/ModuleList.h +++ b/src/ModuleList.h @@ -29,9 +29,28 @@ class LyXModule { public: /// LyXModule(std::string const & n, std::string const & f, - std::string const & d, std::vector const & p); + std::string const & d, std::vector const & p, + std::vector const & r, + std::vector const & e); /// whether the required packages are available bool isAvailable(); + /// + std::string const & getName() const { return name; } + /// + std::string const & getFilename() const { return filename; } + /// + std::string const & getDescription() const { return description; } + /// + std::vector const & getPackageList() const + { return packageList; } + /// + std::vector const & getRequiredModules() const + { return requiredModules; } + /// Modules this one excludes: the list should be treated disjunctively + std::vector const & getExcludedModules() const + { return excludedModules; } + +private: /// what appears in the ui std::string name; /// the filename, without any path @@ -40,7 +59,10 @@ public: std::string description; /// the LaTeX packages on which this depends, if any (not implemented) std::vector packageList; -private: + /// Modules this one requires: at least one + std::vector requiredModules; + /// Modules this one excludes: none of these + std::vector excludedModules; /// bool checked; /// @@ -59,10 +81,6 @@ public: ModuleList() {} /// reads the modules from a file generated by configure.py bool load(); - /// add a module to the list - void addLayoutModule(std::string const & name, - std::string const & filename, std::string const & description, - std::vector const & packages); /// LyXModuleList::const_iterator begin() const; /// @@ -79,8 +97,12 @@ public: private: /// noncopyable ModuleList(ModuleList const &); + /// void operator=(ModuleList const &); - + /// add a module to the list + void addLayoutModule(std::string const &, std::string const &, + std::string const &, std::vector const &, + std::vector const &, std::vector const &); /// std::vector modlist_; }; diff --git a/src/frontends/qt4/GuiDocument.cpp b/src/frontends/qt4/GuiDocument.cpp index ad08b559da..4586ba1972 100644 --- a/src/frontends/qt4/GuiDocument.cpp +++ b/src/frontends/qt4/GuiDocument.cpp @@ -134,6 +134,122 @@ vector > pagestyles; namespace lyx { namespace frontend { +namespace { + vector getRequiredList(string const & modName) + { + LyXModule const * const mod = moduleList[modName]; + if (!mod) + return vector(); //empty such thing + return mod->getRequiredModules(); + } + + + vector getExcludedList(string const & modName) + { + LyXModule const * const mod = moduleList[modName]; + if (!mod) + return vector(); //empty such thing + return mod->getExcludedModules(); + } + + + docstring getModuleDescription(string const & modName) + { + LyXModule const * const mod = moduleList[modName]; + if (!mod) + return _("Module not found!"); + return from_ascii(mod->getDescription()); + } + + + vector getPackageList(string const & modName) + { + LyXModule const * const mod = moduleList[modName]; + if (!mod) + return vector(); //empty such thing + return mod->getPackageList(); + } + + + bool isModuleAvailable(string const & modName) + { + LyXModule * mod = moduleList[modName]; + if (!mod) + return false; + return mod->isAvailable(); + } +} //anonymous namespace + + +ModuleSelMan::ModuleSelMan( + QListView * availableLV, + QListView * selectedLV, + QPushButton * addPB, + QPushButton * delPB, + QPushButton * upPB, + QPushButton * downPB, + QStringListModel * availableModel, + QStringListModel * selectedModel) : +GuiSelectionManager(availableLV, selectedLV, addPB, delPB, + upPB, downPB, availableModel, selectedModel) +{} + + +void ModuleSelMan::updateAddPB() +{ + int const arows = availableLV->model()->rowCount(); + QModelIndexList const availSels = + availableLV->selectionModel()->selectedIndexes(); + if (arows == 0 || availSels.isEmpty() || isSelected(availSels.first())) { + addPB->setEnabled(false); + return; + } + + QModelIndex const & idx = availableLV->selectionModel()->currentIndex(); + string const modName = fromqstr(idx.data().toString()); + vector reqs = getRequiredList(modName); + vector excl = getExcludedList(modName); + + if (reqs.empty() && excl.empty()) { + addPB->setEnabled(true); + return; + } + + QStringList const & qsl = selectedModel->stringList(); + + //Check whether some required module is available + if (!reqs.empty()) { + bool foundOne = false; + vector::const_iterator it = reqs.begin(); + vector::const_iterator end = reqs.end(); + for (; it != end; ++it) { + if (qsl.contains(toqstr(*it))) { + foundOne = true; + break; + } + } + if (!foundOne) { + addPB->setEnabled(false); + return; + } + } + + //Check whether any excluded module is being used + if (!excl.empty()) { + vector::const_iterator it = excl.begin(); + vector::const_iterator end = excl.end(); + for (; it != end; ++it) { + if (qsl.contains(toqstr(*it))) { + addPB->setEnabled(false); + return; + } + } + } + + addPB->setEnabled(true); +} + + ///////////////////////////////////////////////////////////////////// // // PreambleModule @@ -566,7 +682,7 @@ GuiDocument::GuiDocument(GuiView & lv) this, SLOT(classChanged())); selectionManager = - new GuiSelectionManager(latexModule->availableLV, latexModule->selectedLV, + new ModuleSelMan(latexModule->availableLV, latexModule->selectedLV, latexModule->addPB, latexModule->deletePB, latexModule->upPB, latexModule->downPB, availableModel(), selectedModel()); @@ -920,46 +1036,77 @@ void GuiDocument::classChanged() } +namespace { + //This is an insanely complicated attempt to make this sort of thing + //work with RTL languages. + docstring formatStrVec(vector const & v, docstring const & s) + { + //this mess formats the list as "v[0], v[1], ..., [s] v[n]" + int const vSize = v.size(); + if (v.size() == 0) + return docstring(); + else if (v.size() == 1) + return from_ascii(v[0]); + else if (v.size() == 2) { + docstring retval = _("%1$s and %2$s"); + retval = subst(retval, _("and"), s); + return bformat(retval, from_ascii(v[0]), from_ascii(v[1])); + } + //The idea here is to format all but the last two items... + docstring t2 = _("%1$s, %2$s"); + docstring retval = from_ascii(v[0]); + for (int i = 1; i < vSize - 2; ++i) + retval = bformat(t2, retval, from_ascii(v[i])); + //...and then to plug them, and the last two, into this schema + docstring t = _("%1$s, %2$s, and %3$s"); + t = subst(t, _("and"), s); + return bformat(t, retval, from_ascii(v[vSize - 2]), from_ascii(v[vSize - 1])); + } +} + + void GuiDocument::updateModuleInfo() { selectionManager->update(); //Module description QListView const * const lv = selectionManager->selectedFocused() ? latexModule->selectedLV : - latexModule->availableLV; + latexModule->availableLV; if (lv->selectionModel()->selectedIndexes().isEmpty()) latexModule->infoML->document()->clear(); else { - QModelIndex const idx = lv->selectionModel()->currentIndex(); + QModelIndex const & idx = lv->selectionModel()->currentIndex(); string const modName = fromqstr(idx.data().toString()); - string desc = getModuleDescription(modName); + docstring desc = getModuleDescription(modName); + vector pkgList = getPackageList(modName); - string pkgdesc; - //this mess formats the package list as "pkg1, pkg2, and pkg3" - int const pkgListSize = pkgList.size(); - for (int i = 0; i < pkgListSize; ++i) { - if (i == 1) { - if (i == pkgListSize - 1) //last element - pkgdesc += " and "; - else - pkgdesc += ", "; - } else if (i > 1) { - if (i == pkgListSize - 1) //last element - pkgdesc += ", and "; - else - pkgdesc += ", "; - } - pkgdesc += pkgList[i]; - } + docstring pkgdesc = formatStrVec(pkgList, _("and")); if (!pkgdesc.empty()) { if (!desc.empty()) - desc += " "; - desc += ("Requires " + pkgdesc + "."); + desc += "\n"; + desc += bformat(_("Package(s) required: %1$s."), pkgdesc); } + + pkgList = getRequiredList(modName); + pkgdesc = formatStrVec(pkgList, _("or")); + if (!pkgdesc.empty()) { + if (!desc.empty()) + desc += "\n"; + desc += bformat(_("Module required: %1$s."), pkgdesc); + } + + pkgList = getExcludedList(modName); + pkgdesc = formatStrVec(pkgList, _( "and")); + if (!pkgdesc.empty()) { + if (!desc.empty()) + desc += "\n"; + desc += bformat(_("Modules excluded: %1$s."), pkgdesc); + } + if (!isModuleAvailable(modName)) { if (!desc.empty()) desc += "\n"; - desc += "WARNING: Some packages are unavailable!"; + desc += _("WARNING: Some packages are unavailable!"); } latexModule->infoML->document()->setPlainText(toqstr(desc)); } @@ -1631,6 +1778,10 @@ void GuiDocument::updateContents() it = selMods.begin(); for (; it != selMods.end(); ++it) strlist2.push_back(toqstr(*it)); + //FIXME It'd be nice to make sure here that the selected + //modules are consistent: That required modules are actually + //selected, and that we don't have conflicts. If so, we could + //at least pop up a warning. selected_model_.setStringList(strlist2); updateParams(bp_); @@ -1680,7 +1831,7 @@ BufferId GuiDocument::id() const } -vector GuiDocument::getModuleNames() +vector const & GuiDocument::getModuleNames() { return moduleNames_; } @@ -1692,33 +1843,6 @@ vector const & GuiDocument::getSelectedModules() } -string GuiDocument::getModuleDescription(string const & modName) const -{ - LyXModule const * const mod = moduleList[modName]; - if (!mod) - return string("Module not found!"); - return mod->description; -} - - -vector GuiDocument::getPackageList(string const & modName) const -{ - LyXModule const * const mod = moduleList[modName]; - if (!mod) - return vector(); //empty such thing - return mod->packageList; -} - - -bool GuiDocument::isModuleAvailable(string const & modName) const -{ - LyXModule * mod = moduleList[modName]; - if (!mod) - return false; - return mod->isAvailable(); -} - - TextClass const & GuiDocument::textClass() const { return textclasslist[bp_.getBaseClass()]; @@ -1840,7 +1964,7 @@ void GuiDocument::loadModuleNames () moduleNames_.clear(); LyXModuleList::const_iterator it = moduleList.begin(); for (; it != moduleList.end(); ++it) - moduleNames_.push_back(it->name); + moduleNames_.push_back(it->getName()); if (!moduleNames_.empty()) sort(moduleNames_.begin(), moduleNames_.end()); } diff --git a/src/frontends/qt4/GuiDocument.h b/src/frontends/qt4/GuiDocument.h index 90a93f8314..a0fd852563 100644 --- a/src/frontends/qt4/GuiDocument.h +++ b/src/frontends/qt4/GuiDocument.h @@ -67,6 +67,24 @@ public: }; +class ModuleSelMan : public GuiSelectionManager +{ +public: + ModuleSelMan( + QListView * availableLV, + QListView * selectedLV, + QPushButton * addPB, + QPushButton * delPB, + QPushButton * upPB, + QPushButton * downPB, + QStringListModel * availableModel, + QStringListModel * selectedModel); +private: + /// + virtual void updateAddPB(); +}; + + class GuiDocument : public GuiDialog, public Ui::DocumentUi { Q_OBJECT @@ -178,16 +196,10 @@ protected: /// BufferId id() const; /// List of available modules - std::vector getModuleNames(); + std::vector const & getModuleNames(); /// Modules in use in current buffer std::vector const & getSelectedModules(); /// - std::string getModuleDescription(std::string const & modName) const; - /// - std::vector getPackageList(std::string const & modName) const; - /// - bool isModuleAvailable(std::string const & modName) const; - /// void setLanguage() const; /// void saveAsDefault() const; diff --git a/src/frontends/qt4/GuiSelectionManager.cpp b/src/frontends/qt4/GuiSelectionManager.cpp index 7dcf33fb03..88946f501e 100644 --- a/src/frontends/qt4/GuiSelectionManager.cpp +++ b/src/frontends/qt4/GuiSelectionManager.cpp @@ -68,6 +68,15 @@ GuiSelectionManager::GuiSelectionManager( void GuiSelectionManager::update() +{ + updateAddPB(); + updateDelPB(); + updateDownPB(); + updateUpPB(); +} + + +void GuiSelectionManager::updateAddPB() { int const arows = availableLV->model()->rowCount(); QModelIndexList const availSels = @@ -75,17 +84,38 @@ void GuiSelectionManager::update() addPB->setEnabled(arows > 0 && !availSels.isEmpty() && !isSelected(availSels.first())); - +} + + +void GuiSelectionManager::updateDelPB() +{ int const srows = selectedLV->model()->rowCount(); QModelIndexList const selSels = selectedLV->selectionModel()->selectedIndexes(); int const sel_nr = selSels.empty() ? -1 : selSels.first().row(); deletePB->setEnabled(sel_nr >= 0); - upPB->setEnabled(sel_nr > 0); - downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1); } +void GuiSelectionManager::updateDownPB() +{ + int const srows = selectedLV->model()->rowCount(); + QModelIndexList const selSels = + selectedLV->selectionModel()->selectedIndexes(); + int const sel_nr = selSels.empty() ? -1 : selSels.first().row(); + upPB->setEnabled(sel_nr > 0); +} + + +void GuiSelectionManager::updateUpPB() +{ + int const srows = selectedLV->model()->rowCount(); + QModelIndexList const selSels = + selectedLV->selectionModel()->selectedIndexes(); + int const sel_nr = selSels.empty() ? -1 : selSels.first().row(); + downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1); +} + bool GuiSelectionManager::isSelected(const QModelIndex & idx) { QString const str = idx.data().toString(); @@ -211,7 +241,7 @@ void GuiSelectionManager::availableLV_clicked(const QModelIndex &) void GuiSelectionManager::availableLV_doubleClicked(const QModelIndex & idx) { - if (isSelected(idx)) + if (isSelected(idx) || !addPB->isEnabled()) return; if (idx.isValid()) diff --git a/src/frontends/qt4/GuiSelectionManager.h b/src/frontends/qt4/GuiSelectionManager.h index ac4671be45..fa176327e7 100644 --- a/src/frontends/qt4/GuiSelectionManager.h +++ b/src/frontends/qt4/GuiSelectionManager.h @@ -50,6 +50,8 @@ public: /// Sets the state of the various push buttons, depending upon the /// state of the widgets. (E.g., "delete" is enabled only if the /// selection is non-empty.) + /// Note: this is separated out into updateAddPB(), etc, below, + /// for easy over-riding of these functions. void update(); /// Not strictly a matter of focus, which may be elsewhere, but @@ -79,6 +81,22 @@ protected: ///Given a QModelIndex from availableLV, determines whether it has ///been selected (i.e., is also in selectedLV). bool isSelected(const QModelIndex & idx); + /// + QListView * availableLV; + /// + QListView * selectedLV; + /// + QPushButton * addPB; + /// + QPushButton * deletePB; + /// + QPushButton * upPB; + /// + QPushButton * downPB; + /// + QStringListModel * availableModel; + /// + QStringListModel * selectedModel; protected Q_SLOTS: /// @@ -103,15 +121,14 @@ protected Q_SLOTS: bool eventFilter(QObject *, QEvent *); private: - QListView * availableLV; - QListView * selectedLV; - QPushButton * addPB; - QPushButton * deletePB; - QPushButton * upPB; - QPushButton * downPB; - QStringListModel * availableModel; - QStringListModel * selectedModel; - //Dialog::View * dialog; + /// + virtual void updateAddPB(); + /// + virtual void updateDelPB(); + /// + virtual void updateDownPB(); + /// + virtual void updateUpPB(); bool selectedHasFocus_; };