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
This commit is contained in:
Richard Heck 2008-01-09 18:51:02 +00:00
parent e975835530
commit 7c28905b17
20 changed files with 504 additions and 129 deletions

View File

@ -730,35 +730,73 @@ def checkModulesConfig():
tx.close() tx.close()
print '\tdone' print '\tdone'
def processModuleFile(file, bool_docbook, bool_linuxdoc): def processModuleFile(file, bool_docbook, bool_linuxdoc):
''' process module file and get a line of result ''' process module file and get a line of result
Declare lines look like this: The top of a module file should look like this:
\DeclareLyXModule[LaTeX Packages]{Description}{ModuleName}... #\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: We expect output:
"ModuleName" "filename" "Description" "Packages" "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes"
"
''' '''
p = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*)\])?{(.*)}{(.*)}') 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*$')
modname = desc = pkgs = req = excl = ""
readingDescription = False
descLines = []
for line in open(file).readlines(): 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) res = p.search(line)
if res != None: if res != None:
(packages, desc, modname) = res.groups() (pkgs, modname) = res.groups()
#check availability...need to add that if pkgs == None:
if packages == None: pkgs = ""
packages = ""
else: else:
pkgs = [s.strip() for s in packages.split(",")] tmp = [s.strip() for s in pkgs.split(",")]
packages = ",".join(pkgs) pkgs = ",".join(tmp)
filename = file.split(os.sep)[-1] filename = file.split(os.sep)[-1]
return '"%s" "%s" "%s" "%s"\n' % (modname, filename, desc, packages) 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. " print "Module file without \DeclareLyXModule line. "
sys.exit(2) sys.exit(2)
def checkTeXAllowSpaces(): def checkTeXAllowSpaces():
''' Let's check whether spaces are allowed in TeX file names ''' ''' Let's check whether spaces are allowed in TeX file names '''
tex_allows_spaces = 'false' tex_allows_spaces = 'false'

View File

@ -5862,20 +5862,80 @@ A module must begin with a line like the following:
\begin_layout LyX-Code \begin_layout LyX-Code
# #
\backslash \backslash
DeclareLyXModule[endnotes.sty]{Adds an endnote inset.}{Endnotes} DeclareLyXModule[endnotes.sty]{Endnotes}
\end_layout \end_layout
\begin_layout Standard \begin_layout Standard
The argument in square brackets is optional: It declares any LaTeX packages The argument in square brackets is optional: It declares any LaTeX packages
on which the module depends. on which the module depends.
The two mandatory arguments, in curly brackets, are a short description The mandatory argument, in curly brackets, is the name of the module, as
of the module and the name of the module, as they should appear in it should appear in
\family sans \family sans
Document\SpecialChar \menuseparator Document\SpecialChar \menuseparator
Settings Settings
\family default \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 \end_layout
\begin_layout Standard \begin_layout Standard
@ -5901,8 +5961,8 @@ OK
It is strongly recommended that you save your work before doing so It is strongly recommended that you save your work before doing so
\emph default \emph default
. .
In fact, it is strongly recommended that you not attempt to create or edit In fact, it is strongly recommended that you not attempt to edit modules
modules while simultaneously working on documents. while simultaneously working on documents.
Though of course the developers strive to keep LyX stable in such situations, 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. syntax errors and the like in your module file could cause strange behavior.
\end_layout \end_layout

View File

@ -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 <uwestoehr@web.de> # Author: Uwe Stöhr <uwestoehr@web.de>
Format 6 Format 6

View File

@ -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 Format 6

View File

@ -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 Format 6

View File

@ -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 #Author: Richard Heck
#Hanging paragraph code adapted from hanging.sty, available at: #Hanging paragraph code adapted from hanging.sty, available at:

View File

@ -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 <spitz@lyx.org> # Author: Jürgen Spitzmüller <spitz@lyx.org>

View File

@ -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 <martin.vermeer@hut.fi> # Author : Martin vermeer <martin.vermeer@hut.fi>

View File

@ -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 <rgheck@comcast.net> # Author: Richard Heck <rgheck@comcast.net>
# Adapted from amsdefs.inc and amsmaths.inc # Adapted from amsdefs.inc and amsmaths.inc

View File

@ -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 <rgheck@comcast.net> # Author: Richard Heck <rgheck@comcast.net>
# Adapted from amsdefs.inc and amsmaths.inc # Adapted from amsdefs.inc and amsmaths.inc

View File

@ -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 <rgheck@comcast.net> # Author: Richard Heck <rgheck@comcast.net>
# Adapted from amsmaths.inc # Adapted from amsmaths.inc

View File

@ -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 <rgheck@comcast.net> # Author: Richard Heck <rgheck@comcast.net>
# Adapted from amsmaths.inc # Adapted from amsmaths.inc

View File

@ -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 <rgheck@comcast.net> # Author: Richard Heck <rgheck@comcast.net>
# Adapted from amsmaths.inc # Adapted from amsmaths.inc

View File

@ -1421,7 +1421,7 @@ void BufferParams::makeTextClass()
"may not be possible.\n"), from_utf8(modName)); "may not be possible.\n"), from_utf8(modName));
frontend::Alert::warning(_("Package not available"), msg); 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); textClass_->read(layout_file, TextClass::MODULE);
} }
} }

View File

@ -35,8 +35,11 @@ ModuleList moduleList;
LyXModule::LyXModule(string const & n, string const & f, LyXModule::LyXModule(string const & n, string const & f,
string const & d, vector<string> const & p) : string const & d, vector<string> const & p,
name(n), filename(f), description(d), packageList(p), checked(false) vector<string> const & r, vector<string> const & e):
name(n), filename(f), description(d),
packageList(p), requiredModules(r), excludedModules(e),
checked(false)
{} {}
@ -64,7 +67,7 @@ class ModuleSorter
public: public:
int operator()(LyXModule const & lm1, LyXModule const & lm2) const 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 //FIXME Add packages
if (!lex.next()) if (!lex.next())
break; break;
string packages = lex.getString(); string str = lex.getString();
LYXERR(Debug::TCLASS, "Packages: " << packages); LYXERR(Debug::TCLASS, "Packages: " << str);
vector<string> pkgs; vector<string> pkgs;
while (!packages.empty()) { while (!str.empty()) {
string p; string p;
packages = split(packages, p, ','); str = split(str, p, ',');
pkgs.push_back(p); pkgs.push_back(p);
} }
if (!lex.next())
break;
str = lex.getString();
LYXERR(Debug::TCLASS, "Required: " << str);
vector<string> 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<string> exc;
while (!str.empty()) {
string p;
str = split(str, p, '|');
exc.push_back(p);
}
// This code is run when we have // This code is run when we have
// modName, fname, desc, and pkgs // modName, fname, desc, pkgs, req, and exc
addLayoutModule(modName, fname, desc, pkgs); addLayoutModule(modName, fname, desc, pkgs, req, exc);
} // end switch } // end switch
} //end while } //end while
@ -147,9 +170,10 @@ bool ModuleList::load()
void ModuleList::addLayoutModule(string const & moduleName, void ModuleList::addLayoutModule(string const & moduleName,
string const & filename, string const & description, string const & filename, string const & description,
vector<string> const & pkgs) vector<string> const & pkgs, vector<string> const & req,
vector<string> const & exc)
{ {
LyXModule lm(moduleName, filename, description, pkgs); LyXModule lm(moduleName, filename, description, pkgs, req, exc);
modlist_.push_back(lm); modlist_.push_back(lm);
} }
@ -182,7 +206,7 @@ LyXModule * ModuleList::operator[](string const & str)
{ {
LyXModuleList::iterator it = modlist_.begin(); LyXModuleList::iterator it = modlist_.begin();
for (; it != modlist_.end(); ++it) for (; it != modlist_.end(); ++it)
if (it->name == str) { if (it->getName() == str) {
LyXModule & mod = *it; LyXModule & mod = *it;
return &mod; return &mod;
} }

View File

@ -29,9 +29,28 @@ class LyXModule {
public: public:
/// ///
LyXModule(std::string const & n, std::string const & f, LyXModule(std::string const & n, std::string const & f,
std::string const & d, std::vector<std::string> const & p); std::string const & d, std::vector<std::string> const & p,
std::vector<std::string> const & r,
std::vector<std::string> const & e);
/// whether the required packages are available /// whether the required packages are available
bool isAvailable(); 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<std::string> const & getPackageList() const
{ return packageList; }
///
std::vector<std::string> const & getRequiredModules() const
{ return requiredModules; }
/// Modules this one excludes: the list should be treated disjunctively
std::vector<std::string> const & getExcludedModules() const
{ return excludedModules; }
private:
/// what appears in the ui /// what appears in the ui
std::string name; std::string name;
/// the filename, without any path /// the filename, without any path
@ -40,7 +59,10 @@ public:
std::string description; std::string description;
/// the LaTeX packages on which this depends, if any (not implemented) /// the LaTeX packages on which this depends, if any (not implemented)
std::vector<std::string> packageList; std::vector<std::string> packageList;
private: /// Modules this one requires: at least one
std::vector<std::string> requiredModules;
/// Modules this one excludes: none of these
std::vector<std::string> excludedModules;
/// ///
bool checked; bool checked;
/// ///
@ -59,10 +81,6 @@ public:
ModuleList() {} ModuleList() {}
/// reads the modules from a file generated by configure.py /// reads the modules from a file generated by configure.py
bool load(); bool load();
/// add a module to the list
void addLayoutModule(std::string const & name,
std::string const & filename, std::string const & description,
std::vector<std::string> const & packages);
/// ///
LyXModuleList::const_iterator begin() const; LyXModuleList::const_iterator begin() const;
/// ///
@ -79,8 +97,12 @@ public:
private: private:
/// noncopyable /// noncopyable
ModuleList(ModuleList const &); ModuleList(ModuleList const &);
///
void operator=(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<std::string> const &,
std::vector<std::string> const &, std::vector<std::string> const &);
/// ///
std::vector<LyXModule> modlist_; std::vector<LyXModule> modlist_;
}; };

View File

@ -134,6 +134,122 @@ vector<pair<string, lyx::docstring> > pagestyles;
namespace lyx { namespace lyx {
namespace frontend { namespace frontend {
namespace {
vector<string> getRequiredList(string const & modName)
{
LyXModule const * const mod = moduleList[modName];
if (!mod)
return vector<string>(); //empty such thing
return mod->getRequiredModules();
}
vector<string> getExcludedList(string const & modName)
{
LyXModule const * const mod = moduleList[modName];
if (!mod)
return vector<string>(); //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<string> getPackageList(string const & modName)
{
LyXModule const * const mod = moduleList[modName];
if (!mod)
return vector<string>(); //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<string> reqs = getRequiredList(modName);
vector<string> 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<string>::const_iterator it = reqs.begin();
vector<string>::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<string>::const_iterator it = excl.begin();
vector<string>::const_iterator end = excl.end();
for (; it != end; ++it) {
if (qsl.contains(toqstr(*it))) {
addPB->setEnabled(false);
return;
}
}
}
addPB->setEnabled(true);
}
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// //
// PreambleModule // PreambleModule
@ -566,7 +682,7 @@ GuiDocument::GuiDocument(GuiView & lv)
this, SLOT(classChanged())); this, SLOT(classChanged()));
selectionManager = selectionManager =
new GuiSelectionManager(latexModule->availableLV, latexModule->selectedLV, new ModuleSelMan(latexModule->availableLV, latexModule->selectedLV,
latexModule->addPB, latexModule->deletePB, latexModule->addPB, latexModule->deletePB,
latexModule->upPB, latexModule->downPB, latexModule->upPB, latexModule->downPB,
availableModel(), selectedModel()); availableModel(), selectedModel());
@ -920,6 +1036,35 @@ void GuiDocument::classChanged()
} }
namespace {
//This is an insanely complicated attempt to make this sort of thing
//work with RTL languages.
docstring formatStrVec(vector<string> 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() void GuiDocument::updateModuleInfo()
{ {
selectionManager->update(); selectionManager->update();
@ -930,36 +1075,38 @@ void GuiDocument::updateModuleInfo()
if (lv->selectionModel()->selectedIndexes().isEmpty()) if (lv->selectionModel()->selectedIndexes().isEmpty())
latexModule->infoML->document()->clear(); latexModule->infoML->document()->clear();
else { else {
QModelIndex const idx = lv->selectionModel()->currentIndex(); QModelIndex const & idx = lv->selectionModel()->currentIndex();
string const modName = fromqstr(idx.data().toString()); string const modName = fromqstr(idx.data().toString());
string desc = getModuleDescription(modName); docstring desc = getModuleDescription(modName);
vector<string> pkgList = getPackageList(modName); vector<string> pkgList = getPackageList(modName);
string pkgdesc; docstring pkgdesc = formatStrVec(pkgList, _("and"));
//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];
}
if (!pkgdesc.empty()) { if (!pkgdesc.empty()) {
if (!desc.empty()) if (!desc.empty())
desc += " "; desc += "\n";
desc += ("Requires " + pkgdesc + "."); 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 (!isModuleAvailable(modName)) {
if (!desc.empty()) if (!desc.empty())
desc += "\n"; desc += "\n";
desc += "WARNING: Some packages are unavailable!"; desc += _("WARNING: Some packages are unavailable!");
} }
latexModule->infoML->document()->setPlainText(toqstr(desc)); latexModule->infoML->document()->setPlainText(toqstr(desc));
} }
@ -1631,6 +1778,10 @@ void GuiDocument::updateContents()
it = selMods.begin(); it = selMods.begin();
for (; it != selMods.end(); ++it) for (; it != selMods.end(); ++it)
strlist2.push_back(toqstr(*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); selected_model_.setStringList(strlist2);
updateParams(bp_); updateParams(bp_);
@ -1680,7 +1831,7 @@ BufferId GuiDocument::id() const
} }
vector<string> GuiDocument::getModuleNames() vector<string> const & GuiDocument::getModuleNames()
{ {
return moduleNames_; return moduleNames_;
} }
@ -1692,33 +1843,6 @@ vector<string> 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<string> GuiDocument::getPackageList(string const & modName) const
{
LyXModule const * const mod = moduleList[modName];
if (!mod)
return vector<string>(); //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 TextClass const & GuiDocument::textClass() const
{ {
return textclasslist[bp_.getBaseClass()]; return textclasslist[bp_.getBaseClass()];
@ -1840,7 +1964,7 @@ void GuiDocument::loadModuleNames ()
moduleNames_.clear(); moduleNames_.clear();
LyXModuleList::const_iterator it = moduleList.begin(); LyXModuleList::const_iterator it = moduleList.begin();
for (; it != moduleList.end(); ++it) for (; it != moduleList.end(); ++it)
moduleNames_.push_back(it->name); moduleNames_.push_back(it->getName());
if (!moduleNames_.empty()) if (!moduleNames_.empty())
sort(moduleNames_.begin(), moduleNames_.end()); sort(moduleNames_.begin(), moduleNames_.end());
} }

View File

@ -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 class GuiDocument : public GuiDialog, public Ui::DocumentUi
{ {
Q_OBJECT Q_OBJECT
@ -178,16 +196,10 @@ protected:
/// ///
BufferId id() const; BufferId id() const;
/// List of available modules /// List of available modules
std::vector<std::string> getModuleNames(); std::vector<std::string> const & getModuleNames();
/// Modules in use in current buffer /// Modules in use in current buffer
std::vector<std::string> const & getSelectedModules(); std::vector<std::string> const & getSelectedModules();
/// ///
std::string getModuleDescription(std::string const & modName) const;
///
std::vector<std::string> getPackageList(std::string const & modName) const;
///
bool isModuleAvailable(std::string const & modName) const;
///
void setLanguage() const; void setLanguage() const;
/// ///
void saveAsDefault() const; void saveAsDefault() const;

View File

@ -68,6 +68,15 @@ GuiSelectionManager::GuiSelectionManager(
void GuiSelectionManager::update() void GuiSelectionManager::update()
{
updateAddPB();
updateDelPB();
updateDownPB();
updateUpPB();
}
void GuiSelectionManager::updateAddPB()
{ {
int const arows = availableLV->model()->rowCount(); int const arows = availableLV->model()->rowCount();
QModelIndexList const availSels = QModelIndexList const availSels =
@ -75,17 +84,38 @@ void GuiSelectionManager::update()
addPB->setEnabled(arows > 0 && addPB->setEnabled(arows > 0 &&
!availSels.isEmpty() && !availSels.isEmpty() &&
!isSelected(availSels.first())); !isSelected(availSels.first()));
}
void GuiSelectionManager::updateDelPB()
{
int const srows = selectedLV->model()->rowCount(); int const srows = selectedLV->model()->rowCount();
QModelIndexList const selSels = QModelIndexList const selSels =
selectedLV->selectionModel()->selectedIndexes(); selectedLV->selectionModel()->selectedIndexes();
int const sel_nr = selSels.empty() ? -1 : selSels.first().row(); int const sel_nr = selSels.empty() ? -1 : selSels.first().row();
deletePB->setEnabled(sel_nr >= 0); 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) bool GuiSelectionManager::isSelected(const QModelIndex & idx)
{ {
QString const str = idx.data().toString(); QString const str = idx.data().toString();
@ -211,7 +241,7 @@ void GuiSelectionManager::availableLV_clicked(const QModelIndex &)
void GuiSelectionManager::availableLV_doubleClicked(const QModelIndex & idx) void GuiSelectionManager::availableLV_doubleClicked(const QModelIndex & idx)
{ {
if (isSelected(idx)) if (isSelected(idx) || !addPB->isEnabled())
return; return;
if (idx.isValid()) if (idx.isValid())

View File

@ -50,6 +50,8 @@ public:
/// Sets the state of the various push buttons, depending upon the /// Sets the state of the various push buttons, depending upon the
/// state of the widgets. (E.g., "delete" is enabled only if the /// state of the widgets. (E.g., "delete" is enabled only if the
/// selection is non-empty.) /// selection is non-empty.)
/// Note: this is separated out into updateAddPB(), etc, below,
/// for easy over-riding of these functions.
void update(); void update();
/// Not strictly a matter of focus, which may be elsewhere, but /// 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 ///Given a QModelIndex from availableLV, determines whether it has
///been selected (i.e., is also in selectedLV). ///been selected (i.e., is also in selectedLV).
bool isSelected(const QModelIndex & idx); bool isSelected(const QModelIndex & idx);
///
QListView * availableLV;
///
QListView * selectedLV;
///
QPushButton * addPB;
///
QPushButton * deletePB;
///
QPushButton * upPB;
///
QPushButton * downPB;
///
QStringListModel * availableModel;
///
QStringListModel * selectedModel;
protected Q_SLOTS: protected Q_SLOTS:
/// ///
@ -103,15 +121,14 @@ protected Q_SLOTS:
bool eventFilter(QObject *, QEvent *); bool eventFilter(QObject *, QEvent *);
private: private:
QListView * availableLV; ///
QListView * selectedLV; virtual void updateAddPB();
QPushButton * addPB; ///
QPushButton * deletePB; virtual void updateDelPB();
QPushButton * upPB; ///
QPushButton * downPB; virtual void updateDownPB();
QStringListModel * availableModel; ///
QStringListModel * selectedModel; virtual void updateUpPB();
//Dialog::View * dialog;
bool selectedHasFocus_; bool selectedHasFocus_;
}; };